mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
Merge branch 'recloudstream:master' into master
This commit is contained in:
commit
04ecd15870
138 changed files with 3069 additions and 1232 deletions
3
.github/workflows/prerelease.yml
vendored
3
.github/workflows/prerelease.yml
vendored
|
@ -43,7 +43,8 @@ jobs:
|
||||||
echo "key_pwd=$KEY_PWD" >> $GITHUB_OUTPUT
|
echo "key_pwd=$KEY_PWD" >> $GITHUB_OUTPUT
|
||||||
- name: Run Gradle
|
- name: Run Gradle
|
||||||
run: |
|
run: |
|
||||||
./gradlew assemblePrerelease makeJar androidSourcesJar
|
./gradlew assemblePrerelease build androidSourcesJar
|
||||||
|
./gradlew makeJar # for classes.jar, has to be done after assemblePrerelease
|
||||||
env:
|
env:
|
||||||
SIGNING_KEY_ALIAS: "key0"
|
SIGNING_KEY_ALIAS: "key0"
|
||||||
SIGNING_KEY_PASSWORD: ${{ steps.fetch_keystore.outputs.key_pwd }}
|
SIGNING_KEY_PASSWORD: ${{ steps.fetch_keystore.outputs.key_pwd }}
|
||||||
|
|
1
.idea/gradle.xml
generated
1
.idea/gradle.xml
generated
|
@ -8,6 +8,7 @@
|
||||||
<option name="testRunner" value="GRADLE" />
|
<option name="testRunner" value="GRADLE" />
|
||||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="gradleJvm" value="jbr-17" />
|
||||||
<option name="modules">
|
<option name="modules">
|
||||||
<set>
|
<set>
|
||||||
<option value="$PROJECT_DIR$" />
|
<option value="$PROJECT_DIR$" />
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
|
import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
|
||||||
import org.jetbrains.dokka.gradle.DokkaTask
|
import org.jetbrains.dokka.gradle.DokkaTask
|
||||||
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.application")
|
id("com.android.application")
|
||||||
|
id("com.google.devtools.ksp")
|
||||||
id("kotlin-android")
|
id("kotlin-android")
|
||||||
id("kotlin-kapt")
|
id("kotlin-kapt")
|
||||||
id("org.jetbrains.dokka")
|
id("org.jetbrains.dokka")
|
||||||
|
@ -50,14 +52,15 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://developer.android.com/about/versions/14/behavior-changes-14#safer-dynamic-code-loading
|
compileSdk = 34
|
||||||
compileSdk = 33 // android 14 is fucked
|
|
||||||
buildToolsVersion = "34.0.0"
|
buildToolsVersion = "34.0.0"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.lagradost.cloudstream3"
|
applicationId = "com.lagradost.cloudstream3"
|
||||||
minSdk = 21
|
minSdk = 21
|
||||||
targetSdk = 33
|
|
||||||
|
// https://developer.android.com/about/versions/14/behavior-changes-14#safer-dynamic-code-loading
|
||||||
|
targetSdk = 33 // android 14 is fucked
|
||||||
|
|
||||||
versionCode = 62
|
versionCode = 62
|
||||||
versionName = "4.2.1"
|
versionName = "4.2.1"
|
||||||
|
@ -86,6 +89,11 @@ android {
|
||||||
)
|
)
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
|
ksp {
|
||||||
|
arg("room.schemaLocation", "$projectDir/schemas")
|
||||||
|
arg("exportSchema", "true")
|
||||||
|
}
|
||||||
|
|
||||||
kapt {
|
kapt {
|
||||||
includeCompileClasspath = true
|
includeCompileClasspath = true
|
||||||
}
|
}
|
||||||
|
@ -126,10 +134,6 @@ android {
|
||||||
versionCode = (System.currentTimeMillis() / 60000).toInt()
|
versionCode = (System.currentTimeMillis() / 60000).toInt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//toolchain {
|
|
||||||
// languageVersion.set(JavaLanguageVersion.of(17))
|
|
||||||
// }
|
|
||||||
// jvmToolchain(17)
|
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
isCoreLibraryDesugaringEnabled = true
|
isCoreLibraryDesugaringEnabled = true
|
||||||
|
@ -137,10 +141,6 @@ android {
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
targetCompatibility = JavaVersion.VERSION_1_8
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "1.8"
|
|
||||||
freeCompilerArgs = listOf("-Xjvm-default=compatibility")
|
|
||||||
}
|
|
||||||
lint {
|
lint {
|
||||||
abortOnError = false
|
abortOnError = false
|
||||||
checkReleaseBuilds = false
|
checkReleaseBuilds = false
|
||||||
|
@ -157,18 +157,16 @@ dependencies {
|
||||||
implementation("androidx.test.ext:junit-ktx:1.1.5")
|
implementation("androidx.test.ext:junit-ktx:1.1.5")
|
||||||
testImplementation("org.json:json:20230618")
|
testImplementation("org.json:json:20230618")
|
||||||
|
|
||||||
implementation("androidx.core:core-ktx:1.10.1") // need 34 for higher
|
implementation("androidx.core:core-ktx:1.12.0")
|
||||||
implementation("androidx.appcompat:appcompat:1.6.1") // need target 32 for 1.5.0
|
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||||
|
|
||||||
// dont change this to 1.6.0 it looks ugly af
|
implementation("com.google.android.material:material:1.10.0")
|
||||||
implementation("com.google.android.material:material:1.5.0")
|
|
||||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||||
|
|
||||||
// need 34 for higher
|
implementation("androidx.navigation:navigation-fragment-ktx:2.7.5")
|
||||||
implementation("androidx.navigation:navigation-fragment-ktx:2.6.0")
|
implementation("androidx.navigation:navigation-ui-ktx:2.7.5")
|
||||||
implementation("androidx.navigation:navigation-ui-ktx:2.6.0")
|
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.2")
|
||||||
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.1")
|
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
|
||||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
|
|
||||||
|
|
||||||
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.1.5")
|
||||||
|
@ -180,11 +178,15 @@ dependencies {
|
||||||
// DONT UPDATE, WILL CRASH ANDROID TV ????
|
// DONT UPDATE, WILL CRASH ANDROID TV ????
|
||||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.1")
|
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.1")
|
||||||
|
|
||||||
implementation("androidx.preference:preference-ktx:1.2.0")
|
implementation("androidx.preference:preference-ktx:1.2.1")
|
||||||
|
|
||||||
implementation("com.github.bumptech.glide:glide:4.13.1")
|
implementation("com.github.bumptech.glide:glide:4.15.1")
|
||||||
kapt("com.github.bumptech.glide:compiler:4.13.1")
|
ksp("com.github.bumptech.glide:ksp:4.15.1")
|
||||||
implementation("com.github.bumptech.glide:okhttp3-integration:4.13.0")
|
implementation("com.github.bumptech.glide:okhttp3-integration:4.15.1")
|
||||||
|
// for ksp
|
||||||
|
ksp("dev.zacsweers.autoservice:auto-service-ksp:1.1.0")
|
||||||
|
implementation("dev.zacsweers.autoservice:auto-service-ksp:1.1.0")
|
||||||
|
implementation("com.google.guava:guava:32.1.2-android")
|
||||||
|
|
||||||
implementation("jp.wasabeef:glide-transformations:4.3.0")
|
implementation("jp.wasabeef:glide-transformations:4.3.0")
|
||||||
|
|
||||||
|
@ -208,9 +210,6 @@ dependencies {
|
||||||
implementation("ch.acra:acra-core:5.11.2")
|
implementation("ch.acra:acra-core:5.11.2")
|
||||||
implementation("ch.acra:acra-toast:5.11.2")
|
implementation("ch.acra:acra-toast:5.11.2")
|
||||||
|
|
||||||
compileOnly("com.google.auto.service:auto-service-annotations:1.1.1")
|
|
||||||
//either for java sources:
|
|
||||||
annotationProcessor("com.google.auto.service:auto-service:1.1.1")
|
|
||||||
//or for kotlin sources (requires kapt gradle plugin):
|
//or for kotlin sources (requires kapt gradle plugin):
|
||||||
kapt("com.google.auto.service:auto-service:1.1.1")
|
kapt("com.google.auto.service:auto-service:1.1.1")
|
||||||
|
|
||||||
|
@ -232,7 +231,7 @@ dependencies {
|
||||||
// Networking
|
// Networking
|
||||||
// implementation("com.squareup.okhttp3:okhttp:4.9.2")
|
// implementation("com.squareup.okhttp3:okhttp:4.9.2")
|
||||||
// implementation("com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.1")
|
// implementation("com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.1")
|
||||||
implementation("com.github.Blatzar:NiceHttp:0.4.3")
|
implementation("com.github.Blatzar:NiceHttp:0.4.4") // http library
|
||||||
// To fix SSL fuckery on android 9
|
// To fix SSL fuckery on android 9
|
||||||
implementation("org.conscrypt:conscrypt-android:2.5.2")
|
implementation("org.conscrypt:conscrypt-android:2.5.2")
|
||||||
// Util to skip the URI file fuckery 🙏
|
// Util to skip the URI file fuckery 🙏
|
||||||
|
@ -277,7 +276,13 @@ tasks.register("makeJar", Copy::class) {
|
||||||
from("build/intermediates/compile_app_classes_jar/prereleaseDebug")
|
from("build/intermediates/compile_app_classes_jar/prereleaseDebug")
|
||||||
into("build")
|
into("build")
|
||||||
include("classes.jar")
|
include("classes.jar")
|
||||||
dependsOn("build")
|
}
|
||||||
|
|
||||||
|
tasks.withType<KotlinCompile> {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
freeCompilerArgs = listOf("-Xjvm-default=all-compatibility")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<DokkaTask>().configureEach {
|
tasks.withType<DokkaTask>().configureEach {
|
||||||
|
|
|
@ -19,6 +19,7 @@ import com.lagradost.cloudstream3.databinding.FragmentSearchBinding
|
||||||
import com.lagradost.cloudstream3.databinding.FragmentSearchTvBinding
|
import com.lagradost.cloudstream3.databinding.FragmentSearchTvBinding
|
||||||
import com.lagradost.cloudstream3.databinding.HomeResultGridBinding
|
import com.lagradost.cloudstream3.databinding.HomeResultGridBinding
|
||||||
import com.lagradost.cloudstream3.databinding.HomepageParentBinding
|
import com.lagradost.cloudstream3.databinding.HomepageParentBinding
|
||||||
|
import com.lagradost.cloudstream3.databinding.HomepageParentEmulatorBinding
|
||||||
import com.lagradost.cloudstream3.databinding.HomepageParentTvBinding
|
import com.lagradost.cloudstream3.databinding.HomepageParentTvBinding
|
||||||
import com.lagradost.cloudstream3.databinding.PlayerCustomLayoutBinding
|
import com.lagradost.cloudstream3.databinding.PlayerCustomLayoutBinding
|
||||||
import com.lagradost.cloudstream3.databinding.PlayerCustomLayoutTvBinding
|
import com.lagradost.cloudstream3.databinding.PlayerCustomLayoutTvBinding
|
||||||
|
@ -119,8 +120,9 @@ class ExampleInstrumentedTest {
|
||||||
// testAllLayouts<HomeScrollViewBinding>(activity, R.layout.home_scroll_view, R.layout.home_scroll_view_tv)
|
// testAllLayouts<HomeScrollViewBinding>(activity, R.layout.home_scroll_view, R.layout.home_scroll_view_tv)
|
||||||
// testAllLayouts<HomeScrollViewTvBinding>(activity, R.layout.home_scroll_view, R.layout.home_scroll_view_tv)
|
// testAllLayouts<HomeScrollViewTvBinding>(activity, R.layout.home_scroll_view, R.layout.home_scroll_view_tv)
|
||||||
|
|
||||||
testAllLayouts<HomepageParentTvBinding>(activity, R.layout.homepage_parent_tv, R.layout.homepage_parent)
|
testAllLayouts<HomepageParentTvBinding>(activity, R.layout.homepage_parent_tv, R.layout.homepage_parent_emulator, R.layout.homepage_parent)
|
||||||
testAllLayouts<HomepageParentBinding>(activity, R.layout.homepage_parent_tv, R.layout.homepage_parent)
|
testAllLayouts<HomepageParentEmulatorBinding>(activity, R.layout.homepage_parent_tv, R.layout.homepage_parent_emulator, R.layout.homepage_parent)
|
||||||
|
testAllLayouts<HomepageParentBinding>(activity, R.layout.homepage_parent_tv, R.layout.homepage_parent_emulator, R.layout.homepage_parent)
|
||||||
|
|
||||||
testAllLayouts<FragmentLibraryTvBinding>(activity, R.layout.fragment_library_tv, R.layout.fragment_library)
|
testAllLayouts<FragmentLibraryTvBinding>(activity, R.layout.fragment_library_tv, R.layout.fragment_library)
|
||||||
testAllLayouts<FragmentLibraryBinding>(activity, R.layout.fragment_library_tv, R.layout.fragment_library)
|
testAllLayouts<FragmentLibraryBinding>(activity, R.layout.fragment_library_tv, R.layout.fragment_library)
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<uses-permission android:name="android.permission.INTERNET" /> <!-- unless you only use cs3 as a player for downloaded stuff, you need this -->
|
<uses-permission android:name="android.permission.INTERNET" /> <!-- unless you only use cs3 as a player for downloaded stuff, you need this -->
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- Downloads -->
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- Downloads -->
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- Downloads on low api devices -->
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- Downloads on low api devices -->
|
||||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> <!-- Plugin API -->
|
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" /> <!-- Plugin API -->
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <!-- some dependency needs this -->
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <!-- some dependency needs this -->
|
||||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <!-- Used for player vertical slide -->
|
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <!-- Used for player vertical slide -->
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <!-- Used for app update -->
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <!-- Used for app update -->
|
||||||
|
@ -17,7 +17,11 @@
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> <!-- Required for getting arbitrary Aniyomi packages -->
|
<!-- Required for getting arbitrary Aniyomi packages -->
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||||
|
tools:ignore="QueryAllPackagesPermission" />
|
||||||
|
|
||||||
<!-- Fixes android tv fuckery -->
|
<!-- Fixes android tv fuckery -->
|
||||||
<uses-feature
|
<uses-feature
|
||||||
android:name="android.hardware.touchscreen"
|
android:name="android.hardware.touchscreen"
|
||||||
|
@ -37,9 +41,11 @@
|
||||||
<application
|
<application
|
||||||
android:name=".AcraApplication"
|
android:name=".AcraApplication"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
android:enableOnBackInvokedCallback="true"
|
||||||
android:appCategory="video"
|
android:appCategory="video"
|
||||||
android:banner="@mipmap/ic_banner"
|
android:banner="@mipmap/ic_banner"
|
||||||
android:fullBackupContent="@xml/backup_descriptor"
|
android:fullBackupContent="@xml/backup_descriptor"
|
||||||
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:largeHeap="true"
|
android:largeHeap="true"
|
||||||
|
@ -47,7 +53,7 @@
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="true"
|
||||||
tools:targetApi="o">
|
tools:targetApi="tiramisu">
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
|
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
|
||||||
|
@ -96,12 +102,6 @@
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
android:resizeableActivity="true"
|
android:resizeableActivity="true"
|
||||||
android:supportsPictureInPicture="true">
|
android:supportsPictureInPicture="true">
|
||||||
<intent-filter android:exported="true">
|
|
||||||
<action android:name="android.intent.action.MAIN" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
|
||||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
|
||||||
</intent-filter>
|
|
||||||
|
|
||||||
<!-- cloudstreamplayer://encodedUrl?name=Dune -->
|
<!-- cloudstreamplayer://encodedUrl?name=Dune -->
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
@ -165,6 +165,21 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".ui.account.AccountSelectActivity"
|
||||||
|
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter android:exported="true">
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.EasterEggMonke"
|
android:name=".ui.EasterEggMonke"
|
||||||
android:exported="true" />
|
android:exported="true" />
|
||||||
|
@ -172,8 +187,8 @@
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".receivers.VideoDownloadRestartReceiver"
|
android:name=".receivers.VideoDownloadRestartReceiver"
|
||||||
android:enabled="false"
|
android:enabled="false"
|
||||||
android:exported="true">
|
android:exported="false">
|
||||||
<intent-filter android:exported="true">
|
<intent-filter android:exported="false">
|
||||||
<action android:name="restart_service" />
|
<action android:name="restart_service" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
|
@ -8,7 +8,6 @@ import android.content.Intent
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import com.google.auto.service.AutoService
|
|
||||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||||
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
|
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
|
||||||
import com.lagradost.cloudstream3.plugins.PluginManager
|
import com.lagradost.cloudstream3.plugins.PluginManager
|
||||||
|
@ -37,7 +36,6 @@ import java.lang.ref.WeakReference
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
|
||||||
class CustomReportSender : ReportSender {
|
class CustomReportSender : ReportSender {
|
||||||
// Sends all your crashes to google forms
|
// Sends all your crashes to google forms
|
||||||
override fun send(context: Context, errorContent: CrashReportData) {
|
override fun send(context: Context, errorContent: CrashReportData) {
|
||||||
|
@ -65,7 +63,6 @@ class CustomReportSender : ReportSender {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@AutoService(ReportSenderFactory::class)
|
|
||||||
class CustomSenderFactory : ReportSenderFactory {
|
class CustomSenderFactory : ReportSenderFactory {
|
||||||
override fun create(context: Context, config: CoreConfiguration): ReportSender {
|
override fun create(context: Context, config: CoreConfiguration): ReportSender {
|
||||||
return CustomReportSender()
|
return CustomReportSender()
|
||||||
|
|
|
@ -1246,6 +1246,18 @@ interface LoadResponse {
|
||||||
return this.syncData[aniListIdPrefix]
|
return this.syncData[aniListIdPrefix]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun LoadResponse.getImdbId(): String? {
|
||||||
|
return normalSafeApiCall {
|
||||||
|
SimklApi.readIdFromString(this.syncData[simklIdPrefix])?.get(SimklApi.Companion.SyncServices.Imdb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun LoadResponse.getTMDbId(): String? {
|
||||||
|
return normalSafeApiCall {
|
||||||
|
SimklApi.readIdFromString(this.syncData[simklIdPrefix])?.get(SimklApi.Companion.SyncServices.Tmdb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun LoadResponse.addMalId(id: Int?) {
|
fun LoadResponse.addMalId(id: Int?) {
|
||||||
this.syncData[malIdPrefix] = (id ?: return).toString()
|
this.syncData[malIdPrefix] = (id ?: return).toString()
|
||||||
this.addSimklId(SimklApi.Companion.SyncServices.Mal, id.toString())
|
this.addSimklId(SimklApi.Companion.SyncServices.Mal, id.toString())
|
||||||
|
@ -1453,6 +1465,15 @@ interface EpisodeResponse {
|
||||||
var nextAiring: NextAiring?
|
var nextAiring: NextAiring?
|
||||||
var seasonNames: List<SeasonData>?
|
var seasonNames: List<SeasonData>?
|
||||||
fun getLatestEpisodes(): Map<DubStatus, Int?>
|
fun getLatestEpisodes(): Map<DubStatus, Int?>
|
||||||
|
|
||||||
|
/** Count all episodes in all previous seasons up until this episode to get a total count.
|
||||||
|
* Example:
|
||||||
|
* Season 1: 10 episodes.
|
||||||
|
* Season 2: 6 episodes.
|
||||||
|
*
|
||||||
|
* getTotalEpisodeIndex(episode = 3, season = 2) -> 10 + 3 = 13
|
||||||
|
* */
|
||||||
|
fun getTotalEpisodeIndex(episode: Int, season: Int): Int
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmName("addSeasonNamesString")
|
@JvmName("addSeasonNamesString")
|
||||||
|
@ -1532,6 +1553,12 @@ data class AnimeLoadResponse(
|
||||||
.takeUnless { it == Int.MIN_VALUE }
|
.takeUnless { it == Int.MIN_VALUE }
|
||||||
}.toMap()
|
}.toMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getTotalEpisodeIndex(episode: Int, season: Int): Int {
|
||||||
|
return this.episodes.maxOf { (_, episodes) ->
|
||||||
|
episodes.count { ((it.season ?: Int.MIN_VALUE) < season) && it.season != 0 }
|
||||||
|
} + episode
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1740,6 +1767,12 @@ data class TvSeriesLoadResponse(
|
||||||
.takeUnless { it == Int.MIN_VALUE }
|
.takeUnless { it == Int.MIN_VALUE }
|
||||||
return mapOf(DubStatus.None to max)
|
return mapOf(DubStatus.None to max)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getTotalEpisodeIndex(episode: Int, season: Int): Int {
|
||||||
|
return episodes.count {
|
||||||
|
(it.season ?: Int.MIN_VALUE) < season && it.season != 0
|
||||||
|
} + episode
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun MainAPI.newTvSeriesLoadResponse(
|
suspend fun MainAPI.newTvSeriesLoadResponse(
|
||||||
|
|
|
@ -19,6 +19,7 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.OnBackPressedCallback
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.annotation.IdRes
|
import androidx.annotation.IdRes
|
||||||
import androidx.annotation.MainThread
|
import androidx.annotation.MainThread
|
||||||
|
@ -132,7 +133,6 @@ import com.lagradost.cloudstream3.utils.DataStore.setKey
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.migrateResumeWatching
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.migrateResumeWatching
|
||||||
import com.lagradost.cloudstream3.utils.Event
|
import com.lagradost.cloudstream3.utils.Event
|
||||||
import com.lagradost.cloudstream3.utils.IOnBackPressed
|
|
||||||
import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate
|
import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.changeStatusBarState
|
import com.lagradost.cloudstream3.utils.UIHelper.changeStatusBarState
|
||||||
|
@ -309,9 +309,13 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
// kinda shitty solution, but cant com main->home otherwise for popups
|
// kinda shitty solution, but cant com main->home otherwise for popups
|
||||||
val bookmarksUpdatedEvent = Event<Boolean>()
|
val bookmarksUpdatedEvent = Event<Boolean>()
|
||||||
/**
|
/**
|
||||||
* Used by data store helper to fully reload home when switching accounts
|
* Used by DataStoreHelper to fully reload home when switching accounts
|
||||||
*/
|
*/
|
||||||
val reloadHomeEvent = Event<Boolean>()
|
val reloadHomeEvent = Event<Boolean>()
|
||||||
|
/**
|
||||||
|
* Used by DataStoreHelper to fully reload library when switching accounts
|
||||||
|
*/
|
||||||
|
val reloadLibraryEvent = Event<Boolean>()
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -650,34 +654,6 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
builder.show().setDefaultFocus()
|
builder.show().setDefaultFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun backPressed() {
|
|
||||||
this.window?.navigationBarColor =
|
|
||||||
this.colorFromAttribute(R.attr.primaryGrayBackground)
|
|
||||||
this.updateLocale()
|
|
||||||
this.updateLocale()
|
|
||||||
|
|
||||||
val navHostFragment =
|
|
||||||
supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as? NavHostFragment
|
|
||||||
val navController = navHostFragment?.navController
|
|
||||||
val isAtHome =
|
|
||||||
navController?.currentDestination?.matchDestination(R.id.navigation_home) == true
|
|
||||||
|
|
||||||
if (isAtHome && isTrueTvSettings()) {
|
|
||||||
showConfirmExitDialog()
|
|
||||||
} else {
|
|
||||||
super.onBackPressed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBackPressed() {
|
|
||||||
((supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as? NavHostFragment?)?.childFragmentManager?.primaryNavigationFragment as? IOnBackPressed)?.onBackPressed()
|
|
||||||
?.let { runNormal ->
|
|
||||||
if (runNormal) backPressed()
|
|
||||||
} ?: run {
|
|
||||||
backPressed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
val broadcastIntent = Intent()
|
val broadcastIntent = Intent()
|
||||||
broadcastIntent.action = "restart_service"
|
broadcastIntent.action = "restart_service"
|
||||||
|
@ -1087,6 +1063,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
} catch (_: Throwable) {
|
} catch (_: Throwable) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
app.initClient(this)
|
app.initClient(this)
|
||||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
@ -1306,7 +1283,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
this@MainActivity.getString(R.string.action_add_to_bookmarks),
|
this@MainActivity.getString(R.string.action_add_to_bookmarks),
|
||||||
showApply = false,
|
showApply = false,
|
||||||
{}) {
|
{}) {
|
||||||
viewModel.updateWatchStatus(WatchType.values()[it])
|
viewModel.updateWatchStatus(WatchType.values()[it], this@MainActivity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1384,6 +1361,12 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
this.putString(SearchFragment.SEARCH_QUERY, nextSearchQuery)
|
this.putString(SearchFragment.SEARCH_QUERY, nextSearchQuery)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isTvSettings()) {
|
||||||
|
if (navDestination.matchDestination(R.id.navigation_home)) {
|
||||||
|
attachBackPressedCallback()
|
||||||
|
} else detachBackPressedCallback()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//val navController = findNavController(R.id.nav_host_fragment)
|
//val navController = findNavController(R.id.nav_host_fragment)
|
||||||
|
@ -1598,6 +1581,44 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
onBackPressedDispatcher.addCallback(
|
||||||
|
this,
|
||||||
|
object : OnBackPressedCallback(true) {
|
||||||
|
override fun handleOnBackPressed() {
|
||||||
|
window?.navigationBarColor = colorFromAttribute(R.attr.primaryGrayBackground)
|
||||||
|
updateLocale()
|
||||||
|
|
||||||
|
// If we don't disable we end up in a loop with default behavior calling
|
||||||
|
// this callback as well, so we disable it, run default behavior,
|
||||||
|
// then re-enable this callback so it can be used for next back press.
|
||||||
|
isEnabled = false
|
||||||
|
onBackPressedDispatcher.onBackPressed()
|
||||||
|
isEnabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var backPressedCallback: OnBackPressedCallback? = null
|
||||||
|
|
||||||
|
private fun attachBackPressedCallback() {
|
||||||
|
if (backPressedCallback == null) {
|
||||||
|
backPressedCallback = object : OnBackPressedCallback(true) {
|
||||||
|
override fun handleOnBackPressed() {
|
||||||
|
showConfirmExitDialog()
|
||||||
|
window?.navigationBarColor =
|
||||||
|
colorFromAttribute(R.attr.primaryGrayBackground)
|
||||||
|
updateLocale()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backPressedCallback?.isEnabled = true
|
||||||
|
onBackPressedDispatcher.addCallback(this, backPressedCallback ?: return)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun detachBackPressedCallback() {
|
||||||
|
backPressedCallback?.isEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun checkGithubConnectivity(): Boolean {
|
suspend fun checkGithubConnectivity(): Boolean {
|
||||||
|
|
|
@ -16,13 +16,13 @@ import javax.crypto.Cipher
|
||||||
import javax.crypto.spec.IvParameterSpec
|
import javax.crypto.spec.IvParameterSpec
|
||||||
import javax.crypto.spec.SecretKeySpec
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
|
||||||
// No License found in https://github.com/enimax-anime/key
|
// Code found in https://github.com/theonlymo/keys
|
||||||
// special credits to @enimax for providing key
|
// special credits to @theonlymo for providing key
|
||||||
class Megacloud : Rabbitstream() {
|
class Megacloud : Rabbitstream() {
|
||||||
override val name = "Megacloud"
|
override val name = "Megacloud"
|
||||||
override val mainUrl = "https://megacloud.tv"
|
override val mainUrl = "https://megacloud.tv"
|
||||||
override val embed = "embed-2/ajax/e-1"
|
override val embed = "embed-2/ajax/e-1"
|
||||||
override val key = "https://raw.githubusercontent.com/enimax-anime/key/e6/key.txt"
|
override val key = "https://raw.githubusercontent.com/theonlymo/keys/e1/key"
|
||||||
}
|
}
|
||||||
|
|
||||||
class Dokicloud : Rabbitstream() {
|
class Dokicloud : Rabbitstream() {
|
||||||
|
@ -35,7 +35,7 @@ open class Rabbitstream : ExtractorApi() {
|
||||||
override val mainUrl = "https://rabbitstream.net"
|
override val mainUrl = "https://rabbitstream.net"
|
||||||
override val requiresReferer = false
|
override val requiresReferer = false
|
||||||
open val embed = "ajax/embed-4"
|
open val embed = "ajax/embed-4"
|
||||||
open val key = "https://raw.githubusercontent.com/enimax-anime/key/e4/key.txt"
|
open val key = "https://raw.githubusercontent.com/theonlymo/keys/e4/key"
|
||||||
|
|
||||||
override suspend fun getUrl(
|
override suspend fun getUrl(
|
||||||
url: String,
|
url: String,
|
||||||
|
@ -86,21 +86,23 @@ open class Rabbitstream : ExtractorApi() {
|
||||||
|
|
||||||
private suspend fun getRawKey(): String = app.get(key).text
|
private suspend fun getRawKey(): String = app.get(key).text
|
||||||
|
|
||||||
private fun extractRealKey(originalString: String?, stops: String): Pair<String, String> {
|
private fun extractRealKey(sources: String, stops: String): Pair<String, String> {
|
||||||
val table = parseJson<List<List<Int>>>(stops)
|
val decryptKey = parseJson<List<List<Int>>>(stops)
|
||||||
val decryptedKey = StringBuilder()
|
val sourcesArray = sources.toCharArray()
|
||||||
var offset = 0
|
|
||||||
var encryptedString = originalString
|
|
||||||
|
|
||||||
table.forEach { (start, end) ->
|
var extractedKey = ""
|
||||||
decryptedKey.append(encryptedString?.substring(start - offset, end - offset))
|
var currentIndex = 0
|
||||||
encryptedString = encryptedString?.substring(
|
for (index in decryptKey) {
|
||||||
0,
|
val start = index[0] + currentIndex
|
||||||
start - offset
|
val end = start + index[1]
|
||||||
) + encryptedString?.substring(end - offset)
|
for (i in start until end) {
|
||||||
offset += end - start
|
extractedKey += sourcesArray[i].toString()
|
||||||
|
sourcesArray[i] = ' '
|
||||||
|
}
|
||||||
|
currentIndex += index[1]
|
||||||
}
|
}
|
||||||
return decryptedKey.toString() to encryptedString.toString()
|
|
||||||
|
return extractedKey to sourcesArray.joinToString("")
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun <reified T> decryptMapped(input: String, key: String): T? {
|
private inline fun <reified T> decryptMapped(input: String, key: String): T? {
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
package com.lagradost.cloudstream3.metaproviders
|
|
||||||
|
|
||||||
import com.lagradost.cloudstream3.*
|
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
|
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
|
||||||
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi
|
|
||||||
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
|
||||||
import com.lagradost.cloudstream3.syncproviders.providers.AniListApi
|
|
||||||
import com.lagradost.cloudstream3.syncproviders.providers.MALApi
|
|
||||||
import com.lagradost.cloudstream3.utils.SyncUtil
|
|
||||||
|
|
||||||
// wont be implemented
|
|
||||||
class MultiAnimeProvider : MainAPI() {
|
|
||||||
override var name = "MultiAnime"
|
|
||||||
override var lang = "en"
|
|
||||||
override val usesWebView = true
|
|
||||||
override val supportedTypes = setOf(TvType.Anime)
|
|
||||||
private val syncApi: SyncAPI = aniListApi
|
|
||||||
|
|
||||||
private val syncUtilType by lazy {
|
|
||||||
when (syncApi) {
|
|
||||||
is AniListApi -> "anilist"
|
|
||||||
is MALApi -> "myanimelist"
|
|
||||||
else -> throw ErrorLoadingException("Invalid Api")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val validApis
|
|
||||||
get() =
|
|
||||||
synchronized(APIHolder.apis) {
|
|
||||||
APIHolder.apis.filter {
|
|
||||||
it.lang == this.lang && it::class.java != this::class.java && it.supportedTypes.contains(
|
|
||||||
TvType.Anime
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun filterName(name: String): String {
|
|
||||||
return Regex("""[^a-zA-Z0-9-]""").replace(name, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun search(query: String): List<SearchResponse>? {
|
|
||||||
return syncApi.search(query)?.map {
|
|
||||||
AnimeSearchResponse(it.name, it.url, this.name, TvType.Anime, it.posterUrl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun load(url: String): LoadResponse? {
|
|
||||||
return syncApi.getResult(url)?.let { res ->
|
|
||||||
val data = SyncUtil.getUrlsFromId(res.id, syncUtilType).amap { url ->
|
|
||||||
validApis.firstOrNull { api -> url.startsWith(api.mainUrl) }?.load(url)
|
|
||||||
}.filterNotNull()
|
|
||||||
|
|
||||||
val type =
|
|
||||||
if (data.any { it.type == TvType.AnimeMovie }) TvType.AnimeMovie else TvType.Anime
|
|
||||||
|
|
||||||
newAnimeLoadResponse(
|
|
||||||
res.title ?: throw ErrorLoadingException("No Title found"),
|
|
||||||
url,
|
|
||||||
type
|
|
||||||
) {
|
|
||||||
posterUrl = res.posterUrl
|
|
||||||
plot = res.synopsis
|
|
||||||
tags = res.genres
|
|
||||||
rating = res.publicScore
|
|
||||||
addTrailer(res.trailers)
|
|
||||||
addAniListId(res.id.toIntOrNull())
|
|
||||||
recommendations = res.recommendations
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,7 +8,9 @@ import com.lagradost.cloudstream3.syncproviders.SyncIdName
|
||||||
import com.lagradost.cloudstream3.ui.WatchType
|
import com.lagradost.cloudstream3.ui.WatchType
|
||||||
import com.lagradost.cloudstream3.ui.library.ListSorting
|
import com.lagradost.cloudstream3.ui.library.ListSorting
|
||||||
import com.lagradost.cloudstream3.ui.result.txt
|
import com.lagradost.cloudstream3.ui.result.txt
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioWork
|
import com.lagradost.cloudstream3.utils.Coroutines.ioWork
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllFavorites
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllSubscriptions
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllSubscriptions
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllWatchStateIds
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllWatchStateIds
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getBookmarkedData
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getBookmarkedData
|
||||||
|
@ -69,29 +71,52 @@ class LocalList : SyncAPI {
|
||||||
}?.distinctBy { it.first } ?: return null
|
}?.distinctBy { it.first } ?: return null
|
||||||
|
|
||||||
val list = ioWork {
|
val list = ioWork {
|
||||||
watchStatusIds.groupBy {
|
val isTrueTv = isTrueTvSettings()
|
||||||
it.second.stringRes
|
|
||||||
}.mapValues { group ->
|
val baseMap = WatchType.values().filter { it != WatchType.NONE }.associate {
|
||||||
|
// None is not something to display
|
||||||
|
it.stringRes to emptyList<SyncAPI.LibraryItem>()
|
||||||
|
} + mapOf(
|
||||||
|
R.string.favorites_list_name to emptyList()
|
||||||
|
) + if (!isTrueTv) {
|
||||||
|
mapOf(
|
||||||
|
R.string.subscription_list_name to emptyList()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
emptyMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
val watchStatusMap = watchStatusIds.groupBy { it.second.stringRes }.mapValues { group ->
|
||||||
group.value.mapNotNull {
|
group.value.mapNotNull {
|
||||||
getBookmarkedData(it.first)?.toLibraryItem(it.first.toString())
|
getBookmarkedData(it.first)?.toLibraryItem(it.first.toString())
|
||||||
}
|
}
|
||||||
} + mapOf(R.string.subscription_list_name to getAllSubscriptions().mapNotNull {
|
}
|
||||||
|
|
||||||
|
val favoritesMap = mapOf(R.string.favorites_list_name to getAllFavorites().mapNotNull {
|
||||||
it.toLibraryItem()
|
it.toLibraryItem()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Don't show subscriptions on TV
|
||||||
|
val result = if (isTrueTv) {
|
||||||
|
baseMap + watchStatusMap + favoritesMap
|
||||||
|
} else {
|
||||||
|
val subscriptionsMap = mapOf(R.string.subscription_list_name to getAllSubscriptions().mapNotNull {
|
||||||
|
it.toLibraryItem()
|
||||||
|
})
|
||||||
|
|
||||||
|
baseMap + watchStatusMap + subscriptionsMap + favoritesMap
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
val baseMap = WatchType.values().filter { it != WatchType.NONE }.associate {
|
|
||||||
// None is not something to display
|
|
||||||
it.stringRes to emptyList<SyncAPI.LibraryItem>()
|
|
||||||
} + mapOf(R.string.subscription_list_name to emptyList())
|
|
||||||
|
|
||||||
return SyncAPI.LibraryMetadata(
|
return SyncAPI.LibraryMetadata(
|
||||||
(baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) },
|
list.map { SyncAPI.LibraryList(txt(it.key), it.value) },
|
||||||
setOf(
|
setOf(
|
||||||
ListSorting.AlphabeticalA,
|
ListSorting.AlphabeticalA,
|
||||||
ListSorting.AlphabeticalZ,
|
ListSorting.AlphabeticalZ,
|
||||||
// ListSorting.UpdatedNew,
|
ListSorting.UpdatedNew,
|
||||||
// ListSorting.UpdatedOld,
|
ListSorting.UpdatedOld,
|
||||||
// ListSorting.RatingHigh,
|
// ListSorting.RatingHigh,
|
||||||
// ListSorting.RatingLow,
|
// ListSorting.RatingLow,
|
||||||
)
|
)
|
||||||
|
|
|
@ -203,7 +203,7 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Read the id string to get all other ids */
|
/** Read the id string to get all other ids */
|
||||||
private fun readIdFromString(idString: String?): Map<SyncServices, String> {
|
fun readIdFromString(idString: String?): Map<SyncServices, String> {
|
||||||
return tryParseJson(idString) ?: return emptyMap()
|
return tryParseJson(idString) ?: return emptyMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,6 +376,8 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
private var status: Int? = null,
|
private var status: Int? = null,
|
||||||
private var addEpisodes: Pair<List<MediaObject.Season>?, List<MediaObject.Season.Episode>?>? = null,
|
private var addEpisodes: Pair<List<MediaObject.Season>?, List<MediaObject.Season.Episode>?>? = null,
|
||||||
private var removeEpisodes: Pair<List<MediaObject.Season>?, List<MediaObject.Season.Episode>?>? = null,
|
private var removeEpisodes: Pair<List<MediaObject.Season>?, List<MediaObject.Season.Episode>?>? = null,
|
||||||
|
// Required for knowing if the status should be overwritten
|
||||||
|
private var onList: Boolean = false
|
||||||
) {
|
) {
|
||||||
fun interceptor(interceptor: Interceptor) = apply { this.interceptor = interceptor }
|
fun interceptor(interceptor: Interceptor) = apply { this.interceptor = interceptor }
|
||||||
fun apiUrl(url: String) = apply { this.url = url }
|
fun apiUrl(url: String) = apply { this.url = url }
|
||||||
|
@ -387,6 +389,7 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun status(newStatus: Int?, oldStatus: Int?) = apply {
|
fun status(newStatus: Int?, oldStatus: Int?) = apply {
|
||||||
|
onList = oldStatus != null
|
||||||
// Only set status if its new
|
// Only set status if its new
|
||||||
if (newStatus != oldStatus) {
|
if (newStatus != oldStatus) {
|
||||||
this.status = newStatus
|
this.status = newStatus
|
||||||
|
@ -412,6 +415,11 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
// Do not add episodes if there is no change
|
// Do not add episodes if there is no change
|
||||||
if (newEpisodes > (oldEpisodes ?: 0)) {
|
if (newEpisodes > (oldEpisodes ?: 0)) {
|
||||||
this.addEpisodes = getEpisodes(allEpisodes.take(newEpisodes))
|
this.addEpisodes = getEpisodes(allEpisodes.take(newEpisodes))
|
||||||
|
|
||||||
|
// Set to watching if episodes are added and there is no current status
|
||||||
|
if (!onList) {
|
||||||
|
status = SimklListStatusType.Watching.value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ((oldEpisodes ?: 0) > newEpisodes) {
|
if ((oldEpisodes ?: 0) > newEpisodes) {
|
||||||
this.removeEpisodes = getEpisodes(allEpisodes.drop(newEpisodes))
|
this.removeEpisodes = getEpisodes(allEpisodes.drop(newEpisodes))
|
||||||
|
@ -431,6 +439,28 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
interceptor = interceptor
|
interceptor = interceptor
|
||||||
).isSuccessful
|
).isSuccessful
|
||||||
} else {
|
} else {
|
||||||
|
val statusResponse = status?.let { setStatus ->
|
||||||
|
val newStatus =
|
||||||
|
SimklListStatusType.values()
|
||||||
|
.firstOrNull { it.value == setStatus }?.originalName
|
||||||
|
?: SimklListStatusType.Watching.originalName!!
|
||||||
|
|
||||||
|
app.post(
|
||||||
|
"${this.url}/sync/add-to-list",
|
||||||
|
json = StatusRequest(
|
||||||
|
shows = listOf(
|
||||||
|
StatusMediaObject(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
ids,
|
||||||
|
newStatus,
|
||||||
|
)
|
||||||
|
), movies = emptyList()
|
||||||
|
),
|
||||||
|
interceptor = interceptor
|
||||||
|
).isSuccessful
|
||||||
|
} ?: true
|
||||||
|
|
||||||
val episodeRemovalResponse = removeEpisodes?.let { (seasons, episodes) ->
|
val episodeRemovalResponse = removeEpisodes?.let { (seasons, episodes) ->
|
||||||
app.post(
|
app.post(
|
||||||
"${this.url}/sync/history/remove",
|
"${this.url}/sync/history/remove",
|
||||||
|
@ -472,28 +502,6 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
val statusResponse = status?.let { setStatus ->
|
|
||||||
val newStatus =
|
|
||||||
SimklListStatusType.values()
|
|
||||||
.firstOrNull { it.value == setStatus }?.originalName
|
|
||||||
?: SimklListStatusType.Watching.originalName!!
|
|
||||||
|
|
||||||
app.post(
|
|
||||||
"${this.url}/sync/add-to-list",
|
|
||||||
json = StatusRequest(
|
|
||||||
shows = listOf(
|
|
||||||
StatusMediaObject(
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
ids,
|
|
||||||
newStatus,
|
|
||||||
)
|
|
||||||
), movies = emptyList()
|
|
||||||
),
|
|
||||||
interceptor = interceptor
|
|
||||||
).isSuccessful
|
|
||||||
} ?: true
|
|
||||||
|
|
||||||
statusResponse && episodeRemovalResponse && historyResponse
|
statusResponse && episodeRemovalResponse && historyResponse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,106 +0,0 @@
|
||||||
package com.lagradost.cloudstream3.ui
|
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
|
||||||
import androidx.recyclerview.widget.ListAdapter
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import androidx.viewbinding.ViewBinding
|
|
||||||
import com.lagradost.cloudstream3.databinding.WhoIsWatchingAccountAddBinding
|
|
||||||
import com.lagradost.cloudstream3.databinding.WhoIsWatchingAccountBinding
|
|
||||||
import com.lagradost.cloudstream3.ui.result.setImage
|
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
|
||||||
|
|
||||||
class WhoIsWatchingAdapter(
|
|
||||||
private val selectCallBack: (DataStoreHelper.Account) -> Unit = { },
|
|
||||||
private val editCallBack: (DataStoreHelper.Account) -> Unit = { },
|
|
||||||
private val addAccountCallback: () -> Unit = {}
|
|
||||||
) :
|
|
||||||
ListAdapter<DataStoreHelper.Account, WhoIsWatchingAdapter.WhoIsWatchingHolder>(DiffCallback()) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val FOOTER = 1
|
|
||||||
const val NORMAL = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
|
||||||
return currentList.size + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemViewType(position: Int): Int = when (position) {
|
|
||||||
currentList.size -> FOOTER
|
|
||||||
else -> NORMAL
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WhoIsWatchingHolder =
|
|
||||||
WhoIsWatchingHolder(
|
|
||||||
binding = when (viewType) {
|
|
||||||
NORMAL -> WhoIsWatchingAccountBinding.inflate(
|
|
||||||
LayoutInflater.from(parent.context),
|
|
||||||
parent,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
|
|
||||||
FOOTER -> WhoIsWatchingAccountAddBinding.inflate(
|
|
||||||
LayoutInflater.from(parent.context),
|
|
||||||
parent,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
|
|
||||||
else -> throw NotImplementedError()
|
|
||||||
},
|
|
||||||
selectCallBack = selectCallBack,
|
|
||||||
addAccountCallback = addAccountCallback,
|
|
||||||
editCallBack = editCallBack,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: WhoIsWatchingHolder, position: Int) =
|
|
||||||
holder.bind(currentList.getOrNull(position))
|
|
||||||
|
|
||||||
class WhoIsWatchingHolder(
|
|
||||||
val binding: ViewBinding,
|
|
||||||
val selectCallBack: (DataStoreHelper.Account) -> Unit,
|
|
||||||
val addAccountCallback: () -> Unit,
|
|
||||||
val editCallBack: (DataStoreHelper.Account) -> Unit
|
|
||||||
) :
|
|
||||||
RecyclerView.ViewHolder(binding.root) {
|
|
||||||
|
|
||||||
fun bind(card: DataStoreHelper.Account?) {
|
|
||||||
when (binding) {
|
|
||||||
is WhoIsWatchingAccountBinding -> binding.apply {
|
|
||||||
if(card == null) return@apply
|
|
||||||
outline.isVisible = card.keyIndex == DataStoreHelper.selectedKeyIndex
|
|
||||||
profileText.text = card.name
|
|
||||||
profileImageBackground.setImage(card.image)
|
|
||||||
root.setOnClickListener {
|
|
||||||
selectCallBack(card)
|
|
||||||
}
|
|
||||||
root.setOnLongClickListener {
|
|
||||||
editCallBack(card)
|
|
||||||
return@setOnLongClickListener true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is WhoIsWatchingAccountAddBinding -> binding.apply {
|
|
||||||
root.setOnClickListener {
|
|
||||||
addAccountCallback()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DiffCallback : DiffUtil.ItemCallback<DataStoreHelper.Account>() {
|
|
||||||
override fun areItemsTheSame(
|
|
||||||
oldItem: DataStoreHelper.Account,
|
|
||||||
newItem: DataStoreHelper.Account
|
|
||||||
): Boolean = oldItem.keyIndex == newItem.keyIndex
|
|
||||||
|
|
||||||
override fun areContentsTheSame(
|
|
||||||
oldItem: DataStoreHelper.Account,
|
|
||||||
newItem: DataStoreHelper.Account
|
|
||||||
): Boolean = oldItem == newItem
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
package com.lagradost.cloudstream3.ui.account
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.viewbinding.ViewBinding
|
||||||
|
import com.lagradost.cloudstream3.R
|
||||||
|
import com.lagradost.cloudstream3.databinding.AccountListItemAddBinding
|
||||||
|
import com.lagradost.cloudstream3.databinding.AccountListItemBinding
|
||||||
|
import com.lagradost.cloudstream3.databinding.AccountListItemEditBinding
|
||||||
|
import com.lagradost.cloudstream3.ui.account.AccountHelper.showAccountEditDialog
|
||||||
|
import com.lagradost.cloudstream3.ui.result.setImage
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||||
|
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||||
|
|
||||||
|
class AccountAdapter(
|
||||||
|
private val accounts: List<DataStoreHelper.Account>,
|
||||||
|
private val accountSelectCallback: (DataStoreHelper.Account) -> Unit,
|
||||||
|
private val accountCreateCallback: (DataStoreHelper.Account) -> Unit,
|
||||||
|
private val accountEditCallback: (DataStoreHelper.Account) -> Unit,
|
||||||
|
private val accountDeleteCallback: (DataStoreHelper.Account) -> Unit
|
||||||
|
) : RecyclerView.Adapter<AccountAdapter.AccountViewHolder>() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val VIEW_TYPE_SELECT_ACCOUNT = 0
|
||||||
|
const val VIEW_TYPE_ADD_ACCOUNT = 1
|
||||||
|
const val VIEW_TYPE_EDIT_ACCOUNT = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class AccountViewHolder(private val binding: ViewBinding) :
|
||||||
|
RecyclerView.ViewHolder(binding.root) {
|
||||||
|
|
||||||
|
fun bind(account: DataStoreHelper.Account?) {
|
||||||
|
when (binding) {
|
||||||
|
is AccountListItemBinding -> binding.apply {
|
||||||
|
if (account == null) return@apply
|
||||||
|
|
||||||
|
val isTv = isTvSettings() || !root.isInTouchMode
|
||||||
|
|
||||||
|
val isLastUsedAccount = account.keyIndex == DataStoreHelper.selectedKeyIndex
|
||||||
|
|
||||||
|
accountName.text = account.name
|
||||||
|
accountImage.setImage(account.image)
|
||||||
|
lockIcon.isVisible = account.lockPin != null
|
||||||
|
outline.isVisible = !isTv && isLastUsedAccount
|
||||||
|
|
||||||
|
if (isTv) {
|
||||||
|
// For emulator but this is fine on TV also
|
||||||
|
root.isFocusableInTouchMode = true
|
||||||
|
if (isLastUsedAccount) {
|
||||||
|
root.requestFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
root.foreground = ContextCompat.getDrawable(
|
||||||
|
root.context,
|
||||||
|
R.drawable.outline_drawable
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
root.setOnLongClickListener {
|
||||||
|
showAccountEditDialog(
|
||||||
|
context = root.context,
|
||||||
|
account = account,
|
||||||
|
isNewAccount = false,
|
||||||
|
accountEditCallback = { account -> accountEditCallback.invoke(account) },
|
||||||
|
accountDeleteCallback = { account -> accountDeleteCallback.invoke(account) }
|
||||||
|
)
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
root.setOnClickListener {
|
||||||
|
accountSelectCallback.invoke(account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is AccountListItemEditBinding -> binding.apply {
|
||||||
|
if (account == null) return@apply
|
||||||
|
|
||||||
|
val isTv = isTvSettings() || !root.isInTouchMode
|
||||||
|
|
||||||
|
val isLastUsedAccount = account.keyIndex == DataStoreHelper.selectedKeyIndex
|
||||||
|
|
||||||
|
accountName.text = account.name
|
||||||
|
accountImage.setImage(
|
||||||
|
account.image,
|
||||||
|
fadeIn = false,
|
||||||
|
radius = 10
|
||||||
|
)
|
||||||
|
lockIcon.isVisible = account.lockPin != null
|
||||||
|
outline.isVisible = !isTv && isLastUsedAccount
|
||||||
|
|
||||||
|
if (isTv) {
|
||||||
|
// For emulator but this is fine on TV also
|
||||||
|
root.isFocusableInTouchMode = true
|
||||||
|
if (isLastUsedAccount) {
|
||||||
|
root.requestFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
root.foreground = ContextCompat.getDrawable(
|
||||||
|
root.context,
|
||||||
|
R.drawable.outline_drawable
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
root.setOnClickListener {
|
||||||
|
showAccountEditDialog(
|
||||||
|
context = root.context,
|
||||||
|
account = account,
|
||||||
|
isNewAccount = false,
|
||||||
|
accountEditCallback = { account -> accountEditCallback.invoke(account) },
|
||||||
|
accountDeleteCallback = { account -> accountDeleteCallback.invoke(account) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is AccountListItemAddBinding -> binding.apply {
|
||||||
|
root.setOnClickListener {
|
||||||
|
val remainingImages =
|
||||||
|
DataStoreHelper.profileImages.toSet() - accounts.filter { it.customImage == null }
|
||||||
|
.mapNotNull { DataStoreHelper.profileImages.getOrNull(it.defaultImageIndex) }.toSet()
|
||||||
|
|
||||||
|
val image =
|
||||||
|
DataStoreHelper.profileImages.indexOf(remainingImages.randomOrNull() ?: DataStoreHelper.profileImages.random())
|
||||||
|
val keyIndex = (accounts.maxOfOrNull { it.keyIndex } ?: 0) + 1
|
||||||
|
|
||||||
|
val accountName = root.context.getString(R.string.account)
|
||||||
|
|
||||||
|
showAccountEditDialog(
|
||||||
|
root.context,
|
||||||
|
DataStoreHelper.Account(
|
||||||
|
keyIndex = keyIndex,
|
||||||
|
name = "$accountName $keyIndex",
|
||||||
|
customImage = null,
|
||||||
|
defaultImageIndex = image
|
||||||
|
),
|
||||||
|
isNewAccount = true,
|
||||||
|
accountEditCallback = { account -> accountCreateCallback.invoke(account) },
|
||||||
|
accountDeleteCallback = {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AccountViewHolder =
|
||||||
|
AccountViewHolder(
|
||||||
|
binding = when (viewType) {
|
||||||
|
VIEW_TYPE_SELECT_ACCOUNT -> {
|
||||||
|
AccountListItemBinding.inflate(
|
||||||
|
LayoutInflater.from(parent.context),
|
||||||
|
parent,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
VIEW_TYPE_ADD_ACCOUNT -> {
|
||||||
|
AccountListItemAddBinding.inflate(
|
||||||
|
LayoutInflater.from(parent.context),
|
||||||
|
parent,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
VIEW_TYPE_EDIT_ACCOUNT -> {
|
||||||
|
AccountListItemEditBinding.inflate(
|
||||||
|
LayoutInflater.from(parent.context),
|
||||||
|
parent,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> throw IllegalArgumentException("Invalid view type")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: AccountViewHolder, position: Int) {
|
||||||
|
holder.bind(accounts.getOrNull(position))
|
||||||
|
}
|
||||||
|
|
||||||
|
var viewType = 0
|
||||||
|
|
||||||
|
override fun getItemViewType(position: Int): Int {
|
||||||
|
if (viewType != 0 && position != accounts.count()) {
|
||||||
|
return viewType
|
||||||
|
}
|
||||||
|
|
||||||
|
return when (position) {
|
||||||
|
accounts.count() -> VIEW_TYPE_ADD_ACCOUNT
|
||||||
|
else -> VIEW_TYPE_SELECT_ACCOUNT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return accounts.count() + 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,356 @@
|
||||||
|
package com.lagradost.cloudstream3.ui.account
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.content.Intent
|
||||||
|
import android.text.Editable
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.inputmethod.EditorInfo
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.view.isGone
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.core.widget.doOnTextChanged
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity
|
||||||
|
import com.lagradost.cloudstream3.MainActivity
|
||||||
|
import com.lagradost.cloudstream3.R
|
||||||
|
import com.lagradost.cloudstream3.databinding.AccountEditDialogBinding
|
||||||
|
import com.lagradost.cloudstream3.databinding.AccountSelectLinearBinding
|
||||||
|
import com.lagradost.cloudstream3.databinding.LockPinDialogBinding
|
||||||
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
|
import com.lagradost.cloudstream3.mvvm.observe
|
||||||
|
import com.lagradost.cloudstream3.ui.result.setImage
|
||||||
|
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
||||||
|
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getDefaultAccount
|
||||||
|
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
||||||
|
import com.lagradost.cloudstream3.utils.UIHelper.showInputMethod
|
||||||
|
|
||||||
|
object AccountHelper {
|
||||||
|
fun showAccountEditDialog(
|
||||||
|
context: Context,
|
||||||
|
account: DataStoreHelper.Account,
|
||||||
|
isNewAccount: Boolean,
|
||||||
|
accountEditCallback: (DataStoreHelper.Account) -> Unit,
|
||||||
|
accountDeleteCallback: (DataStoreHelper.Account) -> Unit
|
||||||
|
) {
|
||||||
|
val binding = AccountEditDialogBinding.inflate(LayoutInflater.from(context), null, false)
|
||||||
|
val builder = AlertDialog.Builder(context, R.style.AlertDialogCustom)
|
||||||
|
.setView(binding.root)
|
||||||
|
|
||||||
|
var currentEditAccount = account
|
||||||
|
val dialog = builder.show()
|
||||||
|
|
||||||
|
if (!isNewAccount) binding.title.setText(R.string.edit_account)
|
||||||
|
|
||||||
|
// Set up the dialog content
|
||||||
|
binding.accountName.text = Editable.Factory.getInstance()?.newEditable(account.name)
|
||||||
|
binding.accountName.doOnTextChanged { text, _, _, _ ->
|
||||||
|
currentEditAccount = currentEditAccount.copy(name = text?.toString() ?: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.deleteBtt.isGone = isNewAccount
|
||||||
|
binding.deleteBtt.setOnClickListener {
|
||||||
|
val dialogClickListener = DialogInterface.OnClickListener { _, which ->
|
||||||
|
when (which) {
|
||||||
|
DialogInterface.BUTTON_POSITIVE -> {
|
||||||
|
accountDeleteCallback.invoke(account)
|
||||||
|
dialog?.dismissSafe()
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogInterface.BUTTON_NEGATIVE -> {
|
||||||
|
dialog?.dismissSafe()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
AlertDialog.Builder(context).setTitle(R.string.delete).setMessage(
|
||||||
|
context.getString(R.string.delete_message).format(
|
||||||
|
currentEditAccount.name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.setPositiveButton(R.string.delete, dialogClickListener)
|
||||||
|
.setNegativeButton(R.string.cancel, dialogClickListener)
|
||||||
|
.show().setDefaultFocus()
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
logError(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.cancelBtt.setOnClickListener {
|
||||||
|
dialog?.dismissSafe()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the profile picture and its interactions
|
||||||
|
binding.accountImage.setImage(account.image)
|
||||||
|
binding.accountImage.setOnClickListener {
|
||||||
|
// Roll the image forwards once
|
||||||
|
currentEditAccount =
|
||||||
|
currentEditAccount.copy(defaultImageIndex = (currentEditAccount.defaultImageIndex + 1) % DataStoreHelper.profileImages.size)
|
||||||
|
binding.accountImage.setImage(currentEditAccount.image)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle applying changes
|
||||||
|
binding.applyBtt.setOnClickListener {
|
||||||
|
if (currentEditAccount.lockPin != null) {
|
||||||
|
// Ask for the current PIN
|
||||||
|
showPinInputDialog(context, currentEditAccount.lockPin, false) { pin ->
|
||||||
|
if (pin == null) return@showPinInputDialog
|
||||||
|
// PIN is correct, proceed to update the account
|
||||||
|
accountEditCallback.invoke(currentEditAccount)
|
||||||
|
dialog.dismissSafe()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No lock PIN set, proceed to update the account
|
||||||
|
accountEditCallback.invoke(currentEditAccount)
|
||||||
|
dialog.dismissSafe()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle setting or changing the PIN
|
||||||
|
if (currentEditAccount.keyIndex == getDefaultAccount(context).keyIndex) {
|
||||||
|
binding.lockProfileCheckbox.isVisible = false
|
||||||
|
if (currentEditAccount.lockPin != null) {
|
||||||
|
currentEditAccount = currentEditAccount.copy(lockPin = null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var canSetPin = true
|
||||||
|
|
||||||
|
binding.lockProfileCheckbox.isChecked = currentEditAccount.lockPin != null
|
||||||
|
|
||||||
|
binding.lockProfileCheckbox.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
if (isChecked) {
|
||||||
|
if (canSetPin) {
|
||||||
|
showPinInputDialog(context, null, true) { pin ->
|
||||||
|
if (pin == null) {
|
||||||
|
binding.lockProfileCheckbox.isChecked = false
|
||||||
|
return@showPinInputDialog
|
||||||
|
}
|
||||||
|
|
||||||
|
currentEditAccount = currentEditAccount.copy(lockPin = pin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (currentEditAccount.lockPin != null) {
|
||||||
|
// Ask for the current PIN
|
||||||
|
showPinInputDialog(context, currentEditAccount.lockPin, true) { pin ->
|
||||||
|
if (pin == null || pin != currentEditAccount.lockPin) {
|
||||||
|
canSetPin = false
|
||||||
|
binding.lockProfileCheckbox.isChecked = true
|
||||||
|
} else {
|
||||||
|
currentEditAccount = currentEditAccount.copy(lockPin = null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
canSetPin = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showPinInputDialog(
|
||||||
|
context: Context,
|
||||||
|
currentPin: String?,
|
||||||
|
editAccount: Boolean,
|
||||||
|
forStartup: Boolean = false,
|
||||||
|
errorText: String? = null,
|
||||||
|
callback: (String?) -> Unit
|
||||||
|
) {
|
||||||
|
fun TextView.visibleWithText(@StringRes textRes: Int) {
|
||||||
|
isVisible = true
|
||||||
|
setText(textRes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun TextView.visibleWithText(text: String?) {
|
||||||
|
isVisible = true
|
||||||
|
setText(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
val binding = LockPinDialogBinding.inflate(LayoutInflater.from(context))
|
||||||
|
|
||||||
|
val isPinSet = currentPin != null
|
||||||
|
val isNewPin = editAccount && !isPinSet
|
||||||
|
val isEditPin = editAccount && isPinSet
|
||||||
|
|
||||||
|
val titleRes = if (isEditPin) R.string.enter_current_pin else R.string.enter_pin
|
||||||
|
|
||||||
|
var isPinValid = false
|
||||||
|
|
||||||
|
val builder = AlertDialog.Builder(context, R.style.AlertDialogCustom)
|
||||||
|
.setView(binding.root)
|
||||||
|
.setTitle(titleRes)
|
||||||
|
.setNegativeButton(R.string.cancel) { _, _ ->
|
||||||
|
callback.invoke(null)
|
||||||
|
}
|
||||||
|
.setOnCancelListener {
|
||||||
|
callback.invoke(null)
|
||||||
|
}
|
||||||
|
.setOnDismissListener {
|
||||||
|
if (!isPinValid) {
|
||||||
|
callback.invoke(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forStartup) {
|
||||||
|
val currentAccount = DataStoreHelper.accounts.firstOrNull {
|
||||||
|
it.keyIndex == DataStoreHelper.selectedKeyIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.setTitle(context.getString(R.string.enter_pin_with_name, currentAccount?.name))
|
||||||
|
builder.setOnDismissListener {
|
||||||
|
if (!isPinValid) {
|
||||||
|
context.getActivity()?.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// So that if they don't know the PIN for the current account,
|
||||||
|
// they don't get completely locked out
|
||||||
|
builder.setNeutralButton(R.string.use_default_account) { _, _ ->
|
||||||
|
val activity = context.getActivity()
|
||||||
|
if (activity is AccountSelectActivity) {
|
||||||
|
isPinValid = true
|
||||||
|
activity.viewModel.handleAccountSelect(getDefaultAccount(context), activity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNewPin) {
|
||||||
|
if (errorText != null) binding.pinEditTextError.visibleWithText(errorText)
|
||||||
|
builder.setPositiveButton(R.string.setup_done) { _, _ ->
|
||||||
|
if (!isPinValid) {
|
||||||
|
// If the done button is pressed and there is an error,
|
||||||
|
// ask again, and mention the error that caused this.
|
||||||
|
showPinInputDialog(
|
||||||
|
context = binding.root.context,
|
||||||
|
currentPin = null,
|
||||||
|
editAccount = true,
|
||||||
|
errorText = binding.pinEditTextError.text.toString(),
|
||||||
|
callback = callback
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
val enteredPin = binding.pinEditText.text.toString()
|
||||||
|
callback.invoke(enteredPin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val dialog = builder.create()
|
||||||
|
|
||||||
|
binding.pinEditText.doOnTextChanged { text, _, _, _ ->
|
||||||
|
val enteredPin = text.toString()
|
||||||
|
val isEnteredPinValid = enteredPin.length == 4
|
||||||
|
|
||||||
|
if (isEnteredPinValid) {
|
||||||
|
if (isPinSet) {
|
||||||
|
if (enteredPin != currentPin) {
|
||||||
|
binding.pinEditTextError.visibleWithText(R.string.pin_error_incorrect)
|
||||||
|
binding.pinEditText.text = null
|
||||||
|
isPinValid = false
|
||||||
|
} else {
|
||||||
|
binding.pinEditTextError.isVisible = false
|
||||||
|
isPinValid = true
|
||||||
|
|
||||||
|
callback.invoke(enteredPin)
|
||||||
|
dialog.dismissSafe()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
binding.pinEditTextError.isVisible = false
|
||||||
|
isPinValid = true
|
||||||
|
}
|
||||||
|
} else if (isNewPin) {
|
||||||
|
binding.pinEditTextError.visibleWithText(R.string.pin_error_length)
|
||||||
|
isPinValid = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect IME_ACTION_DONE
|
||||||
|
binding.pinEditText.setOnEditorActionListener { _, actionId, _ ->
|
||||||
|
if (actionId == EditorInfo.IME_ACTION_DONE && isPinValid) {
|
||||||
|
val enteredPin = binding.pinEditText.text.toString()
|
||||||
|
callback.invoke(enteredPin)
|
||||||
|
dialog.dismissSafe()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't want to accidentally have the dialog dismiss when clicking outside of it.
|
||||||
|
// That is what the cancel button is for.
|
||||||
|
dialog.setCanceledOnTouchOutside(false)
|
||||||
|
|
||||||
|
dialog.show()
|
||||||
|
|
||||||
|
// Auto focus on PIN input and show keyboard
|
||||||
|
binding.pinEditText.requestFocus()
|
||||||
|
binding.pinEditText.postDelayed({
|
||||||
|
showInputMethod(binding.pinEditText)
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Activity?.showAccountSelectLinear() {
|
||||||
|
val activity = this as? MainActivity ?: return
|
||||||
|
val viewModel = ViewModelProvider(activity)[AccountViewModel::class.java]
|
||||||
|
|
||||||
|
val binding: AccountSelectLinearBinding = AccountSelectLinearBinding.inflate(
|
||||||
|
LayoutInflater.from(activity)
|
||||||
|
)
|
||||||
|
|
||||||
|
val builder = BottomSheetDialog(activity)
|
||||||
|
builder.setContentView(binding.root)
|
||||||
|
builder.show()
|
||||||
|
|
||||||
|
binding.manageAccountsButton.setOnClickListener {
|
||||||
|
val accountSelectIntent = Intent(activity, AccountSelectActivity::class.java)
|
||||||
|
accountSelectIntent.putExtra("isEditingFromMainActivity", true)
|
||||||
|
activity.startActivity(accountSelectIntent)
|
||||||
|
builder.dismissSafe()
|
||||||
|
}
|
||||||
|
|
||||||
|
val recyclerView: RecyclerView = binding.accountRecyclerView
|
||||||
|
|
||||||
|
val itemSize = recyclerView.resources.getDimensionPixelSize(
|
||||||
|
R.dimen.account_select_linear_item_size
|
||||||
|
)
|
||||||
|
|
||||||
|
recyclerView.addItemDecoration(AccountSelectLinearItemDecoration(itemSize))
|
||||||
|
|
||||||
|
recyclerView.setLinearListLayout(isHorizontal = true)
|
||||||
|
|
||||||
|
val currentAccount = DataStoreHelper.accounts.firstOrNull {
|
||||||
|
it.keyIndex == DataStoreHelper.selectedKeyIndex
|
||||||
|
} ?: getDefaultAccount(activity)
|
||||||
|
|
||||||
|
// We want to make sure the accounts are up-to-date
|
||||||
|
viewModel.handleAccountSelect(
|
||||||
|
currentAccount,
|
||||||
|
activity,
|
||||||
|
reloadForActivity = true
|
||||||
|
)
|
||||||
|
|
||||||
|
activity.observe(viewModel.accounts) { liveAccounts ->
|
||||||
|
recyclerView.adapter = AccountAdapter(
|
||||||
|
liveAccounts,
|
||||||
|
accountSelectCallback = { account ->
|
||||||
|
viewModel.handleAccountSelect(account, activity)
|
||||||
|
builder.dismissSafe()
|
||||||
|
},
|
||||||
|
accountCreateCallback = { viewModel.handleAccountUpdate(it, activity) },
|
||||||
|
accountEditCallback = { viewModel.handleAccountUpdate(it, activity) },
|
||||||
|
accountDeleteCallback = { viewModel.handleAccountDelete(it, activity) }
|
||||||
|
)
|
||||||
|
|
||||||
|
activity.observe(viewModel.selectedKeyIndex) { selectedKeyIndex ->
|
||||||
|
// Scroll to current account (which is focused by default)
|
||||||
|
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
|
||||||
|
layoutManager.scrollToPositionWithOffset(selectedKeyIndex, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,168 @@
|
||||||
|
package com.lagradost.cloudstream3.ui.account
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import com.lagradost.cloudstream3.CommonActivity
|
||||||
|
import com.lagradost.cloudstream3.CommonActivity.loadThemes
|
||||||
|
import com.lagradost.cloudstream3.CommonActivity.showToast
|
||||||
|
import com.lagradost.cloudstream3.MainActivity
|
||||||
|
import com.lagradost.cloudstream3.R
|
||||||
|
import com.lagradost.cloudstream3.databinding.ActivityAccountSelectBinding
|
||||||
|
import com.lagradost.cloudstream3.mvvm.observe
|
||||||
|
import com.lagradost.cloudstream3.ui.AutofitRecyclerView
|
||||||
|
import com.lagradost.cloudstream3.ui.account.AccountAdapter.Companion.VIEW_TYPE_EDIT_ACCOUNT
|
||||||
|
import com.lagradost.cloudstream3.ui.account.AccountAdapter.Companion.VIEW_TYPE_SELECT_ACCOUNT
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.accounts
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.selectedKeyIndex
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.setAccount
|
||||||
|
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
||||||
|
|
||||||
|
class AccountSelectActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
lateinit var viewModel: AccountViewModel
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
loadThemes(this)
|
||||||
|
|
||||||
|
window.navigationBarColor = colorFromAttribute(R.attr.primaryBlackBackground)
|
||||||
|
|
||||||
|
// Are we editing and coming from MainActivity?
|
||||||
|
val isEditingFromMainActivity = intent.getBooleanExtra(
|
||||||
|
"isEditingFromMainActivity",
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
val skipStartup = settingsManager.getBoolean(
|
||||||
|
getString(R.string.skip_startup_account_select_key),
|
||||||
|
false
|
||||||
|
) || accounts.count() <= 1
|
||||||
|
|
||||||
|
viewModel = ViewModelProvider(this)[AccountViewModel::class.java]
|
||||||
|
|
||||||
|
// Don't show account selection if there is only
|
||||||
|
// one account that exists
|
||||||
|
if (!isEditingFromMainActivity && skipStartup) {
|
||||||
|
val currentAccount = accounts.firstOrNull { it.keyIndex == selectedKeyIndex }
|
||||||
|
if (currentAccount?.lockPin != null) {
|
||||||
|
CommonActivity.init(this)
|
||||||
|
viewModel.handleAccountSelect(currentAccount, this, true)
|
||||||
|
observe(viewModel.isAllowedLogin) { isAllowedLogin ->
|
||||||
|
if (isAllowedLogin) {
|
||||||
|
// We are allowed to continue to MainActivity
|
||||||
|
navigateToMainActivity()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (accounts.count() > 1) {
|
||||||
|
showToast(this, getString(
|
||||||
|
R.string.logged_account,
|
||||||
|
currentAccount?.name
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateToMainActivity()
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
CommonActivity.init(this)
|
||||||
|
|
||||||
|
val binding = ActivityAccountSelectBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
val recyclerView: AutofitRecyclerView = binding.accountRecyclerView
|
||||||
|
|
||||||
|
observe(viewModel.accounts) { liveAccounts ->
|
||||||
|
val adapter = AccountAdapter(
|
||||||
|
liveAccounts,
|
||||||
|
// Handle the selected account
|
||||||
|
accountSelectCallback = {
|
||||||
|
viewModel.handleAccountSelect(it, this)
|
||||||
|
observe(viewModel.isAllowedLogin) { isAllowedLogin ->
|
||||||
|
if (isAllowedLogin) {
|
||||||
|
// We are allowed to continue to MainActivity
|
||||||
|
navigateToMainActivity()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
accountCreateCallback = { viewModel.handleAccountUpdate(it, this) },
|
||||||
|
accountEditCallback = {
|
||||||
|
viewModel.handleAccountUpdate(it, this)
|
||||||
|
|
||||||
|
// We came from MainActivity, return there
|
||||||
|
// and switch to the edited account
|
||||||
|
if (isEditingFromMainActivity) {
|
||||||
|
setAccount(it)
|
||||||
|
navigateToMainActivity()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
accountDeleteCallback = { viewModel.handleAccountDelete(it,this) }
|
||||||
|
)
|
||||||
|
|
||||||
|
recyclerView.adapter = adapter
|
||||||
|
|
||||||
|
if (isTvSettings()) {
|
||||||
|
binding.editAccountButton.setBackgroundResource(
|
||||||
|
R.drawable.player_button_tv_attr_no_bg
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
observe(viewModel.selectedKeyIndex) { selectedKeyIndex ->
|
||||||
|
// Scroll to current account (which is focused by default)
|
||||||
|
val layoutManager = recyclerView.layoutManager as GridLayoutManager
|
||||||
|
layoutManager.scrollToPositionWithOffset(selectedKeyIndex, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
observe(viewModel.isEditing) { isEditing ->
|
||||||
|
if (isEditing) {
|
||||||
|
binding.editAccountButton.setImageResource(R.drawable.ic_baseline_close_24)
|
||||||
|
binding.title.setText(R.string.manage_accounts)
|
||||||
|
adapter.viewType = VIEW_TYPE_EDIT_ACCOUNT
|
||||||
|
} else {
|
||||||
|
binding.editAccountButton.setImageResource(R.drawable.ic_baseline_edit_24)
|
||||||
|
binding.title.setText(R.string.select_an_account)
|
||||||
|
adapter.viewType = VIEW_TYPE_SELECT_ACCOUNT
|
||||||
|
}
|
||||||
|
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEditingFromMainActivity) {
|
||||||
|
viewModel.setIsEditing(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.editAccountButton.setOnClickListener {
|
||||||
|
// We came from MainActivity, return there
|
||||||
|
// and resume its state
|
||||||
|
if (isEditingFromMainActivity) {
|
||||||
|
navigateToMainActivity()
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.toggleIsEditing()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTvSettings()) {
|
||||||
|
recyclerView.spanCount = if (liveAccounts.count() + 1 <= 6) {
|
||||||
|
liveAccounts.count() + 1
|
||||||
|
} else 6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun navigateToMainActivity() {
|
||||||
|
val mainIntent = Intent(this, MainActivity::class.java)
|
||||||
|
startActivity(mainIntent)
|
||||||
|
finish() // Finish the account selection activity
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.lagradost.cloudstream3.ui.account
|
||||||
|
|
||||||
|
import android.graphics.Rect
|
||||||
|
import android.view.View
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
|
||||||
|
class AccountSelectLinearItemDecoration(private val size: Int) : RecyclerView.ItemDecoration() {
|
||||||
|
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
|
||||||
|
val layoutParams = view.layoutParams as RecyclerView.LayoutParams
|
||||||
|
layoutParams.width = size
|
||||||
|
layoutParams.height = size
|
||||||
|
view.layoutParams = layoutParams
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
package com.lagradost.cloudstream3.ui.account
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.context
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys
|
||||||
|
import com.lagradost.cloudstream3.ui.account.AccountHelper.showPinInputDialog
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAccounts
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getDefaultAccount
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.setAccount
|
||||||
|
|
||||||
|
class AccountViewModel : ViewModel() {
|
||||||
|
private fun getAllAccounts(): List<DataStoreHelper.Account> {
|
||||||
|
return context?.let { getAccounts(it) } ?: DataStoreHelper.accounts.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val _accounts: MutableLiveData<List<DataStoreHelper.Account>> = MutableLiveData(getAllAccounts())
|
||||||
|
val accounts: LiveData<List<DataStoreHelper.Account>> = _accounts
|
||||||
|
|
||||||
|
private val _isEditing = MutableLiveData(false)
|
||||||
|
val isEditing: LiveData<Boolean> = _isEditing
|
||||||
|
|
||||||
|
private val _isAllowedLogin = MutableLiveData(false)
|
||||||
|
val isAllowedLogin: LiveData<Boolean> = _isAllowedLogin
|
||||||
|
|
||||||
|
private val _selectedKeyIndex = MutableLiveData(
|
||||||
|
getAllAccounts().indexOfFirst {
|
||||||
|
it.keyIndex == DataStoreHelper.selectedKeyIndex
|
||||||
|
}
|
||||||
|
)
|
||||||
|
val selectedKeyIndex: LiveData<Int> = _selectedKeyIndex
|
||||||
|
|
||||||
|
fun setIsEditing(value: Boolean) {
|
||||||
|
_isEditing.postValue(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toggleIsEditing() {
|
||||||
|
_isEditing.postValue(!(_isEditing.value ?: false))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleAccountUpdate(
|
||||||
|
account: DataStoreHelper.Account,
|
||||||
|
context: Context
|
||||||
|
) {
|
||||||
|
val currentAccounts = getAccounts(context).toMutableList()
|
||||||
|
|
||||||
|
val overrideIndex = currentAccounts.indexOfFirst { it.keyIndex == account.keyIndex }
|
||||||
|
|
||||||
|
if (overrideIndex != -1) {
|
||||||
|
currentAccounts[overrideIndex] = account
|
||||||
|
} else currentAccounts.add(account)
|
||||||
|
|
||||||
|
val currentHomePage = DataStoreHelper.currentHomePage
|
||||||
|
|
||||||
|
setAccount(account)
|
||||||
|
|
||||||
|
DataStoreHelper.currentHomePage = currentHomePage
|
||||||
|
DataStoreHelper.accounts = currentAccounts.toTypedArray()
|
||||||
|
|
||||||
|
_accounts.postValue(getAccounts(context))
|
||||||
|
_selectedKeyIndex.postValue(getAccounts(context).indexOf(account))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleAccountDelete(
|
||||||
|
account: DataStoreHelper.Account,
|
||||||
|
context: Context
|
||||||
|
) {
|
||||||
|
removeKeys(account.keyIndex.toString())
|
||||||
|
|
||||||
|
val currentAccounts = getAccounts(context).toMutableList()
|
||||||
|
|
||||||
|
currentAccounts.removeIf { it.keyIndex == account.keyIndex }
|
||||||
|
|
||||||
|
DataStoreHelper.accounts = currentAccounts.toTypedArray()
|
||||||
|
|
||||||
|
if (account.keyIndex == DataStoreHelper.selectedKeyIndex) {
|
||||||
|
setAccount(getDefaultAccount(context))
|
||||||
|
}
|
||||||
|
|
||||||
|
_accounts.postValue(getAccounts(context))
|
||||||
|
_selectedKeyIndex.postValue(getAllAccounts().indexOfFirst {
|
||||||
|
it.keyIndex == DataStoreHelper.selectedKeyIndex
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleAccountSelect(
|
||||||
|
account: DataStoreHelper.Account,
|
||||||
|
context: Context,
|
||||||
|
forStartup: Boolean = false,
|
||||||
|
reloadForActivity: Boolean = false
|
||||||
|
) {
|
||||||
|
if (reloadForActivity) {
|
||||||
|
_accounts.postValue(getAccounts(context))
|
||||||
|
_selectedKeyIndex.postValue(getAccounts(context).indexOf(account))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the selected account has a lock PIN set
|
||||||
|
if (account.lockPin != null) {
|
||||||
|
// The selected account has a PIN set, prompt the user to enter the PIN
|
||||||
|
showPinInputDialog(
|
||||||
|
context,
|
||||||
|
account.lockPin,
|
||||||
|
false,
|
||||||
|
forStartup
|
||||||
|
) { pin ->
|
||||||
|
if (pin == null) return@showPinInputDialog
|
||||||
|
// Pin is correct, proceed
|
||||||
|
_isAllowedLogin.postValue(true)
|
||||||
|
_selectedKeyIndex.postValue(getAccounts(context).indexOf(account))
|
||||||
|
setAccount(account)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No PIN set for the selected account, proceed
|
||||||
|
_isAllowedLogin.postValue(true)
|
||||||
|
_selectedKeyIndex.postValue(getAccounts(context).indexOf(account))
|
||||||
|
setAccount(account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -60,7 +60,7 @@ class DownloadChildFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}.sortedBy { it.data.episode + (it.data.season ?: 0) * 100000 }
|
}.sortedBy { it.data.episode + (it.data.season ?: 0) * 100000 }
|
||||||
if (eps.isEmpty()) {
|
if (eps.isEmpty()) {
|
||||||
activity?.onBackPressed()
|
activity?.onBackPressedDispatcher?.onBackPressed()
|
||||||
return@main
|
return@main
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ class DownloadChildFragment : Fragment() {
|
||||||
val folder = arguments?.getString("folder")
|
val folder = arguments?.getString("folder")
|
||||||
val name = arguments?.getString("name")
|
val name = arguments?.getString("name")
|
||||||
if (folder == null) {
|
if (folder == null) {
|
||||||
activity?.onBackPressed() // TODO FIX
|
activity?.onBackPressedDispatcher?.onBackPressed() // TODO FIX
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fixPaddingStatusbar(binding?.downloadChildRoot)
|
fixPaddingStatusbar(binding?.downloadChildRoot)
|
||||||
|
@ -87,7 +87,7 @@ class DownloadChildFragment : Fragment() {
|
||||||
title = name
|
title = name
|
||||||
setNavigationIcon(R.drawable.ic_baseline_arrow_back_24)
|
setNavigationIcon(R.drawable.ic_baseline_arrow_back_24)
|
||||||
setNavigationOnClickListener {
|
setNavigationOnClickListener {
|
||||||
activity?.onBackPressed()
|
activity?.onBackPressedDispatcher?.onBackPressed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ import com.lagradost.cloudstream3.mvvm.observe
|
||||||
import com.lagradost.cloudstream3.mvvm.observeNullable
|
import com.lagradost.cloudstream3.mvvm.observeNullable
|
||||||
import com.lagradost.cloudstream3.ui.APIRepository.Companion.noneApi
|
import com.lagradost.cloudstream3.ui.APIRepository.Companion.noneApi
|
||||||
import com.lagradost.cloudstream3.ui.APIRepository.Companion.randomApi
|
import com.lagradost.cloudstream3.ui.APIRepository.Companion.randomApi
|
||||||
|
import com.lagradost.cloudstream3.ui.account.AccountHelper.showAccountSelectLinear
|
||||||
import com.lagradost.cloudstream3.ui.result.txt
|
import com.lagradost.cloudstream3.ui.result.txt
|
||||||
import com.lagradost.cloudstream3.ui.search.*
|
import com.lagradost.cloudstream3.ui.search.*
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchHelper.handleSearchClickCallback
|
import com.lagradost.cloudstream3.ui.search.SearchHelper.handleSearchClickCallback
|
||||||
|
@ -495,9 +496,10 @@ class HomeFragment : Fragment() {
|
||||||
//homeChangeApiLoading.setOnClickListener(apiChangeClickListener)
|
//homeChangeApiLoading.setOnClickListener(apiChangeClickListener)
|
||||||
homeApiFab.setOnClickListener(apiChangeClickListener)
|
homeApiFab.setOnClickListener(apiChangeClickListener)
|
||||||
homeChangeApi.setOnClickListener(apiChangeClickListener)
|
homeChangeApi.setOnClickListener(apiChangeClickListener)
|
||||||
homeSwitchAccount.setOnClickListener { v ->
|
homeSwitchAccount.setOnClickListener {
|
||||||
DataStoreHelper.showWhoIsWatching(v?.context ?: return@setOnClickListener)
|
activity?.showAccountSelectLinear()
|
||||||
}
|
}
|
||||||
|
|
||||||
homeRandom.setOnClickListener {
|
homeRandom.setOnClickListener {
|
||||||
if (listHomepageItems.isNotEmpty()) {
|
if (listHomepageItems.isNotEmpty()) {
|
||||||
activity.loadSearchResult(listHomepageItems.random())
|
activity.loadSearchResult(listHomepageItems.random())
|
||||||
|
|
|
@ -15,7 +15,8 @@ import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
|
||||||
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
|
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchFragment.Companion.filterSearchResponse
|
import com.lagradost.cloudstream3.ui.search.SearchFragment.Companion.filterSearchResponse
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.isRecyclerScrollable
|
import com.lagradost.cloudstream3.utils.AppUtils.isRecyclerScrollable
|
||||||
|
|
||||||
class LoadClickCallback(
|
class LoadClickCallback(
|
||||||
|
@ -34,11 +35,13 @@ open class ParentItemAdapter(
|
||||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
|
|
||||||
val root = LayoutInflater.from(parent.context).inflate(
|
val layoutResId = when {
|
||||||
if (isTvSettings()) R.layout.homepage_parent_tv else R.layout.homepage_parent,
|
isTrueTvSettings() -> R.layout.homepage_parent_tv
|
||||||
parent,
|
parent.context.isEmulatorSettings() -> R.layout.homepage_parent_emulator
|
||||||
false
|
else -> R.layout.homepage_parent
|
||||||
)
|
}
|
||||||
|
|
||||||
|
val root = LayoutInflater.from(parent.context).inflate(layoutResId, parent, false)
|
||||||
|
|
||||||
val binding = HomepageParentBinding.bind(root)
|
val binding = HomepageParentBinding.bind(root)
|
||||||
|
|
||||||
|
@ -234,7 +237,7 @@ open class ParentItemAdapter(
|
||||||
})
|
})
|
||||||
|
|
||||||
//(recyclerView.adapter as HomeChildItemAdapter).notifyDataSetChanged()
|
//(recyclerView.adapter as HomeChildItemAdapter).notifyDataSetChanged()
|
||||||
if (!isTvSettings()) {
|
if (!isTrueTvSettings()) {
|
||||||
title.setOnClickListener {
|
title.setOnClickListener {
|
||||||
moreInfoClickCallback.invoke(expand)
|
moreInfoClickCallback.invoke(expand)
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import com.lagradost.cloudstream3.mvvm.Resource
|
||||||
import com.lagradost.cloudstream3.mvvm.debugException
|
import com.lagradost.cloudstream3.mvvm.debugException
|
||||||
import com.lagradost.cloudstream3.mvvm.observe
|
import com.lagradost.cloudstream3.mvvm.observe
|
||||||
import com.lagradost.cloudstream3.ui.WatchType
|
import com.lagradost.cloudstream3.ui.WatchType
|
||||||
|
import com.lagradost.cloudstream3.ui.account.AccountHelper.showAccountSelectLinear
|
||||||
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.selectHomepage
|
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.selectHomepage
|
||||||
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
|
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
|
||||||
import com.lagradost.cloudstream3.ui.result.ResultViewModel2
|
import com.lagradost.cloudstream3.ui.result.ResultViewModel2
|
||||||
|
@ -35,6 +36,7 @@ import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
||||||
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_LOAD
|
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_LOAD
|
||||||
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_SHOW_METADATA
|
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_SHOW_METADATA
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
|
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
||||||
|
@ -81,6 +83,28 @@ class HomeParentItemAdapterPreview(
|
||||||
parent,
|
parent,
|
||||||
false
|
false
|
||||||
) else FragmentHomeHeadBinding.inflate(inflater, parent, false)
|
) else FragmentHomeHeadBinding.inflate(inflater, parent, false)
|
||||||
|
|
||||||
|
if (binding is FragmentHomeHeadTvBinding && parent.context.isEmulatorSettings()) {
|
||||||
|
binding.homeBookmarkParentItemMoreInfo.isVisible = true
|
||||||
|
|
||||||
|
val marginInDp = 50
|
||||||
|
val density = binding.horizontalScrollChips.context.resources.displayMetrics.density
|
||||||
|
val marginInPixels = (marginInDp * density).toInt()
|
||||||
|
|
||||||
|
val params = binding.horizontalScrollChips.layoutParams as ViewGroup.MarginLayoutParams
|
||||||
|
params.marginEnd = marginInPixels
|
||||||
|
binding.horizontalScrollChips.layoutParams = params
|
||||||
|
binding.homeWatchParentItemTitle.setCompoundDrawablesWithIntrinsicBounds(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
ContextCompat.getDrawable(
|
||||||
|
parent.context,
|
||||||
|
R.drawable.ic_baseline_arrow_forward_24
|
||||||
|
),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
HeaderViewHolder(
|
HeaderViewHolder(
|
||||||
binding,
|
binding,
|
||||||
viewModel,
|
viewModel,
|
||||||
|
@ -355,21 +379,25 @@ class HomeParentItemAdapterPreview(
|
||||||
showApply = false,
|
showApply = false,
|
||||||
{}) {
|
{}) {
|
||||||
val newValue = WatchType.values()[it]
|
val newValue = WatchType.values()[it]
|
||||||
homePreviewBookmark.setCompoundDrawablesWithIntrinsicBounds(
|
|
||||||
null,
|
|
||||||
ContextCompat.getDrawable(
|
|
||||||
homePreviewBookmark.context,
|
|
||||||
newValue.iconRes
|
|
||||||
),
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
homePreviewBookmark.setText(newValue.stringRes)
|
|
||||||
|
|
||||||
ResultViewModel2.updateWatchStatus(
|
ResultViewModel2().updateWatchStatus(
|
||||||
item,
|
newValue,
|
||||||
newValue
|
fab.context,
|
||||||
)
|
item
|
||||||
|
) { statusChanged: Boolean ->
|
||||||
|
if (!statusChanged) return@updateWatchStatus
|
||||||
|
|
||||||
|
homePreviewBookmark.setCompoundDrawablesWithIntrinsicBounds(
|
||||||
|
null,
|
||||||
|
ContextCompat.getDrawable(
|
||||||
|
homePreviewBookmark.context,
|
||||||
|
newValue.iconRes
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
homePreviewBookmark.setText(newValue.stringRes)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -450,8 +478,8 @@ class HomeParentItemAdapterPreview(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
homeAccount?.setOnClickListener { v ->
|
homeAccount?.setOnClickListener {
|
||||||
DataStoreHelper.showWhoIsWatching(v?.context ?: return@setOnClickListener)
|
activity?.showAccountSelectLinear()
|
||||||
}
|
}
|
||||||
|
|
||||||
(binding as? FragmentHomeHeadTvBinding)?.apply {
|
(binding as? FragmentHomeHeadTvBinding)?.apply {
|
||||||
|
@ -553,12 +581,19 @@ class HomeParentItemAdapterPreview(
|
||||||
resumeHolder.isVisible = resumeWatching.isNotEmpty()
|
resumeHolder.isVisible = resumeWatching.isNotEmpty()
|
||||||
resumeAdapter.updateList(resumeWatching)
|
resumeAdapter.updateList(resumeWatching)
|
||||||
|
|
||||||
if (binding is FragmentHomeHeadBinding) {
|
if (
|
||||||
binding.homeWatchParentItemTitle.setOnClickListener {
|
binding is FragmentHomeHeadBinding ||
|
||||||
|
binding is FragmentHomeHeadTvBinding &&
|
||||||
|
binding.root.context.isEmulatorSettings()
|
||||||
|
) {
|
||||||
|
val title = (binding as? FragmentHomeHeadBinding)?.homeWatchParentItemTitle
|
||||||
|
?: (binding as? FragmentHomeHeadTvBinding)?.homeWatchParentItemTitle
|
||||||
|
|
||||||
|
title?.setOnClickListener {
|
||||||
viewModel.popup(
|
viewModel.popup(
|
||||||
HomeViewModel.ExpandableHomepageList(
|
HomeViewModel.ExpandableHomepageList(
|
||||||
HomePageList(
|
HomePageList(
|
||||||
binding.homeWatchParentItemTitle.text.toString(),
|
title.text.toString(),
|
||||||
resumeWatching,
|
resumeWatching,
|
||||||
false
|
false
|
||||||
), 1, false
|
), 1, false
|
||||||
|
@ -576,8 +611,15 @@ class HomeParentItemAdapterPreview(
|
||||||
bookmarkHolder.isVisible = visible
|
bookmarkHolder.isVisible = visible
|
||||||
bookmarkAdapter.updateList(list)
|
bookmarkAdapter.updateList(list)
|
||||||
|
|
||||||
if (binding is FragmentHomeHeadBinding) {
|
if (
|
||||||
binding.homeBookmarkParentItemTitle.setOnClickListener {
|
binding is FragmentHomeHeadBinding ||
|
||||||
|
binding is FragmentHomeHeadTvBinding &&
|
||||||
|
binding.root.context.isEmulatorSettings()
|
||||||
|
) {
|
||||||
|
val title = (binding as? FragmentHomeHeadBinding)?.homeBookmarkParentItemTitle
|
||||||
|
?: (binding as? FragmentHomeHeadTvBinding)?.homeBookmarkParentItemTitle
|
||||||
|
|
||||||
|
title?.setOnClickListener {
|
||||||
val items = toggleList.map { it.first }.filter { it.isChecked }
|
val items = toggleList.map { it.first }.filter { it.isChecked }
|
||||||
if (items.isEmpty()) return@setOnClickListener // we don't want to show an empty dialog
|
if (items.isEmpty()) return@setOnClickListener // we don't want to show an empty dialog
|
||||||
val textSum = items
|
val textSum = items
|
||||||
|
|
|
@ -40,7 +40,6 @@ import com.lagradost.cloudstream3.utils.AppUtils.loadResult
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||||
import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE
|
import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.deleteAllBookmarkedData
|
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.deleteAllResumeStateIds
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.deleteAllResumeStateIds
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllResumeStateIds
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllResumeStateIds
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllWatchStateIds
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllWatchStateIds
|
||||||
|
@ -102,11 +101,6 @@ class HomeViewModel : ViewModel() {
|
||||||
loadStoredData()
|
loadStoredData()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteBookmarks() {
|
|
||||||
deleteAllBookmarkedData()
|
|
||||||
loadStoredData()
|
|
||||||
}
|
|
||||||
|
|
||||||
var repo: APIRepository? = null
|
var repo: APIRepository? = null
|
||||||
|
|
||||||
private val _apiName = MutableLiveData<String>()
|
private val _apiName = MutableLiveData<String>()
|
||||||
|
|
|
@ -8,12 +8,14 @@ import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.util.TypedValue
|
||||||
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 android.view.ViewGroup.FOCUS_AFTER_DESCENDANTS
|
import android.view.ViewGroup.FOCUS_AFTER_DESCENDANTS
|
||||||
import android.view.ViewGroup.FOCUS_BLOCK_DESCENDANTS
|
import android.view.ViewGroup.FOCUS_BLOCK_DESCENDANTS
|
||||||
import android.view.animation.AlphaAnimation
|
import android.view.animation.AlphaAnimation
|
||||||
|
import android.widget.ImageView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
@ -25,6 +27,7 @@ import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.viewpager2.widget.ViewPager2
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
import com.google.android.material.tabs.TabLayout
|
import com.google.android.material.tabs.TabLayout
|
||||||
import com.google.android.material.tabs.TabLayoutMediator
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
import com.lagradost.cloudstream3.APIHolder
|
import com.lagradost.cloudstream3.APIHolder
|
||||||
|
@ -60,7 +63,7 @@ const val LIBRARY_FOLDER = "library_folder"
|
||||||
|
|
||||||
|
|
||||||
enum class LibraryOpenerType(@StringRes val stringRes: Int) {
|
enum class LibraryOpenerType(@StringRes val stringRes: Int) {
|
||||||
Default(R.string.default_subtitles), // TODO FIX AFTER MERGE
|
Default(R.string.action_default),
|
||||||
Provider(R.string.none),
|
Provider(R.string.none),
|
||||||
Browser(R.string.browser),
|
Browser(R.string.browser),
|
||||||
Search(R.string.search),
|
Search(R.string.search),
|
||||||
|
@ -135,8 +138,19 @@ class LibraryFragment : Fragment() {
|
||||||
|
|
||||||
binding?.libraryRoot?.findViewById<TextView>(R.id.search_src_text)?.apply {
|
binding?.libraryRoot?.findViewById<TextView>(R.id.search_src_text)?.apply {
|
||||||
tag = "tv_no_focus_tag"
|
tag = "tv_no_focus_tag"
|
||||||
|
//Expand the Appbar when search bar is focused, fixing scroll up issue
|
||||||
|
setOnFocusChangeListener { _, _ ->
|
||||||
|
binding?.searchBar?.setExpanded(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the color for the search exit icon to the correct theme text color
|
||||||
|
val searchExitIcon = binding?.mainSearch?.findViewById<ImageView>(androidx.appcompat.R.id.search_close_btn)
|
||||||
|
val searchExitIconColor = TypedValue()
|
||||||
|
|
||||||
|
activity?.theme?.resolveAttribute(android.R.attr.textColor, searchExitIconColor, true)
|
||||||
|
searchExitIcon?.setColorFilter(searchExitIconColor.data)
|
||||||
|
|
||||||
binding?.mainSearch?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
binding?.mainSearch?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||||
override fun onQueryTextSubmit(query: String?): Boolean {
|
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||||
libraryViewModel.sort(ListSorting.Query, query)
|
libraryViewModel.sort(ListSorting.Query, query)
|
||||||
|
@ -333,6 +347,7 @@ class LibraryFragment : Fragment() {
|
||||||
binding?.apply {
|
binding?.apply {
|
||||||
viewpager.offscreenPageLimit = 2
|
viewpager.offscreenPageLimit = 2
|
||||||
viewpager.reduceDragSensitivity()
|
viewpager.reduceDragSensitivity()
|
||||||
|
searchBar.setExpanded(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
val startLoading = Runnable {
|
val startLoading = Runnable {
|
||||||
|
@ -432,6 +447,10 @@ class LibraryFragment : Fragment() {
|
||||||
val distance = abs(position - currentItem)
|
val distance = abs(position - currentItem)
|
||||||
hideViewpager(distance)
|
hideViewpager(distance)
|
||||||
}
|
}
|
||||||
|
//Expand the appBar on tab focus
|
||||||
|
tab.view.setOnFocusChangeListener { view, b ->
|
||||||
|
binding?.searchBar?.setExpanded(true)
|
||||||
|
}
|
||||||
}.attach()
|
}.attach()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,11 +120,11 @@ class LibraryViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
MainActivity.reloadHomeEvent += ::reloadPages
|
MainActivity.reloadLibraryEvent += ::reloadPages
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
MainActivity.reloadHomeEvent -= ::reloadPages
|
MainActivity.reloadLibraryEvent -= ::reloadPages
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,14 +1,18 @@
|
||||||
package com.lagradost.cloudstream3.ui.library
|
package com.lagradost.cloudstream3.ui.library
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.core.view.doOnAttach
|
import androidx.core.view.doOnAttach
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.recyclerview.widget.RecyclerView.OnFlingListener
|
import androidx.recyclerview.widget.RecyclerView.OnFlingListener
|
||||||
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.databinding.LibraryViewpagerPageBinding
|
import com.lagradost.cloudstream3.databinding.LibraryViewpagerPageBinding
|
||||||
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
|
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
|
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
|
||||||
|
|
||||||
class ViewpagerAdapter(
|
class ViewpagerAdapter(
|
||||||
|
@ -67,6 +71,17 @@ class ViewpagerAdapter(
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
setOnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
|
setOnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
|
||||||
val diff = scrollY - oldScrollY
|
val diff = scrollY - oldScrollY
|
||||||
|
|
||||||
|
//Expand the top Appbar based on scroll direction up/down, simulate phone behavior
|
||||||
|
if (SettingsFragment.isTvSettings()) {
|
||||||
|
binding.root.rootView.findViewById<AppBarLayout>(R.id.search_bar)
|
||||||
|
.apply {
|
||||||
|
if (diff <= 0)
|
||||||
|
setExpanded(true)
|
||||||
|
else
|
||||||
|
setExpanded(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
if (diff == 0) return@setOnScrollChangeListener
|
if (diff == 0) return@setOnScrollChangeListener
|
||||||
|
|
||||||
scrollCallback.invoke(diff > 0)
|
scrollCallback.invoke(diff > 0)
|
||||||
|
@ -80,8 +95,6 @@ class ViewpagerAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1014,7 +1014,8 @@ class CS3IPlayer : IPlayer {
|
||||||
format.id!!,
|
format.id!!,
|
||||||
SubtitleOrigin.EMBEDDED_IN_VIDEO,
|
SubtitleOrigin.EMBEDDED_IN_VIDEO,
|
||||||
format.sampleMimeType ?: MimeTypes.APPLICATION_SUBRIP,
|
format.sampleMimeType ?: MimeTypes.APPLICATION_SUBRIP,
|
||||||
emptyMap()
|
emptyMap(),
|
||||||
|
format.language
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1254,7 +1255,7 @@ class CS3IPlayer : IPlayer {
|
||||||
.setMimeType(sub.mimeType)
|
.setMimeType(sub.mimeType)
|
||||||
.setLanguage("_${sub.name}")
|
.setLanguage("_${sub.name}")
|
||||||
.setId(sub.getId())
|
.setId(sub.getId())
|
||||||
.setSelectionFlags(SELECTION_FLAG_DEFAULT)
|
.setSelectionFlags(0)
|
||||||
.build()
|
.build()
|
||||||
when (sub.origin) {
|
when (sub.origin) {
|
||||||
SubtitleOrigin.DOWNLOADED_FILE -> {
|
SubtitleOrigin.DOWNLOADED_FILE -> {
|
||||||
|
|
|
@ -100,7 +100,8 @@ class DownloadFileGenerator(
|
||||||
uri.toString(),
|
uri.toString(),
|
||||||
SubtitleOrigin.DOWNLOADED_FILE,
|
SubtitleOrigin.DOWNLOADED_FILE,
|
||||||
name.toSubtitleMimeType(),
|
name.toSubtitleMimeType(),
|
||||||
emptyMap()
|
emptyMap(),
|
||||||
|
null
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
|
import androidx.activity.OnBackPressedCallback
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import com.lagradost.cloudstream3.CommonActivity
|
import com.lagradost.cloudstream3.CommonActivity
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
|
@ -34,10 +35,6 @@ class DownloadedPlayerActivity : AppCompatActivity() {
|
||||||
CommonActivity.onUserLeaveHint(this)
|
CommonActivity.onUserLeaveHint(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackPressed() {
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun playLink(url: String) {
|
private fun playLink(url: String) {
|
||||||
this.navigate(
|
this.navigate(
|
||||||
R.id.global_to_navigation_player, GeneratorPlayer.newInstance(
|
R.id.global_to_navigation_player, GeneratorPlayer.newInstance(
|
||||||
|
@ -109,6 +106,15 @@ class DownloadedPlayerActivity : AppCompatActivity() {
|
||||||
finish()
|
finish()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onBackPressedDispatcher.addCallback(
|
||||||
|
this,
|
||||||
|
object : OnBackPressedCallback(true) {
|
||||||
|
override fun handleOnBackPressed() {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
|
|
@ -23,6 +23,7 @@ import androidx.media3.common.Format.NO_VALUE
|
||||||
import androidx.media3.common.MimeTypes
|
import androidx.media3.common.MimeTypes
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
|
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||||
import com.lagradost.cloudstream3.CommonActivity.showToast
|
import com.lagradost.cloudstream3.CommonActivity.showToast
|
||||||
import com.lagradost.cloudstream3.databinding.DialogOnlineSubtitlesBinding
|
import com.lagradost.cloudstream3.databinding.DialogOnlineSubtitlesBinding
|
||||||
import com.lagradost.cloudstream3.databinding.FragmentPlayerBinding
|
import com.lagradost.cloudstream3.databinding.FragmentPlayerBinding
|
||||||
|
@ -38,6 +39,7 @@ import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper
|
||||||
import com.lagradost.cloudstream3.ui.player.source_priority.QualityProfileDialog
|
import com.lagradost.cloudstream3.ui.player.source_priority.QualityProfileDialog
|
||||||
import com.lagradost.cloudstream3.ui.result.*
|
import com.lagradost.cloudstream3.ui.result.*
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
||||||
|
import com.lagradost.cloudstream3.ui.subtitles.SUBTITLE_AUTO_SELECT_KEY
|
||||||
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment.Companion.getAutoSelectLanguageISO639_1
|
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment.Companion.getAutoSelectLanguageISO639_1
|
||||||
import com.lagradost.cloudstream3.utils.*
|
import com.lagradost.cloudstream3.utils.*
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||||
|
@ -100,10 +102,33 @@ class GeneratorPlayer : FullScreenPlayer() {
|
||||||
binding?.playerLoadingOverlay?.isVisible = true
|
binding?.playerLoadingOverlay?.isVisible = true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setSubtitles(sub: SubtitleData?): Boolean {
|
private fun setSubtitles(subtitle: SubtitleData?): Boolean {
|
||||||
currentSelectedSubtitles = sub
|
// If subtitle is changed -> Save the language
|
||||||
//Log.i(TAG, "setSubtitles = $sub")
|
if (subtitle != currentSelectedSubtitles) {
|
||||||
return player.setPreferredSubtitles(sub)
|
val subtitleLanguage639 = if (subtitle == null) {
|
||||||
|
// "" is No Subtitles
|
||||||
|
""
|
||||||
|
} else if (subtitle.languageCode != null) {
|
||||||
|
// Could be "English 4" which is why it is trimmed.
|
||||||
|
val trimmedLanguage = subtitle.languageCode.replace(Regex("\\d"), "").trim()
|
||||||
|
|
||||||
|
languages.firstOrNull { language ->
|
||||||
|
language.languageName.equals(trimmedLanguage, ignoreCase = true) ||
|
||||||
|
language.ISO_639_1 == subtitle.languageCode
|
||||||
|
}?.ISO_639_1
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subtitleLanguage639 != null) {
|
||||||
|
setKey(SUBTITLE_AUTO_SELECT_KEY, subtitleLanguage639)
|
||||||
|
preferredAutoSelectSubtitles = subtitleLanguage639
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentSelectedSubtitles = subtitle
|
||||||
|
//Log.i(TAG, "setSubtitles = $subtitle")
|
||||||
|
return player.setPreferredSubtitles(subtitle)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun embeddedSubtitlesFetched(subtitles: List<SubtitleData>) {
|
override fun embeddedSubtitlesFetched(subtitles: List<SubtitleData>) {
|
||||||
|
@ -448,7 +473,8 @@ class GeneratorPlayer : FullScreenPlayer() {
|
||||||
url = url,
|
url = url,
|
||||||
origin = SubtitleOrigin.URL,
|
origin = SubtitleOrigin.URL,
|
||||||
mimeType = url.toSubtitleMimeType(),
|
mimeType = url.toSubtitleMimeType(),
|
||||||
headers = currentSubtitle.headers
|
headers = currentSubtitle.headers,
|
||||||
|
currentSubtitle.lang
|
||||||
)
|
)
|
||||||
runOnMainThread {
|
runOnMainThread {
|
||||||
addAndSelectSubtitles(subtitle)
|
addAndSelectSubtitles(subtitle)
|
||||||
|
@ -536,7 +562,8 @@ class GeneratorPlayer : FullScreenPlayer() {
|
||||||
uri.toString(),
|
uri.toString(),
|
||||||
SubtitleOrigin.DOWNLOADED_FILE,
|
SubtitleOrigin.DOWNLOADED_FILE,
|
||||||
name.toSubtitleMimeType(),
|
name.toSubtitleMimeType(),
|
||||||
emptyMap()
|
emptyMap(),
|
||||||
|
null
|
||||||
)
|
)
|
||||||
|
|
||||||
addAndSelectSubtitles(subtitleData)
|
addAndSelectSubtitles(subtitleData)
|
||||||
|
@ -946,7 +973,7 @@ class GeneratorPlayer : FullScreenPlayer() {
|
||||||
|
|
||||||
var maxEpisodeSet: Int? = null
|
var maxEpisodeSet: Int? = null
|
||||||
var hasRequestedStamps: Boolean = false
|
var hasRequestedStamps: Boolean = false
|
||||||
override fun playerPositionChanged(position: Long, duration : Long) {
|
override fun playerPositionChanged(position: Long, duration: Long) {
|
||||||
// Don't save livestream data
|
// Don't save livestream data
|
||||||
if ((currentMeta as? ResultEpisode)?.tvType?.isLiveStream() == true) return
|
if ((currentMeta as? ResultEpisode)?.tvType?.isLiveStream() == true) return
|
||||||
|
|
||||||
|
@ -1023,7 +1050,7 @@ class GeneratorPlayer : FullScreenPlayer() {
|
||||||
ctx.getString(R.string.episode_sync_enabled_key), true
|
ctx.getString(R.string.episode_sync_enabled_key), true
|
||||||
)
|
)
|
||||||
) maxEpisodeSet = meta.episode
|
) maxEpisodeSet = meta.episode
|
||||||
sync.modifyMaxEpisode(meta.episode)
|
sync.modifyMaxEpisode(meta.totalEpisodeIndex ?: meta.episode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1209,7 +1236,7 @@ class GeneratorPlayer : FullScreenPlayer() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun playerDimensionsLoaded(width: Int, height : Int) {
|
override fun playerDimensionsLoaded(width: Int, height: Int) {
|
||||||
setPlayerDimen(width to height)
|
setPlayerDimen(width to height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,13 +30,15 @@ enum class SubtitleOrigin {
|
||||||
* @param name To be displayed in the player
|
* @param name To be displayed in the player
|
||||||
* @param url Url for the subtitle, when EMBEDDED_IN_VIDEO this variable is used as the real backend id
|
* @param url Url for the subtitle, when EMBEDDED_IN_VIDEO this variable is used as the real backend id
|
||||||
* @param headers if empty it will use the base onlineDataSource headers else only the specified headers
|
* @param headers if empty it will use the base onlineDataSource headers else only the specified headers
|
||||||
|
* @param languageCode Not guaranteed to follow any standard. Could be something like "English 4" or "en".
|
||||||
* */
|
* */
|
||||||
data class SubtitleData(
|
data class SubtitleData(
|
||||||
val name: String,
|
val name: String,
|
||||||
val url: String,
|
val url: String,
|
||||||
val origin: SubtitleOrigin,
|
val origin: SubtitleOrigin,
|
||||||
val mimeType: String,
|
val mimeType: String,
|
||||||
val headers: Map<String, String>
|
val headers: Map<String, String>,
|
||||||
|
val languageCode: String?
|
||||||
) {
|
) {
|
||||||
/** Internal ID for exoplayer, unique for each link*/
|
/** Internal ID for exoplayer, unique for each link*/
|
||||||
fun getId(): String {
|
fun getId(): String {
|
||||||
|
@ -80,7 +82,8 @@ class PlayerSubtitleHelper {
|
||||||
url = subtitleFile.url,
|
url = subtitleFile.url,
|
||||||
origin = SubtitleOrigin.URL,
|
origin = SubtitleOrigin.URL,
|
||||||
mimeType = subtitleFile.url.toSubtitleMimeType(),
|
mimeType = subtitleFile.url.toSubtitleMimeType(),
|
||||||
headers = emptyMap()
|
headers = emptyMap(),
|
||||||
|
languageCode = subtitleFile.lang
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.TypedValue
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
@ -215,10 +216,16 @@ class QuickSearchFragment : Fragment() {
|
||||||
binding?.quickSearch?.findViewById<ImageView>(androidx.appcompat.R.id.search_close_btn)
|
binding?.quickSearch?.findViewById<ImageView>(androidx.appcompat.R.id.search_close_btn)
|
||||||
|
|
||||||
//val searchMagIcon =
|
//val searchMagIcon =
|
||||||
// binding.quickSearch.findViewById<ImageView>(androidx.appcompat.R.id.search_mag_icon)
|
// binding?.quickSearch?.findViewById<ImageView>(androidx.appcompat.R.id.search_mag_icon)
|
||||||
|
|
||||||
//searchMagIcon?.scaleX = 0.65f
|
// searchMagIcon?.scaleX = 0.65f
|
||||||
//searchMagIcon?.scaleY = 0.65f
|
// searchMagIcon?.scaleY = 0.65f
|
||||||
|
|
||||||
|
// Set the color for the search exit icon to the correct theme text color
|
||||||
|
val searchExitIconColor = TypedValue()
|
||||||
|
|
||||||
|
activity?.theme?.resolveAttribute(android.R.attr.textColor, searchExitIconColor, true)
|
||||||
|
searchExitIcon?.setColorFilter(searchExitIconColor.data)
|
||||||
|
|
||||||
binding?.quickSearch?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
binding?.quickSearch?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||||
override fun onQueryTextSubmit(query: String): Boolean {
|
override fun onQueryTextSubmit(query: String): Boolean {
|
||||||
|
|
|
@ -47,7 +47,9 @@ data class ResultEpisode(
|
||||||
/**
|
/**
|
||||||
* Conveys if the episode itself is marked as watched
|
* Conveys if the episode itself is marked as watched
|
||||||
**/
|
**/
|
||||||
val videoWatchState: VideoWatchState
|
val videoWatchState: VideoWatchState,
|
||||||
|
/** Sum of all previous season episode counts + episode */
|
||||||
|
val totalEpisodeIndex: Int? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun ResultEpisode.getRealPosition(): Long {
|
fun ResultEpisode.getRealPosition(): Long {
|
||||||
|
@ -82,6 +84,7 @@ fun buildResultEpisode(
|
||||||
isFiller: Boolean? = null,
|
isFiller: Boolean? = null,
|
||||||
tvType: TvType,
|
tvType: TvType,
|
||||||
parentId: Int,
|
parentId: Int,
|
||||||
|
totalEpisodeIndex: Int? = null,
|
||||||
): ResultEpisode {
|
): ResultEpisode {
|
||||||
val posDur = getViewPos(id)
|
val posDur = getViewPos(id)
|
||||||
val videoWatchState = getVideoWatchState(id) ?: VideoWatchState.None
|
val videoWatchState = getVideoWatchState(id) ?: VideoWatchState.None
|
||||||
|
@ -103,7 +106,8 @@ fun buildResultEpisode(
|
||||||
isFiller,
|
isFiller,
|
||||||
tvType,
|
tvType,
|
||||||
parentId,
|
parentId,
|
||||||
videoWatchState
|
videoWatchState,
|
||||||
|
totalEpisodeIndex
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ import android.view.animation.DecelerateInterpolator
|
||||||
import android.widget.AbsListView
|
import android.widget.AbsListView
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.widget.NestedScrollView
|
import androidx.core.widget.NestedScrollView
|
||||||
|
@ -66,6 +65,7 @@ import com.lagradost.cloudstream3.utils.AppUtils.openBrowser
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogInstant
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogInstant
|
||||||
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogText
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper
|
import com.lagradost.cloudstream3.utils.UIHelper
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
||||||
|
@ -430,20 +430,36 @@ open class ResultFragmentPhone : FullScreenPlayer() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
resultSubscribe.setOnClickListener {
|
resultSubscribe.setOnClickListener {
|
||||||
val isSubscribed =
|
viewModel.toggleSubscriptionStatus(context) { newStatus: Boolean? ->
|
||||||
viewModel.toggleSubscriptionStatus() ?: return@setOnClickListener
|
if (newStatus == null) return@toggleSubscriptionStatus
|
||||||
|
|
||||||
val message = if (isSubscribed) {
|
val message = if (newStatus) {
|
||||||
// Kinda icky to have this here, but it works.
|
// Kinda icky to have this here, but it works.
|
||||||
SubscriptionWorkManager.enqueuePeriodicWork(context)
|
SubscriptionWorkManager.enqueuePeriodicWork(context)
|
||||||
R.string.subscription_new
|
R.string.subscription_new
|
||||||
} else {
|
} else {
|
||||||
R.string.subscription_deleted
|
R.string.subscription_deleted
|
||||||
|
}
|
||||||
|
|
||||||
|
val name = (viewModel.page.value as? Resource.Success)?.value?.title
|
||||||
|
?: txt(R.string.no_data).asStringNull(context) ?: ""
|
||||||
|
CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
resultFavorite.setOnClickListener {
|
||||||
|
viewModel.toggleFavoriteStatus(context) { newStatus: Boolean? ->
|
||||||
|
if (newStatus == null) return@toggleFavoriteStatus
|
||||||
|
|
||||||
val name = (viewModel.page.value as? Resource.Success)?.value?.title
|
val message = if (newStatus) {
|
||||||
?: txt(R.string.no_data).asStringNull(context) ?: ""
|
R.string.favorite_added
|
||||||
CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
|
} else {
|
||||||
|
R.string.favorite_removed
|
||||||
|
}
|
||||||
|
|
||||||
|
val name = (viewModel.page.value as? Resource.Success)?.value?.title
|
||||||
|
?: txt(R.string.no_data).asStringNull(context) ?: ""
|
||||||
|
CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mediaRouteButton.apply {
|
mediaRouteButton.apply {
|
||||||
val chromecastSupport = api?.hasChromecastSupport == true
|
val chromecastSupport = api?.hasChromecastSupport == true
|
||||||
|
@ -564,6 +580,19 @@ open class ResultFragmentPhone : FullScreenPlayer() {
|
||||||
binding?.resultSubscribe?.setImageResource(drawable)
|
binding?.resultSubscribe?.setImageResource(drawable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
observeNullable(viewModel.favoriteStatus) { isFavorite ->
|
||||||
|
binding?.resultFavorite?.isVisible = isFavorite != null
|
||||||
|
if (isFavorite == null) return@observeNullable
|
||||||
|
|
||||||
|
val drawable = if (isFavorite) {
|
||||||
|
R.drawable.ic_baseline_favorite_24
|
||||||
|
} else {
|
||||||
|
R.drawable.ic_baseline_favorite_border_24
|
||||||
|
}
|
||||||
|
|
||||||
|
binding?.resultFavorite?.setImageResource(drawable)
|
||||||
|
}
|
||||||
|
|
||||||
observe(viewModel.trailers) { trailers ->
|
observe(viewModel.trailers) { trailers ->
|
||||||
setTrailers(trailers.flatMap { it.mirros }) // I dont care about subtitles yet!
|
setTrailers(trailers.flatMap { it.mirros }) // I dont care about subtitles yet!
|
||||||
}
|
}
|
||||||
|
@ -654,14 +683,13 @@ open class ResultFragmentPhone : FullScreenPlayer() {
|
||||||
resultPoster.setImage(d.posterImage)
|
resultPoster.setImage(d.posterImage)
|
||||||
resultPosterBackground.setImage(d.posterBackgroundImage)
|
resultPosterBackground.setImage(d.posterBackgroundImage)
|
||||||
resultDescription.setTextHtml(d.plotText)
|
resultDescription.setTextHtml(d.plotText)
|
||||||
resultDescription.setOnClickListener { view ->
|
resultDescription.setOnClickListener {
|
||||||
// todo bottom view?
|
activity?.let { activity ->
|
||||||
view.context?.let { ctx ->
|
activity.showBottomDialogText(
|
||||||
val builder: AlertDialog.Builder =
|
d.titleText.asString(activity),
|
||||||
AlertDialog.Builder(ctx, R.style.AlertDialogCustom)
|
d.plotText.asString(activity).html(),
|
||||||
builder.setMessage(d.plotText.asString(ctx).html())
|
{}
|
||||||
.setTitle(d.plotHeaderText.asString(ctx))
|
)
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -852,16 +880,11 @@ open class ResultFragmentPhone : FullScreenPlayer() {
|
||||||
setRecommendations(recommendations, null)
|
setRecommendations(recommendations, null)
|
||||||
}
|
}
|
||||||
observe(viewModel.episodeSynopsis) { description ->
|
observe(viewModel.episodeSynopsis) { description ->
|
||||||
// TODO bottom dialog
|
activity?.let { activity ->
|
||||||
view.context?.let { ctx ->
|
activity.showBottomDialogText(
|
||||||
val builder: AlertDialog.Builder =
|
activity.getString(R.string.synopsis),
|
||||||
AlertDialog.Builder(ctx, R.style.AlertDialogCustom)
|
description.html()
|
||||||
builder.setMessage(description.html())
|
) { viewModel.releaseEpisodeSynopsis() }
|
||||||
.setTitle(R.string.synopsis)
|
|
||||||
.setOnDismissListener {
|
|
||||||
viewModel.releaseEpisodeSynopsis()
|
|
||||||
}
|
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
context?.let { ctx ->
|
context?.let { ctx ->
|
||||||
|
@ -939,7 +962,7 @@ open class ResultFragmentPhone : FullScreenPlayer() {
|
||||||
fab.context.getString(R.string.action_add_to_bookmarks),
|
fab.context.getString(R.string.action_add_to_bookmarks),
|
||||||
showApply = false,
|
showApply = false,
|
||||||
{}) {
|
{}) {
|
||||||
viewModel.updateWatchStatus(WatchType.values()[it])
|
viewModel.updateWatchStatus(WatchType.values()[it], context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.animation.DecelerateInterpolator
|
import android.view.animation.DecelerateInterpolator
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
@ -17,6 +18,7 @@ import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
|
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
|
||||||
|
import com.lagradost.cloudstream3.CommonActivity
|
||||||
import com.lagradost.cloudstream3.DubStatus
|
import com.lagradost.cloudstream3.DubStatus
|
||||||
import com.lagradost.cloudstream3.LoadResponse
|
import com.lagradost.cloudstream3.LoadResponse
|
||||||
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
|
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
|
||||||
|
@ -26,6 +28,7 @@ import com.lagradost.cloudstream3.databinding.FragmentResultTvBinding
|
||||||
import com.lagradost.cloudstream3.mvvm.Resource
|
import com.lagradost.cloudstream3.mvvm.Resource
|
||||||
import com.lagradost.cloudstream3.mvvm.observe
|
import com.lagradost.cloudstream3.mvvm.observe
|
||||||
import com.lagradost.cloudstream3.mvvm.observeNullable
|
import com.lagradost.cloudstream3.mvvm.observeNullable
|
||||||
|
import com.lagradost.cloudstream3.services.SubscriptionWorkManager
|
||||||
import com.lagradost.cloudstream3.ui.WatchType
|
import com.lagradost.cloudstream3.ui.WatchType
|
||||||
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup
|
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup
|
||||||
import com.lagradost.cloudstream3.ui.player.ExtractorLinkGenerator
|
import com.lagradost.cloudstream3.ui.player.ExtractorLinkGenerator
|
||||||
|
@ -35,6 +38,7 @@ import com.lagradost.cloudstream3.ui.result.ResultFragment.updateUIEvent
|
||||||
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_FOCUSED
|
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_FOCUSED
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchAdapter
|
import com.lagradost.cloudstream3.ui.search.SearchAdapter
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchHelper
|
import com.lagradost.cloudstream3.ui.search.SearchHelper
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
|
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.html
|
import com.lagradost.cloudstream3.utils.AppUtils.html
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.isRtl
|
import com.lagradost.cloudstream3.utils.AppUtils.isRtl
|
||||||
|
@ -265,6 +269,7 @@ class ResultFragmentTv : Fragment() {
|
||||||
resultEpisodesShow.onFocusChangeListener = rightListener
|
resultEpisodesShow.onFocusChangeListener = rightListener
|
||||||
resultDescription.onFocusChangeListener = leftListener
|
resultDescription.onFocusChangeListener = leftListener
|
||||||
resultBookmarkButton.onFocusChangeListener = leftListener
|
resultBookmarkButton.onFocusChangeListener = leftListener
|
||||||
|
resultFavoriteButton.onFocusChangeListener = leftListener
|
||||||
resultEpisodesShow.setOnClickListener {
|
resultEpisodesShow.setOnClickListener {
|
||||||
// toggle, to make it more touch accessable just in case someone thinks that a
|
// toggle, to make it more touch accessable just in case someone thinks that a
|
||||||
// tv layout is better but is using a touch device
|
// tv layout is better but is using a touch device
|
||||||
|
@ -283,7 +288,9 @@ class ResultFragmentTv : Fragment() {
|
||||||
resultPlaySeries,
|
resultPlaySeries,
|
||||||
resultResumeSeries,
|
resultResumeSeries,
|
||||||
resultPlayTrailer,
|
resultPlayTrailer,
|
||||||
resultBookmarkButton
|
resultBookmarkButton,
|
||||||
|
resultFavoriteButton,
|
||||||
|
resultSubscribeButton
|
||||||
)
|
)
|
||||||
for (requestView in views) {
|
for (requestView in views) {
|
||||||
if (!requestView.isVisible) continue
|
if (!requestView.isVisible) continue
|
||||||
|
@ -424,6 +431,8 @@ class ResultFragmentTv : Fragment() {
|
||||||
val aboveCast = listOf(
|
val aboveCast = listOf(
|
||||||
binding?.resultEpisodesShow,
|
binding?.resultEpisodesShow,
|
||||||
binding?.resultBookmarkButton,
|
binding?.resultBookmarkButton,
|
||||||
|
binding?.resultFavoriteButton,
|
||||||
|
binding?.resultSubscribeButton,
|
||||||
).firstOrNull {
|
).firstOrNull {
|
||||||
it?.isVisible == true
|
it?.isVisible == true
|
||||||
}
|
}
|
||||||
|
@ -526,7 +535,83 @@ class ResultFragmentTv : Fragment() {
|
||||||
view.context.getString(R.string.action_add_to_bookmarks),
|
view.context.getString(R.string.action_add_to_bookmarks),
|
||||||
showApply = false,
|
showApply = false,
|
||||||
{}) {
|
{}) {
|
||||||
viewModel.updateWatchStatus(WatchType.values()[it])
|
viewModel.updateWatchStatus(WatchType.values()[it], context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
observeNullable(viewModel.favoriteStatus) { isFavorite ->
|
||||||
|
binding?.resultFavoriteButton?.apply {
|
||||||
|
isVisible = isFavorite != null
|
||||||
|
if (isFavorite == null) return@observeNullable
|
||||||
|
|
||||||
|
val drawable = if (isFavorite) {
|
||||||
|
R.drawable.ic_baseline_favorite_24
|
||||||
|
} else {
|
||||||
|
R.drawable.ic_baseline_favorite_border_24
|
||||||
|
}
|
||||||
|
|
||||||
|
val text = if (isFavorite) {
|
||||||
|
R.string.action_remove_from_favorites
|
||||||
|
} else {
|
||||||
|
R.string.action_add_to_favorites
|
||||||
|
}
|
||||||
|
|
||||||
|
setIconResource(drawable)
|
||||||
|
setText(text)
|
||||||
|
setOnClickListener {
|
||||||
|
viewModel.toggleFavoriteStatus(context) { newStatus: Boolean? ->
|
||||||
|
if (newStatus == null) return@toggleFavoriteStatus
|
||||||
|
|
||||||
|
val message = if (newStatus) {
|
||||||
|
R.string.favorite_added
|
||||||
|
} else {
|
||||||
|
R.string.favorite_removed
|
||||||
|
}
|
||||||
|
|
||||||
|
val name = (viewModel.page.value as? Resource.Success)?.value?.title
|
||||||
|
?: txt(R.string.no_data).asStringNull(context) ?: ""
|
||||||
|
CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
observeNullable(viewModel.subscribeStatus) { isSubscribed ->
|
||||||
|
binding?.resultSubscribeButton?.apply {
|
||||||
|
isVisible = isSubscribed != null && context.isEmulatorSettings()
|
||||||
|
if (isSubscribed == null) return@observeNullable
|
||||||
|
|
||||||
|
val drawable = if (isSubscribed) {
|
||||||
|
R.drawable.ic_baseline_notifications_active_24
|
||||||
|
} else {
|
||||||
|
R.drawable.baseline_notifications_none_24
|
||||||
|
}
|
||||||
|
|
||||||
|
val text = if (isSubscribed) {
|
||||||
|
R.string.action_unsubscribe
|
||||||
|
} else {
|
||||||
|
R.string.action_subscribe
|
||||||
|
}
|
||||||
|
|
||||||
|
setIconResource(drawable)
|
||||||
|
setText(text)
|
||||||
|
setOnClickListener {
|
||||||
|
viewModel.toggleSubscriptionStatus(context) { newStatus: Boolean? ->
|
||||||
|
if (newStatus == null) return@toggleSubscriptionStatus
|
||||||
|
|
||||||
|
val message = if (newStatus) {
|
||||||
|
// Kinda icky to have this here, but it works.
|
||||||
|
SubscriptionWorkManager.enqueuePeriodicWork(context)
|
||||||
|
R.string.subscription_new
|
||||||
|
} else {
|
||||||
|
R.string.subscription_deleted
|
||||||
|
}
|
||||||
|
|
||||||
|
val name = (viewModel.page.value as? Resource.Success)?.value?.title
|
||||||
|
?: txt(R.string.no_data).asStringNull(context) ?: ""
|
||||||
|
CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -535,6 +620,7 @@ class ResultFragmentTv : Fragment() {
|
||||||
observeNullable(viewModel.movie) { data ->
|
observeNullable(viewModel.movie) { data ->
|
||||||
binding?.apply {
|
binding?.apply {
|
||||||
resultPlayMovie.isVisible = data is Resource.Success
|
resultPlayMovie.isVisible = data is Resource.Success
|
||||||
|
resultPlaySeries.isVisible = data == null
|
||||||
seriesHolder.isVisible = data == null
|
seriesHolder.isVisible = data == null
|
||||||
resultEpisodesShow.isVisible = data == null
|
resultEpisodesShow.isVisible = data == null
|
||||||
|
|
||||||
|
@ -764,12 +850,14 @@ class ResultFragmentTv : Fragment() {
|
||||||
R.drawable.profile_bg_red,
|
R.drawable.profile_bg_red,
|
||||||
R.drawable.profile_bg_teal
|
R.drawable.profile_bg_teal
|
||||||
).random()
|
).random()
|
||||||
|
//Change poster crop area to 20% from Top
|
||||||
|
backgroundPoster.cropYCenterOffsetPct = 0.20F
|
||||||
|
|
||||||
backgroundPoster.setImage(
|
backgroundPoster.setImage(
|
||||||
d.posterBackgroundImage ?: UiImage.Drawable(error),
|
d.posterBackgroundImage ?: UiImage.Drawable(error),
|
||||||
radius = 0,
|
radius = 0,
|
||||||
errorImageDrawable = error
|
errorImageDrawable = error
|
||||||
)
|
)
|
||||||
|
|
||||||
resultComingSoon.isVisible = d.comingSoon
|
resultComingSoon.isVisible = d.comingSoon
|
||||||
resultDataHolder.isGone = d.comingSoon
|
resultDataHolder.isGone = d.comingSoon
|
||||||
UIHelper.populateChips(resultTag, d.tags)
|
UIHelper.populateChips(resultTag, d.tags)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
|
import androidx.activity.OnBackPressedCallback
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import com.lagradost.cloudstream3.CommonActivity.screenHeight
|
import com.lagradost.cloudstream3.CommonActivity.screenHeight
|
||||||
|
@ -15,10 +16,8 @@ import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.ui.player.CSPlayerEvent
|
import com.lagradost.cloudstream3.ui.player.CSPlayerEvent
|
||||||
import com.lagradost.cloudstream3.ui.player.PlayerEventSource
|
import com.lagradost.cloudstream3.ui.player.PlayerEventSource
|
||||||
import com.lagradost.cloudstream3.ui.player.SubtitleData
|
import com.lagradost.cloudstream3.ui.player.SubtitleData
|
||||||
import com.lagradost.cloudstream3.utils.IOnBackPressed
|
|
||||||
|
|
||||||
|
open class ResultTrailerPlayer : ResultFragmentPhone() {
|
||||||
open class ResultTrailerPlayer : ResultFragmentPhone(), IOnBackPressed {
|
|
||||||
|
|
||||||
override var lockRotation = false
|
override var lockRotation = false
|
||||||
override var isFullScreenPlayer = false
|
override var isFullScreenPlayer = false
|
||||||
|
@ -28,7 +27,7 @@ open class ResultTrailerPlayer : ResultFragmentPhone(), IOnBackPressed {
|
||||||
const val TAG = "RESULT_TRAILER"
|
const val TAG = "RESULT_TRAILER"
|
||||||
}
|
}
|
||||||
|
|
||||||
var playerWidthHeight: Pair<Int, Int>? = null
|
private var playerWidthHeight: Pair<Int, Int>? = null
|
||||||
|
|
||||||
override fun nextEpisode() {}
|
override fun nextEpisode() {}
|
||||||
|
|
||||||
|
@ -154,6 +153,10 @@ open class ResultTrailerPlayer : ResultFragmentPhone(), IOnBackPressed {
|
||||||
}
|
}
|
||||||
fixPlayerSize()
|
fixPlayerSize()
|
||||||
uiReset()
|
uiReset()
|
||||||
|
|
||||||
|
if (isFullScreenPlayer) {
|
||||||
|
attachBackPressedCallback()
|
||||||
|
} else detachBackPressedCallback()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
@ -172,12 +175,26 @@ open class ResultTrailerPlayer : ResultFragmentPhone(), IOnBackPressed {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackPressed(): Boolean {
|
private var backPressedCallback: OnBackPressedCallback? = null
|
||||||
return if (isFullScreenPlayer) {
|
|
||||||
updateFullscreen(false)
|
private fun attachBackPressedCallback() {
|
||||||
false
|
if (backPressedCallback == null) {
|
||||||
} else {
|
backPressedCallback = object : OnBackPressedCallback(true) {
|
||||||
true
|
override fun handleOnBackPressed() {
|
||||||
|
updateFullscreen(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
backPressedCallback?.isEnabled = true
|
||||||
|
|
||||||
|
activity?.onBackPressedDispatcher?.addCallback(
|
||||||
|
activity ?: return,
|
||||||
|
backPressedCallback ?: return
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun detachBackPressedCallback() {
|
||||||
|
backPressedCallback?.isEnabled = false
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,6 +7,8 @@ import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.annotation.MainThread
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
|
@ -31,6 +33,7 @@ import com.lagradost.cloudstream3.mvvm.*
|
||||||
import com.lagradost.cloudstream3.syncproviders.AccountManager
|
import com.lagradost.cloudstream3.syncproviders.AccountManager
|
||||||
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
||||||
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
|
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.providers.SimklApi
|
||||||
import com.lagradost.cloudstream3.ui.APIRepository
|
import com.lagradost.cloudstream3.ui.APIRepository
|
||||||
import com.lagradost.cloudstream3.ui.WatchType
|
import com.lagradost.cloudstream3.ui.WatchType
|
||||||
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO
|
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO
|
||||||
|
@ -45,19 +48,37 @@ import com.lagradost.cloudstream3.utils.*
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
|
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.isAppInstalled
|
import com.lagradost.cloudstream3.utils.AppUtils.isAppInstalled
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.isConnectedToChromecast
|
import com.lagradost.cloudstream3.utils.AppUtils.isConnectedToChromecast
|
||||||
|
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
|
||||||
import com.lagradost.cloudstream3.utils.CastHelper.startCast
|
import com.lagradost.cloudstream3.utils.CastHelper.startCast
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioWork
|
import com.lagradost.cloudstream3.utils.Coroutines.ioWork
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioWorkSafe
|
import com.lagradost.cloudstream3.utils.Coroutines.ioWorkSafe
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.deleteBookmarkedData
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllBookmarkedData
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllFavorites
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllSubscriptions
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getBookmarkedData
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getDub
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getDub
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getFavoritesData
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getLastWatched
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultEpisode
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultEpisode
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultSeason
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultSeason
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getSubscribedData
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getVideoWatchState
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.removeFavoritesData
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.removeSubscribedData
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.setBookmarkedData
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.setDub
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.setDub
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.setFavoritesData
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultEpisode
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultEpisode
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultSeason
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultSeason
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultWatchState
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.setSubscribedData
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.setVideoWatchState
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.updateSubscribedData
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.navigate
|
import com.lagradost.cloudstream3.utils.UIHelper.navigate
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
@ -110,6 +131,18 @@ data class ResultData(
|
||||||
val plotHeaderText: UiText,
|
val plotHeaderText: UiText,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class CheckDuplicateData(
|
||||||
|
val name: String,
|
||||||
|
val year: Int?,
|
||||||
|
val syncData: Map<String, String>?
|
||||||
|
)
|
||||||
|
|
||||||
|
enum class LibraryListType {
|
||||||
|
BOOKMARKS,
|
||||||
|
FAVORITES,
|
||||||
|
SUBSCRIPTIONS
|
||||||
|
}
|
||||||
|
|
||||||
fun txt(status: DubStatus?): UiText? {
|
fun txt(status: DubStatus?): UiText? {
|
||||||
return txt(
|
return txt(
|
||||||
when (status) {
|
when (status) {
|
||||||
|
@ -425,6 +458,9 @@ class ResultViewModel2 : ViewModel() {
|
||||||
private val _subscribeStatus: MutableLiveData<Boolean?> = MutableLiveData(null)
|
private val _subscribeStatus: MutableLiveData<Boolean?> = MutableLiveData(null)
|
||||||
val subscribeStatus: LiveData<Boolean?> = _subscribeStatus
|
val subscribeStatus: LiveData<Boolean?> = _subscribeStatus
|
||||||
|
|
||||||
|
private val _favoriteStatus: MutableLiveData<Boolean?> = MutableLiveData(null)
|
||||||
|
val favoriteStatus: LiveData<Boolean?> = _favoriteStatus
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "RVM2"
|
const val TAG = "RVM2"
|
||||||
//private const val EPISODE_RANGE_SIZE = 20
|
//private const val EPISODE_RANGE_SIZE = 20
|
||||||
|
@ -435,33 +471,6 @@ class ResultViewModel2 : ViewModel() {
|
||||||
return this?.firstOrNull { it.season == season }
|
return this?.firstOrNull { it.season == season }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateWatchStatus(currentResponse: LoadResponse, status: WatchType) {
|
|
||||||
val currentId = currentResponse.getId()
|
|
||||||
|
|
||||||
val currentWatchType = getResultWatchState(currentId)
|
|
||||||
|
|
||||||
DataStoreHelper.setResultWatchState(currentId, status.internalId)
|
|
||||||
val current = DataStoreHelper.getBookmarkedData(currentId)
|
|
||||||
val currentTime = System.currentTimeMillis()
|
|
||||||
DataStoreHelper.setBookmarkedData(
|
|
||||||
currentId,
|
|
||||||
DataStoreHelper.BookmarkedData(
|
|
||||||
currentId,
|
|
||||||
current?.bookmarkedTime ?: currentTime,
|
|
||||||
currentTime,
|
|
||||||
currentResponse.name,
|
|
||||||
currentResponse.url,
|
|
||||||
currentResponse.apiName,
|
|
||||||
currentResponse.type,
|
|
||||||
currentResponse.posterUrl,
|
|
||||||
currentResponse.year
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if (currentWatchType != status) {
|
|
||||||
MainActivity.bookmarksUpdatedEvent(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun filterName(name: String?): String? {
|
private fun filterName(name: String?): String? {
|
||||||
if (name == null) return null
|
if (name == null) return null
|
||||||
Regex("[eE]pisode [0-9]*(.*)").find(name)?.groupValues?.get(1)?.let {
|
Regex("[eE]pisode [0-9]*(.*)").find(name)?.groupValues?.get(1)?.let {
|
||||||
|
@ -816,9 +825,77 @@ class ResultViewModel2 : ViewModel() {
|
||||||
val selectPopup: LiveData<SelectPopup?> = _selectPopup
|
val selectPopup: LiveData<SelectPopup?> = _selectPopup
|
||||||
|
|
||||||
|
|
||||||
fun updateWatchStatus(status: WatchType) {
|
fun updateWatchStatus(
|
||||||
updateWatchStatus(currentResponse ?: return, status)
|
status: WatchType,
|
||||||
_watchStatus.postValue(status)
|
context: Context?,
|
||||||
|
loadResponse: LoadResponse? = null,
|
||||||
|
statusChangedCallback: ((statusChanged: Boolean) -> Unit)? = null
|
||||||
|
) {
|
||||||
|
val response = loadResponse ?: currentResponse ?: return
|
||||||
|
|
||||||
|
val currentId = response.getId()
|
||||||
|
|
||||||
|
val currentStatus = getResultWatchState(currentId)
|
||||||
|
|
||||||
|
// If the current status is "NONE" and the new status is not "NONE",
|
||||||
|
// fetch the bookmarked data to check for duplicates, otherwise set this
|
||||||
|
// to an empty list, so that we don't show the duplicate warning dialog,
|
||||||
|
// but we still want to update the current bookmark and refresh the data anyway.
|
||||||
|
val bookmarkedData = if (currentStatus == WatchType.NONE && status != WatchType.NONE) {
|
||||||
|
getAllBookmarkedData()
|
||||||
|
} else emptyList()
|
||||||
|
|
||||||
|
checkAndWarnDuplicates(
|
||||||
|
context,
|
||||||
|
LibraryListType.BOOKMARKS,
|
||||||
|
CheckDuplicateData(
|
||||||
|
name = response.name,
|
||||||
|
year = response.year,
|
||||||
|
syncData = response.syncData,
|
||||||
|
),
|
||||||
|
bookmarkedData
|
||||||
|
) { shouldContinue: Boolean, duplicateIds: List<Int?> ->
|
||||||
|
if (!shouldContinue) return@checkAndWarnDuplicates
|
||||||
|
|
||||||
|
if (duplicateIds.isNotEmpty()) {
|
||||||
|
duplicateIds.forEach { duplicateId ->
|
||||||
|
deleteBookmarkedData(duplicateId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setResultWatchState(currentId, status.internalId)
|
||||||
|
|
||||||
|
// We don't need to store if WatchType.NONE.
|
||||||
|
// The key is removed in setResultWatchState, we don't want to
|
||||||
|
// re-add it again here if it was just removed.
|
||||||
|
if (status != WatchType.NONE) {
|
||||||
|
val current = getBookmarkedData(currentId)
|
||||||
|
|
||||||
|
setBookmarkedData(
|
||||||
|
currentId,
|
||||||
|
DataStoreHelper.BookmarkedData(
|
||||||
|
current?.bookmarkedTime ?: unixTimeMS,
|
||||||
|
currentId,
|
||||||
|
unixTimeMS,
|
||||||
|
response.name,
|
||||||
|
response.url,
|
||||||
|
response.apiName,
|
||||||
|
response.type,
|
||||||
|
response.posterUrl,
|
||||||
|
response.year,
|
||||||
|
response.syncData
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentStatus != status) {
|
||||||
|
MainActivity.bookmarksUpdatedEvent(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
_watchStatus.postValue(status)
|
||||||
|
|
||||||
|
statusChangedCallback?.invoke(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startChromecast(
|
private fun startChromecast(
|
||||||
|
@ -833,39 +910,255 @@ class ResultViewModel2 : ViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if the new status is Subscribed, false if not. Null if not possible to subscribe.
|
* Toggles the subscription status of an item.
|
||||||
**/
|
*
|
||||||
fun toggleSubscriptionStatus(): Boolean? {
|
* @param context The context to use for operations.
|
||||||
val isSubscribed = _subscribeStatus.value ?: return null
|
* @param statusChangedCallback A callback that is invoked when the subscription status changes.
|
||||||
val response = currentResponse ?: return null
|
* It provides the new subscription status (true if subscribed, false if unsubscribed, null if action was canceled).
|
||||||
if (response !is EpisodeResponse) return null
|
*/
|
||||||
|
fun toggleSubscriptionStatus(
|
||||||
|
context: Context?,
|
||||||
|
statusChangedCallback: ((newStatus: Boolean?) -> Unit)? = null
|
||||||
|
) {
|
||||||
|
val isSubscribed = _subscribeStatus.value ?: return
|
||||||
|
val response = currentResponse ?: return
|
||||||
|
if (response !is EpisodeResponse) return
|
||||||
|
|
||||||
val currentId = response.getId()
|
val currentId = response.getId()
|
||||||
|
|
||||||
if (isSubscribed) {
|
if (isSubscribed) {
|
||||||
DataStoreHelper.removeSubscribedData(currentId)
|
removeSubscribedData(currentId)
|
||||||
|
statusChangedCallback?.invoke(false)
|
||||||
|
_subscribeStatus.postValue(false)
|
||||||
} else {
|
} else {
|
||||||
val current = DataStoreHelper.getSubscribedData(currentId)
|
checkAndWarnDuplicates(
|
||||||
|
context,
|
||||||
|
LibraryListType.SUBSCRIPTIONS,
|
||||||
|
CheckDuplicateData(
|
||||||
|
name = response.name,
|
||||||
|
year = response.year,
|
||||||
|
syncData = response.syncData,
|
||||||
|
),
|
||||||
|
getAllSubscriptions(),
|
||||||
|
) { shouldContinue: Boolean, duplicateIds: List<Int?> ->
|
||||||
|
if (!shouldContinue) {
|
||||||
|
statusChangedCallback?.invoke(null)
|
||||||
|
return@checkAndWarnDuplicates
|
||||||
|
}
|
||||||
|
|
||||||
DataStoreHelper.setSubscribedData(
|
if (duplicateIds.isNotEmpty()) {
|
||||||
currentId,
|
duplicateIds.forEach { duplicateId ->
|
||||||
DataStoreHelper.SubscribedData(
|
removeSubscribedData(duplicateId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val current = getSubscribedData(currentId)
|
||||||
|
|
||||||
|
setSubscribedData(
|
||||||
currentId,
|
currentId,
|
||||||
current?.bookmarkedTime ?: unixTimeMS,
|
DataStoreHelper.SubscribedData(
|
||||||
unixTimeMS,
|
current?.subscribedTime ?: unixTimeMS,
|
||||||
response.getLatestEpisodes(),
|
response.getLatestEpisodes(),
|
||||||
response.name,
|
currentId,
|
||||||
response.url,
|
unixTimeMS,
|
||||||
response.apiName,
|
response.name,
|
||||||
response.type,
|
response.url,
|
||||||
response.posterUrl,
|
response.apiName,
|
||||||
response.year
|
response.type,
|
||||||
|
response.posterUrl,
|
||||||
|
response.year,
|
||||||
|
response.syncData
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
_subscribeStatus.postValue(true)
|
||||||
|
|
||||||
|
statusChangedCallback?.invoke(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles the favorite status of an item.
|
||||||
|
*
|
||||||
|
* @param context The context to use.
|
||||||
|
* @param statusChangedCallback A callback that is invoked when the favorite status changes.
|
||||||
|
* It provides the new favorite status (true if added to favorites, false if removed, null if action was canceled).
|
||||||
|
*/
|
||||||
|
fun toggleFavoriteStatus(
|
||||||
|
context: Context?,
|
||||||
|
statusChangedCallback: ((newStatus: Boolean?) -> Unit)? = null
|
||||||
|
) {
|
||||||
|
val isFavorite = _favoriteStatus.value ?: return
|
||||||
|
val response = currentResponse ?: return
|
||||||
|
|
||||||
|
val currentId = response.getId()
|
||||||
|
|
||||||
|
if (isFavorite) {
|
||||||
|
removeFavoritesData(currentId)
|
||||||
|
statusChangedCallback?.invoke(false)
|
||||||
|
_favoriteStatus.postValue(false)
|
||||||
|
} else {
|
||||||
|
checkAndWarnDuplicates(
|
||||||
|
context,
|
||||||
|
LibraryListType.FAVORITES,
|
||||||
|
CheckDuplicateData(
|
||||||
|
name = response.name,
|
||||||
|
year = response.year,
|
||||||
|
syncData = response.syncData,
|
||||||
|
),
|
||||||
|
getAllFavorites(),
|
||||||
|
) { shouldContinue: Boolean, duplicateIds: List<Int?> ->
|
||||||
|
if (!shouldContinue) {
|
||||||
|
statusChangedCallback?.invoke(null)
|
||||||
|
return@checkAndWarnDuplicates
|
||||||
|
}
|
||||||
|
|
||||||
|
if (duplicateIds.isNotEmpty()) {
|
||||||
|
duplicateIds.forEach { duplicateId ->
|
||||||
|
removeFavoritesData(duplicateId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val current = getFavoritesData(currentId)
|
||||||
|
|
||||||
|
setFavoritesData(
|
||||||
|
currentId,
|
||||||
|
DataStoreHelper.FavoritesData(
|
||||||
|
current?.favoritesTime ?: unixTimeMS,
|
||||||
|
currentId,
|
||||||
|
unixTimeMS,
|
||||||
|
response.name,
|
||||||
|
response.url,
|
||||||
|
response.apiName,
|
||||||
|
response.type,
|
||||||
|
response.posterUrl,
|
||||||
|
response.year,
|
||||||
|
response.syncData
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
_favoriteStatus.postValue(true)
|
||||||
|
|
||||||
|
statusChangedCallback?.invoke(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainThread
|
||||||
|
private fun checkAndWarnDuplicates(
|
||||||
|
context: Context?,
|
||||||
|
listType: LibraryListType,
|
||||||
|
checkDuplicateData: CheckDuplicateData,
|
||||||
|
data: List<DataStoreHelper.LibrarySearchResponse>,
|
||||||
|
checkDuplicatesCallback: (shouldContinue: Boolean, duplicateIds: List<Int?>) -> Unit
|
||||||
|
) {
|
||||||
|
val whitespaceRegex = "\\s+".toRegex()
|
||||||
|
fun normalizeString(input: String): String {
|
||||||
|
/**
|
||||||
|
* Trim the input string and replace consecutive spaces with a single space.
|
||||||
|
* This covers some edge-cases where the title does not match exactly across providers,
|
||||||
|
* and one provider has the title with an extra whitespace. This is minor enough that
|
||||||
|
* it should still match in this case.
|
||||||
|
*/
|
||||||
|
return input.trim().replace(whitespaceRegex, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
_subscribeStatus.postValue(!isSubscribed)
|
val syncData = checkDuplicateData.syncData
|
||||||
return !isSubscribed
|
|
||||||
|
val imdbId = getImdbIdFromSyncData(syncData)
|
||||||
|
val tmdbId = getTMDbIdFromSyncData(syncData)
|
||||||
|
val malId = syncData?.get(AccountManager.malApi.idPrefix)
|
||||||
|
val aniListId = syncData?.get(AccountManager.aniListApi.idPrefix)
|
||||||
|
val normalizedName = normalizeString(checkDuplicateData.name)
|
||||||
|
val year = checkDuplicateData.year
|
||||||
|
|
||||||
|
val duplicateEntries = data.filter { it: DataStoreHelper.LibrarySearchResponse ->
|
||||||
|
val librarySyncData = it.syncData
|
||||||
|
|
||||||
|
val checks = listOf(
|
||||||
|
{ imdbId != null && getImdbIdFromSyncData(librarySyncData) == imdbId },
|
||||||
|
{ tmdbId != null && getTMDbIdFromSyncData(librarySyncData) == tmdbId },
|
||||||
|
{ malId != null && librarySyncData?.get(AccountManager.malApi.idPrefix) == malId },
|
||||||
|
{ aniListId != null && librarySyncData?.get(AccountManager.aniListApi.idPrefix) == aniListId },
|
||||||
|
{ normalizedName == normalizeString(it.name) && year == it.year }
|
||||||
|
)
|
||||||
|
|
||||||
|
checks.any { it() }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (duplicateEntries.isEmpty() || context == null) {
|
||||||
|
checkDuplicatesCallback.invoke(true, emptyList())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val replaceMessage = if (duplicateEntries.size > 1) {
|
||||||
|
R.string.duplicate_replace_all
|
||||||
|
} else R.string.duplicate_replace
|
||||||
|
|
||||||
|
val message = if (duplicateEntries.size == 1) {
|
||||||
|
val list = when (listType) {
|
||||||
|
LibraryListType.BOOKMARKS -> getResultWatchState(duplicateEntries[0].id ?: 0).stringRes
|
||||||
|
LibraryListType.FAVORITES -> R.string.favorites_list_name
|
||||||
|
LibraryListType.SUBSCRIPTIONS -> R.string.subscription_list_name
|
||||||
|
}
|
||||||
|
|
||||||
|
context.getString(R.string.duplicate_message_single,
|
||||||
|
"${normalizeString(duplicateEntries[0].name)} (${context.getString(list)}) — ${duplicateEntries[0].apiName}"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
val bulletPoints = duplicateEntries.joinToString("\n") {
|
||||||
|
val list = when (listType) {
|
||||||
|
LibraryListType.BOOKMARKS -> getResultWatchState(it.id ?: 0).stringRes
|
||||||
|
LibraryListType.FAVORITES -> R.string.favorites_list_name
|
||||||
|
LibraryListType.SUBSCRIPTIONS -> R.string.subscription_list_name
|
||||||
|
}
|
||||||
|
|
||||||
|
"• ${it.apiName}: ${normalizeString(it.name)} (${context.getString(list)})"
|
||||||
|
}
|
||||||
|
|
||||||
|
context.getString(R.string.duplicate_message_multiple, bulletPoints)
|
||||||
|
}
|
||||||
|
|
||||||
|
val builder: AlertDialog.Builder = AlertDialog.Builder(context)
|
||||||
|
|
||||||
|
val dialogClickListener =
|
||||||
|
DialogInterface.OnClickListener { _, which ->
|
||||||
|
when (which) {
|
||||||
|
DialogInterface.BUTTON_POSITIVE -> {
|
||||||
|
checkDuplicatesCallback.invoke(true, emptyList())
|
||||||
|
}
|
||||||
|
DialogInterface.BUTTON_NEGATIVE -> {
|
||||||
|
checkDuplicatesCallback.invoke(false, emptyList())
|
||||||
|
}
|
||||||
|
DialogInterface.BUTTON_NEUTRAL -> {
|
||||||
|
checkDuplicatesCallback.invoke(true, duplicateEntries.map { it.id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.setTitle(R.string.duplicate_title)
|
||||||
|
.setMessage(message)
|
||||||
|
.setPositiveButton(R.string.duplicate_add, dialogClickListener)
|
||||||
|
.setNegativeButton(R.string.duplicate_cancel, dialogClickListener)
|
||||||
|
.setNeutralButton(replaceMessage, dialogClickListener)
|
||||||
|
.show().setDefaultFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getImdbIdFromSyncData(syncData: Map<String, String>?): String? {
|
||||||
|
return normalSafeApiCall {
|
||||||
|
SimklApi.readIdFromString(
|
||||||
|
syncData?.get(AccountManager.simklApi.idPrefix)
|
||||||
|
)[SimklApi.Companion.SyncServices.Imdb]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getTMDbIdFromSyncData(syncData: Map<String, String>?): String? {
|
||||||
|
return normalSafeApiCall {
|
||||||
|
SimklApi.readIdFromString(
|
||||||
|
syncData?.get(AccountManager.simklApi.idPrefix)
|
||||||
|
)[SimklApi.Companion.SyncServices.Tmdb]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startChromecast(
|
private fun startChromecast(
|
||||||
|
@ -1219,7 +1512,7 @@ class ResultViewModel2 : ViewModel() {
|
||||||
// Do not add mark as watched on movies
|
// Do not add mark as watched on movies
|
||||||
if (!listOf(TvType.Movie, TvType.AnimeMovie).contains(click.data.tvType)) {
|
if (!listOf(TvType.Movie, TvType.AnimeMovie).contains(click.data.tvType)) {
|
||||||
val isWatched =
|
val isWatched =
|
||||||
DataStoreHelper.getVideoWatchState(click.data.id) == VideoWatchState.Watched
|
getVideoWatchState(click.data.id) == VideoWatchState.Watched
|
||||||
|
|
||||||
val watchedText = if (isWatched) R.string.action_remove_from_watched
|
val watchedText = if (isWatched) R.string.action_remove_from_watched
|
||||||
else R.string.action_mark_as_watched
|
else R.string.action_mark_as_watched
|
||||||
|
@ -1468,12 +1761,12 @@ class ResultViewModel2 : ViewModel() {
|
||||||
|
|
||||||
ACTION_MARK_AS_WATCHED -> {
|
ACTION_MARK_AS_WATCHED -> {
|
||||||
val isWatched =
|
val isWatched =
|
||||||
DataStoreHelper.getVideoWatchState(click.data.id) == VideoWatchState.Watched
|
getVideoWatchState(click.data.id) == VideoWatchState.Watched
|
||||||
|
|
||||||
if (isWatched) {
|
if (isWatched) {
|
||||||
DataStoreHelper.setVideoWatchState(click.data.id, VideoWatchState.None)
|
setVideoWatchState(click.data.id, VideoWatchState.None)
|
||||||
} else {
|
} else {
|
||||||
DataStoreHelper.setVideoWatchState(click.data.id, VideoWatchState.Watched)
|
setVideoWatchState(click.data.id, VideoWatchState.Watched)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kinda dirty to reload all episodes :(
|
// Kinda dirty to reload all episodes :(
|
||||||
|
@ -1682,7 +1975,7 @@ class ResultViewModel2 : ViewModel() {
|
||||||
list.subList(start, end).map {
|
list.subList(start, end).map {
|
||||||
val posDur = getViewPos(it.id)
|
val posDur = getViewPos(it.id)
|
||||||
val watchState =
|
val watchState =
|
||||||
DataStoreHelper.getVideoWatchState(it.id) ?: VideoWatchState.None
|
getVideoWatchState(it.id) ?: VideoWatchState.None
|
||||||
it.copy(
|
it.copy(
|
||||||
position = posDur?.position ?: 0,
|
position = posDur?.position ?: 0,
|
||||||
duration = posDur?.duration ?: 0,
|
duration = posDur?.duration ?: 0,
|
||||||
|
@ -1743,13 +2036,19 @@ class ResultViewModel2 : ViewModel() {
|
||||||
private fun postSubscription(loadResponse: LoadResponse) {
|
private fun postSubscription(loadResponse: LoadResponse) {
|
||||||
if (loadResponse.isEpisodeBased()) {
|
if (loadResponse.isEpisodeBased()) {
|
||||||
val id = loadResponse.getId()
|
val id = loadResponse.getId()
|
||||||
val data = DataStoreHelper.getSubscribedData(id)
|
val data = getSubscribedData(id)
|
||||||
DataStoreHelper.updateSubscribedData(id, data, loadResponse as? EpisodeResponse)
|
updateSubscribedData(id, data, loadResponse as? EpisodeResponse)
|
||||||
val isSubscribed = data != null
|
val isSubscribed = data != null
|
||||||
_subscribeStatus.postValue(isSubscribed)
|
_subscribeStatus.postValue(isSubscribed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun postFavorites(loadResponse: LoadResponse) {
|
||||||
|
val id = loadResponse.getId()
|
||||||
|
val isFavorite = getFavoritesData(id) != null
|
||||||
|
_favoriteStatus.postValue(isFavorite)
|
||||||
|
}
|
||||||
|
|
||||||
private fun postEpisodeRange(indexer: EpisodeIndexer?, range: EpisodeRange?) {
|
private fun postEpisodeRange(indexer: EpisodeIndexer?, range: EpisodeRange?) {
|
||||||
if (range == null || indexer == null) {
|
if (range == null || indexer == null) {
|
||||||
return
|
return
|
||||||
|
@ -1887,6 +2186,7 @@ class ResultViewModel2 : ViewModel() {
|
||||||
currentResponse = loadResponse
|
currentResponse = loadResponse
|
||||||
postPage(loadResponse, apiRepository)
|
postPage(loadResponse, apiRepository)
|
||||||
postSubscription(loadResponse)
|
postSubscription(loadResponse)
|
||||||
|
postFavorites(loadResponse)
|
||||||
if (updateEpisodes)
|
if (updateEpisodes)
|
||||||
postEpisodes(loadResponse, updateFillers)
|
postEpisodes(loadResponse, updateFillers)
|
||||||
}
|
}
|
||||||
|
@ -1915,6 +2215,10 @@ class ResultViewModel2 : ViewModel() {
|
||||||
val id =
|
val id =
|
||||||
mainId + episode + idIndex * 1_000_000 + (i.season?.times(10_000)
|
mainId + episode + idIndex * 1_000_000 + (i.season?.times(10_000)
|
||||||
?: 0)
|
?: 0)
|
||||||
|
|
||||||
|
val totalIndex =
|
||||||
|
i.season?.let { season -> loadResponse.getTotalEpisodeIndex(episode, season) }
|
||||||
|
|
||||||
if (!existingEpisodes.contains(id)) {
|
if (!existingEpisodes.contains(id)) {
|
||||||
existingEpisodes.add(id)
|
existingEpisodes.add(id)
|
||||||
val seasonData = loadResponse.seasonNames.getSeason(i.season)
|
val seasonData = loadResponse.seasonNames.getSeason(i.season)
|
||||||
|
@ -1934,7 +2238,8 @@ class ResultViewModel2 : ViewModel() {
|
||||||
i.description,
|
i.description,
|
||||||
fillers.getOrDefault(episode, false),
|
fillers.getOrDefault(episode, false),
|
||||||
loadResponse.type,
|
loadResponse.type,
|
||||||
mainId
|
mainId,
|
||||||
|
totalIndex
|
||||||
)
|
)
|
||||||
|
|
||||||
val season = eps.seasonIndex ?: 0
|
val season = eps.seasonIndex ?: 0
|
||||||
|
@ -1963,6 +2268,9 @@ class ResultViewModel2 : ViewModel() {
|
||||||
val seasonData =
|
val seasonData =
|
||||||
loadResponse.seasonNames.getSeason(episode.season)
|
loadResponse.seasonNames.getSeason(episode.season)
|
||||||
|
|
||||||
|
val totalIndex =
|
||||||
|
episode.season?.let { season -> loadResponse.getTotalEpisodeIndex(episodeIndex, season) }
|
||||||
|
|
||||||
val ep =
|
val ep =
|
||||||
buildResultEpisode(
|
buildResultEpisode(
|
||||||
loadResponse.name,
|
loadResponse.name,
|
||||||
|
@ -1979,7 +2287,8 @@ class ResultViewModel2 : ViewModel() {
|
||||||
episode.description,
|
episode.description,
|
||||||
null,
|
null,
|
||||||
loadResponse.type,
|
loadResponse.type,
|
||||||
mainId
|
mainId,
|
||||||
|
totalIndex
|
||||||
)
|
)
|
||||||
|
|
||||||
val season = ep.seasonIndex ?: 0
|
val season = ep.seasonIndex ?: 0
|
||||||
|
@ -2010,7 +2319,8 @@ class ResultViewModel2 : ViewModel() {
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
loadResponse.type,
|
loadResponse.type,
|
||||||
mainId
|
mainId,
|
||||||
|
null
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -2032,7 +2342,8 @@ class ResultViewModel2 : ViewModel() {
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
loadResponse.type,
|
loadResponse.type,
|
||||||
mainId
|
mainId,
|
||||||
|
null
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -2054,7 +2365,8 @@ class ResultViewModel2 : ViewModel() {
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
loadResponse.type,
|
loadResponse.type,
|
||||||
mainId
|
mainId,
|
||||||
|
null
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -2115,13 +2427,13 @@ class ResultViewModel2 : ViewModel() {
|
||||||
postResume()
|
postResume()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun postResume() {
|
private fun postResume() {
|
||||||
_resumeWatching.postValue(resume())
|
_resumeWatching.postValue(resume())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resume(): ResumeWatchingStatus? {
|
private fun resume(): ResumeWatchingStatus? {
|
||||||
val correctId = currentId ?: return null
|
val correctId = currentId ?: return null
|
||||||
val resume = DataStoreHelper.getLastWatched(correctId)
|
val resume = getLastWatched(correctId)
|
||||||
val resumeParentId = resume?.parentId
|
val resumeParentId = resume?.parentId
|
||||||
if (resumeParentId != correctId) return null // is null or smth went wrong with getLastWatched
|
if (resumeParentId != correctId) return null // is null or smth went wrong with getLastWatched
|
||||||
val resumeId = resume.episodeId ?: return null// invalid episode id
|
val resumeId = resume.episodeId ?: return null// invalid episode id
|
||||||
|
|
|
@ -3,6 +3,7 @@ package com.lagradost.cloudstream3.ui.search
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.TypedValue
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
@ -231,9 +232,15 @@ class SearchFragment : Fragment() {
|
||||||
val searchExitIcon =
|
val searchExitIcon =
|
||||||
binding?.mainSearch?.findViewById<ImageView>(androidx.appcompat.R.id.search_close_btn)
|
binding?.mainSearch?.findViewById<ImageView>(androidx.appcompat.R.id.search_close_btn)
|
||||||
// val searchMagIcon =
|
// val searchMagIcon =
|
||||||
// main_search.findViewById<ImageView>(androidx.appcompat.R.id.search_mag_icon)
|
// binding?.mainSearch?.findViewById<ImageView>(androidx.appcompat.R.id.search_mag_icon)
|
||||||
//searchMagIcon.scaleX = 0.65f
|
// searchMagIcon.scaleX = 0.65f
|
||||||
//searchMagIcon.scaleY = 0.65f
|
// searchMagIcon.scaleY = 0.65f
|
||||||
|
|
||||||
|
// Set the color for the search exit icon to the correct theme text color
|
||||||
|
val searchExitIconColor = TypedValue()
|
||||||
|
|
||||||
|
activity?.theme?.resolveAttribute(android.R.attr.textColor, searchExitIconColor, true)
|
||||||
|
searchExitIcon?.setColorFilter(searchExitIconColor.data)
|
||||||
|
|
||||||
selectedApis = DataStoreHelper.searchPreferenceProviders.toMutableSet()
|
selectedApis = DataStoreHelper.searchPreferenceProviders.toMutableSet()
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ import com.lagradost.cloudstream3.syncproviders.OAuth2API
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
||||||
|
@ -248,6 +249,7 @@ class SettingsAccount : PreferenceFragmentCompat() {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
setUpToolbar(R.string.category_account)
|
setUpToolbar(R.string.category_account)
|
||||||
setPaddingBottom()
|
setPaddingBottom()
|
||||||
|
setToolBarScrollFlags()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
|
|
@ -12,10 +12,12 @@ import android.widget.ImageView
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.core.view.children
|
import androidx.core.view.children
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
import com.google.android.material.appbar.MaterialToolbar
|
import com.google.android.material.appbar.MaterialToolbar
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.databinding.MainSettingsBinding
|
import com.lagradost.cloudstream3.databinding.MainSettingsBinding
|
||||||
|
@ -54,7 +56,24 @@ class SettingsFragment : Fragment() {
|
||||||
listView?.setPadding(0, 0, 0, 100.toPx)
|
listView?.setPadding(0, 0, 0, 100.toPx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fun PreferenceFragmentCompat.setToolBarScrollFlags() {
|
||||||
|
if (isTvSettings()) {
|
||||||
|
val settingsAppbar = view?.findViewById<MaterialToolbar>(R.id.settings_toolbar)
|
||||||
|
|
||||||
|
settingsAppbar?.updateLayoutParams<AppBarLayout.LayoutParams> {
|
||||||
|
scrollFlags = AppBarLayout.LayoutParams.SCROLL_FLAG_NO_SCROLL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun Fragment?.setToolBarScrollFlags() {
|
||||||
|
if (isTvSettings()) {
|
||||||
|
val settingsAppbar = this?.view?.findViewById<MaterialToolbar>(R.id.settings_toolbar)
|
||||||
|
|
||||||
|
settingsAppbar?.updateLayoutParams<AppBarLayout.LayoutParams> {
|
||||||
|
scrollFlags = AppBarLayout.LayoutParams.SCROLL_FLAG_NO_SCROLL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
fun Fragment?.setUpToolbar(title: String) {
|
fun Fragment?.setUpToolbar(title: String) {
|
||||||
if (this == null) return
|
if (this == null) return
|
||||||
val settingsToolbar = view?.findViewById<MaterialToolbar>(R.id.settings_toolbar) ?: return
|
val settingsToolbar = view?.findViewById<MaterialToolbar>(R.id.settings_toolbar) ?: return
|
||||||
|
@ -63,7 +82,7 @@ class SettingsFragment : Fragment() {
|
||||||
setTitle(title)
|
setTitle(title)
|
||||||
setNavigationIcon(R.drawable.ic_baseline_arrow_back_24)
|
setNavigationIcon(R.drawable.ic_baseline_arrow_back_24)
|
||||||
setNavigationOnClickListener {
|
setNavigationOnClickListener {
|
||||||
activity?.onBackPressed()
|
activity?.onBackPressedDispatcher?.onBackPressed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fixPaddingStatusbar(settingsToolbar)
|
fixPaddingStatusbar(settingsToolbar)
|
||||||
|
@ -78,7 +97,7 @@ class SettingsFragment : Fragment() {
|
||||||
setNavigationIcon(R.drawable.ic_baseline_arrow_back_24)
|
setNavigationIcon(R.drawable.ic_baseline_arrow_back_24)
|
||||||
children.firstOrNull { it is ImageView }?.tag = getString(R.string.tv_no_focus_tag)
|
children.firstOrNull { it is ImageView }?.tag = getString(R.string.tv_no_focus_tag)
|
||||||
setNavigationOnClickListener {
|
setNavigationOnClickListener {
|
||||||
activity?.onBackPressed()
|
activity?.onBackPressedDispatcher?.onBackPressed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fixPaddingStatusbar(settingsToolbar)
|
fixPaddingStatusbar(settingsToolbar)
|
||||||
|
@ -187,13 +206,13 @@ class SettingsFragment : Fragment() {
|
||||||
}
|
}
|
||||||
binding?.apply {
|
binding?.apply {
|
||||||
listOf(
|
listOf(
|
||||||
settingsGeneral to R.id.action_navigation_settings_to_navigation_settings_general,
|
settingsGeneral to R.id.action_navigation_global_to_navigation_settings_general,
|
||||||
settingsPlayer to R.id.action_navigation_settings_to_navigation_settings_player,
|
settingsPlayer to R.id.action_navigation_global_to_navigation_settings_player,
|
||||||
settingsCredits to R.id.action_navigation_settings_to_navigation_settings_account,
|
settingsCredits to R.id.action_navigation_global_to_navigation_settings_account,
|
||||||
settingsUi to R.id.action_navigation_settings_to_navigation_settings_ui,
|
settingsUi to R.id.action_navigation_global_to_navigation_settings_ui,
|
||||||
settingsProviders to R.id.action_navigation_settings_to_navigation_settings_providers,
|
settingsProviders to R.id.action_navigation_global_to_navigation_settings_providers,
|
||||||
settingsUpdates to R.id.action_navigation_settings_to_navigation_settings_updates,
|
settingsUpdates to R.id.action_navigation_global_to_navigation_settings_updates,
|
||||||
settingsExtensions to R.id.action_navigation_settings_to_navigation_settings_extensions,
|
settingsExtensions to R.id.action_navigation_global_to_navigation_settings_extensions,
|
||||||
).forEach { (view, navigationId) ->
|
).forEach { (view, navigationId) ->
|
||||||
view.apply {
|
view.apply {
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
|
|
|
@ -28,6 +28,7 @@ import com.lagradost.cloudstream3.network.initClient
|
||||||
import com.lagradost.cloudstream3.ui.EasterEggMonke
|
import com.lagradost.cloudstream3.ui.EasterEggMonke
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
|
||||||
|
@ -115,6 +116,7 @@ class SettingsGeneral : PreferenceFragmentCompat() {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
setUpToolbar(R.string.category_general)
|
setUpToolbar(R.string.category_general)
|
||||||
setPaddingBottom()
|
setPaddingBottom()
|
||||||
|
setToolBarScrollFlags()
|
||||||
}
|
}
|
||||||
|
|
||||||
data class CustomSite(
|
data class CustomSite(
|
||||||
|
@ -192,7 +194,6 @@ class SettingsGeneral : PreferenceFragmentCompat() {
|
||||||
return@setOnPreferenceClickListener true
|
return@setOnPreferenceClickListener true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun showAdd() {
|
fun showAdd() {
|
||||||
val providers = synchronized(allProviders) { allProviders.distinctBy { it.javaClass }.sortedBy { it.name } }
|
val providers = synchronized(allProviders) { allProviders.distinctBy { it.javaClass }.sortedBy { it.name } }
|
||||||
activity?.showDialog(
|
activity?.showDialog(
|
||||||
|
|
|
@ -10,6 +10,7 @@ import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getFolderSize
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getFolderSize
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
||||||
import com.lagradost.cloudstream3.ui.subtitles.ChromecastSubtitlesFragment
|
import com.lagradost.cloudstream3.ui.subtitles.ChromecastSubtitlesFragment
|
||||||
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment
|
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment
|
||||||
|
@ -23,6 +24,7 @@ class SettingsPlayer : PreferenceFragmentCompat() {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
setUpToolbar(R.string.category_player)
|
setUpToolbar(R.string.category_player)
|
||||||
setPaddingBottom()
|
setPaddingBottom()
|
||||||
|
setToolBarScrollFlags()
|
||||||
}
|
}
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
hideKeyboard()
|
hideKeyboard()
|
||||||
|
|
|
@ -13,6 +13,7 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
|
||||||
import com.lagradost.cloudstream3.ui.APIRepository
|
import com.lagradost.cloudstream3.ui.APIRepository
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
|
||||||
|
@ -25,6 +26,7 @@ class SettingsProviders : PreferenceFragmentCompat() {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
setUpToolbar(R.string.category_providers)
|
setUpToolbar(R.string.category_providers)
|
||||||
setPaddingBottom()
|
setPaddingBottom()
|
||||||
|
setToolBarScrollFlags()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchResultBuilder
|
import com.lagradost.cloudstream3.ui.search.SearchResultBuilder
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.updateTv
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.updateTv
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
||||||
|
@ -23,6 +24,7 @@ class SettingsUI : PreferenceFragmentCompat() {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
setUpToolbar(R.string.category_ui)
|
setUpToolbar(R.string.category_ui)
|
||||||
setPaddingBottom()
|
setPaddingBottom()
|
||||||
|
setToolBarScrollFlags()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
|
|
@ -22,6 +22,7 @@ import com.lagradost.cloudstream3.network.initClient
|
||||||
import com.lagradost.cloudstream3.services.BackupWorkManager
|
import com.lagradost.cloudstream3.services.BackupWorkManager
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
||||||
import com.lagradost.cloudstream3.utils.BackupUtils
|
import com.lagradost.cloudstream3.utils.BackupUtils
|
||||||
import com.lagradost.cloudstream3.utils.BackupUtils.restorePrompt
|
import com.lagradost.cloudstream3.utils.BackupUtils.restorePrompt
|
||||||
|
@ -42,6 +43,7 @@ class SettingsUpdates : PreferenceFragmentCompat() {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
setUpToolbar(R.string.category_updates)
|
setUpToolbar(R.string.category_updates)
|
||||||
setPaddingBottom()
|
setPaddingBottom()
|
||||||
|
setToolBarScrollFlags()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
|
|
@ -30,6 +30,7 @@ import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
|
||||||
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
||||||
import com.lagradost.cloudstream3.ui.result.setText
|
import com.lagradost.cloudstream3.ui.result.setText
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.downloadAllPluginsDialog
|
import com.lagradost.cloudstream3.utils.AppUtils.downloadAllPluginsDialog
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
|
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
|
||||||
|
@ -85,7 +86,7 @@ class ExtensionsFragment : Fragment() {
|
||||||
//context?.fixPaddingStatusbar(extensions_root)
|
//context?.fixPaddingStatusbar(extensions_root)
|
||||||
|
|
||||||
setUpToolbar(R.string.extensions)
|
setUpToolbar(R.string.extensions)
|
||||||
|
setToolBarScrollFlags()
|
||||||
|
|
||||||
binding?.repoRecyclerView?.apply {
|
binding?.repoRecyclerView?.apply {
|
||||||
setLinearListLayout(
|
setLinearListLayout(
|
||||||
|
|
|
@ -21,7 +21,6 @@ import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueT
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.html
|
import com.lagradost.cloudstream3.utils.AppUtils.html
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||||
import com.lagradost.cloudstream3.utils.GlideApp
|
|
||||||
import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage
|
import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage
|
||||||
import com.lagradost.cloudstream3.utils.SubtitleHelper.getFlagFromIso
|
import com.lagradost.cloudstream3.utils.SubtitleHelper.getFlagFromIso
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||||
|
@ -87,7 +86,7 @@ class PluginAdapter(
|
||||||
override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
|
override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
|
||||||
if (holder is PluginViewHolder) {
|
if (holder is PluginViewHolder) {
|
||||||
holder.binding.entryIcon.let { pluginIcon ->
|
holder.binding.entryIcon.let { pluginIcon ->
|
||||||
GlideApp.with(pluginIcon).clear(pluginIcon)
|
com.bumptech.glide.Glide.with(pluginIcon).clear(pluginIcon)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.onViewRecycled(holder)
|
super.onViewRecycled(holder)
|
||||||
|
|
|
@ -18,6 +18,7 @@ import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.bindChips
|
||||||
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
|
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
|
||||||
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
||||||
import com.lagradost.cloudstream3.ui.settings.appLanguages
|
import com.lagradost.cloudstream3.ui.settings.appLanguages
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
|
||||||
|
@ -69,10 +70,11 @@ class PluginsFragment : Fragment() {
|
||||||
val isLocal = arguments?.getBoolean(PLUGINS_BUNDLE_LOCAL) == true
|
val isLocal = arguments?.getBoolean(PLUGINS_BUNDLE_LOCAL) == true
|
||||||
|
|
||||||
if (url == null || name == null) {
|
if (url == null || name == null) {
|
||||||
activity?.onBackPressed()
|
activity?.onBackPressedDispatcher?.onBackPressed()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setToolBarScrollFlags()
|
||||||
setUpToolbar(name)
|
setUpToolbar(name)
|
||||||
binding?.settingsToolbar?.apply {
|
binding?.settingsToolbar?.apply {
|
||||||
setOnMenuItemClickListener { menuItem ->
|
setOnMenuItemClickListener { menuItem ->
|
||||||
|
@ -117,7 +119,7 @@ class PluginsFragment : Fragment() {
|
||||||
if (searchView?.isIconified == false) {
|
if (searchView?.isIconified == false) {
|
||||||
searchView.isIconified = true
|
searchView.isIconified = true
|
||||||
} else {
|
} else {
|
||||||
activity?.onBackPressed()
|
activity?.onBackPressedDispatcher?.onBackPressed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
searchView?.setOnQueryTextFocusChangeListener { _, hasFocus ->
|
searchView?.setOnQueryTextFocusChangeListener { _, hasFocus ->
|
||||||
|
|
|
@ -12,6 +12,7 @@ import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||||
import com.lagradost.cloudstream3.mvvm.observe
|
import com.lagradost.cloudstream3.mvvm.observe
|
||||||
import com.lagradost.cloudstream3.mvvm.observeNullable
|
import com.lagradost.cloudstream3.mvvm.observeNullable
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,6 +28,7 @@ class TestFragment : Fragment() {
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
setUpToolbar(R.string.category_provider_test)
|
setUpToolbar(R.string.category_provider_test)
|
||||||
|
setToolBarScrollFlags()
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
binding?.apply {
|
binding?.apply {
|
||||||
|
|
|
@ -583,7 +583,7 @@ object AppUtils {
|
||||||
//private val viewModel: ResultViewModel by activityViewModels()
|
//private val viewModel: ResultViewModel by activityViewModels()
|
||||||
|
|
||||||
private fun getResultsId(): Int {
|
private fun getResultsId(): Int {
|
||||||
return if (isTrueTvSettings()) {
|
return if (isTvSettings()) {
|
||||||
R.id.global_to_navigation_results_tv
|
R.id.global_to_navigation_results_tv
|
||||||
} else {
|
} else {
|
||||||
R.id.global_to_navigation_results_phone
|
R.id.global_to_navigation_results_phone
|
||||||
|
|
|
@ -1,14 +1,7 @@
|
||||||
package com.lagradost.cloudstream3.utils
|
package com.lagradost.cloudstream3.utils
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.DialogInterface
|
|
||||||
import android.text.Editable
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.core.view.isGone
|
|
||||||
import androidx.core.widget.doOnTextChanged
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia
|
import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia
|
||||||
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
|
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
|
||||||
|
@ -19,21 +12,12 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys
|
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||||
import com.lagradost.cloudstream3.CommonActivity.showToast
|
import com.lagradost.cloudstream3.CommonActivity.showToast
|
||||||
import com.lagradost.cloudstream3.databinding.WhoIsWatchingAccountEditBinding
|
|
||||||
import com.lagradost.cloudstream3.databinding.WhoIsWatchingBinding
|
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
|
||||||
import com.lagradost.cloudstream3.syncproviders.AccountManager
|
import com.lagradost.cloudstream3.syncproviders.AccountManager
|
||||||
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
||||||
import com.lagradost.cloudstream3.ui.WatchType
|
import com.lagradost.cloudstream3.ui.WatchType
|
||||||
import com.lagradost.cloudstream3.ui.WhoIsWatchingAdapter
|
|
||||||
import com.lagradost.cloudstream3.ui.library.ListSorting
|
import com.lagradost.cloudstream3.ui.library.ListSorting
|
||||||
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
|
|
||||||
import com.lagradost.cloudstream3.ui.result.UiImage
|
import com.lagradost.cloudstream3.ui.result.UiImage
|
||||||
import com.lagradost.cloudstream3.ui.result.VideoWatchState
|
import com.lagradost.cloudstream3.ui.result.VideoWatchState
|
||||||
import com.lagradost.cloudstream3.ui.result.setImage
|
|
||||||
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
|
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
@ -42,6 +26,7 @@ const val VIDEO_WATCH_STATE = "video_watch_state"
|
||||||
const val RESULT_WATCH_STATE = "result_watch_state"
|
const val RESULT_WATCH_STATE = "result_watch_state"
|
||||||
const val RESULT_WATCH_STATE_DATA = "result_watch_state_data"
|
const val RESULT_WATCH_STATE_DATA = "result_watch_state_data"
|
||||||
const val RESULT_SUBSCRIBED_STATE_DATA = "result_subscribed_state_data"
|
const val RESULT_SUBSCRIBED_STATE_DATA = "result_subscribed_state_data"
|
||||||
|
const val RESULT_FAVORITES_STATE_DATA = "result_favorites_state_data"
|
||||||
const val RESULT_RESUME_WATCHING = "result_resume_watching_2" // changed due to id changes
|
const val RESULT_RESUME_WATCHING = "result_resume_watching_2" // changed due to id changes
|
||||||
const val RESULT_RESUME_WATCHING_OLD = "result_resume_watching"
|
const val RESULT_RESUME_WATCHING_OLD = "result_resume_watching"
|
||||||
const val RESULT_RESUME_WATCHING_HAS_MIGRATED = "result_resume_watching_migrated"
|
const val RESULT_RESUME_WATCHING_HAS_MIGRATED = "result_resume_watching_migrated"
|
||||||
|
@ -73,7 +58,7 @@ class UserPreferenceDelegate<T : Any>(
|
||||||
|
|
||||||
object DataStoreHelper {
|
object DataStoreHelper {
|
||||||
// be aware, don't change the index of these as Account uses the index for the art
|
// be aware, don't change the index of these as Account uses the index for the art
|
||||||
private val profileImages = arrayOf(
|
val profileImages = arrayOf(
|
||||||
R.drawable.profile_bg_dark_blue,
|
R.drawable.profile_bg_dark_blue,
|
||||||
R.drawable.profile_bg_blue,
|
R.drawable.profile_bg_blue,
|
||||||
R.drawable.profile_bg_orange,
|
R.drawable.profile_bg_orange,
|
||||||
|
@ -135,6 +120,8 @@ object DataStoreHelper {
|
||||||
val customImage: String? = null,
|
val customImage: String? = null,
|
||||||
@JsonProperty("defaultImageIndex")
|
@JsonProperty("defaultImageIndex")
|
||||||
val defaultImageIndex: Int,
|
val defaultImageIndex: Int,
|
||||||
|
@JsonProperty("lockPin")
|
||||||
|
val lockPin: String? = null,
|
||||||
) {
|
) {
|
||||||
val image: UiImage
|
val image: UiImage
|
||||||
get() = customImage?.let { UiImage.Image(it) } ?: UiImage.Drawable(
|
get() = customImage?.let { UiImage.Image(it) } ?: UiImage.Drawable(
|
||||||
|
@ -143,7 +130,7 @@ object DataStoreHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
const val TAG = "data_store_helper"
|
const val TAG = "data_store_helper"
|
||||||
private var accounts by PreferenceDelegate("$TAG/account", arrayOf<Account>())
|
var accounts by PreferenceDelegate("$TAG/account", arrayOf<Account>())
|
||||||
var selectedKeyIndex by PreferenceDelegate("$TAG/account_key_index", 0)
|
var selectedKeyIndex by PreferenceDelegate("$TAG/account_key_index", 0)
|
||||||
val currentAccount: String get() = selectedKeyIndex.toString()
|
val currentAccount: String get() = selectedKeyIndex.toString()
|
||||||
|
|
||||||
|
@ -162,106 +149,21 @@ object DataStoreHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setAccount(account: Account, refreshHomePage: Boolean) {
|
fun setAccount(account: Account) {
|
||||||
|
val homepage = currentHomePage
|
||||||
|
|
||||||
selectedKeyIndex = account.keyIndex
|
selectedKeyIndex = account.keyIndex
|
||||||
showToast(account.name)
|
showToast(context?.getString(R.string.logged_account, account.name) ?: account.name)
|
||||||
MainActivity.bookmarksUpdatedEvent(true)
|
MainActivity.bookmarksUpdatedEvent(true)
|
||||||
if (refreshHomePage) {
|
MainActivity.reloadLibraryEvent(true)
|
||||||
|
val oldAccount = accounts.find { it.keyIndex == account.keyIndex }
|
||||||
|
if (oldAccount != null && currentHomePage != homepage) {
|
||||||
|
// This is not a new account, and the homepage has changed, reload it
|
||||||
MainActivity.reloadHomeEvent(true)
|
MainActivity.reloadHomeEvent(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun editAccount(context: Context, account: Account, isNewAccount: Boolean) {
|
fun getDefaultAccount(context: Context): Account {
|
||||||
val binding =
|
|
||||||
WhoIsWatchingAccountEditBinding.inflate(LayoutInflater.from(context), null, false)
|
|
||||||
val builder =
|
|
||||||
AlertDialog.Builder(context, R.style.AlertDialogCustom)
|
|
||||||
.setView(binding.root)
|
|
||||||
|
|
||||||
var currentEditAccount = account
|
|
||||||
val dialog = builder.show()
|
|
||||||
binding.accountName.text = Editable.Factory.getInstance()?.newEditable(account.name)
|
|
||||||
binding.accountName.doOnTextChanged { text, _, _, _ ->
|
|
||||||
currentEditAccount = currentEditAccount.copy(name = text?.toString() ?: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.deleteBtt.isGone = isNewAccount
|
|
||||||
binding.deleteBtt.setOnClickListener {
|
|
||||||
val dialogClickListener =
|
|
||||||
DialogInterface.OnClickListener { _, which ->
|
|
||||||
when (which) {
|
|
||||||
DialogInterface.BUTTON_POSITIVE -> {
|
|
||||||
// remove all keys as well as the account, note that default wont get
|
|
||||||
// deleted from currentAccounts, as it is not part of "accounts",
|
|
||||||
// but the watch keys will
|
|
||||||
removeKeys(account.keyIndex.toString())
|
|
||||||
val currentAccounts = accounts.toMutableList()
|
|
||||||
currentAccounts.removeIf { it.keyIndex == account.keyIndex }
|
|
||||||
accounts = currentAccounts.toTypedArray()
|
|
||||||
|
|
||||||
// update UI
|
|
||||||
setAccount(getDefaultAccount(context), true)
|
|
||||||
dialog?.dismissSafe()
|
|
||||||
}
|
|
||||||
|
|
||||||
DialogInterface.BUTTON_NEGATIVE -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
AlertDialog.Builder(context).setTitle(R.string.delete).setMessage(
|
|
||||||
context.getString(R.string.delete_message).format(
|
|
||||||
currentEditAccount.name
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.setPositiveButton(R.string.delete, dialogClickListener)
|
|
||||||
.setNegativeButton(R.string.cancel, dialogClickListener)
|
|
||||||
.show().setDefaultFocus()
|
|
||||||
} catch (t: Throwable) {
|
|
||||||
logError(t)
|
|
||||||
// ye you somehow fucked up formatting did you?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.cancelBtt.setOnClickListener {
|
|
||||||
dialog?.dismissSafe()
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.profilePic.setImage(account.image)
|
|
||||||
binding.profilePic.setOnClickListener {
|
|
||||||
// rolls the image forwards once
|
|
||||||
currentEditAccount =
|
|
||||||
currentEditAccount.copy(defaultImageIndex = (currentEditAccount.defaultImageIndex + 1) % profileImages.size)
|
|
||||||
binding.profilePic.setImage(currentEditAccount.image)
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.applyBtt.setOnClickListener {
|
|
||||||
val currentAccounts = accounts.toMutableList()
|
|
||||||
|
|
||||||
val overrideIndex =
|
|
||||||
currentAccounts.indexOfFirst { it.keyIndex == currentEditAccount.keyIndex }
|
|
||||||
|
|
||||||
// if an account is found that has the same keyIndex then override that one, if not then append it
|
|
||||||
if (overrideIndex != -1) {
|
|
||||||
currentAccounts[overrideIndex] = currentEditAccount
|
|
||||||
} else {
|
|
||||||
currentAccounts.add(currentEditAccount)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the current homepage for new accounts
|
|
||||||
val currentHomePage = DataStoreHelper.currentHomePage
|
|
||||||
|
|
||||||
// set the new default account as well as add the key for the new account
|
|
||||||
setAccount(currentEditAccount, false)
|
|
||||||
DataStoreHelper.currentHomePage = currentHomePage
|
|
||||||
|
|
||||||
accounts = currentAccounts.toTypedArray()
|
|
||||||
|
|
||||||
dialog.dismissSafe()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getDefaultAccount(context: Context): Account {
|
|
||||||
return accounts.let { currentAccounts ->
|
return accounts.let { currentAccounts ->
|
||||||
currentAccounts.getOrNull(currentAccounts.indexOfFirst { it.keyIndex == 0 }) ?: Account(
|
currentAccounts.getOrNull(currentAccounts.indexOfFirst { it.keyIndex == 0 }) ?: Account(
|
||||||
keyIndex = 0,
|
keyIndex = 0,
|
||||||
|
@ -271,69 +173,14 @@ object DataStoreHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showWhoIsWatching(context: Context) {
|
fun getAccounts(context: Context): List<Account> {
|
||||||
val binding: WhoIsWatchingBinding = WhoIsWatchingBinding.inflate(
|
return accounts.toMutableList().apply {
|
||||||
LayoutInflater.from(context)
|
|
||||||
)
|
|
||||||
|
|
||||||
val showAccount = accounts.toMutableList().apply {
|
|
||||||
val item = getDefaultAccount(context)
|
val item = getDefaultAccount(context)
|
||||||
remove(item)
|
remove(item)
|
||||||
add(0, item)
|
add(0, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
val builder =
|
|
||||||
BottomSheetDialog(context)
|
|
||||||
builder.setContentView(binding.root)
|
|
||||||
val accountName = context.getString(R.string.account)
|
|
||||||
|
|
||||||
binding.profilesRecyclerview.setLinearListLayout(
|
|
||||||
isHorizontal = true,
|
|
||||||
nextUp = FOCUS_SELF,
|
|
||||||
nextDown = FOCUS_SELF,
|
|
||||||
nextLeft = FOCUS_SELF,
|
|
||||||
nextRight = FOCUS_SELF
|
|
||||||
)
|
|
||||||
binding.profilesRecyclerview.adapter = WhoIsWatchingAdapter(
|
|
||||||
selectCallBack = { account ->
|
|
||||||
setAccount(account, true)
|
|
||||||
builder.dismissSafe()
|
|
||||||
},
|
|
||||||
addAccountCallback = {
|
|
||||||
val currentAccounts = accounts
|
|
||||||
val remainingImages =
|
|
||||||
profileImages.toSet() - currentAccounts.filter { it.customImage == null }
|
|
||||||
.mapNotNull { profileImages.getOrNull(it.defaultImageIndex) }.toSet()
|
|
||||||
val image =
|
|
||||||
profileImages.indexOf(remainingImages.randomOrNull() ?: profileImages.random())
|
|
||||||
val keyIndex = (currentAccounts.maxOfOrNull { it.keyIndex } ?: 0) + 1
|
|
||||||
|
|
||||||
// create a new dummy account
|
|
||||||
editAccount(
|
|
||||||
context,
|
|
||||||
Account(
|
|
||||||
keyIndex = keyIndex,
|
|
||||||
name = "$accountName $keyIndex",
|
|
||||||
customImage = null,
|
|
||||||
defaultImageIndex = image
|
|
||||||
), isNewAccount = true
|
|
||||||
)
|
|
||||||
builder.dismissSafe()
|
|
||||||
},
|
|
||||||
editCallBack = { account ->
|
|
||||||
editAccount(
|
|
||||||
context, account, isNewAccount = false
|
|
||||||
)
|
|
||||||
builder.dismissSafe()
|
|
||||||
}
|
|
||||||
).apply {
|
|
||||||
submitList(showAccount)
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
data class PosDur(
|
data class PosDur(
|
||||||
@JsonProperty("position") val position: Long,
|
@JsonProperty("position") val position: Long,
|
||||||
@JsonProperty("duration") val duration: Long
|
@JsonProperty("duration") val duration: Long
|
||||||
|
@ -351,20 +198,35 @@ object DataStoreHelper {
|
||||||
/**
|
/**
|
||||||
* Used to display notifications on new episodes and posters in library.
|
* Used to display notifications on new episodes and posters in library.
|
||||||
**/
|
**/
|
||||||
data class SubscribedData(
|
abstract class LibrarySearchResponse(
|
||||||
@JsonProperty("id") override var id: Int?,
|
@JsonProperty("id") override var id: Int?,
|
||||||
@JsonProperty("subscribedTime") val bookmarkedTime: Long,
|
@JsonProperty("latestUpdatedTime") open val latestUpdatedTime: Long,
|
||||||
@JsonProperty("latestUpdatedTime") val latestUpdatedTime: Long,
|
|
||||||
@JsonProperty("lastSeenEpisodeCount") val lastSeenEpisodeCount: Map<DubStatus, Int?>,
|
|
||||||
@JsonProperty("name") override val name: String,
|
@JsonProperty("name") override val name: String,
|
||||||
@JsonProperty("url") override val url: String,
|
@JsonProperty("url") override val url: String,
|
||||||
@JsonProperty("apiName") override val apiName: String,
|
@JsonProperty("apiName") override val apiName: String,
|
||||||
@JsonProperty("type") override var type: TvType? = null,
|
@JsonProperty("type") override var type: TvType?,
|
||||||
@JsonProperty("posterUrl") override var posterUrl: String?,
|
@JsonProperty("posterUrl") override var posterUrl: String?,
|
||||||
@JsonProperty("year") val year: Int?,
|
@JsonProperty("year") open val year: Int?,
|
||||||
@JsonProperty("quality") override var quality: SearchQuality? = null,
|
@JsonProperty("syncData") open val syncData: Map<String, String>?,
|
||||||
@JsonProperty("posterHeaders") override var posterHeaders: Map<String, String>? = null,
|
@JsonProperty("quality") override var quality: SearchQuality?,
|
||||||
) : SearchResponse {
|
@JsonProperty("posterHeaders") override var posterHeaders: Map<String, String>?
|
||||||
|
) : SearchResponse
|
||||||
|
|
||||||
|
data class SubscribedData(
|
||||||
|
@JsonProperty("subscribedTime") val subscribedTime: Long,
|
||||||
|
@JsonProperty("lastSeenEpisodeCount") val lastSeenEpisodeCount: Map<DubStatus, Int?>,
|
||||||
|
override var id: Int?,
|
||||||
|
override val latestUpdatedTime: Long,
|
||||||
|
override val name: String,
|
||||||
|
override val url: String,
|
||||||
|
override val apiName: String,
|
||||||
|
override var type: TvType?,
|
||||||
|
override var posterUrl: String?,
|
||||||
|
override val year: Int?,
|
||||||
|
override val syncData: Map<String, String>? = null,
|
||||||
|
override var quality: SearchQuality? = null,
|
||||||
|
override var posterHeaders: Map<String, String>? = null
|
||||||
|
) : LibrarySearchResponse(id, latestUpdatedTime, name, url, apiName, type, posterUrl, year, syncData, quality, posterHeaders) {
|
||||||
fun toLibraryItem(): SyncAPI.LibraryItem? {
|
fun toLibraryItem(): SyncAPI.LibraryItem? {
|
||||||
return SyncAPI.LibraryItem(
|
return SyncAPI.LibraryItem(
|
||||||
name,
|
name,
|
||||||
|
@ -380,18 +242,19 @@ object DataStoreHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
data class BookmarkedData(
|
data class BookmarkedData(
|
||||||
@JsonProperty("id") override var id: Int?,
|
|
||||||
@JsonProperty("bookmarkedTime") val bookmarkedTime: Long,
|
@JsonProperty("bookmarkedTime") val bookmarkedTime: Long,
|
||||||
@JsonProperty("latestUpdatedTime") val latestUpdatedTime: Long,
|
override var id: Int?,
|
||||||
@JsonProperty("name") override val name: String,
|
override val latestUpdatedTime: Long,
|
||||||
@JsonProperty("url") override val url: String,
|
override val name: String,
|
||||||
@JsonProperty("apiName") override val apiName: String,
|
override val url: String,
|
||||||
@JsonProperty("type") override var type: TvType? = null,
|
override val apiName: String,
|
||||||
@JsonProperty("posterUrl") override var posterUrl: String?,
|
override var type: TvType?,
|
||||||
@JsonProperty("year") val year: Int?,
|
override var posterUrl: String?,
|
||||||
@JsonProperty("quality") override var quality: SearchQuality? = null,
|
override val year: Int?,
|
||||||
@JsonProperty("posterHeaders") override var posterHeaders: Map<String, String>? = null,
|
override val syncData: Map<String, String>? = null,
|
||||||
) : SearchResponse {
|
override var quality: SearchQuality? = null,
|
||||||
|
override var posterHeaders: Map<String, String>? = null
|
||||||
|
) : LibrarySearchResponse(id, latestUpdatedTime, name, url, apiName, type, posterUrl, year, syncData, quality, posterHeaders) {
|
||||||
fun toLibraryItem(id: String): SyncAPI.LibraryItem {
|
fun toLibraryItem(id: String): SyncAPI.LibraryItem {
|
||||||
return SyncAPI.LibraryItem(
|
return SyncAPI.LibraryItem(
|
||||||
name,
|
name,
|
||||||
|
@ -406,6 +269,34 @@ object DataStoreHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class FavoritesData(
|
||||||
|
@JsonProperty("favoritesTime") val favoritesTime: Long,
|
||||||
|
override var id: Int?,
|
||||||
|
override val latestUpdatedTime: Long,
|
||||||
|
override val name: String,
|
||||||
|
override val url: String,
|
||||||
|
override val apiName: String,
|
||||||
|
override var type: TvType?,
|
||||||
|
override var posterUrl: String?,
|
||||||
|
override val year: Int?,
|
||||||
|
override val syncData: Map<String, String>? = null,
|
||||||
|
override var quality: SearchQuality? = null,
|
||||||
|
override var posterHeaders: Map<String, String>? = null
|
||||||
|
) : LibrarySearchResponse(id, latestUpdatedTime, name, url, apiName, type, posterUrl, year, syncData, quality, posterHeaders) {
|
||||||
|
fun toLibraryItem(): SyncAPI.LibraryItem? {
|
||||||
|
return SyncAPI.LibraryItem(
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
id?.toString() ?: return null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
latestUpdatedTime,
|
||||||
|
apiName, type, posterUrl, posterHeaders, quality, this.id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class ResumeWatchingResult(
|
data class ResumeWatchingResult(
|
||||||
@JsonProperty("name") override val name: String,
|
@JsonProperty("name") override val name: String,
|
||||||
@JsonProperty("url") override val url: String,
|
@JsonProperty("url") override val url: String,
|
||||||
|
@ -438,15 +329,9 @@ object DataStoreHelper {
|
||||||
removeKeys(folder)
|
removeKeys(folder)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteAllBookmarkedData() {
|
|
||||||
val folder1 = "$currentAccount/$RESULT_WATCH_STATE"
|
|
||||||
val folder2 = "$currentAccount/$RESULT_WATCH_STATE_DATA"
|
|
||||||
removeKeys(folder1)
|
|
||||||
removeKeys(folder2)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun deleteBookmarkedData(id: Int?) {
|
fun deleteBookmarkedData(id: Int?) {
|
||||||
if (id == null) return
|
if (id == null) return
|
||||||
|
AccountManager.localListApi.requireLibraryRefresh = true
|
||||||
removeKey("$currentAccount/$RESULT_WATCH_STATE", id.toString())
|
removeKey("$currentAccount/$RESULT_WATCH_STATE", id.toString())
|
||||||
removeKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString())
|
removeKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString())
|
||||||
}
|
}
|
||||||
|
@ -544,6 +429,12 @@ object DataStoreHelper {
|
||||||
return getKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString())
|
return getKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getAllBookmarkedData(): List<BookmarkedData> {
|
||||||
|
return getKeys("$currentAccount/$RESULT_WATCH_STATE_DATA")?.mapNotNull {
|
||||||
|
getKey(it)
|
||||||
|
} ?: emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
fun getAllSubscriptions(): List<SubscribedData> {
|
fun getAllSubscriptions(): List<SubscribedData> {
|
||||||
return getKeys("$currentAccount/$RESULT_SUBSCRIBED_STATE_DATA")?.mapNotNull {
|
return getKeys("$currentAccount/$RESULT_SUBSCRIBED_STATE_DATA")?.mapNotNull {
|
||||||
getKey(it)
|
getKey(it)
|
||||||
|
@ -579,6 +470,29 @@ object DataStoreHelper {
|
||||||
return getKey("$currentAccount/$RESULT_SUBSCRIBED_STATE_DATA", id.toString())
|
return getKey("$currentAccount/$RESULT_SUBSCRIBED_STATE_DATA", id.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getAllFavorites(): List<FavoritesData> {
|
||||||
|
return getKeys("$currentAccount/$RESULT_FAVORITES_STATE_DATA")?.mapNotNull {
|
||||||
|
getKey(it)
|
||||||
|
} ?: emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeFavoritesData(id: Int?) {
|
||||||
|
if (id == null) return
|
||||||
|
AccountManager.localListApi.requireLibraryRefresh = true
|
||||||
|
removeKey("$currentAccount/$RESULT_FAVORITES_STATE_DATA", id.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setFavoritesData(id: Int?, data: FavoritesData) {
|
||||||
|
if (id == null) return
|
||||||
|
setKey("$currentAccount/$RESULT_FAVORITES_STATE_DATA", id.toString(), data)
|
||||||
|
AccountManager.localListApi.requireLibraryRefresh = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFavoritesData(id: Int?): FavoritesData? {
|
||||||
|
if (id == null) return null
|
||||||
|
return getKey("$currentAccount/$RESULT_FAVORITES_STATE_DATA", id.toString())
|
||||||
|
}
|
||||||
|
|
||||||
fun setViewPos(id: Int?, pos: Long, dur: Long) {
|
fun setViewPos(id: Int?, pos: Long, dur: Long) {
|
||||||
if (id == null) return
|
if (id == null) return
|
||||||
if (dur < 30_000) return // too short
|
if (dur < 30_000) return // too short
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.lagradost.cloudstream3.utils
|
package com.lagradost.cloudstream3.utils
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||||
import com.lagradost.cloudstream3.SubtitleFile
|
import com.lagradost.cloudstream3.SubtitleFile
|
||||||
import com.lagradost.cloudstream3.TvType
|
import com.lagradost.cloudstream3.TvType
|
||||||
import com.lagradost.cloudstream3.USER_AGENT
|
import com.lagradost.cloudstream3.USER_AGENT
|
||||||
|
@ -378,6 +379,7 @@ open class ExtractorLink constructor(
|
||||||
val isM3u8 : Boolean get() = type == ExtractorLinkType.M3U8
|
val isM3u8 : Boolean get() = type == ExtractorLinkType.M3U8
|
||||||
val isDash : Boolean get() = type == ExtractorLinkType.DASH
|
val isDash : Boolean get() = type == ExtractorLinkType.DASH
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
fun getAllHeaders() : Map<String, String> {
|
fun getAllHeaders() : Map<String, String> {
|
||||||
if (referer.isBlank()) {
|
if (referer.isBlank()) {
|
||||||
return headers
|
return headers
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
package com.lagradost.cloudstream3.utils
|
|
||||||
|
|
||||||
interface IOnBackPressed {
|
|
||||||
fun onBackPressed(): Boolean
|
|
||||||
}
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
package com.lagradost.cloudstream3.utils
|
||||||
|
//Reference: https://stackoverflow.com/a/29055283
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Matrix
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.util.AttributeSet
|
||||||
|
|
||||||
|
class PercentageCropImageView : androidx.appcompat.widget.AppCompatImageView {
|
||||||
|
private var mCropYCenterOffsetPct: Float? = null
|
||||||
|
private var mCropXCenterOffsetPct: Float? = null
|
||||||
|
constructor(context: Context?) : super(context!!)
|
||||||
|
constructor(context: Context?, attrs: AttributeSet?) : super(context!!, attrs)
|
||||||
|
constructor(
|
||||||
|
context: Context?, attrs: AttributeSet?,
|
||||||
|
defStyle: Int
|
||||||
|
) : super(context!!, attrs, defStyle)
|
||||||
|
|
||||||
|
var cropYCenterOffsetPct: Float
|
||||||
|
get() = mCropYCenterOffsetPct!!
|
||||||
|
set(cropYCenterOffsetPct) {
|
||||||
|
require(cropYCenterOffsetPct <= 1.0) { "Value too large: Must be <= 1.0" }
|
||||||
|
mCropYCenterOffsetPct = cropYCenterOffsetPct
|
||||||
|
}
|
||||||
|
var cropXCenterOffsetPct: Float
|
||||||
|
get() = mCropXCenterOffsetPct!!
|
||||||
|
set(cropXCenterOffsetPct) {
|
||||||
|
require(cropXCenterOffsetPct <= 1.0) { "Value too large: Must be <= 1.0" }
|
||||||
|
mCropXCenterOffsetPct = cropXCenterOffsetPct
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun myConfigureBounds() {
|
||||||
|
if (this.scaleType == ScaleType.MATRIX) {
|
||||||
|
|
||||||
|
val d = this.drawable
|
||||||
|
if (d != null) {
|
||||||
|
val dWidth = d.intrinsicWidth
|
||||||
|
val dHeight = d.intrinsicHeight
|
||||||
|
val m = Matrix()
|
||||||
|
val vWidth = width - this.paddingLeft - this.paddingRight
|
||||||
|
val vHeight = height - this.paddingTop - this.paddingBottom
|
||||||
|
val scale: Float
|
||||||
|
var dx = 0f
|
||||||
|
var dy = 0f
|
||||||
|
if (dWidth * vHeight > vWidth * dHeight) {
|
||||||
|
val cropXCenterOffsetPct =
|
||||||
|
if (mCropXCenterOffsetPct != null) mCropXCenterOffsetPct!!.toFloat() else 0.5f
|
||||||
|
scale = vHeight.toFloat() / dHeight.toFloat()
|
||||||
|
dx = (vWidth - dWidth * scale) * cropXCenterOffsetPct
|
||||||
|
} else {
|
||||||
|
val cropYCenterOffsetPct =
|
||||||
|
if (mCropYCenterOffsetPct != null) mCropYCenterOffsetPct!!.toFloat() else 0f
|
||||||
|
scale = vWidth.toFloat() / dWidth.toFloat()
|
||||||
|
dy = (vHeight - dHeight * scale) * cropYCenterOffsetPct
|
||||||
|
}
|
||||||
|
m.setScale(scale, scale)
|
||||||
|
m.postTranslate((dx + 0.5f).toInt().toFloat(), (dy + 0.5f).toInt().toFloat())
|
||||||
|
this.imageMatrix = m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// These 3 methods call configureBounds in ImageView.java class, which
|
||||||
|
// adjusts the matrix in a call to center_crop (android's built-in
|
||||||
|
// scaling and centering crop method). We also want to trigger
|
||||||
|
// in the same place, but using our own matrix, which is then set
|
||||||
|
// directly at line 588 of ImageView.java and then copied over
|
||||||
|
// as the draw matrix at line 942 of ImageView.java
|
||||||
|
override fun setFrame(l: Int, t: Int, r: Int, b: Int): Boolean {
|
||||||
|
val changed = super.setFrame(l, t, r, b)
|
||||||
|
myConfigureBounds()
|
||||||
|
return changed
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setImageDrawable(d: Drawable?) {
|
||||||
|
super.setImageDrawable(d)
|
||||||
|
myConfigureBounds()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setImageResource(resId: Int) {
|
||||||
|
super.setImageResource(resId)
|
||||||
|
myConfigureBounds()
|
||||||
|
}
|
||||||
|
// In case you can change the ScaleType in code you have to call redraw()
|
||||||
|
//fullsizeImageView.setScaleType(ScaleType.FIT_CENTER);
|
||||||
|
//fullsizeImageView.redraw();
|
||||||
|
fun redraw() {
|
||||||
|
val d = this.drawable
|
||||||
|
if (d != null) {
|
||||||
|
// Force toggle to recalculate our bounds
|
||||||
|
setImageDrawable(null)
|
||||||
|
setImageDrawable(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,14 +2,12 @@ package com.lagradost.cloudstream3.utils
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
|
import android.text.Spanned
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.AbsListView
|
import android.widget.AbsListView
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.EditText
|
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.ListView
|
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
|
@ -19,7 +17,10 @@ import androidx.core.view.marginRight
|
||||||
import androidx.core.view.marginTop
|
import androidx.core.view.marginTop
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
|
import com.lagradost.cloudstream3.databinding.BottomInputDialogBinding
|
||||||
import com.lagradost.cloudstream3.databinding.BottomSelectionDialogBinding
|
import com.lagradost.cloudstream3.databinding.BottomSelectionDialogBinding
|
||||||
|
import com.lagradost.cloudstream3.databinding.BottomTextDialogBinding
|
||||||
|
import com.lagradost.cloudstream3.databinding.OptionsPopupTvBinding
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
|
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
|
||||||
|
@ -54,14 +55,14 @@ object SingleSelectionHelper {
|
||||||
if (this == null) return
|
if (this == null) return
|
||||||
|
|
||||||
if (isTvSettings()) {
|
if (isTvSettings()) {
|
||||||
val builder =
|
val binding = OptionsPopupTvBinding.inflate(layoutInflater)
|
||||||
AlertDialog.Builder(this, R.style.AlertDialogCustom)
|
val dialog = AlertDialog.Builder(this, R.style.AlertDialogCustom)
|
||||||
.setView(R.layout.options_popup_tv)
|
.setView(binding.root)
|
||||||
|
.create()
|
||||||
|
|
||||||
val dialog = builder.create()
|
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
|
||||||
dialog.findViewById<ListView>(R.id.listview1)?.let { listView ->
|
binding.listview1.let { listView ->
|
||||||
listView.choiceMode = AbsListView.CHOICE_MODE_SINGLE
|
listView.choiceMode = AbsListView.CHOICE_MODE_SINGLE
|
||||||
listView.adapter =
|
listView.adapter =
|
||||||
ArrayAdapter<String>(this, R.layout.sort_bottom_single_choice_color).apply {
|
ArrayAdapter<String>(this, R.layout.sort_bottom_single_choice_color).apply {
|
||||||
|
@ -74,7 +75,7 @@ object SingleSelectionHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog.findViewById<ImageView>(R.id.imageView)?.apply {
|
binding.imageView.apply {
|
||||||
isGone = poster.isNullOrEmpty()
|
isGone = poster.isNullOrEmpty()
|
||||||
setImage(poster)
|
setImage(poster)
|
||||||
}
|
}
|
||||||
|
@ -105,12 +106,12 @@ object SingleSelectionHelper {
|
||||||
if (this == null) return
|
if (this == null) return
|
||||||
|
|
||||||
val realShowApply = showApply || isMultiSelect
|
val realShowApply = showApply || isMultiSelect
|
||||||
val listView = binding.listview1//.findViewById<ListView>(R.id.listview1)!!
|
val listView = binding.listview1
|
||||||
val textView = binding.text1//.findViewById<TextView>(R.id.text1)!!
|
val textView = binding.text1
|
||||||
val applyButton = binding.applyBtt//.findViewById<TextView>(R.id.apply_btt)
|
val applyButton = binding.applyBtt
|
||||||
val cancelButton = binding.cancelBtt//findViewById<TextView>(R.id.cancel_btt)
|
val cancelButton = binding.cancelBtt
|
||||||
val applyHolder =
|
val applyHolder =
|
||||||
binding.applyBttHolder//.findViewById<LinearLayout>(R.id.apply_btt_holder)
|
binding.applyBttHolder
|
||||||
|
|
||||||
applyHolder.isVisible = realShowApply
|
applyHolder.isVisible = realShowApply
|
||||||
if (!realShowApply) {
|
if (!realShowApply) {
|
||||||
|
@ -173,8 +174,8 @@ object SingleSelectionHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun Activity?.showInputDialog(
|
private fun Activity?.showInputDialog(
|
||||||
|
binding: BottomInputDialogBinding,
|
||||||
dialog: Dialog,
|
dialog: Dialog,
|
||||||
value: String,
|
value: String,
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -184,11 +185,11 @@ object SingleSelectionHelper {
|
||||||
) {
|
) {
|
||||||
if (this == null) return
|
if (this == null) return
|
||||||
|
|
||||||
val inputView = dialog.findViewById<EditText>(R.id.nginx_text_input)!!
|
val inputView = binding.nginxTextInput
|
||||||
val textView = dialog.findViewById<TextView>(R.id.text1)!!
|
val textView = binding.text1
|
||||||
val applyButton = dialog.findViewById<TextView>(R.id.apply_btt)!!
|
val applyButton = binding.applyBtt
|
||||||
val cancelButton = dialog.findViewById<TextView>(R.id.cancel_btt)!!
|
val cancelButton = binding.cancelBtt
|
||||||
val applyHolder = dialog.findViewById<LinearLayout>(R.id.apply_btt_holder)!!
|
val applyHolder = binding.applyBttHolder
|
||||||
|
|
||||||
applyHolder.isVisible = true
|
applyHolder.isVisible = true
|
||||||
textView.text = name
|
textView.text = name
|
||||||
|
@ -350,11 +351,17 @@ object SingleSelectionHelper {
|
||||||
dismissCallback: () -> Unit,
|
dismissCallback: () -> Unit,
|
||||||
callback: (String) -> Unit,
|
callback: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
val builder = BottomSheetDialog(this) // probably the stuff at the bottom
|
val builder = BottomSheetDialog(this)
|
||||||
builder.setContentView(R.layout.bottom_input_dialog) // input layout
|
|
||||||
|
val binding: BottomInputDialogBinding = BottomInputDialogBinding.inflate(
|
||||||
|
LayoutInflater.from(this)
|
||||||
|
)
|
||||||
|
|
||||||
|
builder.setContentView(binding.root)
|
||||||
|
|
||||||
builder.show()
|
builder.show()
|
||||||
showInputDialog(
|
showInputDialog(
|
||||||
|
binding,
|
||||||
builder,
|
builder,
|
||||||
value,
|
value,
|
||||||
name,
|
name,
|
||||||
|
@ -363,4 +370,24 @@ object SingleSelectionHelper {
|
||||||
dismissCallback
|
dismissCallback
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Activity.showBottomDialogText(
|
||||||
|
title: String,
|
||||||
|
text: Spanned,
|
||||||
|
dismissCallback: () -> Unit
|
||||||
|
) {
|
||||||
|
val binding = BottomTextDialogBinding.inflate(layoutInflater)
|
||||||
|
val dialog = BottomSheetDialog(this)
|
||||||
|
|
||||||
|
dialog.setContentView(binding.root)
|
||||||
|
|
||||||
|
binding.dialogTitle.text = title
|
||||||
|
binding.dialogText.text = text
|
||||||
|
|
||||||
|
dialog.setOnDismissListener {
|
||||||
|
dismissCallback.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,9 +178,10 @@ object UIHelper {
|
||||||
fun Activity?.navigate(@IdRes navigation: Int, arguments: Bundle? = null) {
|
fun Activity?.navigate(@IdRes navigation: Int, arguments: Bundle? = null) {
|
||||||
try {
|
try {
|
||||||
if (this is FragmentActivity) {
|
if (this is FragmentActivity) {
|
||||||
(supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as? NavHostFragment?)?.navController?.navigate(
|
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as? NavHostFragment?
|
||||||
navigation, arguments
|
navHostFragment?.navController?.let {
|
||||||
)
|
it.navigate(navigation, arguments)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
logError(t)
|
logError(t)
|
||||||
|
@ -300,7 +301,7 @@ object UIHelper {
|
||||||
} ?: return false
|
} ?: return false
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
var builder = GlideApp.with(this)
|
var builder = com.bumptech.glide.Glide.with(this)
|
||||||
.load(glideImage)
|
.load(glideImage)
|
||||||
.skipMemoryCache(true)
|
.skipMemoryCache(true)
|
||||||
.diskCacheStrategy(DiskCacheStrategy.ALL).let { req ->
|
.diskCacheStrategy(DiskCacheStrategy.ALL).let { req ->
|
||||||
|
@ -367,7 +368,7 @@ object UIHelper {
|
||||||
) {
|
) {
|
||||||
if (this == null || url.isNullOrBlank()) return
|
if (this == null || url.isNullOrBlank()) return
|
||||||
try {
|
try {
|
||||||
val res = GlideApp.with(this)
|
val res = com.bumptech.glide.Glide.with(this)
|
||||||
.load(GlideUrl(url) { headers ?: emptyMap() })
|
.load(GlideUrl(url) { headers ?: emptyMap() })
|
||||||
.apply(bitmapTransform(BlurTransformation(radius, sample)))
|
.apply(bitmapTransform(BlurTransformation(radius, sample)))
|
||||||
.transition(
|
.transition(
|
||||||
|
@ -417,7 +418,7 @@ object UIHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun FragmentActivity.popCurrentPage() {
|
fun FragmentActivity.popCurrentPage() {
|
||||||
this.onBackPressed()
|
this.onBackPressedDispatcher.onBackPressed()
|
||||||
/*val currentFragment = supportFragmentManager.fragments.lastOrNull {
|
/*val currentFragment = supportFragmentManager.fragments.lastOrNull {
|
||||||
it.isVisible
|
it.isVisible
|
||||||
} ?: return
|
} ?: return
|
||||||
|
@ -437,7 +438,7 @@ object UIHelper {
|
||||||
val currentFragment = supportFragmentManager.fragments.lastOrNull {
|
val currentFragment = supportFragmentManager.fragments.lastOrNull {
|
||||||
it.isVisible
|
it.isVisible
|
||||||
}
|
}
|
||||||
?: //this.onBackPressed()
|
?: //this.onBackPressedDispatcher.onBackPressed()
|
||||||
return
|
return
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -234,7 +234,7 @@ object VideoDownloadManager {
|
||||||
return cachedBitmaps[url]
|
return cachedBitmaps[url]
|
||||||
}
|
}
|
||||||
|
|
||||||
val bitmap = GlideApp.with(this)
|
val bitmap = com.bumptech.glide.Glide.with(this)
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
.load(GlideUrl(url) { headers ?: emptyMap() })
|
.load(GlideUrl(url) { headers ?: emptyMap() })
|
||||||
.into(720, 720)
|
.into(720, 720)
|
||||||
|
|
11
app/src/main/res/drawable/ic_baseline_edit_24.xml
Normal file
11
app/src/main/res/drawable/ic_baseline_edit_24.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
|
||||||
|
</vector>
|
|
@ -1,4 +1,5 @@
|
||||||
<?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"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
@ -14,13 +15,12 @@
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/text1"
|
android:id="@+id/title"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_rowWeight="1"
|
android:layout_rowWeight="1"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
|
|
||||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||||
android:text="@string/create_account"
|
android:text="@string/create_account"
|
||||||
|
@ -28,34 +28,8 @@
|
||||||
android:textSize="20sp"
|
android:textSize="20sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<!-- <com.google.android.material.button.MaterialButton-->
|
|
||||||
<!-- android:nextFocusDown="@id/repo_name_input"-->
|
|
||||||
<!-- android:id="@+id/list_repositories"-->
|
|
||||||
<!-- android:nextFocusLeft="@id/apply_btt"-->
|
|
||||||
<!-- android:nextFocusRight="@id/cancel_btt"-->
|
|
||||||
<!-- style="@style/WhiteButton"-->
|
|
||||||
<!-- android:layout_width="wrap_content"-->
|
|
||||||
<!-- android:layout_gravity="center_vertical"-->
|
|
||||||
<!-- android:text="@string/view_public_repositories_button_short" />-->
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/text2"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_rowWeight="1"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
|
|
||||||
android:layout_marginBottom="10dp"
|
|
||||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
|
||||||
|
|
||||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
|
||||||
android:textColor="?attr/grayTextColor"
|
|
||||||
android:textSize="15sp"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:text="Gogoanime" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -77,61 +51,67 @@
|
||||||
android:textColorHint="?attr/grayTextColor"
|
android:textColorHint="?attr/grayTextColor"
|
||||||
tools:ignore="LabelFor" />
|
tools:ignore="LabelFor" />
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/lockProfileCheckbox"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/lock_profile" />
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:cardCornerRadius="@dimen/rounded_image_radius">
|
app:cardCornerRadius="@dimen/rounded_image_radius">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:foreground="@drawable/outline_drawable_forced_round"
|
android:id="@+id/account_image"
|
||||||
android:id="@+id/profile_pic"
|
android:src="@drawable/profile_bg_blue"
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="60dp"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:contentDescription="@string/preview_background_img_des"
|
android:contentDescription="@string/preview_background_img_des"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
android:src="@drawable/profile_bg_blue" />
|
android:foreground="@drawable/outline_drawable_forced_round"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:layout_gravity="center" />
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/apply_btt_holder"
|
android:id="@+id/apply_btt_holder"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="10dp"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="60dp"
|
android:layout_height="60dp"
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
android:layout_marginTop="-60dp"
|
android:layout_marginTop="-60dp">
|
||||||
android:orientation="horizontal"
|
|
||||||
android:padding="10dp">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/delete_btt"
|
android:id="@+id/delete_btt"
|
||||||
style="@style/BlackButton"
|
android:text="@string/delete"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:nextFocusRight="@id/apply_btt"
|
android:nextFocusRight="@id/apply_btt"
|
||||||
android:text="@string/delete" />
|
style="@style/BlackButton" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/apply_btt"
|
android:id="@+id/apply_btt"
|
||||||
style="@style/WhiteButton"
|
android:text="@string/sort_apply"
|
||||||
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_gravity="center_vertical|end"
|
android:layout_gravity="center_vertical|end"
|
||||||
android:layout_toStartOf="@+id/cancel_btt"
|
android:layout_toStartOf="@+id/cancel_btt"
|
||||||
android:nextFocusLeft="@id/delete_btt"
|
android:nextFocusLeft="@id/delete_btt"
|
||||||
android:nextFocusRight="@id/cancel_btt"
|
android:nextFocusRight="@id/cancel_btt"
|
||||||
android:text="@string/sort_apply" />
|
style="@style/WhiteButton" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/cancel_btt"
|
android:id="@+id/cancel_btt"
|
||||||
style="@style/BlackButton"
|
android:text="@string/sort_cancel"
|
||||||
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
android:layout_gravity="center_vertical|end"
|
android:layout_gravity="center_vertical|end"
|
||||||
android:nextFocusLeft="@id/apply_btt"
|
android:nextFocusLeft="@id/apply_btt"
|
||||||
android:text="@string/sort_cancel" />
|
style="@style/BlackButton" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
|
@ -4,14 +4,14 @@
|
||||||
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:id="@+id/card_view"
|
android:id="@+id/card_view"
|
||||||
android:layout_width="100dp"
|
android:layout_width="110dp"
|
||||||
android:layout_height="100dp"
|
android:layout_height="110dp"
|
||||||
android:animateLayoutChanges="true"
|
android:animateLayoutChanges="true"
|
||||||
android:backgroundTint="?attr/primaryGrayBackground"
|
android:backgroundTint="?attr/primaryGrayBackground"
|
||||||
android:foreground="?attr/selectableItemBackgroundBorderless"
|
android:foreground="?attr/selectableItemBackground"
|
||||||
app:cardCornerRadius="@dimen/rounded_image_radius"
|
android:layout_margin="10dp"
|
||||||
android:layout_margin="5dp"
|
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
|
app:cardCornerRadius="@dimen/rounded_image_radius"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintDimensionRatio="1"
|
app:layout_constraintDimensionRatio="1"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
@ -20,24 +20,32 @@
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/profile_image_background"
|
android:id="@+id/account_image"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:alpha="0.4"
|
android:alpha="0.4"
|
||||||
android:contentDescription="@string/profile_background_des"
|
|
||||||
android:scaleType="centerCrop" />
|
android:scaleType="centerCrop" />
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/outline"
|
android:id="@+id/outline"
|
||||||
tools:visibility="visible"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@drawable/outline_card"
|
android:background="@drawable/outline_card"
|
||||||
android:visibility="gone" />
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/lock_icon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_gravity="top|end"
|
||||||
|
android:layout_margin="4dp"
|
||||||
|
android:src="@drawable/video_locked"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/profile_text"
|
android:id="@+id/account_name"
|
||||||
tools:text="@string/mobile_data"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
|
@ -2,16 +2,15 @@
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.cardview.widget.CardView 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:id="@+id/card_view"
|
android:id="@+id/card_view"
|
||||||
android:layout_width="100dp"
|
android:layout_width="110dp"
|
||||||
android:layout_height="100dp"
|
android:layout_height="110dp"
|
||||||
android:animateLayoutChanges="true"
|
android:animateLayoutChanges="true"
|
||||||
android:backgroundTint="?attr/primaryGrayBackground"
|
android:backgroundTint="?attr/primaryGrayBackground"
|
||||||
android:foreground="?attr/selectableItemBackgroundBorderless"
|
android:foreground="?attr/selectableItemBackground"
|
||||||
app:cardCornerRadius="@dimen/rounded_image_radius"
|
android:layout_margin="10dp"
|
||||||
android:layout_margin="5dp"
|
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
|
app:cardCornerRadius="@dimen/rounded_image_radius"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintDimensionRatio="1"
|
app:layout_constraintDimensionRatio="1"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
63
app/src/main/res/layout/account_list_item_edit.xml
Normal file
63
app/src/main/res/layout/account_list_item_edit.xml
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/card_view"
|
||||||
|
android:layout_width="110dp"
|
||||||
|
android:layout_height="110dp"
|
||||||
|
android:animateLayoutChanges="true"
|
||||||
|
android:backgroundTint="?attr/primaryGrayBackground"
|
||||||
|
android:foreground="?attr/selectableItemBackground"
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
android:focusable="true"
|
||||||
|
app:cardCornerRadius="@dimen/rounded_image_radius"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintDimensionRatio="1"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHeight_percent="0.4"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/account_image"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:alpha="0.1"
|
||||||
|
android:scaleType="centerCrop" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/outline"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/outline_card"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/lock_icon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_gravity="top|end"
|
||||||
|
android:layout_margin="4dp"
|
||||||
|
android:src="@drawable/video_locked"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/pencil_icon"
|
||||||
|
android:layout_width="42dp"
|
||||||
|
android:layout_height="42dp"
|
||||||
|
android:layout_gravity="top|start"
|
||||||
|
android:src="@drawable/ic_baseline_edit_24" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/account_name"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</androidx.cardview.widget.CardView>
|
44
app/src/main/res/layout/account_select_linear.xml
Normal file
44
app/src/main/res/layout/account_select_linear.xml
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||||
|
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="@string/switch_account"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textColor="?attr/textColor"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_rowWeight="1"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/account_recycler_view"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:descendantFocusability="afterDescendants"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:itemCount="4"
|
||||||
|
tools:listitem="@layout/account_list_item" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/manage_accounts_button"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:text="@string/manage_accounts"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:icon="@drawable/ic_baseline_edit_24"
|
||||||
|
style="@style/BlackButton" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
49
app/src/main/res/layout/activity_account_select.xml
Normal file
49
app/src/main/res/layout/activity_account_select.xml
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:background="?attr/primaryBlackBackground"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/edit_account_button"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_gravity="end|top"
|
||||||
|
android:layout_marginTop="40dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:src="@drawable/ic_baseline_edit_24"
|
||||||
|
android:focusable="true"
|
||||||
|
android:nextFocusDown="@id/account_recycler_view"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:contentDescription="@string/manage_accounts"
|
||||||
|
app:tint="@color/player_on_button_tv_attr" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingTop="36dp"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/select_an_account"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
|
<com.lagradost.cloudstream3.ui.AutofitRecyclerView
|
||||||
|
android:id="@+id/account_recycler_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="16dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
33
app/src/main/res/layout/bottom_text_dialog.xml
Normal file
33
app/src/main/res/layout/bottom_text_dialog.xml
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/dialog_title"
|
||||||
|
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||||
|
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textColor="?attr/textColor"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_rowWeight="1"
|
||||||
|
tools:text="Test"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/dialog_text"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||||
|
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||||
|
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:requiresFadingEdge="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_rowWeight="1" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -9,7 +9,7 @@
|
||||||
android:layout_margin="5dp"
|
android:layout_margin="5dp"
|
||||||
android:foreground="@drawable/outline_drawable"
|
android:foreground="@drawable/outline_drawable"
|
||||||
app:cardBackgroundColor="@color/transparent"
|
app:cardBackgroundColor="@color/transparent"
|
||||||
|
android:focusable="true"
|
||||||
app:cardCornerRadius="@dimen/rounded_image_radius"
|
app:cardCornerRadius="@dimen/rounded_image_radius"
|
||||||
app:cardElevation="0dp">
|
app:cardElevation="0dp">
|
||||||
|
|
||||||
|
|
|
@ -92,15 +92,7 @@
|
||||||
android:layout_height="100dp"
|
android:layout_height="100dp"
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:orientation="vertical">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:gravity="center"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:padding="20dp">
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/home_preview_bookmark"
|
android:id="@+id/home_preview_bookmark"
|
||||||
|
@ -139,7 +131,7 @@
|
||||||
app:drawableTint="?attr/white"
|
app:drawableTint="?attr/white"
|
||||||
app:drawableTopCompat="@drawable/ic_outline_info_24"
|
app:drawableTopCompat="@drawable/ic_outline_info_24"
|
||||||
app:tint="?attr/white" />
|
app:tint="?attr/white" />
|
||||||
</LinearLayout>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
|
@ -232,7 +232,9 @@
|
||||||
android:layout_marginStart="@dimen/navbar_width"
|
android:layout_marginStart="@dimen/navbar_width"
|
||||||
android:layout_marginEnd="0dp"
|
android:layout_marginEnd="0dp"
|
||||||
android:padding="12dp"
|
android:padding="12dp"
|
||||||
android:text="@string/continue_watching" />
|
android:text="@string/continue_watching"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
app:drawableTint="?attr/white" />
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/home_watch_child_recyclerview"
|
android:id="@+id/home_watch_child_recyclerview"
|
||||||
|
@ -258,7 +260,15 @@
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible">
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/home_bookmark_parent_item_title"
|
||||||
|
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?android:attr/selectableItemBackground">
|
||||||
|
|
||||||
<HorizontalScrollView
|
<HorizontalScrollView
|
||||||
|
android:id="@+id/horizontal_scroll_chips"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:fadingEdge="horizontal"
|
android:fadingEdge="horizontal"
|
||||||
|
@ -344,6 +354,18 @@
|
||||||
</com.google.android.material.chip.ChipGroup>
|
</com.google.android.material.chip.ChipGroup>
|
||||||
</HorizontalScrollView>
|
</HorizontalScrollView>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/home_bookmark_parent_item_more_info"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:contentDescription="@string/home_more_info"
|
||||||
|
android:src="@drawable/ic_baseline_arrow_forward_24"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:drawableTint="?attr/white" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/home_bookmarked_child_recyclerview"
|
android:id="@+id/home_bookmarked_child_recyclerview"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|
|
@ -140,6 +140,7 @@
|
||||||
android:layout_gravity="end"
|
android:layout_gravity="end"
|
||||||
android:background="@drawable/player_button_tv_attr_no_bg"
|
android:background="@drawable/player_button_tv_attr_no_bg"
|
||||||
android:contentDescription="@string/account"
|
android:contentDescription="@string/account"
|
||||||
|
android:focusable="true"
|
||||||
android:nextFocusLeft="@id/home_preview_search_button"
|
android:nextFocusLeft="@id/home_preview_search_button"
|
||||||
android:nextFocusRight="@id/home_switch_account"
|
android:nextFocusRight="@id/home_switch_account"
|
||||||
android:nextFocusDown="@id/home_change_api"
|
android:nextFocusDown="@id/home_change_api"
|
||||||
|
|
|
@ -68,6 +68,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginEnd="25dp"
|
||||||
|
|
||||||
android:iconifiedByDefault="false"
|
android:iconifiedByDefault="false"
|
||||||
android:imeOptions="actionSearch"
|
android:imeOptions="actionSearch"
|
||||||
|
@ -81,6 +82,7 @@
|
||||||
app:queryBackground="@color/transparent"
|
app:queryBackground="@color/transparent"
|
||||||
app:queryHint="@string/search_hint"
|
app:queryHint="@string/search_hint"
|
||||||
app:searchIcon="@drawable/search_icon"
|
app:searchIcon="@drawable/search_icon"
|
||||||
|
app:closeIcon="@drawable/ic_baseline_close_24"
|
||||||
tools:ignore="RtlSymmetry">
|
tools:ignore="RtlSymmetry">
|
||||||
|
|
||||||
</androidx.appcompat.widget.SearchView>
|
</androidx.appcompat.widget.SearchView>
|
||||||
|
|
|
@ -27,7 +27,8 @@
|
||||||
android:id="@+id/search_status_bar_padding"
|
android:id="@+id/search_status_bar_padding"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal"
|
||||||
|
app:layout_scrollFlags="scroll|enterAlways">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/provider_selector"
|
android:id="@+id/provider_selector"
|
||||||
|
@ -102,11 +103,13 @@
|
||||||
app:queryBackground="@color/transparent"
|
app:queryBackground="@color/transparent"
|
||||||
app:queryHint="@string/search_hint"
|
app:queryHint="@string/search_hint"
|
||||||
app:searchIcon="@drawable/search_icon"
|
app:searchIcon="@drawable/search_icon"
|
||||||
|
app:closeIcon="@drawable/ic_baseline_close_24"
|
||||||
tools:ignore="RtlSymmetry">
|
tools:ignore="RtlSymmetry">
|
||||||
|
|
||||||
</androidx.appcompat.widget.SearchView>
|
</androidx.appcompat.widget.SearchView>
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<com.google.android.material.tabs.TabLayout
|
<com.google.android.material.tabs.TabLayout
|
||||||
android:id="@+id/library_tab_layout"
|
android:id="@+id/library_tab_layout"
|
||||||
style="@style/Theme.Widget.Tabs"
|
style="@style/Theme.Widget.Tabs"
|
||||||
|
@ -116,7 +119,7 @@
|
||||||
android:nextFocusDown="@id/search_result_root"
|
android:nextFocusDown="@id/search_result_root"
|
||||||
android:background="?attr/primaryGrayBackground"
|
android:background="?attr/primaryGrayBackground"
|
||||||
android:paddingHorizontal="5dp"
|
android:paddingHorizontal="5dp"
|
||||||
app:layout_scrollFlags="noScroll"
|
android:focusable="true"
|
||||||
app:tabGravity="center"
|
app:tabGravity="center"
|
||||||
app:tabIndicator="@drawable/indicator_background"
|
app:tabIndicator="@drawable/indicator_background"
|
||||||
app:tabIndicatorColor="?attr/white"
|
app:tabIndicatorColor="?attr/white"
|
||||||
|
@ -133,15 +136,15 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
|
||||||
<androidx.viewpager2.widget.ViewPager2
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
android:id="@+id/viewpager"
|
android:id="@+id/viewpager"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:paddingBottom="40dp"
|
android:paddingBottom="40dp"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:tag="@string/tv_no_focus_tag"
|
android:tag="@string/tv_no_focus_tag"
|
||||||
tools:listitem="@layout/library_viewpager_page" />
|
tools:listitem="@layout/library_viewpager_page" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/library_loading_overlay"
|
android:id="@+id/library_loading_overlay"
|
||||||
|
@ -181,7 +184,6 @@
|
||||||
tools:listitem="@layout/loading_poster_dynamic" />
|
tools:listitem="@layout/loading_poster_dynamic" />
|
||||||
</com.facebook.shimmer.ShimmerFrameLayout>
|
</com.facebook.shimmer.ShimmerFrameLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
|
|
|
@ -74,7 +74,7 @@
|
||||||
android:nextFocusUp="@id/result_back"
|
android:nextFocusUp="@id/result_back"
|
||||||
android:nextFocusDown="@id/result_description"
|
android:nextFocusDown="@id/result_description"
|
||||||
android:nextFocusLeft="@id/result_add_sync"
|
android:nextFocusLeft="@id/result_add_sync"
|
||||||
android:nextFocusRight="@id/result_share"
|
android:nextFocusRight="@id/result_favorite"
|
||||||
|
|
||||||
tools:visibility="visible"
|
tools:visibility="visible"
|
||||||
|
|
||||||
|
@ -89,10 +89,27 @@
|
||||||
android:layout_gravity="end|center_vertical"
|
android:layout_gravity="end|center_vertical"
|
||||||
app:tint="?attr/textColor" />
|
app:tint="?attr/textColor" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:nextFocusUp="@id/result_back"
|
||||||
|
android:nextFocusDown="@id/result_description"
|
||||||
|
android:nextFocusLeft="@id/result_subscribe"
|
||||||
|
android:nextFocusRight="@id/result_share"
|
||||||
|
|
||||||
|
android:id="@+id/result_favorite"
|
||||||
|
android:layout_width="25dp"
|
||||||
|
android:layout_height="25dp"
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:elevation="10dp"
|
||||||
|
|
||||||
|
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||||
|
android:src="@drawable/ic_baseline_favorite_border_24"
|
||||||
|
android:layout_gravity="end|center_vertical"
|
||||||
|
app:tint="?attr/textColor" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:nextFocusUp="@id/result_back"
|
android:nextFocusUp="@id/result_back"
|
||||||
android:nextFocusDown="@id/result_description"
|
android:nextFocusDown="@id/result_description"
|
||||||
android:nextFocusLeft="@id/result_subscribe"
|
android:nextFocusLeft="@id/result_favorite"
|
||||||
android:nextFocusRight="@id/result_open_in_browser"
|
android:nextFocusRight="@id/result_open_in_browser"
|
||||||
|
|
||||||
android:id="@+id/result_share"
|
android:id="@+id/result_share"
|
||||||
|
|
|
@ -124,28 +124,27 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
||||||
android:textColor="?attr/textColor" />
|
android:textColor="?attr/textColor" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/background_poster_holder"
|
android:id="@+id/background_poster_holder"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="150dp"
|
android:layout_height="250dp"
|
||||||
android:visibility="visible">
|
android:visibility="visible">
|
||||||
|
|
||||||
<ImageView
|
<com.lagradost.cloudstream3.utils.PercentageCropImageView
|
||||||
android:id="@+id/background_poster"
|
android:id="@+id/background_poster"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="250dp"
|
android:layout_height="275dp"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:alpha="0.8"
|
android:alpha="0.8"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="matrix"
|
||||||
tools:src="@drawable/profile_bg_dark_blue" />
|
tools:src="@drawable/profile_bg_dark_blue" >
|
||||||
|
</com.lagradost.cloudstream3.utils.PercentageCropImageView>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="100dp"
|
android:layout_height="120dp"
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
android:src="@drawable/background_shadow">
|
android:src="@drawable/background_shadow">
|
||||||
|
|
||||||
</ImageView>
|
</ImageView>
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
|
@ -155,7 +154,6 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -166,8 +164,70 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="100dp"
|
android:orientation="vertical"
|
||||||
android:orientation="horizontal">
|
android:layout_marginTop="175dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/result_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textColor="?attr/textColor"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
tools:text="The Perfect Run The Perfect Run" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/result_next_airing_holder"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="start"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/result_episodes_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginEnd="20dp"
|
||||||
|
android:textColor="?attr/textColor"
|
||||||
|
android:textSize="17sp"
|
||||||
|
android:textStyle="normal"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:text="8 Episodes" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/result_next_airing"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textColor="?attr/grayTextColor"
|
||||||
|
android:textSize="17sp"
|
||||||
|
android:textStyle="normal"
|
||||||
|
tools:text="Episode 1022 will be released in" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/result_next_airing_time"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:gravity="start"
|
||||||
|
android:textColor="?attr/textColor"
|
||||||
|
android:textSize="17sp"
|
||||||
|
android:textStyle="normal"
|
||||||
|
tools:text="5d 3h 30m" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_marginTop="10dp">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="250dp"
|
android:layout_width="250dp"
|
||||||
|
@ -176,67 +236,11 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
||||||
android:layout_weight="0"
|
android:layout_weight="0"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/result_title"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:ellipsize="marquee"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textColor="?attr/textColor"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
tools:text="The Perfect Run The Perfect Run" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/result_next_airing_holder"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="start"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/result_episodes_text"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:layout_marginEnd="20dp"
|
|
||||||
android:textColor="?attr/textColor"
|
|
||||||
android:textSize="17sp"
|
|
||||||
android:textStyle="normal"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:text="8 Episodes" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/result_next_airing"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:gravity="center"
|
|
||||||
android:textColor="?attr/grayTextColor"
|
|
||||||
android:textSize="17sp"
|
|
||||||
android:textStyle="normal"
|
|
||||||
tools:text="Episode 1022 will be released in" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/result_next_airing_time"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:gravity="start"
|
|
||||||
android:textColor="?attr/textColor"
|
|
||||||
android:textSize="17sp"
|
|
||||||
android:textStyle="normal"
|
|
||||||
tools:text="5d 3h 30m" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/result_movie_parent"
|
android:id="@+id/result_movie_parent"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="start"
|
android:layout_gravity="start"
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:animateLayoutChanges="true"
|
android:animateLayoutChanges="true"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
tools:visibility="visible">
|
tools:visibility="visible">
|
||||||
|
@ -310,19 +314,41 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
||||||
style="@style/ResultButtonTV"
|
style="@style/ResultButtonTV"
|
||||||
android:nextFocusRight="@id/result_description"
|
android:nextFocusRight="@id/result_description"
|
||||||
android:nextFocusUp="@id/result_play_trailer"
|
android:nextFocusUp="@id/result_play_trailer"
|
||||||
android:nextFocusDown="@id/result_episodes_show"
|
android:nextFocusDown="@id/result_favorite_button"
|
||||||
|
|
||||||
android:text="@string/type_none"
|
android:text="@string/type_none"
|
||||||
android:visibility="visible"
|
android:visibility="visible"
|
||||||
app:icon="@drawable/ic_baseline_bookmark_24" />
|
app:icon="@drawable/ic_baseline_bookmark_24" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/result_favorite_button"
|
||||||
|
style="@style/ResultButtonTV"
|
||||||
|
android:nextFocusRight="@id/result_description"
|
||||||
|
android:nextFocusUp="@id/result_bookmark_button"
|
||||||
|
android:nextFocusDown="@id/result_subscribe_button"
|
||||||
|
|
||||||
|
android:text="@string/action_add_to_favorites"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:icon="@drawable/ic_baseline_favorite_border_24" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/result_subscribe_button"
|
||||||
|
style="@style/ResultButtonTV"
|
||||||
|
android:nextFocusRight="@id/result_description"
|
||||||
|
android:nextFocusUp="@id/result_favorite_button"
|
||||||
|
android:nextFocusDown="@id/result_episodes_show"
|
||||||
|
|
||||||
|
android:text="@string/action_subscribe"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:icon="@drawable/ic_baseline_favorite_border_24" />
|
||||||
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/result_episodes_show"
|
android:id="@+id/result_episodes_show"
|
||||||
style="@style/ResultButtonTV"
|
style="@style/ResultButtonTV"
|
||||||
|
|
||||||
android:nextFocusRight="@id/redirect_to_episodes"
|
android:nextFocusRight="@id/redirect_to_episodes"
|
||||||
android:nextFocusUp="@id/result_bookmark_button"
|
android:nextFocusUp="@id/result_subscribe_button"
|
||||||
android:nextFocusDown="@id/result_cast_items"
|
android:nextFocusDown="@id/result_cast_items"
|
||||||
|
|
||||||
android:text="@string/episodes"
|
android:text="@string/episodes"
|
||||||
|
@ -404,6 +430,7 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
||||||
android:fadingEdgeLength="30dp"
|
android:fadingEdgeLength="30dp"
|
||||||
android:foreground="@drawable/outline_drawable"
|
android:foreground="@drawable/outline_drawable"
|
||||||
android:maxLines="7"
|
android:maxLines="7"
|
||||||
|
android:focusable="true"
|
||||||
android:nextFocusUp="@id/result_back"
|
android:nextFocusUp="@id/result_back"
|
||||||
android:nextFocusDown="@id/result_bookmark_button"
|
android:nextFocusDown="@id/result_bookmark_button"
|
||||||
android:padding="5dp"
|
android:padding="5dp"
|
||||||
|
|
|
@ -49,6 +49,7 @@
|
||||||
app:queryBackground="@color/transparent"
|
app:queryBackground="@color/transparent"
|
||||||
app:queryHint="@string/search_hint"
|
app:queryHint="@string/search_hint"
|
||||||
app:searchIcon="@drawable/search_icon"
|
app:searchIcon="@drawable/search_icon"
|
||||||
|
app:closeIcon="@drawable/ic_baseline_close_24"
|
||||||
tools:ignore="RtlSymmetry">
|
tools:ignore="RtlSymmetry">
|
||||||
|
|
||||||
<requestFocus />
|
<requestFocus />
|
||||||
|
|
|
@ -50,6 +50,7 @@
|
||||||
app:queryBackground="@color/transparent"
|
app:queryBackground="@color/transparent"
|
||||||
app:queryHint="@string/search_hint"
|
app:queryHint="@string/search_hint"
|
||||||
app:searchIcon="@drawable/search_icon"
|
app:searchIcon="@drawable/search_icon"
|
||||||
|
app:closeIcon="@drawable/ic_baseline_close_24"
|
||||||
tools:ignore="RtlSymmetry">
|
tools:ignore="RtlSymmetry">
|
||||||
|
|
||||||
<requestFocus />
|
<requestFocus />
|
||||||
|
|
35
app/src/main/res/layout/homepage_parent_emulator.xml
Normal file
35
app/src/main/res/layout/homepage_parent_emulator.xml
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/home_child_more_info"
|
||||||
|
style="@style/WatchHeaderText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/navbar_width"
|
||||||
|
android:layout_marginEnd="0dp"
|
||||||
|
android:padding="12dp"
|
||||||
|
tools:text="@string/continue_watching"
|
||||||
|
app:drawableRightCompat="@drawable/ic_baseline_arrow_forward_24"
|
||||||
|
app:drawableTint="?attr/white"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
android:contentDescription="@string/home_more_info"/>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/home_child_recyclerview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:descendantFocusability="afterDescendants"
|
||||||
|
android:nextFocusUp="@id/home_child_more_info"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingStart="@dimen/navbar_width"
|
||||||
|
android:paddingEnd="5dp"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:listitem="@layout/home_result_grid" />
|
||||||
|
</LinearLayout>
|
25
app/src/main/res/layout/lock_pin_dialog.xml
Normal file
25
app/src/main/res/layout/lock_pin_dialog.xml
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/pinEditText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:hint="@string/pin"
|
||||||
|
android:inputType="numberPassword"
|
||||||
|
android:maxLength="4" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/pinEditTextError"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -49,6 +49,8 @@
|
||||||
app:queryBackground="@color/transparent"
|
app:queryBackground="@color/transparent"
|
||||||
|
|
||||||
app:searchIcon="@drawable/search_icon"
|
app:searchIcon="@drawable/search_icon"
|
||||||
|
app:closeIcon="@drawable/ic_baseline_close_24"
|
||||||
|
|
||||||
android:paddingStart="-10dp"
|
android:paddingStart="-10dp"
|
||||||
android:iconifiedByDefault="false"
|
android:iconifiedByDefault="false"
|
||||||
app:queryHint="@string/search_hint"
|
app:queryHint="@string/search_hint"
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
|
||||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
|
||||||
android:layout_marginTop="20dp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:text="@string/switch_account"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:textColor="?attr/textColor"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_rowWeight="1"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
|
||||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
|
||||||
android:text="@string/history"
|
|
||||||
android:textSize="15sp"
|
|
||||||
android:textColor="?attr/grayTextColor"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_rowWeight="1"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
|
||||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
|
||||||
android:text="@string/error_bookmarks_text"
|
|
||||||
android:textSize="15sp"
|
|
||||||
android:textColor="?attr/grayTextColor"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_rowWeight="1"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:descendantFocusability="afterDescendants"
|
|
||||||
android:id="@+id/profiles_recyclerview"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
|
|
||||||
android:orientation="horizontal"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
tools:itemCount="4"
|
|
||||||
tools:listitem="@layout/who_is_watching_account">
|
|
||||||
|
|
||||||
<requestFocus />
|
|
||||||
</androidx.recyclerview.widget.RecyclerView>
|
|
||||||
</LinearLayout>
|
|
|
@ -331,57 +331,56 @@
|
||||||
app:exitAnim="@anim/exit_anim"
|
app:exitAnim="@anim/exit_anim"
|
||||||
app:popEnterAnim="@anim/enter_anim"
|
app:popEnterAnim="@anim/enter_anim"
|
||||||
app:popExitAnim="@anim/exit_anim"
|
app:popExitAnim="@anim/exit_anim"
|
||||||
tools:layout="@layout/main_settings">
|
tools:layout="@layout/main_settings" />
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_navigation_settings_to_navigation_settings_ui"
|
android:id="@+id/action_navigation_global_to_navigation_settings_ui"
|
||||||
app:destination="@id/navigation_settings_ui"
|
app:destination="@id/navigation_settings_ui"
|
||||||
app:enterAnim="@anim/enter_anim"
|
app:enterAnim="@anim/enter_anim"
|
||||||
app:exitAnim="@anim/exit_anim"
|
app:exitAnim="@anim/exit_anim"
|
||||||
app:popEnterAnim="@anim/enter_anim"
|
app:popEnterAnim="@anim/enter_anim"
|
||||||
app:popExitAnim="@anim/exit_anim" />
|
app:popExitAnim="@anim/exit_anim" />
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_navigation_settings_to_navigation_settings_providers"
|
android:id="@+id/action_navigation_global_to_navigation_settings_providers"
|
||||||
app:destination="@id/navigation_settings_providers"
|
app:destination="@id/navigation_settings_providers"
|
||||||
app:enterAnim="@anim/enter_anim"
|
app:enterAnim="@anim/enter_anim"
|
||||||
app:exitAnim="@anim/exit_anim"
|
app:exitAnim="@anim/exit_anim"
|
||||||
app:popEnterAnim="@anim/enter_anim"
|
app:popEnterAnim="@anim/enter_anim"
|
||||||
app:popExitAnim="@anim/exit_anim" />
|
app:popExitAnim="@anim/exit_anim" />
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_navigation_settings_to_navigation_settings_player"
|
android:id="@+id/action_navigation_global_to_navigation_settings_player"
|
||||||
app:destination="@id/navigation_settings_player"
|
app:destination="@id/navigation_settings_player"
|
||||||
app:enterAnim="@anim/enter_anim"
|
app:enterAnim="@anim/enter_anim"
|
||||||
app:exitAnim="@anim/exit_anim"
|
app:exitAnim="@anim/exit_anim"
|
||||||
app:popEnterAnim="@anim/enter_anim"
|
app:popEnterAnim="@anim/enter_anim"
|
||||||
app:popExitAnim="@anim/exit_anim" />
|
app:popExitAnim="@anim/exit_anim" />
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_navigation_settings_to_navigation_settings_updates"
|
android:id="@+id/action_navigation_global_to_navigation_settings_updates"
|
||||||
app:destination="@id/navigation_settings_updates"
|
app:destination="@id/navigation_settings_updates"
|
||||||
app:enterAnim="@anim/enter_anim"
|
app:enterAnim="@anim/enter_anim"
|
||||||
app:exitAnim="@anim/exit_anim"
|
app:exitAnim="@anim/exit_anim"
|
||||||
app:popEnterAnim="@anim/enter_anim"
|
app:popEnterAnim="@anim/enter_anim"
|
||||||
app:popExitAnim="@anim/exit_anim" />
|
app:popExitAnim="@anim/exit_anim" />
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_navigation_settings_to_navigation_settings_account"
|
android:id="@+id/action_navigation_global_to_navigation_settings_account"
|
||||||
app:destination="@id/navigation_settings_account"
|
app:destination="@id/navigation_settings_account"
|
||||||
app:enterAnim="@anim/enter_anim"
|
app:enterAnim="@anim/enter_anim"
|
||||||
app:exitAnim="@anim/exit_anim"
|
app:exitAnim="@anim/exit_anim"
|
||||||
app:popEnterAnim="@anim/enter_anim"
|
app:popEnterAnim="@anim/enter_anim"
|
||||||
app:popExitAnim="@anim/exit_anim" />
|
app:popExitAnim="@anim/exit_anim" />
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_navigation_settings_to_navigation_settings_general"
|
android:id="@+id/action_navigation_global_to_navigation_settings_general"
|
||||||
app:destination="@id/navigation_settings_general"
|
app:destination="@id/navigation_settings_general"
|
||||||
app:enterAnim="@anim/enter_anim"
|
app:enterAnim="@anim/enter_anim"
|
||||||
app:exitAnim="@anim/exit_anim"
|
app:exitAnim="@anim/exit_anim"
|
||||||
app:popEnterAnim="@anim/enter_anim"
|
app:popEnterAnim="@anim/enter_anim"
|
||||||
app:popExitAnim="@anim/exit_anim" />
|
app:popExitAnim="@anim/exit_anim" />
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_navigation_settings_to_navigation_settings_extensions"
|
android:id="@+id/action_navigation_global_to_navigation_settings_extensions"
|
||||||
app:destination="@id/navigation_settings_extensions"
|
app:destination="@id/navigation_settings_extensions"
|
||||||
app:enterAnim="@anim/enter_anim"
|
app:enterAnim="@anim/enter_anim"
|
||||||
app:exitAnim="@anim/exit_anim"
|
app:exitAnim="@anim/exit_anim"
|
||||||
app:popEnterAnim="@anim/enter_anim"
|
app:popEnterAnim="@anim/enter_anim"
|
||||||
app:popExitAnim="@anim/exit_anim" />
|
app:popExitAnim="@anim/exit_anim" />
|
||||||
</fragment>
|
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/navigation_subtitles"
|
android:id="@+id/navigation_subtitles"
|
||||||
|
|
|
@ -74,12 +74,10 @@
|
||||||
<string name="popup_resume_download">ማውረድ ቀጥል</string>
|
<string name="popup_resume_download">ማውረድ ቀጥል</string>
|
||||||
<string name="subs_text_color">የጽሑፍ ቀለም</string>
|
<string name="subs_text_color">የጽሑፍ ቀለም</string>
|
||||||
<string name="type_completed">የተጠናቀቀ</string>
|
<string name="type_completed">የተጠናቀቀ</string>
|
||||||
<string name="type_none">ምንም</string>
|
|
||||||
<string name="play_trailer_button">የፊልም ማስታወቂያ አጫውት</string>
|
<string name="play_trailer_button">የፊልም ማስታወቂያ አጫውት</string>
|
||||||
<string name="play_livestream_button">የቀጥታ ስርጭት አጫውት</string>
|
<string name="play_livestream_button">የቀጥታ ስርጭት አጫውት</string>
|
||||||
<string name="popup_play_file">ፋይል አጫውት</string>
|
<string name="popup_play_file">ፋይል አጫውት</string>
|
||||||
<string name="type_re_watching">እንደገና በማየት ላይ</string>
|
<string name="type_re_watching">እንደገና በማየት ላይ</string>
|
||||||
<string name="sort_cancel">ሰርዝ</string>
|
|
||||||
<string name="go_back">ወደ ኋላ መመለሻ</string>
|
<string name="go_back">ወደ ኋላ መመለሻ</string>
|
||||||
<string name="home_info">መረጃ</string>
|
<string name="home_info">መረጃ</string>
|
||||||
<string name="sort_save">ያስቀምጡ</string>
|
<string name="sort_save">ያስቀምጡ</string>
|
||||||
|
|
|
@ -12,8 +12,8 @@
|
||||||
<!-- TRANSLATE, BUT DON'T FORGET FORMAT -->
|
<!-- TRANSLATE, BUT DON'T FORGET FORMAT -->
|
||||||
<string name="player_speed_text_format" formatted="true">سرعة (%.2fx)</string>
|
<string name="player_speed_text_format" formatted="true">سرعة (%.2fx)</string>
|
||||||
<string name="rated_format" formatted="true">تقييم: %.1f</string>
|
<string name="rated_format" formatted="true">تقييم: %.1f</string>
|
||||||
<string name="new_update_format" formatted="true">!تم العثور على تحديث جديد
|
<string name="new_update_format" formatted="true">يوجد تحديث جديد!
|
||||||
\n%s -> %s</string>
|
\n%1$s -> %2$s</string>
|
||||||
<string name="duration_format" formatted="true">%d دقيقة</string>
|
<string name="duration_format" formatted="true">%d دقيقة</string>
|
||||||
<string name="app_name">CloudStream</string>
|
<string name="app_name">CloudStream</string>
|
||||||
<string name="play_with_app_name">تشغيل بواسطة CloudStream</string>
|
<string name="play_with_app_name">تشغيل بواسطة CloudStream</string>
|
||||||
|
@ -36,7 +36,6 @@
|
||||||
<string name="type_completed">مكتمل</string>
|
<string name="type_completed">مكتمل</string>
|
||||||
<string name="type_dropped">مهمل</string>
|
<string name="type_dropped">مهمل</string>
|
||||||
<string name="type_plan_to_watch">أخطط لمشاهدته</string>
|
<string name="type_plan_to_watch">أخطط لمشاهدته</string>
|
||||||
<string name="type_none">لا شيء</string>
|
|
||||||
<string name="type_re_watching">إعادة المشاهدة</string>
|
<string name="type_re_watching">إعادة المشاهدة</string>
|
||||||
<string name="play_movie_button">مشاهدة الفيلم</string>
|
<string name="play_movie_button">مشاهدة الفيلم</string>
|
||||||
<string name="play_livestream_button">تشغيل بث حي</string>
|
<string name="play_livestream_button">تشغيل بث حي</string>
|
||||||
|
@ -74,7 +73,6 @@
|
||||||
<string name="action_remove_from_bookmarks">ازالة</string>
|
<string name="action_remove_from_bookmarks">ازالة</string>
|
||||||
<string name="action_add_to_bookmarks">إعداد حالة المشاهدة</string>
|
<string name="action_add_to_bookmarks">إعداد حالة المشاهدة</string>
|
||||||
<string name="sort_apply">تطبيق</string>
|
<string name="sort_apply">تطبيق</string>
|
||||||
<string name="sort_cancel">إلغاء</string>
|
|
||||||
<string name="sort_copy">نسخ</string>
|
<string name="sort_copy">نسخ</string>
|
||||||
<string name="sort_close">إغلاق</string>
|
<string name="sort_close">إغلاق</string>
|
||||||
<string name="sort_clear">مسح</string>
|
<string name="sort_clear">مسح</string>
|
||||||
|
@ -180,6 +178,7 @@
|
||||||
<string name="no_episodes_found">لم يتم العثور على أي حلقات</string>
|
<string name="no_episodes_found">لم يتم العثور على أي حلقات</string>
|
||||||
<string name="delete_file">حذف الملف</string>
|
<string name="delete_file">حذف الملف</string>
|
||||||
<string name="delete">حذف</string>
|
<string name="delete">حذف</string>
|
||||||
|
<string name="cancel">إلغاء</string>
|
||||||
<string name="pause">إيقاف مؤقت</string>
|
<string name="pause">إيقاف مؤقت</string>
|
||||||
<string name="resume">إستئناف</string>
|
<string name="resume">إستئناف</string>
|
||||||
<string name="go_back_30">-٣٠</string>
|
<string name="go_back_30">-٣٠</string>
|
||||||
|
@ -198,7 +197,7 @@
|
||||||
<string name="synopsis">القصة</string>
|
<string name="synopsis">القصة</string>
|
||||||
<string name="queued">في قائمة الانتظار</string>
|
<string name="queued">في قائمة الانتظار</string>
|
||||||
<string name="no_subtitles">الترجمة ليست موجودة</string>
|
<string name="no_subtitles">الترجمة ليست موجودة</string>
|
||||||
<string name="default_subtitles">الإفتراضي</string>
|
<string name="action_default">الإفتراضي</string>
|
||||||
<string name="free_storage">فارغ</string>
|
<string name="free_storage">فارغ</string>
|
||||||
<string name="used_storage">مستخدم</string>
|
<string name="used_storage">مستخدم</string>
|
||||||
<string name="app_storage">التطبيق</string>
|
<string name="app_storage">التطبيق</string>
|
||||||
|
@ -320,7 +319,7 @@
|
||||||
<string name="kitsu_account_settings" translatable="false">Kitsu</string>
|
<string name="kitsu_account_settings" translatable="false">Kitsu</string>
|
||||||
<string name="trakt_account_settings" translatable="false">Trakt</string>
|
<string name="trakt_account_settings" translatable="false">Trakt</string>
|
||||||
-->
|
-->
|
||||||
<string name="login_format" formatted="true">%s %s</string>
|
<string name="login_format" formatted="true">%1$s %2$s</string>
|
||||||
<string name="account">حساب</string>
|
<string name="account">حساب</string>
|
||||||
<string name="logout">تسجيل الخروج</string>
|
<string name="logout">تسجيل الخروج</string>
|
||||||
<string name="login">تسجيل الدخول</string>
|
<string name="login">تسجيل الدخول</string>
|
||||||
|
@ -419,8 +418,8 @@
|
||||||
<string name="plugin_deleted">تم إزالة الإضافة</string>
|
<string name="plugin_deleted">تم إزالة الإضافة</string>
|
||||||
<string name="plugin_load_fail" formatted="true">تعذر التحميل %s</string>
|
<string name="plugin_load_fail" formatted="true">تعذر التحميل %s</string>
|
||||||
<string name="is_adult">18+</string>
|
<string name="is_adult">18+</string>
|
||||||
<string name="batch_download_start_format" formatted="true">بدأ تنزيل %d %s …</string>
|
<string name="batch_download_start_format" formatted="true">بدأ تنزيل %1$d %2$s…</string>
|
||||||
<string name="batch_download_finish_format" formatted="true">تم التنزيل %d %s</string>
|
<string name="batch_download_finish_format" formatted="true">تم تنزيل %1$d %2$s</string>
|
||||||
<string name="batch_download_nothing_to_download_format" formatted="true">جميع %s محملة بالفعل</string>
|
<string name="batch_download_nothing_to_download_format" formatted="true">جميع %s محملة بالفعل</string>
|
||||||
<string name="batch_download">تحميل مكثف</string>
|
<string name="batch_download">تحميل مكثف</string>
|
||||||
<string name="plugin_singular">إضافة</string>
|
<string name="plugin_singular">إضافة</string>
|
||||||
|
@ -462,11 +461,11 @@
|
||||||
<string name="history">السجل</string>
|
<string name="history">السجل</string>
|
||||||
<string name="enable_skip_op_from_database_des">عرض زر تخطي المقدمة/الخاتمة</string>
|
<string name="enable_skip_op_from_database_des">عرض زر تخطي المقدمة/الخاتمة</string>
|
||||||
<string name="cast_format" formatted="true">طاقم العمل: %s</string>
|
<string name="cast_format" formatted="true">طاقم العمل: %s</string>
|
||||||
<string name="next_episode_time_day_format" formatted="true">%d يوم %d ساعة %d دقيقة</string>
|
<string name="next_episode_time_day_format" formatted="true">%1$d يوم %2$d ساعة %3$d دقيقة</string>
|
||||||
<string name="next_episode_time_hour_format" formatted="true">%d ساعة %d دقيقة</string>
|
<string name="next_episode_time_hour_format" formatted="true">%1$d ساعة %2$d دقيقة</string>
|
||||||
<string name="filler" formatted="true">الفيلير</string>
|
<string name="filler" formatted="true">الفيلير</string>
|
||||||
<string name="action_open_play">فتح(تشغيل)</string>
|
<string name="action_open_play">فتح(تشغيل)</string>
|
||||||
<string name="season_format">%s %d%s</string>
|
<string name="season_format">%1$s %2$d%3$s</string>
|
||||||
<string name="plugins_updated" formatted="true">المكونات الإضافية المحدثة %d</string>
|
<string name="plugins_updated" formatted="true">المكونات الإضافية المحدثة %d</string>
|
||||||
<string name="player_settings_play_in_vlc">VLC</string>
|
<string name="player_settings_play_in_vlc">VLC</string>
|
||||||
<string name="player_settings_play_in_mpv">MPV</string>
|
<string name="player_settings_play_in_mpv">MPV</string>
|
||||||
|
@ -483,7 +482,7 @@
|
||||||
<string name="action_mark_as_watched">علّمه كفيديو تمت مشاهدته</string>
|
<string name="action_mark_as_watched">علّمه كفيديو تمت مشاهدته</string>
|
||||||
<string name="yes">نعم</string>
|
<string name="yes">نعم</string>
|
||||||
<string name="no">ﻻ</string>
|
<string name="no">ﻻ</string>
|
||||||
<string name="app_dub_sub_episode_text_format" formatted="true">%s الحلقة %d</string>
|
<string name="app_dub_sub_episode_text_format" formatted="true">%1$s الحلقة %2$d</string>
|
||||||
<string name="next_episode_format" formatted="true">سيتم إصدار الحلقة %d في</string>
|
<string name="next_episode_format" formatted="true">سيتم إصدار الحلقة %d في</string>
|
||||||
<string name="update_notification_failed">تعذر تثبيت الإصدار الجديد من التطبيق</string>
|
<string name="update_notification_failed">تعذر تثبيت الإصدار الجديد من التطبيق</string>
|
||||||
<string name="extension_install_first">تثبيت الإضافة أولا</string>
|
<string name="extension_install_first">تثبيت الإضافة أولا</string>
|
||||||
|
@ -494,8 +493,8 @@
|
||||||
<string name="update_notification_downloading">تنزيل تحديث التطبيق…</string>
|
<string name="update_notification_downloading">تنزيل تحديث التطبيق…</string>
|
||||||
<string name="update_notification_installing">تثبيت تحديث التطبيق…</string>
|
<string name="update_notification_installing">تثبيت تحديث التطبيق…</string>
|
||||||
<string name="next_episode_time_min_format" formatted="true">%d دقيقة</string>
|
<string name="next_episode_time_min_format" formatted="true">%d دقيقة</string>
|
||||||
<string name="episodes_range">%d-%d</string>
|
<string name="episodes_range">%1$d-%2$d</string>
|
||||||
<string name="episode_format" formatted="true">%d %s</string>
|
<string name="episode_format" formatted="true">%1$d %2$s</string>
|
||||||
<string name="confirm_exit_dialog">هل أنت متأكد أنك تريد الخروج؟</string>
|
<string name="confirm_exit_dialog">هل أنت متأكد أنك تريد الخروج؟</string>
|
||||||
<string name="automatic_plugin_download_summary">قم بتثبيت جميع المكونات الإضافية التي لم يتم تثبيتها بعد تلقائيا من المستودعات المضافة.</string>
|
<string name="automatic_plugin_download_summary">قم بتثبيت جميع المكونات الإضافية التي لم يتم تثبيتها بعد تلقائيا من المستودعات المضافة.</string>
|
||||||
<string name="apk_installer_settings">مثبت الحزم</string>
|
<string name="apk_installer_settings">مثبت الحزم</string>
|
||||||
|
@ -581,9 +580,34 @@
|
||||||
<string name="unable_to_inflate">تعذر إنشاء واجهة المستخدم بشكل صحيح ، وهذا خطأ كبير ويجب الإبلاغ عنه على الفور %s</string>
|
<string name="unable_to_inflate">تعذر إنشاء واجهة المستخدم بشكل صحيح ، وهذا خطأ كبير ويجب الإبلاغ عنه على الفور %s</string>
|
||||||
<string name="automatic_plugin_download_mode_title">حدد الوضع لتصفية تنزيل المكونات الإضافية</string>
|
<string name="automatic_plugin_download_mode_title">حدد الوضع لتصفية تنزيل المكونات الإضافية</string>
|
||||||
<string name="disable">تعطيل</string>
|
<string name="disable">تعطيل</string>
|
||||||
<string name="default_account">@string/default_subtitles</string>
|
|
||||||
<string name="no_plugins_found_error">لا توجد اضافة في المستودع</string>
|
<string name="no_plugins_found_error">لا توجد اضافة في المستودع</string>
|
||||||
<string name="no_repository_found_error">المستودع لم يتم العثور عليه، تحقق من العنوان اوجرب شبكة افتراضية خاصة(vpn)</string>
|
<string name="no_repository_found_error">المستودع لم يتم العثور عليه، تحقق من العنوان اوجرب شبكة افتراضية خاصة(vpn)</string>
|
||||||
<string name="already_voted">لقد صوتت بالفعل</string>
|
<string name="already_voted">لقد صوتت بالفعل</string>
|
||||||
<string name="backup_frequency">معدل النسخ الإحتياطي</string>
|
<string name="backup_frequency">معدل النسخ الإحتياطي</string>
|
||||||
|
<string name="favorite_removed">تمت إزالة %s من المفضلة</string>
|
||||||
|
<string name="favorites_list_name">المفضلة</string>
|
||||||
|
<string name="favorite_added">تمت إضافة %s إلى المفضلة</string>
|
||||||
|
<string name="duplicate_message_multiple" formatted="true">احتمال وجود تكرارات في مكتبتك.
|
||||||
|
\n
|
||||||
|
\n%s
|
||||||
|
\n
|
||||||
|
\nهل تريد الاضافة على اي حال مستبدلاً النسخة الموجودة بالفعل, أم تفضل إلغاء العملية؟</string>
|
||||||
|
<string name="duplicate_title">احتمال أن يكون موجود بالفعل</string>
|
||||||
|
<string name="lock_profile">قفل الحساب</string>
|
||||||
|
<string name="action_add_to_favorites">اضافة الى المفضلة</string>
|
||||||
|
<string name="duplicate_replace_all">تبديل الكل</string>
|
||||||
|
<string name="pin_error_incorrect">رقم PIN غير صحيح. برجاء المحاولة مرة اخرى.</string>
|
||||||
|
<string name="action_unsubscribe">إلغاء الاشتراك</string>
|
||||||
|
<string name="pin_error_length">رقم ال PIN يجب ان يكون 4 ارقام</string>
|
||||||
|
<string name="duplicate_replace">استبدال</string>
|
||||||
|
<string name="duplicate_add">اضافة</string>
|
||||||
|
<string name="action_subscribe">إشترك</string>
|
||||||
|
<string name="action_remove_from_favorites">إزالة من المفضلة</string>
|
||||||
|
<string name="select_an_account">اختار حساب</string>
|
||||||
|
<string name="duplicate_message_single">من الظاهر أن \"%1$s\" موجود بالفعل في مكتبتك.
|
||||||
|
\n
|
||||||
|
\nهل تريد الاضافة على أي حال مستبدلاً القديم أو إلغاء العملية؟</string>
|
||||||
|
<string name="enter_pin">ادخال ال PIN</string>
|
||||||
|
<string name="pin">PIN</string>
|
||||||
|
<string name="enter_current_pin">أدخل ال PIN الحالي</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -34,7 +34,6 @@
|
||||||
<string name="result_tags">الأنواع</string>
|
<string name="result_tags">الأنواع</string>
|
||||||
<string name="download_paused">توقف التنزيل</string>
|
<string name="download_paused">توقف التنزيل</string>
|
||||||
<string name="type_plan_to_watch">خطط للمشاهدة</string>
|
<string name="type_plan_to_watch">خطط للمشاهدة</string>
|
||||||
<string name="type_none">لا يوجد</string>
|
|
||||||
<string name="type_re_watching">إعادة المشاهدة</string>
|
<string name="type_re_watching">إعادة المشاهدة</string>
|
||||||
<string name="new_update_format" formatted="true">!تم العثور على تحديث جديد
|
<string name="new_update_format" formatted="true">!تم العثور على تحديث جديد
|
||||||
\n%s->%s</string>
|
\n%s->%s</string>
|
||||||
|
@ -125,6 +124,7 @@
|
||||||
<string name="season">موسم</string>
|
<string name="season">موسم</string>
|
||||||
<string name="copy_link_toast">تم نسخ الرابط إلى الحافظة</string>
|
<string name="copy_link_toast">تم نسخ الرابط إلى الحافظة</string>
|
||||||
<string name="delete">مسح</string>
|
<string name="delete">مسح</string>
|
||||||
|
<string name="cancel">الغي</string>
|
||||||
<string name="pause">وقف</string>
|
<string name="pause">وقف</string>
|
||||||
<string name="update_notification_downloading">جارٍ تنزيل تحديث التطبيق…</string>
|
<string name="update_notification_downloading">جارٍ تنزيل تحديث التطبيق…</string>
|
||||||
<string name="subs_default_reset_toast">إعادة التعيين إلى القيمة العادية</string>
|
<string name="subs_default_reset_toast">إعادة التعيين إلى القيمة العادية</string>
|
||||||
|
@ -208,7 +208,6 @@
|
||||||
<string name="popup_resume_download">استئناف تحميل</string>
|
<string name="popup_resume_download">استئناف تحميل</string>
|
||||||
<string name="home_info">معلومات</string>
|
<string name="home_info">معلومات</string>
|
||||||
<string name="popup_pause_download">وقفة التحميل</string>
|
<string name="popup_pause_download">وقفة التحميل</string>
|
||||||
<string name="sort_cancel">الغي</string>
|
|
||||||
<string name="sort_save">احفظ</string>
|
<string name="sort_save">احفظ</string>
|
||||||
<string name="subtitles_settings">إعدادات الترجمة</string>
|
<string name="subtitles_settings">إعدادات الترجمة</string>
|
||||||
<string name="subs_text_color">لون الخط</string>
|
<string name="subs_text_color">لون الخط</string>
|
||||||
|
|
|
@ -41,7 +41,6 @@
|
||||||
<string name="type_completed">Завършено</string>
|
<string name="type_completed">Завършено</string>
|
||||||
<string name="type_dropped">Изпуснат</string>
|
<string name="type_dropped">Изпуснат</string>
|
||||||
<string name="type_plan_to_watch">План за гледане</string>
|
<string name="type_plan_to_watch">План за гледане</string>
|
||||||
<string name="type_none">Нито един</string>
|
|
||||||
<string name="type_re_watching">Повторно гледане</string>
|
<string name="type_re_watching">Повторно гледане</string>
|
||||||
<string name="play_movie_button">Пускане на филм</string>
|
<string name="play_movie_button">Пускане на филм</string>
|
||||||
<string name="play_livestream_button">Възпроизвеждане на живо</string>
|
<string name="play_livestream_button">Възпроизвеждане на живо</string>
|
||||||
|
@ -78,7 +77,6 @@
|
||||||
<string name="action_remove_from_bookmarks">Премахване</string>
|
<string name="action_remove_from_bookmarks">Премахване</string>
|
||||||
<string name="action_add_to_bookmarks">Задайте статус на гледане</string>
|
<string name="action_add_to_bookmarks">Задайте статус на гледане</string>
|
||||||
<string name="sort_apply">Приложи</string>
|
<string name="sort_apply">Приложи</string>
|
||||||
<string name="sort_cancel">Отказ</string>
|
|
||||||
<string name="sort_copy">Копирай</string>
|
<string name="sort_copy">Копирай</string>
|
||||||
<string name="sort_close">Затвори</string>
|
<string name="sort_close">Затвори</string>
|
||||||
<string name="sort_clear">Изчисти</string>
|
<string name="sort_clear">Изчисти</string>
|
||||||
|
@ -187,6 +185,7 @@
|
||||||
<string name="no_episodes_found">Няма намерени епизоди</string>
|
<string name="no_episodes_found">Няма намерени епизоди</string>
|
||||||
<string name="delete_file">Изтрий файла</string>
|
<string name="delete_file">Изтрий файла</string>
|
||||||
<string name="delete">Изтрий</string>
|
<string name="delete">Изтрий</string>
|
||||||
|
<string name="cancel">Отказ</string>
|
||||||
<string name="pause">Пауза</string>
|
<string name="pause">Пауза</string>
|
||||||
<string name="resume">Продължи</string>
|
<string name="resume">Продължи</string>
|
||||||
<string name="go_back_30">-30</string>
|
<string name="go_back_30">-30</string>
|
||||||
|
@ -205,7 +204,7 @@
|
||||||
<string name="synopsis">Синопсис</string>
|
<string name="synopsis">Синопсис</string>
|
||||||
<string name="queued">На опашката</string>
|
<string name="queued">На опашката</string>
|
||||||
<string name="no_subtitles">Без субтитри</string>
|
<string name="no_subtitles">Без субтитри</string>
|
||||||
<string name="default_subtitles">По подразбиране</string>
|
<string name="action_default">По подразбиране</string>
|
||||||
<string name="free_storage">Безплатно</string>
|
<string name="free_storage">Безплатно</string>
|
||||||
<string name="used_storage">Използвано</string>
|
<string name="used_storage">Използвано</string>
|
||||||
<string name="app_storage">Приложения</string>
|
<string name="app_storage">Приложения</string>
|
||||||
|
|
|
@ -43,7 +43,6 @@
|
||||||
<string name="type_completed">শেষ</string>
|
<string name="type_completed">শেষ</string>
|
||||||
<string name="type_dropped">বাদ</string>
|
<string name="type_dropped">বাদ</string>
|
||||||
<string name="type_plan_to_watch">দেখার ইচ্ছায়</string>
|
<string name="type_plan_to_watch">দেখার ইচ্ছায়</string>
|
||||||
<string name="type_none">কোন কিছুই না</string>
|
|
||||||
<string name="type_re_watching">পুনরায় দেখা হচ্ছে</string>
|
<string name="type_re_watching">পুনরায় দেখা হচ্ছে</string>
|
||||||
<string name="play_torrent_button">টরেন্ট স্ট্রিম করুন</string>
|
<string name="play_torrent_button">টরেন্ট স্ট্রিম করুন</string>
|
||||||
<string name="pick_source">উৎসসমূহ</string>
|
<string name="pick_source">উৎসসমূহ</string>
|
||||||
|
@ -72,7 +71,6 @@
|
||||||
<string name="action_remove_from_bookmarks">বাদ দিন</string>
|
<string name="action_remove_from_bookmarks">বাদ দিন</string>
|
||||||
<string name="action_add_to_bookmarks">বুকমার্ক করুন</string>
|
<string name="action_add_to_bookmarks">বুকমার্ক করুন</string>
|
||||||
<string name="sort_apply">প্রয়োগ করুন</string>
|
<string name="sort_apply">প্রয়োগ করুন</string>
|
||||||
<string name="sort_cancel">বাদ দিন</string>
|
|
||||||
<string name="sort_copy">কপি করুন</string>
|
<string name="sort_copy">কপি করুন</string>
|
||||||
<string name="sort_close">বন্ধ করুন</string>
|
<string name="sort_close">বন্ধ করুন</string>
|
||||||
<string name="sort_clear">মুছুন</string>
|
<string name="sort_clear">মুছুন</string>
|
||||||
|
|
|
@ -44,7 +44,6 @@
|
||||||
<string name="type_completed">Completado</string>
|
<string name="type_completed">Completado</string>
|
||||||
<string name="type_dropped">Deixado</string>
|
<string name="type_dropped">Deixado</string>
|
||||||
<string name="type_plan_to_watch">Planejando assistir</string>
|
<string name="type_plan_to_watch">Planejando assistir</string>
|
||||||
<string name="type_none">Nenhum</string>
|
|
||||||
<string name="type_re_watching">Reassistindo</string>
|
<string name="type_re_watching">Reassistindo</string>
|
||||||
<string name="play_movie_button">Assistir Filme</string>
|
<string name="play_movie_button">Assistir Filme</string>
|
||||||
<string name="play_torrent_button">Transmitir Torrent</string>
|
<string name="play_torrent_button">Transmitir Torrent</string>
|
||||||
|
@ -81,7 +80,6 @@
|
||||||
<string name="action_remove_from_bookmarks">Remover</string>
|
<string name="action_remove_from_bookmarks">Remover</string>
|
||||||
<string name="action_add_to_bookmarks">Selecionar marcador</string>
|
<string name="action_add_to_bookmarks">Selecionar marcador</string>
|
||||||
<string name="sort_apply">Aplicar</string>
|
<string name="sort_apply">Aplicar</string>
|
||||||
<string name="sort_cancel">Cancelar</string>
|
|
||||||
<string name="sort_copy">Copiar</string>
|
<string name="sort_copy">Copiar</string>
|
||||||
<string name="sort_close">Fechar</string>
|
<string name="sort_close">Fechar</string>
|
||||||
<string name="sort_clear">Limpar</string>
|
<string name="sort_clear">Limpar</string>
|
||||||
|
@ -185,6 +183,7 @@
|
||||||
<string name="no_episodes_found">Nenhum Episódio encontrado</string>
|
<string name="no_episodes_found">Nenhum Episódio encontrado</string>
|
||||||
<string name="delete_file">Apagar Arquivo</string>
|
<string name="delete_file">Apagar Arquivo</string>
|
||||||
<string name="delete">Deletar</string>
|
<string name="delete">Deletar</string>
|
||||||
|
<string name="cancel">Cancelar</string>
|
||||||
<string name="pause">Pausar</string>
|
<string name="pause">Pausar</string>
|
||||||
<string name="resume">Retomar</string>
|
<string name="resume">Retomar</string>
|
||||||
<string name="go_back_30">-30</string>
|
<string name="go_back_30">-30</string>
|
||||||
|
@ -203,7 +202,7 @@
|
||||||
<string name="synopsis">Sinopse</string>
|
<string name="synopsis">Sinopse</string>
|
||||||
<string name="queued">Na fila</string>
|
<string name="queued">Na fila</string>
|
||||||
<string name="no_subtitles">Sem Legendas</string>
|
<string name="no_subtitles">Sem Legendas</string>
|
||||||
<string name="default_subtitles">Padrão</string>
|
<string name="action_default">Padrão</string>
|
||||||
<string name="free_storage">Livre</string>
|
<string name="free_storage">Livre</string>
|
||||||
<string name="used_storage">Usado</string>
|
<string name="used_storage">Usado</string>
|
||||||
<string name="app_storage">App</string>
|
<string name="app_storage">App</string>
|
||||||
|
@ -569,7 +568,6 @@
|
||||||
<string name="wifi">Wi-Fi</string>
|
<string name="wifi">Wi-Fi</string>
|
||||||
<string name="player_settings_play_in_web">Lista de videos da web</string>
|
<string name="player_settings_play_in_web">Lista de videos da web</string>
|
||||||
<string name="unable_to_inflate">A interface de usuário não foi gerada corretamente. Isto se trata de um bug importante e deve ser reportado imediatamente %s</string>
|
<string name="unable_to_inflate">A interface de usuário não foi gerada corretamente. Isto se trata de um bug importante e deve ser reportado imediatamente %s</string>
|
||||||
<string name="default_account">Legendas padrão da conta</string>
|
|
||||||
<string name="pref_category_ui_features">Características da interface de usuário</string>
|
<string name="pref_category_ui_features">Características da interface de usuário</string>
|
||||||
<string name="category_provider_test">Provedor de teste</string>
|
<string name="category_provider_test">Provedor de teste</string>
|
||||||
<string name="pref_category_player_layout">Layout</string>
|
<string name="pref_category_player_layout">Layout</string>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<resources>
|
<resources>
|
||||||
<!-- KEYS DON'T TRANSLATE -->
|
<!-- KEYS DON'T TRANSLATE -->
|
||||||
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
|
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
|
||||||
<string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string>
|
<string name="app_dub_sub_episode_text_format" formatted="true">%1$s Ep %2$d</string>
|
||||||
<string name="cast_format" formatted="true">Hrají: %s</string>
|
<string name="cast_format" formatted="true">Hrají: %s</string>
|
||||||
<!-- IS NOT NEEDED TO TRANSLATE AS THEY ARE ONLY USED FOR SCREEN READERS AND WONT SHOW UP TO NORMAL USERS -->
|
<!-- IS NOT NEEDED TO TRANSLATE AS THEY ARE ONLY USED FOR SCREEN READERS AND WONT SHOW UP TO NORMAL USERS -->
|
||||||
<string name="result_poster_img_des">Plakát</string>
|
<string name="result_poster_img_des">Plakát</string>
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
<string name="player_speed_text_format" formatted="true">Rychlost (%.2fx)</string>
|
<string name="player_speed_text_format" formatted="true">Rychlost (%.2fx)</string>
|
||||||
<string name="rated_format" formatted="true">Hodnocení: %.1f</string>
|
<string name="rated_format" formatted="true">Hodnocení: %.1f</string>
|
||||||
<string name="new_update_format" formatted="true">Nalezena nová aktualizace!
|
<string name="new_update_format" formatted="true">Nalezena nová aktualizace!
|
||||||
\n%s -> %s</string>
|
\n%1$s -> %2$s</string>
|
||||||
<string name="filler" formatted="true">Výplň</string>
|
<string name="filler" formatted="true">Výplň</string>
|
||||||
<string name="duration_format" formatted="true">%d min</string>
|
<string name="duration_format" formatted="true">%d min</string>
|
||||||
<string name="app_name">CloudStream</string>
|
<string name="app_name">CloudStream</string>
|
||||||
|
@ -40,7 +40,6 @@
|
||||||
<string name="type_completed">Dokončeno</string>
|
<string name="type_completed">Dokončeno</string>
|
||||||
<string name="type_dropped">Zahozeno</string>
|
<string name="type_dropped">Zahozeno</string>
|
||||||
<string name="type_plan_to_watch">Plánuji sledovat</string>
|
<string name="type_plan_to_watch">Plánuji sledovat</string>
|
||||||
<string name="type_none">Žádné</string>
|
|
||||||
<string name="type_re_watching">Opětovné sledování</string>
|
<string name="type_re_watching">Opětovné sledování</string>
|
||||||
<string name="play_movie_button">Přehrát film</string>
|
<string name="play_movie_button">Přehrát film</string>
|
||||||
<string name="play_torrent_button">Streamovat torrent</string>
|
<string name="play_torrent_button">Streamovat torrent</string>
|
||||||
|
@ -76,7 +75,6 @@
|
||||||
<string name="action_remove_from_bookmarks">Odebrat</string>
|
<string name="action_remove_from_bookmarks">Odebrat</string>
|
||||||
<string name="action_add_to_bookmarks">Nastavit stav sledování</string>
|
<string name="action_add_to_bookmarks">Nastavit stav sledování</string>
|
||||||
<string name="sort_apply">Použít</string>
|
<string name="sort_apply">Použít</string>
|
||||||
<string name="sort_cancel">Zrušit</string>
|
|
||||||
<string name="sort_copy">Kopírovat</string>
|
<string name="sort_copy">Kopírovat</string>
|
||||||
<string name="sort_close">Zavřít</string>
|
<string name="sort_close">Zavřít</string>
|
||||||
<string name="sort_clear">Vymazat</string>
|
<string name="sort_clear">Vymazat</string>
|
||||||
|
@ -176,6 +174,7 @@
|
||||||
<string name="no_episodes_found">Nenalezeny žádné epizody</string>
|
<string name="no_episodes_found">Nenalezeny žádné epizody</string>
|
||||||
<string name="delete_file">Smazat soubor</string>
|
<string name="delete_file">Smazat soubor</string>
|
||||||
<string name="delete">Smazat</string>
|
<string name="delete">Smazat</string>
|
||||||
|
<string name="cancel">Zrušit</string>
|
||||||
<string name="pause">Pozastavit</string>
|
<string name="pause">Pozastavit</string>
|
||||||
<string name="resume">Pokračovat</string>
|
<string name="resume">Pokračovat</string>
|
||||||
<string name="go_back_30">-30</string>
|
<string name="go_back_30">-30</string>
|
||||||
|
@ -194,7 +193,7 @@
|
||||||
<string name="synopsis">Synopse</string>
|
<string name="synopsis">Synopse</string>
|
||||||
<string name="queued">ve frontě</string>
|
<string name="queued">ve frontě</string>
|
||||||
<string name="no_subtitles">Žádné titulky</string>
|
<string name="no_subtitles">Žádné titulky</string>
|
||||||
<string name="default_subtitles">Výchozí</string>
|
<string name="action_default">Výchozí</string>
|
||||||
<string name="free_storage">Volné</string>
|
<string name="free_storage">Volné</string>
|
||||||
<string name="used_storage">Použito</string>
|
<string name="used_storage">Použito</string>
|
||||||
<string name="app_storage">Aplikace</string>
|
<string name="app_storage">Aplikace</string>
|
||||||
|
@ -293,7 +292,7 @@
|
||||||
<string name="kitsu_account_settings" translatable="false">Kitsu</string>
|
<string name="kitsu_account_settings" translatable="false">Kitsu</string>
|
||||||
<string name="trakt_account_settings" translatable="false">Trakt</string>
|
<string name="trakt_account_settings" translatable="false">Trakt</string>
|
||||||
-->
|
-->
|
||||||
<string name="login_format" formatted="true">%s %s</string>
|
<string name="login_format" formatted="true">%1$s %2$s</string>
|
||||||
<string name="account">účet</string>
|
<string name="account">účet</string>
|
||||||
<string name="logout">Odhlásit se</string>
|
<string name="logout">Odhlásit se</string>
|
||||||
<string name="login">Přihlásit se</string>
|
<string name="login">Přihlásit se</string>
|
||||||
|
@ -411,17 +410,17 @@
|
||||||
<string name="clipboard_too_large">Příliš mnoho textu. Nepodařilo se uložit do schránky.</string>
|
<string name="clipboard_too_large">Příliš mnoho textu. Nepodařilo se uložit do schránky.</string>
|
||||||
<string name="yes">Ano</string>
|
<string name="yes">Ano</string>
|
||||||
<string name="browser">Prohlížeč</string>
|
<string name="browser">Prohlížeč</string>
|
||||||
<string name="episodes_range">%d-%d</string>
|
<string name="episodes_range">%1$d-%2$d</string>
|
||||||
<string name="library">Knihovna</string>
|
<string name="library">Knihovna</string>
|
||||||
<string name="kitsu_settings">Zobrazit plakáty z Kitsu</string>
|
<string name="kitsu_settings">Zobrazit plakáty z Kitsu</string>
|
||||||
<string name="automatic_plugin_download">Automaticky stahovat doplňky</string>
|
<string name="automatic_plugin_download">Automaticky stahovat doplňky</string>
|
||||||
<string name="redo_setup_process">Znovu provést proces nastavení</string>
|
<string name="redo_setup_process">Znovu provést proces nastavení</string>
|
||||||
<string name="apk_installer_settings">Instalátor APK</string>
|
<string name="apk_installer_settings">Instalátor APK</string>
|
||||||
<string name="episode_format" formatted="true">%d %s</string>
|
<string name="episode_format" formatted="true">%1$d %2$s</string>
|
||||||
<string name="apk_installer_settings_des">Některé telefony nepodporují nový instalátor balíčků. Pokud se aktualizace nenainstalují, zkuste použít starší možnost.</string>
|
<string name="apk_installer_settings_des">Některé telefony nepodporují nový instalátor balíčků. Pokud se aktualizace nenainstalují, zkuste použít starší možnost.</string>
|
||||||
<string name="pref_category_cache">Mezipaměť</string>
|
<string name="pref_category_cache">Mezipaměť</string>
|
||||||
<string name="next_episode_format" formatted="true">Epizoda %d bude vydána za</string>
|
<string name="next_episode_format" formatted="true">Epizoda %d bude vydána za</string>
|
||||||
<string name="next_episode_time_hour_format" formatted="true">%dh %dm</string>
|
<string name="next_episode_time_hour_format" formatted="true">%1$dh %2$dm</string>
|
||||||
<string name="play_livestream_button">Přehrát přímý přenos</string>
|
<string name="play_livestream_button">Přehrát přímý přenos</string>
|
||||||
<string name="pref_category_extensions">Rozšíření</string>
|
<string name="pref_category_extensions">Rozšíření</string>
|
||||||
<string name="pref_category_actions">Akce</string>
|
<string name="pref_category_actions">Akce</string>
|
||||||
|
@ -437,7 +436,7 @@
|
||||||
<string name="preferred_media_subtext">Co chcete vidět</string>
|
<string name="preferred_media_subtext">Co chcete vidět</string>
|
||||||
<string name="plugin_downloaded">Doplněk stažen</string>
|
<string name="plugin_downloaded">Doplněk stažen</string>
|
||||||
<string name="is_adult">18+</string>
|
<string name="is_adult">18+</string>
|
||||||
<string name="batch_download_start_format" formatted="true">Spuštěno stahování %d %s…</string>
|
<string name="batch_download_start_format" formatted="true">Spuštěno stahování %1$d %2$s…</string>
|
||||||
<string name="blank_repo_message">CloudStream nemá ve výchozím nastavení nainstalované žádné weby. Stránky je třeba nainstalovat z úložišť.
|
<string name="blank_repo_message">CloudStream nemá ve výchozím nastavení nainstalované žádné weby. Stránky je třeba nainstalovat z úložišť.
|
||||||
\n
|
\n
|
||||||
\nKvůli nesmyslnému podání stížnosti DMCA společností Sky UK Limited 🤮 nemůžeme v aplikaci propojit stránky repozitářů.
|
\nKvůli nesmyslnému podání stížnosti DMCA společností Sky UK Limited 🤮 nemůžeme v aplikaci propojit stránky repozitářů.
|
||||||
|
@ -506,12 +505,12 @@
|
||||||
<string name="pref_category_app_updates">Aktualizace aplikace</string>
|
<string name="pref_category_app_updates">Aktualizace aplikace</string>
|
||||||
<string name="setup_done">Hotovo</string>
|
<string name="setup_done">Hotovo</string>
|
||||||
<string name="extension_types">Podporováno</string>
|
<string name="extension_types">Podporováno</string>
|
||||||
<string name="season_format">%s %d%s</string>
|
<string name="season_format">%1$s %2$d%3$s</string>
|
||||||
<string name="live_singular">Živý přenos</string>
|
<string name="live_singular">Živý přenos</string>
|
||||||
<string name="nsfw_singular">NSFW</string>
|
<string name="nsfw_singular">NSFW</string>
|
||||||
<string name="extensions">Rozšíření</string>
|
<string name="extensions">Rozšíření</string>
|
||||||
<string name="play_trailer_button">Přehrát trailer</string>
|
<string name="play_trailer_button">Přehrát trailer</string>
|
||||||
<string name="next_episode_time_day_format" formatted="true">%dd %dh %dm</string>
|
<string name="next_episode_time_day_format" formatted="true">%1$dd %2$dh %3$dm</string>
|
||||||
<string name="view_public_repositories_button">Zobrazit komunitní repozitáře</string>
|
<string name="view_public_repositories_button">Zobrazit komunitní repozitáře</string>
|
||||||
<string name="update_started">Aktualizace zahájena</string>
|
<string name="update_started">Aktualizace zahájena</string>
|
||||||
<string name="stream">Stream</string>
|
<string name="stream">Stream</string>
|
||||||
|
@ -521,7 +520,7 @@
|
||||||
<string name="referer">Referent</string>
|
<string name="referer">Referent</string>
|
||||||
<string name="next">Další</string>
|
<string name="next">Další</string>
|
||||||
<string name="provider_languages_tip">Sledovat videa v těchto jazycích</string>
|
<string name="provider_languages_tip">Sledovat videa v těchto jazycích</string>
|
||||||
<string name="batch_download_finish_format" formatted="true">Staženo %d %s</string>
|
<string name="batch_download_finish_format" formatted="true">Staženo %1$d %2$s</string>
|
||||||
<string name="batch_download_nothing_to_download_format" formatted="true">Všechny %s jsou již staženy</string>
|
<string name="batch_download_nothing_to_download_format" formatted="true">Všechny %s jsou již staženy</string>
|
||||||
<string name="batch_download">Hromadné stahování</string>
|
<string name="batch_download">Hromadné stahování</string>
|
||||||
<string name="plugin_singular">doplněk</string>
|
<string name="plugin_singular">doplněk</string>
|
||||||
|
@ -575,6 +574,32 @@
|
||||||
<string name="automatic_plugin_download_mode_title">Výběr režimu pro filtrování stahování doplňků</string>
|
<string name="automatic_plugin_download_mode_title">Výběr režimu pro filtrování stahování doplňků</string>
|
||||||
<string name="no_plugins_found_error">V repozitáři nebyly nalezeny žádné doplňky</string>
|
<string name="no_plugins_found_error">V repozitáři nebyly nalezeny žádné doplňky</string>
|
||||||
<string name="no_repository_found_error">Repozitář nenalezen, zkontrolujte adresu URL a zkuste použít VPN</string>
|
<string name="no_repository_found_error">Repozitář nenalezen, zkontrolujte adresu URL a zkuste použít VPN</string>
|
||||||
<string name="default_account">@string/default_subtitles</string>
|
|
||||||
<string name="already_voted">Již jste hlasovali</string>
|
<string name="already_voted">Již jste hlasovali</string>
|
||||||
|
<string name="favorite_removed">%s odebráno z oblíbených</string>
|
||||||
|
<string name="favorites_list_name">Oblíbené</string>
|
||||||
|
<string name="favorite_added">%s přidáno do oblíbených</string>
|
||||||
|
<string name="duplicate_message_multiple" formatted="true">Ve vaší knihovně byl nalezen potenciální duplikát:
|
||||||
|
\n
|
||||||
|
\n%s
|
||||||
|
\n
|
||||||
|
\nChcete přesto přidat tuto položku, nahradit existující nebo zrušit akci\?</string>
|
||||||
|
<string name="backup_frequency">Frekvence záloh</string>
|
||||||
|
<string name="duplicate_title">Nalezena potenciální duplicita</string>
|
||||||
|
<string name="lock_profile">Zamknout profil</string>
|
||||||
|
<string name="action_add_to_favorites">Přidat do oblíbených</string>
|
||||||
|
<string name="duplicate_replace_all">Nahradit vše</string>
|
||||||
|
<string name="pin_error_incorrect">Nesprávný PIN. Zkuste to prosím znovu.</string>
|
||||||
|
<string name="action_unsubscribe">Zrušit odběr</string>
|
||||||
|
<string name="pin_error_length">PIN musí obsahovat 4 znaky</string>
|
||||||
|
<string name="duplicate_replace">Nahradit</string>
|
||||||
|
<string name="duplicate_add">Přidat</string>
|
||||||
|
<string name="action_subscribe">Odebírat</string>
|
||||||
|
<string name="action_remove_from_favorites">Odebrat z oblíbených</string>
|
||||||
|
<string name="select_an_account">Vyberte účet</string>
|
||||||
|
<string name="duplicate_message_single">Vypadá to, že ve vaší knihovně již existuje potenciální duplikát: „%1$s“.
|
||||||
|
\n
|
||||||
|
\nChcete přesto přidat tuto položku, nahradit existující nebo zrušit akci\?</string>
|
||||||
|
<string name="enter_pin">Zadejte PIN</string>
|
||||||
|
<string name="pin">PIN</string>
|
||||||
|
<string name="enter_current_pin">Zadejte současný PIN</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -52,7 +52,6 @@
|
||||||
<string name="type_completed">Abgeschlossen</string>
|
<string name="type_completed">Abgeschlossen</string>
|
||||||
<string name="type_dropped">Abgebrochen</string>
|
<string name="type_dropped">Abgebrochen</string>
|
||||||
<string name="type_plan_to_watch">Geplant</string>
|
<string name="type_plan_to_watch">Geplant</string>
|
||||||
<string name="type_none">Nichts</string>
|
|
||||||
<string name="type_re_watching">Erneut schauen</string>
|
<string name="type_re_watching">Erneut schauen</string>
|
||||||
<string name="play_movie_button">Film abspielen</string>
|
<string name="play_movie_button">Film abspielen</string>
|
||||||
<string name="play_livestream_button">Livestream abspielen</string>
|
<string name="play_livestream_button">Livestream abspielen</string>
|
||||||
|
@ -89,7 +88,6 @@
|
||||||
<string name="action_remove_from_bookmarks">Entfernen</string>
|
<string name="action_remove_from_bookmarks">Entfernen</string>
|
||||||
<string name="action_add_to_bookmarks">Status setzen</string>
|
<string name="action_add_to_bookmarks">Status setzen</string>
|
||||||
<string name="sort_apply">Anwenden</string>
|
<string name="sort_apply">Anwenden</string>
|
||||||
<string name="sort_cancel">Abbrechen</string>
|
|
||||||
<string name="sort_copy">Kopieren</string>
|
<string name="sort_copy">Kopieren</string>
|
||||||
<string name="sort_close">Schließen</string>
|
<string name="sort_close">Schließen</string>
|
||||||
<string name="sort_clear">Leeren</string>
|
<string name="sort_clear">Leeren</string>
|
||||||
|
@ -192,6 +190,7 @@
|
||||||
<string name="episode_short">E</string>
|
<string name="episode_short">E</string>
|
||||||
<string name="no_episodes_found">Keine Episoden gefunden</string>
|
<string name="no_episodes_found">Keine Episoden gefunden</string>
|
||||||
<string name="delete">Löschen</string>
|
<string name="delete">Löschen</string>
|
||||||
|
<string name="cancel">Abbrechen</string>
|
||||||
<string name="pause">Pause</string>
|
<string name="pause">Pause</string>
|
||||||
<string name="resume">Fortsetzen</string>
|
<string name="resume">Fortsetzen</string>
|
||||||
<string name="go_back_30">-30</string>
|
<string name="go_back_30">-30</string>
|
||||||
|
@ -210,7 +209,7 @@
|
||||||
<string name="synopsis">Zusammenfassung</string>
|
<string name="synopsis">Zusammenfassung</string>
|
||||||
<string name="queued">In Warteschlange eingereiht</string>
|
<string name="queued">In Warteschlange eingereiht</string>
|
||||||
<string name="no_subtitles">Keine Untertitel</string>
|
<string name="no_subtitles">Keine Untertitel</string>
|
||||||
<string name="default_subtitles">Standard</string>
|
<string name="action_default">Standard</string>
|
||||||
<string name="free_storage">Frei</string>
|
<string name="free_storage">Frei</string>
|
||||||
<string name="used_storage">Belegt</string>
|
<string name="used_storage">Belegt</string>
|
||||||
<string name="app_storage">App</string>
|
<string name="app_storage">App</string>
|
||||||
|
@ -552,5 +551,4 @@
|
||||||
<string name="no_repository_found_error">Repository nicht gefunden, überprüf die URL und versuch ein VPN</string>
|
<string name="no_repository_found_error">Repository nicht gefunden, überprüf die URL und versuch ein VPN</string>
|
||||||
<string name="unable_to_inflate">Die Benutzeroberfläche konnte nicht korrekt erstellt werden. Dies ist ein SCHWERWIEGENDER FEHLER und sollte sofort gemeldet werden. %s</string>
|
<string name="unable_to_inflate">Die Benutzeroberfläche konnte nicht korrekt erstellt werden. Dies ist ein SCHWERWIEGENDER FEHLER und sollte sofort gemeldet werden. %s</string>
|
||||||
<string name="disable">Deaktivieren</string>
|
<string name="disable">Deaktivieren</string>
|
||||||
<string name="default_account">@string/default_subtitles</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
<string name="type_completed">Ολοκληρώθηκε</string>
|
<string name="type_completed">Ολοκληρώθηκε</string>
|
||||||
<string name="type_dropped">Διακόπηκε</string>
|
<string name="type_dropped">Διακόπηκε</string>
|
||||||
<string name="type_plan_to_watch">Για παρακολούθηση</string>
|
<string name="type_plan_to_watch">Για παρακολούθηση</string>
|
||||||
<string name="type_none">Τίποτα</string>
|
|
||||||
<string name="play_movie_button">Αναπαραγωγή ταινίας</string>
|
<string name="play_movie_button">Αναπαραγωγή ταινίας</string>
|
||||||
<string name="play_torrent_button">Μετάδοση Torrent</string>
|
<string name="play_torrent_button">Μετάδοση Torrent</string>
|
||||||
<string name="pick_source">Πηγές</string>
|
<string name="pick_source">Πηγές</string>
|
||||||
|
@ -58,7 +57,6 @@
|
||||||
<string name="action_remove_from_bookmarks">Αφαίρεση</string>
|
<string name="action_remove_from_bookmarks">Αφαίρεση</string>
|
||||||
<string name="play_episode_toast">Αναπαραγωγή επεισοδίου</string>
|
<string name="play_episode_toast">Αναπαραγωγή επεισοδίου</string>
|
||||||
<string name="sort_apply">Υποβολή</string>
|
<string name="sort_apply">Υποβολή</string>
|
||||||
<string name="sort_cancel">Ακύρωση</string>
|
|
||||||
<string name="player_speed">Ταχύτητα αναπαραγωγής</string>
|
<string name="player_speed">Ταχύτητα αναπαραγωγής</string>
|
||||||
<string name="subtitles_settings">Ρυθμίσεις υπότιτλων</string>
|
<string name="subtitles_settings">Ρυθμίσεις υπότιτλων</string>
|
||||||
<string name="subs_text_color">Χρώμα κειμένου</string>
|
<string name="subs_text_color">Χρώμα κειμένου</string>
|
||||||
|
@ -155,6 +153,7 @@
|
||||||
<string name="no_episodes_found">Δεν βρέθηκαν επεισόδια</string>
|
<string name="no_episodes_found">Δεν βρέθηκαν επεισόδια</string>
|
||||||
<string name="delete_file">Διαγραφή αρχείου</string>
|
<string name="delete_file">Διαγραφή αρχείου</string>
|
||||||
<string name="delete">Διαγραφή</string>
|
<string name="delete">Διαγραφή</string>
|
||||||
|
<string name="cancel">Ακύρωση</string>
|
||||||
<string name="pause">Παύση</string>
|
<string name="pause">Παύση</string>
|
||||||
<string name="resume">Συνέχιση</string>
|
<string name="resume">Συνέχιση</string>
|
||||||
<string name="delete_message" formatted="true">Αυτό θα διαγράψει μόνιμα το %s
|
<string name="delete_message" formatted="true">Αυτό θα διαγράψει μόνιμα το %s
|
||||||
|
@ -169,7 +168,7 @@
|
||||||
<string name="synopsis">Περίληψη</string>
|
<string name="synopsis">Περίληψη</string>
|
||||||
<string name="queued">προστέθηκε στην ουρά</string>
|
<string name="queued">προστέθηκε στην ουρά</string>
|
||||||
<string name="no_subtitles">Δεν υπάρχουν διαθέσιμοι υπότιτλοι</string>
|
<string name="no_subtitles">Δεν υπάρχουν διαθέσιμοι υπότιτλοι</string>
|
||||||
<string name="default_subtitles">Προεπιλεγμένοι υπότιτλοι</string>
|
<string name="action_default">Προεπιλεγμένοι υπότιτλοι</string>
|
||||||
<string name="free_storage">Ελεύθερος</string>
|
<string name="free_storage">Ελεύθερος</string>
|
||||||
<string name="used_storage">Σε χρήση</string>
|
<string name="used_storage">Σε χρήση</string>
|
||||||
<string name="app_storage">Εφαρμογή</string>
|
<string name="app_storage">Εφαρμογή</string>
|
||||||
|
@ -547,6 +546,5 @@
|
||||||
<string name="unable_to_inflate">Το UI δεν ήταν σε θέση να δημιουργηθεί σωστά, είναι ένα σφάλμα και θα πρέπει να αναφερθεί αμέσως %s</string>
|
<string name="unable_to_inflate">Το UI δεν ήταν σε θέση να δημιουργηθεί σωστά, είναι ένα σφάλμα και θα πρέπει να αναφερθεί αμέσως %s</string>
|
||||||
<string name="automatic_plugin_download_mode_title">Επιλέξτε κατάσταση για φιλτράρισμα επεκτάσεων για λήψη</string>
|
<string name="automatic_plugin_download_mode_title">Επιλέξτε κατάσταση για φιλτράρισμα επεκτάσεων για λήψη</string>
|
||||||
<string name="disable">Απενεργοποιημένο</string>
|
<string name="disable">Απενεργοποιημένο</string>
|
||||||
<string name="default_account">@string/default_subtitles</string>
|
|
||||||
<string name="stop">Τέλος</string>
|
<string name="stop">Τέλος</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
<string name="extension_description">Priskribo</string>
|
<string name="extension_description">Priskribo</string>
|
||||||
<string name="extension_version">Versio</string>
|
<string name="extension_version">Versio</string>
|
||||||
<string name="extension_status">Stato</string>
|
<string name="extension_status">Stato</string>
|
||||||
<string name="sort_cancel">Nuligi</string>
|
|
||||||
<string name="sort_clear">Forviŝi</string>
|
<string name="sort_clear">Forviŝi</string>
|
||||||
<string name="yes">Jes</string>
|
<string name="yes">Jes</string>
|
||||||
<string name="no">Ne</string>
|
<string name="no">Ne</string>
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
<string name="setup_extensions_subtext">Descargue la lista de sitios que quiera utilizar</string>
|
<string name="setup_extensions_subtext">Descargue la lista de sitios que quiera utilizar</string>
|
||||||
<string name="plugins_downloaded" formatted="true">Descargado:%d</string>
|
<string name="plugins_downloaded" formatted="true">Descargado:%d</string>
|
||||||
<string name="downloaded">Descargado</string>
|
<string name="downloaded">Descargado</string>
|
||||||
<string name="batch_download_finish_format" formatted="true">Descargado %d %s</string>
|
<string name="batch_download_finish_format" formatted="true">Descargado %1$d %2$s</string>
|
||||||
<string name="delete_repository">Borrar repositorio</string>
|
<string name="delete_repository">Borrar repositorio</string>
|
||||||
<string name="next_episode_format" formatted="true">El episodio %d se lanzará en</string>
|
<string name="next_episode_format" formatted="true">El episodio %d se lanzará en</string>
|
||||||
<string name="next_episode_time_hour_format" formatted="true">%dh %dm</string>
|
<string name="next_episode_time_hour_format" formatted="true">%1$dh %2$dm</string>
|
||||||
<string name="next_episode_time_min_format" formatted="true">%dm</string>
|
<string name="next_episode_time_min_format" formatted="true">%dm</string>
|
||||||
<string name="result_poster_img_des">Poster</string>
|
<string name="result_poster_img_des">Poster</string>
|
||||||
<string name="extensions">Extensiones</string>
|
<string name="extensions">Extensiones</string>
|
||||||
|
@ -45,7 +45,7 @@
|
||||||
<string name="player_load_subtitles_online">Cargar desde Internet</string>
|
<string name="player_load_subtitles_online">Cargar desde Internet</string>
|
||||||
<string name="autoplay_next_settings">Reproducir automáticamente episodio siguiente</string>
|
<string name="autoplay_next_settings">Reproducir automáticamente episodio siguiente</string>
|
||||||
<string name="chromecast_subtitles_settings_des">Configuración de subtítulos de Chromecast</string>
|
<string name="chromecast_subtitles_settings_des">Configuración de subtítulos de Chromecast</string>
|
||||||
<string name="default_subtitles">Predeterminado</string>
|
<string name="action_default">Predeterminado</string>
|
||||||
<string name="subtitles_outline">Contorno</string>
|
<string name="subtitles_outline">Contorno</string>
|
||||||
<string name="no_subtitles">Sin Subtítulos</string>
|
<string name="no_subtitles">Sin Subtítulos</string>
|
||||||
<string name="subtitles_raised">Elevado</string>
|
<string name="subtitles_raised">Elevado</string>
|
||||||
|
@ -97,12 +97,12 @@
|
||||||
<string name="search_poster_img_des">Poster</string>
|
<string name="search_poster_img_des">Poster</string>
|
||||||
<string name="home_next_random_img_des">Siguiente al azar</string>
|
<string name="home_next_random_img_des">Siguiente al azar</string>
|
||||||
<string name="all_languages_preference">Todos los Idiomas</string>
|
<string name="all_languages_preference">Todos los Idiomas</string>
|
||||||
<string name="go_back_img_des">Volver</string>
|
<string name="go_back_img_des">Regresar</string>
|
||||||
<string name="home_change_provider_img_des">Cambiar proveedor</string>
|
<string name="home_change_provider_img_des">Cambiar proveedor</string>
|
||||||
<string name="preview_background_img_des">Vista previa del fondo</string>
|
<string name="preview_background_img_des">Vista previa del fondo</string>
|
||||||
<string name="rated_format" formatted="true">Nota:%.1f</string>
|
<string name="rated_format" formatted="true">Nota:%.1f</string>
|
||||||
<string name="new_update_format" formatted="true">Nueva actualización encontrada!
|
<string name="new_update_format" formatted="true">¡Nueva actualización encontrada!
|
||||||
\n%s -> %s</string>
|
\n%1$s -> %2$s</string>
|
||||||
<string name="download">Descargar</string>
|
<string name="download">Descargar</string>
|
||||||
<string name="popup_pause_download">Pausar Descarga</string>
|
<string name="popup_pause_download">Pausar Descarga</string>
|
||||||
<string name="subs_font">Formato de fuente</string>
|
<string name="subs_font">Formato de fuente</string>
|
||||||
|
@ -110,8 +110,8 @@
|
||||||
<string name="subs_font_size">Tamaño de Fuente</string>
|
<string name="subs_font_size">Tamaño de Fuente</string>
|
||||||
<string name="player_speed_text_format" formatted="true">Velocidad (%.2fx)</string>
|
<string name="player_speed_text_format" formatted="true">Velocidad (%.2fx)</string>
|
||||||
<string name="skip_loading">Omitir carga</string>
|
<string name="skip_loading">Omitir carga</string>
|
||||||
<string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string>
|
<string name="app_dub_sub_episode_text_format" formatted="true">%1$s Ep. %2$d</string>
|
||||||
<string name="next_episode_time_day_format" formatted="true">%dd %dh %dm</string>
|
<string name="next_episode_time_day_format" formatted="true">%1$dd %2$dh %3$dm</string>
|
||||||
<string name="cast_format" formatted="true">Elenco %s</string>
|
<string name="cast_format" formatted="true">Elenco %s</string>
|
||||||
<string name="filler" formatted="true">Relleno</string>
|
<string name="filler" formatted="true">Relleno</string>
|
||||||
<string name="duration_format" formatted="true">%d min</string>
|
<string name="duration_format" formatted="true">%d min</string>
|
||||||
|
@ -138,7 +138,6 @@
|
||||||
<string name="type_completed">Completado</string>
|
<string name="type_completed">Completado</string>
|
||||||
<string name="type_dropped">Descartado</string>
|
<string name="type_dropped">Descartado</string>
|
||||||
<string name="type_plan_to_watch">Planeando ver</string>
|
<string name="type_plan_to_watch">Planeando ver</string>
|
||||||
<string name="type_none">Ninguno</string>
|
|
||||||
<string name="type_re_watching">Volviendo a mirar</string>
|
<string name="type_re_watching">Volviendo a mirar</string>
|
||||||
<string name="play_movie_button">Reproducir película</string>
|
<string name="play_movie_button">Reproducir película</string>
|
||||||
<string name="play_trailer_button">Reproducir Trailer</string>
|
<string name="play_trailer_button">Reproducir Trailer</string>
|
||||||
|
@ -146,7 +145,7 @@
|
||||||
<string name="play_torrent_button">Transmitir Torrent</string>
|
<string name="play_torrent_button">Transmitir Torrent</string>
|
||||||
<string name="pick_source">Fuentes</string>
|
<string name="pick_source">Fuentes</string>
|
||||||
<string name="reload_error">Reintentar conexión…</string>
|
<string name="reload_error">Reintentar conexión…</string>
|
||||||
<string name="go_back">Volver</string>
|
<string name="go_back">Regresar</string>
|
||||||
<string name="downloading">Descargando</string>
|
<string name="downloading">Descargando</string>
|
||||||
<string name="download_paused">Descarga pausada</string>
|
<string name="download_paused">Descarga pausada</string>
|
||||||
<string name="download_started">Descarga iniciada</string>
|
<string name="download_started">Descarga iniciada</string>
|
||||||
|
@ -167,7 +166,6 @@
|
||||||
<string name="error_bookmarks_text">Marcadores</string>
|
<string name="error_bookmarks_text">Marcadores</string>
|
||||||
<string name="action_remove_from_bookmarks">Remover</string>
|
<string name="action_remove_from_bookmarks">Remover</string>
|
||||||
<string name="action_add_to_bookmarks">Seleccionar estado de visualización</string>
|
<string name="action_add_to_bookmarks">Seleccionar estado de visualización</string>
|
||||||
<string name="sort_cancel">Cancelar</string>
|
|
||||||
<string name="sort_copy">Copiar</string>
|
<string name="sort_copy">Copiar</string>
|
||||||
<string name="sort_close">Cerrar</string>
|
<string name="sort_close">Cerrar</string>
|
||||||
<string name="sort_clear">Limpiar</string>
|
<string name="sort_clear">Limpiar</string>
|
||||||
|
@ -220,8 +218,8 @@
|
||||||
<string name="play_episode_toast">Reproducir Episodio</string>
|
<string name="play_episode_toast">Reproducir Episodio</string>
|
||||||
<string name="episode">Episodio</string>
|
<string name="episode">Episodio</string>
|
||||||
<string name="episodes">Episodios</string>
|
<string name="episodes">Episodios</string>
|
||||||
<string name="episodes_range">%d-%d</string>
|
<string name="episodes_range">%1$d-%2$d</string>
|
||||||
<string name="episode_format" formatted="true">%d %s</string>
|
<string name="episode_format" formatted="true">%1$d %2$s</string>
|
||||||
<string name="episode_short">E</string>
|
<string name="episode_short">E</string>
|
||||||
<string name="restore_failed_format" formatted="true">Falló la restauración de los datos desde el archivo %s</string>
|
<string name="restore_failed_format" formatted="true">Falló la restauración de los datos desde el archivo %s</string>
|
||||||
<string name="backup_success">Datos guardados</string>
|
<string name="backup_success">Datos guardados</string>
|
||||||
|
@ -235,7 +233,7 @@
|
||||||
<string name="advanced_search_des">Mostrar los resultados de la búsqueda por proveedor</string>
|
<string name="advanced_search_des">Mostrar los resultados de la búsqueda por proveedor</string>
|
||||||
<string name="bug_report_settings_off">Solo envíar los datos si la App se cierra / falla inesperadamente</string>
|
<string name="bug_report_settings_off">Solo envíar los datos si la App se cierra / falla inesperadamente</string>
|
||||||
<string name="bug_report_settings_on">No enviar datos</string>
|
<string name="bug_report_settings_on">No enviar datos</string>
|
||||||
<string name="show_trailers_settings">Mostrar Trailers (avances)</string>
|
<string name="show_trailers_settings">Mostrar los trailers</string>
|
||||||
<string name="kitsu_settings">Mostrar pósters de Kitsu</string>
|
<string name="kitsu_settings">Mostrar pósters de Kitsu</string>
|
||||||
<string name="uprereleases_settings">Actualizar a las versiones preliminares</string>
|
<string name="uprereleases_settings">Actualizar a las versiones preliminares</string>
|
||||||
<string name="uprereleases_settings_des">Buscar actualizaciones preliminares (beta) en lugar de solo versiones completas (stable releases)</string>
|
<string name="uprereleases_settings_des">Buscar actualizaciones preliminares (beta) en lugar de solo versiones completas (stable releases)</string>
|
||||||
|
@ -251,11 +249,12 @@
|
||||||
<string name="subs_default_reset_toast">Reiniciar a valores predefinidos</string>
|
<string name="subs_default_reset_toast">Reiniciar a valores predefinidos</string>
|
||||||
<string name="acra_report_toast">Lo sentimos, la aplicación se bloqueó. Se enviará un informe de error anónimo a los desarrolladores</string>
|
<string name="acra_report_toast">Lo sentimos, la aplicación se bloqueó. Se enviará un informe de error anónimo a los desarrolladores</string>
|
||||||
<string name="season">Temporada</string>
|
<string name="season">Temporada</string>
|
||||||
<string name="season_format">%s %d%s</string>
|
<string name="season_format">%1$s %2$d%3$s</string>
|
||||||
<string name="no_season">Ninguna Temporada</string>
|
<string name="no_season">Ninguna Temporada</string>
|
||||||
<string name="season_short">T</string>
|
<string name="season_short">T</string>
|
||||||
<string name="delete_file">Borrar Archivo</string>
|
<string name="delete_file">Borrar Archivo</string>
|
||||||
<string name="delete">Borrar</string>
|
<string name="delete">Borrar</string>
|
||||||
|
<string name="cancel">Cancelar</string>
|
||||||
<string name="unexpected_error">Error inesperado del reproductor</string>
|
<string name="unexpected_error">Error inesperado del reproductor</string>
|
||||||
<string name="episode_action_chromecast_episode">Episodio en Chromecast</string>
|
<string name="episode_action_chromecast_episode">Episodio en Chromecast</string>
|
||||||
<string name="episode_action_play_in_app">Reproducir en la aplicación</string>
|
<string name="episode_action_play_in_app">Reproducir en la aplicación</string>
|
||||||
|
@ -312,7 +311,7 @@
|
||||||
<string name="switch_account">Cambiar cuenta</string>
|
<string name="switch_account">Cambiar cuenta</string>
|
||||||
<string name="add_account">Añadir cuenta</string>
|
<string name="add_account">Añadir cuenta</string>
|
||||||
<string name="upload_sync">Sincronizar</string>
|
<string name="upload_sync">Sincronizar</string>
|
||||||
<string name="sync_score">Calificación</string>
|
<string name="sync_score">Clasificado</string>
|
||||||
<string name="authenticated_user" formatted="true">%s autenticado</string>
|
<string name="authenticated_user" formatted="true">%s autenticado</string>
|
||||||
<string name="authenticated_user_fail" formatted="true">No se pudo autenticar a %s</string>
|
<string name="authenticated_user_fail" formatted="true">No se pudo autenticar a %s</string>
|
||||||
<string name="recommended">Recomendado</string>
|
<string name="recommended">Recomendado</string>
|
||||||
|
@ -346,7 +345,7 @@
|
||||||
<string name="primary_color_settings">Color primario</string>
|
<string name="primary_color_settings">Color primario</string>
|
||||||
<string name="app_theme_settings">Tema de la aplicación</string>
|
<string name="app_theme_settings">Tema de la aplicación</string>
|
||||||
<string name="example_email">hola@mundo.com</string>
|
<string name="example_email">hola@mundo.com</string>
|
||||||
<string name="login_format" formatted="true">%s %s</string>
|
<string name="login_format" formatted="true">%1$s %2$s</string>
|
||||||
<string name="all">Todo</string>
|
<string name="all">Todo</string>
|
||||||
<string name="subtitle_offset_hint">1000ms</string>
|
<string name="subtitle_offset_hint">1000ms</string>
|
||||||
<string name="subtitle_offset_extra_hint_none_format">Sin retraso de subtítulos</string>
|
<string name="subtitle_offset_extra_hint_none_format">Sin retraso de subtítulos</string>
|
||||||
|
@ -449,7 +448,7 @@
|
||||||
<string name="setup_done">Hecho</string>
|
<string name="setup_done">Hecho</string>
|
||||||
<string name="plugin_loaded">Plugin Cargado</string>
|
<string name="plugin_loaded">Plugin Cargado</string>
|
||||||
<string name="is_adult">18+</string>
|
<string name="is_adult">18+</string>
|
||||||
<string name="batch_download_start_format" formatted="true">Iniciada la descarga %d %s…</string>
|
<string name="batch_download_start_format" formatted="true">Comenzó la descarga de %1$d %2$s…</string>
|
||||||
<string name="batch_download">Descarga por lotes</string>
|
<string name="batch_download">Descarga por lotes</string>
|
||||||
<string name="plugin_singular">plugin</string>
|
<string name="plugin_singular">plugin</string>
|
||||||
<string name="plugin">plugins</string>
|
<string name="plugin">plugins</string>
|
||||||
|
@ -549,9 +548,34 @@
|
||||||
<string name="unable_to_inflate">La interfaz de usuario no se ha podido crear correctamente, se trata de un GRAN BUG y debe ser reportado inmediatamente %s</string>
|
<string name="unable_to_inflate">La interfaz de usuario no se ha podido crear correctamente, se trata de un GRAN BUG y debe ser reportado inmediatamente %s</string>
|
||||||
<string name="automatic_plugin_download_mode_title">Seleccionar modo para filtrar los plugins descargados</string>
|
<string name="automatic_plugin_download_mode_title">Seleccionar modo para filtrar los plugins descargados</string>
|
||||||
<string name="disable">Deshabilitar</string>
|
<string name="disable">Deshabilitar</string>
|
||||||
<string name="default_account">@string/default_subtitles</string>
|
|
||||||
<string name="no_plugins_found_error">No se encontraron complementos en el repositorio</string>
|
<string name="no_plugins_found_error">No se encontraron complementos en el repositorio</string>
|
||||||
<string name="no_repository_found_error">Repositorio no encontrado, comprueba la URL y prueba la VPN</string>
|
<string name="no_repository_found_error">Repositorio no encontrado, comprueba la URL y prueba la VPN</string>
|
||||||
<string name="already_voted">Ya has votado</string>
|
<string name="already_voted">Ya has votado</string>
|
||||||
<string name="backup_frequency">Frecuencia de la copia de seguridad</string>
|
<string name="backup_frequency">Frecuencia de la copia de seguridad</string>
|
||||||
|
<string name="favorite_removed">%s eliminado de favoritos</string>
|
||||||
|
<string name="favorites_list_name">Favoritos</string>
|
||||||
|
<string name="favorite_added">%s añadido a favoritos</string>
|
||||||
|
<string name="duplicate_message_multiple" formatted="true">Se han encontrado posibles elementos duplicados en su biblioteca:
|
||||||
|
\n
|
||||||
|
\n%s
|
||||||
|
\n
|
||||||
|
\n¿Desea añadir este elemento de todos modos, sustituir los existentes o cancelar la acción\?</string>
|
||||||
|
<string name="duplicate_title">Posible duplicado encontrado</string>
|
||||||
|
<string name="lock_profile">Perfil de bloqueo</string>
|
||||||
|
<string name="action_add_to_favorites">Añadido a favoritos</string>
|
||||||
|
<string name="duplicate_replace_all">Sustituir todo</string>
|
||||||
|
<string name="pin_error_incorrect">PIN incorrecto. Por favor, inténtelo de nuevo.</string>
|
||||||
|
<string name="action_unsubscribe">Cancelar la suscripción</string>
|
||||||
|
<string name="pin_error_length">El PIN debe tener 4 caracteres</string>
|
||||||
|
<string name="duplicate_replace">Sustituir</string>
|
||||||
|
<string name="duplicate_add">Añadir</string>
|
||||||
|
<string name="action_subscribe">Suscríbase</string>
|
||||||
|
<string name="action_remove_from_favorites">Eliminar de favoritos</string>
|
||||||
|
<string name="select_an_account">Seleccione una cuenta</string>
|
||||||
|
<string name="duplicate_message_single">Parece que ya existe un elemento potencialmente duplicado en su biblioteca: \'%s.\'
|
||||||
|
\n
|
||||||
|
\n¿Desea añadir este elemento de todos modos, sustituir el existente o cancelar la acción\?</string>
|
||||||
|
<string name="enter_pin">Introducir el PIN</string>
|
||||||
|
<string name="pin">PIN</string>
|
||||||
|
<string name="enter_current_pin">Introduzca el PIN actual</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
<string name="type_completed">Terminé</string>
|
<string name="type_completed">Terminé</string>
|
||||||
<string name="type_dropped">Abandonné</string>
|
<string name="type_dropped">Abandonné</string>
|
||||||
<string name="type_plan_to_watch">À regarder</string>
|
<string name="type_plan_to_watch">À regarder</string>
|
||||||
<string name="type_none">Aucun</string>
|
|
||||||
<string name="play_movie_button">Lire</string>
|
<string name="play_movie_button">Lire</string>
|
||||||
<string name="play_torrent_button">Streamer le Torrent</string>
|
<string name="play_torrent_button">Streamer le Torrent</string>
|
||||||
<string name="pick_source">Sources</string>
|
<string name="pick_source">Sources</string>
|
||||||
|
@ -60,7 +59,6 @@
|
||||||
<string name="error_bookmarks_text">Marque-pages</string>
|
<string name="error_bookmarks_text">Marque-pages</string>
|
||||||
<string name="action_remove_from_bookmarks">Supprimer</string>
|
<string name="action_remove_from_bookmarks">Supprimer</string>
|
||||||
<string name="sort_apply">Appliquer</string>
|
<string name="sort_apply">Appliquer</string>
|
||||||
<string name="sort_cancel">Annuler</string>
|
|
||||||
<string name="player_speed">Vitesse de lecture</string>
|
<string name="player_speed">Vitesse de lecture</string>
|
||||||
<string name="preview_background_img_des">Aperçu de l\'arrière-plan</string>
|
<string name="preview_background_img_des">Aperçu de l\'arrière-plan</string>
|
||||||
<string name="benene">Donner une benene aux devs</string>
|
<string name="benene">Donner une benene aux devs</string>
|
||||||
|
@ -80,6 +78,7 @@
|
||||||
<string name="episode_short">E</string>
|
<string name="episode_short">E</string>
|
||||||
<string name="delete_file">Supprimer le Fichier</string>
|
<string name="delete_file">Supprimer le Fichier</string>
|
||||||
<string name="delete">Supprimer</string>
|
<string name="delete">Supprimer</string>
|
||||||
|
<string name="cancel">Annuler</string>
|
||||||
<string name="pause">Pause</string>
|
<string name="pause">Pause</string>
|
||||||
<string name="resume">Reprendre</string>
|
<string name="resume">Reprendre</string>
|
||||||
<string name="delete_message">Cela va supprimer définitivement %s
|
<string name="delete_message">Cela va supprimer définitivement %s
|
||||||
|
@ -94,7 +93,7 @@
|
||||||
<string name="synopsis">Synopsis</string>
|
<string name="synopsis">Synopsis</string>
|
||||||
<string name="queued">Liste d\'attente</string>
|
<string name="queued">Liste d\'attente</string>
|
||||||
<string name="no_subtitles">Pas de sous-titres</string>
|
<string name="no_subtitles">Pas de sous-titres</string>
|
||||||
<string name="default_subtitles">Défault</string>
|
<string name="action_default">Défault</string>
|
||||||
<string name="free_storage">Libre</string>
|
<string name="free_storage">Libre</string>
|
||||||
<string name="used_storage">Utilisé</string>
|
<string name="used_storage">Utilisé</string>
|
||||||
<string name="app_storage">Application</string>
|
<string name="app_storage">Application</string>
|
||||||
|
@ -553,5 +552,4 @@
|
||||||
<string name="unable_to_inflate">L\'interface utilisateur n\'a pas pu être créée correctement. Il s\'agit d\'un bogue majeur qui doit être signalé immédiatement %s</string>
|
<string name="unable_to_inflate">L\'interface utilisateur n\'a pas pu être créée correctement. Il s\'agit d\'un bogue majeur qui doit être signalé immédiatement %s</string>
|
||||||
<string name="automatic_plugin_download_mode_title">Sélectionnez le mode pour filtrer le téléchargement des plugins</string>
|
<string name="automatic_plugin_download_mode_title">Sélectionnez le mode pour filtrer le téléchargement des plugins</string>
|
||||||
<string name="profile_background_des">Fond de profil</string>
|
<string name="profile_background_des">Fond de profil</string>
|
||||||
<string name="default_account">@string/default_subtitles</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -29,13 +29,11 @@
|
||||||
<string name="type_completed">Completado</string>
|
<string name="type_completed">Completado</string>
|
||||||
<string name="type_dropped">Descartado</string>
|
<string name="type_dropped">Descartado</string>
|
||||||
<string name="type_plan_to_watch">Planeando ver</string>
|
<string name="type_plan_to_watch">Planeando ver</string>
|
||||||
<string name="type_none">Ningún</string>
|
|
||||||
<string name="type_re_watching">Remirando</string>
|
<string name="type_re_watching">Remirando</string>
|
||||||
<string name="error_bookmarks_text">Marcadores</string>
|
<string name="error_bookmarks_text">Marcadores</string>
|
||||||
<string name="action_remove_from_bookmarks">Borrar</string>
|
<string name="action_remove_from_bookmarks">Borrar</string>
|
||||||
<string name="action_add_to_bookmarks">Seleccionar estado de visualización</string>
|
<string name="action_add_to_bookmarks">Seleccionar estado de visualización</string>
|
||||||
<string name="sort_apply">Aplicar</string>
|
<string name="sort_apply">Aplicar</string>
|
||||||
<string name="sort_cancel">Cancelar</string>
|
|
||||||
<string name="sort_copy">Copiar</string>
|
<string name="sort_copy">Copiar</string>
|
||||||
<string name="sort_close">Cerrar</string>
|
<string name="sort_close">Cerrar</string>
|
||||||
<string name="sort_clear">Limpar</string>
|
<string name="sort_clear">Limpar</string>
|
||||||
|
|
|
@ -49,7 +49,6 @@
|
||||||
<string name="error_bookmarks_text">बुकमार्क्स</string>
|
<string name="error_bookmarks_text">बुकमार्क्स</string>
|
||||||
<string name="action_remove_from_bookmarks">हटाएँ</string>
|
<string name="action_remove_from_bookmarks">हटाएँ</string>
|
||||||
<string name="sort_apply">लागू करें</string>
|
<string name="sort_apply">लागू करें</string>
|
||||||
<string name="sort_cancel">रद्द करें</string>
|
|
||||||
<string name="player_speed">प्लेयर स्पीड</string>
|
<string name="player_speed">प्लेयर स्पीड</string>
|
||||||
<string name="search_provider_text_providers">प्रोवाइडरों का उपयोग कर खोजें</string>
|
<string name="search_provider_text_providers">प्रोवाइडरों का उपयोग कर खोजें</string>
|
||||||
<string name="search_provider_text_types">प्रकार का उपयोग करके खोजें</string>
|
<string name="search_provider_text_types">प्रकार का उपयोग करके खोजें</string>
|
||||||
|
@ -91,6 +90,7 @@
|
||||||
<string name="acra_report_toast">क्षमा करें, एप्प क्रैश हो गया है । निर्माताओं को एक अनाम बग रिपोर्ट भेजी जाएगी</string>
|
<string name="acra_report_toast">क्षमा करें, एप्प क्रैश हो गया है । निर्माताओं को एक अनाम बग रिपोर्ट भेजी जाएगी</string>
|
||||||
<string name="delete_file">फ़ाइल डिलीट करें</string>
|
<string name="delete_file">फ़ाइल डिलीट करें</string>
|
||||||
<string name="delete">डिलीट</string>
|
<string name="delete">डिलीट</string>
|
||||||
|
<string name="cancel">रद्द करें</string>
|
||||||
<string name="pause">रोकें</string>
|
<string name="pause">रोकें</string>
|
||||||
<string name="resume">फिर से चलाएं</string>
|
<string name="resume">फिर से चलाएं</string>
|
||||||
<string name="delete_message">इससे %s स्थायी रूप से हट जाएगा
|
<string name="delete_message">इससे %s स्थायी रूप से हट जाएगा
|
||||||
|
|
|
@ -54,7 +54,6 @@
|
||||||
<string name="type_completed">Dovršeno</string>
|
<string name="type_completed">Dovršeno</string>
|
||||||
<string name="type_dropped">Ispušteno</string>
|
<string name="type_dropped">Ispušteno</string>
|
||||||
<string name="type_plan_to_watch">Planiram pogledati</string>
|
<string name="type_plan_to_watch">Planiram pogledati</string>
|
||||||
<string name="type_none">Ništa</string>
|
|
||||||
<string name="type_re_watching">Ponovno gledam</string>
|
<string name="type_re_watching">Ponovno gledam</string>
|
||||||
<string name="play_movie_button">Pokreni Film</string>
|
<string name="play_movie_button">Pokreni Film</string>
|
||||||
<string name="play_livestream_button">Pokreni LiveStream</string>
|
<string name="play_livestream_button">Pokreni LiveStream</string>
|
||||||
|
@ -92,7 +91,6 @@
|
||||||
<string name="action_remove_from_bookmarks">Ukloni</string>
|
<string name="action_remove_from_bookmarks">Ukloni</string>
|
||||||
<string name="action_add_to_bookmarks">Postavi status gledanja</string>
|
<string name="action_add_to_bookmarks">Postavi status gledanja</string>
|
||||||
<string name="sort_apply">Primijeni</string>
|
<string name="sort_apply">Primijeni</string>
|
||||||
<string name="sort_cancel">Poništi</string>
|
|
||||||
<string name="sort_copy">Kopiraj</string>
|
<string name="sort_copy">Kopiraj</string>
|
||||||
<string name="sort_close">Zatvori</string>
|
<string name="sort_close">Zatvori</string>
|
||||||
<string name="sort_clear">Očisti</string>
|
<string name="sort_clear">Očisti</string>
|
||||||
|
@ -200,6 +198,7 @@
|
||||||
<string name="no_episodes_found">Nisu pronađene epizode</string>
|
<string name="no_episodes_found">Nisu pronađene epizode</string>
|
||||||
<string name="delete_file">Izbriši datoteku</string>
|
<string name="delete_file">Izbriši datoteku</string>
|
||||||
<string name="delete">Izbriši</string>
|
<string name="delete">Izbriši</string>
|
||||||
|
<string name="cancel">Poništi</string>
|
||||||
<string name="pause">Pauziraj</string>
|
<string name="pause">Pauziraj</string>
|
||||||
<string name="resume">Nastavi</string>
|
<string name="resume">Nastavi</string>
|
||||||
<string name="go_back_30">-30</string>
|
<string name="go_back_30">-30</string>
|
||||||
|
@ -218,7 +217,7 @@
|
||||||
<string name="synopsis">Sinopsis</string>
|
<string name="synopsis">Sinopsis</string>
|
||||||
<string name="queued">u redu čekanja</string>
|
<string name="queued">u redu čekanja</string>
|
||||||
<string name="no_subtitles">Bez titlova</string>
|
<string name="no_subtitles">Bez titlova</string>
|
||||||
<string name="default_subtitles">Zadano</string>
|
<string name="action_default">Zadano</string>
|
||||||
<string name="free_storage">Slobodno</string>
|
<string name="free_storage">Slobodno</string>
|
||||||
<string name="used_storage">Iskorišteno</string>
|
<string name="used_storage">Iskorišteno</string>
|
||||||
<string name="app_storage">Aplikacija</string>
|
<string name="app_storage">Aplikacija</string>
|
||||||
|
@ -567,7 +566,6 @@
|
||||||
<string name="unable_to_inflate">Nije bilo moguće ispravno izraditi korisničko sučelje. Ovo je ZNAČAJNA GREŠKA i treba se odmah prijaviti %s</string>
|
<string name="unable_to_inflate">Nije bilo moguće ispravno izraditi korisničko sučelje. Ovo je ZNAČAJNA GREŠKA i treba se odmah prijaviti %s</string>
|
||||||
<string name="automatic_plugin_download_mode_title">Odaberi modus za filtriranje preuzimanja dodataka</string>
|
<string name="automatic_plugin_download_mode_title">Odaberi modus za filtriranje preuzimanja dodataka</string>
|
||||||
<string name="disable">Onemogući</string>
|
<string name="disable">Onemogući</string>
|
||||||
<string name="default_account">@string/default_subtitles</string>
|
|
||||||
<string name="no_plugins_found_error">U repozitoriju nisu pronađeni dodaci</string>
|
<string name="no_plugins_found_error">U repozitoriju nisu pronađeni dodaci</string>
|
||||||
<string name="no_repository_found_error">Repozitorij nije pronađen, provjerite URL i pokušajte koristiti VPN</string>
|
<string name="no_repository_found_error">Repozitorij nije pronađen, provjerite URL i pokušajte koristiti VPN</string>
|
||||||
<string name="quality_profile_help">Ovdje možete promijeniti način na koji su izvori poredani. Ako video ima viši prioritet, pojavit će se više u odabiru izvora. Zbroj prioriteta izvora i prioriteta kvalitete je video prioritet.
|
<string name="quality_profile_help">Ovdje možete promijeniti način na koji su izvori poredani. Ako video ima viši prioritet, pojavit će se više u odabiru izvora. Zbroj prioriteta izvora i prioriteta kvalitete je video prioritet.
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
<string name="download">Letöltés</string>
|
<string name="download">Letöltés</string>
|
||||||
<string name="search">Keresés</string>
|
<string name="search">Keresés</string>
|
||||||
<string name="delete">Törlés</string>
|
<string name="delete">Törlés</string>
|
||||||
|
<string name="cancel">Mégse</string>
|
||||||
<string name="pause">Szüneteltetés</string>
|
<string name="pause">Szüneteltetés</string>
|
||||||
<string name="queued">sorba állítva</string>
|
<string name="queued">sorba állítva</string>
|
||||||
<string name="resize_fit">Igazítás</string>
|
<string name="resize_fit">Igazítás</string>
|
||||||
|
@ -61,7 +62,6 @@
|
||||||
<string name="type_watching">Nézés</string>
|
<string name="type_watching">Nézés</string>
|
||||||
<string name="type_completed">Befejezve</string>
|
<string name="type_completed">Befejezve</string>
|
||||||
<string name="type_plan_to_watch">Később megnézés</string>
|
<string name="type_plan_to_watch">Később megnézés</string>
|
||||||
<string name="type_none">Nincs</string>
|
|
||||||
<string name="type_re_watching">Újranézés</string>
|
<string name="type_re_watching">Újranézés</string>
|
||||||
<string name="play_movie_button">Film lejátszása</string>
|
<string name="play_movie_button">Film lejátszása</string>
|
||||||
<string name="play_trailer_button">Előzetes lejátszása</string>
|
<string name="play_trailer_button">Előzetes lejátszása</string>
|
||||||
|
@ -90,7 +90,6 @@
|
||||||
<string name="action_remove_from_bookmarks">Eltávolítás</string>
|
<string name="action_remove_from_bookmarks">Eltávolítás</string>
|
||||||
<string name="action_add_to_bookmarks">Megtekintés állapotának beállítása</string>
|
<string name="action_add_to_bookmarks">Megtekintés állapotának beállítása</string>
|
||||||
<string name="sort_apply">Alkalmazás</string>
|
<string name="sort_apply">Alkalmazás</string>
|
||||||
<string name="sort_cancel">Mégse</string>
|
|
||||||
<string name="sort_copy">Másolás</string>
|
<string name="sort_copy">Másolás</string>
|
||||||
<string name="sort_close">Bezárás</string>
|
<string name="sort_close">Bezárás</string>
|
||||||
<string name="sort_clear">Törlés</string>
|
<string name="sort_clear">Törlés</string>
|
||||||
|
@ -162,7 +161,7 @@
|
||||||
<string name="rating">Értékelés</string>
|
<string name="rating">Értékelés</string>
|
||||||
<string name="cartoons">Rajzfilmek</string>
|
<string name="cartoons">Rajzfilmek</string>
|
||||||
<string name="livestreams">Élőadások</string>
|
<string name="livestreams">Élőadások</string>
|
||||||
<string name="default_subtitles">Alapértelmezett</string>
|
<string name="action_default">Alapértelmezett</string>
|
||||||
<string name="movies">Filmek</string>
|
<string name="movies">Filmek</string>
|
||||||
<string name="tv_series">TV sorozat</string>
|
<string name="tv_series">TV sorozat</string>
|
||||||
<string name="anime">Anime</string>
|
<string name="anime">Anime</string>
|
||||||
|
|
|
@ -39,7 +39,6 @@
|
||||||
<string name="type_completed">Selesai</string>
|
<string name="type_completed">Selesai</string>
|
||||||
<string name="type_dropped">Dihentikan</string>
|
<string name="type_dropped">Dihentikan</string>
|
||||||
<string name="type_plan_to_watch">Rencana untuk Menonton</string>
|
<string name="type_plan_to_watch">Rencana untuk Menonton</string>
|
||||||
<string name="type_none">Tidak Ada</string>
|
|
||||||
<string name="type_re_watching">Menonton Ulang</string>
|
<string name="type_re_watching">Menonton Ulang</string>
|
||||||
<string name="play_movie_button">Putar Movie</string>
|
<string name="play_movie_button">Putar Movie</string>
|
||||||
<string name="play_torrent_button">Streaming Torrent</string>
|
<string name="play_torrent_button">Streaming Torrent</string>
|
||||||
|
@ -75,7 +74,6 @@
|
||||||
<string name="action_remove_from_bookmarks">Hapus</string>
|
<string name="action_remove_from_bookmarks">Hapus</string>
|
||||||
<string name="action_add_to_bookmarks">Atur status tontonan</string>
|
<string name="action_add_to_bookmarks">Atur status tontonan</string>
|
||||||
<string name="sort_apply">Terapkan</string>
|
<string name="sort_apply">Terapkan</string>
|
||||||
<string name="sort_cancel">Batalkan</string>
|
|
||||||
<string name="sort_copy">Salin</string>
|
<string name="sort_copy">Salin</string>
|
||||||
<string name="sort_close">Tutup</string>
|
<string name="sort_close">Tutup</string>
|
||||||
<string name="sort_clear">Bersihkan</string>
|
<string name="sort_clear">Bersihkan</string>
|
||||||
|
@ -174,6 +172,7 @@
|
||||||
<string name="no_episodes_found">Episode Tidak Ditemukan</string>
|
<string name="no_episodes_found">Episode Tidak Ditemukan</string>
|
||||||
<string name="delete_file">Hapus File</string>
|
<string name="delete_file">Hapus File</string>
|
||||||
<string name="delete">Hapus</string>
|
<string name="delete">Hapus</string>
|
||||||
|
<string name="cancel">Batalkan</string>
|
||||||
<string name="pause">Jeda</string>
|
<string name="pause">Jeda</string>
|
||||||
<string name="resume">Lanjutkan</string>
|
<string name="resume">Lanjutkan</string>
|
||||||
<string name="go_back_30">-30</string>
|
<string name="go_back_30">-30</string>
|
||||||
|
@ -192,7 +191,7 @@
|
||||||
<string name="synopsis">Sinopsis</string>
|
<string name="synopsis">Sinopsis</string>
|
||||||
<string name="queued">antri</string>
|
<string name="queued">antri</string>
|
||||||
<string name="no_subtitles">Tidak Ada Subtitle</string>
|
<string name="no_subtitles">Tidak Ada Subtitle</string>
|
||||||
<string name="default_subtitles">Default</string>
|
<string name="action_default">Default</string>
|
||||||
<string name="free_storage">Tersedia</string>
|
<string name="free_storage">Tersedia</string>
|
||||||
<string name="used_storage">Terpakai</string>
|
<string name="used_storage">Terpakai</string>
|
||||||
<string name="app_storage">Aplikasi</string>
|
<string name="app_storage">Aplikasi</string>
|
||||||
|
@ -575,5 +574,4 @@
|
||||||
<string name="no_plugins_found_error">Tidak ada plugin yang ditemukan di repositori</string>
|
<string name="no_plugins_found_error">Tidak ada plugin yang ditemukan di repositori</string>
|
||||||
<string name="no_repository_found_error">Repositori tidak ditemukan, periksa URL dan coba VPN</string>
|
<string name="no_repository_found_error">Repositori tidak ditemukan, periksa URL dan coba VPN</string>
|
||||||
<string name="already_voted">Kamu sudah voting</string>
|
<string name="already_voted">Kamu sudah voting</string>
|
||||||
<string name="default_account">@string/default_subtitles</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<string name="player_speed_text_format" formatted="true">Velocità (%.2fx)</string>
|
<string name="player_speed_text_format" formatted="true">Velocità (%.2fx)</string>
|
||||||
<string name="rated_format" formatted="true">Valutato: %.1f</string>
|
<string name="rated_format" formatted="true">Valutato: %.1f</string>
|
||||||
<string name="new_update_format" formatted="true">Nuovo aggiornamento trovato!
|
<string name="new_update_format" formatted="true">Nuovo aggiornamento trovato!
|
||||||
\n%s -> %s</string>
|
\n%1$s -> %2$s</string>
|
||||||
<string name="filler" formatted="true">Filler</string>
|
<string name="filler" formatted="true">Filler</string>
|
||||||
<string name="duration_format" formatted="true">%d min</string>
|
<string name="duration_format" formatted="true">%d min</string>
|
||||||
<!-- <string name="app_name">CloudStream</string> -->
|
<!-- <string name="app_name">CloudStream</string> -->
|
||||||
|
@ -44,7 +44,6 @@
|
||||||
<string name="type_completed">Completato</string>
|
<string name="type_completed">Completato</string>
|
||||||
<string name="type_dropped">Abbandonato</string>
|
<string name="type_dropped">Abbandonato</string>
|
||||||
<string name="type_plan_to_watch">Da guardare</string>
|
<string name="type_plan_to_watch">Da guardare</string>
|
||||||
<string name="type_none">Nessuno</string>
|
|
||||||
<string name="type_re_watching">Riguardando</string>
|
<string name="type_re_watching">Riguardando</string>
|
||||||
<string name="play_movie_button">Riproduci film</string>
|
<string name="play_movie_button">Riproduci film</string>
|
||||||
<string name="play_livestream_button">Riproduci Livestream</string>
|
<string name="play_livestream_button">Riproduci Livestream</string>
|
||||||
|
@ -81,7 +80,6 @@
|
||||||
<string name="action_remove_from_bookmarks">Rimuovi</string>
|
<string name="action_remove_from_bookmarks">Rimuovi</string>
|
||||||
<string name="action_add_to_bookmarks">Imposta stato riproduzione</string>
|
<string name="action_add_to_bookmarks">Imposta stato riproduzione</string>
|
||||||
<string name="sort_apply">Applica</string>
|
<string name="sort_apply">Applica</string>
|
||||||
<string name="sort_cancel">Cancella</string>
|
|
||||||
<string name="sort_copy">Copia</string>
|
<string name="sort_copy">Copia</string>
|
||||||
<string name="sort_close">Chiudi</string>
|
<string name="sort_close">Chiudi</string>
|
||||||
<string name="sort_clear">Cancella</string>
|
<string name="sort_clear">Cancella</string>
|
||||||
|
@ -190,6 +188,7 @@
|
||||||
<string name="no_episodes_found">Nessun episodio trovato</string>
|
<string name="no_episodes_found">Nessun episodio trovato</string>
|
||||||
<string name="delete_file">Elimina file</string>
|
<string name="delete_file">Elimina file</string>
|
||||||
<string name="delete">Elimina</string>
|
<string name="delete">Elimina</string>
|
||||||
|
<string name="cancel">Cancella</string>
|
||||||
<string name="pause">Pausa</string>
|
<string name="pause">Pausa</string>
|
||||||
<string name="resume">Riprendi</string>
|
<string name="resume">Riprendi</string>
|
||||||
<string name="go_back_30">-30</string>
|
<string name="go_back_30">-30</string>
|
||||||
|
@ -208,7 +207,7 @@
|
||||||
<string name="synopsis">Sinossi</string>
|
<string name="synopsis">Sinossi</string>
|
||||||
<string name="queued">In coda</string>
|
<string name="queued">In coda</string>
|
||||||
<string name="no_subtitles">Nessun sottotiolo</string>
|
<string name="no_subtitles">Nessun sottotiolo</string>
|
||||||
<string name="default_subtitles">Default</string>
|
<string name="action_default">Default</string>
|
||||||
<string name="free_storage">Libero</string>
|
<string name="free_storage">Libero</string>
|
||||||
<string name="used_storage">Usato</string>
|
<string name="used_storage">Usato</string>
|
||||||
<string name="app_storage">App</string>
|
<string name="app_storage">App</string>
|
||||||
|
@ -410,8 +409,8 @@
|
||||||
<string name="plugin_deleted">Plugin eliminato</string>
|
<string name="plugin_deleted">Plugin eliminato</string>
|
||||||
<string name="plugin_load_fail" formatted="true">Impossibile caricare %s</string>
|
<string name="plugin_load_fail" formatted="true">Impossibile caricare %s</string>
|
||||||
<string name="is_adult">18+</string>
|
<string name="is_adult">18+</string>
|
||||||
<string name="batch_download_start_format" formatted="true">Download iniziato %d %s…</string>
|
<string name="batch_download_start_format" formatted="true">Download iniziato %1$d %2$s…</string>
|
||||||
<string name="batch_download_finish_format" formatted="true">Scaricato %d %s</string>
|
<string name="batch_download_finish_format" formatted="true">Scaricato %1$d %2$s</string>
|
||||||
<string name="batch_download_nothing_to_download_format" formatted="true">Tutti %s già scaricati</string>
|
<string name="batch_download_nothing_to_download_format" formatted="true">Tutti %s già scaricati</string>
|
||||||
<string name="batch_download">Download in blocco</string>
|
<string name="batch_download">Download in blocco</string>
|
||||||
<string name="plugin_singular">plugin</string>
|
<string name="plugin_singular">plugin</string>
|
||||||
|
@ -572,7 +571,32 @@
|
||||||
<string name="no_repository_found_error">Repository non trovato, controlla l\'URL e prova la VPN</string>
|
<string name="no_repository_found_error">Repository non trovato, controlla l\'URL e prova la VPN</string>
|
||||||
<string name="unable_to_inflate">Non è stato possibile creare correttamente l\'interfaccia utente, questo è un GRANDE BUG e dovrebbe essere segnalato immediatamente %s</string>
|
<string name="unable_to_inflate">Non è stato possibile creare correttamente l\'interfaccia utente, questo è un GRANDE BUG e dovrebbe essere segnalato immediatamente %s</string>
|
||||||
<string name="automatic_plugin_download_mode_title">Seleziona la modalità per filtrare il download dei plugin</string>
|
<string name="automatic_plugin_download_mode_title">Seleziona la modalità per filtrare il download dei plugin</string>
|
||||||
<string name="default_account">@string/default_subtitles</string>
|
|
||||||
<string name="disable">Disabilita</string>
|
<string name="disable">Disabilita</string>
|
||||||
<string name="already_voted">Hai già votato</string>
|
<string name="already_voted">Hai già votato</string>
|
||||||
|
<string name="favorite_removed">%s rimosso dai preferiti</string>
|
||||||
|
<string name="favorites_list_name">Preferiti</string>
|
||||||
|
<string name="favorite_added">%s aggiunto ai preferiti</string>
|
||||||
|
<string name="duplicate_message_multiple" formatted="true">Dei possibili duplicati sono stati trovati nella tua libreria:
|
||||||
|
\n
|
||||||
|
\n%s
|
||||||
|
\n
|
||||||
|
\nVorresti aggiungere l\'oggetto alla libreria comunque, rimpiazzare l\'esistente, o cancellare l\'azione\?</string>
|
||||||
|
<string name="backup_frequency">Frequenza di backup</string>
|
||||||
|
<string name="duplicate_title">Trovato Possibile Duplicato</string>
|
||||||
|
<string name="action_add_to_favorites">Aggiungi ai preferiti</string>
|
||||||
|
<string name="duplicate_replace_all">Rimpiazza tutti</string>
|
||||||
|
<string name="pin_error_incorrect">PIN non corretto. Riprova.</string>
|
||||||
|
<string name="action_unsubscribe">Disiscriviti</string>
|
||||||
|
<string name="pin_error_length">Il PIN deve essere almeno di 4 caratteri</string>
|
||||||
|
<string name="duplicate_replace">Rimpiazza</string>
|
||||||
|
<string name="duplicate_add">Aggiungi</string>
|
||||||
|
<string name="action_subscribe">Iscriviti</string>
|
||||||
|
<string name="action_remove_from_favorites">Rimuovi dai preferiti</string>
|
||||||
|
<string name="select_an_account">Seleziona un Account</string>
|
||||||
|
<string name="duplicate_message_single">Sembra che un oggetto potenziale duplicato sia già presente nella tua libreria: \'%1$s.\'
|
||||||
|
\n
|
||||||
|
\nVorresti aggiungere l\'oggetto lo stesso, rimpiazzare l\'esistente, o cancellare l\'azione\?</string>
|
||||||
|
<string name="enter_pin">Inserisci PIN</string>
|
||||||
|
<string name="pin">PIN</string>
|
||||||
|
<string name="enter_current_pin">Inserisci PIN Corrente</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -66,7 +66,6 @@
|
||||||
<string name="action_remove_from_bookmarks">הסר</string>
|
<string name="action_remove_from_bookmarks">הסר</string>
|
||||||
<string name="action_add_to_bookmarks">הגדר מצב צפייה</string>
|
<string name="action_add_to_bookmarks">הגדר מצב צפייה</string>
|
||||||
<string name="sort_apply">ליישם</string>
|
<string name="sort_apply">ליישם</string>
|
||||||
<string name="sort_cancel">בטל</string>
|
|
||||||
<string name="sort_copy">העתק</string>
|
<string name="sort_copy">העתק</string>
|
||||||
<string name="sort_close">לסגור</string>
|
<string name="sort_close">לסגור</string>
|
||||||
<string name="sort_clear">נקה</string>
|
<string name="sort_clear">נקה</string>
|
||||||
|
@ -78,7 +77,6 @@
|
||||||
<string name="type_watching">צופה</string>
|
<string name="type_watching">צופה</string>
|
||||||
<string name="pick_subtitle">כתוביות</string>
|
<string name="pick_subtitle">כתוביות</string>
|
||||||
<string name="type_on_hold">בהמתנה</string>
|
<string name="type_on_hold">בהמתנה</string>
|
||||||
<string name="type_none">ללא</string>
|
|
||||||
<string name="download">להוריד</string>
|
<string name="download">להוריד</string>
|
||||||
<string name="app_dubbed_text">מדובב</string>
|
<string name="app_dubbed_text">מדובב</string>
|
||||||
<string name="home_more_info">יותר מידע</string>
|
<string name="home_more_info">יותר מידע</string>
|
||||||
|
@ -146,6 +144,7 @@
|
||||||
<string name="no_episodes_found">לא נמצאו פרקים</string>
|
<string name="no_episodes_found">לא נמצאו פרקים</string>
|
||||||
<string name="delete_file">מחק קובץ</string>
|
<string name="delete_file">מחק קובץ</string>
|
||||||
<string name="delete">מחק</string>
|
<string name="delete">מחק</string>
|
||||||
|
<string name="cancel">בטל</string>
|
||||||
<string name="pause">השהה</string>
|
<string name="pause">השהה</string>
|
||||||
<string name="resume">המשך</string>
|
<string name="resume">המשך</string>
|
||||||
<string name="go_back_30">-30</string>
|
<string name="go_back_30">-30</string>
|
||||||
|
@ -159,7 +158,7 @@
|
||||||
<string name="rating">דירוג</string>
|
<string name="rating">דירוג</string>
|
||||||
<string name="year">שנה</string>
|
<string name="year">שנה</string>
|
||||||
<string name="no_subtitles">ללא כתוביות</string>
|
<string name="no_subtitles">ללא כתוביות</string>
|
||||||
<string name="default_subtitles">ברירת מחדל</string>
|
<string name="action_default">ברירת מחדל</string>
|
||||||
<string name="free_storage">חינם</string>
|
<string name="free_storage">חינם</string>
|
||||||
<string name="used_storage">משומש</string>
|
<string name="used_storage">משומש</string>
|
||||||
<string name="tv_series">סדרת טלוויזיה</string>
|
<string name="tv_series">סדרת טלוויזיה</string>
|
||||||
|
@ -519,7 +518,6 @@
|
||||||
<string name="edit">עריכה</string>
|
<string name="edit">עריכה</string>
|
||||||
<string name="wifi">Wi-Fi</string>
|
<string name="wifi">Wi-Fi</string>
|
||||||
<string name="profile_background_des">רקע הפרופיל</string>
|
<string name="profile_background_des">רקע הפרופיל</string>
|
||||||
<string name="default_account">@string/default_subtitles</string>
|
|
||||||
<string name="test_log">רשומה</string>
|
<string name="test_log">רשומה</string>
|
||||||
<string name="help">עזרה</string>
|
<string name="help">עזרה</string>
|
||||||
<string name="start">התחלה</string>
|
<string name="start">התחלה</string>
|
||||||
|
|
|
@ -37,7 +37,6 @@
|
||||||
<string name="asian_drama_singular">アジアドラマ</string>
|
<string name="asian_drama_singular">アジアドラマ</string>
|
||||||
<string name="live_singular">ライブ配信</string>
|
<string name="live_singular">ライブ配信</string>
|
||||||
<string name="nsfw_singular">NSFW</string>
|
<string name="nsfw_singular">NSFW</string>
|
||||||
<string name="sort_cancel">キャンセル</string>
|
|
||||||
<string name="anime">アニメ</string>
|
<string name="anime">アニメ</string>
|
||||||
<string name="video_lock">ロック</string>
|
<string name="video_lock">ロック</string>
|
||||||
<string name="video_source">ソース</string>
|
<string name="video_source">ソース</string>
|
||||||
|
@ -71,7 +70,6 @@
|
||||||
<string name="home_source">ソース</string>
|
<string name="home_source">ソース</string>
|
||||||
<string name="history">履歴</string>
|
<string name="history">履歴</string>
|
||||||
<string name="result_poster_img_des">ポスター</string>
|
<string name="result_poster_img_des">ポスター</string>
|
||||||
<string name="type_none">なし</string>
|
|
||||||
<string name="sort_copy">コピー</string>
|
<string name="sort_copy">コピー</string>
|
||||||
<string name="sort_close">閉じる</string>
|
<string name="sort_close">閉じる</string>
|
||||||
<string name="sort_save">保存</string>
|
<string name="sort_save">保存</string>
|
||||||
|
@ -135,6 +133,7 @@
|
||||||
<string name="pause">一時停止</string>
|
<string name="pause">一時停止</string>
|
||||||
<string name="play_episode_toast">再生エピソード</string>
|
<string name="play_episode_toast">再生エピソード</string>
|
||||||
<string name="delete">削除</string>
|
<string name="delete">削除</string>
|
||||||
|
<string name="cancel">キャンセル</string>
|
||||||
<string name="start">開始</string>
|
<string name="start">開始</string>
|
||||||
<string name="status">状態</string>
|
<string name="status">状態</string>
|
||||||
<string name="year">年</string>
|
<string name="year">年</string>
|
||||||
|
@ -155,7 +154,7 @@
|
||||||
<string name="extension_version">バージョン</string>
|
<string name="extension_version">バージョン</string>
|
||||||
<string name="extension_rating" formatted="true">視聴率 %s</string>
|
<string name="extension_rating" formatted="true">視聴率 %s</string>
|
||||||
<string name="rating">視聴率</string>
|
<string name="rating">視聴率</string>
|
||||||
<string name="default_subtitles">デフォルト</string>
|
<string name="action_default">デフォルト</string>
|
||||||
<string name="download_failed">ダウンロード失敗</string>
|
<string name="download_failed">ダウンロード失敗</string>
|
||||||
<string name="download_started">ダウンロード開始</string>
|
<string name="download_started">ダウンロード開始</string>
|
||||||
<string name="download_done">ダウンロード完了</string>
|
<string name="download_done">ダウンロード完了</string>
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue