├── .github └── workflows │ └── android.yml ├── .gitignore ├── COPYING ├── README.md ├── app ├── build.gradle ├── proguard-rules.pro └── src │ ├── debug │ └── res │ │ ├── mipmap-anydpi-v26 │ │ └── ic_launcher.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ └── ic_launcher_foreground.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ └── ic_launcher_foreground.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ └── ic_launcher_foreground.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ └── ic_launcher_foreground.png │ │ └── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ └── ic_launcher_foreground.png │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── redcoracle │ │ └── episodes │ │ ├── AboutActivity.java │ │ ├── AddShowPreviewActivity.java │ │ ├── AddShowPreviewFragment.java │ │ ├── AddShowSearchActivity.java │ │ ├── AddShowSearchFragment.java │ │ ├── AddShowSearchResults.java │ │ ├── AutoRefreshHelper.java │ │ ├── EpisodeActivity.java │ │ ├── EpisodeDetailsFragment.java │ │ ├── EpisodesApplication.java │ │ ├── EpisodesCounter.java │ │ ├── EpisodesListFragment.java │ │ ├── FileUtilities.java │ │ ├── MainActivity.java │ │ ├── NextEpisodeFragment.java │ │ ├── Preferences.java │ │ ├── RefreshShowUtil.java │ │ ├── SeasonActivity.java │ │ ├── SeasonsListFragment.java │ │ ├── SelectBackupDialog.java │ │ ├── SettingsActivity.java │ │ ├── SettingsFragment.java │ │ ├── ShowActivity.java │ │ ├── ShowDetailsFragment.java │ │ ├── ShowNotesFragment.java │ │ ├── ShowsListFragment.java │ │ ├── db │ │ ├── DatabaseOpenHelper.java │ │ ├── EpisodesTable.java │ │ ├── ShowsProvider.java │ │ └── ShowsTable.java │ │ ├── services │ │ ├── AddShowTask.java │ │ ├── AsyncTask.java │ │ ├── BackupTask.java │ │ ├── DeleteShowTask.java │ │ ├── RefreshAllShowsTask.java │ │ ├── RefreshShowService.java │ │ ├── RefreshShowTask.java │ │ └── RestoreTask.java │ │ ├── tvdb │ │ ├── Client.java │ │ ├── Episode.java │ │ ├── GetEpisodesParser.java │ │ ├── GetShowParser.java │ │ ├── SearchShowsParser.java │ │ └── Show.java │ │ └── widget │ │ ├── ObservableScrollView.java │ │ ├── WrapContentListView.java │ │ └── WrapContentViewPager.java │ └── res │ ├── color │ └── tab_text.xml │ ├── drawable-hdpi │ ├── ic_menu_add_show.png │ ├── ic_menu_filter_shows_list.png │ └── shows_list_gradient.xml │ ├── drawable-mdpi │ ├── ic_menu_add_show.png │ └── ic_menu_filter_shows_list.png │ ├── drawable-nodpi │ └── blank_show_banner.png │ ├── drawable-xhdpi │ ├── ic_menu_add_show.png │ └── ic_menu_filter_shows_list.png │ ├── drawable-xxhdpi │ ├── ic_menu_add_show.png │ └── ic_menu_filter_shows_list.png │ ├── drawable │ ├── archived_toggle.xml │ ├── gradient.xml │ ├── ic_show_archived.xml │ ├── ic_show_starred.xml │ ├── ic_show_unarchived.xml │ ├── ic_show_unstarred.xml │ ├── starred_toggle.xml │ └── watched_progress.xml │ ├── layout │ ├── about_activity.xml │ ├── add_show_preview_activity.xml │ ├── add_show_preview_fragment.xml │ ├── add_show_search_activity.xml │ ├── add_show_search_fragment.xml │ ├── add_show_search_results_list_item.xml │ ├── episode_activity.xml │ ├── episode_details_fragment.xml │ ├── episodes_list_fragment.xml │ ├── episodes_list_item.xml │ ├── main_activity.xml │ ├── season_activity.xml │ ├── seasons_list_fragment.xml │ ├── seasons_list_item.xml │ ├── settings_activity.xml │ ├── show_activity.xml │ ├── show_details_fragment.xml │ ├── show_notes_fragment.xml │ ├── shows_list_fragment.xml │ └── shows_list_item.xml │ ├── menu │ ├── add_show_preview_fragment.xml │ ├── main.xml │ ├── season_activity.xml │ ├── show_activity.xml │ └── shows_list_fragment.xml │ ├── mipmap-anydpi-v26 │ └── ic_launcher.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ ├── ic_launcher_background.png │ └── ic_launcher_foreground.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ ├── ic_launcher_background.png │ └── ic_launcher_foreground.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ ├── ic_launcher_background.png │ └── ic_launcher_foreground.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ ├── ic_launcher_background.png │ └── ic_launcher_foreground.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ ├── ic_launcher_background.png │ └── ic_launcher_foreground.png │ ├── values-bg │ └── strings.xml │ ├── values-cs │ └── strings.xml │ ├── values-de │ └── strings.xml │ ├── values-eo │ └── strings.xml │ ├── values-es │ └── strings.xml │ ├── values-eu │ └── strings.xml │ ├── values-fr │ └── strings.xml │ ├── values-hr │ └── strings.xml │ ├── values-it │ └── strings.xml │ ├── values-nb-rNO │ └── strings.xml │ ├── values-ru │ └── strings.xml │ ├── values-sv │ └── strings.xml │ ├── values-tr │ └── strings.xml │ ├── values-v14 │ └── styles.xml │ ├── values │ ├── arrays.xml │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ ├── styles.xml │ └── themes.xml │ └── xml │ └── preferences.xml ├── assets ├── feature-graphic.png ├── hi-res-icon.png ├── screenshot-1.png ├── screenshot-2.png └── screenshot-3.png ├── build.gradle ├── fastlane └── metadata │ └── android │ └── en-US │ ├── changelogs │ ├── 0014.txt │ ├── 0015.txt │ ├── 0016.txt │ ├── 0017.txt │ ├── 0018.txt │ ├── 0019.txt │ ├── 0020.txt │ ├── 0021.txt │ ├── 0022.txt │ ├── 0023.txt │ ├── 0024.txt │ └── 0025.txt │ ├── full_description.txt │ ├── images │ ├── icon.png │ └── phoneScreenshots │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.png │ │ └── 6.png │ ├── short_description.txt │ └── title.txt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.github/workflows/android.yml: -------------------------------------------------------------------------------- 1 | name: Android CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - name: Setup JDK 11 | uses: actions/setup-java@v4 12 | with: 13 | distribution: 'temurin' 14 | java-version: '17' 15 | - name: Test with Gradle 16 | run: ./gradlew test 17 | 18 | build: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v4 22 | - name: Setup JDK 23 | uses: actions/setup-java@v4 24 | with: 25 | distribution: 'temurin' 26 | java-version: '17' 27 | - name: Verify Gradle 28 | uses: gradle/actions/wrapper-validation@v4 29 | - name: Build with Gradle 30 | run: ./gradlew build 31 | - name: Upload APK 32 | uses: actions/upload-artifact@v4 33 | with: 34 | name: app-debug 35 | path: app/build/outputs/apk/standard/debug/app-standard-debug.apk 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .gradle/ 3 | local.properties 4 | .idea/ 5 | *.iml 6 | app/fdroid/ 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Episodes 2 | ![GitHub Workflow Status (branch)](https://img.shields.io/github/workflow/status/red-coracle/episodes/Android%20CI/master?style=flat-square) 3 | [![Translation status](https://img.shields.io/weblate/progress/episodes?style=flat-square)](https://hosted.weblate.org/engage/episodes/) 4 | [![F-Droid Version](https://img.shields.io/f-droid/v/com.redcoracle.episodes?style=flat-square&color=%235183C0)](https://f-droid.org/en/packages/com.redcoracle.episodes) 5 | 6 | Keep track of which episodes you've watched of your favourite shows. 7 | 8 | This product uses the [TMDB](https://www.themoviedb.org) API but is not endorsed or certified by TMDB. 9 | 10 | ## Contributing 11 | 12 | Contributions are very welcome. Please file bugs, fork the repository, [translate](https://hosted.weblate.org/projects/episodes/) and send pull requests. 13 | 14 | ## License 15 | 16 | Copylefted libre software licensed [GPLv3](http://www.gnu.org/licenses/gpl-3.0.txt)+. 17 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | def getCommitId = { -> 4 | def gitOutput = new ByteArrayOutputStream() 5 | exec { 6 | commandLine 'git', 'rev-parse', '--short', 'HEAD' 7 | standardOutput = gitOutput 8 | } 9 | return gitOutput.toString().trim() 10 | } 11 | 12 | android { 13 | namespace 'com.redcoracle.episodes' 14 | compileSdkVersion 33 15 | defaultConfig { 16 | minSdkVersion 21 17 | targetSdkVersion 33 18 | versionCode 25 19 | versionName "0.16.1" 20 | buildConfigField "String", "TMDB_KEY", "\"1553d2e4fa2912fc0953305d4d3e7c44\"" 21 | buildConfigField "String", "GIT_COMMIT_ID", "\"${getCommitId()}\"" 22 | } 23 | flavorDimensions "flavor" 24 | productFlavors { 25 | standard { 26 | dimension "flavor" 27 | applicationId = "com.redcoracle.episodes" 28 | } 29 | } 30 | buildTypes { 31 | debug { 32 | applicationIdSuffix '.debug' 33 | versionNameSuffix '-DEBUG' 34 | multiDexEnabled true 35 | } 36 | release { 37 | minifyEnabled true 38 | shrinkResources true 39 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 40 | } 41 | } 42 | lint { 43 | abortOnError false 44 | } 45 | compileOptions { 46 | sourceCompatibility = 1.8 47 | targetCompatibility = 1.8 48 | } 49 | if (project.hasProperty('signingStoreLocation') && 50 | project.hasProperty('signingStorePassword') && 51 | project.hasProperty('signingKeyAlias') && 52 | project.hasProperty('signingKeyPassword')) { 53 | println "Found sign properties in gradle.properties! Signing build…" 54 | 55 | signingConfigs { 56 | release { 57 | storeFile file(signingStoreLocation) 58 | storePassword signingStorePassword 59 | keyAlias signingKeyAlias 60 | keyPassword signingKeyPassword 61 | } 62 | } 63 | 64 | buildTypes.release.signingConfig = signingConfigs.release 65 | } else { 66 | buildTypes.release.signingConfig = null 67 | } 68 | } 69 | 70 | dependencies { 71 | implementation fileTree(include: ['*.jar'], dir: 'libs') 72 | implementation 'androidx.appcompat:appcompat:1.4.2' 73 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 74 | implementation 'androidx.preference:preference:1.2.0' 75 | implementation 'androidx.recyclerview:recyclerview:1.2.1' 76 | implementation 'com.google.android.material:material:1.6.1' 77 | implementation 'com.github.bumptech.glide:glide:4.14.1' 78 | implementation 'com.uwetrottmann.tmdb2:tmdb-java:2.8.1' 79 | implementation 'org.apache.commons:commons-collections4:4.4' 80 | debugImplementation 'com.android.support:multidex:2.0.1' 81 | annotationProcessor 'com.github.bumptech.glide:compiler:4.14.1' 82 | } 83 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /opt/android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # Glide 13 | -keep public class * implements com.bumptech.glide.module.GlideModule 14 | -keep public class * extends com.bumptech.glide.module.AppGlideModule 15 | -keep public enum com.bumptech.glide.load.ImageHeaderParser$** { 16 | **[] $VALUES; 17 | public *; 18 | } 19 | 20 | # TMDB 21 | -keep class com.uwetrottmann.tmdb2.entities.** { *; } 22 | -keep class com.uwetrottmann.tmdb2.enumerations.** { *; } 23 | 24 | # OkHttp platform used only on JVM and when Conscrypt and other security providers are available. 25 | -dontwarn okhttp3.internal.platform.** 26 | -dontwarn org.conscrypt.** 27 | -dontwarn org.bouncycastle.** 28 | -dontwarn org.openjsse.** 29 | 30 | # R8 said to add these 31 | -dontwarn kotlin.coroutines.Continuation 32 | -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/debug/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-hdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/debug/res/mipmap-hdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/debug/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-mdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/debug/res/mipmap-mdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/debug/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/debug/res/mipmap-xhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/debug/res/mipmap-xxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xxxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 17 | 18 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 33 | 36 | 37 | 38 | 41 | 42 | 43 | 46 | 47 | 48 | 51 | 52 | 53 | 57 | 58 | 59 | 62 | 63 | 64 | 67 | 68 | 69 | 71 | 72 | 73 | 76 | 77 | 78 | 79 | 80 | 81 | 85 | 86 | 87 | 88 | 89 | 90 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/AboutActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Jamie Nicol 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.redcoracle.episodes; 19 | 20 | import android.os.Bundle; 21 | import android.view.MenuItem; 22 | import android.widget.TextView; 23 | 24 | import androidx.appcompat.app.AppCompatActivity; 25 | 26 | public class AboutActivity 27 | extends AppCompatActivity 28 | { 29 | @Override 30 | public void onCreate(Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | 33 | setContentView(R.layout.about_activity); 34 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 35 | 36 | TextView versionInfoView = findViewById(R.id.version_view); 37 | versionInfoView.setText( 38 | getBaseContext().getString( 39 | R.string.version, 40 | BuildConfig.VERSION_NAME, 41 | BuildConfig.GIT_COMMIT_ID)); 42 | } 43 | 44 | @Override 45 | public boolean onOptionsItemSelected(MenuItem item) { 46 | switch (item.getItemId()) { 47 | case android.R.id.home: 48 | finish(); 49 | return true; 50 | 51 | default: 52 | return super.onOptionsItemSelected(item); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/AddShowPreviewActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Jamie Nicol 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.redcoracle.episodes; 19 | 20 | import android.content.Intent; 21 | import android.os.Bundle; 22 | import android.view.MenuItem; 23 | 24 | import androidx.appcompat.app.AppCompatActivity; 25 | import androidx.fragment.app.FragmentTransaction; 26 | 27 | import com.redcoracle.episodes.tvdb.Show; 28 | 29 | import java.util.List; 30 | 31 | public class AddShowPreviewActivity 32 | extends AppCompatActivity 33 | { 34 | @Override 35 | public void onCreate(Bundle savedInstanceState) 36 | { 37 | super.onCreate(savedInstanceState); 38 | 39 | setContentView(R.layout.add_show_preview_activity); 40 | 41 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 42 | 43 | Intent intent = getIntent(); 44 | int searchResultIndex = intent.getIntExtra("searchResultIndex", 0); 45 | 46 | AddShowSearchResults results = AddShowSearchResults.getInstance(); 47 | List resultsData = results.getData(); 48 | 49 | // Ensure that there is actually data to display, 50 | // because Android may have destroyed it. 51 | if (resultsData == null) { 52 | // Android has killed the singleton instance which held the 53 | // data. There's nothing this activity can do, so finish. 54 | finish(); 55 | return; 56 | } 57 | 58 | Show show = resultsData.get(searchResultIndex); 59 | getSupportActionBar().setTitle(show.getName()); 60 | 61 | // If this is the first time the activity has been created, 62 | // create and add the preview fragment. 63 | if (savedInstanceState == null) { 64 | AddShowPreviewFragment fragment = 65 | AddShowPreviewFragment.newInstance(searchResultIndex); 66 | FragmentTransaction transaction = 67 | getSupportFragmentManager().beginTransaction(); 68 | transaction.add(R.id.preview_fragment_container, fragment); 69 | transaction.commit(); 70 | } 71 | } 72 | 73 | @Override 74 | public boolean onOptionsItemSelected(MenuItem item) { 75 | switch (item.getItemId()) { 76 | case android.R.id.home: 77 | finish(); 78 | return true; 79 | 80 | default: 81 | return super.onOptionsItemSelected(item); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/AddShowPreviewFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Jamie Nicol 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.redcoracle.episodes; 19 | 20 | import android.os.Bundle; 21 | import android.view.LayoutInflater; 22 | import android.view.Menu; 23 | import android.view.MenuInflater; 24 | import android.view.MenuItem; 25 | import android.view.View; 26 | import android.view.ViewGroup; 27 | import android.widget.TextView; 28 | 29 | import androidx.fragment.app.Fragment; 30 | 31 | import com.redcoracle.episodes.services.AddShowTask; 32 | import com.redcoracle.episodes.services.AsyncTask; 33 | import com.redcoracle.episodes.tvdb.Show; 34 | 35 | import java.text.DateFormat; 36 | import java.util.Date; 37 | import java.util.List; 38 | 39 | public class AddShowPreviewFragment 40 | extends Fragment 41 | { 42 | private Show show; 43 | 44 | public static AddShowPreviewFragment newInstance(int searchResultIndex) { 45 | AddShowPreviewFragment instance = new AddShowPreviewFragment(); 46 | 47 | Bundle args = new Bundle(); 48 | args.putInt("searchResultIndex", searchResultIndex); 49 | 50 | instance.setArguments(args); 51 | return instance; 52 | } 53 | 54 | public void onCreate(Bundle savedInstanceState) { 55 | super.onCreate(savedInstanceState); 56 | setHasOptionsMenu(true); 57 | } 58 | 59 | @Override 60 | public View onCreateView(LayoutInflater inflater, 61 | ViewGroup container, 62 | Bundle savedInstanceState) { 63 | View view = inflater.inflate(R.layout.add_show_preview_fragment, 64 | container, 65 | false); 66 | 67 | TextView overviewView = (TextView)view.findViewById(R.id.overview); 68 | TextView firstAiredView = (TextView)view.findViewById(R.id.first_aired); 69 | 70 | int searchResultIndex = getArguments().getInt("searchResultIndex"); 71 | 72 | AddShowSearchResults results = AddShowSearchResults.getInstance(); 73 | List resultsData = results.getData(); 74 | 75 | // Ensure that there is actually data to display, because Android 76 | // may have destroyed it. If there is data display it, if there 77 | // isn't do nothing and the activity will handle the situation. 78 | if (resultsData != null) { 79 | show = resultsData.get(searchResultIndex); 80 | 81 | overviewView.setText(show.getOverview()); 82 | 83 | Date firstAired = show.getFirstAired(); 84 | if (firstAired != null) { 85 | DateFormat df = DateFormat.getDateInstance(); 86 | String text = getString(R.string.first_aired, 87 | df.format(show.getFirstAired())); 88 | firstAiredView.setText(text); 89 | } else { 90 | firstAiredView.setText(""); 91 | } 92 | } 93 | 94 | return view; 95 | } 96 | 97 | @Override 98 | public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 99 | inflater.inflate(R.menu.add_show_preview_fragment, menu); 100 | } 101 | 102 | @Override 103 | public boolean onOptionsItemSelected(MenuItem item) { 104 | switch (item.getItemId()) { 105 | case R.id.menu_add_show: 106 | addShow(); 107 | getActivity().finish(); 108 | 109 | default: 110 | return super.onOptionsItemSelected(item); 111 | } 112 | } 113 | 114 | private void addShow() { 115 | new AsyncTask().executeAsync(new AddShowTask(show.getId(), show.getName(), show.getLanguage())); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/AddShowSearchActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Jamie Nicol 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.redcoracle.episodes; 19 | 20 | import android.content.Intent; 21 | import android.os.Bundle; 22 | import android.view.MenuItem; 23 | import android.view.Window; 24 | 25 | import androidx.appcompat.app.AppCompatActivity; 26 | import androidx.fragment.app.FragmentTransaction; 27 | 28 | public class AddShowSearchActivity 29 | extends AppCompatActivity 30 | { 31 | @Override 32 | public void onCreate(Bundle savedInstanceState) 33 | { 34 | super.onCreate(savedInstanceState); 35 | 36 | supportRequestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); 37 | setContentView(R.layout.add_show_search_activity); 38 | 39 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 40 | 41 | Intent intent = getIntent(); 42 | String query = intent.getStringExtra("query"); 43 | 44 | getSupportActionBar().setTitle(query); 45 | 46 | // create and add search fragment, 47 | // but only on the first time the activity is created 48 | if (savedInstanceState == null) { 49 | AddShowSearchFragment fragment = 50 | AddShowSearchFragment.newInstance(query); 51 | FragmentTransaction transaction = 52 | getSupportFragmentManager().beginTransaction(); 53 | transaction.add(R.id.search_fragment_container, fragment); 54 | transaction.commit(); 55 | } 56 | } 57 | 58 | @Override 59 | public boolean onOptionsItemSelected(MenuItem item) { 60 | switch (item.getItemId()) { 61 | case android.R.id.home: 62 | Intent intent = new Intent(this, MainActivity.class); 63 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | 64 | Intent.FLAG_ACTIVITY_NEW_TASK); 65 | startActivity(intent); 66 | 67 | finish(); 68 | return true; 69 | 70 | default: 71 | return super.onOptionsItemSelected(item); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/AddShowSearchResults.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Jamie Nicol 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.redcoracle.episodes; 19 | 20 | import com.redcoracle.episodes.tvdb.Show; 21 | 22 | import java.util.List; 23 | 24 | public class AddShowSearchResults 25 | { 26 | // singleton instance 27 | private static AddShowSearchResults instance = new AddShowSearchResults(); 28 | 29 | private List data; 30 | 31 | private AddShowSearchResults() { 32 | } 33 | 34 | public static AddShowSearchResults getInstance() { 35 | return instance; 36 | } 37 | 38 | public List getData() { 39 | return data; 40 | } 41 | 42 | public void setData(List data) { 43 | this.data = data; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/EpisodesApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Jamie Nicol 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.redcoracle.episodes; 19 | 20 | import android.app.Application; 21 | import android.app.NotificationChannel; 22 | import android.app.NotificationManager; 23 | import android.os.Build; 24 | import android.util.Log; 25 | 26 | import com.uwetrottmann.tmdb2.Tmdb; 27 | 28 | public class EpisodesApplication extends Application { 29 | private static final String TAG = EpisodesApplication.class.getName(); 30 | private static EpisodesApplication instance; 31 | private Tmdb tmdbClient; 32 | 33 | @Override 34 | public void onCreate() { 35 | super.onCreate(); 36 | 37 | instance = this; 38 | 39 | try { 40 | this.tmdbClient = new Tmdb(BuildConfig.TMDB_KEY); 41 | } catch (Exception e) { 42 | Log.d(TAG, "Error initialising TmdbClient", e); 43 | } 44 | 45 | createNotificationChannel(); 46 | } 47 | 48 | public static EpisodesApplication getInstance() { 49 | return instance; 50 | } 51 | 52 | public Tmdb getTmdbClient() { 53 | return this.tmdbClient; 54 | } 55 | 56 | private void createNotificationChannel(){ 57 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 58 | CharSequence name = getString(R.string.channel_name); 59 | String description = getString(R.string.channel_description); 60 | int importance = NotificationManager.IMPORTANCE_LOW; 61 | NotificationChannel channel = new NotificationChannel("episodes_channel_id", name, importance); 62 | channel.setDescription(description); 63 | NotificationManager notificationManager = getSystemService(NotificationManager.class); 64 | notificationManager.createNotificationChannel(channel); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/EpisodesCounter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012-2014 Jamie Nicol 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.redcoracle.episodes; 19 | 20 | import android.content.SharedPreferences; 21 | import android.database.Cursor; 22 | import android.util.SparseIntArray; 23 | 24 | import com.redcoracle.episodes.db.EpisodesTable; 25 | 26 | import java.util.Collections; 27 | import java.util.Date; 28 | import java.util.Set; 29 | import java.util.TreeSet; 30 | 31 | public class EpisodesCounter 32 | { 33 | private String keyColumn; 34 | private Set keys; 35 | private SparseIntArray numAiredEpisodesMap; 36 | private SparseIntArray numWatchedEpisodesMap; 37 | private SparseIntArray numUpcomingEpisodesMap; 38 | 39 | public EpisodesCounter(String keyColumn) { 40 | this.keyColumn = keyColumn; 41 | 42 | SharedPreferences preferences = Preferences.getSharedPreferences(); 43 | 44 | if (preferences.getBoolean("reverse_sort_order", false)) { 45 | keys = new TreeSet(Collections.reverseOrder()); 46 | } else { 47 | keys = new TreeSet(); 48 | } 49 | 50 | // keys = new TreeSet(); 51 | numAiredEpisodesMap = new SparseIntArray(); 52 | numWatchedEpisodesMap = new SparseIntArray(); 53 | numUpcomingEpisodesMap = new SparseIntArray(); 54 | } 55 | 56 | public void swapCursor(Cursor episodesCursor) { 57 | keys.clear(); 58 | numAiredEpisodesMap.clear(); 59 | numWatchedEpisodesMap.clear(); 60 | numUpcomingEpisodesMap.clear(); 61 | 62 | if (episodesCursor == null || episodesCursor.moveToFirst() == false) { 63 | return; 64 | } 65 | 66 | do { 67 | final int keyColumnIndex = 68 | episodesCursor.getColumnIndexOrThrow(keyColumn); 69 | final int key = episodesCursor.getInt(keyColumnIndex); 70 | 71 | // check if episode is aired, watched, or upcoming 72 | final int seasonNumberColumnIndex = 73 | episodesCursor.getColumnIndexOrThrow(EpisodesTable.COLUMN_SEASON_NUMBER); 74 | final int seasonNumber = 75 | episodesCursor.getInt(seasonNumberColumnIndex); 76 | 77 | final int firstAiredColumnIndex = 78 | episodesCursor.getColumnIndexOrThrow(EpisodesTable.COLUMN_FIRST_AIRED); 79 | Date firstAired = null; 80 | if (!episodesCursor.isNull(firstAiredColumnIndex)) { 81 | firstAired = 82 | new Date(episodesCursor.getLong(firstAiredColumnIndex) 83 | * 1000); 84 | } 85 | 86 | final int watchedColumnIndex = 87 | episodesCursor.getColumnIndexOrThrow(EpisodesTable.COLUMN_WATCHED); 88 | final boolean watched = 89 | episodesCursor.getInt(watchedColumnIndex) > 0 ? true : false; 90 | 91 | 92 | if (keys.contains(key) == false) { 93 | keys.add(key); 94 | } 95 | 96 | // increment the appropriate counter(s) for this show. 97 | // count shows with no aired date as upcoming, 98 | // unless they're specials in which case count them as aired. 99 | if ((firstAired != null && firstAired.before(new Date())) 100 | || seasonNumber == 0) { 101 | numAiredEpisodesMap.put(key, 102 | numAiredEpisodesMap.get(key) + 1); 103 | if (watched) { 104 | numWatchedEpisodesMap.put(key, 105 | numWatchedEpisodesMap.get(key) + 1); 106 | } 107 | } else { 108 | numUpcomingEpisodesMap.put(key, 109 | numUpcomingEpisodesMap.get(key) + 1); 110 | } 111 | } while (episodesCursor.moveToNext()); 112 | } 113 | 114 | public Set getKeys() { 115 | return keys; 116 | } 117 | 118 | public int getNumAiredEpisodes(int key) { 119 | final Integer value = numAiredEpisodesMap.get(key); 120 | if (value == null) { 121 | return 0; 122 | } else { 123 | return value; 124 | } 125 | } 126 | 127 | public int getNumWatchedEpisodes(int key) { 128 | final Integer value = numWatchedEpisodesMap.get(key); 129 | if (value == null) { 130 | return 0; 131 | } else { 132 | return value; 133 | } 134 | } 135 | 136 | public int getNumUpcomingEpisodes(int key) { 137 | final Integer value = numUpcomingEpisodesMap.get(key); 138 | if (value == null) { 139 | return 0; 140 | } else { 141 | return value; 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/FileUtilities.java: -------------------------------------------------------------------------------- 1 | package com.redcoracle.episodes; 2 | 3 | import android.content.Context; 4 | import android.database.Cursor; 5 | import android.net.Uri; 6 | import android.provider.OpenableColumns; 7 | 8 | import java.io.IOException; 9 | import java.nio.channels.FileChannel; 10 | import java.text.DateFormat; 11 | import java.text.SimpleDateFormat; 12 | import java.util.Date; 13 | import java.util.Locale; 14 | 15 | public class FileUtilities { 16 | public static String get_suggested_filename() { 17 | final Date today = new Date(); 18 | final DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd_HHmm", Locale.getDefault()); 19 | return String.format("episodes_%s.db", formatter.format(today)); 20 | } 21 | 22 | public static String uri_to_filename(Context context, Uri uri) { 23 | Cursor cursor = context.getContentResolver().query(uri, null, null, null, null); 24 | int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); 25 | cursor.moveToFirst(); 26 | String filename = cursor.getString(nameIndex); 27 | cursor.close(); 28 | return filename; 29 | } 30 | 31 | public static void copy_file(FileChannel source, FileChannel destination) { 32 | try { 33 | destination.transferFrom(source, 0, source.size()); 34 | source.close(); 35 | destination.close(); 36 | } catch (IOException e) { 37 | e.printStackTrace(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/Preferences.java: -------------------------------------------------------------------------------- 1 | package com.redcoracle.episodes; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.preference.PreferenceManager; 6 | 7 | public class Preferences { 8 | public static SharedPreferences getSharedPreferences() { 9 | Context context = MainActivity.getAppContext(); 10 | return PreferenceManager.getDefaultSharedPreferences(context); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/SelectBackupDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Jamie Nicol 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.redcoracle.episodes; 19 | 20 | import android.app.Activity; 21 | import android.app.AlertDialog; 22 | import android.app.Dialog; 23 | import android.content.Context; 24 | import android.os.Bundle; 25 | 26 | import androidx.annotation.NonNull; 27 | import androidx.fragment.app.DialogFragment; 28 | 29 | import java.io.File; 30 | import java.util.Arrays; 31 | 32 | public class SelectBackupDialog extends DialogFragment { 33 | public interface OnBackupSelectedListener { 34 | void onBackupSelected(String backupFilename); 35 | } 36 | private OnBackupSelectedListener onBackupSelectedListener; 37 | private Context context; 38 | 39 | @Override 40 | public void onAttach(@NonNull Context context) { 41 | super.onAttach(context); 42 | this.context = context; 43 | Activity activity; 44 | if (context instanceof Activity) { 45 | activity = (Activity) context; 46 | onBackupSelectedListener = (OnBackupSelectedListener) activity; 47 | } 48 | } 49 | 50 | @NonNull 51 | @Override 52 | public Dialog onCreateDialog(Bundle savedInstanceState) { 53 | final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 54 | 55 | final File[] backups = getBackupFiles(); 56 | 57 | if (backups != null && backups.length > 0) { 58 | return createDialogBackups(builder, backups); 59 | } else { 60 | return createDialogNoBackups(builder); 61 | } 62 | } 63 | 64 | private File[] getBackupFiles() { 65 | final File[] files = new File(this.context.getExternalFilesDir(null), "episodes").listFiles(); 66 | 67 | if (files != null) { 68 | Arrays.sort(files, (lhs, rhs) -> Long.compare(rhs.lastModified(), lhs.lastModified())); 69 | return files; 70 | } else { 71 | return null; 72 | } 73 | } 74 | 75 | private Dialog createDialogBackups(AlertDialog.Builder builder, final File[] backups) { 76 | final String[] names = new String[backups.length]; 77 | for (int i = 0; i < backups.length; i++) { 78 | names[i] = backups[i].getName(); 79 | } 80 | 81 | builder.setTitle(R.string.restore_dialog_title) 82 | .setItems(names, (dialog, which) -> { 83 | final String path = backups[which].getPath(); 84 | onBackupSelectedListener.onBackupSelected(path); 85 | }); 86 | 87 | return builder.create(); 88 | } 89 | 90 | private Dialog createDialogNoBackups(AlertDialog.Builder builder) { 91 | final File directory = new File(this.context.getExternalFilesDir(null), "episodes"); 92 | final String message = getActivity().getString(R.string.restore_dialog_no_backups_message, directory); 93 | builder.setTitle(R.string.restore_dialog_title).setMessage(message); 94 | return builder.create(); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/SettingsActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Jamie Nicol 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.redcoracle.episodes; 19 | 20 | import android.os.Bundle; 21 | 22 | import androidx.appcompat.app.AppCompatActivity; 23 | 24 | public class SettingsActivity extends AppCompatActivity { 25 | @Override 26 | public void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | setContentView(R.layout.settings_activity); 29 | getSupportFragmentManager() 30 | .beginTransaction() 31 | .replace(R.id.settings_fragment, new SettingsFragment()) 32 | .commit(); 33 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/SettingsFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Jamie Nicol 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.redcoracle.episodes; 19 | 20 | import android.os.Bundle; 21 | 22 | import androidx.preference.PreferenceFragmentCompat; 23 | 24 | public class SettingsFragment extends PreferenceFragmentCompat { 25 | @Override 26 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 27 | setPreferencesFromResource(R.xml.preferences, rootKey); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/ShowDetailsFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Jamie Nicol 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.redcoracle.episodes; 19 | 20 | import android.database.Cursor; 21 | import android.net.Uri; 22 | import android.os.Bundle; 23 | import android.view.LayoutInflater; 24 | import android.view.View; 25 | import android.view.ViewGroup; 26 | import android.widget.TextView; 27 | 28 | import androidx.fragment.app.Fragment; 29 | import androidx.loader.app.LoaderManager; 30 | import androidx.loader.content.CursorLoader; 31 | import androidx.loader.content.Loader; 32 | 33 | import com.redcoracle.episodes.db.ShowsProvider; 34 | import com.redcoracle.episodes.db.ShowsTable; 35 | 36 | import java.text.DateFormat; 37 | import java.util.Date; 38 | 39 | public class ShowDetailsFragment 40 | extends Fragment 41 | implements LoaderManager.LoaderCallbacks 42 | { 43 | private int showId; 44 | private TextView overviewView; 45 | private TextView firstAiredView; 46 | 47 | public static ShowDetailsFragment newInstance(int showId) { 48 | ShowDetailsFragment instance = new ShowDetailsFragment(); 49 | 50 | Bundle args = new Bundle(); 51 | args.putInt("showId", showId); 52 | 53 | instance.setArguments(args); 54 | return instance; 55 | } 56 | 57 | @Override 58 | public void onCreate(Bundle savedInstanceState) { 59 | super.onCreate(savedInstanceState); 60 | 61 | showId = getArguments().getInt("showId"); 62 | } 63 | 64 | public View onCreateView(LayoutInflater inflater, 65 | ViewGroup container, 66 | Bundle savedInstanceState) { 67 | View view = inflater.inflate(R.layout.show_details_fragment, 68 | container, 69 | false); 70 | 71 | overviewView = (TextView)view.findViewById(R.id.overview); 72 | firstAiredView = (TextView)view.findViewById(R.id.first_aired); 73 | 74 | return view; 75 | } 76 | 77 | @Override 78 | public void onActivityCreated(Bundle savedInstanceState) { 79 | super.onActivityCreated(savedInstanceState); 80 | 81 | Bundle loaderArgs = new Bundle(); 82 | loaderArgs.putInt("showId", showId); 83 | getLoaderManager().initLoader(0, loaderArgs, this); 84 | } 85 | 86 | @Override 87 | public Loader onCreateLoader(int id, Bundle args) { 88 | int showId = args.getInt("showId"); 89 | Uri uri = Uri.withAppendedPath(ShowsProvider.CONTENT_URI_SHOWS, 90 | String.valueOf(showId)); 91 | String[] projection = { 92 | ShowsTable.COLUMN_OVERVIEW, 93 | ShowsTable.COLUMN_FIRST_AIRED 94 | }; 95 | return new CursorLoader(getActivity(), 96 | uri, 97 | projection, 98 | null, 99 | null, 100 | null); 101 | } 102 | 103 | @Override 104 | public void onLoadFinished(Loader loader, Cursor data) { 105 | if (data != null && data.moveToFirst()) { 106 | 107 | int overviewColumnIndex = 108 | data.getColumnIndexOrThrow(ShowsTable.COLUMN_OVERVIEW); 109 | if (data.isNull(overviewColumnIndex)) { 110 | overviewView.setVisibility(View.GONE); 111 | } else { 112 | overviewView.setText(data.getString(overviewColumnIndex).trim()); 113 | overviewView.setVisibility(View.VISIBLE); 114 | } 115 | 116 | int firstAiredColumnIndex = 117 | data.getColumnIndexOrThrow(ShowsTable.COLUMN_FIRST_AIRED); 118 | if (data.isNull(firstAiredColumnIndex)) { 119 | firstAiredView.setVisibility(View.GONE); 120 | } else { 121 | Date firstAired = 122 | new Date(data.getLong(firstAiredColumnIndex) * 1000); 123 | DateFormat df = DateFormat.getDateInstance(); 124 | String firstAiredText = getString(R.string.first_aired, 125 | df.format(firstAired)); 126 | firstAiredView.setText(firstAiredText); 127 | firstAiredView.setVisibility(View.VISIBLE); 128 | } 129 | 130 | } else { 131 | overviewView.setVisibility(View.GONE); 132 | firstAiredView.setVisibility(View.GONE); 133 | } 134 | } 135 | 136 | @Override 137 | public void onLoaderReset(Loader loader) { 138 | onLoadFinished(loader, null); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/ShowNotesFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Daniele Ricci 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.redcoracle.episodes; 19 | 20 | import android.database.Cursor; 21 | import android.net.Uri; 22 | import android.os.Bundle; 23 | import android.view.LayoutInflater; 24 | import android.view.View; 25 | import android.view.ViewGroup; 26 | import android.widget.TextView; 27 | 28 | import androidx.fragment.app.Fragment; 29 | import androidx.loader.app.LoaderManager; 30 | import androidx.loader.content.CursorLoader; 31 | import androidx.loader.content.Loader; 32 | 33 | import com.redcoracle.episodes.db.ShowsProvider; 34 | import com.redcoracle.episodes.db.ShowsTable; 35 | 36 | public class ShowNotesFragment 37 | extends Fragment 38 | implements LoaderManager.LoaderCallbacks 39 | { 40 | private int showId; 41 | private TextView notesView; 42 | 43 | public static ShowNotesFragment newInstance(int showId) { 44 | ShowNotesFragment instance = new ShowNotesFragment(); 45 | 46 | Bundle args = new Bundle(); 47 | args.putInt("showId", showId); 48 | 49 | instance.setArguments(args); 50 | return instance; 51 | } 52 | 53 | @Override 54 | public void onCreate(Bundle savedInstanceState) { 55 | super.onCreate(savedInstanceState); 56 | 57 | showId = getArguments().getInt("showId"); 58 | } 59 | 60 | public View onCreateView(LayoutInflater inflater, 61 | ViewGroup container, 62 | Bundle savedInstanceState) { 63 | final View view = inflater.inflate(R.layout.show_notes_fragment, 64 | container, 65 | false); 66 | 67 | notesView = (TextView)view.findViewById(R.id.notes); 68 | 69 | return view; 70 | } 71 | 72 | @Override 73 | public void onActivityCreated(Bundle savedInstanceState) { 74 | super.onActivityCreated(savedInstanceState); 75 | 76 | final Bundle loaderArgs = new Bundle(); 77 | loaderArgs.putInt("showId", showId); 78 | getLoaderManager().initLoader(0, loaderArgs, this); 79 | } 80 | 81 | @Override 82 | public Loader onCreateLoader(int id, Bundle args) { 83 | final int showId = args.getInt("showId"); 84 | final Uri uri = Uri.withAppendedPath(ShowsProvider.CONTENT_URI_SHOWS, 85 | String.valueOf(showId)); 86 | final String[] projection = { 87 | ShowsTable.COLUMN_NOTES 88 | }; 89 | return new CursorLoader(getActivity(), 90 | uri, 91 | projection, 92 | null, 93 | null, 94 | null); 95 | } 96 | 97 | @Override 98 | public void onLoadFinished(Loader loader, Cursor data) { 99 | if (data != null && data.moveToFirst()) { 100 | 101 | final int notesColumnIndex = 102 | data.getColumnIndexOrThrow(ShowsTable.COLUMN_NOTES); 103 | if (data.isNull(notesColumnIndex)) { 104 | notesView.setText(""); 105 | } else { 106 | final String text = data.getString(notesColumnIndex); 107 | notesView.setText(text); 108 | } 109 | } 110 | } 111 | 112 | @Override 113 | public void onLoaderReset(Loader loader) { 114 | onLoadFinished(loader, null); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/db/DatabaseOpenHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012-2014 Jamie Nicol 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.redcoracle.episodes.db; 19 | 20 | import android.content.Context; 21 | import android.database.sqlite.SQLiteDatabase; 22 | import android.database.sqlite.SQLiteOpenHelper; 23 | import android.util.Log; 24 | 25 | public class DatabaseOpenHelper extends SQLiteOpenHelper { 26 | private static final String TAG = "DatabaseOpenHelper"; 27 | private static final String name = "episodes.db"; 28 | private static final int version = 9; 29 | 30 | DatabaseOpenHelper(Context context) { 31 | super(context, name, null, version); 32 | } 33 | 34 | @Override 35 | public void onCreate(SQLiteDatabase db) { 36 | Log.d(TAG, "creating database"); 37 | ShowsTable.onCreate(db); 38 | EpisodesTable.onCreate(db); 39 | } 40 | 41 | @Override 42 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 43 | Log.d(TAG, String.format("upgrading database from version %d to %d", oldVersion, newVersion)); 44 | ShowsTable.onUpgrade(db, oldVersion, newVersion); 45 | EpisodesTable.onUpgrade(db, oldVersion, newVersion); 46 | } 47 | 48 | @Override 49 | public void onOpen(SQLiteDatabase db) { 50 | Log.d(TAG, "opening database."); 51 | } 52 | 53 | public static String getDbName() { 54 | return name; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/db/EpisodesTable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012-2014 Jamie Nicol 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.redcoracle.episodes.db; 19 | 20 | import android.database.Cursor; 21 | import android.database.sqlite.SQLiteDatabase; 22 | import android.provider.BaseColumns; 23 | import android.util.Log; 24 | 25 | import java.util.Arrays; 26 | 27 | public class EpisodesTable { 28 | private static final String TAG = "EpisodesTable"; 29 | 30 | static final String TABLE_NAME = "episodes"; 31 | 32 | public static final String COLUMN_ID = BaseColumns._ID; 33 | public static final String COLUMN_TVDB_ID = "tvdb_id"; 34 | public static final String COLUMN_TMDB_ID = "tmdb_id"; 35 | public static final String COLUMN_IMDB_ID = "imdb_id"; 36 | public static final String COLUMN_SHOW_ID = "show_id"; 37 | public static final String COLUMN_NAME = "name"; 38 | public static final String COLUMN_LANGUAGE = "language"; 39 | public static final String COLUMN_OVERVIEW = "overview"; 40 | public static final String COLUMN_EPISODE_NUMBER = "episode_number"; 41 | public static final String COLUMN_SEASON_NUMBER = "season_number"; 42 | public static final String COLUMN_FIRST_AIRED = "first_aired"; 43 | public static final String COLUMN_WATCHED = "watched"; 44 | 45 | public static String createTableSQL(String table_name) { 46 | return String.format( 47 | "CREATE TABLE %s (" + 48 | "%s INTEGER PRIMARY KEY," + 49 | "%s INTEGER UNIQUE," + 50 | "%s INTEGER UNIQUE," + 51 | "%s TEXT UNIQUE," + 52 | "%s INTEGER NOT NULL," + 53 | "%s VARCHAR(200) NOT NULL," + 54 | "%s TEXT," + 55 | "%s TEXT," + 56 | "%s INTEGER," + 57 | "%s INTEGER," + 58 | "%s DATE," + 59 | "%s BOOLEAN" + 60 | ");", 61 | table_name, 62 | COLUMN_ID, 63 | COLUMN_TVDB_ID, 64 | COLUMN_TMDB_ID, 65 | COLUMN_IMDB_ID, 66 | COLUMN_SHOW_ID, 67 | COLUMN_NAME, 68 | COLUMN_LANGUAGE, 69 | COLUMN_OVERVIEW, 70 | COLUMN_EPISODE_NUMBER, 71 | COLUMN_SEASON_NUMBER, 72 | COLUMN_FIRST_AIRED, 73 | COLUMN_WATCHED 74 | ); 75 | } 76 | 77 | public static void onCreate(SQLiteDatabase db) { 78 | String create = createTableSQL(TABLE_NAME); 79 | 80 | Log.d(TAG, String.format("creating episodes table: %s", create)); 81 | 82 | db.execSQL(create); 83 | } 84 | 85 | static void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 86 | if (oldVersion <= 7) { 87 | // Add language column 88 | Cursor cursor = db.query(TABLE_NAME, null, null, null, null, null, null); 89 | String[] columns = cursor.getColumnNames(); 90 | if (!Arrays.asList(columns).contains(COLUMN_LANGUAGE)) { 91 | Log.d(TAG, "upgrading episodes table: adding language column"); 92 | db.execSQL(String.format("ALTER TABLE %s ADD COLUMN %s TEXT", TABLE_NAME, COLUMN_LANGUAGE)); 93 | } 94 | cursor.close(); 95 | } 96 | 97 | if (oldVersion <= 8) { 98 | // Add TMDB/IMDB columns 99 | db.beginTransaction(); 100 | try { 101 | final String temp_table_name = String.format("new_%s", TABLE_NAME); 102 | String create_table = createTableSQL(temp_table_name); 103 | String insert_columns = String.format( 104 | "%s, %s, %s, %s, %s, %s, %s, %s, %s, %s", 105 | COLUMN_ID, COLUMN_TVDB_ID, COLUMN_SHOW_ID, COLUMN_NAME, COLUMN_LANGUAGE, 106 | COLUMN_OVERVIEW, COLUMN_EPISODE_NUMBER, COLUMN_SEASON_NUMBER, 107 | COLUMN_FIRST_AIRED, COLUMN_WATCHED 108 | ); 109 | db.execSQL(create_table); 110 | db.execSQL(String.format( 111 | "INSERT INTO %s (%s) SELECT %s FROM %s", 112 | temp_table_name, insert_columns, insert_columns, TABLE_NAME 113 | )); 114 | db.execSQL(String.format("DROP TABLE %s", TABLE_NAME)); 115 | db.execSQL(String.format( 116 | "ALTER TABLE %s RENAME TO %s", 117 | temp_table_name, TABLE_NAME 118 | )); 119 | db.setTransactionSuccessful(); 120 | } finally { 121 | db.endTransaction(); 122 | } 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/services/AddShowTask.java: -------------------------------------------------------------------------------- 1 | package com.redcoracle.episodes.services; 2 | 3 | import android.content.ContentResolver; 4 | import android.content.ContentValues; 5 | import android.content.Context; 6 | import android.database.Cursor; 7 | import android.net.Uri; 8 | import android.os.Handler; 9 | import android.os.Looper; 10 | import android.util.Log; 11 | import android.widget.Toast; 12 | 13 | import com.redcoracle.episodes.EpisodesApplication; 14 | import com.redcoracle.episodes.R; 15 | import com.redcoracle.episodes.db.EpisodesTable; 16 | import com.redcoracle.episodes.db.ShowsProvider; 17 | import com.redcoracle.episodes.db.ShowsTable; 18 | import com.redcoracle.episodes.tvdb.Client; 19 | import com.redcoracle.episodes.tvdb.Episode; 20 | import com.redcoracle.episodes.tvdb.Show; 21 | 22 | import java.util.LinkedList; 23 | import java.util.concurrent.Callable; 24 | 25 | public class AddShowTask implements Callable { 26 | private static final String TAG = "AddShowTask"; 27 | private final int tmdbId; 28 | private final String showName; 29 | private final String showLanguage; 30 | private final Context context; 31 | 32 | public AddShowTask(int tmdbId, String showName, String showLanguage) { 33 | this.tmdbId = tmdbId; 34 | this.showName = showName; 35 | this.showLanguage = showLanguage; 36 | this.context = EpisodesApplication.getInstance().getApplicationContext(); 37 | } 38 | 39 | @Override 40 | public Void call() { 41 | final Client tmdbClient = new Client(); 42 | Show show = tmdbClient.getShow(this.tmdbId, this.showLanguage, false); 43 | 44 | if (!checkAlreadyAdded(show)) { 45 | this.showMessage(this.context.getString(R.string.adding_show, showName)); 46 | show = tmdbClient.getShow(this.tmdbId, this.showLanguage, true); 47 | final int showId = insertShow(show); 48 | this.insertEpisodes(show.getEpisodes().toArray(new Episode[0]), showId); 49 | showMessage(this.context.getString(R.string.show_added, showName)); 50 | } else { 51 | showMessage(this.context.getString(R.string.show_already_added, showName)); 52 | } 53 | return null; 54 | } 55 | 56 | private boolean checkAlreadyAdded(Show show) { 57 | final String[] projection = {}; 58 | String selection = String.format("%s=?", ShowsTable.COLUMN_TMDB_ID); 59 | LinkedList selectionArgs = new LinkedList<>(); 60 | selectionArgs.add(Integer.valueOf(show.getTmdbId()).toString()); 61 | 62 | if (show.getTvdbId() > 0) { 63 | selection += String.format(" OR %s=?", ShowsTable.COLUMN_TVDB_ID); 64 | selectionArgs.add(Integer.valueOf(show.getTvdbId()).toString()); 65 | } 66 | if (show.getImdbId() != null && !show.getImdbId().equals("")) { 67 | selection += String.format(" OR %s=?", ShowsTable.COLUMN_IMDB_ID); 68 | selectionArgs.add(show.getImdbId()); 69 | } 70 | final ContentResolver resolver = this.context.getContentResolver(); 71 | final Cursor cursor = resolver.query( 72 | ShowsProvider.CONTENT_URI_SHOWS, 73 | projection, 74 | selection, 75 | selectionArgs.toArray(new String[0]), 76 | null 77 | ); 78 | final boolean existing = cursor.moveToFirst(); 79 | cursor.close(); 80 | return existing; 81 | } 82 | 83 | private int insertShow(Show show){ 84 | final ContentValues showValues = new ContentValues(); 85 | if (show.getTvdbId() != 0) { 86 | showValues.put(ShowsTable.COLUMN_TVDB_ID, show.getTvdbId()); 87 | } 88 | showValues.put(ShowsTable.COLUMN_TMDB_ID, show.getTmdbId()); 89 | showValues.put(ShowsTable.COLUMN_IMDB_ID, show.getImdbId()); 90 | showValues.put(ShowsTable.COLUMN_NAME, show.getName()); 91 | showValues.put(ShowsTable.COLUMN_LANGUAGE, show.getLanguage()); 92 | showValues.put(ShowsTable.COLUMN_OVERVIEW, show.getOverview()); 93 | if (show.getFirstAired() != null) { 94 | showValues.put(ShowsTable.COLUMN_FIRST_AIRED, show.getFirstAired().getTime() / 1000); 95 | } 96 | showValues.put(ShowsTable.COLUMN_BANNER_PATH, show.getBannerPath()); 97 | showValues.put(ShowsTable.COLUMN_FANART_PATH, show.getFanartPath()); 98 | showValues.put(ShowsTable.COLUMN_POSTER_PATH, show.getPosterPath()); 99 | 100 | final Uri showUri = this.context.getContentResolver().insert(ShowsProvider.CONTENT_URI_SHOWS, showValues); 101 | final int showId = Integer.parseInt(showUri.getLastPathSegment()); 102 | Log.i(TAG, String.format("show %s successfully added to database as row %d. adding episodes", show.getName(), showId)); 103 | return showId; 104 | } 105 | 106 | private void insertEpisodes(Episode[] episodes, int showId) { 107 | final ContentValues[] values = new ContentValues[episodes.length]; 108 | 109 | for (int i = 0; i < episodes.length; i++) { 110 | ContentValues value = new ContentValues(); 111 | value.put(EpisodesTable.COLUMN_TVDB_ID, episodes[i].getTvdbId()); 112 | value.put(EpisodesTable.COLUMN_TMDB_ID, episodes[i].getTmdbId()); 113 | value.put(EpisodesTable.COLUMN_IMDB_ID, episodes[i].getImdbId()); 114 | value.put(EpisodesTable.COLUMN_SHOW_ID, showId); 115 | value.put(EpisodesTable.COLUMN_NAME, episodes[i].getName()); 116 | value.put(EpisodesTable.COLUMN_LANGUAGE, episodes[i].getLanguage()); 117 | value.put(EpisodesTable.COLUMN_OVERVIEW, episodes[i].getOverview()); 118 | value.put(EpisodesTable.COLUMN_EPISODE_NUMBER, episodes[i].getEpisodeNumber()); 119 | value.put(EpisodesTable.COLUMN_SEASON_NUMBER, episodes[i].getSeasonNumber()); 120 | if (episodes[i].getFirstAired() != null) { 121 | value.put(EpisodesTable.COLUMN_FIRST_AIRED, episodes[i].getFirstAired().getTime() / 1000); 122 | } 123 | values[i] = value; 124 | } 125 | 126 | for (ContentValues value : values) { 127 | this.context.getContentResolver().insert(ShowsProvider.CONTENT_URI_EPISODES, value); 128 | } 129 | } 130 | 131 | private void showMessage(String message) { 132 | Handler handler = new Handler(Looper.getMainLooper()); 133 | handler.post(() -> Toast.makeText(context, message, Toast.LENGTH_SHORT).show()); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/services/AsyncTask.java: -------------------------------------------------------------------------------- 1 | package com.redcoracle.episodes.services; 2 | 3 | import java.util.concurrent.Callable; 4 | import java.util.concurrent.Executor; 5 | import java.util.concurrent.Executors; 6 | 7 | public class AsyncTask { 8 | private final Executor executor = Executors.newSingleThreadExecutor(); 9 | 10 | public void executeAsync(Callable callable) { 11 | executor.execute(() -> { 12 | try { 13 | callable.call(); 14 | } catch (Exception e) { 15 | e.printStackTrace(); 16 | } 17 | }); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/services/BackupTask.java: -------------------------------------------------------------------------------- 1 | package com.redcoracle.episodes.services; 2 | 3 | import android.content.Context; 4 | import android.os.Environment; 5 | import android.util.Log; 6 | import android.widget.Toast; 7 | 8 | import androidx.core.content.ContextCompat; 9 | 10 | import com.redcoracle.episodes.EpisodesApplication; 11 | import com.redcoracle.episodes.R; 12 | import com.redcoracle.episodes.db.DatabaseOpenHelper; 13 | 14 | import java.io.File; 15 | import java.io.FileInputStream; 16 | import java.io.FileNotFoundException; 17 | import java.io.FileOutputStream; 18 | import java.io.IOException; 19 | import java.nio.channels.FileChannel; 20 | import java.util.concurrent.Callable; 21 | 22 | public class BackupTask implements Callable { 23 | private final static String TAG = BackupTask.class.getName(); 24 | private final Context context; 25 | private final String destinationFileName; 26 | 27 | public BackupTask(String destinationFileName) { 28 | this.destinationFileName = destinationFileName; 29 | this.context = EpisodesApplication.getInstance().getApplicationContext(); 30 | } 31 | 32 | public Void call() { 33 | Log.i(TAG, "Backing up library."); 34 | if (!isExternalStorageReadable()) { 35 | Log.i(TAG, "Storage is not readable."); 36 | return null; 37 | } 38 | final File databaseFile = this.context.getDatabasePath(DatabaseOpenHelper.getDbName()); 39 | final File destinationDirectory = new File(this.context.getExternalFilesDir(null), "episodes"); 40 | if (!destinationDirectory.mkdirs()) { 41 | Log.e(TAG, String.format("Error creating backup directory '%s'.", destinationDirectory.getPath())); 42 | } 43 | final File destinationFile = new File(destinationDirectory, this.destinationFileName); 44 | try { 45 | FileChannel src = new FileInputStream(databaseFile).getChannel(); 46 | FileChannel dest = new FileOutputStream(destinationFile).getChannel(); 47 | dest.transferFrom(src, 0, src.size()); 48 | ContextCompat.getMainExecutor(this.context).execute(() -> Toast.makeText( 49 | this.context, 50 | String.format(this.context.getString(R.string.back_up_success_message), this.destinationFileName), 51 | Toast.LENGTH_LONG 52 | ).show()); 53 | Log.i(TAG, String.format("Library backed up successfully: '%s'.", destinationFile.getPath())); 54 | src.close(); 55 | dest.close(); 56 | } catch (FileNotFoundException e) { 57 | e.printStackTrace(); 58 | } catch (IOException e) { 59 | e.printStackTrace(); 60 | } 61 | return null; 62 | } 63 | 64 | private boolean isExternalStorageReadable() { 65 | String state = Environment.getExternalStorageState(); 66 | return Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/services/DeleteShowTask.java: -------------------------------------------------------------------------------- 1 | package com.redcoracle.episodes.services; 2 | 3 | import android.content.ContentResolver; 4 | import android.content.Context; 5 | import android.net.Uri; 6 | import android.util.Log; 7 | 8 | import com.redcoracle.episodes.EpisodesApplication; 9 | import com.redcoracle.episodes.db.EpisodesTable; 10 | import com.redcoracle.episodes.db.ShowsProvider; 11 | 12 | import java.util.concurrent.Callable; 13 | 14 | public class DeleteShowTask implements Callable { 15 | private static final String TAG = DeleteShowTask.class.getName(); 16 | private int showId; 17 | private Context context; 18 | 19 | public DeleteShowTask(int showId) { 20 | this.showId = showId; 21 | this.context = EpisodesApplication.getInstance().getApplicationContext(); 22 | } 23 | 24 | @Override 25 | public Void call() { 26 | final ContentResolver resolver = this.context.getContentResolver(); 27 | final String selection = String.format("%s=?", EpisodesTable.COLUMN_SHOW_ID); 28 | final String[] selectionArgs = {String.valueOf(this.showId)}; 29 | int episodes = resolver.delete(ShowsProvider.CONTENT_URI_EPISODES, selection, selectionArgs); 30 | Log.d(TAG, String.format("Deleted %s episodes", episodes)); 31 | resolver.delete(Uri.withAppendedPath(ShowsProvider.CONTENT_URI_SHOWS, String.valueOf(showId)), null, null); 32 | return null; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/services/RefreshAllShowsTask.java: -------------------------------------------------------------------------------- 1 | package com.redcoracle.episodes.services; 2 | 3 | import android.content.ContentResolver; 4 | import android.content.Context; 5 | import android.database.Cursor; 6 | import android.net.Uri; 7 | 8 | import androidx.core.app.NotificationCompat; 9 | import androidx.core.app.NotificationManagerCompat; 10 | 11 | import com.redcoracle.episodes.EpisodesApplication; 12 | import com.redcoracle.episodes.R; 13 | import com.redcoracle.episodes.db.ShowsProvider; 14 | import com.redcoracle.episodes.db.ShowsTable; 15 | 16 | import java.util.concurrent.Callable; 17 | 18 | import static com.redcoracle.episodes.RefreshShowUtil.refreshShow; 19 | 20 | public class RefreshAllShowsTask implements Callable { 21 | @Override 22 | public Void call() { 23 | Context context = EpisodesApplication.getInstance().getApplicationContext(); 24 | ContentResolver resolver = context.getContentResolver(); 25 | final Uri showUri = ShowsProvider.CONTENT_URI_SHOWS; 26 | final String[] projection = { 27 | ShowsTable.COLUMN_ID, 28 | ShowsTable.COLUMN_NAME 29 | }; 30 | final String sort = ShowsTable.COLUMN_NAME + " ASC"; 31 | final Cursor cursor = resolver.query(showUri, projection, null, null, sort); 32 | final int idColumnIndex = cursor.getColumnIndex(ShowsTable.COLUMN_ID); 33 | final int nameColumnIndex = cursor.getColumnIndex(ShowsTable.COLUMN_NAME); 34 | final int total = cursor.getCount(); 35 | 36 | NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); 37 | NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, "episodes_channel_id"); 38 | notificationBuilder 39 | .setContentTitle("Refreshing Shows") 40 | .setSmallIcon(R.drawable.ic_show_starred) 41 | .setPriority(NotificationCompat.PRIORITY_DEFAULT); 42 | int current = 0; 43 | notificationBuilder.setProgress(total, current, false); 44 | notificationManager.notify(0, notificationBuilder.build()); 45 | 46 | int showId; 47 | String showName; 48 | cursor.moveToFirst(); 49 | do { 50 | showId = cursor.getInt(idColumnIndex); 51 | showName = cursor.getString(nameColumnIndex); 52 | notificationBuilder.setContentText(showName); 53 | notificationBuilder.setProgress(total, current, false); 54 | notificationManager.notify(0, notificationBuilder.build()); 55 | refreshShow(showId, resolver); 56 | current += 1; 57 | } while (cursor.moveToNext()); 58 | cursor.close(); 59 | notificationBuilder.setContentText("Refresh complete!").setProgress(0, 0, false); 60 | notificationManager.notify(0, notificationBuilder.build()); 61 | return null; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/services/RefreshShowService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2014 Jamie Nicol 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.redcoracle.episodes.services; 19 | 20 | import android.app.IntentService; 21 | import android.content.Intent; 22 | 23 | import com.redcoracle.episodes.RefreshShowUtil; 24 | 25 | public class RefreshShowService extends IntentService 26 | { 27 | public RefreshShowService() { 28 | super(RefreshShowService.class.getName()); 29 | } 30 | 31 | @Override 32 | protected void onHandleIntent(Intent intent) { 33 | final int showId = intent.getIntExtra("showId", 0); 34 | 35 | RefreshShowUtil.refreshShow(showId, getContentResolver()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/services/RefreshShowTask.java: -------------------------------------------------------------------------------- 1 | package com.redcoracle.episodes.services; 2 | 3 | import android.content.Context; 4 | 5 | import com.redcoracle.episodes.EpisodesApplication; 6 | import com.redcoracle.episodes.RefreshShowUtil; 7 | 8 | import java.util.concurrent.Callable; 9 | 10 | public class RefreshShowTask implements Callable { 11 | private static final String TAG = DeleteShowTask.class.getName(); 12 | private int showId; 13 | private Context context; 14 | 15 | public RefreshShowTask(int showId) { 16 | this.showId = showId; 17 | this.context = EpisodesApplication.getInstance().getApplicationContext(); 18 | } 19 | 20 | @Override 21 | public Void call() { 22 | RefreshShowUtil.refreshShow(showId, this.context.getContentResolver()); 23 | return null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/services/RestoreTask.java: -------------------------------------------------------------------------------- 1 | package com.redcoracle.episodes.services; 2 | 3 | import android.content.Context; 4 | import android.os.Environment; 5 | import android.util.Log; 6 | import android.widget.Toast; 7 | 8 | import androidx.core.content.ContextCompat; 9 | 10 | import com.bumptech.glide.Glide; 11 | import com.redcoracle.episodes.EpisodesApplication; 12 | import com.redcoracle.episodes.R; 13 | import com.redcoracle.episodes.db.DatabaseOpenHelper; 14 | import com.redcoracle.episodes.db.ShowsProvider; 15 | 16 | import java.io.File; 17 | import java.io.FileInputStream; 18 | import java.io.FileOutputStream; 19 | import java.io.IOException; 20 | import java.nio.channels.FileChannel; 21 | import java.util.concurrent.Callable; 22 | 23 | public class RestoreTask implements Callable { 24 | private final static String TAG = RestoreTask.class.getName(); 25 | private final Context context; 26 | private final String filename; 27 | 28 | 29 | public RestoreTask(String filename) { 30 | this.context = EpisodesApplication.getInstance().getApplicationContext(); 31 | this.filename = filename; 32 | } 33 | 34 | public Void call() { 35 | if (!isExternalStorageWritable()) { 36 | return null; 37 | } 38 | final File backupFile = new File(this.filename); 39 | final File databaseFile = this.context.getDatabasePath(DatabaseOpenHelper.getDbName()); 40 | try { 41 | FileChannel src = new FileInputStream(backupFile).getChannel(); 42 | FileChannel dest = new FileOutputStream(databaseFile).getChannel(); 43 | dest.transferFrom(src, 0, src.size()); 44 | Glide.get(this.context).clearDiskCache(); 45 | ContextCompat.getMainExecutor(this.context).execute(() -> Toast.makeText( 46 | this.context, 47 | this.context.getString(R.string.restore_success_message), 48 | Toast.LENGTH_LONG 49 | ).show()); 50 | Log.i(TAG, "Library restored successfully."); 51 | } catch (IOException e) { 52 | Log.e(TAG, String.format("Error restoring library: %s", e.toString())); 53 | } finally { 54 | ShowsProvider.reloadDatabase(this.context); 55 | } 56 | return null; 57 | } 58 | 59 | private boolean isExternalStorageWritable() { 60 | String state = Environment.getExternalStorageState(); 61 | return Environment.MEDIA_MOUNTED.equals(state); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/tvdb/Episode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Jamie Nicol 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.redcoracle.episodes.tvdb; 19 | 20 | import java.util.Date; 21 | 22 | public class Episode { 23 | private int id; 24 | private Integer tvdbId; 25 | private Integer tmdbId; 26 | private String imdbId; 27 | private String name; 28 | private String language; 29 | private String overview; 30 | private int episodeNumber; 31 | private int seasonNumber; 32 | private Date firstAired; 33 | 34 | Episode() { 35 | } 36 | 37 | public String identifier() { 38 | return String.format("%s-%s", this.seasonNumber, this.episodeNumber); 39 | } 40 | 41 | public int getId() { 42 | return id; 43 | } 44 | 45 | public void setId(int id) { 46 | this.id = id; 47 | } 48 | 49 | public Integer getTvdbId() { 50 | return this.tvdbId; 51 | } 52 | 53 | public void setTvdbId(Integer id) { 54 | this.tvdbId = id; 55 | } 56 | 57 | public int getTmdbId() { 58 | return this.tmdbId; 59 | } 60 | 61 | public void setTmdbId(int id) { 62 | this.tmdbId = id; 63 | } 64 | 65 | public String getImdbId() { 66 | return this.imdbId; 67 | } 68 | 69 | public void setImdbId(String id) { 70 | this.imdbId = id; 71 | } 72 | 73 | public String getName() { 74 | return name; 75 | } 76 | 77 | public void setName(String name) { 78 | this.name = name; 79 | } 80 | 81 | public String getLanguage() { 82 | return language; 83 | } 84 | 85 | public void setLanguage(String language) { 86 | this.language = language; 87 | } 88 | 89 | public String getOverview() { 90 | return overview; 91 | } 92 | 93 | public void setOverview(String overview) { 94 | this.overview = overview; 95 | } 96 | 97 | public int getEpisodeNumber() { 98 | return episodeNumber; 99 | } 100 | 101 | void setEpisodeNumber(int episodeNumber) { 102 | this.episodeNumber = episodeNumber; 103 | } 104 | 105 | public int getSeasonNumber() { 106 | return seasonNumber; 107 | } 108 | 109 | void setSeasonNumber(int seasonNumber) { 110 | this.seasonNumber = seasonNumber; 111 | } 112 | 113 | public Date getFirstAired() { 114 | return firstAired; 115 | } 116 | 117 | void setFirstAired(Date firstAired) { 118 | this.firstAired = firstAired; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/tvdb/GetEpisodesParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012-2014 Jamie Nicol 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.redcoracle.episodes.tvdb; 19 | 20 | import android.util.Log; 21 | 22 | import com.uwetrottmann.tmdb2.entities.TvEpisode; 23 | 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | class GetEpisodesParser { 28 | private static final String TAG = GetEpisodesParser.class.getName(); 29 | 30 | ArrayList parse(List tmdbEpisodes) { 31 | try { 32 | ArrayList episodes = new ArrayList<>(tmdbEpisodes.size()); 33 | for (TvEpisode episode : tmdbEpisodes) { 34 | Episode e = new Episode(); 35 | e.setId(episode.id); 36 | e.setTmdbId(episode.id); 37 | if (episode.external_ids != null) { 38 | if (episode.external_ids.tvdb_id != null) { 39 | e.setTvdbId(episode.external_ids.tvdb_id); 40 | } 41 | if (episode.external_ids.imdb_id != null) { 42 | e.setImdbId(episode.external_ids.imdb_id); 43 | } 44 | } 45 | e.setName(episode.name != null ? episode.name : ""); 46 | e.setOverview(episode.overview); 47 | e.setSeasonNumber(episode.season_number); 48 | e.setEpisodeNumber(episode.episode_number); 49 | e.setFirstAired(episode.air_date); 50 | episodes.add(e); 51 | } 52 | return episodes; 53 | } catch (Exception ex) { 54 | Log.w(TAG, ex); 55 | return null; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/tvdb/GetShowParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012-2014 Jamie Nicol 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.redcoracle.episodes.tvdb; 19 | 20 | import android.util.Log; 21 | 22 | import com.uwetrottmann.tmdb2.entities.TvShow; 23 | 24 | class GetShowParser { 25 | private static final String TAG = "GetShowParser"; 26 | 27 | Show parse(TvShow series, String language) { 28 | Show show; 29 | try { 30 | show = new Show(); 31 | if (series.id != null) { 32 | show.setId(series.id); 33 | show.setTmdbId(series.id); 34 | } else { 35 | Log.w(TAG, String.format("Show does not have an ID: %s", series.name)); 36 | return null; 37 | } 38 | if (series.external_ids != null) { 39 | if (series.external_ids.tvdb_id != null) { 40 | show.setTvdbId(series.external_ids.tvdb_id); 41 | } 42 | if (series.external_ids.imdb_id != null) { 43 | show.setImdbId(series.external_ids.imdb_id); 44 | } 45 | } 46 | show.setName(series.name); 47 | show.setLanguage(language); 48 | show.setOverview(series.overview); 49 | show.setFirstAired(series.first_air_date); 50 | show.setBannerPath(series.backdrop_path); 51 | show.setPosterPath(series.poster_path); 52 | } catch (Exception e) { 53 | Log.w(TAG, e); 54 | return null; 55 | } 56 | return show; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/tvdb/SearchShowsParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Jamie Nicol 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.redcoracle.episodes.tvdb; 19 | 20 | import android.util.Log; 21 | 22 | import com.uwetrottmann.tmdb2.entities.BaseTvShow; 23 | import com.uwetrottmann.tmdb2.entities.TvShowResultsPage; 24 | 25 | import java.util.LinkedList; 26 | import java.util.List; 27 | 28 | class SearchShowsParser { 29 | private static final String TAG = SearchShowsParser.class.getName(); 30 | 31 | private List parsed; 32 | 33 | List parse(TvShowResultsPage results, String language) { 34 | try { 35 | List series = results.results; 36 | parsed = new LinkedList<>(); 37 | for(BaseTvShow s : series) { 38 | Show show = new Show(); 39 | show.setId(s.id); 40 | show.setTmdbId(s.id); 41 | show.setName(s.name); 42 | show.setLanguage(language); 43 | show.setOverview(s.overview); 44 | show.setFirstAired(s.first_air_date); 45 | parsed.add(show); 46 | } 47 | } catch (Exception e) { 48 | Log.w(TAG, e); 49 | } 50 | return parsed; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/tvdb/Show.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012-2014 Jamie Nicol 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.redcoracle.episodes.tvdb; 19 | 20 | import java.util.Date; 21 | import java.util.List; 22 | 23 | public class Show { 24 | private int id; 25 | private int tvdbId; 26 | private int tmdbId; 27 | private String imdbId; 28 | private String name; 29 | private String language; 30 | private String overview; 31 | private Date firstAired; 32 | private String bannerPath; 33 | private String fanartPath; 34 | private String posterPath; 35 | private List episodes; 36 | 37 | public Show() { 38 | name = ""; 39 | language = ""; 40 | overview = ""; 41 | firstAired = null; 42 | bannerPath = ""; 43 | fanartPath = ""; 44 | posterPath = ""; 45 | episodes = null; 46 | } 47 | 48 | public int getId() { 49 | return id; 50 | } 51 | 52 | public void setId(int id) { 53 | this.id = id; 54 | } 55 | 56 | public int getTvdbId() { 57 | return tvdbId; 58 | } 59 | 60 | public void setTvdbId(int tvdbId) { 61 | this.tvdbId = tvdbId; 62 | } 63 | 64 | public int getTmdbId() { 65 | return this.tmdbId; 66 | } 67 | 68 | public void setTmdbId(int tmdbId) { 69 | this.tmdbId = tmdbId; 70 | } 71 | 72 | public String getImdbId() { 73 | return this.imdbId; 74 | } 75 | 76 | public void setImdbId(String imdbId) { 77 | this.imdbId = imdbId; 78 | } 79 | 80 | public String getName() { 81 | return name; 82 | } 83 | 84 | public void setName(String name) { 85 | this.name = name; 86 | } 87 | 88 | public String getLanguage() { 89 | return language; 90 | } 91 | 92 | void setLanguage(String language) { 93 | this.language = language; 94 | } 95 | 96 | public String getOverview() { 97 | return overview; 98 | } 99 | 100 | public void setOverview(String overview) { 101 | this.overview = overview; 102 | } 103 | 104 | public Date getFirstAired() { 105 | return firstAired; 106 | } 107 | 108 | void setFirstAired(Date firstAired) { 109 | this.firstAired = firstAired; 110 | } 111 | 112 | public String getBannerPath() { 113 | return bannerPath; 114 | } 115 | 116 | void setBannerPath(String bannerPath) { 117 | this.bannerPath = bannerPath; 118 | } 119 | 120 | public String getFanartPath() { 121 | return fanartPath; 122 | } 123 | 124 | void setFanartPath(String fanartPath) { 125 | this.fanartPath = fanartPath; 126 | } 127 | 128 | public String getPosterPath() { 129 | return posterPath; 130 | } 131 | 132 | public void setPosterPath(String posterPath) { 133 | this.posterPath = posterPath; 134 | } 135 | 136 | public List getEpisodes() { 137 | return episodes; 138 | } 139 | 140 | public void setEpisodes(List episodes) { 141 | this.episodes = episodes; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/widget/ObservableScrollView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Jamie Nicol 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.redcoracle.episodes.widget; 19 | 20 | import android.content.Context; 21 | import android.util.AttributeSet; 22 | import android.widget.ScrollView; 23 | 24 | public class ObservableScrollView 25 | extends ScrollView 26 | { 27 | public static interface OnScrollChangedListener { 28 | public void onScrollChanged(int l, int t, int oldl, int oldt); 29 | } 30 | 31 | private OnScrollChangedListener listener; 32 | 33 | public ObservableScrollView(Context context, AttributeSet attrs) { 34 | super(context, attrs); 35 | } 36 | 37 | @Override 38 | protected void onScrollChanged(int l, int t, int oldl, int oldt) { 39 | super.onScrollChanged(l, t, oldl, oldt); 40 | 41 | if (listener != null) { 42 | listener.onScrollChanged(l, t, oldl, oldt); 43 | } 44 | } 45 | 46 | public void setOnScrollChangedListener(OnScrollChangedListener listener) { 47 | this.listener = listener; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/widget/WrapContentListView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Jamie Nicol 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.redcoracle.episodes.widget; 19 | 20 | import android.content.Context; 21 | import android.util.AttributeSet; 22 | import android.view.View; 23 | import android.widget.ListAdapter; 24 | import android.widget.ListView; 25 | 26 | public class WrapContentListView 27 | extends ListView 28 | { 29 | public WrapContentListView(Context context, AttributeSet attrs) { 30 | super(context, attrs); 31 | } 32 | 33 | @Override 34 | public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 35 | int height = 0; 36 | 37 | final ListAdapter adapter = getAdapter(); 38 | if (adapter != null) { 39 | for (int i = 0; i < adapter.getCount(); i++) { 40 | View item = adapter.getView(i, null, this); 41 | 42 | if (item != null) { 43 | final int unspecified = 44 | MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 45 | item.measure(widthMeasureSpec, unspecified); 46 | 47 | height += item.getMeasuredHeight(); 48 | height += getDividerHeight(); 49 | } 50 | } 51 | } 52 | 53 | heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, 54 | MeasureSpec.EXACTLY); 55 | 56 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/com/redcoracle/episodes/widget/WrapContentViewPager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Jamie Nicol 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.redcoracle.episodes.widget; 19 | 20 | import android.content.Context; 21 | import android.util.AttributeSet; 22 | import android.view.View; 23 | 24 | import androidx.fragment.app.Fragment; 25 | import androidx.fragment.app.FragmentPagerAdapter; 26 | import androidx.viewpager.widget.ViewPager; 27 | 28 | public class WrapContentViewPager 29 | extends ViewPager 30 | { 31 | public WrapContentViewPager(Context context, AttributeSet attrs) { 32 | super(context, attrs); 33 | } 34 | 35 | @Override 36 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 37 | final FragmentPagerAdapter adapter = (FragmentPagerAdapter)getAdapter(); 38 | final Fragment fragment = 39 | (Fragment)getAdapter().instantiateItem(this, getCurrentItem()); 40 | 41 | if (fragment != null) { 42 | final View view = fragment.getView(); 43 | 44 | if (view != null) { 45 | view.measure(widthMeasureSpec, 46 | MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 47 | 48 | final int height = Math.max(view.getMeasuredHeight(), 49 | getSuggestedMinimumHeight()); 50 | heightMeasureSpec = 51 | MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); 52 | } 53 | } 54 | 55 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 56 | } 57 | 58 | public int getMinimumHeightPlease() { 59 | // getMinimumHeight() is only API level >= 16, and 60 | // getSuggestedMinimumHeight() is protected. 61 | return getSuggestedMinimumHeight(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/res/color/tab_text.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_menu_add_show.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/drawable-hdpi/ic_menu_add_show.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_menu_filter_shows_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/drawable-hdpi/ic_menu_filter_shows_list.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/shows_list_gradient.xml: -------------------------------------------------------------------------------- 1 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_menu_add_show.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/drawable-mdpi/ic_menu_add_show.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_menu_filter_shows_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/drawable-mdpi/ic_menu_filter_shows_list.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/blank_show_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/drawable-nodpi/blank_show_banner.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_menu_add_show.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/drawable-xhdpi/ic_menu_add_show.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_menu_filter_shows_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/drawable-xhdpi/ic_menu_filter_shows_list.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_menu_add_show.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/drawable-xxhdpi/ic_menu_add_show.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_menu_filter_shows_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/drawable-xxhdpi/ic_menu_filter_shows_list.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/archived_toggle.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/gradient.xml: -------------------------------------------------------------------------------- 1 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_show_archived.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_show_starred.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_show_unarchived.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_show_unstarred.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/starred_toggle.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/watched_progress.xml: -------------------------------------------------------------------------------- 1 | 3 | 5 | 6 | 8 | 9 | 10 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/about_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 14 | 20 | 28 | 34 | 40 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/res/layout/add_show_preview_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/add_show_preview_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 17 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/add_show_search_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | 26 | 27 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/layout/add_show_search_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/add_show_search_results_list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/episode_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/episode_details_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 12 | 22 | 30 | 39 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/res/layout/episodes_list_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/episodes_list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 15 | 24 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/layout/main_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/season_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/seasons_list_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/layout/seasons_list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 23 | 27 | 32 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/res/layout/settings_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/show_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 22 | 23 | 29 | 30 | 35 | 36 | 42 | 43 | 44 | 54 | 55 | 62 | 63 | 64 | 65 | 70 | 71 | -------------------------------------------------------------------------------- /app/src/main/res/layout/show_details_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 10 | 16 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/show_notes_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/layout/shows_list_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/layout/shows_list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 14 | 15 | 20 | 21 | 36 | 37 | 47 | 58 | 62 | 67 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/res/menu/add_show_preview_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/menu/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 11 | 16 | 21 | 26 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/menu/season_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/menu/show_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 10 | 15 | 19 | 23 | 27 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/menu/shows_list_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 9 | 10 | 12 | 15 | 18 | 21 | 24 | 27 | 28 | 29 | 30 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/mipmap-hdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/mipmap-mdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/values-bg/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Филтърът показва 4 | Звезден 5 | Относно 6 | Изтриване на шоуто 7 | Епизоди 8 | Премахване на звезди 9 | Звездна спектакция 10 | Настройки 11 | Възстановяване от архивиране 12 | Обновяване на шоуто 13 | Опресняване на всички предавания 14 | Маркирай показването като гледано 15 | Маркирай като неподредено 16 | Марк сезон, както беше наблюдаван 17 | Маркиране на сезон като необгледан 18 | Предстоящи 19 | Ход 20 | Архивирани 21 | Всички 22 | Добавяне на показване към библиотеката 23 | Добавяне на ново шоу към библиотеката 24 | Търсене на ново шоу 25 | За 26 | Първо излъчено: %s 27 | Не може да се добави %s в библиотеката 28 | Архивирането е завършено: \'%s\" 29 | Не може да се архивира библиотеката 30 | Версия %s (Къмит на %s) 31 | Интерфейс 32 | Серия за автоматично обновяване 33 | Уведомяване при синхронизиране показва 34 | Показване на синхронизирането 35 | %1$d от %2$d епизоди, наблюдавани 36 | Гледани 37 | (+ %d предстоящи) 38 | Преглед 39 | Бележки 40 | Следващата 41 | %s вече в библиотека 42 | %s добавен към библиотеката 43 | Настройки 44 | Специални 45 | Сезон %d 46 | "%1$02dx%2$02d - " 47 | Библиотеката е възстановена 48 | Не може да се възстанови библиотеката 49 | Възстановяване от архив 50 | Не са намерени архиви в папка \'%s\'. 51 | Показва най-новия сезон 52 | Обратен ред на сортиране 53 | Език 54 | Само при неомерени връзки 55 | Автоматично обновяване 56 | Интервал 57 | Премахни показването от архива 58 | Архивно шоу 59 | Епизодите са софтуер за GPLv3+. Моля, пиши bugs, писане на код, превеждайте или направете всичко друго, което можете да направите, за да помогнете! 60 | Следете кои епизоди сте гледали от любимите си предавания 61 | Архивиране на библиотека 62 | Добавяне %s към библиотеката 63 | -------------------------------------------------------------------------------- /app/src/main/res/values-cs/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Sledujte, které epizody jste sledovali z vašich oblíbených pořadů 4 | Lägg till %s i biblioteket 5 | -------------------------------------------------------------------------------- /app/src/main/res/values-de/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Verfolge, welche Episoden Deiner Lieblingssendungen Du gesehen hast 4 | Benutzeroberfläche 5 | Serien automatisch aktualisieren 6 | %1$d von %2$d Folgen gesehen 7 | Gesehen 8 | (+ %d angekündigt) 9 | Übersicht 10 | Nächste 11 | Folgen 12 | %s bereits in der Bibliothek 13 | %s zur Bibliothek hinzugefügt 14 | Einstellungen 15 | Specials 16 | Staffel %d 17 | "%1$02dx%2$02d - " 18 | Bibliothek wiederhergestellt 19 | Bibliothek konnte nicht wiederhergestellt werden 20 | Aus einer Sicherung wiederherstellen 21 | Keine Datensicherungen im Ordner „%s“ gefunden 22 | Neueste Staffel zuerst 23 | Reihenfolge umkehren 24 | Sprache 25 | Nur bei nicht gemessenen Verbindungen 26 | Automatisch aktualisieren 27 | Intervall 28 | Serie aus Archiv entfernen 29 | Serie archivieren 30 | Serie aus Favoriten entfernen 31 | Serie favorisieren 32 | Einstellungen 33 | Datensicherung wiederherstellen 34 | Serie aktualisieren 35 | Serien aktualisieren 36 | Folge als gesehen markieren 37 | Folge als ungesehen markieren 38 | Staffel als gesehen markieren 39 | Staffel als ungesehen markieren 40 | Angekündigte 41 | Angefangene 42 | Archivierte 43 | Favoriten 44 | Alle 45 | Serien filtern 46 | Serie löschen 47 | Datensicherung erstellen 48 | Serie hinzufügen 49 | Serie hinzufügen 50 | Neue Serie suchen 51 | Über 52 | Erstausstrahlung: %s 53 | %s konnte nicht zur Bibliothek hinzugefügt werden 54 | Datensicherung fertiggestellt: „%s“ 55 | Datensicherung der Bibliothek konnte nicht erstellt werden 56 | %s wird zur Bibliothek hinzugefügt 57 | Über 58 | Episodes ist freie Software unter GPLv3+. Hilf gern mit, indem Du Fehler meldest, Code oder Übersetzungen schreibst! 59 | Synchronisierung anzeigen 60 | Benachrichtigung beim Synchronisieren von Sendungen 61 | Anmerkungen 62 | Version %s (Commit %s) 63 | -------------------------------------------------------------------------------- /app/src/main/res/values-eo/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Fasado 4 | %1$d el %2$d epizodoj spektiĝis 5 | Spektita 6 | Superrigardo 7 | Notoj 8 | Epizodoj 9 | %s jam en biblioteko 10 | %s aldonita al biblioteko 11 | Agordoj 12 | Sezono %d 13 | Biblioteko restaŭriĝis 14 | Ne povis restaŭri bibliotekon 15 | Restaŭri savkopion 16 | Neniu savkopio troviĝis en la dosierujo \'%s\'. 17 | Lingvo 18 | Elarkivigi spektaĵon 19 | Enarkivigi spektaĵon 20 | Malmarki kiel plej ŝatatan 21 | Marki kiel plej ŝatatan 22 | Agordoj 23 | Marki spektaĵon kiel spektitan 24 | Marki spektaĵon kiel nespektitan 25 | Marki sezonon kiel spektita 26 | Marki sezonon kiel nespektita 27 | En arkivo 28 | Plej ŝatataj 29 | Ĉiuj 30 | Forigi spektaĵon 31 | Savkopii bibliotekon 32 | Aldoni spektaĵon al biblioteko 33 | Aldoni novan spektaĵon al biblioteko 34 | Serĉi novan spektaĵon 35 | Pri 36 | Ne povis aldoni %s al biblioteko 37 | Savkopio kompleta: \'%s\' 38 | Ne povis savkopii bibliotekon 39 | Aldonante %s al biblioteko 40 | Pri 41 | Filtri spektaĵojn 42 | Versio %s (Enmeto %s) 43 | Specialaĵoj 44 | "%1$02d×%2$02d - " 45 | Intervalo 46 | Prezentata 47 | Prezentota 48 | (+ %d prezentota(j)) 49 | Unua Prezentado: %s 50 | Sciigo pri sinkronigo de spektaĵoj 51 | Reŝargi spektaĵon 52 | Reŝargi ĉiujn spektaĵojn 53 | Episodes estas kopilasita libera programo sub la permesiloj GPLv3+. Bonvolu raporti cimojn, skribi kodojn, traduki aŭ helpi ĉiel ajn! 54 | Marku la spektitajn epizodojn de viaj plej ŝatataj spektaĵoj 55 | Aŭtomate reŝargi spektaĵon 56 | Sekva 57 | Sciigi pri sinkronigo 58 | Nur per nemezurataj retkonektoj 59 | Aŭtomate reŝargata 60 | Montri unue la plej novan sezonon 61 | Mala ordo 62 | Restaŭri savkopion 63 | -------------------------------------------------------------------------------- /app/src/main/res/values-es/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | (+ %d próximamente) 4 | Descripción 5 | Sólo en conexiones no medidas 6 | Próximamente 7 | En emisión 8 | Emitida por primera vez: %s 9 | Episodes es software libre copyleft bajo licencia GPLv3+. ¡Por favor registra bugs, escribe código, traduce o haz cualquier cosa que puedas para ayudar! 10 | Mantén un registro de qué episodios de tus series favoritas has visto 11 | Versión %s (Commit %s) 12 | Interfaz 13 | Actualizar series automáticamente 14 | Notificación cuando se sincronizan series 15 | Sincronizar serie 16 | %1$d de %2$d episodios vistos 17 | Visto 18 | Notas 19 | Siguiente 20 | Episodios 21 | %s ya está en la biblioteca 22 | %s añadida a la biblioteca 23 | Ajustes 24 | Especiales 25 | Temporada %d 26 | "%1$02dx%2$02d - " 27 | Biblioteca restaurada 28 | No se ha podido restaurar la biblioteca 29 | Restaurar desde copia de seguridad 30 | No se han encontrado copias de seguridad en la carpeta \'%s\'. 31 | Muestra primero la temporada más nueva 32 | Orden inverso 33 | Lenguaje 34 | Actualización automática activada 35 | Intervalo 36 | Quitar serie del archivo 37 | Archivar serie 38 | No destacar serie 39 | Destacar serie 40 | Ajustes 41 | Restaurar desde copia de seguridad 42 | Actualizar serie 43 | Actualizar todas las series 44 | Marcar serie como vista 45 | Marcar serie como no vista 46 | Marcar temporada como vista 47 | Marcar temporada como no vista 48 | Archivadas 49 | Destacadas 50 | Todas 51 | Filtrar series 52 | Borrar serie 53 | Realizar copia de seguridad de la biblioteca 54 | Añadir serie a la biblioteca 55 | Añadir nueva serie a la biblioteca 56 | Buscar una nueva serie 57 | Acerca de 58 | No se ha podido añadir %s a la biblioteca 59 | Copia de seguridad completada: \'%s\' 60 | No se ha podido realizar la copia de seguridad de la biblioteca 61 | Añadiendo %s a la biblioteca 62 | Acerca de 63 | -------------------------------------------------------------------------------- /app/src/main/res/values-eu/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Bertsioa %s (Egin %s) 4 | Interfazea 5 | Automatikoki freskatu serieak 6 | Ikuskizunak sinkronizatzean jakinarazpena 7 | Erakutsi sinkronizazioa 8 | %1$d of %2$d ikusitako pasarteak 9 | Ikusita 10 | (+ %d datozenak) 11 | Ikuspegi orokorra 12 | Oharrak 13 | Hurrengoa 14 | Episodes 15 | %s dagoeneko liburutegian 16 | %s liburutegian gehitu da 17 | Ezarpenak 18 | Bereziak 19 | Denboraldia %d 20 | "%1$02dx%2$02d - " 21 | Liburutegia zaharberritu da 22 | Ezin izan da liburutegia leheneratu 23 | Leheneratu segurtasun kopiatik 24 | Ez da babeskopiarik aurkitu \'%s\' karpetan. 25 | Denboraldi berriena erakusten du lehenengo 26 | Alderantziz ordenatzeko ordena 27 | Hizkuntza 28 | Meditu gabeko konexioetan soilik 29 | Freskatze automatikoa aktibatuta 30 | Tartea 31 | Kendu ikuskizuna artxibotik 32 | Artxibo ikuskizuna 33 | Erakutsi izarra 34 | Izar ikuskizuna 35 | Ezarpenak 36 | Leheneratu segurtasun kopiatik 37 | Freskatu ikuskizuna 38 | Freskatu ikuskizun guztiak 39 | Markatu ikuskizuna ikusi bezala 40 | Markatu ikuskizuna ikusi gabeko moduan 41 | Markatu denboraldia ikusitako moduan 42 | Markatu denboraldia ikusi gabeko moduan 43 | Datozenak 44 | Martxan 45 | Artxibatuta 46 | Izarrak 47 | Guztiak 48 | Iragazkien ikuskizunak 49 | Ezabatu ikuskizuna 50 | Egin liburutegiaren babeskopia 51 | Gehitu ikuskizuna liburutegian 52 | Gehitu ikuskizun berria liburutegian 53 | Bilatu ikuskizun berria 54 | Buruz 55 | Lehenengo igorpena: %s 56 | Ezin izan da %s gehitu liburutegian 57 | Babeskopia osatua: \'%s\' 58 | Ezin izan da liburutegiaren segurtasun kopia egin 59 | liburutegian %s gehitzen 60 | Buruz 61 | Episodes copylefted libre software lizentziadun GPLv3 + da. Mesedez, bidali akatsak, idatzi kodea, itzuli edo egin ezazu laguntzeko duzun guztia! 62 | Jarrai itzazu zure saio gogokoenen atalak ikusi dituzunak 63 | -------------------------------------------------------------------------------- /app/src/main/res/values-fr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | À propos 4 | Première diffusion : %s 5 | À propos 6 | Ajout de %s à la bibliothèque 7 | Episodes est un logiciel libre sous licence GPLv3+. Vous pouvez signaler des erreurs, écrire du code, traduire ou faire quoi que ce soit d\'autre pour contribuer ! 8 | Gardez une trace des épisodes que vous avez regardés dans vos séries préférées 9 | Ajouter la série à la bibliothèque 10 | Ajouter une nouvelle série à la bibliothèque 11 | Rechercher une nouvelle série 12 | Impossible d\'ajouter %s à la bibliothèque 13 | Sauvegarde terminée : « %s » 14 | Impossible de sauvegarder la bibliothèque 15 | Interface 16 | Actualiser automatiquement les séries 17 | Notification lors de la synchronisation des séries 18 | Synchronisation de la série 19 | %1$d épisode sur %2$d regardé 20 | Regardé 21 | (+ %d à venir) 22 | Aperçu 23 | Notes 24 | Suivant 25 | Épisodes 26 | %s déjà dans la bibliothèque 27 | %s ajouté à la bibliothèque 28 | Paramètres 29 | Spécial 30 | Saison %d 31 | "%1$02dx%2$02d - " 32 | Bibliothèque restaurée 33 | Impossible de restaurer la bibliothèque 34 | Restaurer à partir d\'une sauvegarde 35 | Aucune sauvegarde trouvée dans le dossier « %s ». 36 | Affiche la dernière saison en premier 37 | Inverser l\'ordre de tri 38 | Langue 39 | Seulement sur les connexions non limitées 40 | Actualisation auto activée 41 | Intervale 42 | Retirer la série des archives 43 | Archiver la série 44 | Retirer la série des favoris 45 | Ajouter la série aux favoris 46 | Paramètres 47 | Restaurer une sauvegarde 48 | Actualiser la série 49 | Actualiser toutes les séries 50 | Marquer la série comme regardée 51 | Marquer la série comme non regardée 52 | Marquer la saison comme regardée 53 | Marquer la saison comme non regardée 54 | À venir 55 | En cours 56 | Archivé 57 | Favoris 58 | Tout 59 | Filtrer les séries 60 | Supprimer la série 61 | Restaurer la bibliothèque 62 | %s verzió (Commit %s) 63 | -------------------------------------------------------------------------------- /app/src/main/res/values-hr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Prikaži sinkronizaciju 4 | Označi sezonu kao pogledanu 5 | Filtriraj emisije 6 | Odznači emisiju 7 | Obnovi iz sigurnosne kopije 8 | Sezona %d 9 | Automatsko aktualiziranje uključeno 10 | Označi emisiju 11 | Označi emisiju kao pogledanu 12 | Prvi put emitirano: %s 13 | (+ %d predstojeće) 14 | Spremi sigurnosnu kopiju biblioteke 15 | Ukloni emisjiu iz arhive 16 | Prati stanja gledanja epizoda tvojih omiljenih emisija 17 | Emisija „%s” je dodana u biblioteku 18 | Dodaj novu emisiju u biblioteku 19 | Obrni redoslijed 20 | Pogledano 21 | Epizode 22 | Obnovi iz sigurnosne kopije 23 | Aktualiziraj emisiju 24 | Predstojeće 25 | Neuspjelo spremanje sigurnosne kopije biblioteke 26 | „Epizode” je copyleft slobodni softver s licencom GPLv3+. Prijavi greške, programiraj, prevodi ili pomogni na bilo koji drugi način! 27 | Pregled 28 | Emisija „%s” se već nalazi u biblioteci 29 | Prikazuje najnoviju sezonu na prvom mjestu 30 | Arhivirane 31 | Napomene 32 | Sljedeća 33 | Postavke 34 | Neuspjelo obnavljanje biblioteke 35 | Jezik 36 | Traži novu emisiju 37 | Informacije 38 | Nadolazeće 39 | Označi emisiju kao nepogledanu 40 | Obavijesti pri sinkronizaciji emisija 41 | Aktualiziraj sve emisije 42 | Informacije 43 | Pogledanih epizoda: %1$d od %2$d 44 | Biblioteka obnovljena 45 | Postavke 46 | Označene 47 | Označi sezonu kao nepogledanu 48 | Izbriši emisiju 49 | Samo na vezama bez ograničenja 50 | Interval 51 | Arhiviraj emisiju 52 | Sučelje 53 | Emisija „%s” se dodaje u biblioteku 54 | Neuspjelo dodavanje emisije „%s” u biblioteku 55 | Sve 56 | Dodaj emisiju u biblioteku 57 | Automatsko aktualiziranje serija 58 | Specijalne 59 | Sigurnosna kopija gotova: „%s” 60 | U mapi „%s” nema sigurnosnih kopija. 61 | "%1$02d × %2$02d - " 62 | Verzija %s (Izmjena %s) 63 | -------------------------------------------------------------------------------- /app/src/main/res/values-it/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Aggiunta di %s alla libreria 4 | Informazioni 5 | Episodes è un software libero con copyleft di licenza GPLv3+. Per favore, segnalate errori, scrivete codice, traducete o fate qualsiasi altra cosa per aiutare! 6 | Tieni traccia di quali episodi hai guardato dei tuoi programmi preferiti 7 | Versione %s (Commit %s) 8 | Interfaccia 9 | Aggiornamento automatico serie 10 | Notifica quando le serie si sincronizzano 11 | Sincronizzazione della serie 12 | %1$d di %2$d episodi guardati 13 | Guardato 14 | (+ %d prossimamente) 15 | Panoramica 16 | Note 17 | Successivo 18 | Episodes 19 | %s già nella raccolta 20 | %s aggiunto alla raccolta 21 | Impostazioni 22 | Speciali 23 | Stagione %d 24 | "%1$02dx%2$02d - " 25 | Raccolta ripristinata 26 | Non è stato possibile ripristinare la raccolta 27 | Ripristina da un backup 28 | Nessun backup trovato nella cartella «%s». 29 | Visualizza prima la stagione più recente 30 | Invertisci l\'ordine di ordinamento 31 | Lingua 32 | Solo con connessioni non misurate 33 | Aggiornamento automatico abilitato 34 | Periodicità 35 | Rimuovi la serie dall\'archivio 36 | Archivia la serie 37 | Rimuovi la serie dai favoriti 38 | Aggiungi la serie ai favoriti 39 | Impostazioni 40 | Ripristino da backup 41 | Aggiorna la serie 42 | Aggiorna tutte le serie 43 | Segna la serie come guardata 44 | Segna come non guardato 45 | Segna la stagione come guardato 46 | Segna la stagione come non guardato 47 | Prossimamente 48 | In corso 49 | Archiviato 50 | Stellato 51 | Tutto 52 | Filtra le serie 53 | Elimina la serie 54 | Backup della raccolta 55 | Aggiungi la serie alla raccolta 56 | Aggiungi una nuova serie alla raccolta 57 | Cerca una nuova serie 58 | Informazioni 59 | Prima messa in onda: %s 60 | Impossibile aggiungere %s alla raccolta 61 | Backup completato: «%s» 62 | Impossibile eseguire il backup della raccolta 63 | -------------------------------------------------------------------------------- /app/src/main/res/values-nb-rNO/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Grensesnitt 4 | Auto-gjenoppfrisk serier 5 | Merknad ved synkronisering av programmer 6 | Vis synkronisering 7 | %1$d av %2$d episoder sett 8 | Sett 9 | (+ %d kommende) 10 | Oversikt 11 | Notater 12 | Neste 13 | Episoder 14 | %s allerede i bibliotek 15 | %s lagt til i bibliotek 16 | Innstillinger 17 | Sesong %d 18 | Spesialsendinger 19 | "%1$02dx%2$02d - " 20 | Bibliotek gjenopprettet 21 | Klarte ikke å gjenopprette bibliotek 22 | Gjenopprett fra sikkehetskopi 23 | Fant ingen sikkerhetskopier i «%s»-mappen. 24 | Vis nyeste sesong først 25 | Reverser sorteringsrekkefølge 26 | Språk 27 | Kun på ubegrensede tilkoblinger 28 | Auto-gjenoppfrisking påskrudd 29 | Intervall 30 | Fjern program fra arkiv 31 | Arkiver program 32 | Fjern stjernemerking 33 | Stjernemerk program 34 | Innstillinger 35 | Gjenopprett fra sikkerhetskopi 36 | Gjenoppfrisk program 37 | Gjenoppfrisk alle programmer 38 | Marker program som sett 39 | Marker program som usett 40 | Marker sesong som sett 41 | Marker sesong som usett 42 | Kommende 43 | Underveis 44 | Arkivert 45 | Stjernemerkede 46 | Alle 47 | Filtrer programmer 48 | Slett program 49 | Sikkerhetskopier bibliotek 50 | Legg til program i bibliotek 51 | Legg til nytt program i bibliotek 52 | Søk etter nytt program 53 | Om 54 | Først sendt: %s 55 | Klarte ikke å legge til %s i biblioteket 56 | Sikkerhetskopi utført: «%s» 57 | Klarte ikke å sikkerhetskopiere bibliotek 58 | Legger til %s i biblioteket 59 | Om 60 | Episodes er gemenlig fri programvare lisensiert GPLv3+. Send inn feil, skriv kode, oversett og gjør hva du kan for å hjelpe til. 61 | Hold orden på hvilke episoder du har sett av dine favoritt-programmer 62 | Versjon %s (innsendelse %s) 63 | -------------------------------------------------------------------------------- /app/src/main/res/values-ru/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Интерфейс 4 | Автообновление серий 5 | Уведомление при синхронизации шоу 6 | Показать синхронизацию 7 | %1$d из %2$d эпизодов просмотрено 8 | Просмотрено 9 | (+ %d предстоящие) 10 | Обзор 11 | Примечания 12 | Следующий 13 | Эпизоды 14 | %s уже в библиотеке 15 | %s добавлен в библиотеку 16 | Настройки 17 | Specials 18 | Сезон %d 19 | "%1$02dx%2$02d - " 20 | Библиотека восстановлена 21 | Не удалось восстановить библиотеку 22 | Восстановить из резервной копии 23 | В папке \'%s\' нет резервных копий. 24 | Отображать сначала новейший сезон 25 | Обратный порядок сортировки 26 | Язык 27 | Только на безлимитных соединениях 28 | Автоматическое обновление 29 | Интервал 30 | Удалить из избранного 31 | Удалить шоу из архива 32 | Добавить в архив 33 | Избранные 34 | Добавить в избранное 35 | Настройки 36 | Восстановить из резервной копии 37 | Обновить шоу 38 | Обновить все шоу 39 | Отметить шоу как просмотренное 40 | Отметить шоу как непросмотренное 41 | Отметить сезон как просмотренный 42 | Отметить сезон как непросмотренный 43 | Предстоящие 44 | В процессе 45 | Архивные 46 | Все 47 | Фильтр шоу 48 | Удалить шоу 49 | Резервное копирование библиотеки 50 | Добавить шоу в библиотеку 51 | Добавить новое шоу в библиотеку 52 | Поиск нового шоу 53 | О приложении 54 | Первый эфир: %s 55 | Не удалось добавить %s в библиотеку 56 | Резервное копирование завершено: \'%s\' 57 | Не удалось создать резервную копию библиотеки 58 | Добавление %s в библиотеку 59 | О приложении 60 | Episodes - это бесплатное программное обеспечение с авторским левом под лицензией GPLv3 +. Пожалуйста, сообщайте об ошибках, пишите код, переводите или делайте что-нибудь ещё, чтобы помочь! 61 | Следите за тем, какие серии любимых шоу вы смотрели 62 | Версия %s (Изменение %s) 63 | -------------------------------------------------------------------------------- /app/src/main/res/values-sv/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Version %s (åtagande %s) 4 | Gränssnitt 5 | %1$d av %2$d avsnitt sedda 6 | "%1$02dx%2$02d - " 7 | Avisering när program synkroniseras 8 | Programsynkronisering 9 | Sedda 10 | Överblick 11 | Anteckningar 12 | Nästa 13 | Avsnitt 14 | %s redan i biblioteket 15 | %s lades till i biblioteket 16 | Inställningar 17 | Säsong %d 18 | Biblioteket återställdes 19 | Kunde inte återställa bibliotek 20 | Återställ från säkerhetskopia 21 | Inga säkerhetskopior hittades i mappen \'%s\'. 22 | Visar den nyaste säsongen först 23 | Omvänd sorteringsordning 24 | Språk 25 | Endast på obegränsade anslutningar 26 | Automatisk uppdatering på 27 | Intervall 28 | Ta bort program från arkiv 29 | Arkivera program 30 | Sluta stjärnmarkera program 31 | Stjärnmarkera program 32 | Inställningar 33 | Återställ från säkerhetskopia 34 | Uppdatera program 35 | Uppdatera alla program 36 | Kommande 37 | Pågående 38 | Arkiverade 39 | Stjärnmärkta 40 | Alla 41 | Filtrera program 42 | Ta bort program 43 | Säkerhetskopiera biblioteket 44 | Lägg till program i biblioteket 45 | Lägg till program i biblioteket 46 | Sök efter nytt program 47 | Om 48 | Sändes först: %s 49 | Kunde inte lägga till %s i biblioteket 50 | Säkerhetskopieringen färdig: \'%s\' 51 | Kunde inte säkerhetskopiera biblioteket 52 | Lägger till %s i biblioteket 53 | Om 54 | (+ %d kommande) 55 | Håll koll på vilka avsnitt du har tittat på av dina favoritprogram 56 | Episodes är copylefted libre software licensierad GPLv3+. Var snäll och skicka in felrapporter, skriv kod, översätt eller gör vad du kan för att hjälpa till! 57 | Markera säsong som osedd 58 | Markera säsong som sedd 59 | Markera program som osett 60 | Markera program som sett 61 | Specialavsnitt 62 | Automatisk uppdatering av serier 63 | -------------------------------------------------------------------------------- /app/src/main/res/values-tr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | "%1$02dx%2$02d - " 4 | Sürüm %s (Kaydetme %s) 5 | Arayüz 6 | Dizileri otomatik yenile 7 | Şovları senkronize ederken bildirim 8 | Senkronizasyonu göster 9 | %2$d bölümden %1$d görüldü 10 | İzlendi 11 | (+%d yakında) 12 | Genel Bakış 13 | Notlar 14 | Sıradaki 15 | Bölümler 16 | %s zaten kitaplıkta 17 | %s kitaplığa eklendi 18 | Ayarlar 19 | Özel 20 | Sezon %d 21 | Kitaplık geri yüklendi 22 | Kitaplık geri yüklenemedi 23 | Yedekten geri yükle 24 | \'%s\' klasöründe yedek bulunamadı. 25 | Önce en yeni sezonu görüntüler 26 | Ters sıralama düzeni 27 | Dil 28 | Yalnızca ölçülmemiş bağlantılarda 29 | Otomatik yenileme açık 30 | Aralık 31 | Diziyi arşivden kaldır 32 | Diziyi arşivle 33 | Gösterinin yıldızını kaldır 34 | Gösteriyi yıldızla 35 | Ayarlar 36 | Yedekten geri yükle 37 | Diziyi yenile 38 | Tüm dizileri yenile 39 | Diziyi izlenmiş olarak işaretle 40 | Diziyi izlenmemiş olarak işaretle 41 | Sezonu izlendi olarak işaretle 42 | Sezonu izlenmemiş olarak işaretle 43 | Yakında 44 | Yolda 45 | Arşivlendi 46 | Yıldızlı 47 | Hepsi 48 | Filtrele 49 | Diziyi sil 50 | Kitaplığı yedekle 51 | Kitaplığa dizi ekle 52 | Kitaplığa yeni dizi ekle 53 | Yeni dizi ara 54 | Hakkında 55 | İlk Yayınlanma Tarihi: %s 56 | %s kitaplığa eklenemedi 57 | Yedekleme tamamlandı: \'%s\' 58 | Kitaplık yedeklenemedi 59 | %s kitaplığa ekleniyor 60 | Hakkında 61 | Episodes, GPLv3+ lisanslı copyleft özgür yazılımdır. Lütfen hataları bildirin, kod yazın, tercüme edin veya yardım etmek için elinizden gelen her şeyi yapın! 62 | En sevdiğiniz dizilerin hangi bölümlerini izlediğinizi takip edin 63 | -------------------------------------------------------------------------------- /app/src/main/res/values-v14/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Daily 5 | Twice weekly 6 | Weekly 7 | Fortnightly 8 | 9 | 10 | 24 11 | 84 12 | 168 13 | 336 14 | 15 | 16 | Chinese 17 | Croation 18 | Czech 19 | Dansk 20 | Deutsch 21 | English 22 | Español 23 | Français 24 | Greek 25 | Hebrew 26 | Italiano 27 | Japanese 28 | Korean 29 | Magyar 30 | Nederlands 31 | Norsk 32 | Polski 33 | Portuguese 34 | Russian 35 | Slovenian 36 | Suomeksi 37 | Svenska 38 | Turkish 39 | 40 | 41 | zh 42 | hr 43 | cs 44 | da 45 | de 46 | en 47 | es 48 | fr 49 | el 50 | he 51 | it 52 | ja 53 | ko 54 | hu 55 | nl 56 | no 57 | pl 58 | pt 59 | ru 60 | sl 61 | fi 62 | sv 63 | tr 64 | 65 | 66 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #009688 4 | #212121 5 | #181818 6 | #bdbdbd 7 | @color/accent 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 200dp 4 | 16dp 5 | 24sp 6 | 48dp 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Keep track of which episodes you\'ve watched of your favourite shows 4 | Episodes is copylefted libre software licensed GPLv3+. Please file bugs, write code, translate or do anything else you can to help! 5 | About 6 | This product uses the TMDB API but is not endorsed or certified by TMDB. 7 | https://github.com/red-coracle/episodes 8 | Episodes 9 | Adding %s to library 10 | Could not back up library 11 | Back-up complete: \'%s\' 12 | Could not add %s to library 13 | First Aired: %s 14 | About 15 | Search for new show 16 | Add new show to library 17 | Add show to library 18 | Back up library 19 | Delete show 20 | Filter shows 21 | All 22 | Starred 23 | Archived 24 | Underway 25 | Upcoming 26 | Mark season as unwatched 27 | Mark season as watched 28 | Mark show as unwatched 29 | Mark show as watched 30 | Refresh all shows 31 | Refresh show 32 | Restore from backup 33 | Settings 34 | Star show 35 | Unstar show 36 | Archive show 37 | Remove show from archive 38 | Interval 39 | Auto-refresh on 40 | Only on unmetered connections 41 | Language 42 | Reverse sorting order 43 | Displays the newest season first 44 | No backups found in \'%s\' folder. 45 | Restore from backup 46 | Could not restore library 47 | Library restored 48 | "%1$02dx%2$02d - " 49 | Season %d 50 | Specials 51 | Settings 52 | %s added to library 53 | %s already in library 54 | Episodes 55 | Next 56 | Notes 57 | Overview 58 | (+ %d upcoming) 59 | Watched 60 | %1$d of %2$d episodes seen 61 | Show sync 62 | Notification when syncing shows 63 | Auto-refresh series 64 | Interface 65 | Version %s (Commit %s) 66 | No search results found 67 | Metadata provider has switched to TMDB. We recommend backing up your database before refreshing all metadata. Press Continue to proceed with refreshing all shows or Cancel to return. 68 | Metadata Provider Change 69 | Continue 70 | Cancel 71 | 72 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 16 | 17 | 22 | 23 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/xml/preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 17 | 18 | 19 | 21 | 25 | 33 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /assets/feature-graphic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/assets/feature-graphic.png -------------------------------------------------------------------------------- /assets/hi-res-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/assets/hi-res-icon.png -------------------------------------------------------------------------------- /assets/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/assets/screenshot-1.png -------------------------------------------------------------------------------- /assets/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/assets/screenshot-2.png -------------------------------------------------------------------------------- /assets/screenshot-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/assets/screenshot-3.png -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | google() 5 | } 6 | dependencies { 7 | classpath 'com.android.tools.build:gradle:7.3.0' 8 | } 9 | } 10 | 11 | allprojects { 12 | repositories { 13 | mavenCentral() 14 | google() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0014.txt: -------------------------------------------------------------------------------- 1 | - Upgrade SDK and gradle versions 2 | - Switch from support library to androidx 3 | - Replace universal-image-loader with Glide 4 | - Use newer OkHttp & TVDB client versions 5 | - Fix sync notification/progress 6 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0015.txt: -------------------------------------------------------------------------------- 1 | - Replace deprecated AsyncTask 2 | - Searching and adding shows now uses language preference 3 | - Use poster image if fanart image is unavailable 4 | - Display loading indicator when fetching fanart/poster image 5 | - Add preference categories and use switches instead of checkboxes 6 | - Update dependencies and gradle 7 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0016.txt: -------------------------------------------------------------------------------- 1 | - Add filter for upcoming shows 2 | - Fix app crash when searching for shows 3 | - Add language column to episodes table if it does not exist 4 | - Update dependencies/gradle 5 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0017.txt: -------------------------------------------------------------------------------- 1 | - Added translations for Croatian, French, German, Norwegian Bokmål, Russian, and Spanish. Many thanks to the contributors on Weblate! 2 | - Add version and commit info to the About screen 3 | - Use the configured language when downloading show data 4 | - Update dependencies/gradle 5 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0018.txt: -------------------------------------------------------------------------------- 1 | - New and updated translations 2 | 3 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0019.txt: -------------------------------------------------------------------------------- 1 | - Fix searching for shows to add 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0020.txt: -------------------------------------------------------------------------------- 1 | - Display a message when a search returns no results 2 | - Show a loading spinner when searching for new shows 3 | - When marking a season as watched, only mark aired episodes 4 | - Updated dependencies 5 | - Updated translations 6 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0021.txt: -------------------------------------------------------------------------------- 1 | - Fix back button on season details 2 | - Update R8 configuration 3 | - Fix crash on Android 12 4 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0022.txt: -------------------------------------------------------------------------------- 1 | - Update badges in README 2 | - New launcher icon 3 | - Use storage framework on API19+ 4 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0023.txt: -------------------------------------------------------------------------------- 1 | - Final release with TVDB as metadata provider 2 | - Target version 33 (Android 13) 3 | - Raise minimum version to 21 (Android L) 4 | - Request notification permission on Android 13+ 5 | - Update gradle version 6 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0024.txt: -------------------------------------------------------------------------------- 1 | - Switch to TMDB as metadata provider 2 | - Add support for themed app icons 3 | - Fix for null/zero TVDB ID 4 | - Update setup-java CI action 5 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0025.txt: -------------------------------------------------------------------------------- 1 | - Fix sorting in shows list 2 | - Update star/archive icons to improve visibility 3 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/full_description.txt: -------------------------------------------------------------------------------- 1 | Keep track of which episodes you’ve watched of your favourite TV shows. 2 | This product uses the TMDB API but is not endorsed or certified by TMDB. 3 | 4 | Anti-Feature: NonFreeNet - uses a non-free network service which is impossible, or not easy to replace without rebuilding the app 5 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/fastlane/metadata/android/en-US/images/icon.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/short_description.txt: -------------------------------------------------------------------------------- 1 | Keep track of which episodes you’ve watched of your favourite TV shows. 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/title.txt: -------------------------------------------------------------------------------- 1 | Episodes 2 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | android.enableJetifier=true 2 | android.useAndroidX=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-coracle/episodes/fac7e844ff9e537935c91c9294f9aa12a3e92975/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionSha256Sum=db9c8211ed63f61f60292c69e80d89196f9eb36665e369e7f00ac4cc841c2219 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto init 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto init 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :init 65 | @rem Get command-line arguments, handling Windows variants 66 | 67 | if not "%OS%" == "Windows_NT" goto win9xME_args 68 | 69 | :win9xME_args 70 | @rem Slurp the command line arguments. 71 | set CMD_LINE_ARGS= 72 | set _SKIP=2 73 | 74 | :win9xME_args_slurp 75 | if "x%~1" == "x" goto execute 76 | 77 | set CMD_LINE_ARGS=%* 78 | 79 | :execute 80 | @rem Setup the command line 81 | 82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 83 | 84 | @rem Execute Gradle 85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 86 | 87 | :end 88 | @rem End local scope for the variables with windows NT shell 89 | if "%ERRORLEVEL%"=="0" goto mainEnd 90 | 91 | :fail 92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 93 | rem the _cmd.exe /c_ return code! 94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 95 | exit /b 1 96 | 97 | :mainEnd 98 | if "%OS%"=="Windows_NT" endlocal 99 | 100 | :omega 101 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------