aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYoungsang Cho <youngsang@google.com>2016-05-09 18:52:12 -0700
committerYoungsang Cho <youngsang@google.com>2016-05-09 18:53:20 -0700
commit369b6a409204a9b2a95f7ba575d7c3b7bdc94ab7 (patch)
tree9bd059c5ef3414659427204c753a1389c52966c9
parent4a3a606c96e6cc6e2837cda8fa16c4dcce13774d (diff)
downloadTV-369b6a409204a9b2a95f7ba575d7c3b7bdc94ab7.tar.gz
DO NOT MERGE Sync to joey ub-tv-dev at e7fbaa585b1eb7afec05f05032d2e8d99fb595d4
Bug: 28469968 Change-Id: Ie0d3c74af84777dd8f2e2a79aa0454c7e6a7f0d8
-rw-r--r--.gitignore1
-rw-r--r--Android.mk3
-rw-r--r--AndroidManifest.xml13
-rw-r--r--LiveChannelsAndroidStyle.xml39
-rw-r--r--assets/licenses.html206
-rw-r--r--common/Android.mk9
-rw-r--r--common/BuildConfig.java.in8
-rw-r--r--common/buildconfig.mk28
-rw-r--r--common/res/drawable/setup_action_button_done.xml1
-rw-r--r--common/res/values-af/strings.xml1
-rw-r--r--common/res/values-am/strings.xml1
-rw-r--r--common/res/values-ar/strings.xml1
-rw-r--r--common/res/values-az-rAZ/strings.xml1
-rw-r--r--common/res/values-bg/strings.xml1
-rw-r--r--common/res/values-bn-rBD/strings.xml1
-rw-r--r--common/res/values-ca/strings.xml1
-rw-r--r--common/res/values-cs/strings.xml1
-rw-r--r--common/res/values-da/strings.xml1
-rw-r--r--common/res/values-de/strings.xml1
-rw-r--r--common/res/values-el/strings.xml1
-rw-r--r--common/res/values-en-rAU/strings.xml1
-rw-r--r--common/res/values-en-rGB/strings.xml1
-rw-r--r--common/res/values-en-rIN/strings.xml1
-rw-r--r--common/res/values-es-rUS/strings.xml1
-rw-r--r--common/res/values-es/strings.xml1
-rw-r--r--common/res/values-et-rEE/strings.xml1
-rw-r--r--common/res/values-eu-rES/strings.xml1
-rw-r--r--common/res/values-fa/strings.xml1
-rw-r--r--common/res/values-fi/strings.xml1
-rw-r--r--common/res/values-fr-rCA/strings.xml1
-rw-r--r--common/res/values-fr/strings.xml1
-rw-r--r--common/res/values-gl-rES/strings.xml1
-rw-r--r--common/res/values-hi/strings.xml1
-rw-r--r--common/res/values-hr/strings.xml1
-rw-r--r--common/res/values-hu/strings.xml1
-rw-r--r--common/res/values-hy-rAM/strings.xml1
-rw-r--r--common/res/values-in/strings.xml1
-rw-r--r--common/res/values-is-rIS/strings.xml1
-rw-r--r--common/res/values-it/strings.xml1
-rw-r--r--common/res/values-iw/strings.xml1
-rw-r--r--common/res/values-ja/strings.xml1
-rw-r--r--common/res/values-ka-rGE/strings.xml1
-rw-r--r--common/res/values-kk-rKZ/strings.xml1
-rw-r--r--common/res/values-km-rKH/strings.xml1
-rw-r--r--common/res/values-kn-rIN/strings.xml1
-rw-r--r--common/res/values-ko/strings.xml1
-rw-r--r--common/res/values-ky-rKG/strings.xml1
-rw-r--r--common/res/values-lo-rLA/strings.xml1
-rw-r--r--common/res/values-lt/strings.xml1
-rw-r--r--common/res/values-lv/strings.xml1
-rw-r--r--common/res/values-mk-rMK/strings.xml1
-rw-r--r--common/res/values-ml-rIN/strings.xml1
-rw-r--r--common/res/values-mn-rMN/strings.xml1
-rw-r--r--common/res/values-mr-rIN/strings.xml1
-rw-r--r--common/res/values-ms-rMY/strings.xml1
-rw-r--r--common/res/values-my-rMM/strings.xml1
-rw-r--r--common/res/values-nb/strings.xml1
-rw-r--r--common/res/values-ne-rNP/strings.xml1
-rw-r--r--common/res/values-nl/strings.xml1
-rw-r--r--common/res/values-pl/strings.xml1
-rw-r--r--common/res/values-pt-rPT/strings.xml1
-rw-r--r--common/res/values-pt/strings.xml1
-rw-r--r--common/res/values-ro/strings.xml1
-rw-r--r--common/res/values-ru/strings.xml1
-rw-r--r--common/res/values-si-rLK/strings.xml1
-rw-r--r--common/res/values-sk/strings.xml1
-rw-r--r--common/res/values-sl/strings.xml1
-rw-r--r--common/res/values-sr/strings.xml1
-rw-r--r--common/res/values-sv/strings.xml1
-rw-r--r--common/res/values-sw/strings.xml1
-rw-r--r--common/res/values-ta-rIN/strings.xml1
-rw-r--r--common/res/values-te-rIN/strings.xml1
-rw-r--r--common/res/values-th/strings.xml1
-rw-r--r--common/res/values-tl/strings.xml1
-rw-r--r--common/res/values-tr/strings.xml1
-rw-r--r--common/res/values-uk/strings.xml1
-rw-r--r--common/res/values-ur-rPK/strings.xml1
-rw-r--r--common/res/values-uz-rUZ/strings.xml1
-rw-r--r--common/res/values-vi/strings.xml1
-rw-r--r--common/res/values-zh-rCN/strings.xml1
-rw-r--r--common/res/values-zh-rHK/strings.xml1
-rw-r--r--common/res/values-zh-rTW/strings.xml1
-rw-r--r--common/res/values-zu/strings.xml1
-rw-r--r--common/res/values/strings.xml4
-rw-r--r--common/res_leanback/animator/lb_onboarding_logo_exit.xml25
-rw-r--r--common/res_leanback/animator/lb_onboarding_page_indicator_enter.xml25
-rw-r--r--common/res_leanback/animator/lb_onboarding_page_indicator_fade_in.xml25
-rw-r--r--common/res_leanback/animator/lb_onboarding_page_indicator_fade_out.xml25
-rw-r--r--common/res_leanback/animator/lb_onboarding_start_button_fade_in.xml31
-rw-r--r--common/res_leanback/animator/lb_onboarding_start_button_fade_out.xml31
-rw-r--r--common/res_leanback/animator/lb_onboarding_title_enter.xml31
-rw-r--r--common/res_leanback/animator/lb_page_indicator_dot_hide.xml37
-rw-r--r--common/res_leanback/animator/lb_page_indicator_dot_show.xml37
-rw-r--r--common/res_leanback/drawable-xhdpi/lb_ic_nav_arrow.pngbin354 -> 0 bytes
-rw-r--r--common/res_leanback/layout/lb_onboarding_fragment.xml120
-rw-r--r--common/res_leanback/values-ar/strings.xml22
-rw-r--r--common/res_leanback/values-az-rAZ/strings.xml22
-rw-r--r--common/res_leanback/values-bg/strings.xml22
-rw-r--r--common/res_leanback/values-bn-rBD/strings.xml22
-rw-r--r--common/res_leanback/values-ca/strings.xml22
-rw-r--r--common/res_leanback/values-cs/strings.xml22
-rw-r--r--common/res_leanback/values-da/strings.xml22
-rw-r--r--common/res_leanback/values-de/strings.xml22
-rw-r--r--common/res_leanback/values-el/strings.xml22
-rw-r--r--common/res_leanback/values-en-rAU/strings.xml22
-rw-r--r--common/res_leanback/values-en-rGB/strings.xml22
-rw-r--r--common/res_leanback/values-en-rIN/strings.xml22
-rw-r--r--common/res_leanback/values-es-rUS/strings.xml22
-rw-r--r--common/res_leanback/values-es/strings.xml22
-rw-r--r--common/res_leanback/values-et-rEE/strings.xml22
-rw-r--r--common/res_leanback/values-eu-rES/strings.xml22
-rw-r--r--common/res_leanback/values-fa/strings.xml22
-rw-r--r--common/res_leanback/values-fi/strings.xml22
-rw-r--r--common/res_leanback/values-fr-rCA/strings.xml22
-rw-r--r--common/res_leanback/values-fr/strings.xml22
-rw-r--r--common/res_leanback/values-gl-rES/strings.xml22
-rw-r--r--common/res_leanback/values-hi/strings.xml22
-rw-r--r--common/res_leanback/values-hr/strings.xml22
-rw-r--r--common/res_leanback/values-hu/strings.xml22
-rw-r--r--common/res_leanback/values-hy-rAM/strings.xml22
-rw-r--r--common/res_leanback/values-in/strings.xml22
-rw-r--r--common/res_leanback/values-is-rIS/strings.xml22
-rw-r--r--common/res_leanback/values-it/strings.xml22
-rw-r--r--common/res_leanback/values-iw/strings.xml22
-rw-r--r--common/res_leanback/values-ja/strings.xml22
-rw-r--r--common/res_leanback/values-ka-rGE/strings.xml22
-rw-r--r--common/res_leanback/values-kk-rKZ/strings.xml22
-rw-r--r--common/res_leanback/values-km-rKH/strings.xml22
-rw-r--r--common/res_leanback/values-kn-rIN/strings.xml22
-rw-r--r--common/res_leanback/values-ko/strings.xml22
-rw-r--r--common/res_leanback/values-ky-rKG/strings.xml22
-rw-r--r--common/res_leanback/values-lo-rLA/strings.xml22
-rw-r--r--common/res_leanback/values-lt/strings.xml22
-rw-r--r--common/res_leanback/values-lv/strings.xml22
-rw-r--r--common/res_leanback/values-mk-rMK/strings.xml22
-rw-r--r--common/res_leanback/values-ml-rIN/strings.xml22
-rw-r--r--common/res_leanback/values-mn-rMN/strings.xml22
-rw-r--r--common/res_leanback/values-mr-rIN/strings.xml22
-rw-r--r--common/res_leanback/values-ms-rMY/strings.xml22
-rw-r--r--common/res_leanback/values-my-rMM/strings.xml22
-rw-r--r--common/res_leanback/values-nb/strings.xml22
-rw-r--r--common/res_leanback/values-ne-rNP/strings.xml22
-rw-r--r--common/res_leanback/values-nl/strings.xml22
-rw-r--r--common/res_leanback/values-pl/strings.xml22
-rw-r--r--common/res_leanback/values-pt-rPT/strings.xml22
-rw-r--r--common/res_leanback/values-pt/strings.xml22
-rw-r--r--common/res_leanback/values-ro/strings.xml22
-rw-r--r--common/res_leanback/values-ru/strings.xml22
-rw-r--r--common/res_leanback/values-si-rLK/strings.xml22
-rw-r--r--common/res_leanback/values-sk/strings.xml22
-rw-r--r--common/res_leanback/values-sl/strings.xml22
-rw-r--r--common/res_leanback/values-sr/strings.xml22
-rw-r--r--common/res_leanback/values-sv/strings.xml22
-rw-r--r--common/res_leanback/values-sw/strings.xml22
-rw-r--r--common/res_leanback/values-ta-rIN/strings.xml22
-rw-r--r--common/res_leanback/values-te-rIN/strings.xml22
-rw-r--r--common/res_leanback/values-th/strings.xml22
-rw-r--r--common/res_leanback/values-tl/strings.xml22
-rw-r--r--common/res_leanback/values-tr/strings.xml22
-rw-r--r--common/res_leanback/values-uk/strings.xml22
-rw-r--r--common/res_leanback/values-ur-rPK/strings.xml22
-rw-r--r--common/res_leanback/values-uz-rUZ/strings.xml22
-rw-r--r--common/res_leanback/values-vi/strings.xml22
-rw-r--r--common/res_leanback/values-zh-rCN/strings.xml22
-rw-r--r--common/res_leanback/values-zh-rHK/strings.xml22
-rw-r--r--common/res_leanback/values-zh-rTW/strings.xml22
-rw-r--r--common/res_leanback/values-zu/strings.xml22
-rw-r--r--common/res_leanback/values/colors.xml23
-rw-r--r--common/res_leanback/values/dimens.xml39
-rw-r--r--common/res_leanback/values/strings.xml25
-rw-r--r--common/src/com/android/tv/common/BuildConfig.java10
-rw-r--r--common/src/com/android/tv/common/CollectionUtils.java33
-rw-r--r--common/src/com/android/tv/common/SoftPreconditions.java (renamed from src/com/android/tv/util/SoftPreconditions.java)33
-rw-r--r--common/src/com/android/tv/common/TvContentRatingCache.java4
-rw-r--r--common/src/com/android/tv/common/feature/CommonFeatures.java15
-rw-r--r--common/src/com/android/tv/common/feature/Sdk.java33
-rw-r--r--common/src/com/android/tv/common/recording/PlaybackTvView.java191
-rw-r--r--common/src/com/android/tv/common/recording/RecordedProgram.java760
-rw-r--r--common/src/com/android/tv/common/recording/RecordingTvInputService.java378
-rw-r--r--common/src/com/android/tv/common/recording/RecordingUtils.java50
-rw-r--r--common/src/com/android/tv/common/recording/TvRecording.java384
-rw-r--r--common/src/com/android/tv/common/ui/setup/animation/FadeAndShortSlide.java13
-rw-r--r--common/src/com/android/tv/common/ui/setup/leanback/OnboardingFragment.java531
-rw-r--r--common/src/com/android/tv/common/ui/setup/leanback/PagingIndicator.java377
-rw-r--r--res/anim/half_sized_dialog_enter.xml (renamed from common/res_leanback/animator/lb_onboarding_description_enter.xml)24
-rw-r--r--res/anim/half_sized_dialog_exit.xml (renamed from common/res_leanback/animator/lb_onboarding_logo_enter.xml)16
-rw-r--r--res/drawable-xhdpi/dvr_default_program_art.pngbin0 -> 12853 bytes
-rw-r--r--res/drawable-xhdpi/ic_dvr.pngbin0 -> 684 bytes
-rw-r--r--res/drawable-xhdpi/ic_playstore.pngbin2431 -> 3822 bytes
-rw-r--r--res/drawable-xhdpi/ic_record_start_white.pngbin0 -> 614 bytes
-rw-r--r--res/drawable-xhdpi/ic_record_stop.pngbin0 -> 95 bytes
-rw-r--r--res/drawable-xhdpi/ic_store.pngbin16294 -> 3822 bytes
-rw-r--r--res/drawable-xhdpi/ic_youtube.pngbin1008 -> 0 bytes
-rw-r--r--res/drawable/ic_record_start.xml (renamed from common/res_leanback/drawable/lb_onboarding_start_button_background.xml)14
-rw-r--r--res/layout/block_screen.xml20
-rw-r--r--res/layout/dvr_recording_card_view.xml55
-rw-r--r--res/layout/halfsized_dialog.xml26
-rw-r--r--res/layout/halfsized_guidance.xml45
-rw-r--r--res/layout/menu_card_dvr.xml2
-rw-r--r--res/layout/menu_card_record.xml54
-rw-r--r--res/layout/onboarding_welcome_content.xml1
-rw-r--r--res/layout/program_guide_table_header_column_item.xml17
-rw-r--r--res/layout/tunable_tv_view.xml16
-rw-r--r--res/values-af/arrays.xml52
-rw-r--r--res/values-af/rating_system_strings.xml16
-rw-r--r--res/values-af/strings.xml10
-rw-r--r--res/values-am/arrays.xml52
-rw-r--r--res/values-am/rating_system_strings.xml16
-rw-r--r--res/values-am/strings.xml10
-rw-r--r--res/values-ar/arrays.xml52
-rw-r--r--res/values-ar/rating_system_strings.xml16
-rw-r--r--res/values-ar/strings.xml10
-rw-r--r--res/values-az-rAZ/arrays.xml52
-rw-r--r--res/values-az-rAZ/rating_system_strings.xml53
-rw-r--r--res/values-az-rAZ/strings.xml10
-rw-r--r--res/values-bg/arrays.xml52
-rw-r--r--res/values-bg/rating_system_strings.xml16
-rw-r--r--res/values-bg/strings.xml10
-rw-r--r--res/values-bn-rBD/arrays.xml52
-rw-r--r--res/values-bn-rBD/rating_system_strings.xml16
-rw-r--r--res/values-bn-rBD/strings.xml10
-rw-r--r--res/values-ca/arrays.xml52
-rw-r--r--res/values-ca/rating_system_strings.xml16
-rw-r--r--res/values-ca/strings.xml14
-rw-r--r--res/values-cs/arrays.xml52
-rw-r--r--res/values-cs/rating_system_strings.xml18
-rw-r--r--res/values-cs/strings.xml10
-rw-r--r--res/values-da/arrays.xml52
-rw-r--r--res/values-da/rating_system_strings.xml16
-rw-r--r--res/values-da/strings.xml10
-rw-r--r--res/values-de/arrays.xml52
-rw-r--r--res/values-de/rating_system_strings.xml16
-rw-r--r--res/values-de/strings.xml10
-rw-r--r--res/values-el/arrays.xml52
-rw-r--r--res/values-el/rating_system_strings.xml16
-rw-r--r--res/values-el/strings.xml10
-rw-r--r--res/values-en-rAU/arrays.xml52
-rw-r--r--res/values-en-rAU/rating_system_strings.xml16
-rw-r--r--res/values-en-rAU/strings.xml10
-rw-r--r--res/values-en-rGB/arrays.xml52
-rw-r--r--res/values-en-rGB/rating_system_strings.xml16
-rw-r--r--res/values-en-rGB/strings.xml10
-rw-r--r--res/values-en-rIN/arrays.xml52
-rw-r--r--res/values-en-rIN/rating_system_strings.xml16
-rw-r--r--res/values-en-rIN/strings.xml10
-rw-r--r--res/values-es-rUS/arrays.xml52
-rw-r--r--res/values-es-rUS/rating_system_strings.xml16
-rw-r--r--res/values-es-rUS/strings.xml10
-rw-r--r--res/values-es/arrays.xml52
-rw-r--r--res/values-es/rating_system_strings.xml16
-rw-r--r--res/values-es/strings.xml10
-rw-r--r--res/values-et-rEE/arrays.xml52
-rw-r--r--res/values-et-rEE/rating_system_strings.xml16
-rw-r--r--res/values-et-rEE/strings.xml10
-rw-r--r--res/values-eu-rES/arrays.xml52
-rw-r--r--res/values-eu-rES/rating_system_strings.xml16
-rw-r--r--res/values-eu-rES/strings.xml10
-rw-r--r--res/values-fa/arrays.xml52
-rw-r--r--res/values-fa/rating_system_strings.xml16
-rw-r--r--res/values-fa/strings.xml10
-rw-r--r--res/values-fi/arrays.xml52
-rw-r--r--res/values-fi/rating_system_strings.xml16
-rw-r--r--res/values-fi/strings.xml10
-rw-r--r--res/values-fr-rCA/arrays.xml52
-rw-r--r--res/values-fr-rCA/rating_system_strings.xml16
-rw-r--r--res/values-fr-rCA/strings.xml14
-rw-r--r--res/values-fr/arrays.xml52
-rw-r--r--res/values-fr/rating_system_strings.xml16
-rw-r--r--res/values-fr/strings.xml10
-rw-r--r--res/values-gl-rES/arrays.xml52
-rw-r--r--res/values-gl-rES/rating_system_strings.xml16
-rw-r--r--res/values-gl-rES/strings.xml10
-rw-r--r--res/values-hi/arrays.xml52
-rw-r--r--res/values-hi/rating_system_strings.xml16
-rw-r--r--res/values-hi/strings.xml10
-rw-r--r--res/values-hr/arrays.xml52
-rw-r--r--res/values-hr/rating_system_strings.xml16
-rw-r--r--res/values-hr/strings.xml10
-rw-r--r--res/values-hu/arrays.xml52
-rw-r--r--res/values-hu/rating_system_strings.xml16
-rw-r--r--res/values-hu/strings.xml10
-rw-r--r--res/values-hy-rAM/arrays.xml52
-rw-r--r--res/values-hy-rAM/rating_system_strings.xml16
-rw-r--r--res/values-hy-rAM/strings.xml10
-rw-r--r--res/values-in/arrays.xml52
-rw-r--r--res/values-in/rating_system_strings.xml16
-rw-r--r--res/values-in/strings.xml10
-rw-r--r--res/values-is-rIS/arrays.xml52
-rw-r--r--res/values-is-rIS/rating_system_strings.xml16
-rw-r--r--res/values-is-rIS/strings.xml10
-rw-r--r--res/values-it/arrays.xml52
-rw-r--r--res/values-it/rating_system_strings.xml16
-rw-r--r--res/values-it/strings.xml10
-rw-r--r--res/values-iw/arrays.xml52
-rw-r--r--res/values-iw/rating_system_strings.xml16
-rw-r--r--res/values-iw/strings.xml10
-rw-r--r--res/values-ja/arrays.xml52
-rw-r--r--res/values-ja/rating_system_strings.xml16
-rw-r--r--res/values-ja/strings.xml10
-rw-r--r--res/values-ka-rGE/arrays.xml52
-rw-r--r--res/values-ka-rGE/rating_system_strings.xml16
-rw-r--r--res/values-ka-rGE/strings.xml10
-rw-r--r--res/values-kk-rKZ/arrays.xml52
-rw-r--r--res/values-kk-rKZ/rating_system_strings.xml16
-rw-r--r--res/values-kk-rKZ/strings.xml10
-rw-r--r--res/values-km-rKH/arrays.xml52
-rw-r--r--res/values-km-rKH/rating_system_strings.xml16
-rw-r--r--res/values-km-rKH/strings.xml10
-rw-r--r--res/values-kn-rIN/arrays.xml52
-rw-r--r--res/values-kn-rIN/rating_system_strings.xml16
-rw-r--r--res/values-kn-rIN/strings.xml10
-rw-r--r--res/values-ko/arrays.xml52
-rw-r--r--res/values-ko/rating_system_strings.xml16
-rw-r--r--res/values-ko/rating_system_strings_ko.xml (renamed from common/res_leanback/values-af/strings.xml)14
-rw-r--r--res/values-ko/strings.xml10
-rw-r--r--res/values-ky-rKG/arrays.xml52
-rw-r--r--res/values-ky-rKG/rating_system_strings.xml16
-rw-r--r--res/values-ky-rKG/strings.xml10
-rw-r--r--res/values-lo-rLA/arrays.xml52
-rw-r--r--res/values-lo-rLA/rating_system_strings.xml16
-rw-r--r--res/values-lo-rLA/strings.xml10
-rw-r--r--res/values-lt/arrays.xml52
-rw-r--r--res/values-lt/rating_system_strings.xml16
-rw-r--r--res/values-lt/strings.xml10
-rw-r--r--res/values-lv/arrays.xml52
-rw-r--r--res/values-lv/rating_system_strings.xml16
-rw-r--r--res/values-lv/strings.xml10
-rw-r--r--res/values-mk-rMK/arrays.xml52
-rw-r--r--res/values-mk-rMK/rating_system_strings.xml16
-rw-r--r--res/values-mk-rMK/strings.xml10
-rw-r--r--res/values-ml-rIN/arrays.xml52
-rw-r--r--res/values-ml-rIN/rating_system_strings.xml16
-rw-r--r--res/values-ml-rIN/strings.xml10
-rw-r--r--res/values-mn-rMN/arrays.xml52
-rw-r--r--res/values-mn-rMN/rating_system_strings.xml16
-rw-r--r--res/values-mn-rMN/strings.xml10
-rw-r--r--res/values-mr-rIN/arrays.xml52
-rw-r--r--res/values-mr-rIN/rating_system_strings.xml16
-rw-r--r--res/values-mr-rIN/strings.xml10
-rw-r--r--res/values-ms-rMY/arrays.xml52
-rw-r--r--res/values-ms-rMY/rating_system_strings.xml16
-rw-r--r--res/values-ms-rMY/strings.xml10
-rw-r--r--res/values-my-rMM/arrays.xml52
-rw-r--r--res/values-my-rMM/rating_system_strings.xml16
-rw-r--r--res/values-my-rMM/strings.xml10
-rw-r--r--res/values-nb/arrays.xml52
-rw-r--r--res/values-nb/rating_system_strings.xml16
-rw-r--r--res/values-nb/strings.xml10
-rw-r--r--res/values-ne-rNP/arrays.xml52
-rw-r--r--res/values-ne-rNP/rating_system_strings.xml16
-rw-r--r--res/values-ne-rNP/strings.xml10
-rw-r--r--res/values-nl/arrays.xml52
-rw-r--r--res/values-nl/rating_system_strings.xml16
-rw-r--r--res/values-nl/strings.xml12
-rw-r--r--res/values-pl/arrays.xml52
-rw-r--r--res/values-pl/rating_system_strings.xml16
-rw-r--r--res/values-pl/strings.xml10
-rw-r--r--res/values-pt-rPT/arrays.xml52
-rw-r--r--res/values-pt-rPT/rating_system_strings.xml16
-rw-r--r--res/values-pt-rPT/strings.xml10
-rw-r--r--res/values-pt/arrays.xml52
-rw-r--r--res/values-pt/rating_system_strings.xml16
-rw-r--r--res/values-pt/rating_system_strings_pt.xml (renamed from common/res_leanback/values-am/strings.xml)15
-rw-r--r--res/values-pt/strings.xml10
-rw-r--r--res/values-ro/arrays.xml52
-rw-r--r--res/values-ro/rating_system_strings.xml16
-rw-r--r--res/values-ro/strings.xml10
-rw-r--r--res/values-ru/arrays.xml52
-rw-r--r--res/values-ru/rating_system_strings.xml16
-rw-r--r--res/values-ru/strings.xml10
-rw-r--r--res/values-si-rLK/arrays.xml52
-rw-r--r--res/values-si-rLK/rating_system_strings.xml16
-rw-r--r--res/values-si-rLK/strings.xml10
-rw-r--r--res/values-sk/arrays.xml52
-rw-r--r--res/values-sk/rating_system_strings.xml18
-rw-r--r--res/values-sk/strings.xml10
-rw-r--r--res/values-sl/arrays.xml52
-rw-r--r--res/values-sl/rating_system_strings.xml16
-rw-r--r--res/values-sl/strings.xml10
-rw-r--r--res/values-sr/arrays.xml52
-rw-r--r--res/values-sr/rating_system_strings.xml16
-rw-r--r--res/values-sr/strings.xml10
-rw-r--r--res/values-sv/arrays.xml52
-rw-r--r--res/values-sv/rating_system_strings.xml16
-rw-r--r--res/values-sv/strings.xml10
-rw-r--r--res/values-sw/arrays.xml52
-rw-r--r--res/values-sw/rating_system_strings.xml16
-rw-r--r--res/values-sw/strings.xml10
-rw-r--r--res/values-ta-rIN/arrays.xml52
-rw-r--r--res/values-ta-rIN/rating_system_strings.xml16
-rw-r--r--res/values-ta-rIN/strings.xml10
-rw-r--r--res/values-te-rIN/arrays.xml52
-rw-r--r--res/values-te-rIN/rating_system_strings.xml16
-rw-r--r--res/values-te-rIN/strings.xml10
-rw-r--r--res/values-th/arrays.xml52
-rw-r--r--res/values-th/rating_system_strings.xml16
-rw-r--r--res/values-th/strings.xml10
-rw-r--r--res/values-tl/arrays.xml52
-rw-r--r--res/values-tl/rating_system_strings.xml16
-rw-r--r--res/values-tl/strings.xml10
-rw-r--r--res/values-tr/arrays.xml52
-rw-r--r--res/values-tr/rating_system_strings.xml16
-rw-r--r--res/values-tr/strings.xml10
-rw-r--r--res/values-uk/arrays.xml52
-rw-r--r--res/values-uk/rating_system_strings.xml16
-rw-r--r--res/values-uk/strings.xml12
-rw-r--r--res/values-ur-rPK/arrays.xml52
-rw-r--r--res/values-ur-rPK/rating_system_strings.xml16
-rw-r--r--res/values-ur-rPK/strings.xml16
-rw-r--r--res/values-uz-rUZ/arrays.xml52
-rw-r--r--res/values-uz-rUZ/rating_system_strings.xml16
-rw-r--r--res/values-uz-rUZ/strings.xml10
-rw-r--r--res/values-vi/arrays.xml52
-rw-r--r--res/values-vi/rating_system_strings.xml16
-rw-r--r--res/values-vi/strings.xml10
-rw-r--r--res/values-zh-rCN/arrays.xml52
-rw-r--r--res/values-zh-rCN/rating_system_strings.xml16
-rw-r--r--res/values-zh-rCN/strings.xml10
-rw-r--r--res/values-zh-rHK/arrays.xml52
-rw-r--r--res/values-zh-rHK/rating_system_strings.xml16
-rw-r--r--res/values-zh-rHK/strings.xml10
-rw-r--r--res/values-zh-rTW/arrays.xml52
-rw-r--r--res/values-zh-rTW/rating_system_strings.xml16
-rw-r--r--res/values-zh-rTW/strings.xml10
-rw-r--r--res/values-zu/arrays.xml52
-rw-r--r--res/values-zu/rating_system_strings.xml16
-rw-r--r--res/values-zu/strings.xml10
-rw-r--r--res/values/arrays.xml27
-rw-r--r--res/values/attr.xml2
-rw-r--r--res/values/dimens.xml21
-rw-r--r--res/values/integers.xml5
-rw-r--r--res/values/rating_system_strings.xml49
-rw-r--r--res/values/strings.xml49
-rw-r--r--res/values/styles.xml31
-rw-r--r--res/values/themes.xml27
-rw-r--r--src/com/android/tv/ChannelTuner.java6
-rw-r--r--src/com/android/tv/Features.java63
-rw-r--r--src/com/android/tv/MainActivity.java584
-rw-r--r--src/com/android/tv/MainActivityWrapper.java4
-rw-r--r--src/com/android/tv/SetupPassthroughActivity.java2
-rw-r--r--src/com/android/tv/TimeShiftManager.java204
-rw-r--r--src/com/android/tv/TvApplication.java65
-rw-r--r--src/com/android/tv/TvOptionsManager.java13
-rw-r--r--src/com/android/tv/analytics/SendConfigInfoRunnable.java4
-rw-r--r--src/com/android/tv/data/Channel.java39
-rw-r--r--src/com/android/tv/data/ChannelDataManager.java19
-rw-r--r--src/com/android/tv/data/GenreItems.java39
-rw-r--r--src/com/android/tv/data/Program.java153
-rw-r--r--src/com/android/tv/data/ProgramDataManager.java49
-rw-r--r--src/com/android/tv/data/StreamInfo.java1
-rw-r--r--src/com/android/tv/data/WatchedHistoryManager.java9
-rw-r--r--src/com/android/tv/data/epg/EpgFetcher.java356
-rw-r--r--src/com/android/tv/data/epg/EpgReader.java53
-rw-r--r--src/com/android/tv/data/epg/StubEpgReader.java53
-rw-r--r--src/com/android/tv/dialog/FullscreenDialogFragment.java5
-rw-r--r--src/com/android/tv/dvr/BaseDvrDataManager.java135
-rw-r--r--src/com/android/tv/dvr/DvrDataManager.java73
-rw-r--r--src/com/android/tv/dvr/DvrDataManagerImpl.java308
-rw-r--r--src/com/android/tv/dvr/DvrDataManagerInMemoryImpl.java144
-rw-r--r--src/com/android/tv/dvr/DvrManager.java116
-rw-r--r--src/com/android/tv/dvr/DvrPlayActivity.java10
-rw-r--r--src/com/android/tv/dvr/DvrRecordingService.java20
-rw-r--r--src/com/android/tv/dvr/DvrSessionManager.java117
-rw-r--r--src/com/android/tv/dvr/RecordingTask.java208
-rw-r--r--src/com/android/tv/dvr/ScheduledProgramReaper.java53
-rw-r--r--src/com/android/tv/dvr/ScheduledRecording.java (renamed from src/com/android/tv/dvr/Recording.java)225
-rw-r--r--src/com/android/tv/dvr/Scheduler.java51
-rw-r--r--src/com/android/tv/dvr/SeasonRecording.java2
-rw-r--r--src/com/android/tv/dvr/WritableDvrDataManager.java6
-rw-r--r--src/com/android/tv/dvr/provider/AsyncDvrDbTask.java81
-rw-r--r--src/com/android/tv/dvr/provider/DvrContract.java66
-rw-r--r--src/com/android/tv/dvr/provider/DvrDatabaseHelper.java101
-rw-r--r--src/com/android/tv/dvr/ui/DvrBrowseFragment.java102
-rw-r--r--src/com/android/tv/dvr/ui/DvrDialogFragment.java50
-rw-r--r--src/com/android/tv/dvr/ui/DvrGuidedStepFragment.java73
-rw-r--r--src/com/android/tv/dvr/ui/DvrRecordConflictFragment.java82
-rw-r--r--src/com/android/tv/dvr/ui/DvrRecordDeleteFragment.java48
-rw-r--r--src/com/android/tv/dvr/ui/DvrRecordScheduleFragment.java70
-rw-r--r--src/com/android/tv/dvr/ui/EmptyHolder.java27
-rw-r--r--src/com/android/tv/dvr/ui/EmptyItemPresenter.java66
-rw-r--r--src/com/android/tv/dvr/ui/GridItemPresenter.java165
-rw-r--r--src/com/android/tv/dvr/ui/HalfSizedDialogFragment.java30
-rw-r--r--src/com/android/tv/dvr/ui/RecordedProgramPresenter.java121
-rw-r--r--src/com/android/tv/dvr/ui/RecordedProgramsAdapter.java65
-rw-r--r--src/com/android/tv/dvr/ui/RecordingCardView.java115
-rw-r--r--src/com/android/tv/dvr/ui/ScheduledRecordingPresenter.java171
-rw-r--r--src/com/android/tv/dvr/ui/ScheduledRecordingsAdapter.java85
-rw-r--r--src/com/android/tv/dvr/ui/SortedArrayAdapter.java193
-rw-r--r--src/com/android/tv/guide/ProgramGrid.java60
-rw-r--r--src/com/android/tv/guide/ProgramGuide.java19
-rw-r--r--src/com/android/tv/guide/ProgramItemView.java136
-rw-r--r--src/com/android/tv/guide/ProgramListAdapter.java48
-rw-r--r--src/com/android/tv/guide/ProgramManager.java179
-rw-r--r--src/com/android/tv/guide/ProgramRow.java2
-rw-r--r--src/com/android/tv/guide/ProgramTableAdapter.java92
-rw-r--r--src/com/android/tv/guide/TimelineRow.java4
-rw-r--r--src/com/android/tv/menu/ActionCardView.java3
-rw-r--r--src/com/android/tv/menu/BaseCardView.java3
-rw-r--r--src/com/android/tv/menu/ChannelsPosterPrefetcher.java2
-rw-r--r--src/com/android/tv/menu/ChannelsRowAdapter.java63
-rw-r--r--src/com/android/tv/menu/ItemListRowView.java8
-rw-r--r--src/com/android/tv/menu/Menu.java4
-rw-r--r--src/com/android/tv/menu/MenuAction.java7
-rw-r--r--src/com/android/tv/menu/MenuLayoutManager.java18
-rw-r--r--src/com/android/tv/menu/MenuView.java7
-rw-r--r--src/com/android/tv/menu/PlayControlsRowView.java42
-rw-r--r--src/com/android/tv/menu/RecordCardView.java189
-rw-r--r--src/com/android/tv/menu/TvOptionsRowAdapter.java36
-rw-r--r--src/com/android/tv/onboarding/OnboardingActivity.java55
-rw-r--r--src/com/android/tv/onboarding/SetupSourcesFragment.java33
-rw-r--r--src/com/android/tv/onboarding/WelcomeFragment.java82
-rw-r--r--src/com/android/tv/parental/ContentRatingSystem.java13
-rw-r--r--src/com/android/tv/receiver/BootCompletedReceiver.java4
-rw-r--r--src/com/android/tv/receiver/GlobalKeyReceiver.java21
-rw-r--r--src/com/android/tv/receiver/PackageIntentsReceiver.java41
-rw-r--r--src/com/android/tv/recommendation/NotificationService.java8
-rw-r--r--src/com/android/tv/recommendation/RecommendationDataManager.java290
-rw-r--r--src/com/android/tv/recommendation/Recommender.java2
-rw-r--r--src/com/android/tv/recommendation/RoutineWatchEvaluator.java27
-rw-r--r--src/com/android/tv/ui/AppLayerTvView.java5
-rw-r--r--src/com/android/tv/ui/BlockScreenView.java241
-rw-r--r--src/com/android/tv/ui/ChannelBannerView.java187
-rw-r--r--src/com/android/tv/ui/DialogUtils.java61
-rw-r--r--src/com/android/tv/ui/KeypadChannelSwitchView.java2
-rw-r--r--src/com/android/tv/ui/OnRepeatedKeyInterceptListener.java4
-rw-r--r--src/com/android/tv/ui/SelectInputView.java8
-rw-r--r--src/com/android/tv/ui/TunableTvView.java408
-rw-r--r--src/com/android/tv/ui/TvOverlayManager.java23
-rw-r--r--src/com/android/tv/ui/TvTransitionManager.java11
-rw-r--r--src/com/android/tv/ui/TvViewUiManager.java122
-rw-r--r--src/com/android/tv/ui/sidepanel/CustomizeChannelListFragment.java24
-rw-r--r--src/com/android/tv/ui/sidepanel/DeveloperFragment.java84
-rw-r--r--src/com/android/tv/ui/sidepanel/PipInputSelectorFragment.java7
-rw-r--r--src/com/android/tv/ui/sidepanel/SettingsFragment.java17
-rw-r--r--src/com/android/tv/ui/sidepanel/parentalcontrols/RatingsFragment.java82
-rw-r--r--src/com/android/tv/util/AsyncDbTask.java83
-rw-r--r--src/com/android/tv/util/ImageLoader.java46
-rw-r--r--src/com/android/tv/util/MultiLongSparseArray.java5
-rw-r--r--src/com/android/tv/util/NetworkUtils.java66
-rw-r--r--src/com/android/tv/util/OnboardingUtils.java4
-rw-r--r--src/com/android/tv/util/PipInputManager.java65
-rw-r--r--src/com/android/tv/util/RecurringRunner.java1
-rw-r--r--src/com/android/tv/util/SetupUtils.java51
-rw-r--r--src/com/android/tv/util/SystemProperties.java7
-rw-r--r--src/com/android/tv/util/TvInputManagerHelper.java1
-rw-r--r--src/com/android/tv/util/Utils.java102
-rw-r--r--src/com/android/usbtuner/UsbInputController.java21
-rw-r--r--tests/common/src/com/android/tv/testing/ChannelInfo.java20
-rw-r--r--tests/common/src/com/android/tv/testing/FakeClock.java2
-rw-r--r--tests/common/src/com/android/tv/testing/dvr/RecordingTestUtils.java45
-rw-r--r--tests/common/src/com/android/tv/testing/uihelper/SidePanelHelper.java4
-rw-r--r--tests/func/Android.mk1
-rw-r--r--tests/func/src/com/android/tv/tests/ui/ParentalControlsTest.java154
-rw-r--r--tests/input/Android.mk1
-rw-r--r--tests/jank/Android.mk1
-rw-r--r--tests/unit/Android.mk1
-rw-r--r--tests/unit/src/com/android/tv/common/ui/setup/leanback/PagingIndicatorTest.java94
-rw-r--r--tests/unit/src/com/android/tv/data/ChannelDataManagerTest.java14
-rw-r--r--tests/unit/src/com/android/tv/data/GenreItemTest.java84
-rw-r--r--tests/unit/src/com/android/tv/data/TvInputNewComparatorTest.java3
-rw-r--r--tests/unit/src/com/android/tv/dvr/BaseDvrDataManagerTest.java72
-rw-r--r--tests/unit/src/com/android/tv/dvr/DvrDataManagerImplTest.java54
-rw-r--r--tests/unit/src/com/android/tv/dvr/DvrRecordingServiceTest.java7
-rw-r--r--tests/unit/src/com/android/tv/dvr/RecordingTaskTest.java73
-rw-r--r--tests/unit/src/com/android/tv/dvr/ScheduledProgramReaperTest.java107
-rw-r--r--tests/unit/src/com/android/tv/dvr/ScheduledRecordingTest.java (renamed from tests/unit/src/com/android/tv/dvr/RecordingTest.java)39
-rw-r--r--tests/unit/src/com/android/tv/dvr/SchedulerTest.java32
-rw-r--r--tests/unit/src/com/android/tv/dvr/ui/SortedArrayAdapterTest.java166
-rw-r--r--tests/unit/src/com/android/tv/recommendation/RoutineWatchEvaluatorTest.java110
-rw-r--r--tests/unit/src/com/android/tv/util/TestUtils.java11
-rw-r--r--tests/unit/src/com/android/tv/util/TvInputManagerHelperTest.java2
-rw-r--r--usbtuner/Android.mk13
-rw-r--r--usbtuner/AndroidManifest.xml4
-rw-r--r--usbtuner/icu/icu4j/main/classes/core/src/com/ibm/icu/text/UnicodeDecompressor.java4
-rw-r--r--usbtuner/libs/exoplayer_1.5.6.jarbin0 -> 752820 bytes
-rw-r--r--usbtuner/libs/tv-exoplayer.jarbin552865 -> 0 bytes
-rw-r--r--usbtuner/res/xml/ut_tvinputservice.xml4
-rw-r--r--usbtuner/src/com/android/usbtuner/DvbDeviceAccessor.java33
-rw-r--r--usbtuner/src/com/android/usbtuner/FileDataSource.java11
-rw-r--r--usbtuner/src/com/android/usbtuner/TunerHal.java10
-rw-r--r--usbtuner/src/com/android/usbtuner/UsbTunerDataSource.java21
-rw-r--r--usbtuner/src/com/android/usbtuner/UsbTunerPreferences.java41
-rw-r--r--usbtuner/src/com/android/usbtuner/UsbTunerTsScannerSource.java2
-rw-r--r--usbtuner/src/com/android/usbtuner/cc/CaptionWindowLayout.java1
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/CachedSampleSourceExtractor.java331
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/Cea708TextTrackRenderer.java111
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/DefaultSampleSource.java163
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/MediaCodecVideoTrackRenderer.java609
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/MpegTsPassthroughAc3RendererBuilder.java32
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/MpegTsPlayer.java54
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/MpegTsSampleSource.java186
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/MpegTsSampleSourceExtractor.java62
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/MpegTsVideoTrackRenderer.java60
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/PlaySampleExtractor.java (renamed from usbtuner/src/com/android/usbtuner/exoplayer/BaseSampleSourceExtractor.java)118
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/RecordSampleSourceExtractor.java328
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/Recorder.java216
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/ReplaySampleSourceExtractor.java242
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/SampleExtractor.java10
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/ac3/Ac3TrackRenderer.java108
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/ac3/AudioClock.java (renamed from usbtuner/src/com/android/usbtuner/exoplayer/ac3/MediaClock.java)2
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/ac3/AudioTrackWrapper.java18
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/cache/CacheManager.java (renamed from usbtuner/src/com/android/usbtuner/exoplayer/CacheManager.java)114
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/cache/DvrStorageManager.java (renamed from usbtuner/src/com/android/usbtuner/exoplayer/DvrStorageManager.java)2
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/cache/RecordingSampleBuffer.java419
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/cache/SampleCache.java (renamed from usbtuner/src/com/android/usbtuner/exoplayer/SampleCache.java)2
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/cache/SamplePool.java (renamed from usbtuner/src/com/android/usbtuner/exoplayer/SamplePool.java)6
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/cache/SampleQueue.java (renamed from usbtuner/src/com/android/usbtuner/exoplayer/SampleQueue.java)2
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/cache/SimpleSampleBuffer.java (renamed from usbtuner/src/com/android/usbtuner/exoplayer/SimpleSampleSourceExtractor.java)148
-rw-r--r--usbtuner/src/com/android/usbtuner/exoplayer/cache/TrickplayStorageManager.java (renamed from usbtuner/src/com/android/usbtuner/exoplayer/TrickplayStorageManager.java)2
-rw-r--r--usbtuner/src/com/android/usbtuner/setup/ScanResultFragment.java9
-rw-r--r--usbtuner/src/com/android/usbtuner/tvinput/BaseTunerTvInputService.java60
-rw-r--r--usbtuner/src/com/android/usbtuner/tvinput/ChannelDataManager.java7
-rw-r--r--usbtuner/src/com/android/usbtuner/tvinput/DvrSessionImplInternal.java313
-rw-r--r--usbtuner/src/com/android/usbtuner/tvinput/InternalTunerTvInputService.java12
-rw-r--r--usbtuner/src/com/android/usbtuner/tvinput/RecordingSessionImpl.java87
-rw-r--r--usbtuner/src/com/android/usbtuner/tvinput/TunerRecordingSession.java114
-rw-r--r--usbtuner/src/com/android/usbtuner/tvinput/TunerRecordingSessionWorker.java549
-rw-r--r--usbtuner/src/com/android/usbtuner/tvinput/TunerSession.java (renamed from usbtuner/src/com/android/usbtuner/tvinput/TvInputSessionImpl.java)124
-rw-r--r--usbtuner/src/com/android/usbtuner/tvinput/TunerSessionWorker.java (renamed from usbtuner/src/com/android/usbtuner/tvinput/TvInputSessionImplInternal.java)265
-rw-r--r--usbtuner/src/com/android/usbtuner/tvinput/UsbTunerDebug.java1
-rw-r--r--usbtuner/src/com/android/usbtuner/tvinput/UsbTunerTvInputService.java16
-rw-r--r--usbtuner/src/com/google/android/exoplayer/MediaFormatUtil.java93
-rw-r--r--usbtuner/src/com/google/android/exoplayer/MediaSoftwareCodecUtil.java282
-rw-r--r--usbtuner/src/com/google/android/exoplayer/text/SubtitleView.java309
-rw-r--r--version.mk8
625 files changed, 15118 insertions, 11470 deletions
diff --git a/.gitignore b/.gitignore
index 0b3f4541..cc89ad62 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,3 @@
-BuildConfig.java
# Samples
samples/SampleTvInput/
diff --git a/Android.mk b/Android.mk
index 1585ae58..76d76ee4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -29,10 +29,10 @@ LOCAL_PACKAGE_NAME := LiveTv
LOCAL_PRIVILEGED_MODULE := true
LOCAL_SDK_VERSION := system_current
+LOCAL_MIN_SDK_VERSION := 23 # M
LOCAL_RESOURCE_DIR := \
$(LOCAL_PATH)/res \
$(LOCAL_PATH)/common/res \
- $(LOCAL_PATH)/common/res_leanback \
$(TOP)/prebuilts/sdk/current/support/v17/leanback/res \
$(TOP)/prebuilts/sdk/current/support/v7/recyclerview/res \
@@ -56,6 +56,7 @@ LOCAL_AAPT_FLAGS := --auto-add-overlay \
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+
LOCAL_RESOURCE_DIR += $(LOCAL_PATH)/usbtuner/res
LOCAL_STATIC_JAVA_LIBRARIES += usbtuner-tvinput
LOCAL_JNI_SHARED_LIBRARIES := libtunertvinput_jni
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9790798c..a40313cc 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -44,7 +44,7 @@
android:protectionLevel="signatureOrSystem"
android:label="@string/permlab_receiveInputEvent"
android:description="@string/permdesc_receiveInputEvent" tools:ignore="SignatureOrSystemPermissions"/>
- <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="21"/>
+ <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="23"/>
<application android:label="@string/app_name"
android:name=".TvApplication"
@@ -62,9 +62,12 @@
</activity>
<activity android:name="com.android.tv.MainActivity"
- android:configChanges="keyboard|keyboardHidden"
+ android:configChanges="keyboard|keyboardHidden|screenSize|smallestScreenSize|screenLayout|orientation"
android:screenOrientation="landscape"
- android:launchMode="singleTask" >
+ android:launchMode="singleTask"
+ android:resizeableActivity="true"
+ android:supportsPictureInPicture="true"
+ android:theme="@style/Theme.TV.MainActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
@@ -74,6 +77,10 @@
<data android:mimeType="vnd.android.cursor.dir/program" />
</intent-filter>
<intent-filter>
+ <action android:name="android.media.tv.action.SETUP_INPUTS" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data android:name="supports_leanback" android:value="true" />
diff --git a/LiveChannelsAndroidStyle.xml b/LiveChannelsAndroidStyle.xml
index 2f5bd30c..a2627ce2 100644
--- a/LiveChannelsAndroidStyle.xml
+++ b/LiveChannelsAndroidStyle.xml
@@ -34,6 +34,8 @@
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="9999" />
<option name="IMPORT_LAYOUT_TABLE">
<value>
+ <package name="" withSubpackages="true" static="true" />
+ <emptyLine />
<package name="android" withSubpackages="true" static="false" />
<emptyLine />
<package name="com.google" withSubpackages="true" static="false" />
@@ -51,8 +53,6 @@
<package name="javax" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="false" />
- <emptyLine />
- <package name="" withSubpackages="true" static="true" />
</value>
</option>
<option name="RIGHT_MARGIN" value="100" />
@@ -88,7 +88,40 @@
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
+ <AndroidXmlCodeStyleSettings>
+ <option name="USE_CUSTOM_SETTINGS" value="true" />
+ </AndroidXmlCodeStyleSettings>
+ <Objective-C-extensions>
+ <option name="GENERATE_INSTANCE_VARIABLES_FOR_PROPERTIES" value="ASK" />
+ <option name="RELEASE_STYLE" value="IVAR" />
+ <option name="TYPE_QUALIFIERS_PLACEMENT" value="BEFORE" />
+ <file>
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
+ </file>
+ <class>
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
+ </class>
+ <extensions>
+ <pair source="cpp" header="h" />
+ <pair source="c" header="h" />
+ </extensions>
+ </Objective-C-extensions>
<XML>
+ <option name="XML_KEEP_BLANK_LINES" value="1" />
+ <option name="XML_ALIGN_ATTRIBUTES" value="false" />
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="CFML">
@@ -244,4 +277,4 @@
<option name="FOR_BRACE_FORCE" value="3" />
<option name="PARENT_SETTINGS_INSTALLED" value="true" />
</codeStyleSettings>
-</code_scheme>
+</code_scheme> \ No newline at end of file
diff --git a/assets/licenses.html b/assets/licenses.html
index 83b740a0..000aacf6 100644
--- a/assets/licenses.html
+++ b/assets/licenses.html
@@ -87,6 +87,212 @@ Except as contained in this notice, the name of a copyright holder shall not be
used in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization of the copyright holder.
</pre>
+<h3>Notices for files:</h3>
+<ul>
+ <li>ExoPlayer</li>
+</ul>
+<pre> Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+</pre>
<!-- END USB Tuner licenses -->
</body></html> \ No newline at end of file
diff --git a/common/Android.mk b/common/Android.mk
index 96913a1b..bd4d99bc 100644
--- a/common/Android.mk
+++ b/common/Android.mk
@@ -1,11 +1,11 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-# Include all test java files.
-LOCAL_SRC_FILES := $(call all-java-files-under, src) \
- $(LOCAL_BUILDCONFIG_CLASS)
+# Include all common java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_MODULE := tv-common
+LOCAL_MODULE_CLASS := STATIC_JAVA_LIBRARIES
LOCAL_MODULE_TAGS := optional
LOCAL_SDK_VERSION := system_current
@@ -13,7 +13,6 @@ LOCAL_RESOURCE_DIR := \
$(TOP)/prebuilts/sdk/current/support/v7/recyclerview/res \
$(TOP)/prebuilts/sdk/current/support/v17/leanback/res \
$(LOCAL_PATH)/res \
- $(LOCAL_PATH)/res_leanback \
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-annotations \
@@ -26,4 +25,6 @@ LOCAL_AAPT_FLAGS := --auto-add-overlay \
--extra-packages android.support.v17.leanback \
+include $(LOCAL_PATH)/buildconfig.mk
+
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/common/BuildConfig.java.in b/common/BuildConfig.java.in
new file mode 100644
index 00000000..f4631340
--- /dev/null
+++ b/common/BuildConfig.java.in
@@ -0,0 +1,8 @@
+/* This file is auto generated. Do not modify. */
+package com.android.tv.common;
+
+public final class BuildConfig {
+ public static final boolean DEBUG = %DEBUG%;
+ public static final boolean ENG = %ENG%;
+ private BuildConfig() {}
+} \ No newline at end of file
diff --git a/common/buildconfig.mk b/common/buildconfig.mk
index 93fcbcb0..cece7f2b 100644
--- a/common/buildconfig.mk
+++ b/common/buildconfig.mk
@@ -17,27 +17,23 @@
# Emulate gradles BuildConfig.java
ifeq "$(TARGET_BUILD_VARIANT)" "eng"
- BC_DEBUG_STATUS := "true"
+ BC_DEBUG_STATUS := true
else ifeq "$(TARGET_BUILD_VARIANT)" "userdebug"
- BC_DEBUG_STATUS := "true"
+ BC_DEBUG_STATUS := true
else
- BC_DEBUG_STATUS := "false"
+ BC_DEBUG_STATUS := false
endif
ifeq "$(TARGET_BUILD_VARIANT)" "eng"
- BC_ENG_STATUS := "true"
+ BC_ENG_STATUS := true
else
- BC_ENG_STATUS := "false"
+ BC_ENG_STATUS := false
endif
-$(BC_OUT_DIR)/$(LOCAL_BUILDCONFIG_CLASS): FORCE
- echo "/**" > $(BC_OUT_DIR)/$(LOCAL_BUILDCONFIG_CLASS)
- echo "* Automatically generated file. DO NOT MODIFY" >> $(BC_OUT_DIR)/$(LOCAL_BUILDCONFIG_CLASS)
- echo "*/" >> $(BC_OUT_DIR)/$(LOCAL_BUILDCONFIG_CLASS)
- echo "package "$(BC_APPLICATION_ID)";" >> $(BC_OUT_DIR)/$(LOCAL_BUILDCONFIG_CLASS)
- echo "public final class BuildConfig {" >> $(BC_OUT_DIR)/$(LOCAL_BUILDCONFIG_CLASS)
- echo " public static final boolean DEBUG = "$(BC_DEBUG_STATUS)";" >> $(BC_OUT_DIR)/$(LOCAL_BUILDCONFIG_CLASS)
- echo " public static final boolean ENG = "$(BC_ENG_STATUS)";" >> $(BC_OUT_DIR)/$(LOCAL_BUILDCONFIG_CLASS)
- echo " private BuildConfig() {}" >> $(BC_OUT_DIR)/$(LOCAL_BUILDCONFIG_CLASS)
- echo "}" >> $(BC_OUT_DIR)/$(LOCAL_BUILDCONFIG_CLASS)
-FORCE:
+gen := $(local-generated-sources-dir)/$(TARGET_BUILD_VARIANT)/BuildConfig.java
+$(gen): PRIVATE_CUSTOM_TOOL = sed -e \
+ 's/%DEBUG%/$(BC_DEBUG_STATUS)/;s/%ENG%/$(BC_ENG_STATUS)/' \
+ $< > $@
+$(gen) : $(LOCAL_PATH)/BuildConfig.java.in
+ $(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(gen) \ No newline at end of file
diff --git a/common/res/drawable/setup_action_button_done.xml b/common/res/drawable/setup_action_button_done.xml
index 64592b69..9015367a 100644
--- a/common/res/drawable/setup_action_button_done.xml
+++ b/common/res/drawable/setup_action_button_done.xml
@@ -15,7 +15,6 @@
~ limitations under the License.
-->
-<!-- STOPSHIP: Remove this resource once setup page is implemented correctly. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_focused="true">
<shape>
diff --git a/common/res/values-af/strings.xml b/common/res/values-af/strings.xml
index 11b3990a..83ad9ae6 100644
--- a/common/res/values-af/strings.xml
+++ b/common/res/values-af/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Klaar"</string>
+ <string name="episode_format" msgid="5985513854870276591">"S<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: ep. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-am/strings.xml b/common/res/values-am/strings.xml
index b72ab7d0..17442b05 100644
--- a/common/res/values-am/strings.xml
+++ b/common/res/values-am/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"ተከናውኗል"</string>
+ <string name="episode_format" msgid="5985513854870276591">"ምዕ <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>፦ ክፍል <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g><xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-ar/strings.xml b/common/res/values-ar/strings.xml
index d234a33b..27d6ce66 100644
--- a/common/res/values-ar/strings.xml
+++ b/common/res/values-ar/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"تم"</string>
+ <string name="episode_format" msgid="5985513854870276591">"الموسم <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: الحلقة <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-az-rAZ/strings.xml b/common/res/values-az-rAZ/strings.xml
index 4770dd1e..35e847a9 100644
--- a/common/res/values-az-rAZ/strings.xml
+++ b/common/res/values-az-rAZ/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Hazırdır"</string>
+ <string name="episode_format" msgid="5985513854870276591">"S<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-bg/strings.xml b/common/res/values-bg/strings.xml
index a8d42c3a..87c5c919 100644
--- a/common/res/values-bg/strings.xml
+++ b/common/res/values-bg/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Готово"</string>
+ <string name="episode_format" msgid="5985513854870276591">"Сезон <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: Еп. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> – „<xliff:g id="EPISODE_TITLE">%3$s</xliff:g>“"</string>
</resources>
diff --git a/common/res/values-bn-rBD/strings.xml b/common/res/values-bn-rBD/strings.xml
index ef20cd3f..5a7614f8 100644
--- a/common/res/values-bn-rBD/strings.xml
+++ b/common/res/values-bn-rBD/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"সম্পন্ন"</string>
+ <string name="episode_format" msgid="5985513854870276591">"সিঃ<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: এপিঃ <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-ca/strings.xml b/common/res/values-ca/strings.xml
index 790c58aa..c3c62cc0 100644
--- a/common/res/values-ca/strings.xml
+++ b/common/res/values-ca/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Fet"</string>
+ <string name="episode_format" msgid="5985513854870276591">"Temporada <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: episodi <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g>, <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-cs/strings.xml b/common/res/values-cs/strings.xml
index 6bc7a34d..8550e53c 100644
--- a/common/res/values-cs/strings.xml
+++ b/common/res/values-cs/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Hotovo"</string>
+ <string name="episode_format" msgid="5985513854870276591">"S<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-da/strings.xml b/common/res/values-da/strings.xml
index a3ff65d6..ccc2d53c 100644
--- a/common/res/values-da/strings.xml
+++ b/common/res/values-da/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Udført"</string>
+ <string name="episode_format" msgid="5985513854870276591">"S. <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>, afsn. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g>, <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-de/strings.xml b/common/res/values-de/strings.xml
index 08cecfa4..965c63fb 100644
--- a/common/res/values-de/strings.xml
+++ b/common/res/values-de/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Fertig"</string>
+ <string name="episode_format" msgid="5985513854870276591">"Staffel <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: Folge <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-el/strings.xml b/common/res/values-el/strings.xml
index 8d178e29..361a0d55 100644
--- a/common/res/values-el/strings.xml
+++ b/common/res/values-el/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Τέλος"</string>
+ <string name="episode_format" msgid="5985513854870276591">"Σεζ. <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: Επ. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-en-rAU/strings.xml b/common/res/values-en-rAU/strings.xml
index 5b24567c..423dc885 100644
--- a/common/res/values-en-rAU/strings.xml
+++ b/common/res/values-en-rAU/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Finished"</string>
+ <string name="episode_format" msgid="5985513854870276591">"S<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-en-rGB/strings.xml b/common/res/values-en-rGB/strings.xml
index 5b24567c..423dc885 100644
--- a/common/res/values-en-rGB/strings.xml
+++ b/common/res/values-en-rGB/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Finished"</string>
+ <string name="episode_format" msgid="5985513854870276591">"S<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-en-rIN/strings.xml b/common/res/values-en-rIN/strings.xml
index 5b24567c..423dc885 100644
--- a/common/res/values-en-rIN/strings.xml
+++ b/common/res/values-en-rIN/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Finished"</string>
+ <string name="episode_format" msgid="5985513854870276591">"S<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-es-rUS/strings.xml b/common/res/values-es-rUS/strings.xml
index 76f39ab4..ac34b685 100644
--- a/common/res/values-es-rUS/strings.xml
+++ b/common/res/values-es-rUS/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Listo"</string>
+ <string name="episode_format" msgid="5985513854870276591">"Temporada <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>. Episodio <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g>: <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-es/strings.xml b/common/res/values-es/strings.xml
index 76f39ab4..5d21f0a5 100644
--- a/common/res/values-es/strings.xml
+++ b/common/res/values-es/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Listo"</string>
+ <string name="episode_format" msgid="5985513854870276591">"Temporada <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: episodio <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-et-rEE/strings.xml b/common/res/values-et-rEE/strings.xml
index 58a71664..d97e467a 100644
--- a/common/res/values-et-rEE/strings.xml
+++ b/common/res/values-et-rEE/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Valmis"</string>
+ <string name="episode_format" msgid="5985513854870276591">"<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>. hooaeg: jagu <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-eu-rES/strings.xml b/common/res/values-eu-rES/strings.xml
index bdc86896..4e84f936 100644
--- a/common/res/values-eu-rES/strings.xml
+++ b/common/res/values-eu-rES/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Eginda"</string>
+ <string name="episode_format" msgid="5985513854870276591">"<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>. denboraldiko <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g>. atala: <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-fa/strings.xml b/common/res/values-fa/strings.xml
index 919c37fe..3f531a2b 100644
--- a/common/res/values-fa/strings.xml
+++ b/common/res/values-fa/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"تمام"</string>
+ <string name="episode_format" msgid="5985513854870276591">"ف<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: ق <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-fi/strings.xml b/common/res/values-fi/strings.xml
index 58a71664..566ca328 100644
--- a/common/res/values-fi/strings.xml
+++ b/common/res/values-fi/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Valmis"</string>
+ <string name="episode_format" msgid="5985513854870276591">"Kausi <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: jakso <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g>, <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-fr-rCA/strings.xml b/common/res/values-fr-rCA/strings.xml
index a5334fed..59d6bbf2 100644
--- a/common/res/values-fr-rCA/strings.xml
+++ b/common/res/values-fr-rCA/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Terminé"</string>
+ <string name="episode_format" msgid="5985513854870276591">"Saison <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>, épisode <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g>, « <xliff:g id="EPISODE_TITLE">%3$s</xliff:g> »"</string>
</resources>
diff --git a/common/res/values-fr/strings.xml b/common/res/values-fr/strings.xml
index a018b053..10a0b5ee 100644
--- a/common/res/values-fr/strings.xml
+++ b/common/res/values-fr/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"OK"</string>
+ <string name="episode_format" msgid="5985513854870276591">"S<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>, ép. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> : <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-gl-rES/strings.xml b/common/res/values-gl-rES/strings.xml
index 04167ec0..356e84d2 100644
--- a/common/res/values-gl-rES/strings.xml
+++ b/common/res/values-gl-rES/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Feito"</string>
+ <string name="episode_format" msgid="5985513854870276591">"T<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: ep. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-hi/strings.xml b/common/res/values-hi/strings.xml
index 93ddf14d..2aaad475 100644
--- a/common/res/values-hi/strings.xml
+++ b/common/res/values-hi/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"हो गया"</string>
+ <string name="episode_format" msgid="5985513854870276591">"सीज़न<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: एपिसोड <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-hr/strings.xml b/common/res/values-hr/strings.xml
index e359fa47..6ab8b924 100644
--- a/common/res/values-hr/strings.xml
+++ b/common/res/values-hr/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Gotovo"</string>
+ <string name="episode_format" msgid="5985513854870276591">"S. <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: ep. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g>, <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-hu/strings.xml b/common/res/values-hu/strings.xml
index c7edacc5..d73e295c 100644
--- a/common/res/values-hu/strings.xml
+++ b/common/res/values-hu/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Kész"</string>
+ <string name="episode_format" msgid="5985513854870276591">"<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>. évad, <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g>. rész: <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-hy-rAM/strings.xml b/common/res/values-hy-rAM/strings.xml
index 2832c576..b9b2b5d6 100644
--- a/common/res/values-hy-rAM/strings.xml
+++ b/common/res/values-hy-rAM/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Պատրաստ է"</string>
+ <string name="episode_format" msgid="5985513854870276591">"Ե<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>՝ դրվ. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-in/strings.xml b/common/res/values-in/strings.xml
index 315e2eac..7435341c 100644
--- a/common/res/values-in/strings.xml
+++ b/common/res/values-in/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Selesai"</string>
+ <string name="episode_format" msgid="5985513854870276591">"S<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-is-rIS/strings.xml b/common/res/values-is-rIS/strings.xml
index a2d8da79..f699ea19 100644
--- a/common/res/values-is-rIS/strings.xml
+++ b/common/res/values-is-rIS/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Lokið"</string>
+ <string name="episode_format" msgid="5985513854870276591">"S<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: Þ. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-it/strings.xml b/common/res/values-it/strings.xml
index ba1ecc42..48a7751b 100644
--- a/common/res/values-it/strings.xml
+++ b/common/res/values-it/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Fine"</string>
+ <string name="episode_format" msgid="5985513854870276591">"Stag. <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: Punt. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-iw/strings.xml b/common/res/values-iw/strings.xml
index 106e5453..b0e49d7f 100644
--- a/common/res/values-iw/strings.xml
+++ b/common/res/values-iw/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"סיום"</string>
+ <string name="episode_format" msgid="5985513854870276591">"עונה <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: פרק <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-ja/strings.xml b/common/res/values-ja/strings.xml
index 9cf0ba31..d5f072f3 100644
--- a/common/res/values-ja/strings.xml
+++ b/common/res/values-ja/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"完了"</string>
+ <string name="episode_format" msgid="5985513854870276591">"シーズン <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: エピソード <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g>「<xliff:g id="EPISODE_TITLE">%3$s</xliff:g>」"</string>
</resources>
diff --git a/common/res/values-ka-rGE/strings.xml b/common/res/values-ka-rGE/strings.xml
index d7297b9d..b209520a 100644
--- a/common/res/values-ka-rGE/strings.xml
+++ b/common/res/values-ka-rGE/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"მზადაა"</string>
+ <string name="episode_format" msgid="5985513854870276591">"სეზ. <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>, ეპ. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g>: <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-kk-rKZ/strings.xml b/common/res/values-kk-rKZ/strings.xml
index 9994dc20..5f732dc0 100644
--- a/common/res/values-kk-rKZ/strings.xml
+++ b/common/res/values-kk-rKZ/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Орындалды"</string>
+ <string name="episode_format" msgid="5985513854870276591">"<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>-маусым, <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g>-эпизод: <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-km-rKH/strings.xml b/common/res/values-km-rKH/strings.xml
index f12663fb..0a83185a 100644
--- a/common/res/values-km-rKH/strings.xml
+++ b/common/res/values-km-rKH/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"រួចរាល់"</string>
+ <string name="episode_format" msgid="5985513854870276591">"រដូវកាលទី <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>៖ វគ្គ <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-kn-rIN/strings.xml b/common/res/values-kn-rIN/strings.xml
index 589ff1be..27821741 100644
--- a/common/res/values-kn-rIN/strings.xml
+++ b/common/res/values-kn-rIN/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"ಮುಗಿದಿದೆ"</string>
+ <string name="episode_format" msgid="5985513854870276591">"ಸೀ<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: ಸಂಚಿಕೆ <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-ko/strings.xml b/common/res/values-ko/strings.xml
index e2e47fa4..e5ca8d69 100644
--- a/common/res/values-ko/strings.xml
+++ b/common/res/values-ko/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"완료"</string>
+ <string name="episode_format" msgid="5985513854870276591">"시즌 <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: 에피소드 <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-ky-rKG/strings.xml b/common/res/values-ky-rKG/strings.xml
index 3397fc32..333232af 100644
--- a/common/res/values-ky-rKG/strings.xml
+++ b/common/res/values-ky-rKG/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Бүттү"</string>
+ <string name="episode_format" msgid="5985513854870276591">"<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>-м.: Эп. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-lo-rLA/strings.xml b/common/res/values-lo-rLA/strings.xml
index 88653b54..a448b0b8 100644
--- a/common/res/values-lo-rLA/strings.xml
+++ b/common/res/values-lo-rLA/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"ສຳເລັດແລ້ວ"</string>
+ <string name="episode_format" msgid="5985513854870276591">"S<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: ຕອນ <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-lt/strings.xml b/common/res/values-lt/strings.xml
index 6f51b3fc..46e4adef 100644
--- a/common/res/values-lt/strings.xml
+++ b/common/res/values-lt/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Atlikta"</string>
+ <string name="episode_format" msgid="5985513854870276591">"<xliff:g id="SEASON_NUMBER">%1$s</xliff:g> sezonas: <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> serija „<xliff:g id="EPISODE_TITLE">%3$s</xliff:g>“"</string>
</resources>
diff --git a/common/res/values-lv/strings.xml b/common/res/values-lv/strings.xml
index 8660efbd..0b2c91d3 100644
--- a/common/res/values-lv/strings.xml
+++ b/common/res/values-lv/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Gatavs"</string>
+ <string name="episode_format" msgid="5985513854870276591">"<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>. sezona, <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g>. sērija “<xliff:g id="EPISODE_TITLE">%3$s</xliff:g>”"</string>
</resources>
diff --git a/common/res/values-mk-rMK/strings.xml b/common/res/values-mk-rMK/strings.xml
index a8d42c3a..406bd745 100644
--- a/common/res/values-mk-rMK/strings.xml
+++ b/common/res/values-mk-rMK/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Готово"</string>
+ <string name="episode_format" msgid="5985513854870276591">"Сез. <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: Еп. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-ml-rIN/strings.xml b/common/res/values-ml-rIN/strings.xml
index 81e9a8c6..5b40e844 100644
--- a/common/res/values-ml-rIN/strings.xml
+++ b/common/res/values-ml-rIN/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"പൂർത്തിയായി"</string>
+ <string name="episode_format" msgid="5985513854870276591">"സീസൺ<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: എപ്പിസോഡ് <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-mn-rMN/strings.xml b/common/res/values-mn-rMN/strings.xml
index ac4435f3..3a6295b0 100644
--- a/common/res/values-mn-rMN/strings.xml
+++ b/common/res/values-mn-rMN/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Дууссан"</string>
+ <string name="episode_format" msgid="5985513854870276591">"Бүлэг<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: Анги. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-mr-rIN/strings.xml b/common/res/values-mr-rIN/strings.xml
index 82edcb03..2709733e 100644
--- a/common/res/values-mr-rIN/strings.xml
+++ b/common/res/values-mr-rIN/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"पूर्ण झाले"</string>
+ <string name="episode_format" msgid="5985513854870276591">"हंगाम<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: भाग. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-ms-rMY/strings.xml b/common/res/values-ms-rMY/strings.xml
index 315e2eac..c0372b30 100644
--- a/common/res/values-ms-rMY/strings.xml
+++ b/common/res/values-ms-rMY/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Selesai"</string>
+ <string name="episode_format" msgid="5985513854870276591">"M<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-my-rMM/strings.xml b/common/res/values-my-rMM/strings.xml
index a7faa7d9..6577fbed 100644
--- a/common/res/values-my-rMM/strings.xml
+++ b/common/res/values-my-rMM/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"ပြီးပါပြီ"</string>
+ <string name="episode_format" msgid="5985513854870276591">"ဇာတ်လမ်းတွဲ<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>− အပိုင်းငယ် <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-nb/strings.xml b/common/res/values-nb/strings.xml
index 3ff3ede1..e9bf0493 100644
--- a/common/res/values-nb/strings.xml
+++ b/common/res/values-nb/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Ferdig"</string>
+ <string name="episode_format" msgid="5985513854870276591">"Sesong <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-ne-rNP/strings.xml b/common/res/values-ne-rNP/strings.xml
index 4a08d5ef..265ec91d 100644
--- a/common/res/values-ne-rNP/strings.xml
+++ b/common/res/values-ne-rNP/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"सम्पन्न भयो"</string>
+ <string name="episode_format" msgid="5985513854870276591">"सिजन <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: एपिसोड <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-nl/strings.xml b/common/res/values-nl/strings.xml
index 8756e624..cc3ee7eb 100644
--- a/common/res/values-nl/strings.xml
+++ b/common/res/values-nl/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Gereed"</string>
+ <string name="episode_format" msgid="5985513854870276591">"S. <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: afl. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-pl/strings.xml b/common/res/values-pl/strings.xml
index 97beca37..ddcd5614 100644
--- a/common/res/values-pl/strings.xml
+++ b/common/res/values-pl/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Gotowe"</string>
+ <string name="episode_format" msgid="5985513854870276591">"Sez. <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>, odc. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g>: <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-pt-rPT/strings.xml b/common/res/values-pt-rPT/strings.xml
index a967a341..b08d7084 100644
--- a/common/res/values-pt-rPT/strings.xml
+++ b/common/res/values-pt-rPT/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Concluído"</string>
+ <string name="episode_format" msgid="5985513854870276591">"T<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: ep. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-pt/strings.xml b/common/res/values-pt/strings.xml
index a967a341..a6d69e48 100644
--- a/common/res/values-pt/strings.xml
+++ b/common/res/values-pt/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Concluído"</string>
+ <string name="episode_format" msgid="5985513854870276591">"S<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-ro/strings.xml b/common/res/values-ro/strings.xml
index 2cbda306..5cb24634 100644
--- a/common/res/values-ro/strings.xml
+++ b/common/res/values-ro/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Terminat"</string>
+ <string name="episode_format" msgid="5985513854870276591">"Sezonul <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>, episodul <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g>: <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-ru/strings.xml b/common/res/values-ru/strings.xml
index a8d42c3a..52e06816 100644
--- a/common/res/values-ru/strings.xml
+++ b/common/res/values-ru/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Готово"</string>
+ <string name="episode_format" msgid="5985513854870276591">"Сезон <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: серия <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g>, \"<xliff:g id="EPISODE_TITLE">%3$s</xliff:g>\""</string>
</resources>
diff --git a/common/res/values-si-rLK/strings.xml b/common/res/values-si-rLK/strings.xml
index 6293bbec..4b62ae21 100644
--- a/common/res/values-si-rLK/strings.xml
+++ b/common/res/values-si-rLK/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"නිමයි"</string>
+ <string name="episode_format" msgid="5985513854870276591">"වාරය<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: කථාංගය <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-sk/strings.xml b/common/res/values-sk/strings.xml
index 6bc7a34d..aa0345ff 100644
--- a/common/res/values-sk/strings.xml
+++ b/common/res/values-sk/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Hotovo"</string>
+ <string name="episode_format" msgid="5985513854870276591">"S <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: ep. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-sl/strings.xml b/common/res/values-sl/strings.xml
index 29c414ac..06e4218a 100644
--- a/common/res/values-sl/strings.xml
+++ b/common/res/values-sl/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Končano"</string>
+ <string name="episode_format" msgid="5985513854870276591">"<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>. sezona – <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g>. epizoda: <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-sr/strings.xml b/common/res/values-sr/strings.xml
index a8d42c3a..780d096e 100644
--- a/common/res/values-sr/strings.xml
+++ b/common/res/values-sr/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Готово"</string>
+ <string name="episode_format" msgid="5985513854870276591">"<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>. серијал: <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g>. епизода, <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-sv/strings.xml b/common/res/values-sv/strings.xml
index ded93590..4cf53a0c 100644
--- a/common/res/values-sv/strings.xml
+++ b/common/res/values-sv/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Klar"</string>
+ <string name="episode_format" msgid="5985513854870276591">"Säsong <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: avsnitt <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> – <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-sw/strings.xml b/common/res/values-sw/strings.xml
index 1cce7469..7ad2f1e5 100644
--- a/common/res/values-sw/strings.xml
+++ b/common/res/values-sw/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Nimemaliza"</string>
+ <string name="episode_format" msgid="5985513854870276591">"Msimu wa <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: Kipindi cha <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-ta-rIN/strings.xml b/common/res/values-ta-rIN/strings.xml
index df4ce9ab..fb7b541a 100644
--- a/common/res/values-ta-rIN/strings.xml
+++ b/common/res/values-ta-rIN/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"முடிந்தது"</string>
+ <string name="episode_format" msgid="5985513854870276591">"சீ<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: எபி. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-te-rIN/strings.xml b/common/res/values-te-rIN/strings.xml
index 527f9e02..be10c428 100644
--- a/common/res/values-te-rIN/strings.xml
+++ b/common/res/values-te-rIN/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"పూర్తయింది"</string>
+ <string name="episode_format" msgid="5985513854870276591">"సీ<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: ఎపి. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-th/strings.xml b/common/res/values-th/strings.xml
index ca4158ef..3acaefd0 100644
--- a/common/res/values-th/strings.xml
+++ b/common/res/values-th/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"เสร็จสิ้น"</string>
+ <string name="episode_format" msgid="5985513854870276591">"ซีซัน <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: ตอนที่ <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-tl/strings.xml b/common/res/values-tl/strings.xml
index 4b53b66e..0e176018 100644
--- a/common/res/values-tl/strings.xml
+++ b/common/res/values-tl/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Tapos Na"</string>
+ <string name="episode_format" msgid="5985513854870276591">"S<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-tr/strings.xml b/common/res/values-tr/strings.xml
index 34e0cb58..63000a1d 100644
--- a/common/res/values-tr/strings.xml
+++ b/common/res/values-tr/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Bitti"</string>
+ <string name="episode_format" msgid="5985513854870276591">"S<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: Böl. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-uk/strings.xml b/common/res/values-uk/strings.xml
index a8d42c3a..b229340b 100644
--- a/common/res/values-uk/strings.xml
+++ b/common/res/values-uk/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Готово"</string>
+ <string name="episode_format" msgid="5985513854870276591">"Сезон <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: серія <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> \"<xliff:g id="EPISODE_TITLE">%3$s</xliff:g>\""</string>
</resources>
diff --git a/common/res/values-ur-rPK/strings.xml b/common/res/values-ur-rPK/strings.xml
index 87b1cec4..0baab923 100644
--- a/common/res/values-ur-rPK/strings.xml
+++ b/common/res/values-ur-rPK/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"ہو گیا"</string>
+ <string name="episode_format" msgid="5985513854870276591">"سیزن <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: قسط <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-uz-rUZ/strings.xml b/common/res/values-uz-rUZ/strings.xml
index 67468cec..d595a5ff 100644
--- a/common/res/values-uz-rUZ/strings.xml
+++ b/common/res/values-uz-rUZ/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Tayyor"</string>
+ <string name="episode_format" msgid="5985513854870276591">"<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>-fasl <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g>-qism: <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-vi/strings.xml b/common/res/values-vi/strings.xml
index 756fb991..bad8efcb 100644
--- a/common/res/values-vi/strings.xml
+++ b/common/res/values-vi/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Xong"</string>
+ <string name="episode_format" msgid="5985513854870276591">"Phần <xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: Tập <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-zh-rCN/strings.xml b/common/res/values-zh-rCN/strings.xml
index b8bd7736..0865cb50 100644
--- a/common/res/values-zh-rCN/strings.xml
+++ b/common/res/values-zh-rCN/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"完成"</string>
+ <string name="episode_format" msgid="5985513854870276591">"第 <xliff:g id="SEASON_NUMBER">%1$s</xliff:g> 季第 <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> 集《<xliff:g id="EPISODE_TITLE">%3$s</xliff:g>》"</string>
</resources>
diff --git a/common/res/values-zh-rHK/strings.xml b/common/res/values-zh-rHK/strings.xml
index b8bd7736..d8e8a030 100644
--- a/common/res/values-zh-rHK/strings.xml
+++ b/common/res/values-zh-rHK/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"完成"</string>
+ <string name="episode_format" msgid="5985513854870276591">"第 <xliff:g id="SEASON_NUMBER">%1$s</xliff:g> 季:第 <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> 集 <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values-zh-rTW/strings.xml b/common/res/values-zh-rTW/strings.xml
index b8bd7736..4c318bc1 100644
--- a/common/res/values-zh-rTW/strings.xml
+++ b/common/res/values-zh-rTW/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"完成"</string>
+ <string name="episode_format" msgid="5985513854870276591">"第 <xliff:g id="SEASON_NUMBER">%1$s</xliff:g> 季:第 <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> 集「<xliff:g id="EPISODE_TITLE">%3$s</xliff:g>」"</string>
</resources>
diff --git a/common/res/values-zu/strings.xml b/common/res/values-zu/strings.xml
index 91b5042c..138dca92 100644
--- a/common/res/values-zu/strings.xml
+++ b/common/res/values-zu/strings.xml
@@ -18,4 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done" msgid="298287050387266501">"Kwenziwe"</string>
+ <string name="episode_format" msgid="5985513854870276591">"S<xliff:g id="SEASON_NUMBER">%1$s</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$s</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
</resources>
diff --git a/common/res/values/strings.xml b/common/res/values/strings.xml
index f337af42..0d1d3514 100644
--- a/common/res/values/strings.xml
+++ b/common/res/values/strings.xml
@@ -15,6 +15,8 @@
~ limitations under the License.
-->
-<resources>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done">Done</string>
+ <!-- The episode title format displayed on the info banner. For example, "S1: Ep. 1 Winter is coming". -->
+ <string name="episode_format">S<xliff:g id="season_number" example="1">%1$s</xliff:g>: Ep. <xliff:g id="episode_number" example="1">%2$s</xliff:g> <xliff:g id="episode_title" example="Winder is coming">%3$s</xliff:g></string>
</resources>
diff --git a/common/res_leanback/animator/lb_onboarding_logo_exit.xml b/common/res_leanback/animator/lb_onboarding_logo_exit.xml
deleted file mode 100644
index 40b618e6..00000000
--- a/common/res_leanback/animator/lb_onboarding_logo_exit.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <objectAnimator
- android:propertyName="alpha"
- android:valueFrom="1.0"
- android:valueTo="0.0"
- android:duration="666"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/common/res_leanback/animator/lb_onboarding_page_indicator_enter.xml b/common/res_leanback/animator/lb_onboarding_page_indicator_enter.xml
deleted file mode 100644
index e9fc46ef..00000000
--- a/common/res_leanback/animator/lb_onboarding_page_indicator_enter.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <objectAnimator
- android:propertyName="alpha"
- android:valueFrom="0.0"
- android:valueTo="1.0"
- android:duration="500"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/common/res_leanback/animator/lb_onboarding_page_indicator_fade_in.xml b/common/res_leanback/animator/lb_onboarding_page_indicator_fade_in.xml
deleted file mode 100644
index f21fc23b..00000000
--- a/common/res_leanback/animator/lb_onboarding_page_indicator_fade_in.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <objectAnimator
- android:propertyName="alpha"
- android:valueFrom="0.0"
- android:valueTo="1.0"
- android:duration="417"
- android:interpolator="@android:anim/decelerate_interpolator" />
-</set>
diff --git a/common/res_leanback/animator/lb_onboarding_page_indicator_fade_out.xml b/common/res_leanback/animator/lb_onboarding_page_indicator_fade_out.xml
deleted file mode 100644
index 4c69c5d0..00000000
--- a/common/res_leanback/animator/lb_onboarding_page_indicator_fade_out.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <objectAnimator
- android:propertyName="alpha"
- android:valueFrom="1.0"
- android:valueTo="0.0"
- android:duration="417"
- android:interpolator="@android:anim/decelerate_interpolator" />
-</set>
diff --git a/common/res_leanback/animator/lb_onboarding_start_button_fade_in.xml b/common/res_leanback/animator/lb_onboarding_start_button_fade_in.xml
deleted file mode 100644
index 4125622b..00000000
--- a/common/res_leanback/animator/lb_onboarding_start_button_fade_in.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <objectAnimator
- android:propertyName="alpha"
- android:valueFrom="0.0"
- android:valueTo="1.0"
- android:duration="417"
- android:interpolator="@android:anim/decelerate_interpolator" />
- <objectAnimator
- android:propertyName="translationX"
- android:valueFrom="16dp"
- android:valueTo="0dp"
- android:duration="417"
- android:interpolator="@android:anim/decelerate_interpolator" />
-</set>
diff --git a/common/res_leanback/animator/lb_onboarding_start_button_fade_out.xml b/common/res_leanback/animator/lb_onboarding_start_button_fade_out.xml
deleted file mode 100644
index 26590ecd..00000000
--- a/common/res_leanback/animator/lb_onboarding_start_button_fade_out.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <objectAnimator
- android:propertyName="alpha"
- android:valueFrom="1.0"
- android:valueTo="0.0"
- android:duration="417"
- android:interpolator="@android:anim/decelerate_interpolator" />
- <objectAnimator
- android:propertyName="translationX"
- android:valueFrom="0dp"
- android:valueTo="16dp"
- android:duration="417"
- android:interpolator="@android:anim/decelerate_interpolator" />
-</set>
diff --git a/common/res_leanback/animator/lb_onboarding_title_enter.xml b/common/res_leanback/animator/lb_onboarding_title_enter.xml
deleted file mode 100644
index 5f26cdd1..00000000
--- a/common/res_leanback/animator/lb_onboarding_title_enter.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <objectAnimator
- android:propertyName="alpha"
- android:valueFrom="0.0"
- android:valueTo="1.0"
- android:duration="533"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
- <objectAnimator
- android:propertyName="translationY"
- android:valueFrom="60dp"
- android:valueTo="0dp"
- android:duration="533"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/common/res_leanback/animator/lb_page_indicator_dot_hide.xml b/common/res_leanback/animator/lb_page_indicator_dot_hide.xml
deleted file mode 100644
index fdd60342..00000000
--- a/common/res_leanback/animator/lb_page_indicator_dot_hide.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <objectAnimator
- android:propertyName="alpha"
- android:valueFrom="1.0"
- android:valueTo="0.0"
- android:duration="167"
- android:interpolator="@android:anim/decelerate_interpolator" />
- <objectAnimator
- android:propertyName="diameter"
- android:valueFrom="36dp"
- android:valueTo="10dp"
- android:duration="417"
- android:interpolator="@android:anim/decelerate_interpolator" />
- <objectAnimator
- android:propertyName="translationX"
- android:valueFrom="-16dp"
- android:valueTo="0dp"
- android:duration="417"
- android:interpolator="@android:anim/decelerate_interpolator" />
-</set>
diff --git a/common/res_leanback/animator/lb_page_indicator_dot_show.xml b/common/res_leanback/animator/lb_page_indicator_dot_show.xml
deleted file mode 100644
index 392fb2c6..00000000
--- a/common/res_leanback/animator/lb_page_indicator_dot_show.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <objectAnimator
- android:propertyName="alpha"
- android:valueFrom="0.0"
- android:valueTo="1.0"
- android:duration="167"
- android:interpolator="@android:anim/decelerate_interpolator" />
- <objectAnimator
- android:propertyName="diameter"
- android:valueFrom="10dp"
- android:valueTo="36dp"
- android:duration="417"
- android:interpolator="@android:anim/decelerate_interpolator" />
- <objectAnimator
- android:propertyName="translationX"
- android:valueFrom="-16dp"
- android:valueTo="0dp"
- android:duration="417"
- android:interpolator="@android:anim/decelerate_interpolator" />
-</set>
diff --git a/common/res_leanback/drawable-xhdpi/lb_ic_nav_arrow.png b/common/res_leanback/drawable-xhdpi/lb_ic_nav_arrow.png
deleted file mode 100644
index 04578a75..00000000
--- a/common/res_leanback/drawable-xhdpi/lb_ic_nav_arrow.png
+++ /dev/null
Binary files differ
diff --git a/common/res_leanback/layout/lb_onboarding_fragment.xml b/common/res_leanback/layout/lb_onboarding_fragment.xml
deleted file mode 100644
index 20489ed0..00000000
--- a/common/res_leanback/layout/lb_onboarding_fragment.xml
+++ /dev/null
@@ -1,120 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/onboarding_fragment_root"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipChildren="false"
- android:clipToPadding="false">
-
- <FrameLayout
- android:id="@+id/background_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
-
- <LinearLayout
- android:id="@+id/page_container"
- android:layout_width="@dimen/lb_onboarding_content_width"
- android:layout_height="@dimen/lb_onboarding_header_height"
- android:layout_alignParentTop="true"
- android:layout_centerHorizontal="true"
- android:layout_marginTop="@dimen/lb_onboarding_header_margin_top"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:orientation="vertical"
- android:visibility="gone">
- <TextView
- android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="0.5"
- android:layout_marginBottom="3dp"
- android:fontFamily="sans-serif-light"
- android:gravity="center"
- android:textColor="#EEEEEE"
- android:textSize="34sp"
- android:lineSpacingExtra="14sp"/>
- <TextView
- android:id="@+id/description"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="0.5"
- android:layout_marginTop="3dp"
- android:fontFamily="sans-serif-light"
- android:gravity="center"
- android:textColor="#B3EEEEEE"
- android:textSize="14sp"
- android:lineSpacingExtra="10sp" />
- </LinearLayout>
-
- <FrameLayout
- android:id="@+id/content_container"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_above="@+id/page_indicator"
- android:layout_below="@id/page_container"
- android:layout_centerHorizontal="true"
- android:visibility="gone" />
-
- <com.android.tv.common.ui.setup.leanback.PagingIndicator
- android:id="@id/page_indicator"
- android:layout_width="@dimen/lb_onboarding_content_width"
- android:layout_height="@dimen/lb_onboarding_navigation_height"
- android:layout_centerHorizontal="true"
- android:layout_alignParentBottom="true"
- android:layout_marginBottom="58dp"
- android:focusable="true"
- android:contentDescription="@string/lb_onboarding_accessibility_next"
- android:visibility="gone" />
-
- <Button
- android:id="@+id/button_start"
- android:layout_width="wrap_content"
- android:layout_height="36dp"
- android:layout_centerHorizontal="true"
- android:layout_alignParentBottom="true"
- android:layout_marginBottom="62dp"
- android:alpha="0.0"
- android:background="@drawable/lb_onboarding_start_button_background"
- android:elevation="1.5dp"
- android:fontFamily="sans-serif"
- android:gravity="center_vertical"
- android:paddingEnd="24dp"
- android:paddingStart="24dp"
- android:stateListAnimator="@null"
- android:text="@string/lb_onboarding_get_started"
- android:textAllCaps="true"
- android:textColor="#014269"
- android:textSize="16sp"
- android:visibility="gone"/>
-
- <FrameLayout
- android:id="@+id/foreground_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
-
- <ImageView
- android:id="@+id/logo"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerInParent="true"
- android:contentDescription="@null" />
-
-</RelativeLayout>
diff --git a/common/res_leanback/values-ar/strings.xml b/common/res_leanback/values-ar/strings.xml
deleted file mode 100644
index f7ba5716..00000000
--- a/common/res_leanback/values-ar/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"البدء"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"التالية"</string>
-</resources>
diff --git a/common/res_leanback/values-az-rAZ/strings.xml b/common/res_leanback/values-az-rAZ/strings.xml
deleted file mode 100644
index 2293accf..00000000
--- a/common/res_leanback/values-az-rAZ/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"BAŞLAYIN"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Növbəti"</string>
-</resources>
diff --git a/common/res_leanback/values-bg/strings.xml b/common/res_leanback/values-bg/strings.xml
deleted file mode 100644
index a6c3cc03..00000000
--- a/common/res_leanback/values-bg/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"ПЪРВИ СТЪПКИ"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Напред"</string>
-</resources>
diff --git a/common/res_leanback/values-bn-rBD/strings.xml b/common/res_leanback/values-bn-rBD/strings.xml
deleted file mode 100644
index 31b7a515..00000000
--- a/common/res_leanback/values-bn-rBD/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"শুরু করা যাক"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"পরবর্তী"</string>
-</resources>
diff --git a/common/res_leanback/values-ca/strings.xml b/common/res_leanback/values-ca/strings.xml
deleted file mode 100644
index 8de1bb62..00000000
--- a/common/res_leanback/values-ca/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"COMENÇA"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Següent"</string>
-</resources>
diff --git a/common/res_leanback/values-cs/strings.xml b/common/res_leanback/values-cs/strings.xml
deleted file mode 100644
index 231f92e7..00000000
--- a/common/res_leanback/values-cs/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"ZAČÍNÁME"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Další"</string>
-</resources>
diff --git a/common/res_leanback/values-da/strings.xml b/common/res_leanback/values-da/strings.xml
deleted file mode 100644
index 25375bc2..00000000
--- a/common/res_leanback/values-da/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"KOM GODT I GANG"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Næste"</string>
-</resources>
diff --git a/common/res_leanback/values-de/strings.xml b/common/res_leanback/values-de/strings.xml
deleted file mode 100644
index 0f492e2f..00000000
--- a/common/res_leanback/values-de/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"JETZT STARTEN"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Weiter"</string>
-</resources>
diff --git a/common/res_leanback/values-el/strings.xml b/common/res_leanback/values-el/strings.xml
deleted file mode 100644
index 1df4b216..00000000
--- a/common/res_leanback/values-el/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"ΕΝΑΡΞΗ"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Επόμενο"</string>
-</resources>
diff --git a/common/res_leanback/values-en-rAU/strings.xml b/common/res_leanback/values-en-rAU/strings.xml
deleted file mode 100644
index d6995936..00000000
--- a/common/res_leanback/values-en-rAU/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"GET STARTED"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Next"</string>
-</resources>
diff --git a/common/res_leanback/values-en-rGB/strings.xml b/common/res_leanback/values-en-rGB/strings.xml
deleted file mode 100644
index d6995936..00000000
--- a/common/res_leanback/values-en-rGB/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"GET STARTED"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Next"</string>
-</resources>
diff --git a/common/res_leanback/values-en-rIN/strings.xml b/common/res_leanback/values-en-rIN/strings.xml
deleted file mode 100644
index d6995936..00000000
--- a/common/res_leanback/values-en-rIN/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"GET STARTED"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Next"</string>
-</resources>
diff --git a/common/res_leanback/values-es-rUS/strings.xml b/common/res_leanback/values-es-rUS/strings.xml
deleted file mode 100644
index 0a1bb255..00000000
--- a/common/res_leanback/values-es-rUS/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"COMENZAR"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Siguiente"</string>
-</resources>
diff --git a/common/res_leanback/values-es/strings.xml b/common/res_leanback/values-es/strings.xml
deleted file mode 100644
index 0a17e9f2..00000000
--- a/common/res_leanback/values-es/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"EMPEZAR"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Siguiente"</string>
-</resources>
diff --git a/common/res_leanback/values-et-rEE/strings.xml b/common/res_leanback/values-et-rEE/strings.xml
deleted file mode 100644
index f74e1773..00000000
--- a/common/res_leanback/values-et-rEE/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"ALUSTAGE"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Järgmine"</string>
-</resources>
diff --git a/common/res_leanback/values-eu-rES/strings.xml b/common/res_leanback/values-eu-rES/strings.xml
deleted file mode 100644
index 25dadb75..00000000
--- a/common/res_leanback/values-eu-rES/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"LEHEN URRATSAK"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Hurrengoa"</string>
-</resources>
diff --git a/common/res_leanback/values-fa/strings.xml b/common/res_leanback/values-fa/strings.xml
deleted file mode 100644
index 21b1f969..00000000
--- a/common/res_leanback/values-fa/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"شروع به‌ کار"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"بعدی"</string>
-</resources>
diff --git a/common/res_leanback/values-fi/strings.xml b/common/res_leanback/values-fi/strings.xml
deleted file mode 100644
index c8474c4a..00000000
--- a/common/res_leanback/values-fi/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"ALOITA"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Seuraava"</string>
-</resources>
diff --git a/common/res_leanback/values-fr-rCA/strings.xml b/common/res_leanback/values-fr-rCA/strings.xml
deleted file mode 100644
index 9f7933bc..00000000
--- a/common/res_leanback/values-fr-rCA/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"COMMENCER"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Suivant"</string>
-</resources>
diff --git a/common/res_leanback/values-fr/strings.xml b/common/res_leanback/values-fr/strings.xml
deleted file mode 100644
index 9f7933bc..00000000
--- a/common/res_leanback/values-fr/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"COMMENCER"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Suivant"</string>
-</resources>
diff --git a/common/res_leanback/values-gl-rES/strings.xml b/common/res_leanback/values-gl-rES/strings.xml
deleted file mode 100644
index 8fa1ed80..00000000
--- a/common/res_leanback/values-gl-rES/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"COMEZAR"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Seguinte"</string>
-</resources>
diff --git a/common/res_leanback/values-hi/strings.xml b/common/res_leanback/values-hi/strings.xml
deleted file mode 100644
index c966d80d..00000000
--- a/common/res_leanback/values-hi/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"प्रारंभ करें"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"अगला"</string>
-</resources>
diff --git a/common/res_leanback/values-hr/strings.xml b/common/res_leanback/values-hr/strings.xml
deleted file mode 100644
index 31676fcf..00000000
--- a/common/res_leanback/values-hr/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"POČETAK"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Dalje"</string>
-</resources>
diff --git a/common/res_leanback/values-hu/strings.xml b/common/res_leanback/values-hu/strings.xml
deleted file mode 100644
index cfd19a8d..00000000
--- a/common/res_leanback/values-hu/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"KEZDŐ LÉPÉSEK"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Következő"</string>
-</resources>
diff --git a/common/res_leanback/values-hy-rAM/strings.xml b/common/res_leanback/values-hy-rAM/strings.xml
deleted file mode 100644
index 7b99acad..00000000
--- a/common/res_leanback/values-hy-rAM/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"ՍԿՍԵԼ"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Հաջորդը"</string>
-</resources>
diff --git a/common/res_leanback/values-in/strings.xml b/common/res_leanback/values-in/strings.xml
deleted file mode 100644
index 940a5060..00000000
--- a/common/res_leanback/values-in/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"MULAI"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Berikutnya"</string>
-</resources>
diff --git a/common/res_leanback/values-is-rIS/strings.xml b/common/res_leanback/values-is-rIS/strings.xml
deleted file mode 100644
index 0cbf8935..00000000
--- a/common/res_leanback/values-is-rIS/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"HEFJAST HANDA"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Áfram"</string>
-</resources>
diff --git a/common/res_leanback/values-it/strings.xml b/common/res_leanback/values-it/strings.xml
deleted file mode 100644
index 68681952..00000000
--- a/common/res_leanback/values-it/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"INIZIA"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Avanti"</string>
-</resources>
diff --git a/common/res_leanback/values-iw/strings.xml b/common/res_leanback/values-iw/strings.xml
deleted file mode 100644
index 1df4245e..00000000
--- a/common/res_leanback/values-iw/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"התחל"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"הבא"</string>
-</resources>
diff --git a/common/res_leanback/values-ja/strings.xml b/common/res_leanback/values-ja/strings.xml
deleted file mode 100644
index 67183065..00000000
--- a/common/res_leanback/values-ja/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"使ってみる"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"次へ"</string>
-</resources>
diff --git a/common/res_leanback/values-ka-rGE/strings.xml b/common/res_leanback/values-ka-rGE/strings.xml
deleted file mode 100644
index d5238fc7..00000000
--- a/common/res_leanback/values-ka-rGE/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"დაწყება"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"შემდეგი"</string>
-</resources>
diff --git a/common/res_leanback/values-kk-rKZ/strings.xml b/common/res_leanback/values-kk-rKZ/strings.xml
deleted file mode 100644
index bf67ae09..00000000
--- a/common/res_leanback/values-kk-rKZ/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"ІСКЕ КІРІСУ"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Келесі"</string>
-</resources>
diff --git a/common/res_leanback/values-km-rKH/strings.xml b/common/res_leanback/values-km-rKH/strings.xml
deleted file mode 100644
index 39b3a3c4..00000000
--- a/common/res_leanback/values-km-rKH/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"ចាប់ផ្ដើម"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"បន្ទាប់"</string>
-</resources>
diff --git a/common/res_leanback/values-kn-rIN/strings.xml b/common/res_leanback/values-kn-rIN/strings.xml
deleted file mode 100644
index 8113d1ac..00000000
--- a/common/res_leanback/values-kn-rIN/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"ಪ್ರಾರಂಭಿಸು"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"ಮುಂದೆ"</string>
-</resources>
diff --git a/common/res_leanback/values-ko/strings.xml b/common/res_leanback/values-ko/strings.xml
deleted file mode 100644
index b64e2d96..00000000
--- a/common/res_leanback/values-ko/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"시작하기"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"다음"</string>
-</resources>
diff --git a/common/res_leanback/values-ky-rKG/strings.xml b/common/res_leanback/values-ky-rKG/strings.xml
deleted file mode 100644
index d789e873..00000000
--- a/common/res_leanback/values-ky-rKG/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"БАШТАДЫК"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Кийинки"</string>
-</resources>
diff --git a/common/res_leanback/values-lo-rLA/strings.xml b/common/res_leanback/values-lo-rLA/strings.xml
deleted file mode 100644
index de4d7ead..00000000
--- a/common/res_leanback/values-lo-rLA/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"ເລີ່ມຕົ້ນນຳໃຊ້"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"ຕໍ່​ໄປ"</string>
-</resources>
diff --git a/common/res_leanback/values-lt/strings.xml b/common/res_leanback/values-lt/strings.xml
deleted file mode 100644
index 2938c289..00000000
--- a/common/res_leanback/values-lt/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"PRADĖTI"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Kitas"</string>
-</resources>
diff --git a/common/res_leanback/values-lv/strings.xml b/common/res_leanback/values-lv/strings.xml
deleted file mode 100644
index a65b8919..00000000
--- a/common/res_leanback/values-lv/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"SĀKT DARBU"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Tālāk"</string>
-</resources>
diff --git a/common/res_leanback/values-mk-rMK/strings.xml b/common/res_leanback/values-mk-rMK/strings.xml
deleted file mode 100644
index a7df3e64..00000000
--- a/common/res_leanback/values-mk-rMK/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"ЗАПОЧНИ"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Следно"</string>
-</resources>
diff --git a/common/res_leanback/values-ml-rIN/strings.xml b/common/res_leanback/values-ml-rIN/strings.xml
deleted file mode 100644
index dc051da9..00000000
--- a/common/res_leanback/values-ml-rIN/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"ആരംഭിക്കുക"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"അടുത്തത്"</string>
-</resources>
diff --git a/common/res_leanback/values-mn-rMN/strings.xml b/common/res_leanback/values-mn-rMN/strings.xml
deleted file mode 100644
index eb4b74bd..00000000
--- a/common/res_leanback/values-mn-rMN/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"ЭХЭЛЦГЭЭЕ"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Дараах"</string>
-</resources>
diff --git a/common/res_leanback/values-mr-rIN/strings.xml b/common/res_leanback/values-mr-rIN/strings.xml
deleted file mode 100644
index 293aa75b..00000000
--- a/common/res_leanback/values-mr-rIN/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"प्रारंभ करा"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"पुढील"</string>
-</resources>
diff --git a/common/res_leanback/values-ms-rMY/strings.xml b/common/res_leanback/values-ms-rMY/strings.xml
deleted file mode 100644
index 08e6a0a6..00000000
--- a/common/res_leanback/values-ms-rMY/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"MULAKAN"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Seterusnya"</string>
-</resources>
diff --git a/common/res_leanback/values-my-rMM/strings.xml b/common/res_leanback/values-my-rMM/strings.xml
deleted file mode 100644
index 37ca64e0..00000000
--- a/common/res_leanback/values-my-rMM/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"စတင်ပါ"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"ရှေ့သို့"</string>
-</resources>
diff --git a/common/res_leanback/values-nb/strings.xml b/common/res_leanback/values-nb/strings.xml
deleted file mode 100644
index f79a3fb5..00000000
--- a/common/res_leanback/values-nb/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"KOM I GANG"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Neste"</string>
-</resources>
diff --git a/common/res_leanback/values-ne-rNP/strings.xml b/common/res_leanback/values-ne-rNP/strings.xml
deleted file mode 100644
index 0b479416..00000000
--- a/common/res_leanback/values-ne-rNP/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"सुरू गरौँ"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"अर्को"</string>
-</resources>
diff --git a/common/res_leanback/values-nl/strings.xml b/common/res_leanback/values-nl/strings.xml
deleted file mode 100644
index 98580f53..00000000
--- a/common/res_leanback/values-nl/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"AAN DE SLAG"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Volgende"</string>
-</resources>
diff --git a/common/res_leanback/values-pl/strings.xml b/common/res_leanback/values-pl/strings.xml
deleted file mode 100644
index 59482365..00000000
--- a/common/res_leanback/values-pl/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"WYPRÓBUJ"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Dalej"</string>
-</resources>
diff --git a/common/res_leanback/values-pt-rPT/strings.xml b/common/res_leanback/values-pt-rPT/strings.xml
deleted file mode 100644
index 1529e3aa..00000000
--- a/common/res_leanback/values-pt-rPT/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"INICIAR"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Seguinte"</string>
-</resources>
diff --git a/common/res_leanback/values-pt/strings.xml b/common/res_leanback/values-pt/strings.xml
deleted file mode 100644
index 596351c5..00000000
--- a/common/res_leanback/values-pt/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"PRIMEIROS PASSOS"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Próxima"</string>
-</resources>
diff --git a/common/res_leanback/values-ro/strings.xml b/common/res_leanback/values-ro/strings.xml
deleted file mode 100644
index 0a993f46..00000000
--- a/common/res_leanback/values-ro/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"ÎNCEPEȚI"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Înainte"</string>
-</resources>
diff --git a/common/res_leanback/values-ru/strings.xml b/common/res_leanback/values-ru/strings.xml
deleted file mode 100644
index 1c07da67..00000000
--- a/common/res_leanback/values-ru/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"НАЧАТЬ"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Далее"</string>
-</resources>
diff --git a/common/res_leanback/values-si-rLK/strings.xml b/common/res_leanback/values-si-rLK/strings.xml
deleted file mode 100644
index 78dee7c6..00000000
--- a/common/res_leanback/values-si-rLK/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"ආරම්භ කරන්න"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"ඊළඟ"</string>
-</resources>
diff --git a/common/res_leanback/values-sk/strings.xml b/common/res_leanback/values-sk/strings.xml
deleted file mode 100644
index 998585dc..00000000
--- a/common/res_leanback/values-sk/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"ZAČÍNAME"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Ďalej"</string>
-</resources>
diff --git a/common/res_leanback/values-sl/strings.xml b/common/res_leanback/values-sl/strings.xml
deleted file mode 100644
index ec5e4719..00000000
--- a/common/res_leanback/values-sl/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"ZAČNITE"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Naprej"</string>
-</resources>
diff --git a/common/res_leanback/values-sr/strings.xml b/common/res_leanback/values-sr/strings.xml
deleted file mode 100644
index e65e0e56..00000000
--- a/common/res_leanback/values-sr/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"ЗАПОЧНИТЕ"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Следећа"</string>
-</resources>
diff --git a/common/res_leanback/values-sv/strings.xml b/common/res_leanback/values-sv/strings.xml
deleted file mode 100644
index 6866b1d2..00000000
--- a/common/res_leanback/values-sv/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"KOM IGÅNG"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Nästa"</string>
-</resources>
diff --git a/common/res_leanback/values-sw/strings.xml b/common/res_leanback/values-sw/strings.xml
deleted file mode 100644
index 5f921c04..00000000
--- a/common/res_leanback/values-sw/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"ANZA KUTUMIA"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Inayofuata"</string>
-</resources>
diff --git a/common/res_leanback/values-ta-rIN/strings.xml b/common/res_leanback/values-ta-rIN/strings.xml
deleted file mode 100644
index 5b3c2749..00000000
--- a/common/res_leanback/values-ta-rIN/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"தொடங்குக"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"அடுத்து"</string>
-</resources>
diff --git a/common/res_leanback/values-te-rIN/strings.xml b/common/res_leanback/values-te-rIN/strings.xml
deleted file mode 100644
index dc1954d5..00000000
--- a/common/res_leanback/values-te-rIN/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"ప్రారంభించు"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"తదుపరి"</string>
-</resources>
diff --git a/common/res_leanback/values-th/strings.xml b/common/res_leanback/values-th/strings.xml
deleted file mode 100644
index 00e40d11..00000000
--- a/common/res_leanback/values-th/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"เริ่มต้นใช้งาน"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"ถัดไป"</string>
-</resources>
diff --git a/common/res_leanback/values-tl/strings.xml b/common/res_leanback/values-tl/strings.xml
deleted file mode 100644
index f4a93e7f..00000000
--- a/common/res_leanback/values-tl/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"MAGSIMULA"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Susunod"</string>
-</resources>
diff --git a/common/res_leanback/values-tr/strings.xml b/common/res_leanback/values-tr/strings.xml
deleted file mode 100644
index 126fe3b5..00000000
--- a/common/res_leanback/values-tr/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"BAŞLA"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Sonraki"</string>
-</resources>
diff --git a/common/res_leanback/values-uk/strings.xml b/common/res_leanback/values-uk/strings.xml
deleted file mode 100644
index 61dd2034..00000000
--- a/common/res_leanback/values-uk/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"ПОЧАТИ"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Далі"</string>
-</resources>
diff --git a/common/res_leanback/values-ur-rPK/strings.xml b/common/res_leanback/values-ur-rPK/strings.xml
deleted file mode 100644
index 2a3299ff..00000000
--- a/common/res_leanback/values-ur-rPK/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"شروع کریں"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"اگلا"</string>
-</resources>
diff --git a/common/res_leanback/values-uz-rUZ/strings.xml b/common/res_leanback/values-uz-rUZ/strings.xml
deleted file mode 100644
index 923ea077..00000000
--- a/common/res_leanback/values-uz-rUZ/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"BOSHLADIK"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Keyingi"</string>
-</resources>
diff --git a/common/res_leanback/values-vi/strings.xml b/common/res_leanback/values-vi/strings.xml
deleted file mode 100644
index 5e2c4c07..00000000
--- a/common/res_leanback/values-vi/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"BẮT ĐẦU"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Tiếp theo"</string>
-</resources>
diff --git a/common/res_leanback/values-zh-rCN/strings.xml b/common/res_leanback/values-zh-rCN/strings.xml
deleted file mode 100644
index 7e032d76..00000000
--- a/common/res_leanback/values-zh-rCN/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"开始使用"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"下一页"</string>
-</resources>
diff --git a/common/res_leanback/values-zh-rHK/strings.xml b/common/res_leanback/values-zh-rHK/strings.xml
deleted file mode 100644
index 4c054d96..00000000
--- a/common/res_leanback/values-zh-rHK/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"開始使用"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"下一頁"</string>
-</resources>
diff --git a/common/res_leanback/values-zh-rTW/strings.xml b/common/res_leanback/values-zh-rTW/strings.xml
deleted file mode 100644
index 4c054d96..00000000
--- a/common/res_leanback/values-zh-rTW/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"開始使用"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"下一頁"</string>
-</resources>
diff --git a/common/res_leanback/values-zu/strings.xml b/common/res_leanback/values-zu/strings.xml
deleted file mode 100644
index fe862d14..00000000
--- a/common/res_leanback/values-zu/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"QALISA"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Okulandelayo"</string>
-</resources>
diff --git a/common/res_leanback/values/colors.xml b/common/res_leanback/values/colors.xml
deleted file mode 100644
index 5230dfb8..00000000
--- a/common/res_leanback/values/colors.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources>
- <!-- Onboarding screen -->
- <color name="lb_page_indicator_dot">#014269</color>
- <color name="lb_page_indicator_arrow_background">#EEEEEE</color>
- <color name="lb_page_indicator_arrow_shadow">#4C000000</color>
-</resources>
diff --git a/common/res_leanback/values/dimens.xml b/common/res_leanback/values/dimens.xml
deleted file mode 100644
index 275d22ae..00000000
--- a/common/res_leanback/values/dimens.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources>
- <!-- Onboarding screen -->
- <dimen name="lb_onboarding_content_width">536dp</dimen>
- <dimen name="lb_onboarding_header_height">100dp</dimen>
- <dimen name="lb_onboarding_header_margin_top">64dp</dimen>
- <dimen name="lb_onboarding_start_button_height">36dp</dimen>
- <dimen name="lb_onboarding_start_button_margin_bottom">62dp</dimen>
- <!-- This value should be lb_onboarding_header_margin_top + lb_onboarding_header_height -->
- <dimen name="lb_onboarding_content_margin_top">164dp</dimen>
- <!-- This value should be lb_onboarding_start_button_height + lb_onboarding_start_button_margin_bottom -->
- <dimen name="lb_onboarding_content_margin_bottom">98dp</dimen>
- <!-- This value should be lb_page_indicator_arrow_diameter + 2 * lb_page_indicator_arrow_shadow_radius -->
- <dimen name="lb_onboarding_navigation_height">40dp</dimen>
- <dimen name="lb_page_indicator_arrow_diameter">36dp</dimen>
- <dimen name="lb_page_indicator_arrow_shadow_radius">2dp</dimen>
- <dimen name="lb_page_indicator_arrow_shadow_offset">1dp</dimen>
- <dimen name="lb_page_indicator_arrow_gap">32dp</dimen>
- <!-- This value must be lb_twice page_indicator_dot_radius -->
- <dimen name="lb_page_indicator_dot_diameter">10dp</dimen>
- <dimen name="lb_page_indicator_dot_radius">5dp</dimen>
- <dimen name="lb_page_indicator_dot_gap">16dp</dimen>
-</resources>
diff --git a/common/res_leanback/values/strings.xml b/common/res_leanback/values/strings.xml
deleted file mode 100644
index 2f585c0b..00000000
--- a/common/res_leanback/values/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<resources>
- <!-- Onboarding screen.-->
- <eat-comment />
- <!-- Text for "GET STARTED" button. This text should be in ALL CAPS. -->
- <string name="lb_onboarding_get_started">GET STARTED</string>
- <!-- Content description for page navigator. -->
- <string name="lb_onboarding_accessibility_next">Next</string>
-</resources>
diff --git a/common/src/com/android/tv/common/BuildConfig.java b/common/src/com/android/tv/common/BuildConfig.java
deleted file mode 100644
index 635903c3..00000000
--- a/common/src/com/android/tv/common/BuildConfig.java
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * TODO: It's manually generated file to fix build breakage.
- * It should be automatically generated.
- */
-package com.android.tv.common;
-public final class BuildConfig {
- public static final boolean DEBUG = false;
- public static final boolean ENG = false;
- private BuildConfig() {}
-}
diff --git a/common/src/com/android/tv/common/CollectionUtils.java b/common/src/com/android/tv/common/CollectionUtils.java
index 4a7a81f2..f81e51a5 100644
--- a/common/src/com/android/tv/common/CollectionUtils.java
+++ b/common/src/com/android/tv/common/CollectionUtils.java
@@ -16,45 +16,12 @@
package com.android.tv.common;
-import android.os.Build;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-
import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
/**
* Static utilities for collections
*/
public class CollectionUtils {
- /**
- * Returns a new Set suitable for small data sets.
- *
- * <p>In M and above this is a {@link ArraySet} otherwise it is a {@link HashSet}.
- */
- public static <T> Set<T> createSmallSet() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- return new ArraySet<>();
- } else {
- return new HashSet<>();
- }
- }
-
- /**
- * Returns a new Map suitable for small data sets.
- *
- * <p>In M and above this is a {@link ArrayMap} otherwise it is a {@link HashMap}.
- */
- public static <K, V> Map<K, V> createSmallMap() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- return new ArrayMap<>();
- } else {
- return new HashMap<>();
- }
- }
/**
* Returns an array with the arrays concatenated together.
diff --git a/src/com/android/tv/util/SoftPreconditions.java b/common/src/com/android/tv/common/SoftPreconditions.java
index 3643fca4..9b7713f6 100644
--- a/src/com/android/tv/util/SoftPreconditions.java
+++ b/common/src/com/android/tv/common/SoftPreconditions.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.tv.util;
+package com.android.tv.common;
import android.content.Context;
import android.text.TextUtils;
@@ -136,24 +136,25 @@ public final class SoftPreconditions {
* @param tag Used to identify the source of a log message. It usually
* identifies the class or activity where the log call occurs.
* @param msg The message you would like logged
- * @param e The exception to throw
+ * @param e The exception to wrap with a RuntimeException when thrown.
*/
- public static void warn(String tag, String prefix, String msg, RuntimeException e)
- throws RuntimeException{
+ public static void warn(String tag, String prefix, String msg, Exception e)
+ throws RuntimeException {
+ if (TextUtils.isEmpty(tag)) {
+ tag = TAG;
+ }
+ String logMessage;
+ if (TextUtils.isEmpty(msg)) {
+ logMessage = prefix;
+ } else if (TextUtils.isEmpty(prefix)) {
+ logMessage = msg;
+ } else {
+ logMessage = prefix + ": " + msg;
+ }
+
if (BuildConfig.ENG) {
- throw e;
+ throw new RuntimeException(msg, e);
} else {
- if (TextUtils.isEmpty(tag)) {
- tag = TAG;
- }
- String logMessage;
- if (TextUtils.isEmpty(msg)) {
- logMessage = prefix;
- } else if (TextUtils.isEmpty(prefix)) {
- logMessage = msg;
- } else {
- logMessage = prefix + ": " + msg;
- }
Log.w(tag, logMessage, e);
}
}
diff --git a/common/src/com/android/tv/common/TvContentRatingCache.java b/common/src/com/android/tv/common/TvContentRatingCache.java
index 5ca780e3..7ea86287 100644
--- a/common/src/com/android/tv/common/TvContentRatingCache.java
+++ b/common/src/com/android/tv/common/TvContentRatingCache.java
@@ -20,6 +20,7 @@ import android.media.tv.TvContentRating;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Log;
import java.util.ArrayList;
@@ -42,8 +43,7 @@ public final class TvContentRatingCache implements MemoryManageable {
return INSTANCE;
}
- private final Map<String, TvContentRating[]> mRatingsMultiMap = CollectionUtils
- .createSmallMap();
+ private final Map<String, TvContentRating[]> mRatingsMultiMap = new ArrayMap<>();
/**
* Returns an array TvContentRatings from a string of comma separated set of rating strings
diff --git a/common/src/com/android/tv/common/feature/CommonFeatures.java b/common/src/com/android/tv/common/feature/CommonFeatures.java
index bfef19a6..9925833f 100644
--- a/common/src/com/android/tv/common/feature/CommonFeatures.java
+++ b/common/src/com/android/tv/common/feature/CommonFeatures.java
@@ -17,7 +17,7 @@
package com.android.tv.common.feature;
import static com.android.tv.common.feature.EngOnlyFeature.ENG_ONLY_FEATURE;
-import static com.android.tv.common.feature.FeatureUtils.AND;
+import static com.android.tv.common.feature.FeatureUtils.OR;
import static com.android.tv.common.feature.TestableFeature.createTestableFeature;
/**
@@ -32,9 +32,12 @@ public class CommonFeatures {
* <p>See <a href="https://goto.google.com/atv-dvr-onepager">go/atv-dvr-onepager</a>
*/
public static TestableFeature DVR = createTestableFeature(
- AND(
- ENG_ONLY_FEATURE,
- new PropertyFeature("dvr_enabled", false),
- Sdk.M_FEATURE // TODO(dvr): Sdk.N_PREVIEW_FEATURE
- ));
+ OR(ENG_ONLY_FEATURE, Sdk.N_PRE_2_OR_HIGHER));
+
+ /**
+ * USE_SW_CODEC_FOR_SD
+ *
+ * Prefer software based codec for SD channels.
+ */
+ public static Feature USE_SW_CODEC_FOR_SD = new PropertyFeature("use_sw_codec_for_sd", true);
}
diff --git a/common/src/com/android/tv/common/feature/Sdk.java b/common/src/com/android/tv/common/feature/Sdk.java
index 1efefd89..268eaea7 100644
--- a/common/src/com/android/tv/common/feature/Sdk.java
+++ b/common/src/com/android/tv/common/feature/Sdk.java
@@ -18,25 +18,50 @@ package com.android.tv.common.feature;
import android.content.Context;
import android.os.Build;
+import android.support.v4.os.BuildCompat;
/**
* Holder for SDK version features
*/
public class Sdk {
- public static Feature M_FEATURE = new SdkVersionFeature(Build.VERSION_CODES.M);
- private static class SdkVersionFeature implements Feature {
+ public static Feature N_PRE_2_OR_HIGHER =
+ new SdkPreviewVersionFeature(Build.VERSION_CODES.M, 2, true);
+
+ private static class SdkPreviewVersionFeature implements Feature {
private final int mVersionCode;
+ private final int mPreviewCode;
+ private final boolean mAllowHigherPreview;
- private SdkVersionFeature(int versionCode) {
+ private SdkPreviewVersionFeature(int versionCode, int previewCode,
+ boolean allowHigerPreview) {
mVersionCode = versionCode;
+ mPreviewCode = previewCode;
+ mAllowHigherPreview = allowHigerPreview;
}
@Override
public boolean isEnabled(Context context) {
- return Build.VERSION.SDK_INT >= mVersionCode;
+ try {
+ if (mAllowHigherPreview) {
+ return Build.VERSION.SDK_INT == mVersionCode
+ && Build.VERSION.PREVIEW_SDK_INT >= mPreviewCode;
+ } else {
+ return Build.VERSION.SDK_INT == mVersionCode
+ && Build.VERSION.PREVIEW_SDK_INT == mPreviewCode;
+ }
+ } catch (NoSuchFieldError e) {
+ return false;
+ }
}
}
+ public static Feature AT_LEAST_N = new Feature() {
+ @Override
+ public boolean isEnabled(Context context) {
+ return BuildCompat.isAtLeastN();
+ }
+ };
+
private Sdk() {}
}
diff --git a/common/src/com/android/tv/common/recording/PlaybackTvView.java b/common/src/com/android/tv/common/recording/PlaybackTvView.java
deleted file mode 100644
index e62ee06f..00000000
--- a/common/src/com/android/tv/common/recording/PlaybackTvView.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tv.common.recording;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.media.tv.TvContentRating;
-import android.media.tv.TvContract;
-import android.media.tv.TvTrackInfo;
-import android.media.tv.TvView;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.util.AttributeSet;
-
-import com.android.tv.common.feature.CommonFeatures;
-
-import java.util.List;
-
-/**
- * Extend {@link TvView} to support recording playback.
- */
-@TargetApi(Build.VERSION_CODES.M) // TODO(DVR): set to N
-public class PlaybackTvView extends TvView {
-
- final TvInputCallback mInternalCallback = new TvInputCallback() {
- @Override
- public void onChannelRetuned(String inputId, Uri channelUri) {
- if (mCallback != null) {
- mCallback.onChannelRetuned(inputId, channelUri);
- }
- }
-
- @Override
- public void onConnectionFailed(String inputId) {
- if (mCallback != null) {
- mCallback.onConnectionFailed(inputId);
- }
- }
-
- @Override
- public void onContentAllowed(String inputId) {
- if (mCallback != null) {
- mCallback.onContentAllowed(inputId);
- }
- }
-
- @Override
- public void onContentBlocked(String inputId, TvContentRating rating) {
- if (mCallback != null) {
- mCallback.onContentBlocked(inputId, rating);
- }
- }
-
- @Override
- public void onDisconnected(String inputId) {
- if (mCallback != null) {
- mCallback.onDisconnected(inputId);
- }
- }
-
- @Override
- public void onEvent(String inputId, String eventType, Bundle eventArgs) {
- if (mCallback != null) {
- if (eventType.equals(RecordingUtils.EVENT_TYPE_TIMESHIFT_END_POSITION)) {
- if (mTimeshiftCallback != null) {
- mTimeshiftCallback.onTimeShiftEndPositionChanged(inputId,
- eventArgs.getLong(RecordingUtils.BUNDLE_TIMESHIFT_END_POSITION));
- }
- return;
- }
- mCallback.onEvent(inputId, eventType, eventArgs);
- }
- }
-
- @Override
- public void onTimeShiftStatusChanged(String inputId, int status) {
- if (mCallback != null) {
- mCallback.onTimeShiftStatusChanged(inputId, status);
- }
- }
-
- @Override
- public void onTracksChanged(String inputId, List<TvTrackInfo> tracks) {
- if (mCallback != null) {
- mCallback.onTracksChanged(inputId, tracks);
- }
- }
-
- @Override
- public void onTrackSelected(String inputId, int type, String trackId) {
- if (mCallback != null) {
- mCallback.onTrackSelected(inputId, type, trackId);
- }
- }
-
- @Override
- public void onVideoAvailable(String inputId) {
- if (mCallback != null) {
- mCallback.onVideoAvailable(inputId);
- }
- }
-
- @Override
- public void onVideoSizeChanged(String inputId, int width, int height) {
- if (mCallback != null) {
- mCallback.onVideoSizeChanged(inputId, width, height);
- }
- }
-
- @Override
- public void onVideoUnavailable(String inputId, int reason) {
- if (mCallback != null) {
- mCallback.onVideoUnavailable(inputId, reason);
- }
- }
- };
-
- private TvInputCallback mCallback;
- private TimeShiftPositionCallback2 mTimeshiftCallback;
-
- public PlaybackTvView(Context context) {
- this(context, null, 0);
- }
-
- public PlaybackTvView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public PlaybackTvView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- /**
- * Start playback of recording. Once TvInput is ready to play, onVideoAvailable will be called.
- * Playback control will be done with timeshift method for seek, play, pause.
- */
- public void playMedia(String inputId, Uri mediaUri) {
- tune(inputId, TvContract.buildChannelUri(0), RecordingUtils.buildMediaUri(mediaUri));
- }
-
- @Override
- public void tune(String inputId, Uri channelUri, Bundle params) {
- super.tune(inputId, channelUri, params);
- if (CommonFeatures.DVR.isEnabled(getContext())) {
- sendAppPrivateCommand(RecordingUtils.APP_PRIV_CREATE_PLAYBACK_SESSION, null);
- }
- }
-
- public void setTimeShiftPositionCallback(TimeShiftPositionCallback2 callback) {
- if (CommonFeatures.DVR.isEnabled(getContext())) {
- mTimeshiftCallback = callback;
- }
- super.setTimeShiftPositionCallback(callback);
- }
-
- @Override
- public void setCallback(TvInputCallback callback) {
- if (CommonFeatures.DVR.isEnabled(getContext())) {
- mCallback = callback;
- if (callback == null) {
- super.setCallback(null);
- } else {
- super.setCallback(mInternalCallback);
- }
- } else {
- super.setCallback(callback);
- }
- }
-
- /**
- * We need end position for recording playback.
- */
- public abstract static class TimeShiftPositionCallback2 extends TimeShiftPositionCallback {
- public void onTimeShiftEndPositionChanged(String inputId, long timeMs) { }
- }
-}
diff --git a/common/src/com/android/tv/common/recording/RecordedProgram.java b/common/src/com/android/tv/common/recording/RecordedProgram.java
new file mode 100644
index 00000000..63ce6ff9
--- /dev/null
+++ b/common/src/com/android/tv/common/recording/RecordedProgram.java
@@ -0,0 +1,760 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.tv.common.recording;
+
+import static android.media.tv.TvContract.RecordedPrograms;
+
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.media.tv.TvContract;
+import android.net.Uri;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+
+import com.android.tv.common.R;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Objects;
+
+/**
+ * Immutable instance of {@link android.media.tv.TvContract.RecordedPrograms}.
+ */
+public class RecordedProgram {
+ public static final int ID_NOT_SET = -1;
+
+ public final static String[] PROJECTION = {
+ // These are in exactly the order listed in RecordedPrograms
+ RecordedPrograms._ID,
+ RecordedPrograms.COLUMN_INPUT_ID,
+ RecordedPrograms.COLUMN_CHANNEL_ID,
+ RecordedPrograms.COLUMN_TITLE,
+ RecordedPrograms.COLUMN_SEASON_DISPLAY_NUMBER,
+ RecordedPrograms.COLUMN_SEASON_TITLE,
+ RecordedPrograms.COLUMN_EPISODE_DISPLAY_NUMBER,
+ RecordedPrograms.COLUMN_EPISODE_TITLE,
+ RecordedPrograms.COLUMN_START_TIME_UTC_MILLIS,
+ RecordedPrograms.COLUMN_END_TIME_UTC_MILLIS,
+ RecordedPrograms.COLUMN_BROADCAST_GENRE,
+ RecordedPrograms.COLUMN_CANONICAL_GENRE,
+ RecordedPrograms.COLUMN_SHORT_DESCRIPTION,
+ RecordedPrograms.COLUMN_LONG_DESCRIPTION,
+ RecordedPrograms.COLUMN_VIDEO_WIDTH,
+ RecordedPrograms.COLUMN_VIDEO_HEIGHT,
+ RecordedPrograms.COLUMN_AUDIO_LANGUAGE,
+ RecordedPrograms.COLUMN_CONTENT_RATING,
+ RecordedPrograms.COLUMN_POSTER_ART_URI,
+ RecordedPrograms.COLUMN_THUMBNAIL_URI,
+ RecordedPrograms.COLUMN_SEARCHABLE,
+ RecordedPrograms.COLUMN_RECORDING_DATA_URI,
+ RecordedPrograms.COLUMN_RECORDING_DATA_BYTES,
+ RecordedPrograms.COLUMN_RECORDING_DURATION_MILLIS,
+ RecordedPrograms.COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS,
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_DATA,
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG1,
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2,
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3,
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4,
+ RecordedPrograms.COLUMN_VERSION_NUMBER,
+ };
+
+ public static final RecordedProgram fromCursor(Cursor cursor) {
+ int index = 0;
+ return builder()
+ .setId(cursor.getLong(index++))
+ .setInputId(cursor.getString(index++))
+ .setChannelId(cursor.getLong(index++))
+ .setTitle(cursor.getString(index++))
+ .setSeasonNumber(cursor.getString(index++))
+ .setSeasonTitle(cursor.getString(index++))
+ .setEpisodeNumber(cursor.getString(index++))
+ .setEpisodeTitle(cursor.getString(index++))
+ .setStartTimeUtcMillis(cursor.getLong(index++))
+ .setEndTimeUtcMillis(cursor.getLong(index++))
+ .setBroadcastGenres(cursor.getString(index++))
+ .setCanonicalGenres(cursor.getString(index++))
+ .setShortDescription(cursor.getString(index++))
+ .setLongDescription(cursor.getString(index++))
+ .setVideoWidth(cursor.getInt(index++))
+ .setVideoHeight(cursor.getInt(index++))
+ .setAudioLanguage(cursor.getString(index++))
+ .setContentRating(cursor.getString(index++))
+ .setPosterArt(cursor.getString(index++))
+ .setThumbnail(cursor.getString(index++))
+ .setSearchable(cursor.getInt(index++) == 1)
+ .setDataUri(cursor.getString(index++))
+ .setDataBytes(cursor.getLong(index++))
+ .setDurationMillis(cursor.getLong(index++))
+ .setExpireTimeUtcMillis(cursor.getLong(index++))
+ .setInternalProviderData(cursor.getBlob(index++))
+ .setInternalProviderFlag1(cursor.getInt(index++))
+ .setInternalProviderFlag2(cursor.getInt(index++))
+ .setInternalProviderFlag3(cursor.getInt(index++))
+ .setInternalProviderFlag4(cursor.getInt(index++))
+ .setVersionNumber(cursor.getInt(index++))
+ .build();
+ }
+
+ public static ContentValues toValues(RecordedProgram recordedProgram) {
+ ContentValues values = new ContentValues();
+ if (recordedProgram.mId != ID_NOT_SET) {
+ values.put(RecordedPrograms._ID, recordedProgram.mId);
+ }
+ values.put(RecordedPrograms.COLUMN_INPUT_ID, recordedProgram.mInputId);
+ values.put(RecordedPrograms.COLUMN_CHANNEL_ID, recordedProgram.mChannelId);
+ values.put(RecordedPrograms.COLUMN_TITLE, recordedProgram.mTitle);
+ values.put(RecordedPrograms.COLUMN_SEASON_DISPLAY_NUMBER, recordedProgram.mSeasonNumber);
+ values.put(RecordedPrograms.COLUMN_SEASON_TITLE, recordedProgram.mSeasonTitle);
+ values.put(RecordedPrograms.COLUMN_EPISODE_DISPLAY_NUMBER, recordedProgram.mEpisodeNumber);
+ values.put(RecordedPrograms.COLUMN_EPISODE_TITLE, recordedProgram.mTitle);
+ values.put(RecordedPrograms.COLUMN_START_TIME_UTC_MILLIS,
+ recordedProgram.mStartTimeUtcMillis);
+ values.put(RecordedPrograms.COLUMN_END_TIME_UTC_MILLIS, recordedProgram.mEndTimeUtcMillis);
+ values.put(RecordedPrograms.COLUMN_BROADCAST_GENRE,
+ safeEncode(recordedProgram.mBroadcastGenres));
+ values.put(RecordedPrograms.COLUMN_CANONICAL_GENRE,
+ safeEncode(recordedProgram.mCanonicalGenres));
+ values.put(RecordedPrograms.COLUMN_SHORT_DESCRIPTION, recordedProgram.mShortDescription);
+ values.put(RecordedPrograms.COLUMN_LONG_DESCRIPTION, recordedProgram.mLongDescription);
+ if (recordedProgram.mVideoWidth == 0) {
+ values.putNull(RecordedPrograms.COLUMN_VIDEO_WIDTH);
+ } else {
+ values.put(RecordedPrograms.COLUMN_VIDEO_WIDTH, recordedProgram.mVideoWidth);
+ }
+ if (recordedProgram.mVideoHeight == 0) {
+ values.putNull(RecordedPrograms.COLUMN_VIDEO_HEIGHT);
+ } else {
+ values.put(RecordedPrograms.COLUMN_VIDEO_HEIGHT, recordedProgram.mVideoHeight);
+ }
+ values.put(RecordedPrograms.COLUMN_AUDIO_LANGUAGE, recordedProgram.mAudioLanguage);
+ values.put(RecordedPrograms.COLUMN_CONTENT_RATING, recordedProgram.mContentRating);
+ values.put(RecordedPrograms.COLUMN_POSTER_ART_URI,
+ safeToString(recordedProgram.mPosterArt));
+ values.put(RecordedPrograms.COLUMN_THUMBNAIL_URI, safeToString(recordedProgram.mThumbnail));
+ values.put(RecordedPrograms.COLUMN_SEARCHABLE, recordedProgram.mSearchable ? 1 : 0);
+ values.put(RecordedPrograms.COLUMN_RECORDING_DATA_URI,
+ safeToString(recordedProgram.mDataUri));
+ values.put(RecordedPrograms.COLUMN_RECORDING_DATA_BYTES, recordedProgram.mDataBytes);
+ values.put(RecordedPrograms.COLUMN_RECORDING_DURATION_MILLIS,
+ recordedProgram.mDurationMillis);
+ values.put(RecordedPrograms.COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS,
+ recordedProgram.mExpireTimeUtcMillis);
+ values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_DATA,
+ recordedProgram.mInternalProviderData);
+ values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG1,
+ recordedProgram.mInternalProviderFlag1);
+ values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2,
+ recordedProgram.mInternalProviderFlag2);
+ values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3,
+ recordedProgram.mInternalProviderFlag3);
+ values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4,
+ recordedProgram.mInternalProviderFlag4);
+ values.put(RecordedPrograms.COLUMN_VERSION_NUMBER, recordedProgram.mVersionNumber);
+ return values;
+ }
+
+ public static class Builder{
+ private long mId = ID_NOT_SET;
+ private String mInputId;
+ private long mChannelId;
+ private String mTitle;
+ private String mSeasonNumber;
+ private String mSeasonTitle;
+ private String mEpisodeNumber;
+ private String mEpisodeTitle;
+ private long mStartTimeUtcMillis;
+ private long mEndTimeUtcMillis;
+ private String[] mBroadcastGenres;
+ private String[] mCanonicalGenres;
+ private String mShortDescription;
+ private String mLongDescription;
+ private int mVideoWidth;
+ private int mVideoHeight;
+ private String mAudioLanguage;
+ private String mContentRating;
+ private Uri mPosterArt;
+ private Uri mThumbnail;
+ private boolean mSearchable = true;
+ private Uri mDataUri;
+ private long mDataBytes;
+ private long mDurationMillis;
+ private long mExpireTimeUtcMillis;
+ private byte[] mInternalProviderData;
+ private int mInternalProviderFlag1;
+ private int mInternalProviderFlag2;
+ private int mInternalProviderFlag3;
+ private int mInternalProviderFlag4;
+ private int mVersionNumber;
+
+ public Builder setId(long id) {
+ mId = id;
+ return this;
+ }
+
+ public Builder setInputId(String inputId) {
+ mInputId = inputId;
+ return this;
+ }
+
+ public Builder setChannelId(long channelId) {
+ mChannelId = channelId;
+ return this;
+ }
+
+ public Builder setTitle(String title) {
+ mTitle = title;
+ return this;
+ }
+
+ public Builder setSeasonNumber(String seasonNumber) {
+ mSeasonNumber = seasonNumber;
+ return this;
+ }
+
+ public Builder setSeasonTitle(String seasonTitle) {
+ mSeasonTitle = seasonTitle;
+ return this;
+ }
+
+ public Builder setEpisodeNumber(String episodeNumber) {
+ mEpisodeNumber = episodeNumber;
+ return this;
+ }
+
+ public Builder setEpisodeTitle(String episodeTitle) {
+ mEpisodeTitle = episodeTitle;
+ return this;
+ }
+
+ public Builder setStartTimeUtcMillis(long startTimeUtcMillis) {
+ mStartTimeUtcMillis = startTimeUtcMillis;
+ return this;
+ }
+
+ public Builder setEndTimeUtcMillis(long endTimeUtcMillis) {
+ mEndTimeUtcMillis = endTimeUtcMillis;
+ return this;
+ }
+
+ public Builder setBroadcastGenres(String broadcastGenres) {
+ if (TextUtils.isEmpty(broadcastGenres)) {
+ mBroadcastGenres = null;
+ return this;
+ }
+ return setBroadcastGenres(TvContract.Programs.Genres.decode(broadcastGenres));
+ }
+
+ private Builder setBroadcastGenres(String[] broadcastGenres) {
+ mBroadcastGenres = broadcastGenres;
+ return this;
+ }
+
+ public Builder setCanonicalGenres(String canonicalGenres) {
+ if (TextUtils.isEmpty(canonicalGenres)) {
+ mCanonicalGenres = null;
+ return this;
+ }
+ return setCanonicalGenres(TvContract.Programs.Genres.decode(canonicalGenres));
+ }
+
+ private Builder setCanonicalGenres(String[] canonicalGenres) {
+ mCanonicalGenres = canonicalGenres;
+ return this;
+ }
+
+ public Builder setShortDescription(String shortDescription) {
+ mShortDescription = shortDescription;
+ return this;
+ }
+
+ public Builder setLongDescription(String longDescription) {
+ mLongDescription = longDescription;
+ return this;
+ }
+
+ public Builder setVideoWidth(int videoWidth) {
+ mVideoWidth = videoWidth;
+ return this;
+ }
+
+ public Builder setVideoHeight(int videoHeight) {
+ mVideoHeight = videoHeight;
+ return this;
+ }
+
+ public Builder setAudioLanguage(String audioLanguage) {
+ mAudioLanguage = audioLanguage;
+ return this;
+ }
+
+ public Builder setContentRating(String contentRating) {
+ mContentRating = contentRating;
+ return this;
+ }
+
+ private Uri toUri(String uriString) {
+ try {
+ return uriString == null ? null : Uri.parse(uriString);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ public Builder setPosterArt(String posterArtUri) {
+ return setPosterArt(toUri(posterArtUri));
+ }
+
+ public Builder setPosterArt(Uri posterArt) {
+ mPosterArt = posterArt;
+ return this;
+ }
+
+ public Builder setThumbnail(String thumbnailUri) {
+ return setThumbnail(toUri(thumbnailUri));
+ }
+
+ public Builder setThumbnail(Uri thumbnail) {
+ mThumbnail = thumbnail;
+ return this;
+ }
+
+ public Builder setSearchable(boolean searchable) {
+ mSearchable = searchable;
+ return this;
+ }
+
+ public Builder setDataUri(String dataUri) {
+ return setDataUri(toUri(dataUri));
+ }
+
+ public Builder setDataUri(Uri dataUri) {
+ mDataUri = dataUri;
+ return this;
+ }
+
+ public Builder setDataBytes(long dataBytes) {
+ mDataBytes = dataBytes;
+ return this;
+ }
+
+ public Builder setDurationMillis(long durationMillis) {
+ mDurationMillis = durationMillis;
+ return this;
+ }
+
+ public Builder setExpireTimeUtcMillis(long expireTimeUtcMillis) {
+ mExpireTimeUtcMillis = expireTimeUtcMillis;
+ return this;
+ }
+
+ public Builder setInternalProviderData(byte[] internalProviderData) {
+ mInternalProviderData = internalProviderData;
+ return this;
+ }
+
+ public Builder setInternalProviderFlag1(int internalProviderFlag1) {
+ mInternalProviderFlag1 = internalProviderFlag1;
+ return this;
+ }
+
+ public Builder setInternalProviderFlag2(int internalProviderFlag2) {
+ mInternalProviderFlag2 = internalProviderFlag2;
+ return this;
+ }
+
+ public Builder setInternalProviderFlag3(int internalProviderFlag3) {
+ mInternalProviderFlag3 = internalProviderFlag3;
+ return this;
+ }
+
+ public Builder setInternalProviderFlag4(int internalProviderFlag4) {
+ mInternalProviderFlag4 = internalProviderFlag4;
+ return this;
+ }
+
+ public Builder setVersionNumber(int versionNumber) {
+ mVersionNumber = versionNumber;
+ return this;
+ }
+
+ public RecordedProgram build() {
+ return new RecordedProgram(mId, mInputId, mChannelId, mTitle, mSeasonNumber,
+ mSeasonTitle, mEpisodeNumber, mEpisodeTitle, mStartTimeUtcMillis,
+ mEndTimeUtcMillis, mBroadcastGenres, mCanonicalGenres, mShortDescription,
+ mLongDescription, mVideoWidth, mVideoHeight, mAudioLanguage, mContentRating,
+ mPosterArt, mThumbnail, mSearchable, mDataUri, mDataBytes, mDurationMillis,
+ mExpireTimeUtcMillis, mInternalProviderData, mInternalProviderFlag1,
+ mInternalProviderFlag2, mInternalProviderFlag3, mInternalProviderFlag4,
+ mVersionNumber);
+ }
+ }
+
+ public static Builder builder() { return new Builder(); }
+
+ public static Builder buildFrom(RecordedProgram orig) {
+ return builder()
+ .setId(orig.getId())
+ .setInputId(orig.getInputId())
+ .setChannelId(orig.getChannelId())
+ .setTitle(orig.getTitle())
+ .setSeasonNumber(orig.getSeasonNumber())
+ .setSeasonTitle(orig.getSeasonTitle())
+ .setEpisodeNumber(orig.getEpisodeNumber())
+ .setEpisodeTitle(orig.getEpisodeTitle())
+ .setStartTimeUtcMillis(orig.getStartTimeUtcMillis())
+ .setEndTimeUtcMillis(orig.getEndTimeUtcMillis())
+ .setBroadcastGenres(orig.getBroadcastGenres())
+ .setCanonicalGenres(orig.getCanonicalGenres())
+ .setShortDescription(orig.getShortDescription())
+ .setLongDescription(orig.getLongDescription())
+ .setVideoWidth(orig.getVideoWidth())
+ .setVideoHeight(orig.getVideoHeight())
+ .setAudioLanguage(orig.getAudioLanguage())
+ .setContentRating(orig.getContentRating())
+ .setPosterArt(orig.getPosterArt())
+ .setThumbnail(orig.getThumbnail())
+ .setSearchable(orig.isSearchable())
+ .setInternalProviderData(orig.getInternalProviderData())
+ .setInternalProviderFlag1(orig.getInternalProviderFlag1())
+ .setInternalProviderFlag2(orig.getInternalProviderFlag2())
+ .setInternalProviderFlag3(orig.getInternalProviderFlag3())
+ .setInternalProviderFlag4(orig.getInternalProviderFlag4())
+ .setVersionNumber(orig.getVersionNumber());
+ }
+
+ public static final Comparator<RecordedProgram> START_TIME_THEN_ID_COMPARATOR
+ = new Comparator<RecordedProgram>() {
+ @Override
+ public int compare(RecordedProgram lhs, RecordedProgram rhs) {
+ int res = Long.compare(lhs.getStartTimeUtcMillis(), rhs.getStartTimeUtcMillis());
+ if (res != 0) {
+ return res;
+ }
+ return Long.compare(lhs.mId, rhs.mId);
+ }
+ };
+
+ private final long mId;
+ private final String mInputId;
+ private final long mChannelId;
+ private final String mTitle;
+ private final String mSeasonNumber;
+ private final String mSeasonTitle;
+ private final String mEpisodeNumber;
+ private final String mEpisodeTitle;
+ private final long mStartTimeUtcMillis;
+ private final long mEndTimeUtcMillis;
+ private final String[] mBroadcastGenres;
+ private final String[] mCanonicalGenres;
+ private final String mShortDescription;
+ private final String mLongDescription;
+ private final int mVideoWidth;
+ private final int mVideoHeight;
+ private final String mAudioLanguage;
+ private final String mContentRating;
+ private final Uri mPosterArt;
+ private final Uri mThumbnail;
+ private final boolean mSearchable;
+ private final Uri mDataUri;
+ private final long mDataBytes;
+ private final long mDurationMillis;
+ private final long mExpireTimeUtcMillis;
+ private final byte[] mInternalProviderData;
+ private final int mInternalProviderFlag1;
+ private final int mInternalProviderFlag2;
+ private final int mInternalProviderFlag3;
+ private final int mInternalProviderFlag4;
+ private final int mVersionNumber;
+
+ private RecordedProgram(long id, String inputId, long channelId, String title,
+ String seasonNumber, String seasonTitle, String episodeNumber, String episodeTitle,
+ long startTimeUtcMillis, long endTimeUtcMillis, String[] broadcastGenres,
+ String[] canonicalGenres, String shortDescription, String longDescription,
+ int videoWidth, int videoHeight, String audioLanguage, String contentRating,
+ Uri posterArt, Uri thumbnail, boolean searchable, Uri dataUri, long dataBytes,
+ long durationMillis, long expireTimeUtcMillis, byte[] internalProviderData,
+ int internalProviderFlag1, int internalProviderFlag2, int internalProviderFlag3,
+ int internalProviderFlag4, int versionNumber) {
+ mId = id;
+ mInputId = inputId;
+ mChannelId = channelId;
+ mTitle = title;
+ mSeasonNumber = seasonNumber;
+ mSeasonTitle = seasonTitle;
+ mEpisodeNumber = episodeNumber;
+ mEpisodeTitle = episodeTitle;
+ mStartTimeUtcMillis = startTimeUtcMillis;
+ mEndTimeUtcMillis = endTimeUtcMillis;
+ mBroadcastGenres = broadcastGenres;
+ mCanonicalGenres = canonicalGenres;
+ mShortDescription = shortDescription;
+ mLongDescription = longDescription;
+ mVideoWidth = videoWidth;
+ mVideoHeight = videoHeight;
+
+ mAudioLanguage = audioLanguage;
+ mContentRating = contentRating;
+ mPosterArt = posterArt;
+ mThumbnail = thumbnail;
+ mSearchable = searchable;
+ mDataUri = dataUri;
+ mDataBytes = dataBytes;
+ mDurationMillis = durationMillis;
+ mExpireTimeUtcMillis = expireTimeUtcMillis;
+ mInternalProviderData = internalProviderData;
+ mInternalProviderFlag1 = internalProviderFlag1;
+ mInternalProviderFlag2 = internalProviderFlag2;
+ mInternalProviderFlag3 = internalProviderFlag3;
+ mInternalProviderFlag4 = internalProviderFlag4;
+ mVersionNumber = versionNumber;
+ }
+
+ public String getAudioLanguage() {
+ return mAudioLanguage;
+ }
+
+ public String[] getBroadcastGenres() {
+ return mBroadcastGenres;
+ }
+
+ public String[] getCanonicalGenres() {
+ return mCanonicalGenres;
+ }
+
+ public long getChannelId() {
+ return mChannelId;
+ }
+
+ public String getContentRating() {
+ return mContentRating;
+ }
+
+ public Uri getDataUri() {
+ return mDataUri;
+ }
+
+ public long getDataBytes() {
+ return mDataBytes;
+ }
+
+ public long getDurationMillis() {
+ return mDurationMillis;
+ }
+
+ public long getEndTimeUtcMillis() {
+ return mEndTimeUtcMillis;
+ }
+
+ public String getEpisodeNumber() {
+ return mEpisodeNumber;
+ }
+
+ public String getEpisodeTitle() {
+ return mEpisodeTitle;
+ }
+
+ public String getEpisodeDisplayTitle(Context context) {
+ if (!TextUtils.isEmpty(mSeasonNumber) && !TextUtils.isEmpty(mEpisodeNumber)
+ && !TextUtils.isEmpty(mEpisodeTitle)) {
+ return String.format(context.getResources().getString(R.string.episode_format),
+ mSeasonNumber, mEpisodeNumber, mEpisodeTitle);
+ }
+ return mEpisodeTitle;
+ }
+
+ public long getExpireTimeUtcMillis() {
+ return mExpireTimeUtcMillis;
+ }
+
+ public long getId() {
+ return mId;
+ }
+
+ public String getInputId() {
+ return mInputId;
+ }
+
+ public byte[] getInternalProviderData() {
+ return mInternalProviderData;
+ }
+
+ public int getInternalProviderFlag1() {
+ return mInternalProviderFlag1;
+ }
+
+ public int getInternalProviderFlag2() {
+ return mInternalProviderFlag2;
+ }
+
+ public int getInternalProviderFlag3() {
+ return mInternalProviderFlag3;
+ }
+
+ public int getInternalProviderFlag4() {
+ return mInternalProviderFlag4;
+ }
+
+ public String getLongDescription() {
+ return mLongDescription;
+ }
+
+ public Uri getPosterArt() {
+ return mPosterArt;
+ }
+
+ public boolean isSearchable() {
+ return mSearchable;
+ }
+
+ public String getSeasonNumber() {
+ return mSeasonNumber;
+ }
+
+ public String getSeasonTitle() {
+ return mSeasonTitle;
+ }
+
+ public String getShortDescription() {
+ return mShortDescription;
+ }
+
+ public long getStartTimeUtcMillis() {
+ return mStartTimeUtcMillis;
+ }
+
+ public Uri getThumbnail() {
+ return mThumbnail;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public Uri getUri() {
+ return ContentUris.withAppendedId(RecordedPrograms.CONTENT_URI, mId);
+ }
+
+ public int getVersionNumber() {
+ return mVersionNumber;
+ }
+
+ public int getVideoHeight() {
+ return mVideoHeight;
+ }
+
+ public int getVideoWidth() {
+ return mVideoWidth;
+ }
+
+ /**
+ * Compares everything except {@link #getInternalProviderData()}
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ RecordedProgram that = (RecordedProgram) o;
+ return Objects.equals(mId, that.mId) &&
+ Objects.equals(mChannelId, that.mChannelId) &&
+ Objects.equals(mSeasonNumber, that.mSeasonNumber) &&
+ Objects.equals(mSeasonTitle, that.mSeasonTitle) &&
+ Objects.equals(mEpisodeNumber, that.mEpisodeNumber) &&
+ Objects.equals(mStartTimeUtcMillis, that.mStartTimeUtcMillis) &&
+ Objects.equals(mEndTimeUtcMillis, that.mEndTimeUtcMillis) &&
+ Objects.equals(mVideoWidth, that.mVideoWidth) &&
+ Objects.equals(mVideoHeight, that.mVideoHeight) &&
+ Objects.equals(mSearchable, that.mSearchable) &&
+ Objects.equals(mDataBytes, that.mDataBytes) &&
+ Objects.equals(mDurationMillis, that.mDurationMillis) &&
+ Objects.equals(mExpireTimeUtcMillis, that.mExpireTimeUtcMillis) &&
+ Objects.equals(mInternalProviderFlag1, that.mInternalProviderFlag1) &&
+ Objects.equals(mInternalProviderFlag2, that.mInternalProviderFlag2) &&
+ Objects.equals(mInternalProviderFlag3, that.mInternalProviderFlag3) &&
+ Objects.equals(mInternalProviderFlag4, that.mInternalProviderFlag4) &&
+ Objects.equals(mVersionNumber, that.mVersionNumber) &&
+ Objects.equals(mTitle, that.mTitle) &&
+ Objects.equals(mEpisodeTitle, that.mEpisodeTitle) &&
+ Arrays.equals(mBroadcastGenres, that.mBroadcastGenres) &&
+ Arrays.equals(mCanonicalGenres, that.mCanonicalGenres) &&
+ Objects.equals(mShortDescription, that.mShortDescription) &&
+ Objects.equals(mLongDescription, that.mLongDescription) &&
+ Objects.equals(mAudioLanguage, that.mAudioLanguage) &&
+ Objects.equals(mContentRating, that.mContentRating) &&
+ Objects.equals(mPosterArt, that.mPosterArt) &&
+ Objects.equals(mThumbnail, that.mThumbnail);
+ }
+
+ /**
+ * Hashes based on the ID.
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(mId);
+ }
+
+ @Override
+ public String toString() {
+ return "RecordedProgram"
+ + "[" + mId +
+ "]{ mInputId=" + mInputId +
+ ", mChannelId='" + mChannelId + '\'' +
+ ", mTitle='" + mTitle + '\'' +
+ ", mEpisodeNumber=" + mEpisodeNumber +
+ ", mEpisodeTitle='" + mEpisodeTitle + '\'' +
+ ", mStartTimeUtcMillis=" + mStartTimeUtcMillis +
+ ", mEndTimeUtcMillis=" + mEndTimeUtcMillis +
+ ", mBroadcastGenres=" +
+ (mBroadcastGenres != null ? Arrays.toString(mBroadcastGenres) : "null") +
+ ", mCanonicalGenres=" +
+ (mCanonicalGenres != null ? Arrays.toString(mCanonicalGenres) : "null") +
+ ", mShortDescription='" + mShortDescription + '\'' +
+ ", mLongDescription='" + mLongDescription + '\'' +
+ ", mVideoHeight=" + mVideoHeight +
+ ", mVideoWidth=" + mVideoWidth +
+ ", mAudioLanguage='" + mAudioLanguage + '\'' +
+ ", mContentRating='" + mContentRating + '\'' +
+ ", mPosterArt=" + mPosterArt +
+ ", mThumbnail=" + mThumbnail +
+ ", mSearchable=" + mSearchable +
+ ", mDataUri=" + mDataUri +
+ ", mDataBytes=" + mDataBytes +
+ ", mDurationMillis=" + mDurationMillis +
+ ", mExpireTimeUtcMillis=" + mExpireTimeUtcMillis +
+ ", mInternalProviderData.length=" +
+ (mInternalProviderData == null ? "null" : mInternalProviderData.length) +
+ ", mInternalProviderFlag1=" + mInternalProviderFlag1 +
+ ", mInternalProviderFlag2=" + mInternalProviderFlag2 +
+ ", mInternalProviderFlag3=" + mInternalProviderFlag3 +
+ ", mInternalProviderFlag4=" + mInternalProviderFlag4 +
+ ", mSeasonNumber=" + mSeasonNumber +
+ ", mSeasonTitle=" + mSeasonTitle +
+ ", mVersionNumber=" + mVersionNumber +
+ '}';
+ }
+
+ @Nullable
+ private static String safeToString(@Nullable Object o) {
+ return o == null ? null : o.toString();
+ }
+
+ @Nullable
+ private static String safeEncode(@Nullable String[] genres) {
+ return genres == null ? null : TvContract.Programs.Genres.encode(genres);
+ }
+}
diff --git a/common/src/com/android/tv/common/recording/RecordingTvInputService.java b/common/src/com/android/tv/common/recording/RecordingTvInputService.java
deleted file mode 100644
index 6eea6ae7..00000000
--- a/common/src/com/android/tv/common/recording/RecordingTvInputService.java
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tv.common.recording;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.media.PlaybackParams;
-import android.media.tv.TvContentRating;
-import android.media.tv.TvInputService;
-import android.media.tv.TvTrackInfo;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.view.MotionEvent;
-import android.view.Surface;
-
-import com.android.tv.common.feature.CommonFeatures;
-
-import java.util.List;
-
-/**
- * {@link TvInputService} class that supports recording and playback.
- */
-@TargetApi(Build.VERSION_CODES.M) // TODO(DVR): set to N
-public abstract class RecordingTvInputService extends TvInputService {
- private static final String TAG = "DvrTvInputService";
- private static final boolean DEBUG = true;
-
- @Override
- public final Session onCreateSession(String inputId) {
- if (CommonFeatures.DVR.isEnabled(this)) {
- return new InternalSession(this, inputId);
- } else {
- return onCreatePlaybackSession(inputId);
- }
- }
-
- /**
- * Called when {@link com.android.tv.common.recording.DvrSession#connect} is called.
- */
- protected TvRecording.RecordingSession onCreateDvrSession(String inputId) {
- return null;
- }
-
- protected abstract PlaybackSession onCreatePlaybackSession(String inputId);
-
- private class InternalSession extends TvInputService.Session {
- final String mInputId;
- BaseSession mSessionImpl;
-
- public InternalSession(Context context, String inputId) {
- super(context);
- mInputId = inputId;
- }
-
- @Override
- public void onRelease() {
- if (mSessionImpl != null) {
- mSessionImpl.onRelease();
- }
- }
-
- @Override
- public boolean onSetSurface(Surface surface) {
- return mSessionImpl.onSetSurface(surface);
- }
-
- @Override
- public void onSetStreamVolume(float volume) {
- mSessionImpl.onSetStreamVolume(volume);
- }
-
- @Override
- public boolean onTune(Uri channelUri) {
- return mSessionImpl.onTune(channelUri);
- }
-
- @Override
- public void onAppPrivateCommand(String action, Bundle data) {
- if (action.equals(RecordingUtils.APP_PRIV_CREATE_DVR_SESSION)) {
- if (mSessionImpl == null) {
- mSessionImpl = onCreateDvrSession(mInputId);
- if (mSessionImpl != null) {
- mSessionImpl.setPassthroughSession(this);
- notifySessionEvent(RecordingUtils.EVENT_TYPE_CONNECTED, null);
- }
- }
- } else if (action.equals(RecordingUtils.APP_PRIV_CREATE_PLAYBACK_SESSION)) {
- if (mSessionImpl == null) {
- mSessionImpl = onCreatePlaybackSession(mInputId);
- if (mSessionImpl != null) {
- mSessionImpl.setPassthroughSession(this);
- }
- }
- } else {
- if (mSessionImpl == null) {
- throw new IllegalStateException();
- }
- mSessionImpl.onAppPrivateCommand(action, data);
- }
- }
-
- @Override
- public android.view.View onCreateOverlayView() {
- return mSessionImpl.onCreateOverlayView();
- }
-
- @Override
- public boolean onGenericMotionEvent(android.view.MotionEvent event) {
- return mSessionImpl.onGenericMotionEvent(event);
- }
-
- @Override
- public boolean onKeyDown(int keyCode, android.view.KeyEvent event) {
- return mSessionImpl.onKeyDown(keyCode, event);
- }
-
- @Override
- public boolean onKeyLongPress(int keyCode, android.view.KeyEvent event) {
- return mSessionImpl.onKeyLongPress(keyCode, event);
- }
-
- @Override
- public boolean onKeyMultiple(int keyCode, int count, android.view.KeyEvent event) {
- return mSessionImpl.onKeyMultiple(keyCode, count, event);
- }
-
- @Override
- public boolean onKeyUp(int keyCode, android.view.KeyEvent event) {
- return mSessionImpl.onKeyUp(keyCode, event);
- }
-
- @Override
- public void onOverlayViewSizeChanged(int width, int height) {
- mSessionImpl.onOverlayViewSizeChanged(width, height);
- }
-
- @Override
- public boolean onSelectTrack(int type, String trackId) {
- return mSessionImpl.onSelectTrack(type, trackId);
- }
-
- @Override
- public void onSetMain(boolean isMain) {
- mSessionImpl.onSetMain(isMain);
- }
-
- @Override
- public void onSurfaceChanged(int format, int width, int height) {
- mSessionImpl.onSurfaceChanged(format, width, height);
- }
-
- @Override
- public long onTimeShiftGetCurrentPosition() {
- return mSessionImpl.onTimeShiftGetCurrentPosition();
- }
-
- @Override
- public long onTimeShiftGetStartPosition() {
- return mSessionImpl.onTimeShiftGetStartPosition();
- }
-
- @Override
- public void onTimeShiftPause() {
- mSessionImpl.onTimeShiftPause();
- }
-
- @Override
- public void onTimeShiftResume() {
- mSessionImpl.onTimeShiftResume();
- }
-
- @Override
- public void onTimeShiftSeekTo(long timeMs) {
- mSessionImpl.onTimeShiftSeekTo(timeMs);
- }
-
- @Override
- public void onTimeShiftSetPlaybackParams(PlaybackParams params) {
- mSessionImpl.onTimeShiftSetPlaybackParams(params);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- return mSessionImpl.onTouchEvent(event);
- }
-
- @Override
- public boolean onTrackballEvent(MotionEvent event) {
- return mSessionImpl.onTrackballEvent(event);
- }
-
- @Override
- public boolean onTune(Uri channelUri, Bundle params) {
- return mSessionImpl.onTune(channelUri, params);
- }
-
- @Override
- public void onUnblockContent(TvContentRating unblockedRating) {
- mSessionImpl.onUnblockContent(unblockedRating);
- }
-
- @Override
- public void onSetCaptionEnabled(boolean enabled) {
- mSessionImpl.onSetCaptionEnabled(enabled);
- }
- }
-
- /**
- * Base class for {@link PlaybackSession} and {@link TvRecording.RecordingSession}. Do not use it directly
- * outside of this class.
- */
- public static abstract class BaseSession extends TvInputService.Session {
- private Session mPassthroughSession;
-
- public BaseSession(Context context) {
- super(context);
- }
-
- private void setPassthroughSession(Session passthroughSession) {
- mPassthroughSession = passthroughSession;
- }
-
- @Override
- public void setOverlayViewEnabled(boolean enable) {
- if (mPassthroughSession != null) {
- mPassthroughSession.setOverlayViewEnabled(enable);
- } else {
- super.setOverlayViewEnabled(enable);
- }
- }
-
- @Override
- public void notifyChannelRetuned(Uri channelUri) {
- if (mPassthroughSession != null) {
- mPassthroughSession.notifyChannelRetuned(channelUri);
- } else {
- super.notifyChannelRetuned(channelUri);
- }
- }
-
- @Override
- public void notifyContentAllowed() {
- if (mPassthroughSession != null) {
- mPassthroughSession.notifyContentAllowed();
- } else {
- super.notifyContentAllowed();
- }
- }
-
- @Override
- public void notifyContentBlocked(TvContentRating rating) {
- if (mPassthroughSession != null) {
- mPassthroughSession.notifyContentBlocked(rating);
- } else {
- super.notifyContentBlocked(rating);
- }
- }
-
- @Override
- public void notifySessionEvent(String eventType, Bundle eventArgs) {
- if (mPassthroughSession != null) {
- mPassthroughSession.notifySessionEvent(eventType, eventArgs);
- } else {
- super.notifySessionEvent(eventType, eventArgs);
- }
- }
-
- @Override
- public void notifyTimeShiftStatusChanged(int status) {
- if (mPassthroughSession != null) {
- mPassthroughSession.notifyTimeShiftStatusChanged(status);
- } else {
- super.notifyTimeShiftStatusChanged(status);
- }
- }
-
- @Override
- public void notifyTracksChanged(List<TvTrackInfo> tracks) {
- if (mPassthroughSession != null) {
- mPassthroughSession.notifyTracksChanged(tracks);
- } else {
- super.notifyTracksChanged(tracks);
- }
- }
-
- @Override
- public void notifyTrackSelected(int type, String trackId) {
- if (mPassthroughSession != null) {
- mPassthroughSession.notifyTrackSelected(type, trackId);
- } else {
- super.notifyTrackSelected(type, trackId);
- }
- }
-
- @Override
- public void notifyVideoAvailable() {
- if (mPassthroughSession != null) {
- mPassthroughSession.notifyVideoAvailable();
- } else {
- super.notifyVideoAvailable();
- }
- }
-
- @Override
- public void notifyVideoUnavailable(int reason) {
- if (mPassthroughSession != null) {
- mPassthroughSession.notifyVideoUnavailable(reason);
- } else {
- super.notifyVideoUnavailable(reason);
- }
- }
- }
-
- /**
- * Session linked to {@link android.media.tv.TvView} to tune to a channel or play an recording.
- */
- public static abstract class PlaybackSession extends BaseSession {
- private boolean mIsRecordingPlayback;
-
- public PlaybackSession(Context context) {
- super(context);
- }
-
- /**
- * Returns {@code true}, if the current playback is for a recording.
- */
- public boolean isRecordingPlayback() {
- return mIsRecordingPlayback;
- }
-
- /**
- * Called when it is requested to play an recording {@code mediaUri}. When playback and
- * rendering starts, {@link #notifyVideoAvailable} should be called.
- */
- public void onPlayMedia(Uri mediaUri) { }
-
- /**
- * Notifies TimeShift end position. It should have the form like onTimeShiftEndPosition.
- * But, it's not trivial to add that in the prototyping. The method is recommended to be
- * called inside {@link #onTimeShiftGetStartPosition()}, when a recording is played.
- */
- public void notifyTimeShiftEndPosition(long endPosition) {
- Bundle params = new Bundle();
- params.putLong(RecordingUtils.BUNDLE_TIMESHIFT_END_POSITION, endPosition);
- notifySessionEvent(RecordingUtils.EVENT_TYPE_TIMESHIFT_END_POSITION, params);
- }
-
- @Override
- public final boolean onTune(Uri channelUri, Bundle params) {
- if (params != null && params.getBoolean(RecordingUtils.BUNDLE_IS_DVR, false)) {
- notifySessionEvent(RecordingUtils.EVENT_TYPE_CONNECTED, null);
- return true;
- } else if (params != null && params.containsKey(RecordingUtils.BUNDLE_MEDIA_URI)) {
- mIsRecordingPlayback = true;
- onPlayMedia(Uri.parse(params.getString(RecordingUtils.BUNDLE_MEDIA_URI)));
- return true;
- } else {
- mIsRecordingPlayback = false;
- return onTune(channelUri);
- }
- }
- }
-}
diff --git a/common/src/com/android/tv/common/recording/RecordingUtils.java b/common/src/com/android/tv/common/recording/RecordingUtils.java
deleted file mode 100644
index ae91659f..00000000
--- a/common/src/com/android/tv/common/recording/RecordingUtils.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tv.common.recording;
-
-import android.net.Uri;
-import android.os.Bundle;
-
-public class RecordingUtils {
- static final int ACTION_START_RECORD = 10055;
- static final int ACTION_STOP_RECORD = 10056;
-
- static final String EVENT_TYPE_CONNECTED = "event_type_connected";
- static final String EVENT_TYPE_TIMESHIFT_END_POSITION = "event_type_timeshift_end_position";
-
- static final String APP_PRIV_CREATE_PLAYBACK_SESSION = "app_priv_create_playback_session";
- static final String APP_PRIV_CREATE_DVR_SESSION = "app_priv_create_dvr_session";
-
- // Type: boolean
- static final String BUNDLE_IS_DVR = "bundle_is_dvr";
- // Type: String (Uri)
- static final String BUNDLE_MEDIA_URI = "bundle_media_uri";
- // Type: String
- static final String BUNDLE_CHANNEL_URI = "bundle_channel_uri";
- // Type: long
- static final String BUNDLE_TIMESHIFT_END_POSITION = "timeshift_end_position";
-
- /**
- * Builds a {@link Bundle} with {@code mediaUri}. If the bundle is sent with tune command,
- * the {@code mediaUri} will be played.
- */
- public static Bundle buildMediaUri(Uri mediaUri) {
- Bundle params = new Bundle();
- params.putString(RecordingUtils.BUNDLE_MEDIA_URI, mediaUri.toString());
- return params;
- }
-}
diff --git a/common/src/com/android/tv/common/recording/TvRecording.java b/common/src/com/android/tv/common/recording/TvRecording.java
deleted file mode 100644
index 28a611a0..00000000
--- a/common/src/com/android/tv/common/recording/TvRecording.java
+++ /dev/null
@@ -1,384 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.tv.common.recording;
-
-import android.content.Context;
-import android.media.tv.TvContract;
-import android.media.tv.TvView;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.annotation.IntDef;
-import android.util.Log;
-import android.view.Surface;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * API for making TV Recordings.
- * This class holds both the API under development and the session app private command magic needed
- * to simulate the API.
- */
-public final class TvRecording {
- private static final String TAG = "TvRecording";
- private static final boolean DEBUG = true; // STOPSHIP(DVR)
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({RECORD_STOP_REASON_DISKFULL, RECORD_STOP_REASON_CONFLICT,
- RECORD_STOP_REASON_CONNECT_FAILED, RECORD_STOP_REASON_DISCONNECTED,
- RECORD_STOP_REASON_UNKNOWN})
- public @interface RecordStopReason {
- }
-
- private static final int FIRST_REASON = 1;
- public static final int RECORD_STOP_REASON_DISKFULL = 1;
- public static final int RECORD_STOP_REASON_CONFLICT = 2;
- public static final int RECORD_STOP_REASON_CONNECT_FAILED = 3;
- public static final int RECORD_STOP_REASON_DISCONNECTED = 4;
- public static final int RECORD_STOP_REASON_UNKNOWN = 5;
- private static final int LAST_REASON = 5;
-
- public abstract static class ClientCallback {
- public void onConnected() { }
-
- public void onDisconnected() { }
-
- public void onRecordStarted(Uri mediaUri) { }
-
- public void onRecordStopped(Uri mediaUri, @RecordStopReason int reason) { }
-
- public void onRecordDeleted(Uri mediaUri) { }
-
- public void onRecordDeleteFailed(Uri mediaUri, int reason) { }
-
- public void onCapabilityReceived(RecordingCapability capability) { }
- }
-
- public interface RecordingClientApi {
- void release();
-
- void startRecord(Uri channelUri, Uri mediaUri);
-
- void stopRecord();
-
- void delete(Uri mediaUri);
-
- void getCapability();
- }
-
- public interface RecordingSessionApi {
- /**
- * Start recording on {@code channelUri}.
- * <p>{@link RecordingSession#notifyRecordStarted(Uri)} should be called as soon as the
- * recording is started.
- */
- void onStartRecord(Uri channelUri, Uri mediaUri);
-
- /**
- * Called when it stops to record.
- */
- void onStopRecord();
-
- /**
- * Called when it is requested to delete {@code mediaUri}.
- */
- void onDelete(Uri mediaUri);
-
- /**
- * Called when the client request {@link RecordingCapability}.
- */
- RecordingCapability onGetCapability();
- }
-
- ///////////
- // BELOW IS IMPLEMENTATION DETAILS OFTEN SPECIFIC TO USING APP PRIVATE COMMANDS
- //////////
-
- private static final String PREFIX = "record_";
-
- private static final String APP_PRIV_DELETE = PREFIX + "app_priv_delete";
- private static final String APP_PRIV_GET_CAPABILITY = PREFIX + "app_priv_get_capability";
- private static final String APP_PRIV_START_RECORD = PREFIX + "app_priv_start_record";
- private static final String APP_PRIV_STOP_RECORD = PREFIX + "app_priv_stop_record";
-
- private static final String EVENT_TYPE_DELETED = PREFIX + "event_type_deleted";
- private static final String EVENT_TYPE_DELETE_FAILED = PREFIX + "event_type_delete_failed";
- private static final String EVENT_TYPE_CAPABILITY_RECEIVED = PREFIX
- + "event_type_capability_received";
- private static final String EVENT_TYPE_RECORD_STARTED = PREFIX + "event_type_record_started";
- private static final String EVENT_TYPE_RECORD_STOPPED = PREFIX + "event_type_record_stopped";
-
- // Type: int
- private static final String BUNDLE_STOPPED_REASON = PREFIX + "stopped_reason";
- // Type: int
- private static final String BUNDLE_DELETE_FAILED_REASON = PREFIX + "delete_failed_reason";
- // Type: RecordingCapability
- private static final String BUNDLE_CAPABILITY = PREFIX + "capability";
-
-
- /**
- * Session linked to {@link TvRecordingClient} to record contents.
- */
- public static abstract class RecordingSession extends RecordingTvInputService.BaseSession
- implements RecordingSessionApi {
- private final static String TAG = "RecordingSession";
-
- public RecordingSession(Context context) {
- super(context);
- }
-
- @Override
- public final boolean onTune(Uri channelUri) {
- // no-op
- return false;
- }
-
- @Override
- public final boolean onSetSurface(Surface surface) {
- // no-op
- return false;
- }
-
- @Override
- public final void onSetStreamVolume(float volume) {
- // no-op
- }
-
- @Override
- public final void onSetCaptionEnabled(boolean enabled) {
- // no-op
- }
-
- /**
- * Notifies when recording starts. It is an response of {@link #onStartRecord}.
- */
- public final void notifyRecordStarted(Uri mediaUri) {
- notifySessionEvent(EVENT_TYPE_RECORD_STARTED, RecordingUtils.buildMediaUri(mediaUri));
- }
-
- /**
- * Notifies when recording is unexpectedly stopped.
- */
- public final void notifyRecordUnexpectedlyStopped(Uri mediaUri, int reason) {
- Bundle params = RecordingUtils.buildMediaUri(mediaUri);
- params.putInt(BUNDLE_STOPPED_REASON, reason);
- notifySessionEvent(EVENT_TYPE_RECORD_STOPPED, params);
- }
-
- /**
- * Notifies when the recording {@code mediaUri} is deleted.
- */
- public final void notifyDeleted(Uri mediaUri) {
- notifySessionEvent(EVENT_TYPE_DELETED, RecordingUtils.buildMediaUri(mediaUri));
- }
-
- /**
- * Notifies when the deletion of the recording {@code mediaUri} is requested through
- * {@link #onDelete} but failed.
- */
- public final void notifyDeleteFailed(Uri mediaUri, int reason) {
- Bundle params = RecordingUtils.buildMediaUri(mediaUri);
- params.putInt(BUNDLE_DELETE_FAILED_REASON, reason);
- notifySessionEvent(EVENT_TYPE_DELETE_FAILED, params);
- }
-
- @Override
- public final void onAppPrivateCommand(String action, Bundle data) {
- if (DEBUG) Log.d(TAG, "onAppPrivateCommand(" + action + ", " + data + ")");
- switch (action) {
- case APP_PRIV_GET_CAPABILITY:
- RecordingCapability capability = onGetCapability();
- Bundle params = new Bundle();
- params.putParcelable(BUNDLE_CAPABILITY, capability);
- notifySessionEvent(EVENT_TYPE_CAPABILITY_RECEIVED, params);
- break;
- case APP_PRIV_DELETE:
- onDelete(Uri.parse(data.getString(RecordingUtils.BUNDLE_CHANNEL_URI)));
- break;
- case APP_PRIV_START_RECORD:
- onStartRecord(Uri.parse(data.getString(RecordingUtils.BUNDLE_CHANNEL_URI)),
- Uri.parse(data.getString(RecordingUtils.BUNDLE_MEDIA_URI)));
- break;
- case APP_PRIV_STOP_RECORD:
- onStopRecord();
- break;
- }
- }
- }
-
- /**
- * A session used for recording.
- */
- public static class TvRecordingClient implements RecordingClientApi {
- private static final String TAG = "DvrSessionClient";
-
- private ClientCallback mCallback;
- private TvView mTvView;
-
- public TvRecordingClient(Context context) {
- if (DEBUG) {
- Log.d(TAG, "creating client");
- }
- mTvView = new TvView(context);
- }
-
- /**
- * Connects the session to a specific input {@code inputId}.
- */
- public void connect(String inputId, ClientCallback callback) {
- if (DEBUG) {
- Log.d(TAG, "connect " + inputId + " with " + callback);
- }
- mCallback = callback;
- Bundle bundle = new Bundle();
- bundle.putBoolean(RecordingUtils.BUNDLE_IS_DVR, true);
- mTvView.tune(inputId, TvContract.buildChannelUri(0), bundle);
- mTvView.sendAppPrivateCommand(RecordingUtils.APP_PRIV_CREATE_DVR_SESSION, null);
- mTvView.setCallback(new TvView.TvInputCallback() {
- @Override
- public void onConnectionFailed(String inputId) {
- if (mCallback == null) {
- return;
- }
- mCallback.onDisconnected();
- }
-
- @Override
- public void onDisconnected(String inputId) {
- if (mCallback == null) {
- return;
- }
- mCallback.onDisconnected();
- }
-
- @Override
- public void onEvent(String inputId, String eventType, Bundle eventArgs) {
- if (mCallback == null) {
- return;
- }
- String mediaUriString = eventArgs == null ? null
- : eventArgs.getString(RecordingUtils.BUNDLE_MEDIA_URI, null);
- Uri mediaUri = mediaUriString == null ? null : Uri.parse(mediaUriString);
- switch (eventType) {
- case RecordingUtils.EVENT_TYPE_CONNECTED:
- mCallback.onConnected();
- break;
- case EVENT_TYPE_DELETED:
- mCallback.onRecordDeleted(mediaUri);
- break;
- case EVENT_TYPE_DELETE_FAILED: {
- // TODO(DVR) use reasons from API
- int reason = eventArgs == null ? 0
- : eventArgs.getInt(BUNDLE_DELETE_FAILED_REASON);
- mCallback.onRecordDeleteFailed(mediaUri, reason);
- break;
- }
- case EVENT_TYPE_CAPABILITY_RECEIVED: {
- RecordingCapability capability = eventArgs
- .getParcelable(BUNDLE_CAPABILITY);
- mCallback.onCapabilityReceived(capability);
- break;
- }
- case EVENT_TYPE_RECORD_STARTED:
- mCallback.onRecordStarted(mediaUri);
- break;
- case EVENT_TYPE_RECORD_STOPPED: {
- int reason = getRecordStopReason(eventArgs);
- mCallback.onRecordStopped(mediaUri, reason);
- break;
- }
- }
- }
-
- // TODO: handle track select.
- });
- }
-
- /**
- * Releases the session.
- */
- @Override
- public void release() {
- if (DEBUG) {
- Log.d(TAG, "release " + this);
- }
- mTvView.reset();
- mCallback = null;
- }
-
- /**
- * Starts recording.
- */
- @Override
- public void startRecord(Uri channelUri, Uri mediaUri) {
- if (DEBUG) {
- Log.d(TAG, "startRecord " + channelUri + ", " + mediaUri);
- }
- Bundle params = RecordingUtils.buildMediaUri(mediaUri);
- params.putString(RecordingUtils.BUNDLE_CHANNEL_URI, channelUri.toString());
- mTvView.sendAppPrivateCommand(APP_PRIV_START_RECORD, params);
- }
-
- /**
- * Stops recording.
- */
- @Override
- public void stopRecord() {
- if (DEBUG) {
- Log.d(TAG, "stopRecord " + this);
- }
- mTvView.sendAppPrivateCommand(APP_PRIV_STOP_RECORD, null);
- }
-
- /**
- * Deletes a recorded media.
- */
- @Override
- public void delete(Uri mediaUri) {
- mTvView.sendAppPrivateCommand(APP_PRIV_DELETE,
- RecordingUtils.buildMediaUri(mediaUri));
- }
-
- @Override
- public void getCapability() {
- mTvView.sendAppPrivateCommand(APP_PRIV_GET_CAPABILITY, null);
- }
-
- @Override
- public String toString() {
- return TvRecordingClient.class.getName() + "{" + "callBack=" + mCallback + "}";
- }
- }
-
- @SuppressWarnings("ResourceType")
- @RecordStopReason
- private static int getRecordStopReason(Bundle eventArgs) {
- if(eventArgs == null) {
- if (DEBUG) Log.d(TAG, "Null stop reason");
- return RECORD_STOP_REASON_UNKNOWN;
- }
- int reason = eventArgs.getInt(BUNDLE_STOPPED_REASON);
- if (reason < FIRST_REASON || reason > LAST_REASON) {
- if (DEBUG) Log.d(TAG, "Unknown stop reason " + reason);
- reason = RECORD_STOP_REASON_UNKNOWN;
- }
- return reason;
- }
-
- private TvRecording() {
- }
-}
diff --git a/common/src/com/android/tv/common/ui/setup/animation/FadeAndShortSlide.java b/common/src/com/android/tv/common/ui/setup/animation/FadeAndShortSlide.java
index 28ab97de..5c57d84d 100644
--- a/common/src/com/android/tv/common/ui/setup/animation/FadeAndShortSlide.java
+++ b/common/src/com/android/tv/common/ui/setup/animation/FadeAndShortSlide.java
@@ -223,6 +223,9 @@ public class FadeAndShortSlide extends Visibility {
float startX = mSlideCalculator.getGoneX(sceneRoot, view, position, mDistance);
final Animator slideAnimator = TranslationAnimationCreator.createAnimation(view, endValues,
left, startX, endX, APPEAR_INTERPOLATOR, this);
+ if (slideAnimator == null) {
+ return null;
+ }
mFade.setInterpolator(APPEAR_INTERPOLATOR);
final AnimatorSet set = new AnimatorSet();
set.play(slideAnimator).with(mFade.onAppear(sceneRoot, view, startValues, endValues));
@@ -245,9 +248,15 @@ public class FadeAndShortSlide extends Visibility {
float endX = mSlideCalculator.getGoneX(sceneRoot, view, position, mDistance);
final Animator slideAnimator = TranslationAnimationCreator.createAnimation(view,
startValues, left, startX, endX, DISAPPEAR_INTERPOLATOR, this);
+ if (slideAnimator == null) { // slideAnimator is null if startX == endX
+ return null;
+ }
+
mFade.setInterpolator(DISAPPEAR_INTERPOLATOR);
- final AnimatorSet set = new AnimatorSet();
final Animator fadeAnimator = mFade.onDisappear(sceneRoot, view, startValues, endValues);
+ if (fadeAnimator == null) {
+ return null;
+ }
fadeAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animator) {
@@ -255,6 +264,8 @@ public class FadeAndShortSlide extends Visibility {
view.setAlpha(0.0f);
}
});
+
+ final AnimatorSet set = new AnimatorSet();
set.play(slideAnimator).with(fadeAnimator);
Long delay = (Long) startValues.values.get(PROPNAME_DELAY);
if (delay != null) {
diff --git a/common/src/com/android/tv/common/ui/setup/leanback/OnboardingFragment.java b/common/src/com/android/tv/common/ui/setup/leanback/OnboardingFragment.java
deleted file mode 100644
index adbd98c2..00000000
--- a/common/src/com/android/tv/common/ui/setup/leanback/OnboardingFragment.java
+++ /dev/null
@@ -1,531 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tv.common.ui.setup.leanback;
-
-import android.animation.Animator;
-import android.animation.AnimatorInflater;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.app.Fragment;
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnKeyListener;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver.OnPreDrawListener;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.tv.common.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * An OnboardingFragment provides a common and simple way to build onboarding screen for
- * applications.
- * <p>
- * <h3>Building the screen</h3>
- * The view structure of onboarding screen is composed of the common parts and custom parts. The
- * common parts are composed of title, description and page navigator and the custom parts are
- * composed of background, contents and foreground.
- * <p>
- * To build the screen views, the inherited class should override:
- * <ul>
- * <li>{@link #onCreateBackgroundView} to provide the background view. Background view has the same
- * size as the screen and the lowest z-order.</li>
- * <li>{@link #onCreateContentView} to provide the contents view. The content view is located in
- * the content area at the center of the screen.</li>
- * <li>{@link #onCreateForegroundView} to provide the foreground view. Foreground view has the same
- * size as the screen and the highest z-order</li>
- * </ul>
- * <p>
- * Each of these methods can return {@code null} if the application doesn't want to provide it.
- * <p>
- * <h3>Page information</h3>
- * The onboarding screen may have several pages which explain the functionality of the application.
- * The inherited class should provide the page information by overriding the methods:
- * <p>
- * <ul>
- * <li>{@link #getPageCount} to provide the number of pages.</li>
- * <li>{@link #getPageTitle} to provide the title of the page.</li>
- * <li>{@link #getPageDescription} to provide the description of the page.</li>
- * </ul>
- * <p>
- * <h3><a name="logoAnimation">Logo Splash Animation</a></h3>
- * When onboarding screen appears, the logo splash animation is played by default. The animation
- * fades in the logo image, pauses in a few seconds and fades it out. To support this animation with
- * its own logo image, the inherited class should override the following method.
- * <p>
- * <ul>
- * <li>{@link #getLogoResourceId()}</li>
- * </ul>
- * <p>
- * <h3>Animation</h3>
- * This page has three kinds of animations:
- * <p>
- * <ul>
- * <li><b>Logo splash animation</b> which starts as soon as onboarding screen is shown as described
- * in <a href="#logoAnimation">Logo Splash Animation</a>.</li>
- * <li><b>Page enter animation</b> which runs just after the logo animation finishes. The
- * application can run the animations of their custom views by overriding
- * {@link #onStartEnterAnimation}.</li>
- * <li><b>Page change animation</b> which runs when the page changes. The pages can move backward or
- * forward direction and the application can start the page change animations by overriding
- * {@link #onStartPageChangeAnimation}.</li>
- * </ul>
- * <p>
- * <h3>Finishing the screen</h3>
- * <p>
- * If the user finishes the onboarding screen after navigating all the pages,
- * {@link #onFinishFragment} is called. The inherited class can override this method to show another
- * fragment or activity, or just remove this fragment.
- *
- * @hide
- */
-abstract public class OnboardingFragment extends Fragment {
- private static final long LOGO_SPLASH_PAUSE_DURATION_MS = 1333;
- private static final long START_DELAY_TITLE_MS = 33;
- private static final long START_DELAY_DESCRIPTION_MS = 33;
-
- private static final long HEADER_ANIMATION_DURATION_MS = 417;
- private static final long DESCRIPTION_START_DELAY_MS = 33;
- private static final long HEADER_APPEAR_DELAY_MS = 500;
- private static final int SLIDE_DISTANCE = 60;
-
- private static int sSlideDistance;
-
- private static final TimeInterpolator HEADER_APPEAR_INTERPOLATOR = new DecelerateInterpolator();
- private static final TimeInterpolator HEADER_DISAPPEAR_INTERPOLATOR
- = new AccelerateInterpolator();
-
- private PagingIndicator mPageIndicator;
- private View mStartButton;
- private ImageView mLogoView;
- private TextView mTitleView;
- private TextView mDescriptionView;
-
- private boolean mEnterTransitionFinished;
- private int mCurrentPageIndex;
-
- private AnimatorSet mAnimator;
-
- /**
- * Called to have the inherited class create its own start animation. The start animation runs
- * after logo splash animation ends.
- */
- abstract protected void onStartEnterAnimation();
-
- private final OnClickListener mOnClickListener = new OnClickListener() {
- @Override
- public void onClick(View view) {
- if (!mEnterTransitionFinished) {
- // Do not change page until the enter transition finishes.
- return;
- }
- if (mCurrentPageIndex == getPageCount() - 1) {
- onFinishFragment();
- } else {
- ++mCurrentPageIndex;
- onPageChanged(mCurrentPageIndex - 1);
- }
- }
- };
-
- private final OnKeyListener mOnKeyListener = new OnKeyListener() {
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (!mEnterTransitionFinished) {
- // Ignore key event until the enter transition finishes.
- return keyCode != KeyEvent.KEYCODE_BACK;
- }
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- return false;
- }
- switch (keyCode) {
- case KeyEvent.KEYCODE_BACK:
- if (mCurrentPageIndex == 0) {
- return false;
- }
- // pass through
- case KeyEvent.KEYCODE_DPAD_LEFT:
- if (mCurrentPageIndex > 0) {
- --mCurrentPageIndex;
- onPageChanged(mCurrentPageIndex + 1);
- }
- return true;
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- if (mCurrentPageIndex < getPageCount() - 1) {
- ++mCurrentPageIndex;
- onPageChanged(mCurrentPageIndex - 1);
- }
- return true;
- }
- return false;
- }
- };
-
- @Nullable
- @Override
- public View onCreateView(LayoutInflater inflater, final ViewGroup container,
- Bundle savedInstanceState) {
- ViewGroup view = (ViewGroup) inflater.inflate(R.layout.lb_onboarding_fragment, container,
- false);
- mPageIndicator = (PagingIndicator) view.findViewById(R.id.page_indicator);
- mPageIndicator.setPageCount(getPageCount());
- mPageIndicator.setOnClickListener(mOnClickListener);
- mPageIndicator.setOnKeyListener(mOnKeyListener);
- mStartButton = view.findViewById(R.id.button_start);
- mStartButton.setOnClickListener(mOnClickListener);
- mStartButton.setOnKeyListener(mOnKeyListener);
- mLogoView = (ImageView) view.findViewById(R.id.logo);
- mLogoView.setImageResource(getLogoResourceId());
- mTitleView = (TextView) view.findViewById(R.id.title);
- mTitleView.setText(getPageTitle(0));
- mDescriptionView = (TextView) view.findViewById(R.id.description);
- mDescriptionView.setText(getPageDescription(0));
- if (sSlideDistance == 0) {
- sSlideDistance = (int) (SLIDE_DISTANCE * getActivity().getResources()
- .getDisplayMetrics().scaledDensity);
- }
- mCurrentPageIndex = 0;
- mPageIndicator.onPageSelected(0, false);
- view.requestFocus();
- if (getLogoResourceId() != 0) {
- container.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- container.getViewTreeObserver().removeOnPreDrawListener(this);
- startLogoAnimation();
- return true;
- }
- });
- } else {
- onLogoAnimationFinished();
- }
- return view;
- }
-
- private void startLogoAnimation() {
- mLogoView.setVisibility(View.VISIBLE);
- Animator inAnimator = AnimatorInflater.loadAnimator(getActivity(),
- R.animator.lb_onboarding_logo_enter);
- Animator outAnimator = AnimatorInflater.loadAnimator(getActivity(),
- R.animator.lb_onboarding_logo_exit);
- outAnimator.setStartDelay(LOGO_SPLASH_PAUSE_DURATION_MS);
- AnimatorSet animator = new AnimatorSet();
- animator.playSequentially(inAnimator, outAnimator);
- animator.setTarget(mLogoView);
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mEnterTransitionFinished = true;
- if (getActivity() != null) {
- onLogoAnimationFinished();
- onStartEnterAnimation();
- }
- }
- });
- animator.start();
- }
-
- private void onLogoAnimationFinished() {
- mLogoView.setVisibility(View.GONE);
- // Create custom views.
- LayoutInflater inflater = LayoutInflater.from(getActivity());
- ViewGroup backgroundContainer = (ViewGroup) getView().findViewById(
- R.id.background_container);
- View background = onCreateBackgroundView(inflater, backgroundContainer);
- if (background != null) {
- backgroundContainer.setVisibility(View.VISIBLE);
- backgroundContainer.addView(background);
- }
- ViewGroup contentContainer = (ViewGroup) getView().findViewById(R.id.content_container);
- View content = onCreateContentView(inflater, contentContainer);
- if (content != null) {
- contentContainer.setVisibility(View.VISIBLE);
- contentContainer.addView(content);
- }
- ViewGroup foregroundContainer = (ViewGroup) getView().findViewById(
- R.id.foreground_container);
- View foreground = onCreateForegroundView(inflater, foregroundContainer);
- if (foreground != null) {
- foregroundContainer.setVisibility(View.VISIBLE);
- foregroundContainer.addView(foreground);
- }
- // Make views visible which were invisible while logo animation is running.
- getView().findViewById(R.id.page_container).setVisibility(View.VISIBLE);
- getView().findViewById(R.id.content_container).setVisibility(View.VISIBLE);
-
- List<Animator> animators = new ArrayList<>();
- Animator animator = AnimatorInflater.loadAnimator(getActivity(),
- R.animator.lb_onboarding_page_indicator_enter);
- if (getPageCount() <= 1) {
- // Start button
- mStartButton.setVisibility(View.VISIBLE);
- animator.setTarget(mStartButton);
- } else {
- // Page indicator
- mPageIndicator.setVisibility(View.VISIBLE);
- animator.setTarget(mPageIndicator);
- }
- animators.add(animator);
- // Header title
- View view = getActivity().findViewById(R.id.title);
- view.setAlpha(0);
- animator = AnimatorInflater.loadAnimator(getActivity(),
- R.animator.lb_onboarding_title_enter);
- animator.setStartDelay(START_DELAY_TITLE_MS);
- animator.setTarget(view);
- animators.add(animator);
- // Header description
- view = getActivity().findViewById(R.id.description);
- view.setAlpha(0);
- animator = AnimatorInflater.loadAnimator(getActivity(),
- R.animator.lb_onboarding_description_enter);
- animator.setStartDelay(START_DELAY_DESCRIPTION_MS);
- animator.setTarget(view);
- animators.add(animator);
- mAnimator = new AnimatorSet();
- mAnimator.playTogether(animators);
- mAnimator.start();
- onStartEnterAnimation();
- // Search focus and give the focus to the appropriate child which has become visible.
- getView().requestFocus();
- }
-
- /**
- * Returns the page count.
- *
- * @return The page count.
- */
- abstract protected int getPageCount();
-
- /**
- * Returns the title of the given page.
- *
- * @param pageIndex The page index.
- *
- * @return The title of the page.
- */
- abstract protected String getPageTitle(int pageIndex);
-
- /**
- * Returns the description of the given page.
- *
- * @param pageIndex The page index.
- *
- * @return The description of the page.
- */
- abstract protected String getPageDescription(int pageIndex);
-
- /**
- * Returns the index of the current page.
- *
- * @return The index of the current page.
- */
- protected final int getCurrentPageIndex() {
- return mCurrentPageIndex;
- }
-
- /**
- * Returns the resource ID of the splash logo image.
- *
- * @return The resource ID of the splash logo image.
- */
- abstract protected int getLogoResourceId();
-
- /**
- * Called to have the inherited class create background view. This is optional and the fragment
- * which doesn't have the background view can return {@code null}. This is called inside
- * {@link #onCreateView}.
- *
- * @param inflater The LayoutInflater object that can be used to inflate the views,
- * @param container The parent view that the additional views are attached to.The fragment
- * should not add the view by itself.
- *
- * @return The background view for the onboarding screen, or {@code null}.
- */
- @Nullable
- abstract protected View onCreateBackgroundView(LayoutInflater inflater, ViewGroup container);
-
- /**
- * Called to have the inherited class create content view. This is optional and the fragment
- * which doesn't have the content view can return {@code null}. This is called inside
- * {@link #onCreateView}.
- *
- * <p>The content view would be located at the center of the screen.
- *
- * @param inflater The LayoutInflater object that can be used to inflate the views,
- * @param container The parent view that the additional views are attached to.The fragment
- * should not add the view by itself.
- *
- * @return The content view for the onboarding screen, or {@code null}.
- */
- @Nullable
- abstract protected View onCreateContentView(LayoutInflater inflater, ViewGroup container);
-
- /**
- * Called to have the inherited class create foreground view. This is optional and the fragment
- * which doesn't need the foreground view can return {@code null}. This is called inside
- * {@link #onCreateView}.
- *
- * <p>This foreground view would have the highest z-order.
- *
- * @param inflater The LayoutInflater object that can be used to inflate the views,
- * @param container The parent view that the additional views are attached to.The fragment
- * should not add the view by itself.
- *
- * @return The foreground view for the onboarding screen, or {@code null}.
- */
- @Nullable
- abstract protected View onCreateForegroundView(LayoutInflater inflater, ViewGroup container);
-
- /**
- * Called when the onboarding flow finishes.
- */
- protected void onFinishFragment() { }
-
- /**
- * Called when the page changes.
- */
- private void onPageChanged(int previousPage) {
- if (mAnimator != null) {
- mAnimator.end();
- }
- mPageIndicator.onPageSelected(mCurrentPageIndex, true);
-
- List<Animator> animators = new ArrayList<>();
- // Header animation
- Animator fadeAnimator = null;
- if (previousPage < getCurrentPageIndex()) {
- // sliding to left
- animators.add(createAnimator(mTitleView, false, Gravity.START, 0));
- animators.add(fadeAnimator = createAnimator(mDescriptionView, false, Gravity.START,
- DESCRIPTION_START_DELAY_MS));
- animators.add(createAnimator(mTitleView, true, Gravity.END,
- HEADER_APPEAR_DELAY_MS));
- animators.add(createAnimator(mDescriptionView, true, Gravity.END,
- HEADER_APPEAR_DELAY_MS + DESCRIPTION_START_DELAY_MS));
- } else {
- // sliding to right
- animators.add(createAnimator(mTitleView, false, Gravity.END, 0));
- animators.add(fadeAnimator = createAnimator(mDescriptionView, false, Gravity.END,
- DESCRIPTION_START_DELAY_MS));
- animators.add(createAnimator(mTitleView, true, Gravity.START,
- HEADER_APPEAR_DELAY_MS));
- animators.add(createAnimator(mDescriptionView, true, Gravity.START,
- HEADER_APPEAR_DELAY_MS + DESCRIPTION_START_DELAY_MS));
- }
- final int currentPageIndex = getCurrentPageIndex();
- fadeAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mTitleView.setText(getPageTitle(currentPageIndex));
- mDescriptionView.setText(getPageDescription(currentPageIndex));
- }
- });
-
- // Animator for switching between page indicator and button.
- if (getCurrentPageIndex() == getPageCount() - 1) {
- mStartButton.setVisibility(View.VISIBLE);
- Animator navigatorFadeOutAnimator = AnimatorInflater.loadAnimator(getActivity(),
- R.animator.lb_onboarding_page_indicator_fade_out);
- navigatorFadeOutAnimator.setTarget(mPageIndicator);
- Animator buttonFadeInAnimator = AnimatorInflater.loadAnimator(getActivity(),
- R.animator.lb_onboarding_start_button_fade_in);
- buttonFadeInAnimator.setTarget(mStartButton);
- animators.add(navigatorFadeOutAnimator);
- navigatorFadeOutAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mPageIndicator.setVisibility(View.GONE);
- }
- });
- animators.add(buttonFadeInAnimator);
- } else if (previousPage == getPageCount() - 1) {
- mPageIndicator.setVisibility(View.VISIBLE);
- Animator navigatorFadeInAnimator = AnimatorInflater.loadAnimator(getActivity(),
- R.animator.lb_onboarding_page_indicator_fade_in);
- navigatorFadeInAnimator.setTarget(mPageIndicator);
- Animator buttonFadeOutAnimator = AnimatorInflater.loadAnimator(getActivity(),
- R.animator.lb_onboarding_start_button_fade_out);
- buttonFadeOutAnimator.setTarget(mStartButton);
- buttonFadeOutAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mStartButton.setVisibility(View.GONE);
- }
- });
- mAnimator = new AnimatorSet();
- mAnimator.playTogether(navigatorFadeInAnimator, buttonFadeOutAnimator);
- mAnimator.start();
- }
- mAnimator = new AnimatorSet();
- mAnimator.playTogether(animators);
- mAnimator.start();
- onStartPageChangeAnimation(previousPage);
- }
-
- private Animator createAnimator(View view, boolean fadeIn, int slideDirection,
- long startDelay) {
- boolean isLtr = getView().getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
- boolean slideRight = (isLtr && slideDirection == Gravity.END)
- || (!isLtr && slideDirection == Gravity.START)
- || slideDirection == Gravity.RIGHT;
- Animator fadeAnimator;
- Animator slideAnimator;
- if (fadeIn) {
- fadeAnimator = ObjectAnimator.ofFloat(view, View.ALPHA, 0.0f, 1.0f);
- slideAnimator = ObjectAnimator.ofFloat(view, View.TRANSLATION_X,
- slideRight ? sSlideDistance : -sSlideDistance, 0);
- fadeAnimator.setInterpolator(HEADER_APPEAR_INTERPOLATOR);
- slideAnimator.setInterpolator(HEADER_APPEAR_INTERPOLATOR);
- } else {
- fadeAnimator = ObjectAnimator.ofFloat(view, View.ALPHA, 1.0f, 0.0f);
- slideAnimator = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, 0,
- slideRight ? sSlideDistance : -sSlideDistance);
- fadeAnimator.setInterpolator(HEADER_DISAPPEAR_INTERPOLATOR);
- slideAnimator.setInterpolator(HEADER_DISAPPEAR_INTERPOLATOR);
- }
- fadeAnimator.setDuration(HEADER_ANIMATION_DURATION_MS);
- fadeAnimator.setTarget(view);
- slideAnimator.setDuration(HEADER_ANIMATION_DURATION_MS);
- slideAnimator.setTarget(view);
- AnimatorSet animator = new AnimatorSet();
- animator.playTogether(fadeAnimator, slideAnimator);
- if (startDelay > 0) {
- animator.setStartDelay(startDelay);
- }
- return animator;
- }
-
- /**
- * Called to have the inherited class run its own page change animation
- *
- * @param previousPage The previous page.
- */
- abstract protected void onStartPageChangeAnimation(int previousPage);
-}
diff --git a/common/src/com/android/tv/common/ui/setup/leanback/PagingIndicator.java b/common/src/com/android/tv/common/ui/setup/leanback/PagingIndicator.java
deleted file mode 100644
index e2c9be72..00000000
--- a/common/src/com/android/tv/common/ui/setup/leanback/PagingIndicator.java
+++ /dev/null
@@ -1,377 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tv.common.ui.setup.leanback;
-
-import android.animation.Animator;
-import android.animation.AnimatorInflater;
-import android.animation.AnimatorSet;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.os.Build;
-import android.support.annotation.ColorInt;
-import android.support.annotation.VisibleForTesting;
-import android.util.AttributeSet;
-import android.view.View;
-
-import com.android.tv.common.R;
-import com.android.tv.common.annotation.UsedByReflection;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A page indicator with dots.
- * @hide
- */
-public class PagingIndicator extends View {
- // attribute
- private final int mDotDiameter;
- private final int mDotRadius;
- private final int mDotGap;
- private final int mArrowDiameter;
- private final int mArrowRadius;
- private final int mArrowGap;
- private final int mShadowRadius;
- private Dot[] mDots;
- // X position when the dot is selected.
- private int[] mDotSelectedX;
- // X position when the dot is located to the left of the selected dot.
- private int[] mDotSelectedLeftX;
- // X position when the dot is located to the right of the selected dot.
- private int[] mDotSelectedRightX;
- private int mDotCenterY;
-
- // state
- private int mPageCount;
- private int mCurrentPage;
- private int mPreviousPage;
-
- // drawing
- @ColorInt
- private final int mDotFgSelectColor;
- private final Paint mBgPaint;
- private final Paint mFgPaint;
- private final Animator mShowAnimator;
- private final Animator mHideAnimator;
- private final AnimatorSet mAnimator = new AnimatorSet();
- private final Bitmap mArrow;
- private final Rect mArrowRect;
- private final float mArrowToBgRatio;
-
- public PagingIndicator(Context context) {
- this(context, null, 0);
- }
-
- public PagingIndicator(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public PagingIndicator(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- Resources res = getResources();
- mDotRadius = res.getDimensionPixelSize(R.dimen.lb_page_indicator_dot_radius);
- mDotDiameter = mDotRadius * 2;
- mDotGap = res.getDimensionPixelSize(R.dimen.lb_page_indicator_dot_gap);
- mArrowGap = res.getDimensionPixelSize(R.dimen.lb_page_indicator_arrow_gap);
- mArrowDiameter = res.getDimensionPixelSize(R.dimen.lb_page_indicator_arrow_diameter);
- mArrowRadius = mArrowDiameter / 2;
- mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mDotFgSelectColor = res.getColor(R.color.lb_page_indicator_arrow_background);
- int bgColor = res.getColor(R.color.lb_page_indicator_dot);
- int shadowColor = res.getColor(R.color.lb_page_indicator_arrow_shadow);
- mBgPaint.setColor(bgColor);
- mShadowRadius = res.getDimensionPixelSize(R.dimen.lb_page_indicator_arrow_shadow_radius);
- mFgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- int shadowOffset = res.getDimensionPixelSize(R.dimen.lb_page_indicator_arrow_shadow_offset);
- mFgPaint.setShadowLayer(mShadowRadius, shadowOffset, shadowOffset, shadowColor);
- mArrow = BitmapFactory.decodeResource(res, R.drawable.lb_ic_nav_arrow);
- mArrowRect = new Rect(0, 0, mArrow.getWidth(), mArrow.getHeight());
- mArrowToBgRatio = (float) mArrow.getWidth() / (float) mArrowDiameter;
- // Initialize animations.
- List<Animator> animators = new ArrayList<>();
- mShowAnimator = AnimatorInflater.loadAnimator(getContext(),
- R.animator.lb_page_indicator_dot_show);
- mHideAnimator = AnimatorInflater.loadAnimator(getContext(),
- R.animator.lb_page_indicator_dot_hide);
- animators.add(mShowAnimator);
- animators.add(mHideAnimator);
- mAnimator.playTogether(animators);
- // Use software layer to show shadows.
- setLayerType(View.LAYER_TYPE_SOFTWARE, null);
- }
-
- /**
- * Sets the page count.
- */
- public void setPageCount(int pages) {
- if (pages <= 0) {
- throw new IllegalArgumentException("The page count should be a positive integer");
- }
- mPageCount = pages;
- mDots = new Dot[mPageCount];
- for (int i = 0; i < mPageCount; ++i) {
- mDots[i] = new Dot();
- }
- calculateDotPositions();
- setSelectedPage(0);
- }
-
- /**
- * Called when the page has been selected.
- */
- public void onPageSelected(int pageIndex, boolean withAnimation) {
- if (mCurrentPage == pageIndex) {
- return;
- }
- if (mAnimator.isStarted()) {
- mAnimator.end();
- }
- mPreviousPage = mCurrentPage;
- if (withAnimation) {
- mHideAnimator.setTarget(mDots[mPreviousPage]);
- mShowAnimator.setTarget(mDots[pageIndex]);
- mAnimator.start();
- }
- setSelectedPage(pageIndex);
- }
-
- private void calculateDotPositions() {
- int left = getPaddingLeft();
- int top = getPaddingTop();
- int right = getWidth() - getPaddingRight();
- int requiredWidth = getRequiredWidth();
- int mid = (left + right) / 2;
- int startLeft = mid - requiredWidth / 2;
- mDotSelectedX = new int[mPageCount];
- mDotSelectedLeftX = new int[mPageCount];
- mDotSelectedRightX = new int[mPageCount];
- // mDotSelectedX[0] should be mDotSelectedLeftX[-1] + mArrowGap
- mDotSelectedX[0] = startLeft + mDotRadius - mDotGap + mArrowGap;
- mDotSelectedLeftX[0] = startLeft + mDotRadius;
- mDotSelectedRightX[0] = 0;
- for (int i = 1; i < mPageCount; i++) {
- mDotSelectedX[i] = mDotSelectedLeftX[i - 1] + mArrowGap;
- mDotSelectedLeftX[i] = mDotSelectedLeftX[i - 1] + mDotGap;
- mDotSelectedRightX[i] = mDotSelectedX[i - 1] + mArrowGap;
- }
- mDotCenterY = top + mArrowRadius;
- adjustDotPosition();
- }
-
- @VisibleForTesting
- int getPageCount() {
- return mPageCount;
- }
-
- @VisibleForTesting
- int[] getDotSelectedX() {
- return mDotSelectedX;
- }
-
- @VisibleForTesting
- int[] getDotSelectedLeftX() {
- return mDotSelectedLeftX;
- }
-
- @VisibleForTesting
- int[] getDotSelectedRightX() {
- return mDotSelectedRightX;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int desiredHeight = getDesiredHeight();
- int height;
- switch (MeasureSpec.getMode(heightMeasureSpec)) {
- case MeasureSpec.EXACTLY:
- height = MeasureSpec.getSize(heightMeasureSpec);
- break;
- case MeasureSpec.AT_MOST:
- height = Math.min(desiredHeight, MeasureSpec.getSize(heightMeasureSpec));
- break;
- case MeasureSpec.UNSPECIFIED:
- default:
- height = desiredHeight;
- break;
- }
- int desiredWidth = getDesiredWidth();
- int width;
- switch (MeasureSpec.getMode(widthMeasureSpec)) {
- case MeasureSpec.EXACTLY:
- width = MeasureSpec.getSize(widthMeasureSpec);
- break;
- case MeasureSpec.AT_MOST:
- width = Math.min(desiredWidth, MeasureSpec.getSize(widthMeasureSpec));
- break;
- case MeasureSpec.UNSPECIFIED:
- default:
- width = desiredWidth;
- break;
- }
- setMeasuredDimension(width, height);
- }
-
- @Override
- protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
- setMeasuredDimension(width, height);
- calculateDotPositions();
- }
-
- private int getDesiredHeight() {
- return getPaddingTop() + mArrowDiameter + getPaddingBottom() + mShadowRadius;
- }
-
- private int getRequiredWidth() {
- return 2 * mDotRadius + 2 * mArrowGap + (mPageCount - 3) * mDotGap;
- }
-
- private int getDesiredWidth() {
- return getPaddingLeft() + getRequiredWidth() + getPaddingRight();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- for (int i = 0; i < mPageCount; ++i) {
- mDots[i].draw(canvas);
- }
- }
-
- private void setSelectedPage(int now) {
- if (now == mCurrentPage) {
- return;
- }
-
- mCurrentPage = now;
- adjustDotPosition();
- }
-
- private void adjustDotPosition() {
- for (int i = 0; i < mCurrentPage; ++i) {
- mDots[i].deselect();
- mDots[i].mDirection = i == mPreviousPage ? Dot.LEFT : Dot.RIGHT;
- mDots[i].mCenterX = mDotSelectedLeftX[i];
- }
- mDots[mCurrentPage].select();
- mDots[mCurrentPage].mDirection = mPreviousPage < mCurrentPage ? Dot.LEFT : Dot.RIGHT;
- mDots[mCurrentPage].mCenterX = mDotSelectedX[mCurrentPage];
- for (int i = mCurrentPage + 1; i < mPageCount; ++i) {
- mDots[i].deselect();
- mDots[i].mDirection = Dot.RIGHT;
- mDots[i].mCenterX = mDotSelectedRightX[i];
- }
- }
-
- public class Dot {
- static final float LEFT = -1;
- static final float RIGHT = 1;
-
- float mAlpha;
- @ColorInt
- int mBgColor;
- @ColorInt
- int mFgColor;
- float mTranslationX;
- float mCenterX;
- float mDiameter;
- float mRadius;
- float mArrowImageRadius;
- float mDirection = RIGHT;
-
- void select() {
- mTranslationX = 0.0f;
- mCenterX = 0.0f;
- mDiameter = mArrowDiameter;
- mRadius = mArrowRadius;
- mArrowImageRadius = mRadius * mArrowToBgRatio;
- mAlpha = 1.0f;
- adjustAlpha();
- }
-
- void deselect() {
- mTranslationX = 0.0f;
- mCenterX = 0.0f;
- mDiameter = mDotDiameter;
- mRadius = mDotRadius;
- mArrowImageRadius = mRadius * mArrowToBgRatio;
- mAlpha = 0.0f;
- adjustAlpha();
- }
-
- public void adjustAlpha() {
- int alpha = Math.round(0xFF * mAlpha);
- int red = Color.red(mDotFgSelectColor);
- int green = Color.green(mDotFgSelectColor);
- int blue = Color.blue(mDotFgSelectColor);
- mFgColor = Color.argb(alpha, red, green, blue);
- }
-
- @UsedByReflection
- public float getAlpha() {
- return mAlpha;
- }
-
- @UsedByReflection
- public void setAlpha(float alpha) {
- this.mAlpha = alpha;
- adjustAlpha();
- invalidate();
- }
-
- @UsedByReflection
- public float getTranslationX() {
- return mTranslationX;
- }
-
- @UsedByReflection
- public void setTranslationX(float translationX) {
- this.mTranslationX = translationX * mDirection;
- invalidate();
- }
-
- @UsedByReflection
- public float getDiameter() {
- return mDiameter;
- }
-
- @UsedByReflection
- public void setDiameter(float diameter) {
- this.mDiameter = diameter;
- this.mRadius = diameter / 2;
- this.mArrowImageRadius = diameter / 2 * mArrowToBgRatio;
- invalidate();
- }
-
- void draw(Canvas canvas) {
- float centerX = mCenterX + mTranslationX;
- canvas.drawCircle(centerX, mDotCenterY, mRadius, mBgPaint);
- if (mAlpha > 0) {
- mFgPaint.setColor(mFgColor);
- canvas.drawCircle(centerX, mDotCenterY, mRadius, mFgPaint);
- canvas.drawBitmap(mArrow, mArrowRect, new Rect((int) (centerX - mArrowImageRadius),
- (int) (mDotCenterY - mArrowImageRadius),
- (int) (centerX + mArrowImageRadius),
- (int) (mDotCenterY + mArrowImageRadius)), null);
- }
- }
- }
-}
diff --git a/common/res_leanback/animator/lb_onboarding_description_enter.xml b/res/anim/half_sized_dialog_enter.xml
index 5f26cdd1..aca49dd2 100644
--- a/common/res_leanback/animator/lb_onboarding_description_enter.xml
+++ b/res/anim/half_sized_dialog_enter.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2015 The Android Open Source Project
+ ~ Copyright (C) 2016 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -16,16 +16,14 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
- <objectAnimator
- android:propertyName="alpha"
- android:valueFrom="0.0"
- android:valueTo="1.0"
- android:duration="533"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
- <objectAnimator
- android:propertyName="translationY"
- android:valueFrom="60dp"
- android:valueTo="0dp"
- android:duration="533"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <translate
+ android:fromYDelta="@integer/half_sized_dialog_enter_offset_y"
+ android:toYDelta="0"
+ android:duration="@integer/half_sized_dialog_anim_duration"
+ android:interpolator="@android:interpolator/linear_out_slow_in" />
+ <alpha
+ android:fromAlpha="0.0"
+ android:toAlpha="1.0"
+ android:duration="@integer/half_sized_dialog_anim_duration"
+ android:interpolator="@android:interpolator/linear_out_slow_in" />
</set>
diff --git a/common/res_leanback/animator/lb_onboarding_logo_enter.xml b/res/anim/half_sized_dialog_exit.xml
index 76a4609a..d75252c4 100644
--- a/common/res_leanback/animator/lb_onboarding_logo_enter.xml
+++ b/res/anim/half_sized_dialog_exit.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2015 The Android Open Source Project
+ ~ Copyright (C) 2016 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -16,10 +16,14 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
- <objectAnimator
- android:propertyName="alpha"
- android:valueFrom="0.0"
- android:valueTo="1.0"
- android:duration="333"
+ <translate
+ android:fromYDelta="0"
+ android:toYDelta="@integer/half_sized_dialog_exit_offset_y"
+ android:duration="@integer/half_sized_dialog_anim_duration"
+ android:interpolator="@android:interpolator/linear_out_slow_in" />
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="@integer/half_sized_dialog_anim_duration"
android:interpolator="@android:interpolator/linear_out_slow_in" />
</set>
diff --git a/res/drawable-xhdpi/dvr_default_program_art.png b/res/drawable-xhdpi/dvr_default_program_art.png
new file mode 100644
index 00000000..e0ae149f
--- /dev/null
+++ b/res/drawable-xhdpi/dvr_default_program_art.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_dvr.png b/res/drawable-xhdpi/ic_dvr.png
new file mode 100644
index 00000000..c8c84f37
--- /dev/null
+++ b/res/drawable-xhdpi/ic_dvr.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_playstore.png b/res/drawable-xhdpi/ic_playstore.png
index 8f7c6a5c..6b092ce7 100644
--- a/res/drawable-xhdpi/ic_playstore.png
+++ b/res/drawable-xhdpi/ic_playstore.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_record_start_white.png b/res/drawable-xhdpi/ic_record_start_white.png
new file mode 100644
index 00000000..591b54a5
--- /dev/null
+++ b/res/drawable-xhdpi/ic_record_start_white.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_record_stop.png b/res/drawable-xhdpi/ic_record_stop.png
new file mode 100644
index 00000000..52393366
--- /dev/null
+++ b/res/drawable-xhdpi/ic_record_stop.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_store.png b/res/drawable-xhdpi/ic_store.png
index d57b5427..6b092ce7 100644
--- a/res/drawable-xhdpi/ic_store.png
+++ b/res/drawable-xhdpi/ic_store.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_youtube.png b/res/drawable-xhdpi/ic_youtube.png
deleted file mode 100644
index 653b270d..00000000
--- a/res/drawable-xhdpi/ic_youtube.png
+++ /dev/null
Binary files differ
diff --git a/common/res_leanback/drawable/lb_onboarding_start_button_background.xml b/res/drawable/ic_record_start.xml
index 57c31387..8d154c37 100644
--- a/common/res_leanback/drawable/lb_onboarding_start_button_background.xml
+++ b/res/drawable/ic_record_start.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2015 The Android Open Source Project
+ ~ Copyright (C) 2016 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -14,11 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item>
- <shape>
- <solid android:color="#EEEEEE"/>
- <corners android:radius="2dp" />
- </shape>
- </item>
-</selector>
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_record_start_white"
+ android:tint="#FFF44336" />
diff --git a/res/layout/block_screen.xml b/res/layout/block_screen.xml
index c355f5d8..892b8e5e 100644
--- a/res/layout/block_screen.xml
+++ b/res/layout/block_screen.xml
@@ -15,23 +15,22 @@
~ limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/block_screen"
+<com.android.tv.ui.BlockScreenView xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:visibility="gone"
android:background="@android:color/black" >
-
+ <!-- This layout is used for the animation -->
<LinearLayout
- android:id="@+id/block_screen_description"
+ android:id="@+id/block_screen_container"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center" >
-
<FrameLayout
+ android:id="@+id/image_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" >
@@ -40,28 +39,29 @@
android:layout_width="@dimen/tvview_block_icon_width"
android:layout_height="@dimen/tvview_block_icon_height"
android:layout_gravity="center"
- android:src="@drawable/ic_message_lock"
+ android:scaleType="fitCenter"
android:contentDescription="@null" />
<ImageView
android:id="@+id/block_screen_shrunken_icon"
android:layout_width="@dimen/shrunken_tvview_block_icon_width"
android:layout_height="@dimen/shrunken_tvview_block_icon_height"
android:layout_gravity="center"
- android:src="@drawable/ic_message_lock_preview"
android:visibility="gone"
android:contentDescription="@null" />
</FrameLayout>
-
+ <Space
+ android:id="@+id/space"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/tvview_block_vertical_spacing" />
<TextView
android:id="@+id/block_screen_text"
android:layout_width="600dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
- android:paddingTop="@dimen/tvview_block_text_padding_top"
android:fontFamily="@string/font"
android:textSize="@dimen/tvview_block_text_size"
android:lineSpacingExtra="4sp"
android:textColor="@color/tvview_block_text_color" />
</LinearLayout>
-</LinearLayout>
+</com.android.tv.ui.BlockScreenView>
diff --git a/res/layout/dvr_recording_card_view.xml b/res/layout/dvr_recording_card_view.xml
new file mode 100644
index 00000000..ccaf838f
--- /dev/null
+++ b/res/layout/dvr_recording_card_view.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2016 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<merge
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tv="http://schemas.android.com/apk/res/com.android.tv"
+ android:layout_width="@dimen/dvr_card_layout_width"
+ android:layout_height="@dimen/dvr_card_layout_height">
+
+ <ImageView
+ android:id="@+id/image"
+ android:layout_width="@dimen/dvr_card_image_layout_width"
+ android:layout_height="@dimen/dvr_card_image_layout_height"
+ android:layout_gravity="center_horizontal"
+ android:layout_margin="2dp"
+ android:gravity="center"
+ tv:layout_viewType="main"/>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:id="@+id/info_area"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tv:layout_viewType="info">
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="2dp"
+ android:gravity="center"
+ android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+ <TextView
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="2dp"
+ android:gravity="center"
+ android:textAppearance="?android:attr/textAppearanceMedium"/>
+ </LinearLayout>
+</merge>
diff --git a/res/layout/halfsized_dialog.xml b/res/layout/halfsized_dialog.xml
new file mode 100644
index 00000000..a5e017e4
--- /dev/null
+++ b/res/layout/halfsized_dialog.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- container for hosting HalfSizedDialog -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <FrameLayout android:id="@+id/halfsized_dialog_host"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginTop="300dp"/>
+</FrameLayout> \ No newline at end of file
diff --git a/res/layout/halfsized_guidance.xml b/res/layout/halfsized_guidance.xml
new file mode 100644
index 00000000..4b444d3e
--- /dev/null
+++ b/res/layout/halfsized_guidance.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <RelativeLayout
+ android:id="@+id/guidance_container"
+ style="?attr/guidanceContainerStyle" >
+
+ <ImageView
+ android:id="@+id/guidance_icon"
+ style="?attr/guidanceIconStyle"
+ tools:ignore="ContentDescription"/>
+
+ <TextView
+ android:id="@+id/guidance_breadcrumb"
+ style="?attr/guidanceBreadcrumbStyle"/>
+
+ <TextView
+ android:id="@+id/guidance_title"
+ style="?attr/guidanceTitleStyle"/>
+
+ <TextView
+ android:id="@+id/guidance_description"
+ style="?attr/guidanceDescriptionStyle"/>
+
+ </RelativeLayout>
+
+</FrameLayout> \ No newline at end of file
diff --git a/res/layout/menu_card_dvr.xml b/res/layout/menu_card_dvr.xml
index 4cc178f3..0ba7131d 100644
--- a/res/layout/menu_card_dvr.xml
+++ b/res/layout/menu_card_dvr.xml
@@ -31,7 +31,7 @@
android:layout_width="82dp"
android:layout_height="48dp"
android:layout_marginTop="16dp"
- android:src="@drawable/ic_channel_guide"
+ android:src="@drawable/ic_dvr"
android:layout_gravity="center_horizontal" />
</FrameLayout>
diff --git a/res/layout/menu_card_record.xml b/res/layout/menu_card_record.xml
new file mode 100644
index 00000000..6a195f0c
--- /dev/null
+++ b/res/layout/menu_card_record.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<com.android.tv.menu.RecordCardView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/card_layout_width"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:elevation="@dimen/card_elevation_normal"
+ android:focusable="true"
+ android:clickable="true">
+
+ <FrameLayout
+ android:layout_width="@dimen/card_image_layout_width"
+ android:layout_height="@dimen/card_image_layout_height"
+ android:background="@color/channel_card_guide">
+ <ImageView
+ android:id="@+id/record_icon"
+ android:layout_width="82dp"
+ android:layout_height="48dp"
+ android:layout_marginTop="16dp"
+ android:src="@drawable/ic_record_start"
+ android:layout_gravity="center_horizontal" />
+ </FrameLayout>
+
+ <TextView
+ android:id="@+id/record_label"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/card_meta_layout_height"
+ android:paddingStart="@dimen/card_meta_padding_start"
+ android:paddingEnd="@dimen/card_meta_padding_end"
+ android:paddingTop="@dimen/card_meta_padding_top"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:fontFamily="@string/condensed_font"
+ android:textColor="@color/card_meta_text_color"
+ android:background="@color/guide_card_meta_background"
+ android:text="@string/channels_item_record_start"
+ android:textSize="12sp" />
+
+</com.android.tv.menu.RecordCardView>
diff --git a/res/layout/onboarding_welcome_content.xml b/res/layout/onboarding_welcome_content.xml
index 57954df3..91c4ae75 100644
--- a/res/layout/onboarding_welcome_content.xml
+++ b/res/layout/onboarding_welcome_content.xml
@@ -18,6 +18,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/onboarding_welcome_content_margin_top"
android:layout_marginBottom="@dimen/onboarding_welcome_content_margin_bottom"
android:orientation="vertical">
diff --git a/res/layout/program_guide_table_header_column_item.xml b/res/layout/program_guide_table_header_column_item.xml
index ab66b7de..11fa2b91 100644
--- a/res/layout/program_guide_table_header_column_item.xml
+++ b/res/layout/program_guide_table_header_column_item.xml
@@ -24,10 +24,21 @@
android:paddingStart="@dimen/program_guide_table_header_column_padding_start"
android:paddingEnd="@dimen/program_guide_table_header_column_padding_end">
+ <!-- The top margin is set to ensure input logo is in the same height as channel number.
+ Center gravity doesn't work when the row is selected. -->
+ <ImageView
+ android:id="@+id/input_logo"
+ android:layout_width="@dimen/program_guide_table_header_column_input_logo_width"
+ android:layout_height="@dimen/program_guide_table_header_column_input_logo_height"
+ android:layout_marginTop="@dimen/program_guide_table_header_column_input_logo_margin_top"
+ android:scaleType="fitCenter"
+ android:visibility="gone" />
+
<TextView
android:id="@+id/channel_number"
android:layout_width="@dimen/program_guide_table_header_column_channel_number_width"
android:layout_height="@dimen/program_guide_table_item_row_height"
+ android:layout_marginStart="@dimen/program_guide_table_header_column_channel_number_margin_start"
android:gravity="start|center_vertical"
android:fontFamily="@string/light_font"
android:textSize="@dimen/program_guide_table_header_column_channel_number_large_font_size"
@@ -37,7 +48,7 @@
android:id="@+id/channel_name"
android:layout_width="@dimen/program_guide_table_header_column_channel_name_width"
android:layout_height="@dimen/program_guide_table_item_row_height"
- android:layout_marginStart="@dimen/program_guide_table_header_column_channel_number_width"
+ android:layout_marginStart="@dimen/program_guide_table_header_column_channel_name_margin_start"
android:gravity="start|center_vertical"
android:fontFamily="@string/condensed_font"
android:textSize="@dimen/program_guide_table_header_column_channel_name_font_size"
@@ -49,7 +60,7 @@
android:layout_width="@dimen/program_guide_table_header_column_channel_logo_width"
android:layout_height="@dimen/program_guide_table_header_column_channel_logo_height"
android:layout_marginTop="@dimen/program_guide_table_header_column_channel_logo_margin_top"
- android:layout_marginStart="@dimen/program_guide_table_header_column_channel_number_width"
+ android:layout_marginStart="@dimen/program_guide_table_header_column_channel_name_margin_start"
android:scaleType="fitStart"
android:visibility="gone" />
@@ -58,7 +69,7 @@
android:layout_width="@dimen/program_guide_table_header_column_channel_block_width"
android:layout_height="@dimen/program_guide_table_header_column_channel_block_height"
android:layout_marginTop="@dimen/program_guide_table_header_column_channel_block_margin_top"
- android:layout_marginStart="@dimen/program_guide_table_header_column_channel_number_width"
+ android:layout_marginStart="@dimen/program_guide_table_header_column_channel_name_margin_start"
android:scaleType="center"
android:src="@drawable/ic_guide_lock"
android:alpha="@dimen/program_guide_table_header_column_channel_block_opacity"
diff --git a/res/layout/tunable_tv_view.xml b/res/layout/tunable_tv_view.xml
index 4ca9426a..6c45caa8 100644
--- a/res/layout/tunable_tv_view.xml
+++ b/res/layout/tunable_tv_view.xml
@@ -16,17 +16,8 @@
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
- <TextView
- android:id="@+id/hide_screen"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:gravity="center"
- android:fontFamily="@string/font"
- android:textSize="@dimen/tvview_block_text_size"
- android:lineSpacingExtra="4sp"
- android:background="@android:color/black"
- android:textColor="@color/tvview_block_text_color" />
+ <include layout="@layout/block_screen"
+ android:id="@+id/hide_screen" />
<ProgressBar
android:id="@+id/buffering_spinner"
@@ -43,7 +34,8 @@
android:layout_height="match_parent"
android:background="@android:color/black" />
- <include layout="@layout/block_screen" />
+ <include layout="@layout/block_screen"
+ android:id="@+id/block_screen" />
<View
android:id="@+id/dim"
diff --git a/res/values-af/arrays.xml b/res/values-af/arrays.xml
index 1f94e3ce..f0513f6a 100644
--- a/res/values-af/arrays.xml
+++ b/res/values-af/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Vol"</item>
<item msgid="8568284598210500589">"Zoem"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Alle kanale"</item>
- <item msgid="928298872841713530">"Gesin/kinders"</item>
- <item msgid="2751606947569857164">"Sport"</item>
- <item msgid="7345749789651321496">"Inkopies"</item>
- <item msgid="167201149441442173">"Flieks"</item>
- <item msgid="525966731464264290">"Komedie"</item>
- <item msgid="6096710741527327836">"Reis"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Opvoeding"</item>
- <item msgid="7221999662426308394">"Diere/natuur"</item>
- <item msgid="375300513250925001">"Nuus"</item>
- <item msgid="7746320336582330410">"Speletjies"</item>
- <item msgid="1255741860568329178">"Kunste"</item>
- <item msgid="7603949681065702867">"Vermaak"</item>
- <item msgid="4453821994746804366">"Leefstyl"</item>
- <item msgid="3488534597567932843">"Musiek"</item>
- <item msgid="7452153120614274095">"Beste"</item>
- <item msgid="8215762047341133299">"Tegnologie/wetenskap"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Alle kanale"</item>
+ <item msgid="6897460857821394118">"Gesin/kinders"</item>
+ <item msgid="551257741825778215">"Sport"</item>
+ <item msgid="452133796804325879">"Inkopies"</item>
+ <item msgid="3296058637230163031">"Flieks"</item>
+ <item msgid="1054540282883891201">"Komedie"</item>
+ <item msgid="7900158429062595471">"Reis"</item>
+ <item msgid="3768998587825611787">"Drama"</item>
+ <item msgid="8340620094959282881">"Opvoeding"</item>
+ <item msgid="7396447839483867269">"Diere/natuur"</item>
+ <item msgid="4738043455148062673">"Nuus"</item>
+ <item msgid="7405041316051047427">"Speletjies"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Alle kanale"</item>
+ <item msgid="7909003973960375395">"Gesin/kinders"</item>
+ <item msgid="3185279732911635789">"Sport"</item>
+ <item msgid="4704858492065325964">"Inkopies"</item>
+ <item msgid="6083795019290250078">"Flieks"</item>
+ <item msgid="8302638329222449550">"Komedie"</item>
+ <item msgid="3803709976021475052">"Reis"</item>
+ <item msgid="8116747365234169059">"Drama"</item>
+ <item msgid="7356447541595315913">"Opvoeding"</item>
+ <item msgid="7511135485827589547">"Diere/natuur"</item>
+ <item msgid="6961248112238009967">"Nuus"</item>
+ <item msgid="6484685553679698447">"Speletjies"</item>
+ <item msgid="2737158328243183190">"Kunste"</item>
+ <item msgid="6577176952650166615">"Vermaak"</item>
+ <item msgid="7886693831871777617">"Leefstyl"</item>
+ <item msgid="8145832312485577062">"Musiek"</item>
+ <item msgid="1345789204804308580">"Beste"</item>
+ <item msgid="2736680312770771994">"Tegnologie/wetenskap"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Regstreekse Kanale"</item>
diff --git a/res/values-af/rating_system_strings.xml b/res/values-af/rating_system_strings.xml
index 1d09ef52..eb2883cf 100644
--- a/res/values-af/rating_system_strings.xml
+++ b/res/values-af/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Programme kan materiaal bevat wat onvanpas is vir gehore jonger as 15 jaar, en ouers moet dus vrye oordeel ten opsigte daarvan gebruik."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Programme kan materiaal bevat wat onvanpas is vir gehore jonger as 19 jaar, en is dus nie geskik vir kinders jonger as 19 jaar nie."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Suggestiewe dialoog"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Vuil taal"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Seksuele inhoud"</string>
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 64f3cf4a..f290edac 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Oopbronlisensies"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Oopbronlisensies"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Weergawe"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Ontwikkelaaropsies"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Aktiveer USB-TV-ontvanger"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Jou TV moet AC3-deurgang steun om USB-TV-ontvanger se klank te hoor."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3-oudiovermoë"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Jou TV steun AC3-deurgang."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Jou TV steun nie AC3-deurgang nie."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Help om Regstreekse Kanale te verbeter"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Deel anonieme gebruik- en diagnostiese data met Google sodat ons Regstreekse Kanale beter kan maak en kwessies soos omval en vries kan voorkom."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Druk Regs en voer jou PIN in om hierdie kanaal te kyk"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Program is geblokkeer"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Hierdie program is <xliff:g id="RATING">%1$s</xliff:g> gegradeer"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Net oudio"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Swak sein"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Geen internetverbinding nie"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Titelloos"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Kanaal is geblokkeer"</string>
- <string name="episode_format" msgid="4881195874563241096">"S<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Nuut"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Bronne"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Kon nie instel nie"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Geen program is gevind om hierdie handeling te behartig nie."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Alle bronkanale is versteek.\nKies minstens een kanaal om te kyk."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Onbeskikbaar weens swak videosein"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Die video is onverwags onbeskikbaar"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"TERUG-sleutel is vir gekoppelde toestelle. Druk TUIS-knoppie om uit te gaan."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Regstreekse kanale word nie op hierdie toestel met Android Lollipop gesteun nie."</string>
diff --git a/res/values-am/arrays.xml b/res/values-am/arrays.xml
index bed9cdf7..137c932a 100644
--- a/res/values-am/arrays.xml
+++ b/res/values-am/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"ሙሉ"</item>
<item msgid="8568284598210500589">"አጉላ"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"ሁሉም ሰርጦች"</item>
- <item msgid="928298872841713530">"ቤተሰብ/ህጻናት"</item>
- <item msgid="2751606947569857164">"ስፖርት"</item>
- <item msgid="7345749789651321496">"ግዢ"</item>
- <item msgid="167201149441442173">"ፊልሞች"</item>
- <item msgid="525966731464264290">"አስቂኝ"</item>
- <item msgid="6096710741527327836">"ጉዞ"</item>
- <item msgid="2851882187117833883">"ድራማ"</item>
- <item msgid="78492781188719038">"ትምህርት"</item>
- <item msgid="7221999662426308394">"እንስሳት/የዱር ህይወት"</item>
- <item msgid="375300513250925001">"ዜና"</item>
- <item msgid="7746320336582330410">"ጨዋታ"</item>
- <item msgid="1255741860568329178">"ጥበባት"</item>
- <item msgid="7603949681065702867">"መዝናኛ"</item>
- <item msgid="4453821994746804366">"የአኗኗር ዘይቤ"</item>
- <item msgid="3488534597567932843">"ሙዚቃ"</item>
- <item msgid="7452153120614274095">"ፕሪሚየር"</item>
- <item msgid="8215762047341133299">"ቴክኖሎጂ/ሳይንስ"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"ሁሉም ሰርጦች"</item>
+ <item msgid="6897460857821394118">"ቤተሰብ/ህጻናት"</item>
+ <item msgid="551257741825778215">"ስፖርት"</item>
+ <item msgid="452133796804325879">"ግዢ"</item>
+ <item msgid="3296058637230163031">"ፊልሞች"</item>
+ <item msgid="1054540282883891201">"ኮሜዲ"</item>
+ <item msgid="7900158429062595471">"ጉዞ"</item>
+ <item msgid="3768998587825611787">"ድራማ"</item>
+ <item msgid="8340620094959282881">"ትምህርት"</item>
+ <item msgid="7396447839483867269">"እንስሳት/የዱር ህይወት"</item>
+ <item msgid="4738043455148062673">"ዜና"</item>
+ <item msgid="7405041316051047427">"ጨዋታ"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"ሁሉም ሰርጦች"</item>
+ <item msgid="7909003973960375395">"ቤተሰብ/ህጻናት"</item>
+ <item msgid="3185279732911635789">"ስፖርት"</item>
+ <item msgid="4704858492065325964">"ግዢ"</item>
+ <item msgid="6083795019290250078">"ፊልሞች"</item>
+ <item msgid="8302638329222449550">"ኮሜዲ"</item>
+ <item msgid="3803709976021475052">"ጉዞ"</item>
+ <item msgid="8116747365234169059">"ድራማ"</item>
+ <item msgid="7356447541595315913">"ትምህርት"</item>
+ <item msgid="7511135485827589547">"እንስሳት/የዱር ህይወት"</item>
+ <item msgid="6961248112238009967">"ዜና"</item>
+ <item msgid="6484685553679698447">"ጨዋታ"</item>
+ <item msgid="2737158328243183190">"ኪነ ጥበባት"</item>
+ <item msgid="6577176952650166615">"መዝናኛ"</item>
+ <item msgid="7886693831871777617">"የአኗኗር ዘይቤ"</item>
+ <item msgid="8145832312485577062">"ሙዚቃ"</item>
+ <item msgid="1345789204804308580">"ፕሪሚየር"</item>
+ <item msgid="2736680312770771994">"ቴክ/ሳይንስ"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"የቀጥታ ስርጭት ሰርጦች"</item>
diff --git a/res/values-am/rating_system_strings.xml b/res/values-am/rating_system_strings.xml
index ac9696c4..156efc47 100644
--- a/res/values-am/rating_system_strings.xml
+++ b/res/values-am/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"ፕሮግራሞች ዕድሜያቸው ከ15 ዓመት በታች ለሆኑ ታዳሚዎች ተገቢ ያልሆኑ ማቴሪያሎችን ይዘው ሊሆን ይችላል፣ ስለዚህ ለእነርሱ የወላጅ ፈቃድ መጠቀም ያስፈልጋል።"</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"ፕሮግራሞች ዕድሜያቸው ከ19 ዓመት በታች ለሆኑ ታዳሚዎች ተገቢነት የሌላቸው ማቴሪያል ይዘው ሊሆን ይችላል፣ እና ስለዚህ ዕድሜያቸው ከ19 ዓመት በታች ለሆኑ ተስማሚ አይደሉም።"</string>
<string name="description_us_tv_d" msgid="12333789157204816">"ስሜት ቀስቃሽ ንግግር"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"ከባድ ቋንቋ"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"ወሲባዊ ይዘት"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 00077915..f397b81e 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"የክፍት ምንጭ ፍቃዶች"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"የክፍት ምንጭ ፍቃዶች"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"ስሪት"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"የገንቢ አማራጮች"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"የዩኤስቢ ቴሌቪዥን መቃኛን አንቃ"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"የዩኤስቢ ቴሌቪዥን መቃኛ ድምጽ ለመስማት የእርስዎ ቴሌቪዥን የAC3 ማሳለፊያን መደገፍ አለበት።"</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 ኦዲዮ አቅም"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"የእርስዎ ቴሌቪዥን የAC3 ማሳለፊያን ይደግፋል።"</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"የእርስዎ ቴሌቪዥን የAC3 ማሳለፊያን አይደግፍም።"</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Live TVን ለማሻሻል አግዝ"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"የቀጥታ ስርጭት ሰርጦችን የተሻሉ ማድረግ እንድንችል እና እንደ መሰናከል እና ቀጥ ማለት የመሳሰሉ ችግሮችን መከላከል እንድንችል ከGoogle ጋ ማንነትን የማያሳውቅ የአጠቃቀም እና የምርመራ ውሂብን ይጋሩ።"</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"ይህን ሰርጥ ለመመልከት ቀኝን ይጫኑ እና የእርስዎን ፒን ያስገቡ"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"ፕሮግራም ታግዷል"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"ይህ ፕሮግራም <xliff:g id="RATING">%1$s</xliff:g> ደረጃ ነው የተሰጠው"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"ኦዲዮ ብቻ"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"ደካማ ምልክት"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"ምንም የበይነመረብ ግንኙነት የለም"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"ርእስ የለውም"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"ሰርጥ ታግዷል"</string>
- <string name="episode_format" msgid="4881195874563241096">"ምዕ <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>፦ ክፍል <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"አዲስ"</string>
<string name="setup_category_done" msgid="4750902502852212319">"ምንጮች"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"መቃኘት አልተሳካም"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"ይህን እርምጃ የሚያከናውን ምንም መተግበሪያ አልተገኘም።"</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"ሁሉም የምንጭ ሰርጦች ተደብቀዋል።\nቢያንስ አንድ የሚመለከቱት ሰርጥ ይምረጡ።"</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"በደካማ የቪዲዮ ምልክት ምክንያት የማይገኝ"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"ቪዲዮው በማይጠበቅ ሁኔታ የማይገኝ ሆኗል"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"BACK ቁልፍ ለተገናኙ መሳሪያዎች። ለመውጣት HOME አዝራርን ይጫኑ።"</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"ቀጥተኛ ሰርጦች እዚህ መሣሪያ ላይ በAndroid Lollipop አይደገፉም።"</string>
diff --git a/res/values-ar/arrays.xml b/res/values-ar/arrays.xml
index 30874771..160838e1 100644
--- a/res/values-ar/arrays.xml
+++ b/res/values-ar/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"كامل"</item>
<item msgid="8568284598210500589">"تكبير/تصغير"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"كل القنوات"</item>
- <item msgid="928298872841713530">"الأسرة/الأطفال"</item>
- <item msgid="2751606947569857164">"رياضة"</item>
- <item msgid="7345749789651321496">"تسوّق"</item>
- <item msgid="167201149441442173">"أفلام"</item>
- <item msgid="525966731464264290">"كوميديا"</item>
- <item msgid="6096710741527327836">"سفر"</item>
- <item msgid="2851882187117833883">"دراما"</item>
- <item msgid="78492781188719038">"تعليم"</item>
- <item msgid="7221999662426308394">"حيوانات/حياة برية"</item>
- <item msgid="375300513250925001">"أخبار"</item>
- <item msgid="7746320336582330410">"ألعاب"</item>
- <item msgid="1255741860568329178">"الفنون"</item>
- <item msgid="7603949681065702867">"ترفيه"</item>
- <item msgid="4453821994746804366">"نمط حياة"</item>
- <item msgid="3488534597567932843">"موسيقي"</item>
- <item msgid="7452153120614274095">"العرض الأول"</item>
- <item msgid="8215762047341133299">"تكنولوجيا/علوم"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"كل القنوات"</item>
+ <item msgid="6897460857821394118">"العائلات/الأطفال"</item>
+ <item msgid="551257741825778215">"الرياضة"</item>
+ <item msgid="452133796804325879">"التسوق"</item>
+ <item msgid="3296058637230163031">"الأفلام"</item>
+ <item msgid="1054540282883891201">"الكوميديا"</item>
+ <item msgid="7900158429062595471">"السفر"</item>
+ <item msgid="3768998587825611787">"الدراما"</item>
+ <item msgid="8340620094959282881">"التعليم"</item>
+ <item msgid="7396447839483867269">"حيوانات/حياة برية"</item>
+ <item msgid="4738043455148062673">"الأخبار"</item>
+ <item msgid="7405041316051047427">"ألعاب الفيديو"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"كل القنوات"</item>
+ <item msgid="7909003973960375395">"العائلات/الأطفال"</item>
+ <item msgid="3185279732911635789">"الرياضة"</item>
+ <item msgid="4704858492065325964">"التسوق"</item>
+ <item msgid="6083795019290250078">"الأفلام"</item>
+ <item msgid="8302638329222449550">"الكوميديا"</item>
+ <item msgid="3803709976021475052">"السفر"</item>
+ <item msgid="8116747365234169059">"الدراما"</item>
+ <item msgid="7356447541595315913">"التعليم"</item>
+ <item msgid="7511135485827589547">"حيوانات/حياة برية"</item>
+ <item msgid="6961248112238009967">"الأخبار"</item>
+ <item msgid="6484685553679698447">"ألعاب الفيديو"</item>
+ <item msgid="2737158328243183190">"الفنون"</item>
+ <item msgid="6577176952650166615">"الترفيه"</item>
+ <item msgid="7886693831871777617">"نمط الحياة"</item>
+ <item msgid="8145832312485577062">"الموسيقى"</item>
+ <item msgid="1345789204804308580">"العرض الأول"</item>
+ <item msgid="2736680312770771994">"التكنولوجيا / العلوم"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"قنوات البث التلفزيوني المباشر"</item>
diff --git a/res/values-ar/rating_system_strings.xml b/res/values-ar/rating_system_strings.xml
index c95ac1cb..997a0f41 100644
--- a/res/values-ar/rating_system_strings.xml
+++ b/res/values-ar/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"يمكن أن تحتوي البرامج على مواد غير مناسبة لفئات الجماهير الأقل من 15 عامًا، وبالتالي يقتضي الأمر ترك التقدير للوالدين."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"يمكن أن تحتوي البرامج على مواد غير مناسبة لفئات الجماهير الأقل من 19 عامًا، وبالتالي فهي ليست مناسبة للشباب الأقل من 19 عامًا."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"حوار موحي جنسيًا"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"لغة غليظة"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"محتوى جنسي"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 5884cc5c..6061f180 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -152,12 +152,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"تراخيص البرامج المفتوحة المصدر"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"تراخيص البرامج المفتوحة المصدر"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"الإصدار"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"خيارات المطورين"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"‏تمكين موالف التلفزيون عبر USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"‏لسماع الصوت الصادر عن موالف التلفزيون عبر USB، يجب أن يكون تلفزيونك متوافقًا مع إمكانية العبور AC3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"‏إمكانية الصوت AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"‏تلفزيونك متوافق مع إمكانية العبور AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"‏تلفزيونك غير متوافق مع إمكانية العبور AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"المساعدة في تحسين القنوات المباشرة"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"‏يمكنك مشاركة بيانات مجهولة المصدر حول الاستخدام والتشخيص باستخدام Google، حتى نتمكن من تحسين تطبيق البث التلفزيوني المباشر ومنع حدوث مشكلات مثل الأعطال والتجميد."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"لمشاهدة هذه القناة، اضغط على اليمين وأدخل رقم التعريف الشخصي"</string>
@@ -169,9 +163,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"البرنامج محظور"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"تم تصنيف هذا البرنامج على أنه <xliff:g id="RATING">%1$s</xliff:g>."</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"الصوت فقط"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"الإشارة ضعيفة"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"عدم توفر اتصال بالإنترنت"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"بلا عنوان"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"تم حظر القناة"</string>
- <string name="episode_format" msgid="4881195874563241096">"الموسم <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: الحلقة <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"جديدة"</string>
<string name="setup_category_done" msgid="4750902502852212319">"المصادر"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -202,7 +197,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"أخفق التوليف"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"لم يتم العثور على تطبيق يمكنه مباشرة هذا الإجراء."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"كل قنوات المصدر مخفية.\nحدد على الأقل قناة واحدة لمشاهدتها."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"غير متوفر بسبب ضعف في إشارة الفيديو"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"الفيديو غير متوفر بشكل مفاجئ"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"زر الرجوع مخصص للجهاز المتصل. يمكنك الضغط على زر الصفحة الرئيسية للخروج."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"‏القنوات المباشرة غير متوافقة على هذا الجهاز مع Android Lollipop."</string>
diff --git a/res/values-az-rAZ/arrays.xml b/res/values-az-rAZ/arrays.xml
index 7f891617..8c6e5aae 100644
--- a/res/values-az-rAZ/arrays.xml
+++ b/res/values-az-rAZ/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Tam"</item>
<item msgid="8568284598210500589">"Zoom"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Bütün kanallar"</item>
- <item msgid="928298872841713530">"Ailə/Uşaqlar"</item>
- <item msgid="2751606947569857164">"İdman"</item>
- <item msgid="7345749789651321496">"Şoppinq"</item>
- <item msgid="167201149441442173">"Filmlər"</item>
- <item msgid="525966731464264290">"Komediya"</item>
- <item msgid="6096710741527327836">"Səyahət"</item>
- <item msgid="2851882187117833883">"Dram"</item>
- <item msgid="78492781188719038">"Təhsil"</item>
- <item msgid="7221999662426308394">"Heyvan/Vəhşi təbiət"</item>
- <item msgid="375300513250925001">"Xəbərlər"</item>
- <item msgid="7746320336582330410">"Oyun"</item>
- <item msgid="1255741860568329178">"İncəsənət"</item>
- <item msgid="7603949681065702867">"Əyləncə"</item>
- <item msgid="4453821994746804366">"Həyat tərzi"</item>
- <item msgid="3488534597567932843">"Musiqi"</item>
- <item msgid="7452153120614274095">"Premyera"</item>
- <item msgid="8215762047341133299">"Texnologiya/Elm"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Bütün kanallar"</item>
+ <item msgid="6897460857821394118">"Ailə/Uşaqlar"</item>
+ <item msgid="551257741825778215">"İdman"</item>
+ <item msgid="452133796804325879">"Şoppinq"</item>
+ <item msgid="3296058637230163031">"Filmlər"</item>
+ <item msgid="1054540282883891201">"Komediya"</item>
+ <item msgid="7900158429062595471">"Səyahət"</item>
+ <item msgid="3768998587825611787">"Dram"</item>
+ <item msgid="8340620094959282881">"Təhsil"</item>
+ <item msgid="7396447839483867269">"Heyvan/Vəhşi təbiət"</item>
+ <item msgid="4738043455148062673">"Xəbərlər"</item>
+ <item msgid="7405041316051047427">"Oyun"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Bütün kanallar"</item>
+ <item msgid="7909003973960375395">"Ailə/Uşaqlar"</item>
+ <item msgid="3185279732911635789">"İdman"</item>
+ <item msgid="4704858492065325964">"Şoppinq"</item>
+ <item msgid="6083795019290250078">"Filmlər"</item>
+ <item msgid="8302638329222449550">"Komediya"</item>
+ <item msgid="3803709976021475052">"Səyahət"</item>
+ <item msgid="8116747365234169059">"Dram"</item>
+ <item msgid="7356447541595315913">"Təhsil"</item>
+ <item msgid="7511135485827589547">"Heyvan/Vəhşi təbiət"</item>
+ <item msgid="6961248112238009967">"Xəbərlər"</item>
+ <item msgid="6484685553679698447">"Oyun"</item>
+ <item msgid="2737158328243183190">"İncəsənət"</item>
+ <item msgid="6577176952650166615">"Əyləncə"</item>
+ <item msgid="7886693831871777617">"Həyat tərzi"</item>
+ <item msgid="8145832312485577062">"Musiqi"</item>
+ <item msgid="1345789204804308580">"Premyera"</item>
+ <item msgid="2736680312770771994">"Texnologiya/Elm"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Canlı Kanallar"</item>
diff --git a/res/values-az-rAZ/rating_system_strings.xml b/res/values-az-rAZ/rating_system_strings.xml
new file mode 100644
index 00000000..278af47e
--- /dev/null
+++ b/res/values-az-rAZ/rating_system_strings.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Proqramlarda 15 yaşından kiçik auditoriya üçün uyğun olmayan material ola bilər və onlar üçün valideyn istədiyi istifadə olunmalıdır."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Proqramlarda 19 yaşından kiçik auditoriya üçün uyğun olmayan material ola bilər və bunlar 19 yaşından kiçiklər üçün nəzərdə tutulmayıb."</string>
+ <string name="description_us_tv_d" msgid="12333789157204816">"Nalayiq dialoq"</string>
+ <string name="description_us_tv_l" msgid="4105102855627964319">"Kobud dil"</string>
+ <string name="description_us_tv_s" msgid="7552447251273237176">"Seksual məzmun"</string>
+ <string name="description_us_tv_v" msgid="4799470820740236198">"Qəddarlıq"</string>
+ <string name="description_us_tv_fv" msgid="4760884520245003167">"Fantaziya Zorakılığı"</string>
+ <string name="description_us_tv_y" msgid="1352346035366881161">"Bu proqram bütün uşaqlara uyğun şəkildə hazırlanmışdır."</string>
+ <string name="description_us_tv_y7" msgid="8074158131815307880">"Bu proqram 7 yaşdan böyük uşaqlar üçün hazırlanmışdır."</string>
+ <string name="description_us_tv_g" msgid="4129784125991285944">"Bir çox valideyn bu proqramı bütün yaş kateqoriyalarına uyğun hesab edir."</string>
+ <string name="description_us_tv_pg" msgid="5647218792100947596">"Bu proqramdakı materiallar bir çox valideyn tərəfindən kiçik uşaqlara uyğun hesab edilmir. Bir çox valideynlər buna kiçik uşaqları ilə baxmaq istəyə bilər."</string>
+ <string name="description_us_tv_14" msgid="8682987672893857317">"Bu proqramdakı materiallar bir çox valideyn tərəfindən 14 yaşdan aşağı uşaqlara uyğun hesab edilmir."</string>
+ <string name="description_us_tv_ma" msgid="8912237130594289176">"Bu proqram xüsusən böyüklər üçün nəzərdə tutulub və 17 yaşdan aşağı şəxslər tərəfindən baxılması məsləhətli deyil."</string>
+ <string name="title_us_mv" msgid="7861766023361439035">"Film reytinqləri"</string>
+ <string name="description_us_mv_g" msgid="9185817407450418638">"Ümumi auditoriya. Uşaqların izləməsi valideynlərin narahatlığına səbəb olmayacaq."</string>
+ <string name="description_us_mv_pg" msgid="8578620326248525384">"Valideyn təlimatı təklif olunur. Bəzi valideynlərin bəyənməyəcəyi materialdan ibarət ola bilər."</string>
+ <string name="description_us_mv_pg13" msgid="7077331984538950084">"Valideynlərə ciddi xəbərdarlıq verilir.Bəzi materiallar erkən yeniyetmələr üçün uyğun olmaya bilər."</string>
+ <string name="description_us_mv_r" msgid="4482914375592527277">"Qadağandır, Böyüklər üçün materialdan ibarətdir. Uşaqlar izləməmişdən əvvəl valideynlər film haqqında ətraflı məlumat almağa məcburdurlar."</string>
+ <string name="description_us_mv_nc17" msgid="1897668555117874369">"17 və daha daha az yaşı olanlar qəbul edilmir. Yalnız böyüklər üçün. Uşaqlar qəbul edilmir."</string>
+</resources>
diff --git a/res/values-az-rAZ/strings.xml b/res/values-az-rAZ/strings.xml
index 46b1d9c1..643299c3 100644
--- a/res/values-az-rAZ/strings.xml
+++ b/res/values-az-rAZ/strings.xml
@@ -146,12 +146,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Açıq mənbə lisenziyaları"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Açıq mənbə lisenziyaları"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Versiya"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Tərtibatçı seçimləri"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"USB TV kökləyicisini aktiv edin"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"USB TV kökləyicisindən səs eşitmək üçün, TV-niz AC3 ötürüçüsünü dəstəkləməlidir."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 audio tutumu"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"TV-niz AC3 ötürücüsünü dəstəkləyir."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"TV-niz AC3 ötürücüsünü dəstəkləmir."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Canlı Kanalları inkişaf etdirməyə kömək edin"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Anonim istifadə və diaqnostika datasını Google ilə paylaşın, beləcə Canlı Kanalları daha yaxşı edə və qırılma və donma problemlərinin qarşısını ala bilərik."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Bu kanalı izləmək üçün Sağa basın və PİN kodunuzu daxil edin"</string>
@@ -163,9 +157,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Proqram blok edilib"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Bu proqram <xliff:g id="RATING">%1$s</xliff:g> ilə qiymətləndirilib."</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Yalnız Audio"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Zəif siqnal"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"İnternet bağlantısı yoxdur"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Başlıq yoxdur"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Kanal blok edilib"</string>
- <string name="episode_format" msgid="4881195874563241096">"S<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Yeni"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Mənbələr"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -192,7 +187,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Sazlama alınmadı"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Bu əməliyyatı idarə etmək üçün heç bir tətbiq tapılmadı."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Bütün mənbə kanalları gizlədilib.\nİzləmək üçün ən azı bir kanal seçin."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Zəif video bağlantı ilə əlaqədar əlçatmazdır"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Bu video gözlənilmədən əlçatmazdır"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Geri düyməsi qoşulu cihazlar üçündür. Çıxmaq üçün ƏSAS SƏHİFƏ düyməsini basın."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Canlı Kanallar Android Lollipop ilə bu cihazda dəstəklənmir"</string>
diff --git a/res/values-bg/arrays.xml b/res/values-bg/arrays.xml
index 66313ab6..7ab868dc 100644
--- a/res/values-bg/arrays.xml
+++ b/res/values-bg/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Пълно (16:9)"</item>
<item msgid="8568284598210500589">"Променен мащаб"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Всички канали"</item>
- <item msgid="928298872841713530">"Семейство/деца"</item>
- <item msgid="2751606947569857164">"Спорт"</item>
- <item msgid="7345749789651321496">"Пазаруване"</item>
- <item msgid="167201149441442173">"Филми"</item>
- <item msgid="525966731464264290">"Комедия"</item>
- <item msgid="6096710741527327836">"Пътувания"</item>
- <item msgid="2851882187117833883">"Драма"</item>
- <item msgid="78492781188719038">"Образование"</item>
- <item msgid="7221999662426308394">"Животни/дива природа"</item>
- <item msgid="375300513250925001">"Новини"</item>
- <item msgid="7746320336582330410">"Игри"</item>
- <item msgid="1255741860568329178">"Изкуства"</item>
- <item msgid="7603949681065702867">"Развлечения"</item>
- <item msgid="4453821994746804366">"Начин на живот"</item>
- <item msgid="3488534597567932843">"Музика"</item>
- <item msgid="7452153120614274095">"Премиери"</item>
- <item msgid="8215762047341133299">"Наука и технологии"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Всички канали"</item>
+ <item msgid="6897460857821394118">"Семейство/деца"</item>
+ <item msgid="551257741825778215">"Спорт"</item>
+ <item msgid="452133796804325879">"Пазаруване"</item>
+ <item msgid="3296058637230163031">"Филми"</item>
+ <item msgid="1054540282883891201">"Комедия"</item>
+ <item msgid="7900158429062595471">"Пътувания"</item>
+ <item msgid="3768998587825611787">"Драма"</item>
+ <item msgid="8340620094959282881">"Образование"</item>
+ <item msgid="7396447839483867269">"Животни/дива природа"</item>
+ <item msgid="4738043455148062673">"Новини"</item>
+ <item msgid="7405041316051047427">"Игри"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Всички канали"</item>
+ <item msgid="7909003973960375395">"Семейство/деца"</item>
+ <item msgid="3185279732911635789">"Спорт"</item>
+ <item msgid="4704858492065325964">"Пазаруване"</item>
+ <item msgid="6083795019290250078">"Филми"</item>
+ <item msgid="8302638329222449550">"Комедия"</item>
+ <item msgid="3803709976021475052">"Пътувания"</item>
+ <item msgid="8116747365234169059">"Драма"</item>
+ <item msgid="7356447541595315913">"Образование"</item>
+ <item msgid="7511135485827589547">"Животни/дива природа"</item>
+ <item msgid="6961248112238009967">"Новини"</item>
+ <item msgid="6484685553679698447">"Игри"</item>
+ <item msgid="2737158328243183190">"Изкуство"</item>
+ <item msgid="6577176952650166615">"Развлечения"</item>
+ <item msgid="7886693831871777617">"Начин на живот"</item>
+ <item msgid="8145832312485577062">"Музика"</item>
+ <item msgid="1345789204804308580">"Премиери"</item>
+ <item msgid="2736680312770771994">"Технологии/наука"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Канали на живо"</item>
diff --git a/res/values-bg/rating_system_strings.xml b/res/values-bg/rating_system_strings.xml
index c87674d2..d58017fb 100644
--- a/res/values-bg/rating_system_strings.xml
+++ b/res/values-bg/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Програмите може да съдържат материали, неподходящи за аудитория под 15 години, и затова решението е по усмотрение на родителите."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Програмите може да съдържат материали, неподходящи за аудитория под 19 години, и затова не са уместни за лица под тази възраст."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Диалог, предизвикващ неприлични асоциации"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Груб език"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Сексуално съдържание"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 95b4f488..201418a4 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Лицензи за отворен код"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Лицензи за отворен код"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Версия"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Опции за програмисти"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Активиране на телевизионния USB тунер"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"За да чувате звук от телевизионния USB тунер, телевизорът ви трябва да поддържа формата AC3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Поддръжка за аудио във формат AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Телевизорът ви поддържа формата AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Телевизорът ви не поддържа формата AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Съдействие за подобряването на Live TV"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Споделяйте с Google анонимни данни за употребата и диагностиката, за да подобрим Телевизия онлайн и да предотвратим проблеми като сривове и замръзвания."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"За да гледате този канал, натиснете стрелката за надясно и въведете ПИН кода си"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Програмата е блокирана"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Класификацията на тази програма е „<xliff:g id="RATING">%1$s</xliff:g>“"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Само аудио"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Слаб сигнал"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Няма връзка с интернет"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Без заглавие"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Каналът е блокиран"</string>
- <string name="episode_format" msgid="4881195874563241096">"Сезон <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Еп. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> ˜– „<xliff:g id="EPISODE_TITLE">%3$s</xliff:g>“"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Нови"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Източници"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Настройването не бе успешно"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Не бе намерено приложение за извършване на това действие."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Всички входящи канали са скрити.\nИзберете поне един, който да гледате."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Не е налице поради слаб видеосигнал"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Видеоклипът неочаквано не е налице"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Клавишът за връщане назад е за свързаното устройство. За изход натиснете бутона „Начало“."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Приложението Live TV не се поддържа на това устройство с Android Lollipop."</string>
diff --git a/res/values-bn-rBD/arrays.xml b/res/values-bn-rBD/arrays.xml
index 76a0792a..31fbf35b 100644
--- a/res/values-bn-rBD/arrays.xml
+++ b/res/values-bn-rBD/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"সম্পূর্ণ"</item>
<item msgid="8568284598210500589">"জুম"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"সমস্ত চ্যানেল"</item>
- <item msgid="928298872841713530">"পরিবার/শিশু"</item>
- <item msgid="2751606947569857164">"খেলাধুলা"</item>
- <item msgid="7345749789651321496">"কেনাকাটা"</item>
- <item msgid="167201149441442173">"চলচ্চিত্র"</item>
- <item msgid="525966731464264290">"কমেডি"</item>
- <item msgid="6096710741527327836">"ভ্রমণ"</item>
- <item msgid="2851882187117833883">"নাটক"</item>
- <item msgid="78492781188719038">"শিক্ষা"</item>
- <item msgid="7221999662426308394">"প্রাণী/বন্যজীবন"</item>
- <item msgid="375300513250925001">"সংবাদ"</item>
- <item msgid="7746320336582330410">"গেমিং"</item>
- <item msgid="1255741860568329178">"কলা"</item>
- <item msgid="7603949681065702867">"বিনোদন"</item>
- <item msgid="4453821994746804366">"জীবনশৈলী"</item>
- <item msgid="3488534597567932843">"সঙ্গীত"</item>
- <item msgid="7452153120614274095">"প্রিমিয়ার"</item>
- <item msgid="8215762047341133299">"প্রযুক্তি/বিজ্ঞান"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"সমস্ত চ্যানেল"</item>
+ <item msgid="6897460857821394118">"পরিবার/শিশু"</item>
+ <item msgid="551257741825778215">"খেলাধুলা"</item>
+ <item msgid="452133796804325879">"কেনাকাটা"</item>
+ <item msgid="3296058637230163031">"চলচ্চিত্র"</item>
+ <item msgid="1054540282883891201">"হাস্যরস"</item>
+ <item msgid="7900158429062595471">"ভ্রমণ"</item>
+ <item msgid="3768998587825611787">"নাটক"</item>
+ <item msgid="8340620094959282881">"শিক্ষা"</item>
+ <item msgid="7396447839483867269">"প্রাণী/বন্যজীবন"</item>
+ <item msgid="4738043455148062673">"সংবাদ"</item>
+ <item msgid="7405041316051047427">"গেমিং"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"সমস্ত চ্যানেল"</item>
+ <item msgid="7909003973960375395">"পরিবার/শিশু"</item>
+ <item msgid="3185279732911635789">"খেলাধুলা"</item>
+ <item msgid="4704858492065325964">"কেনাকাটা"</item>
+ <item msgid="6083795019290250078">"চলচ্চিত্র"</item>
+ <item msgid="8302638329222449550">"হাস্যরস"</item>
+ <item msgid="3803709976021475052">"ভ্রমণ"</item>
+ <item msgid="8116747365234169059">"নাটক"</item>
+ <item msgid="7356447541595315913">"শিক্ষা"</item>
+ <item msgid="7511135485827589547">"প্রাণী/বন্যজীবন"</item>
+ <item msgid="6961248112238009967">"সংবাদ"</item>
+ <item msgid="6484685553679698447">"গেমিং"</item>
+ <item msgid="2737158328243183190">"কলা"</item>
+ <item msgid="6577176952650166615">"বিনোদন"</item>
+ <item msgid="7886693831871777617">"জীবনশৈলী"</item>
+ <item msgid="8145832312485577062">"সঙ্গীত"</item>
+ <item msgid="1345789204804308580">"প্রিমিয়ার"</item>
+ <item msgid="2736680312770771994">"প্রযুক্তি/বিজ্ঞান"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"লাইভ চ্যানেলগুলি"</item>
diff --git a/res/values-bn-rBD/rating_system_strings.xml b/res/values-bn-rBD/rating_system_strings.xml
index 1e93d4be..a8916a03 100644
--- a/res/values-bn-rBD/rating_system_strings.xml
+++ b/res/values-bn-rBD/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"প্রোগ্র্রামগুলির মধ্যে ১৫ বছরের চাইতে কমবয়সী শিশুদের জন্য অনুপযুক্ত উপাদান থাকতে পারে এবং অভিভাবকদের বিবেচনার ভিত্তিতে সেগুলি ব্যবহার করা উচিৎ৷"</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"প্রোগ্র্রামগুলির মধ্যে থাকা উপাদান ১৯ বছরের চেয়ে কমবয়সীদের জন্য অনুপযুক্ত হতে পারে এবং এগুলি ১৯ বছরের চেয়ে কমবয়সী বালকদের জন্য উপযুক্ত নয়৷"</string>
<string name="description_us_tv_d" msgid="12333789157204816">"প্রস্তাবিত সংলাপ"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"অমার্জিত ভাষা"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"যৌন সামগ্রী"</string>
diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml
index a7708c15..7205c73a 100644
--- a/res/values-bn-rBD/strings.xml
+++ b/res/values-bn-rBD/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"মুক্ত উৎস লাইসেন্সগুলি"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"মুক্ত উৎস লাইসেন্সগুলি"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"সংস্করণ"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"বিকাশকারীর বিকল্পগুলি"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"USB টিভি টিউনার সক্ষম করুন"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"USB টিভি টিউনারের শব্দ শুনতে, আপনার টিভিকে AC3 পাসথ্রু সমর্থন করতে হবে৷"</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 অডিও সমর্থন করে"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"আপনার টিভি AC3 পাসথ্রু সমর্থন করে৷"</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"আপনার টিভি AC3 পাসথ্রু সমর্থন করে না৷"</string>
<string name="about_menu_improve" msgid="3712578027009311401">"লাইভ চ্যানেলগুলি উন্নত করতে সহায়তা করুন"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Google এর সাথে নামবিহীন ব্যবহার এবং ডায়াগনস্টিক ডেটা শেয়ার করুন যাতে করে আমরা লাইভ চ্যানেলগুলিকে আরো উন্নত করতে এবং ক্র্যাশ ও ফ্রিজ হয়ে যাওয়ার মতো সমস্যাগুলিকে আটকাতে পারি৷"</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"এই চ্যানেলটিকে দেখতে, ডানদিকে চাপুন এবং আপনার পিন লিখুন"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"প্রোগ্রামটি অবরুদ্ধ"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"এই প্রোগ্রামটি <xliff:g id="RATING">%1$s</xliff:g> রেট প্রাপ্ত৷"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"কেবলমাত্র অডিও"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"সিগন্যাল দুর্বল"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"কোনো ইন্টারনেট সংযোগ নেই"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"কোনো শিরোনাম নেই"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"চ্যানেল অবরুদ্ধ করা হয়েছে"</string>
- <string name="episode_format" msgid="4881195874563241096">"সে<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: এপিঃ <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"নতুন"</string>
<string name="setup_category_done" msgid="4750902502852212319">"উৎসগুলি"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"টিউন করা ব্যর্থ হয়েছে"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"এই ক্রিয়াটিকে চালনা করার জন্য কোনো অ্যাপ্লিকেশান পাওয়া যায়নি৷"</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"সমস্ত উৎস চ্যানেল লুকানো আছে৷\nদেখার জন্য কমপক্ষে একটি চ্যানেল নির্বাচন করুন৷"</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"দুর্বল ভিডিও সংকেতের কারণে অনুপলব্ধ"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"ভিডিওটি অপ্রত্যাশিতভাবে অনুপলব্ধ"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"\'ব্যাক\' কীটি সংযুক্ত ডিভাইসের ক্ষেত্রে ব্যবহারের জন্য৷ প্রস্থান করতে হোম বোতামটি টিপুন৷"</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"এই ডিভাইসটিতে Android Lollipop এর সাথে লাইভ চ্যানেলগুলি সমর্থিত নয়।"</string>
diff --git a/res/values-ca/arrays.xml b/res/values-ca/arrays.xml
index d00927f6..69033753 100644
--- a/res/values-ca/arrays.xml
+++ b/res/values-ca/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Complet"</item>
<item msgid="8568284598210500589">"Zoom"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Tots els canals"</item>
- <item msgid="928298872841713530">"Familiar/infantil"</item>
- <item msgid="2751606947569857164">"Esports"</item>
- <item msgid="7345749789651321496">"Compres"</item>
- <item msgid="167201149441442173">"Pel·lícules"</item>
- <item msgid="525966731464264290">"Comèdia"</item>
- <item msgid="6096710741527327836">"Viatges"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Educació"</item>
- <item msgid="7221999662426308394">"Animals/fauna"</item>
- <item msgid="375300513250925001">"Notícies"</item>
- <item msgid="7746320336582330410">"Jocs"</item>
- <item msgid="1255741860568329178">"Art"</item>
- <item msgid="7603949681065702867">"Entreteniment"</item>
- <item msgid="4453821994746804366">"Estil de vida"</item>
- <item msgid="3488534597567932843">"Música"</item>
- <item msgid="7452153120614274095">"Principal"</item>
- <item msgid="8215762047341133299">"Ciència i tecnologia"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Tots els canals"</item>
+ <item msgid="6897460857821394118">"Familiar/infantil"</item>
+ <item msgid="551257741825778215">"Esports"</item>
+ <item msgid="452133796804325879">"Compres"</item>
+ <item msgid="3296058637230163031">"Pel·lícules"</item>
+ <item msgid="1054540282883891201">"Comèdia"</item>
+ <item msgid="7900158429062595471">"Viatges"</item>
+ <item msgid="3768998587825611787">"Drama"</item>
+ <item msgid="8340620094959282881">"Educació"</item>
+ <item msgid="7396447839483867269">"Animals/fauna"</item>
+ <item msgid="4738043455148062673">"Notícies"</item>
+ <item msgid="7405041316051047427">"Jocs"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Tots els canals"</item>
+ <item msgid="7909003973960375395">"Familiar/infantil"</item>
+ <item msgid="3185279732911635789">"Esports"</item>
+ <item msgid="4704858492065325964">"Compres"</item>
+ <item msgid="6083795019290250078">"Pel·lícules"</item>
+ <item msgid="8302638329222449550">"Comèdia"</item>
+ <item msgid="3803709976021475052">"Viatges"</item>
+ <item msgid="8116747365234169059">"Drama"</item>
+ <item msgid="7356447541595315913">"Educació"</item>
+ <item msgid="7511135485827589547">"Animals/fauna"</item>
+ <item msgid="6961248112238009967">"Notícies"</item>
+ <item msgid="6484685553679698447">"Jocs"</item>
+ <item msgid="2737158328243183190">"Art"</item>
+ <item msgid="6577176952650166615">"Entreteniment"</item>
+ <item msgid="7886693831871777617">"Estil de vida"</item>
+ <item msgid="8145832312485577062">"Música"</item>
+ <item msgid="1345789204804308580">"Principal"</item>
+ <item msgid="2736680312770771994">"Ciència i tecnologia"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"TV en directe"</item>
diff --git a/res/values-ca/rating_system_strings.xml b/res/values-ca/rating_system_strings.xml
index 98dcfef2..1ee038aa 100644
--- a/res/values-ca/rating_system_strings.xml
+++ b/res/values-ca/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Els programes poden contenir material inapropiat per a menors de 15 anys i, per tant, els pares han de decidir si els menors d\'aquesta edat els poden mirar."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Els programes poden contenir material inapropiat per a menors de 19 anys i, per tant, no són aptes per a menors d\'aquesta edat."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Diàlegs suggerents"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Llenguatge vulgar"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Contingut sexual"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index fc640d32..7509b64c 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -85,8 +85,8 @@
<string name="side_panel_title_group_by" msgid="1783176601425788939">"Agrupa per"</string>
<string name="program_guide_content_locked" msgid="198056836554559553">"Aquest programa està bloquejat"</string>
<string name="program_guide_content_locked_format" msgid="514915272862967389">"Aquest programa està classificat com a <xliff:g id="RATING">%1$s</xliff:g>"</string>
- <string name="msg_no_setup_activity" msgid="7746893144640239857">"L\'entrada no és compatible amb l\'exploració automàtica."</string>
- <string name="msg_unable_to_start_setup_activity" msgid="8402612466599977855">"No es pot iniciar l\'exploració automàtica de: <xliff:g id="TV_INPUT">%s</xliff:g>."</string>
+ <string name="msg_no_setup_activity" msgid="7746893144640239857">"L\'entrada no és compatible amb la cerca automàtica."</string>
+ <string name="msg_unable_to_start_setup_activity" msgid="8402612466599977855">"No es pot iniciar la cerca automàtica de: <xliff:g id="TV_INPUT">%s</xliff:g>."</string>
<string name="msg_unable_to_start_system_captioning_settings" msgid="705242616044165668">"No es poden iniciar les preferències de subtítols del sistema."</string>
<plurals name="msg_channel_added" formatted="false" msgid="5301526166755938705">
<item quantity="other">S\'han afegit %1$d canals</item>
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Llicències de programari lliure"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Llicències de programari lliure"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Versió"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Opcions per a desenvolupadors"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Activa el sintonitzador de canals USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Per sentir el so del sintonitzador de canals USB, el teu televisor ha d\'admetre la connexió AC3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Funcions d\'àudio AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"El teu televisor admet la connexió AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"El teu televisor no admet la connexió AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Vull contribuir a millorar Canals en directe"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Comparteix dades d\'ús i de diagnòstic anònimes amb Google per millorar TV en directe i evitar problemes, com ara bloqueigs i errors."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Per veure aquest canal, prem el botó dret i introdueix el PIN."</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"El programa està bloquejat."</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Aquest programa està classificat com a <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Només àudio"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"El senyal és feble"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"No hi ha connexió a Internet"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Sense títol"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Canal bloquejat"</string>
- <string name="episode_format" msgid="4881195874563241096">"Temporada <xliff:g id="SEASON_NUMBER">%1$d</xliff:g> Capítol <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g>: <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Noves"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Fonts"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"No s\'ha pogut sintonitzar"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"No s\'ha trobat cap aplicació per processar aquesta acció."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Tots els canals d\'origen estan amagats.\nSelecciona com a mínim un canal per veure\'l."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"El senyal de vídeo és feble i, per tant, no està disponible"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"El vídeo ha deixat d\'estar disponible de manera inesperada"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"La tecla ENRERE és per als dispositius connectats. Prem el botó INICI per sortir."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Canals en directe no s\'admet en aquest dispositiu amb Android Lollipop."</string>
diff --git a/res/values-cs/arrays.xml b/res/values-cs/arrays.xml
index c116f645..b56275a8 100644
--- a/res/values-cs/arrays.xml
+++ b/res/values-cs/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Úplné"</item>
<item msgid="8568284598210500589">"Přiblížení"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Všechny kanály"</item>
- <item msgid="928298872841713530">"Rodina/děti"</item>
- <item msgid="2751606947569857164">"Sport"</item>
- <item msgid="7345749789651321496">"Nakupování"</item>
- <item msgid="167201149441442173">"Filmy"</item>
- <item msgid="525966731464264290">"Komedie"</item>
- <item msgid="6096710741527327836">"Cestování"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Vzdělávání"</item>
- <item msgid="7221999662426308394">"Zvířata/příroda"</item>
- <item msgid="375300513250925001">"Zpravodajství"</item>
- <item msgid="7746320336582330410">"Hraní her"</item>
- <item msgid="1255741860568329178">"Umění"</item>
- <item msgid="7603949681065702867">"Zábava"</item>
- <item msgid="4453821994746804366">"Životní styl"</item>
- <item msgid="3488534597567932843">"Hudba"</item>
- <item msgid="7452153120614274095">"Premier"</item>
- <item msgid="8215762047341133299">"Věda a technika"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Všechny kanály"</item>
+ <item msgid="6897460857821394118">"Rodina/děti"</item>
+ <item msgid="551257741825778215">"Sport"</item>
+ <item msgid="452133796804325879">"Nakupování"</item>
+ <item msgid="3296058637230163031">"Filmy"</item>
+ <item msgid="1054540282883891201">"Komedie"</item>
+ <item msgid="7900158429062595471">"Cestování"</item>
+ <item msgid="3768998587825611787">"Drama"</item>
+ <item msgid="8340620094959282881">"Vzdělávání"</item>
+ <item msgid="7396447839483867269">"Zvířata/příroda"</item>
+ <item msgid="4738043455148062673">"Zpravodajství"</item>
+ <item msgid="7405041316051047427">"Hry"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Všechny kanály"</item>
+ <item msgid="7909003973960375395">"Rodina/děti"</item>
+ <item msgid="3185279732911635789">"Sport"</item>
+ <item msgid="4704858492065325964">"Nakupování"</item>
+ <item msgid="6083795019290250078">"Filmy"</item>
+ <item msgid="8302638329222449550">"Komedie"</item>
+ <item msgid="3803709976021475052">"Cestování"</item>
+ <item msgid="8116747365234169059">"Drama"</item>
+ <item msgid="7356447541595315913">"Vzdělávání"</item>
+ <item msgid="7511135485827589547">"Zvířata/příroda"</item>
+ <item msgid="6961248112238009967">"Zpravodajství"</item>
+ <item msgid="6484685553679698447">"Hry"</item>
+ <item msgid="2737158328243183190">"Umění"</item>
+ <item msgid="6577176952650166615">"Zábava"</item>
+ <item msgid="7886693831871777617">"Životní styl"</item>
+ <item msgid="8145832312485577062">"Hudba"</item>
+ <item msgid="1345789204804308580">"Šampionáty"</item>
+ <item msgid="2736680312770771994">"Věda a technika"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Televize online"</item>
diff --git a/res/values-cs/rating_system_strings.xml b/res/values-cs/rating_system_strings.xml
index fac6da1d..fe416df5 100644
--- a/res/values-cs/rating_system_strings.xml
+++ b/res/values-cs/rating_system_strings.xml
@@ -17,7 +17,23 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="description_us_tv_d" msgid="12333789157204816">"Obscénní dialogy"</string>
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Programy mohou obsahovat materiál nevhodný pro publikum mladší 15 let, posouzení vhodnosti závisí na zvážení rodičů."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Programy mohou obsahovat materiál nevhodný pro publikum mladší 19 let, pro osoby mladší 19 let proto nejsou vhodné."</string>
+ <string name="description_us_tv_d" msgid="12333789157204816">"Dialogy se sexuálním podtextem"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Hrubé výrazy"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Sexuální obsah"</string>
<string name="description_us_tv_v" msgid="4799470820740236198">"Násilí"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index d9daf93b..53fefb0b 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -148,12 +148,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Licence open source"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Licence open source"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Verze"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Možnosti pro vývojáře"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Aktivovat televizní tuner USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Aby televizní tuner USB mohl přehrávat zvuk, vaše televize musí podporovat formát AC3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Podpora zvuku AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Vaše televize formát AC3 podporuje."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Vaše televize formát AC3 nepodporuje."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Pomáhat s vylepšováním aktivních kanálů"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Sdílet s Googlem anonymní statistiky využití a diagnostické údaje, abychom Televizi online mohli zlepšit a zabránit problémům, jako jsou selhání a zamrzání."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Chcete-li tento kanál sledovat, stiskněte šipku vpravo a zadejte kód PIN."</string>
@@ -165,9 +159,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Program je blokován"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Tento program má hodnocení <xliff:g id="RATING">%1$s</xliff:g>."</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Pouze zvuk"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Slabý signál"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Nejste připojeni k internetu"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Bez názvu"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Kanál byl zablokován"</string>
- <string name="episode_format" msgid="4881195874563241096">"S<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Nové"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Zdroje"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -196,7 +191,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Ladění se nezdařilo."</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Aplikace potřebná k provedení této akce nebyla nalezena."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Všechny zdrojové kanály jsou skryty.\nVyberte alespoň jeden kanál, který chcete sledovat."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Nedostupné z důvodu slabého signálu videa."</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Video nečekaně přestalo být dostupné."</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Tlačítko Zpět je určeno pro připojené zařízení. Ukončení provedete pomocí tlačítka Domů."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"V tomto zařízení se systémem Android Lollipop nejsou aktivní kanály podporovány."</string>
diff --git a/res/values-da/arrays.xml b/res/values-da/arrays.xml
index 9e0d84c6..95f67ea3 100644
--- a/res/values-da/arrays.xml
+++ b/res/values-da/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Fuld"</item>
<item msgid="8568284598210500589">"Zoom"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Alle kanaler"</item>
- <item msgid="928298872841713530">"Familie/børn"</item>
- <item msgid="2751606947569857164">"Sport"</item>
- <item msgid="7345749789651321496">"Shopping"</item>
- <item msgid="167201149441442173">"Film"</item>
- <item msgid="525966731464264290">"Komedie"</item>
- <item msgid="6096710741527327836">"Rejser"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Uddannelse"</item>
- <item msgid="7221999662426308394">"Dyr/naturens verden"</item>
- <item msgid="375300513250925001">"Nyheder"</item>
- <item msgid="7746320336582330410">"Spil"</item>
- <item msgid="1255741860568329178">"Kunst"</item>
- <item msgid="7603949681065702867">"Underholdning"</item>
- <item msgid="4453821994746804366">"Livsstil"</item>
- <item msgid="3488534597567932843">"Musik"</item>
- <item msgid="7452153120614274095">"Premiere"</item>
- <item msgid="8215762047341133299">"Teknik/videnskab"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Alle kanaler"</item>
+ <item msgid="6897460857821394118">"Familie/børn"</item>
+ <item msgid="551257741825778215">"Sport"</item>
+ <item msgid="452133796804325879">"Shopping"</item>
+ <item msgid="3296058637230163031">"Film"</item>
+ <item msgid="1054540282883891201">"Komedie"</item>
+ <item msgid="7900158429062595471">"Rejser"</item>
+ <item msgid="3768998587825611787">"Drama"</item>
+ <item msgid="8340620094959282881">"Uddannelse"</item>
+ <item msgid="7396447839483867269">"Dyr/naturens verden"</item>
+ <item msgid="4738043455148062673">"Nyheder"</item>
+ <item msgid="7405041316051047427">"Spil"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Alle kanaler"</item>
+ <item msgid="7909003973960375395">"Familie/børn"</item>
+ <item msgid="3185279732911635789">"Sport"</item>
+ <item msgid="4704858492065325964">"Shopping"</item>
+ <item msgid="6083795019290250078">"Film"</item>
+ <item msgid="8302638329222449550">"Komedie"</item>
+ <item msgid="3803709976021475052">"Rejser"</item>
+ <item msgid="8116747365234169059">"Drama"</item>
+ <item msgid="7356447541595315913">"Uddannelse"</item>
+ <item msgid="7511135485827589547">"Dyr/naturens verden"</item>
+ <item msgid="6961248112238009967">"Nyheder"</item>
+ <item msgid="6484685553679698447">"Spil"</item>
+ <item msgid="2737158328243183190">"Kunst"</item>
+ <item msgid="6577176952650166615">"Underholdning"</item>
+ <item msgid="7886693831871777617">"Livsstil"</item>
+ <item msgid="8145832312485577062">"Musik"</item>
+ <item msgid="1345789204804308580">"Premiere"</item>
+ <item msgid="2736680312770771994">"Teknik/videnskab"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Tv-kanaler"</item>
diff --git a/res/values-da/rating_system_strings.xml b/res/values-da/rating_system_strings.xml
index 3b9bbd58..316020b0 100644
--- a/res/values-da/rating_system_strings.xml
+++ b/res/values-da/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Programmerne kan have indhold, der er upassende for børn under 15 år, og forældrene bør beslutte, om indholdet er passende for deres barn."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Programmerne kan have indehold, der er upassende for personer under 19 år, og de er derfor ikke passende for unge under 19 år."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Dialog med seksuelt orienteret indhold"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Groft sprogbrug"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Seksuelt indhold"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 32ab8b45..244270e9 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Open source-licenser"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Open source-licenser"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Version"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Indstillinger for udviklere"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Aktivér USB-tuner til fjernsynet"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Dit fjernsyn skal understøtte AC3-gennemførsel, for at du kan høre lyd fra USB-tuneren til fjernsynet."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3-lydfunktion"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Dit fjernsyn understøtter AC3-gennemførsel."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Dit fjernsyn understøtter ikke AC3-gennemførsel."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Hjælp med at forbedre Tv-kanaler"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Del anonyme forbrugs- og diagnostikdata med Google, så vi kan forbedre Tv-kanaler og forhindre problemer, som f.eks. nedbrud og fastfrysning."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Se denne kanal ved at trykke på højreknappen og angive din pinkode"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Programmet er blokeret"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Dette program er klassificereret som <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Kun lyd"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Svagt signal"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Der er ingen internetforbindelse"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Ingen titel"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Kanalen er blokeret"</string>
- <string name="episode_format" msgid="4881195874563241096">"S<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Afsnit <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g><xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Nye"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Kilder"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Tuningen mislykkedes"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Der blev ikke fundet nogen app, der kan håndtere denne handling."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Alle kildekanaler er skjult.\nVælg mindst én kanal, som du vil se."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Utilgængelig på grund af svagt videosignal"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Videoen er mod forventning ikke tilgængelig"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Knappen Tilbage er til den tilsluttede enhed. Tryk på knappen Hjem for at afslutte."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Tv-kanaler understøttes ikke på denne enhed med Android Lollipop."</string>
diff --git a/res/values-de/arrays.xml b/res/values-de/arrays.xml
index d7fad130..6cf5c03f 100644
--- a/res/values-de/arrays.xml
+++ b/res/values-de/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Voll"</item>
<item msgid="8568284598210500589">"Zoom"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Alle Kanäle"</item>
- <item msgid="928298872841713530">"Familie/Kinder"</item>
- <item msgid="2751606947569857164">"Sport"</item>
- <item msgid="7345749789651321496">"Shopping"</item>
- <item msgid="167201149441442173">"Filme"</item>
- <item msgid="525966731464264290">"Komödien"</item>
- <item msgid="6096710741527327836">"Reisen"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Bildung"</item>
- <item msgid="7221999662426308394">"Tiere/Wildtiere"</item>
- <item msgid="375300513250925001">"Nachrichten"</item>
- <item msgid="7746320336582330410">"Spiele"</item>
- <item msgid="1255741860568329178">"Kunst"</item>
- <item msgid="7603949681065702867">"Unterhaltung"</item>
- <item msgid="4453821994746804366">"Lifestyle"</item>
- <item msgid="3488534597567932843">"Musik"</item>
- <item msgid="7452153120614274095">"Premiere"</item>
- <item msgid="8215762047341133299">"Technik/Wissenschaft"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Alle Kanäle"</item>
+ <item msgid="6897460857821394118">"Familie/Kinder"</item>
+ <item msgid="551257741825778215">"Sport"</item>
+ <item msgid="452133796804325879">"Einkaufen"</item>
+ <item msgid="3296058637230163031">"Filme"</item>
+ <item msgid="1054540282883891201">"Komödien"</item>
+ <item msgid="7900158429062595471">"Reisen"</item>
+ <item msgid="3768998587825611787">"Drama"</item>
+ <item msgid="8340620094959282881">"Bildung"</item>
+ <item msgid="7396447839483867269">"Tiere/Wildtiere"</item>
+ <item msgid="4738043455148062673">"Nachrichten"</item>
+ <item msgid="7405041316051047427">"Spiele"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Alle Kanäle"</item>
+ <item msgid="7909003973960375395">"Familie/Kinder"</item>
+ <item msgid="3185279732911635789">"Sport"</item>
+ <item msgid="4704858492065325964">"Einkaufen"</item>
+ <item msgid="6083795019290250078">"Filme"</item>
+ <item msgid="8302638329222449550">"Komödien"</item>
+ <item msgid="3803709976021475052">"Reisen"</item>
+ <item msgid="8116747365234169059">"Drama"</item>
+ <item msgid="7356447541595315913">"Bildung"</item>
+ <item msgid="7511135485827589547">"Tiere/Wildtiere"</item>
+ <item msgid="6961248112238009967">"Nachrichten"</item>
+ <item msgid="6484685553679698447">"Spiele"</item>
+ <item msgid="2737158328243183190">"Kunst"</item>
+ <item msgid="6577176952650166615">"Unterhaltung"</item>
+ <item msgid="7886693831871777617">"Lifestyle"</item>
+ <item msgid="8145832312485577062">"Musik"</item>
+ <item msgid="1345789204804308580">"Premiere"</item>
+ <item msgid="2736680312770771994">"Technik/Wissenschaft"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Live-TV"</item>
diff --git a/res/values-de/rating_system_strings.xml b/res/values-de/rating_system_strings.xml
index 543de4ac..66e49606 100644
--- a/res/values-de/rating_system_strings.xml
+++ b/res/values-de/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Die Sendungen sind möglicherweise für Personen unter 15 Jahren nicht geeignet. Die Einschätzung liegt daher im Ermessen der Eltern."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Die Sendungen sind möglicherweise für Jugendliche unter 19 Jahren nicht geeignet, da sie für diese Altersgruppe unangemessene Inhalte enthalten."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Anzüglicher Dialog"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Derbe Sprache"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Pornografische Inhalte"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 7c58d930..525039bb 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Open-Source-Lizenzen"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Open-Source-Lizenzen"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Version"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Entwickleroptionen"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"USB-TV-Empfänger aktivieren"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Damit die Audioausgabe über den USB-TV-Empfänger funktioniert, muss Ihr Fernseher AC-3-Passthrough unterstützen."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC-3-Audio-Funktion"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Ihr Fernseher unterstützt AC-3-Passthrough."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Ihr Fernseher unterstützt AC-3-Passthrough nicht."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Bei der Verbesserung von Live TV helfen"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Teilen Sie anonyme Nutzungs- und Diagnosedaten mit Google, damit wir Live-TV verbessern können und die App nicht mehr hängen bleibt oder abstürzt."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Um sich diesen Kanal anzusehen, drücken Sie rechts und geben Sie die PIN ein."</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Das Programm wurde blockiert."</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Diese Sendung wurde als \"<xliff:g id="RATING">%1$s</xliff:g>\" eingestuft."</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Nur Audio"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Schwaches Signal"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Keine Internetverbindung"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Kein Titel"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Kanal blockiert"</string>
- <string name="episode_format" msgid="4881195874563241096">"Staffel <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Folge <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Neu"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Quellen"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Fehler beim Einstellen"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Für diese Aktion wurde keine App gefunden."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Alle Quellkanäle sind ausgeblendet.\nWählen Sie mindestens einen Kanal aus, den Sie anschauen möchten."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Aufgrund von schwachem Videosignal nicht verfügbar"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Das Video ist unerwarteterweise nicht verfügbar."</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Die Taste \"Zurück\" gilt für das verbundene Gerät. Zum Beenden drücken Sie auf \"Startseite\"."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Live TV wird auf diesem Gerät mit Android Lollipop nicht unterstützt."</string>
diff --git a/res/values-el/arrays.xml b/res/values-el/arrays.xml
index 15114cc8..2978fa85 100644
--- a/res/values-el/arrays.xml
+++ b/res/values-el/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Πλήρης"</item>
<item msgid="8568284598210500589">"Ζουμ"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Όλα τα κανάλια"</item>
- <item msgid="928298872841713530">"Οικογενεικά/παιδικά"</item>
- <item msgid="2751606947569857164">"Αθλητικά"</item>
- <item msgid="7345749789651321496">"Αγορές"</item>
- <item msgid="167201149441442173">"Ταινίες"</item>
- <item msgid="525966731464264290">"Κωμωδία"</item>
- <item msgid="6096710741527327836">"Ταξίδια"</item>
- <item msgid="2851882187117833883">"Δραματική"</item>
- <item msgid="78492781188719038">"Εκπαίδευση"</item>
- <item msgid="7221999662426308394">"Ζώα/άγρια φύση"</item>
- <item msgid="375300513250925001">"Ειδήσεις"</item>
- <item msgid="7746320336582330410">"Παιχνίδια"</item>
- <item msgid="1255741860568329178">"Τέχνες"</item>
- <item msgid="7603949681065702867">"Ψυχαγωγία"</item>
- <item msgid="4453821994746804366">"Lifestyle"</item>
- <item msgid="3488534597567932843">"Μουσική"</item>
- <item msgid="7452153120614274095">"Πρεμιέρες"</item>
- <item msgid="8215762047341133299">"Τεχνολογία/Επιστήμη"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Όλα τα κανάλια"</item>
+ <item msgid="6897460857821394118">"Οικογένεια/παιδικά"</item>
+ <item msgid="551257741825778215">"Αθλητικά"</item>
+ <item msgid="452133796804325879">"Αγορές"</item>
+ <item msgid="3296058637230163031">"Ταινίες"</item>
+ <item msgid="1054540282883891201">"Κωμωδία"</item>
+ <item msgid="7900158429062595471">"Ταξίδια"</item>
+ <item msgid="3768998587825611787">"Δραματικές"</item>
+ <item msgid="8340620094959282881">"Εκπαίδευση"</item>
+ <item msgid="7396447839483867269">"Ζώα/άγρια φύση"</item>
+ <item msgid="4738043455148062673">"Ειδήσεις"</item>
+ <item msgid="7405041316051047427">"Παιχνίδια"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Όλα τα κανάλια"</item>
+ <item msgid="7909003973960375395">"Οικογένεια/παιδικά"</item>
+ <item msgid="3185279732911635789">"Αθλητικά"</item>
+ <item msgid="4704858492065325964">"Αγορές"</item>
+ <item msgid="6083795019290250078">"Ταινίες"</item>
+ <item msgid="8302638329222449550">"Κωμωδία"</item>
+ <item msgid="3803709976021475052">"Ταξίδια"</item>
+ <item msgid="8116747365234169059">"Δραματικές"</item>
+ <item msgid="7356447541595315913">"Εκπαίδευση"</item>
+ <item msgid="7511135485827589547">"Ζώα/άγρια φύση"</item>
+ <item msgid="6961248112238009967">"Ειδήσεις"</item>
+ <item msgid="6484685553679698447">"Παιχνίδια"</item>
+ <item msgid="2737158328243183190">"Τέχνες"</item>
+ <item msgid="6577176952650166615">"Διασκέδαση"</item>
+ <item msgid="7886693831871777617">"Τρόπος ζωής"</item>
+ <item msgid="8145832312485577062">"Μουσική"</item>
+ <item msgid="1345789204804308580">"Πρεμιέρες"</item>
+ <item msgid="2736680312770771994">"Τεχνολογία/Επιστήμη"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Ζωντανά κανάλια"</item>
diff --git a/res/values-el/rating_system_strings.xml b/res/values-el/rating_system_strings.xml
index 08a94d60..ea494c97 100644
--- a/res/values-el/rating_system_strings.xml
+++ b/res/values-el/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Τα προγράμματα ενδέχεται να περιέχουν υλικό που είναι ακατάλληλο για κοινό κάτω των 15 ετών και, συνεπώς, συνιστάται η κρίση του κηδεμόνα για την παρακολούθησή τους."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Τα προγράμματα ενδέχεται να περιέχουν υλικό που είναι ακατάλληλο για κοινό κάτω των 19 ετών και, συνεπώς, δεν είναι κατάλληλα για νέους κάτω των 19 ετών."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Προκλητικοί διάλογοι"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Χυδαία γλώσσα"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Σεξουαλικό περιεχόμενο"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 9aab6c54..478c0191 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Άδειες λογισμικού ανοικτού κώδικα"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Άδειες λογισμικού ανοικτού κώδικα"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Έκδοση"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Επιλογές για προγραμματιστές"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Ενεργοποίηση δέκτη USB τηλεόρασης"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Για να ακούσετε ήχο από τον δέκτη USB τηλεόρασης, η τηλεόρασή σας θα πρέπει να υποστηρίζει διέλευση AC3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Δυνατότητα διέλευσης ήχου AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Η τηλεόρασή σας υποστηρίζει τη διέλευση AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Η τηλεόρασή σας δεν υποστηρίζει τη διέλευση AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Συμβολή στη βελτίωση των Zωντανών καναλιών"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Μοιραστείτε ανώνυμα δεδομένα χρήσης και διαγνωστικών στοιχείων με τη Google για να βελτιώσουμε τα Ζωντανά κανάλια και να αποτρέψουμε προβλήματα όπως διακοπές λειτουργίας και πάγωμα της εφαρμογής."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Για να παρακολουθήσετε αυτό το κανάλι, πατήστε το δεξιά και εισαγάγετε το PIN σας"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Το πρόγραμμα αποκλείστηκε"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Αυτό το πρόγραμμα αξιολογήθηκε ως <xliff:g id="RATING">%1$s</xliff:g>."</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Μόνο ήχος"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Ασθενές σήμα"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Δεν υπάρχει σύνδεση στο διαδίκτυο"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Χωρίς τίτλο"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Το κανάλι αποκλείστηκε"</string>
- <string name="episode_format" msgid="4881195874563241096">"Σεζ. <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Επ. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Νέα"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Πηγές"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Αποτυχία συντονισμού"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Δεν βρέθηκε εφαρμογή για τη διαχείριση αυτής της ενέργειας."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Όλα τα κανάλια προέλευσης είναι κρυφά.\nΕπιλέξτε τουλάχιστον ένα κανάλι για να παρακολουθήσετε."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Μη διαθέσιμο λόγω αδύναμου σήματος βίντεο"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Το βίντεο δεν είναι διαθέσιμο απροσδόκητα"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Το πλήκτρο BACK αφορά τη συνδεδεμένη συσκευή. Πατήστε το πλήκτρο HOME για έξοδο."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Τα ζωντανά κανάλια δεν υποστηρίζονται σε αυτήν τη συσκευή με Android Lollipop."</string>
diff --git a/res/values-en-rAU/arrays.xml b/res/values-en-rAU/arrays.xml
index 082a7de4..3c2ac8c6 100644
--- a/res/values-en-rAU/arrays.xml
+++ b/res/values-en-rAU/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Full"</item>
<item msgid="8568284598210500589">"Zoom"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"All channels"</item>
- <item msgid="928298872841713530">"Family/Kids"</item>
- <item msgid="2751606947569857164">"Sports"</item>
- <item msgid="7345749789651321496">"Shopping"</item>
- <item msgid="167201149441442173">"Films"</item>
- <item msgid="525966731464264290">"Comedy"</item>
- <item msgid="6096710741527327836">"Travel"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Education"</item>
- <item msgid="7221999662426308394">"Animal/Wildlife"</item>
- <item msgid="375300513250925001">"News"</item>
- <item msgid="7746320336582330410">"Gaming"</item>
- <item msgid="1255741860568329178">"Arts"</item>
- <item msgid="7603949681065702867">"Entertainment"</item>
- <item msgid="4453821994746804366">"Lifestyle"</item>
- <item msgid="3488534597567932843">"Music"</item>
- <item msgid="7452153120614274095">"Premier"</item>
- <item msgid="8215762047341133299">"Tech/Science"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"All channels"</item>
+ <item msgid="6897460857821394118">"Family/Children\'s"</item>
+ <item msgid="551257741825778215">"Sport"</item>
+ <item msgid="452133796804325879">"Shopping"</item>
+ <item msgid="3296058637230163031">"Films"</item>
+ <item msgid="1054540282883891201">"Comedy"</item>
+ <item msgid="7900158429062595471">"Travel"</item>
+ <item msgid="3768998587825611787">"Drama"</item>
+ <item msgid="8340620094959282881">"Education"</item>
+ <item msgid="7396447839483867269">"Animal/Wildlife"</item>
+ <item msgid="4738043455148062673">"News"</item>
+ <item msgid="7405041316051047427">"Gaming"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"All channels"</item>
+ <item msgid="7909003973960375395">"Family/Children\'s"</item>
+ <item msgid="3185279732911635789">"Sport"</item>
+ <item msgid="4704858492065325964">"Shopping"</item>
+ <item msgid="6083795019290250078">"Films"</item>
+ <item msgid="8302638329222449550">"Comedy"</item>
+ <item msgid="3803709976021475052">"Travel"</item>
+ <item msgid="8116747365234169059">"Drama"</item>
+ <item msgid="7356447541595315913">"Education"</item>
+ <item msgid="7511135485827589547">"Animal/Wildlife"</item>
+ <item msgid="6961248112238009967">"News"</item>
+ <item msgid="6484685553679698447">"Gaming"</item>
+ <item msgid="2737158328243183190">"Arts"</item>
+ <item msgid="6577176952650166615">"Entertainment"</item>
+ <item msgid="7886693831871777617">"Lifestyle"</item>
+ <item msgid="8145832312485577062">"Music"</item>
+ <item msgid="1345789204804308580">"Premier"</item>
+ <item msgid="2736680312770771994">"Tech/Science"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Live TV"</item>
diff --git a/res/values-en-rAU/rating_system_strings.xml b/res/values-en-rAU/rating_system_strings.xml
index 31545fa3..1eece436 100644
--- a/res/values-en-rAU/rating_system_strings.xml
+++ b/res/values-en-rAU/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Programs may contain material inappropriate for audiences under 15, and therefore parental discretion should be used for them."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Programs may contain material inappropriate for audiences under 19, and thus are not suitable for youngsters under 19."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Suggestive dialogue"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Coarse language"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Sexual content"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 512a7f1a..987b4505 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Open source licences"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Open source licences"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Version"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Developer options"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Enable USB TV tuner"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"To hear sound from the USB TV tuner, your TV should support AC3 passthrough."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 audio capability"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Your TV supports AC3 passthrough."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Your TV doesn\'t support AC3 passthrough."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Help improve Live TV"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Share anonymous usage and diagnostics data with Google so we can make Live TV better and prevent issues such as crashing and freezing."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"To watch this channel, press Right and enter your PIN"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Programme is blocked"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"This programme is rated <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Audio only"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Weak signal"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"No Internet connection"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"No title"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Channel blocked"</string>
- <string name="episode_format" msgid="4881195874563241096">"S<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"N"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Sources"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Tune failed"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"No app was found to handle this action."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"All source channels are hidden.\nSelect at least one channel to watch."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Unavailable due to weak video signal"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"The video is unexpectedly unavailable"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"BACK key is for connected device. Press HOME button to exit."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Live TV is not supported on this device with Android Lollipop."</string>
diff --git a/res/values-en-rGB/arrays.xml b/res/values-en-rGB/arrays.xml
index 082a7de4..3c2ac8c6 100644
--- a/res/values-en-rGB/arrays.xml
+++ b/res/values-en-rGB/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Full"</item>
<item msgid="8568284598210500589">"Zoom"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"All channels"</item>
- <item msgid="928298872841713530">"Family/Kids"</item>
- <item msgid="2751606947569857164">"Sports"</item>
- <item msgid="7345749789651321496">"Shopping"</item>
- <item msgid="167201149441442173">"Films"</item>
- <item msgid="525966731464264290">"Comedy"</item>
- <item msgid="6096710741527327836">"Travel"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Education"</item>
- <item msgid="7221999662426308394">"Animal/Wildlife"</item>
- <item msgid="375300513250925001">"News"</item>
- <item msgid="7746320336582330410">"Gaming"</item>
- <item msgid="1255741860568329178">"Arts"</item>
- <item msgid="7603949681065702867">"Entertainment"</item>
- <item msgid="4453821994746804366">"Lifestyle"</item>
- <item msgid="3488534597567932843">"Music"</item>
- <item msgid="7452153120614274095">"Premier"</item>
- <item msgid="8215762047341133299">"Tech/Science"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"All channels"</item>
+ <item msgid="6897460857821394118">"Family/Children\'s"</item>
+ <item msgid="551257741825778215">"Sport"</item>
+ <item msgid="452133796804325879">"Shopping"</item>
+ <item msgid="3296058637230163031">"Films"</item>
+ <item msgid="1054540282883891201">"Comedy"</item>
+ <item msgid="7900158429062595471">"Travel"</item>
+ <item msgid="3768998587825611787">"Drama"</item>
+ <item msgid="8340620094959282881">"Education"</item>
+ <item msgid="7396447839483867269">"Animal/Wildlife"</item>
+ <item msgid="4738043455148062673">"News"</item>
+ <item msgid="7405041316051047427">"Gaming"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"All channels"</item>
+ <item msgid="7909003973960375395">"Family/Children\'s"</item>
+ <item msgid="3185279732911635789">"Sport"</item>
+ <item msgid="4704858492065325964">"Shopping"</item>
+ <item msgid="6083795019290250078">"Films"</item>
+ <item msgid="8302638329222449550">"Comedy"</item>
+ <item msgid="3803709976021475052">"Travel"</item>
+ <item msgid="8116747365234169059">"Drama"</item>
+ <item msgid="7356447541595315913">"Education"</item>
+ <item msgid="7511135485827589547">"Animal/Wildlife"</item>
+ <item msgid="6961248112238009967">"News"</item>
+ <item msgid="6484685553679698447">"Gaming"</item>
+ <item msgid="2737158328243183190">"Arts"</item>
+ <item msgid="6577176952650166615">"Entertainment"</item>
+ <item msgid="7886693831871777617">"Lifestyle"</item>
+ <item msgid="8145832312485577062">"Music"</item>
+ <item msgid="1345789204804308580">"Premier"</item>
+ <item msgid="2736680312770771994">"Tech/Science"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Live TV"</item>
diff --git a/res/values-en-rGB/rating_system_strings.xml b/res/values-en-rGB/rating_system_strings.xml
index 31545fa3..1eece436 100644
--- a/res/values-en-rGB/rating_system_strings.xml
+++ b/res/values-en-rGB/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Programs may contain material inappropriate for audiences under 15, and therefore parental discretion should be used for them."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Programs may contain material inappropriate for audiences under 19, and thus are not suitable for youngsters under 19."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Suggestive dialogue"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Coarse language"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Sexual content"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 512a7f1a..987b4505 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Open source licences"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Open source licences"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Version"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Developer options"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Enable USB TV tuner"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"To hear sound from the USB TV tuner, your TV should support AC3 passthrough."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 audio capability"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Your TV supports AC3 passthrough."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Your TV doesn\'t support AC3 passthrough."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Help improve Live TV"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Share anonymous usage and diagnostics data with Google so we can make Live TV better and prevent issues such as crashing and freezing."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"To watch this channel, press Right and enter your PIN"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Programme is blocked"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"This programme is rated <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Audio only"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Weak signal"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"No Internet connection"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"No title"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Channel blocked"</string>
- <string name="episode_format" msgid="4881195874563241096">"S<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"N"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Sources"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Tune failed"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"No app was found to handle this action."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"All source channels are hidden.\nSelect at least one channel to watch."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Unavailable due to weak video signal"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"The video is unexpectedly unavailable"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"BACK key is for connected device. Press HOME button to exit."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Live TV is not supported on this device with Android Lollipop."</string>
diff --git a/res/values-en-rIN/arrays.xml b/res/values-en-rIN/arrays.xml
index 082a7de4..3c2ac8c6 100644
--- a/res/values-en-rIN/arrays.xml
+++ b/res/values-en-rIN/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Full"</item>
<item msgid="8568284598210500589">"Zoom"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"All channels"</item>
- <item msgid="928298872841713530">"Family/Kids"</item>
- <item msgid="2751606947569857164">"Sports"</item>
- <item msgid="7345749789651321496">"Shopping"</item>
- <item msgid="167201149441442173">"Films"</item>
- <item msgid="525966731464264290">"Comedy"</item>
- <item msgid="6096710741527327836">"Travel"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Education"</item>
- <item msgid="7221999662426308394">"Animal/Wildlife"</item>
- <item msgid="375300513250925001">"News"</item>
- <item msgid="7746320336582330410">"Gaming"</item>
- <item msgid="1255741860568329178">"Arts"</item>
- <item msgid="7603949681065702867">"Entertainment"</item>
- <item msgid="4453821994746804366">"Lifestyle"</item>
- <item msgid="3488534597567932843">"Music"</item>
- <item msgid="7452153120614274095">"Premier"</item>
- <item msgid="8215762047341133299">"Tech/Science"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"All channels"</item>
+ <item msgid="6897460857821394118">"Family/Children\'s"</item>
+ <item msgid="551257741825778215">"Sport"</item>
+ <item msgid="452133796804325879">"Shopping"</item>
+ <item msgid="3296058637230163031">"Films"</item>
+ <item msgid="1054540282883891201">"Comedy"</item>
+ <item msgid="7900158429062595471">"Travel"</item>
+ <item msgid="3768998587825611787">"Drama"</item>
+ <item msgid="8340620094959282881">"Education"</item>
+ <item msgid="7396447839483867269">"Animal/Wildlife"</item>
+ <item msgid="4738043455148062673">"News"</item>
+ <item msgid="7405041316051047427">"Gaming"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"All channels"</item>
+ <item msgid="7909003973960375395">"Family/Children\'s"</item>
+ <item msgid="3185279732911635789">"Sport"</item>
+ <item msgid="4704858492065325964">"Shopping"</item>
+ <item msgid="6083795019290250078">"Films"</item>
+ <item msgid="8302638329222449550">"Comedy"</item>
+ <item msgid="3803709976021475052">"Travel"</item>
+ <item msgid="8116747365234169059">"Drama"</item>
+ <item msgid="7356447541595315913">"Education"</item>
+ <item msgid="7511135485827589547">"Animal/Wildlife"</item>
+ <item msgid="6961248112238009967">"News"</item>
+ <item msgid="6484685553679698447">"Gaming"</item>
+ <item msgid="2737158328243183190">"Arts"</item>
+ <item msgid="6577176952650166615">"Entertainment"</item>
+ <item msgid="7886693831871777617">"Lifestyle"</item>
+ <item msgid="8145832312485577062">"Music"</item>
+ <item msgid="1345789204804308580">"Premier"</item>
+ <item msgid="2736680312770771994">"Tech/Science"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Live TV"</item>
diff --git a/res/values-en-rIN/rating_system_strings.xml b/res/values-en-rIN/rating_system_strings.xml
index 31545fa3..1eece436 100644
--- a/res/values-en-rIN/rating_system_strings.xml
+++ b/res/values-en-rIN/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Programs may contain material inappropriate for audiences under 15, and therefore parental discretion should be used for them."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Programs may contain material inappropriate for audiences under 19, and thus are not suitable for youngsters under 19."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Suggestive dialogue"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Coarse language"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Sexual content"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 512a7f1a..987b4505 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Open source licences"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Open source licences"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Version"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Developer options"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Enable USB TV tuner"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"To hear sound from the USB TV tuner, your TV should support AC3 passthrough."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 audio capability"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Your TV supports AC3 passthrough."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Your TV doesn\'t support AC3 passthrough."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Help improve Live TV"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Share anonymous usage and diagnostics data with Google so we can make Live TV better and prevent issues such as crashing and freezing."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"To watch this channel, press Right and enter your PIN"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Programme is blocked"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"This programme is rated <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Audio only"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Weak signal"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"No Internet connection"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"No title"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Channel blocked"</string>
- <string name="episode_format" msgid="4881195874563241096">"S<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"N"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Sources"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Tune failed"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"No app was found to handle this action."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"All source channels are hidden.\nSelect at least one channel to watch."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Unavailable due to weak video signal"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"The video is unexpectedly unavailable"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"BACK key is for connected device. Press HOME button to exit."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Live TV is not supported on this device with Android Lollipop."</string>
diff --git a/res/values-es-rUS/arrays.xml b/res/values-es-rUS/arrays.xml
index de5b7910..522e6352 100644
--- a/res/values-es-rUS/arrays.xml
+++ b/res/values-es-rUS/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Completa"</item>
<item msgid="8568284598210500589">"Zoom"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Todos los canales"</item>
- <item msgid="928298872841713530">"Familiar/Infantil"</item>
- <item msgid="2751606947569857164">"Deportes"</item>
- <item msgid="7345749789651321496">"Compras"</item>
- <item msgid="167201149441442173">"Películas"</item>
- <item msgid="525966731464264290">"Comedia"</item>
- <item msgid="6096710741527327836">"Viajes"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Educación"</item>
- <item msgid="7221999662426308394">"Animal/Vida salvaje"</item>
- <item msgid="375300513250925001">"Noticias"</item>
- <item msgid="7746320336582330410">"Juegos"</item>
- <item msgid="1255741860568329178">"Arte"</item>
- <item msgid="7603949681065702867">"Entretenimiento"</item>
- <item msgid="4453821994746804366">"Estilo de vida"</item>
- <item msgid="3488534597567932843">"Música"</item>
- <item msgid="7452153120614274095">"Estrenos"</item>
- <item msgid="8215762047341133299">"Ciencia y tecnología"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Todos los canales"</item>
+ <item msgid="6897460857821394118">"Infantil y familiar"</item>
+ <item msgid="551257741825778215">"Deportes"</item>
+ <item msgid="452133796804325879">"Compras"</item>
+ <item msgid="3296058637230163031">"Películas"</item>
+ <item msgid="1054540282883891201">"Comedia"</item>
+ <item msgid="7900158429062595471">"Viajes"</item>
+ <item msgid="3768998587825611787">"Drama"</item>
+ <item msgid="8340620094959282881">"Educación"</item>
+ <item msgid="7396447839483867269">"Vida salvaje"</item>
+ <item msgid="4738043455148062673">"Noticias"</item>
+ <item msgid="7405041316051047427">"Videojuegos"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Todos los canales"</item>
+ <item msgid="7909003973960375395">"Infantil y familiar"</item>
+ <item msgid="3185279732911635789">"Deportes"</item>
+ <item msgid="4704858492065325964">"Compras"</item>
+ <item msgid="6083795019290250078">"Películas"</item>
+ <item msgid="8302638329222449550">"Comedia"</item>
+ <item msgid="3803709976021475052">"Viajes"</item>
+ <item msgid="8116747365234169059">"Drama"</item>
+ <item msgid="7356447541595315913">"Educación"</item>
+ <item msgid="7511135485827589547">"Vida salvaje"</item>
+ <item msgid="6961248112238009967">"Noticias"</item>
+ <item msgid="6484685553679698447">"Videojuegos"</item>
+ <item msgid="2737158328243183190">"Arte"</item>
+ <item msgid="6577176952650166615">"Entretenimiento"</item>
+ <item msgid="7886693831871777617">"Estilo de vida"</item>
+ <item msgid="8145832312485577062">"Música"</item>
+ <item msgid="1345789204804308580">"Estrenos"</item>
+ <item msgid="2736680312770771994">"Ciencia y tecnología"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Canales en vivo"</item>
diff --git a/res/values-es-rUS/rating_system_strings.xml b/res/values-es-rUS/rating_system_strings.xml
index 90e83090..8beadacf 100644
--- a/res/values-es-rUS/rating_system_strings.xml
+++ b/res/values-es-rUS/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Como es posible que los programas contengan material inapropiado para menores de quince años, se recomienda la supervisión de los padres."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Es posible que los programas contengan material inapropiado no recomendable para menores de diecinueve años."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Diálogo con contenido provocativo"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Lenguaje vulgar"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Contenido sexual"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 3397798b..1a52b29e 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Licencias de código abierto"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Licencias de código abierto"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Versión"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Opciones para programador"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Habilitar sintonizador de TV USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Para escuchar el sonido del sintonizador de TV USB, la TV debe ser compatible con el formato AC-3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Compatibilidad con audio AC-3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"La TV es compatible con el formato AC-3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"La TV no es compatible con el formato AC-3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Ayudar a mejorar Canales en vivo"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Comparte datos anónimos acerca del uso y el diagnóstico con Google para que podamos mejorar Canales en vivo y evitar problemas como el bloqueo o el congelamiento de la imagen."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Para mirar este canal, presiona la tecla hacia la derecha e ingresa el PIN."</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"El programa está bloqueado."</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Calificación de este programa: <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Solo audio"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Señal débil"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Sin conexión a Internet"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Sin título"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Canal bloqueado"</string>
- <string name="episode_format" msgid="4881195874563241096">"Temporada <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>. Episodio <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g>: <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>."</string>
<string name="setup_category_new" msgid="2899355289563443627">"Nuevas"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Fuentes"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Error al sintonizar"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"No se encontró ninguna aplicación que pueda realizar esta acción."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Todos los canales de salida están ocultos.\nSelecciona al menos un canal para mirar."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"No está disponible porque la señal de video es débil."</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"El video no está disponible por motivos inesperados."</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"La tecla ATRÁS es para el dispositivo conectado. Presiona el botón INICIO para salir."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"\"Canales en vivo\" no es compatible con este dispositivo con Android Lollipop."</string>
diff --git a/res/values-es/arrays.xml b/res/values-es/arrays.xml
index 20d54571..3bc164ca 100644
--- a/res/values-es/arrays.xml
+++ b/res/values-es/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Completa"</item>
<item msgid="8568284598210500589">"Zoom"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Todos los canales"</item>
- <item msgid="928298872841713530">"Familiar/Infantil"</item>
- <item msgid="2751606947569857164">"Deportes"</item>
- <item msgid="7345749789651321496">"Compras"</item>
- <item msgid="167201149441442173">"Películas"</item>
- <item msgid="525966731464264290">"Comedia"</item>
- <item msgid="6096710741527327836">"Viajes"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Educación"</item>
- <item msgid="7221999662426308394">"Animales/Vida salvaje"</item>
- <item msgid="375300513250925001">"Noticias"</item>
- <item msgid="7746320336582330410">"Juegos"</item>
- <item msgid="1255741860568329178">"Arte"</item>
- <item msgid="7603949681065702867">"Ocio"</item>
- <item msgid="4453821994746804366">"Estilo de vida"</item>
- <item msgid="3488534597567932843">"Música"</item>
- <item msgid="7452153120614274095">"De primera línea"</item>
- <item msgid="8215762047341133299">"Ciencia y tecnología"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Todos los canales"</item>
+ <item msgid="6897460857821394118">"Familiar/Infantil"</item>
+ <item msgid="551257741825778215">"Deportes"</item>
+ <item msgid="452133796804325879">"Compras"</item>
+ <item msgid="3296058637230163031">"Películas"</item>
+ <item msgid="1054540282883891201">"Comedia"</item>
+ <item msgid="7900158429062595471">"Viajes"</item>
+ <item msgid="3768998587825611787">"Drama"</item>
+ <item msgid="8340620094959282881">"Formación"</item>
+ <item msgid="7396447839483867269">"Animales/Vida salvaje"</item>
+ <item msgid="4738043455148062673">"Noticias"</item>
+ <item msgid="7405041316051047427">"Juegos"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Todos los canales"</item>
+ <item msgid="7909003973960375395">"Familiar/Infantil"</item>
+ <item msgid="3185279732911635789">"Deportes"</item>
+ <item msgid="4704858492065325964">"Compras"</item>
+ <item msgid="6083795019290250078">"Películas"</item>
+ <item msgid="8302638329222449550">"Comedia"</item>
+ <item msgid="3803709976021475052">"Viajes"</item>
+ <item msgid="8116747365234169059">"Drama"</item>
+ <item msgid="7356447541595315913">"Formación"</item>
+ <item msgid="7511135485827589547">"Animales/Vida salvaje"</item>
+ <item msgid="6961248112238009967">"Noticias"</item>
+ <item msgid="6484685553679698447">"Juegos"</item>
+ <item msgid="2737158328243183190">"Arte"</item>
+ <item msgid="6577176952650166615">"Entretenimiento"</item>
+ <item msgid="7886693831871777617">"Estilo de vida"</item>
+ <item msgid="8145832312485577062">"Música"</item>
+ <item msgid="1345789204804308580">"De primera línea"</item>
+ <item msgid="2736680312770771994">"Ciencia y tecnología"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"TV en directo"</item>
diff --git a/res/values-es/rating_system_strings.xml b/res/values-es/rating_system_strings.xml
index 63c35d36..974146b0 100644
--- a/res/values-es/rating_system_strings.xml
+++ b/res/values-es/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Los programas pueden contener material inadecuado para menores de 15 años, por lo que deben estar supervisados por sus padres."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Los programas pueden contener material inadecuado para menores de 19 años, por lo que no son apropiados para jóvenes de esas edades."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Diálogo sugerente"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Lenguaje grosero"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Contenido sexual"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index cb7bdcff..29e62228 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Licencias de software libre"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Licencias software libre"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Versión"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Opciones de desarrollo"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Habilitar sintonizador de canales USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Para escuchar el sonido del sintonizador de canales USB, tu TV debe admitir la conexión AC3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Compatible con audio AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Tu TV admite la conexión AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Tu TV no admite la conexión AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Ayudar a mejorar Canales en directo"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Comparte datos de uso y de diagnóstico anónimos con Google para mejorar TV en directo y evitar bloqueos y fallos."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Para ver este canal, pulsa la tecla hacia la derecha e introduce el número PIN"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"El programa está bloqueado"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Este programa se ha clasificado como <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Solo audio"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Señal débil"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Sin conexión a Internet"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Sin título"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Canal bloqueado"</string>
- <string name="episode_format" msgid="4881195874563241096">"Temporada <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: episodio <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> (<xliff:g id="EPISODE_TITLE">%3$s</xliff:g>)"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Nuevas"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Fuentes"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Error al sintonizar"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"No se ha encontrado ninguna aplicación que pueda realizar esta acción."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Todos los canales de origen están ocultos.\nSelecciona al menos un canal para verlo."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"No está disponible porque la señal de vídeo es débil"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"El vídeo no está disponible por motivos desconocidos"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"La tecla VOLVER es para dispositivos conectados. Pulsa el botón de inicio para salir."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Canales en directo no se admite en este dispositivo con Android Lollipop."</string>
diff --git a/res/values-et-rEE/arrays.xml b/res/values-et-rEE/arrays.xml
index 2d1d11fd..23f3dc9c 100644
--- a/res/values-et-rEE/arrays.xml
+++ b/res/values-et-rEE/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Täis"</item>
<item msgid="8568284598210500589">"Suumimine"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Kõik kanalid"</item>
- <item msgid="928298872841713530">"Pere/lapsed"</item>
- <item msgid="2751606947569857164">"Sport"</item>
- <item msgid="7345749789651321496">"Ostud"</item>
- <item msgid="167201149441442173">"Filmid"</item>
- <item msgid="525966731464264290">"Komöödia"</item>
- <item msgid="6096710741527327836">"Reisimine"</item>
- <item msgid="2851882187117833883">"Draama"</item>
- <item msgid="78492781188719038">"Haridus"</item>
- <item msgid="7221999662426308394">"Loomad/loodus"</item>
- <item msgid="375300513250925001">"Uudised"</item>
- <item msgid="7746320336582330410">"Mängud"</item>
- <item msgid="1255741860568329178">"Kunstid"</item>
- <item msgid="7603949681065702867">"Meelelahutus"</item>
- <item msgid="4453821994746804366">"Elustiil"</item>
- <item msgid="3488534597567932843">"Muusika"</item>
- <item msgid="7452153120614274095">"Esilinastus"</item>
- <item msgid="8215762047341133299">"Tehnika/teadus"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Kõik kanalid"</item>
+ <item msgid="6897460857821394118">"Pere/lapsed"</item>
+ <item msgid="551257741825778215">"Sport"</item>
+ <item msgid="452133796804325879">"Ostud"</item>
+ <item msgid="3296058637230163031">"Filmid"</item>
+ <item msgid="1054540282883891201">"Komöödia"</item>
+ <item msgid="7900158429062595471">"Reisimine"</item>
+ <item msgid="3768998587825611787">"Draama"</item>
+ <item msgid="8340620094959282881">"Haridus"</item>
+ <item msgid="7396447839483867269">"Loomad/loodus"</item>
+ <item msgid="4738043455148062673">"Uudised"</item>
+ <item msgid="7405041316051047427">"Mängud"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Kõik kanalid"</item>
+ <item msgid="7909003973960375395">"Pere/lapsed"</item>
+ <item msgid="3185279732911635789">"Sport"</item>
+ <item msgid="4704858492065325964">"Ostud"</item>
+ <item msgid="6083795019290250078">"Filmid"</item>
+ <item msgid="8302638329222449550">"Komöödia"</item>
+ <item msgid="3803709976021475052">"Reisimine"</item>
+ <item msgid="8116747365234169059">"Draama"</item>
+ <item msgid="7356447541595315913">"Haridus"</item>
+ <item msgid="7511135485827589547">"Loomad/loodus"</item>
+ <item msgid="6961248112238009967">"Uudised"</item>
+ <item msgid="6484685553679698447">"Mängud"</item>
+ <item msgid="2737158328243183190">"Kunst"</item>
+ <item msgid="6577176952650166615">"Meelelahutus"</item>
+ <item msgid="7886693831871777617">"Elustiil"</item>
+ <item msgid="8145832312485577062">"Muusika"</item>
+ <item msgid="1345789204804308580">"Esilinastus"</item>
+ <item msgid="2736680312770771994">"Tehnika/teadus"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Reaalajakanalid"</item>
diff --git a/res/values-et-rEE/rating_system_strings.xml b/res/values-et-rEE/rating_system_strings.xml
index 84c92834..f7a34763 100644
--- a/res/values-et-rEE/rating_system_strings.xml
+++ b/res/values-et-rEE/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Saated võivad sisaldada alla 15-aastastele lastele sobimatut materjali ja otsuse sobilikkuse kohta peaks langetama lapsevanem."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Saated võivad sisaldada alla 19-aastastele sobimatut materjali, mistõttu ei ole need sobilikud alla 19-aastastele noortele."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Kahemõttelise sisuga dialoog"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Vulgaarne tekst"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Seksuaalne sisu"</string>
diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml
index f68c9e6d..e28f2ccb 100644
--- a/res/values-et-rEE/strings.xml
+++ b/res/values-et-rEE/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Avatud lähtekoodi litsentsid"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Avatud lähtekoodi litsentsid"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Versioon"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Arendaja valikud"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Luba USB-telerituuner"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"USB-telerituuneri heli kuulmiseks peab teie teler toetama AC3-ülekannet."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3-heli võimalus"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Teie teler toetab AC3-ülekannet."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Teie teler ei toeta AC3-ülekannet."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Aita täiustada reaalajas kanaleid"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Jagage Google\'iga anonüümseid kasutus- ja diagnostikaandmeid, et saaksime rakenduse Reaalajakanalid paremaks muuta ning vältida näiteks kokkujooksmist ja hangumist."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Kanali vaatamiseks vajutage paremale ja sisestage PIN-kood"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Saade on blokeeritud"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Selle saate reiting on <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Ainult heli"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Nõrk signaal"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Interneti-ühendus puudub"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Pealkiri puudub"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Kanal on blokeeritud"</string>
- <string name="episode_format" msgid="4881195874563241096">"Hooaeg <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: jagu <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g>, <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Uus"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Allikad"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Häälestamine ebaõnnestus"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Selle toimingu käsitlemiseks ei leitud ühtegi rakendust."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Kõik allikakanalid on peidetud.\nValige vaatamiseks vähemalt üks kanal."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Pole saadaval nõrga videosignaali tõttu"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Video pole ootamatult saadaval"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Klahv TAGASI on mõeldud ühendatud seadme jaoks. Väljumiseks vajutage nuppu AVAEKRAAN."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Reaalajas kanaleid selles seadmes operatsioonisüsteemiga Android Lollipop ei toetata."</string>
diff --git a/res/values-eu-rES/arrays.xml b/res/values-eu-rES/arrays.xml
index 89b32135..e17ea273 100644
--- a/res/values-eu-rES/arrays.xml
+++ b/res/values-eu-rES/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Osoa"</item>
<item msgid="8568284598210500589">"Zooma"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Kanal guztiak"</item>
- <item msgid="928298872841713530">"Familia/Umeak"</item>
- <item msgid="2751606947569857164">"Kirolak"</item>
- <item msgid="7345749789651321496">"Erosketak"</item>
- <item msgid="167201149441442173">"Filmak"</item>
- <item msgid="525966731464264290">"Komedia"</item>
- <item msgid="6096710741527327836">"Bidaiak"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Hezkuntza"</item>
- <item msgid="7221999662426308394">"Animaliak/Basabizitza"</item>
- <item msgid="375300513250925001">"Albisteak"</item>
- <item msgid="7746320336582330410">"Jokoak"</item>
- <item msgid="1255741860568329178">"Artea"</item>
- <item msgid="7603949681065702867">"Ikuskizunak"</item>
- <item msgid="4453821994746804366">"Bizimodua"</item>
- <item msgid="3488534597567932843">"Musika"</item>
- <item msgid="7452153120614274095">"Onenak"</item>
- <item msgid="8215762047341133299">"Zientzia/Teknologia"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Kanal guztiak"</item>
+ <item msgid="6897460857821394118">"Familia/Umeak"</item>
+ <item msgid="551257741825778215">"Kirolak"</item>
+ <item msgid="452133796804325879">"Erosketak"</item>
+ <item msgid="3296058637230163031">"Filmak"</item>
+ <item msgid="1054540282883891201">"Komedia"</item>
+ <item msgid="7900158429062595471">"Bidaiak"</item>
+ <item msgid="3768998587825611787">"Drama"</item>
+ <item msgid="8340620094959282881">"Hezkuntza"</item>
+ <item msgid="7396447839483867269">"Animaliak/Basabizitza"</item>
+ <item msgid="4738043455148062673">"Albisteak"</item>
+ <item msgid="7405041316051047427">"Jokoak"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Kanal guztiak"</item>
+ <item msgid="7909003973960375395">"Familia/Umeak"</item>
+ <item msgid="3185279732911635789">"Kirolak"</item>
+ <item msgid="4704858492065325964">"Erosketak"</item>
+ <item msgid="6083795019290250078">"Filmak"</item>
+ <item msgid="8302638329222449550">"Komedia"</item>
+ <item msgid="3803709976021475052">"Bidaiak"</item>
+ <item msgid="8116747365234169059">"Drama"</item>
+ <item msgid="7356447541595315913">"Hezkuntza"</item>
+ <item msgid="7511135485827589547">"Animaliak/Basabizitza"</item>
+ <item msgid="6961248112238009967">"Albisteak"</item>
+ <item msgid="6484685553679698447">"Jokoak"</item>
+ <item msgid="2737158328243183190">"Artea"</item>
+ <item msgid="6577176952650166615">"Denbora-pasak"</item>
+ <item msgid="7886693831871777617">"Bizimodua"</item>
+ <item msgid="8145832312485577062">"Musika"</item>
+ <item msgid="1345789204804308580">"Estreinaldiak"</item>
+ <item msgid="2736680312770771994">"Zientzia/Teknologia"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Telebista zuzenean"</item>
diff --git a/res/values-eu-rES/rating_system_strings.xml b/res/values-eu-rES/rating_system_strings.xml
index 2e9cc03e..cb629c14 100644
--- a/res/values-eu-rES/rating_system_strings.xml
+++ b/res/values-eu-rES/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Programako materiala desegokia izan liteke 15 urtetik beherakoentzat. Gurasoek erabaki beharko dute beren umeek erabil dezaketen ala ez."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Programako materiala desegokia izan liteke 19 urtetik beherakoentzat. Beraz, ez da egokitzat jotzen adin horietako gaztetxoentzat."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Elkarrizketa iradokitzailea"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Hizkera zakarra"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Eduki sexuala"</string>
diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml
index 065e764c..1eea239e 100644
--- a/res/values-eu-rES/strings.xml
+++ b/res/values-eu-rES/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Kode irekiko lizentziak"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Kode irekiko lizentziak"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Bertsioa"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Garatzaileen aukerak"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Gaitu USB bidezko sintonizadorea"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"USB bidezko sintonizadorearen soinua entzuteko, AC3 sarbide zuzenarekin bateragarria izan behar du telebistak."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 audio-gaitasuna"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Telebista bateragarria da AC3 sarbide zuzenarekin."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Telebista ez da bateragarria AC3 sarbide zuzenarekin."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Lagundu Zuzeneko kanalak hobetzen"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Partekatu erabilerari buruzko datu eta diagnostiko anonimoak Google-rekin, Telebista zuzenean zerbitzua hobe dezagun, besteak beste, hutsegiteak gertatzea edo irudia izoztuta geratzea ekiditeko."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Kanal hau ikusteko, sakatu Eskuinera tekla eta idatzi PIN kodea."</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Telesaioa blokeatuta dago"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Telesaioa \"<xliff:g id="RATING">%1$s</xliff:g>\" gisa sailkatu da"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Audioa soilik"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Seinale ahula"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Ez zaude Internetera konektatuta"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Izenik ez"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Kanala blokeatuta dago"</string>
- <string name="episode_format" msgid="4881195874563241096">"<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>. denboraldia, <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g>. atala (<xliff:g id="EPISODE_TITLE">%3$s</xliff:g>)"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Berriak"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Iturburuak"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Ezin izan da sintonizatu"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Ez da aurkitu ekintza gauza dezakeen aplikaziorik."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Iturburuko kanal guztiak ezkutuan daude.\nHautatu gutxienez kanal bat ikusteko."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Ez dago erabilgarri, seinalea ahulegia delako"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Bideoa ez dago erabilgarri"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Atzera tekla konektatutako gailuari dagokio. Irteteko, sakatu Hasiera botoia."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Android Lollipop duen gailu honetan ezin da erabili Zuzeneko kanalak aplikazioa."</string>
diff --git a/res/values-fa/arrays.xml b/res/values-fa/arrays.xml
index a9b956f5..7b288615 100644
--- a/res/values-fa/arrays.xml
+++ b/res/values-fa/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"کامل"</item>
<item msgid="8568284598210500589">"بزرگ‌نمایی"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"همه کانال‌ها"</item>
- <item msgid="928298872841713530">"خانواده/کودکان"</item>
- <item msgid="2751606947569857164">"ورزش"</item>
- <item msgid="7345749789651321496">"خرید"</item>
- <item msgid="167201149441442173">"فیلم"</item>
- <item msgid="525966731464264290">"کمدی"</item>
- <item msgid="6096710741527327836">"سفر"</item>
- <item msgid="2851882187117833883">"درام"</item>
- <item msgid="78492781188719038">"آموزش"</item>
- <item msgid="7221999662426308394">"حیوانات/حیات وحش"</item>
- <item msgid="375300513250925001">"اخبار"</item>
- <item msgid="7746320336582330410">"بازی"</item>
- <item msgid="1255741860568329178">"هنر"</item>
- <item msgid="7603949681065702867">"سرگرمی"</item>
- <item msgid="4453821994746804366">"روش زندگی"</item>
- <item msgid="3488534597567932843">"موسیقی"</item>
- <item msgid="7452153120614274095">"برتر"</item>
- <item msgid="8215762047341133299">"فناوری/علم"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"همه کانال‌ها"</item>
+ <item msgid="6897460857821394118">"خانواده/کودکان"</item>
+ <item msgid="551257741825778215">"ورزش"</item>
+ <item msgid="452133796804325879">"خرید"</item>
+ <item msgid="3296058637230163031">"فیلم‌"</item>
+ <item msgid="1054540282883891201">"کمدی"</item>
+ <item msgid="7900158429062595471">"سفر"</item>
+ <item msgid="3768998587825611787">"درام"</item>
+ <item msgid="8340620094959282881">"آموزشی"</item>
+ <item msgid="7396447839483867269">"حیوانات/حیات وحش"</item>
+ <item msgid="4738043455148062673">"اخبار"</item>
+ <item msgid="7405041316051047427">"بازی"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"همه کانال‌ها"</item>
+ <item msgid="7909003973960375395">"خانواده/کودکان"</item>
+ <item msgid="3185279732911635789">"ورزش"</item>
+ <item msgid="4704858492065325964">"خرید"</item>
+ <item msgid="6083795019290250078">"فیلم‌"</item>
+ <item msgid="8302638329222449550">"کمدی"</item>
+ <item msgid="3803709976021475052">"سفر"</item>
+ <item msgid="8116747365234169059">"درام"</item>
+ <item msgid="7356447541595315913">"آموزشی"</item>
+ <item msgid="7511135485827589547">"حیوانات/حیات وحش"</item>
+ <item msgid="6961248112238009967">"اخبار"</item>
+ <item msgid="6484685553679698447">"بازی"</item>
+ <item msgid="2737158328243183190">"هنر"</item>
+ <item msgid="6577176952650166615">"سرگرمی"</item>
+ <item msgid="7886693831871777617">"سبک زندگی"</item>
+ <item msgid="8145832312485577062">"موسیقی"</item>
+ <item msgid="1345789204804308580">"برتر"</item>
+ <item msgid="2736680312770771994">"فناوری/علم"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"کانال‌های زنده"</item>
diff --git a/res/values-fa/rating_system_strings.xml b/res/values-fa/rating_system_strings.xml
index 0e8c1cf7..fdc5a75d 100644
--- a/res/values-fa/rating_system_strings.xml
+++ b/res/values-fa/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"ممکن است برنامه‌ها برای مخاطبین زیر ۱۵ سال نامناسب باشد و صلاحدید والدین باید استفاده شود."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"ممکن است برنامه‌ها حاوی مطالب نامناسب برای مخاطبین کوچک‌تر از ۲۹ سال باشند، بنابراین برای جوانان زیر ۱۹ مناسب نیستند."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"گفتگوی وسوسه برانگیز"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"دشنام‌گویی"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"محتوای جنسی"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 01f2ff18..46a58f15 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"مجوزهای منبع آزاد"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"مجوزهای منبع آزاد"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"نسخه"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"گزینه‌های برنامه‌نویس"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"‏فعال کردن تنظیم‌کننده تلویزیون USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"‏برای شنیدن صدای تنظیم‌کننده تلویزیون USB، تلویزیونتان باید از انتقال AC3 پشتیبانی کند."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"‏قابلیت صوتی AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"‏تلویزیون شما از انتقال AC3 پشتیبانی می‌کند."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"‏تلویزیون شما از انتقال AC3 پشتیبانی نمی‌کند."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"کمک به بهبود «کانال‌های مستقیم»"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"‏داده‌های عیب‌یابی و مصرف بی‌نام را با Google به اشتراک بگذارید تا ما کانال‌های زنده را بهتر کنیم و از مشکلاتی مانند خرابی و ثابت ماندن تصویر جلوگیری کنیم."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"برای مشاهده این کانال، راست را فشار دهید و پین خودتان را وارد کنید"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"برنامه مسدود شده است"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"رتبه‌بندی این برنامه <xliff:g id="RATING">%1$s</xliff:g> است."</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"فقط صدا"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"سیگنال ضعیف است"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"اتصال اینترنت قطع است"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"بدون عنوان"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"کانال مسدود شد"</string>
- <string name="episode_format" msgid="4881195874563241096">"ف <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: ق <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> ‏<xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"جدید"</string>
<string name="setup_category_done" msgid="4750902502852212319">"منابع"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"تنظیم نشد"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"برنامه‌ای برای انجام این اقدام پیدا نشد."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"همه کانال‌های منبع پنهان هستند.\nحداقل یک کانال را برای تماشا انتخاب کنید."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"به علت سیگنال ضعیف ویدیو، در دسترس نیست"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"این ویدیو به‌طور غیر منتظره‌ای در دسترس نیست."</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"کلید بازگشت برای دستگاه‌ متصل است. برای خروج دکمه صفحه اصلی را فشار دهید."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"‏کانال‌های مستقیم در این دستگاه دارای Android Lollipop پشتیبانی نمی‌شوند."</string>
diff --git a/res/values-fi/arrays.xml b/res/values-fi/arrays.xml
index 01266b27..c13f5788 100644
--- a/res/values-fi/arrays.xml
+++ b/res/values-fi/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Täysi"</item>
<item msgid="8568284598210500589">"Zoomattu"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Kaikki kanavat"</item>
- <item msgid="928298872841713530">"Perhe/lapset"</item>
- <item msgid="2751606947569857164">"Urheilu"</item>
- <item msgid="7345749789651321496">"Ostokset"</item>
- <item msgid="167201149441442173">"Elokuvat"</item>
- <item msgid="525966731464264290">"Komedia"</item>
- <item msgid="6096710741527327836">"Matkailu"</item>
- <item msgid="2851882187117833883">"Draama"</item>
- <item msgid="78492781188719038">"Opetus"</item>
- <item msgid="7221999662426308394">"Eläimet/luonto"</item>
- <item msgid="375300513250925001">"Uutiset"</item>
- <item msgid="7746320336582330410">"Pelit"</item>
- <item msgid="1255741860568329178">"Taiteet"</item>
- <item msgid="7603949681065702867">"Viihde"</item>
- <item msgid="4453821994746804366">"Elämäntapa"</item>
- <item msgid="3488534597567932843">"Musiikki"</item>
- <item msgid="7452153120614274095">"Ensiesitys"</item>
- <item msgid="8215762047341133299">"Tekniikka/tiede"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Kaikki kanavat"</item>
+ <item msgid="6897460857821394118">"Perhe ja lapset"</item>
+ <item msgid="551257741825778215">"Urheilu"</item>
+ <item msgid="452133796804325879">"Ostokset"</item>
+ <item msgid="3296058637230163031">"Elokuvat"</item>
+ <item msgid="1054540282883891201">"Komedia"</item>
+ <item msgid="7900158429062595471">"Matkailu"</item>
+ <item msgid="3768998587825611787">"Draama"</item>
+ <item msgid="8340620094959282881">"Opetus"</item>
+ <item msgid="7396447839483867269">"Eläimet ja luonto"</item>
+ <item msgid="4738043455148062673">"Uutiset"</item>
+ <item msgid="7405041316051047427">"Pelit"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Kaikki kanavat"</item>
+ <item msgid="7909003973960375395">"Perhe ja lapset"</item>
+ <item msgid="3185279732911635789">"Urheilu"</item>
+ <item msgid="4704858492065325964">"Ostokset"</item>
+ <item msgid="6083795019290250078">"Elokuvat"</item>
+ <item msgid="8302638329222449550">"Komedia"</item>
+ <item msgid="3803709976021475052">"Matkailu"</item>
+ <item msgid="8116747365234169059">"Draama"</item>
+ <item msgid="7356447541595315913">"Opetus"</item>
+ <item msgid="7511135485827589547">"Eläimet ja luonto"</item>
+ <item msgid="6961248112238009967">"Uutiset"</item>
+ <item msgid="6484685553679698447">"Pelit"</item>
+ <item msgid="2737158328243183190">"Taide"</item>
+ <item msgid="6577176952650166615">"Viihde"</item>
+ <item msgid="7886693831871777617">"Lifestyle"</item>
+ <item msgid="8145832312485577062">"Musiikki"</item>
+ <item msgid="1345789204804308580">"Ensiesitys"</item>
+ <item msgid="2736680312770771994">"Tekniikka ja tiede"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Live-kanavat"</item>
diff --git a/res/values-fi/rating_system_strings.xml b/res/values-fi/rating_system_strings.xml
index 41622490..50851dfb 100644
--- a/res/values-fi/rating_system_strings.xml
+++ b/res/values-fi/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Ohjelmat saattavat sisältää materiaalia, joka ei sovi alle 15-vuotiaille. Vanhempien valvontaa suositellaan."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Ohjelmat saattavat sisältää materiaalia, joka ei sovi alle 19-vuotiaille."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Sanallisia viittauksia seksiin"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Karkea kielenkäyttö"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Seksuaalinen sisältö"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 05c134ad..bb0a0000 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Avoimen lähdekoodin käyttöluvat"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Avoimen lähdekoodin käyttöluvat"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Versio"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Kehittäjäasetukset"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Ota käyttöön USB-TV-viritin"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Televisiosi on tuettava AC3-läpäisyä, jotta voit kuulla TV-virittimen äänet."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3-äänivalmius"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Televisiosi tukee AC3-läpäisyä."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Televisiosi ei tue AC3-läpäisyä."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Auta parantamaan live-kanavia"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Jakamalla Googlelle anonyymejä käyttö- ja diagnostiikkatietoja autat meitä parantamaan livekanavia ja estämään kaatumisen ja pysähtymisen kaltaisia ongelmia."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Katsele tätä kanavaa painamalla näppäimellä oikealle ja antamalla PIN-koodi"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Ohjelma on estetty"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Tämän ohjelman luokitus on <xliff:g id="RATING">%1$s</xliff:g>."</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Vain ääni"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Heikko signaali"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Ei internetyhteyttä"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Ei nimeä"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Kanava estetty"</string>
- <string name="episode_format" msgid="4881195874563241096">"Kausi <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: jakso <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g>, <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Uudet"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Lähteet"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Viritys epäonnistui."</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Tätä toimintoa käsittelevää sovellusta ei löydy."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Kaikki lähdekanavat on piilotettu.\nValitse vähintään yksi katseltava kanava."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Ei käytettävissä heikon videosignaalin takia."</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Video ei yllättäen ole käytettävissä."</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"EDELLINEN-painike koskee liitettyä laitetta. Poistu painamalla ETUSIVU-painiketta."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Live-kanavia ei tueta tällä Android Lollipop -laitteella."</string>
diff --git a/res/values-fr-rCA/arrays.xml b/res/values-fr-rCA/arrays.xml
index 753019ee..12605879 100644
--- a/res/values-fr-rCA/arrays.xml
+++ b/res/values-fr-rCA/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Plein écran"</item>
<item msgid="8568284598210500589">"Zoom"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Toutes les chaînes"</item>
- <item msgid="928298872841713530">"Famille/Enfants"</item>
- <item msgid="2751606947569857164">"Sports"</item>
- <item msgid="7345749789651321496">"Produits"</item>
- <item msgid="167201149441442173">"Films"</item>
- <item msgid="525966731464264290">"Humour"</item>
- <item msgid="6096710741527327836">"Voyages et déplacements"</item>
- <item msgid="2851882187117833883">"Drame"</item>
- <item msgid="78492781188719038">"Éducation"</item>
- <item msgid="7221999662426308394">"Animaux/Faune sauvage"</item>
- <item msgid="375300513250925001">"Actualités"</item>
- <item msgid="7746320336582330410">"Jeux"</item>
- <item msgid="1255741860568329178">"Arts"</item>
- <item msgid="7603949681065702867">"Divertissement"</item>
- <item msgid="4453821994746804366">"Style de vie"</item>
- <item msgid="3488534597567932843">"Musique"</item>
- <item msgid="7452153120614274095">"Premier"</item>
- <item msgid="8215762047341133299">"Technologies et sciences"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Toutes les chaînes"</item>
+ <item msgid="6897460857821394118">"Famille/Enfants"</item>
+ <item msgid="551257741825778215">"Sports"</item>
+ <item msgid="452133796804325879">"Magasinage"</item>
+ <item msgid="3296058637230163031">"Films"</item>
+ <item msgid="1054540282883891201">"Humour"</item>
+ <item msgid="7900158429062595471">"Voyage"</item>
+ <item msgid="3768998587825611787">"Drame"</item>
+ <item msgid="8340620094959282881">"Éducation"</item>
+ <item msgid="7396447839483867269">"Animaux/Faune sauvage"</item>
+ <item msgid="4738043455148062673">"Actualités"</item>
+ <item msgid="7405041316051047427">"Jeux"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Toutes les chaînes"</item>
+ <item msgid="7909003973960375395">"Famille/Enfants"</item>
+ <item msgid="3185279732911635789">"Sports"</item>
+ <item msgid="4704858492065325964">"Magasinage"</item>
+ <item msgid="6083795019290250078">"Films"</item>
+ <item msgid="8302638329222449550">"Humour"</item>
+ <item msgid="3803709976021475052">"Voyage"</item>
+ <item msgid="8116747365234169059">"Drame"</item>
+ <item msgid="7356447541595315913">"Éducation"</item>
+ <item msgid="7511135485827589547">"Animaux/Faune sauvage"</item>
+ <item msgid="6961248112238009967">"Actualités"</item>
+ <item msgid="6484685553679698447">"Jeux"</item>
+ <item msgid="2737158328243183190">"Arts"</item>
+ <item msgid="6577176952650166615">"Divertissement"</item>
+ <item msgid="7886693831871777617">"Style de vie"</item>
+ <item msgid="8145832312485577062">"Musique"</item>
+ <item msgid="1345789204804308580">"Premier"</item>
+ <item msgid="2736680312770771994">"Technologies et sciences"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Chaînes en direct"</item>
diff --git a/res/values-fr-rCA/rating_system_strings.xml b/res/values-fr-rCA/rating_system_strings.xml
index 9630ca68..737e8d2d 100644
--- a/res/values-fr-rCA/rating_system_strings.xml
+++ b/res/values-fr-rCA/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Programmes susceptibles de présenter du contenu non adapté aux jeunes de moins de 15 ans. Contrôle parental nécessaire."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Programmes susceptibles de présenter du contenu non adapté aux jeunes de moins de 19 ans."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Dialogue suggestif"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Langage grossier"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Contenu à caractère sexuel"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 347e9537..7d3b9900 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -64,7 +64,7 @@
<string name="program_title_for_blocked_channel" msgid="5358005891746983819">"Chaîne bloquée"</string>
<string name="default_language" msgid="4122326459624337928">"Langue : indéterminée"</string>
<string name="side_panel_title_closed_caption" msgid="2513905054082568780">"Sous-titres"</string>
- <string name="closed_caption_option_item_off" msgid="4824009036785647753">"Désactiver"</string>
+ <string name="closed_caption_option_item_off" msgid="4824009036785647753">"Désactivé"</string>
<string name="closed_caption_system_settings" msgid="1856974607743827178">"Personnaliser format"</string>
<string name="closed_caption_system_settings_description" msgid="6285276836057964524">"Déf. param. s-titres, pr tout le système"</string>
<string name="side_panel_title_display_mode" msgid="6346286034015991229">"Mode d\'affichage"</string>
@@ -96,7 +96,7 @@
<string name="input_selector_tuner_label" msgid="6631205039926880892">"Syntoniseur"</string>
<string name="menu_parental_controls" msgid="2474294054521345840">"Contrôle parental"</string>
<string name="option_toggle_parental_controls_on" msgid="9122851821454622696">"Activer"</string>
- <string name="option_toggle_parental_controls_off" msgid="7797910199040440618">"Désactiver"</string>
+ <string name="option_toggle_parental_controls_off" msgid="7797910199040440618">"Désactivé"</string>
<string name="option_channels_locked" msgid="5797855082297549907">"Chaînes bloquées"</string>
<string name="option_channels_lock_all" msgid="6594512884477342940">"Tout bloquer"</string>
<string name="option_channels_unlock_all" msgid="6839513296447567623">"Tout débloquer"</string>
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Licences de logiciels libres"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Licences de logiciels libres"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Version"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Options pour les développeurs"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Activer le syntoniseur télé USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Pour entendre le son du syntoniseur télé USB, votre téléviseur doit prendre en charge la conversion AC3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Compatibilité audio AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Votre téléviseur prend en charge la conversion AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Votre téléviseur ne prend pas en charge la conversion AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Aider à améliorer les chaînes en direct"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Envoyez-nous des données anonymes d\'utilisation et de diagnostic pour nous aider à améliorer Chaîne en direct et à éviter les problèmes de plantage ou de blocage."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Pour regarder cette chaîne, touchez la droite, puis entrez votre NIP."</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Programme bloqué"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Ce programme est classé « <xliff:g id="RATING">%1$s</xliff:g> »"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Audio uniquement"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Signal faible"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Aucune connexion Internet"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Sans titre"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Chaîne bloquée"</string>
- <string name="episode_format" msgid="4881195874563241096">"Saison <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>, épisode <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g>, « <xliff:g id="EPISODE_TITLE">%3$s</xliff:g> »"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Nouveau"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Sources"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Échec des réglages"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Aucune application pouvant gérer cette action n\'a été trouvée."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Toutes les chaînes source sont masquées.\nSélectionnez au moins une chaîne à regarder."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Indisponible en raison d\'un signal vidéo faible"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Indisponibilité inattendue de la vidéo"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"La touche RETOUR concerne l\'appareil connecté. Appuyez sur le bouton ACCUEIL pour quitter."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Les chaînes en direct ne sont pas prises en charge sur cet appareil avec Android Lollipop."</string>
diff --git a/res/values-fr/arrays.xml b/res/values-fr/arrays.xml
index c893079f..bce4a116 100644
--- a/res/values-fr/arrays.xml
+++ b/res/values-fr/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Plein écran"</item>
<item msgid="8568284598210500589">"Zoom"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Toutes les chaînes"</item>
- <item msgid="928298872841713530">"Famille/Enfants"</item>
- <item msgid="2751606947569857164">"Sports"</item>
- <item msgid="7345749789651321496">"Shopping"</item>
- <item msgid="167201149441442173">"Films"</item>
- <item msgid="525966731464264290">"Humour"</item>
- <item msgid="6096710741527327836">"Voyage"</item>
- <item msgid="2851882187117833883">"Drame"</item>
- <item msgid="78492781188719038">"Enseignement"</item>
- <item msgid="7221999662426308394">"Animaux/Faune sauvage"</item>
- <item msgid="375300513250925001">"Actualités"</item>
- <item msgid="7746320336582330410">"Jeux"</item>
- <item msgid="1255741860568329178">"Arts"</item>
- <item msgid="7603949681065702867">"Divertissement"</item>
- <item msgid="4453821994746804366">"Style de vie"</item>
- <item msgid="3488534597567932843">"Musique"</item>
- <item msgid="7452153120614274095">"Haut de gamme"</item>
- <item msgid="8215762047341133299">"Technologie/Science"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Toutes les chaînes"</item>
+ <item msgid="6897460857821394118">"Famille/Enfants"</item>
+ <item msgid="551257741825778215">"Sport"</item>
+ <item msgid="452133796804325879">"Achats"</item>
+ <item msgid="3296058637230163031">"Films"</item>
+ <item msgid="1054540282883891201">"Humour"</item>
+ <item msgid="7900158429062595471">"Voyage"</item>
+ <item msgid="3768998587825611787">"Drame"</item>
+ <item msgid="8340620094959282881">"Éducation"</item>
+ <item msgid="7396447839483867269">"Animaux/Faune sauvage"</item>
+ <item msgid="4738043455148062673">"Actualités"</item>
+ <item msgid="7405041316051047427">"Jeux"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Toutes les chaînes"</item>
+ <item msgid="7909003973960375395">"Famille/Enfants"</item>
+ <item msgid="3185279732911635789">"Sport"</item>
+ <item msgid="4704858492065325964">"Achats"</item>
+ <item msgid="6083795019290250078">"Films"</item>
+ <item msgid="8302638329222449550">"Humour"</item>
+ <item msgid="3803709976021475052">"Voyage"</item>
+ <item msgid="8116747365234169059">"Drame"</item>
+ <item msgid="7356447541595315913">"Éducation"</item>
+ <item msgid="7511135485827589547">"Animaux/Faune sauvage"</item>
+ <item msgid="6961248112238009967">"Actualités"</item>
+ <item msgid="6484685553679698447">"Jeux"</item>
+ <item msgid="2737158328243183190">"Arts"</item>
+ <item msgid="6577176952650166615">"Divertissements"</item>
+ <item msgid="7886693831871777617">"Style de vie"</item>
+ <item msgid="8145832312485577062">"Musique"</item>
+ <item msgid="1345789204804308580">"Contenu premium"</item>
+ <item msgid="2736680312770771994">"Technologie/Science"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"TV en direct"</item>
diff --git a/res/values-fr/rating_system_strings.xml b/res/values-fr/rating_system_strings.xml
index 52539e5d..97cbad73 100644
--- a/res/values-fr/rating_system_strings.xml
+++ b/res/values-fr/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Programmes susceptibles de présenter des contenus non adaptés aux enfants de moins de 15 ans. Contrôle parental nécessaire."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Programmes susceptibles de présenter des contenus non adaptés aux personnes de moins de 19 ans."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Dialogue suggestif"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Propos grossiers"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Contenu à caractère sexuel"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 4a21f5ab..96133a8e 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Licences Open Source"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Licences Open Source"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Version"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Options pour les développeurs"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Activer le tuner TV USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Pour entendre le son du tuner TV USB, votre téléviseur doit être compatible avec la conversion AC3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Fonctionnalité de conversion audio AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Votre téléviseur est compatible avec la conversion AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Votre téléviseur n\'est pas compatible avec la conversion AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Contribuer à l\'amélioration de l\'application Chaînes en direct"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Envoyez-nous des données anonymes d\'utilisation et de diagnostic pour nous aider à améliorer TV en direct et à éviter les problèmes de plantage ou de blocage."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Pour regarder cette chaîne, appuyez sur le bouton droit, puis saisissez votre code d\'accès."</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Programme bloqué"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Classification de ce programme : <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Audio uniquement"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Signal faible"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Aucune connexion Internet"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Sans titre"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Chaîne bloquée"</string>
- <string name="episode_format" msgid="4881195874563241096">"S<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>, ép. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> : <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Nouvelle"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Sources"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Échec des réglages."</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Aucune application trouvée pour gérer cette action."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Toutes les chaînes source sont masquées.\nSélectionnez au moins une chaîne à regarder."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Indisponible en raison d\'un signal vidéo faible"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Indisponibilité inattendue de la vidéo"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"La touche RETOUR concerne l\'appareil connecté. Appuyez sur le bouton ACCUEIL pour quitter."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"L\'application Chaînes en direct n\'est pas compatible avec les appareils équipés d\'Android Lollipop."</string>
diff --git a/res/values-gl-rES/arrays.xml b/res/values-gl-rES/arrays.xml
index 0bbc3853..e9a308fa 100644
--- a/res/values-gl-rES/arrays.xml
+++ b/res/values-gl-rES/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Completa"</item>
<item msgid="8568284598210500589">"Zoom"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Todas as canles"</item>
- <item msgid="928298872841713530">"Familiar/infantil"</item>
- <item msgid="2751606947569857164">"Deportes"</item>
- <item msgid="7345749789651321496">"Compras"</item>
- <item msgid="167201149441442173">"Películas"</item>
- <item msgid="525966731464264290">"Comedia"</item>
- <item msgid="6096710741527327836">"Viaxes"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Educación"</item>
- <item msgid="7221999662426308394">"Animais/vida salvaxe"</item>
- <item msgid="375300513250925001">"Noticias"</item>
- <item msgid="7746320336582330410">"Xogos"</item>
- <item msgid="1255741860568329178">"Arte"</item>
- <item msgid="7603949681065702867">"Entretemento"</item>
- <item msgid="4453821994746804366">"Estilo de vida"</item>
- <item msgid="3488534597567932843">"Música"</item>
- <item msgid="7452153120614274095">"Estrea"</item>
- <item msgid="8215762047341133299">"Ciencia e tecnoloxía"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Todas as canles"</item>
+ <item msgid="6897460857821394118">"Familiar/infantil"</item>
+ <item msgid="551257741825778215">"Deportes"</item>
+ <item msgid="452133796804325879">"Compras"</item>
+ <item msgid="3296058637230163031">"Películas"</item>
+ <item msgid="1054540282883891201">"Comedia"</item>
+ <item msgid="7900158429062595471">"Viaxe"</item>
+ <item msgid="3768998587825611787">"Drama"</item>
+ <item msgid="8340620094959282881">"Educación"</item>
+ <item msgid="7396447839483867269">"Animais/vida salvaxe"</item>
+ <item msgid="4738043455148062673">"Noticias"</item>
+ <item msgid="7405041316051047427">"Xogos"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Todas as canles"</item>
+ <item msgid="7909003973960375395">"Familiar/infantil"</item>
+ <item msgid="3185279732911635789">"Deportes"</item>
+ <item msgid="4704858492065325964">"Compras"</item>
+ <item msgid="6083795019290250078">"Películas"</item>
+ <item msgid="8302638329222449550">"Comedia"</item>
+ <item msgid="3803709976021475052">"Viaxe"</item>
+ <item msgid="8116747365234169059">"Drama"</item>
+ <item msgid="7356447541595315913">"Educación"</item>
+ <item msgid="7511135485827589547">"Animais/vida salvaxe"</item>
+ <item msgid="6961248112238009967">"Noticias"</item>
+ <item msgid="6484685553679698447">"Xogos"</item>
+ <item msgid="2737158328243183190">"Arte"</item>
+ <item msgid="6577176952650166615">"Entretemento"</item>
+ <item msgid="7886693831871777617">"Estilo de vida"</item>
+ <item msgid="8145832312485577062">"Música"</item>
+ <item msgid="1345789204804308580">"Estrea"</item>
+ <item msgid="2736680312770771994">"Ciencia e tecnoloxía"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"TV en directo"</item>
diff --git a/res/values-gl-rES/rating_system_strings.xml b/res/values-gl-rES/rating_system_strings.xml
index 51857a45..c320636f 100644
--- a/res/values-gl-rES/rating_system_strings.xml
+++ b/res/values-gl-rES/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Os programas poden conter material inadecuado para os menores de 15 anos e, polo tanto, deberían quedar a discreción dos pais."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Os programas poden conter material inadecuado para os menores de 19 anos e, polo tanto, non son adecuados para rapaces de menos de 19 anos."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Diálogo insinuante"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Linguaxe vulgar"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Contido sexual"</string>
diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml
index 63ebbcbc..dc1624a4 100644
--- a/res/values-gl-rES/strings.xml
+++ b/res/values-gl-rES/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Licenzas de código aberto"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Licenzas de código aberto"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Versión"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Opcións de programador"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Activar sintonizador de televisión USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Para escoitar o son do sintonizador de televisión USB, a túa televisión debe ser compatible coa conexión AC3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Compatibilidade con audio AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"A túa televisión é compatible coa conexión AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"A túa televisión non é compatible coa conexión AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Axudar a mellorar Canles en directo"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Comparte datos de uso e de diagnóstico anónimos con Google para que poidamos mellorar a TV en directo e evitar bloqueos e fallos."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Para ver esta canle, preme na tecla cara á dereita e introduce o PIN"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"O programa está bloqueado"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Este programa está clasificado como <xliff:g id="RATING">%1$s</xliff:g>."</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Só audio"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Sinal feble"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Non hai conexión a Internet"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Sen título"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Canle bloqueada"</string>
- <string name="episode_format" msgid="4881195874563241096">"T<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Novas"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Fontes"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Erro de sintonización"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Non se encontrou ningunha aplicación para procesar esta acción."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Todas as canles de orixe están ocultas.\nSelecciona polo menos unha canle para ver."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Non dispoñible debido a un sinal de vídeo feble"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"O vídeo deixou de estar dispoñible de forma inesperada"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"A tecla Atrás aplícase ao dispositivo conectado. Preme o botón de inicio para saír."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"As canles en directo non son compatibles neste dispositivo con Android Lollipop."</string>
diff --git a/res/values-hi/arrays.xml b/res/values-hi/arrays.xml
index 37e4222c..5d91d9f2 100644
--- a/res/values-hi/arrays.xml
+++ b/res/values-hi/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"पूर्ण"</item>
<item msgid="8568284598210500589">"ज़ूम करें"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"सभी चैनल"</item>
- <item msgid="928298872841713530">"परिवार/बच्चे"</item>
- <item msgid="2751606947569857164">"खेलकूद"</item>
- <item msgid="7345749789651321496">"खरीदारी"</item>
- <item msgid="167201149441442173">"फ़िल्में"</item>
- <item msgid="525966731464264290">"हास्य"</item>
- <item msgid="6096710741527327836">"यात्रा"</item>
- <item msgid="2851882187117833883">"नाटक"</item>
- <item msgid="78492781188719038">"शिक्षा"</item>
- <item msgid="7221999662426308394">"पशु/वन्य जीवन"</item>
- <item msgid="375300513250925001">"समाचार"</item>
- <item msgid="7746320336582330410">"गेमिंग"</item>
- <item msgid="1255741860568329178">"कला"</item>
- <item msgid="7603949681065702867">"मनोरंजन"</item>
- <item msgid="4453821994746804366">"जीवनशैली"</item>
- <item msgid="3488534597567932843">"संगीत"</item>
- <item msgid="7452153120614274095">"प्रीमियर"</item>
- <item msgid="8215762047341133299">"तकनीक/विज्ञान"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"सभी चैनल"</item>
+ <item msgid="6897460857821394118">"परिवार/बच्चे"</item>
+ <item msgid="551257741825778215">"खेलकूद"</item>
+ <item msgid="452133796804325879">"शॉपिंग"</item>
+ <item msgid="3296058637230163031">"फ़िल्में"</item>
+ <item msgid="1054540282883891201">"हास्य"</item>
+ <item msgid="7900158429062595471">"यात्रा"</item>
+ <item msgid="3768998587825611787">"ड्रामा"</item>
+ <item msgid="8340620094959282881">"शिक्षा"</item>
+ <item msgid="7396447839483867269">"पशु/वन्य जीवन"</item>
+ <item msgid="4738043455148062673">"समाचार"</item>
+ <item msgid="7405041316051047427">"गेमिंग"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"सभी चैनल"</item>
+ <item msgid="7909003973960375395">"परिवार/बच्चे"</item>
+ <item msgid="3185279732911635789">"खेलकूद"</item>
+ <item msgid="4704858492065325964">"शॉपिंग"</item>
+ <item msgid="6083795019290250078">"फ़िल्में"</item>
+ <item msgid="8302638329222449550">"हास्य"</item>
+ <item msgid="3803709976021475052">"यात्रा"</item>
+ <item msgid="8116747365234169059">"ड्रामा"</item>
+ <item msgid="7356447541595315913">"शिक्षा"</item>
+ <item msgid="7511135485827589547">"पशु/वन्य जीवन"</item>
+ <item msgid="6961248112238009967">"समाचार"</item>
+ <item msgid="6484685553679698447">"गेमिंग"</item>
+ <item msgid="2737158328243183190">"कला"</item>
+ <item msgid="6577176952650166615">"मनोरंजन"</item>
+ <item msgid="7886693831871777617">"जीवनशैली"</item>
+ <item msgid="8145832312485577062">"संगीत"</item>
+ <item msgid="1345789204804308580">"प्रीमियर"</item>
+ <item msgid="2736680312770771994">"तकनीक/विज्ञान"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"लाइव चैनल"</item>
diff --git a/res/values-hi/rating_system_strings.xml b/res/values-hi/rating_system_strings.xml
index 6935e16b..09419c68 100644
--- a/res/values-hi/rating_system_strings.xml
+++ b/res/values-hi/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"प्रोग्राम में 15 वर्ष से कम आयु वाले दर्शकों के लिए अनुपयुक्त सामग्री हो सकती है और इसलिए उनके लिए अभिभावकीय स्वविवेक का उपयोग किया जाना चाहिए."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"प्रोग्राम में 19 वर्ष से कम आयु वाले दर्शकों के लिए अनुपयुक्त सामग्री हो सकती है और इसलिए वे 19 वर्ष से कम आयु के युवाओं के लिए उपयुक्त नहीं हैं."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"अश्लील संवाद"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"अशिष्ट भाषा"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"यौन सामग्री"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 53676212..09d4567a 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"ओपन सोर्स लाइसेंस"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"ओपन सोर्स लाइसेंस"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"वर्शन"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"डेवलपर विकल्प"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"USB टीवी ट्यूनर सक्षम करें"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"USB टीवी ट्यूनर की आवाज़ सुनने के लिए, आपके टीवी को AC3 पासथ्रू का समर्थन करना चाहिए."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 ऑडियो क्षमता"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"आपका टीवी AC3 पासथ्रू का समर्थन करता है."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"आपका टीवी AC3 पासथ्रू का समर्थन नहीं करता."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"लाइव चैनल बेहतर बनाने में सहायता करें"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Google के साथ अनाम उपयोग और निदान डेटा साझा करें ताकि हम लाइव चैनल को बेहतर बना सकें और क्रैश होने तथा फ़्रीज होने जैसी समस्‍याओं को रोक सकें."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"इस चैनल को देखने के लिए, दाईं ओर दबाएं और अपना पिन डालें"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"यह कार्यक्रम बंद कर दिया गया है"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"इस कार्यक्रम को <xliff:g id="RATING">%1$s</xliff:g> रेट किया गया है"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"केवल ऑडियो"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"कमज़ोर सिग्नल"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"कोई इंटरनेट कनेक्शन नहीं"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"कोई शीर्षक नहीं"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"चैनल अवरोधित"</string>
- <string name="episode_format" msgid="4881195874563241096">"क्रमांक<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: एपिसोड <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"नए"</string>
<string name="setup_category_done" msgid="4750902502852212319">"स्रोत"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"ट्यून विफल रहा"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"यह कार्रवाई प्रबंधित करने के लिए कोई ऐप नहीं मिला."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"सभी स्रोत चैनल छिपे हुए हैं.\nदेखने के लिए कम से कम कोई एक चैनल चुनें."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"कमज़ोर वीडियो सिग्नल के कारण अनुपलब्ध है"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"वीडियो अनपेक्षित रूप से अनुपलब्ध है"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"BACK कुंजी कनेक्ट किए गए डिवाइस के लिए है. बाहर निकलने के लिए HOME बटन दबाएं."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Android Lollipop वाले इस डिवाइस पर लाइव चैनल समर्थित नहीं है."</string>
diff --git a/res/values-hr/arrays.xml b/res/values-hr/arrays.xml
index 463686b8..6a8b8709 100644
--- a/res/values-hr/arrays.xml
+++ b/res/values-hr/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Cijeli"</item>
<item msgid="8568284598210500589">"Zumiranje"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Svi kanali"</item>
- <item msgid="928298872841713530">"Obitelj/djeca"</item>
- <item msgid="2751606947569857164">"Sport"</item>
- <item msgid="7345749789651321496">"Kupnja"</item>
- <item msgid="167201149441442173">"Filmovi"</item>
- <item msgid="525966731464264290">"Komedija"</item>
- <item msgid="6096710741527327836">"Putovanja"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Obrazovanje"</item>
- <item msgid="7221999662426308394">"Životinjski svijet"</item>
- <item msgid="375300513250925001">"Vijesti"</item>
- <item msgid="7746320336582330410">"Igre"</item>
- <item msgid="1255741860568329178">"Umjetnost"</item>
- <item msgid="7603949681065702867">"Zabava"</item>
- <item msgid="4453821994746804366">"Životni stil"</item>
- <item msgid="3488534597567932843">"Glazba"</item>
- <item msgid="7452153120614274095">"Premijere"</item>
- <item msgid="8215762047341133299">"Tehnologija/znanost"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Svi kanali"</item>
+ <item msgid="6897460857821394118">"Obitelj/djeca"</item>
+ <item msgid="551257741825778215">"Sport"</item>
+ <item msgid="452133796804325879">"Kupnja"</item>
+ <item msgid="3296058637230163031">"Filmovi"</item>
+ <item msgid="1054540282883891201">"Komedija"</item>
+ <item msgid="7900158429062595471">"Putovanja"</item>
+ <item msgid="3768998587825611787">"Drama"</item>
+ <item msgid="8340620094959282881">"Obrazovanje"</item>
+ <item msgid="7396447839483867269">"Životinjski svijet"</item>
+ <item msgid="4738043455148062673">"Vijesti"</item>
+ <item msgid="7405041316051047427">"Igre"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Svi kanali"</item>
+ <item msgid="7909003973960375395">"Obitelj/djeca"</item>
+ <item msgid="3185279732911635789">"Sport"</item>
+ <item msgid="4704858492065325964">"Kupnja"</item>
+ <item msgid="6083795019290250078">"Filmovi"</item>
+ <item msgid="8302638329222449550">"Komedija"</item>
+ <item msgid="3803709976021475052">"Putovanja"</item>
+ <item msgid="8116747365234169059">"Drama"</item>
+ <item msgid="7356447541595315913">"Obrazovanje"</item>
+ <item msgid="7511135485827589547">"Životinjski svijet"</item>
+ <item msgid="6961248112238009967">"Vijesti"</item>
+ <item msgid="6484685553679698447">"Igre"</item>
+ <item msgid="2737158328243183190">"Umjetnost"</item>
+ <item msgid="6577176952650166615">"Zabava"</item>
+ <item msgid="7886693831871777617">"Životni stil"</item>
+ <item msgid="8145832312485577062">"Glazba"</item>
+ <item msgid="1345789204804308580">"Premijere"</item>
+ <item msgid="2736680312770771994">"Tehnologija/znanost"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"TV kanali uživo"</item>
diff --git a/res/values-hr/rating_system_strings.xml b/res/values-hr/rating_system_strings.xml
index 40dbe04b..3e7377a7 100644
--- a/res/values-hr/rating_system_strings.xml
+++ b/res/values-hr/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Programi mogu sadržavati materijale neprikladne za mlađe od 15 godina, pa se roditeljima preporučuje oprez."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Programi mogu sadržavati materijale neprikladne za mlađe od 19 godina, pa se ne preporučuju za tu dobnu skupinu."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Sugestivan dijalog"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Vulgarni izrazi"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Seksualni sadržaj"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 196c79f6..52ba8831 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -146,12 +146,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Licence otvorenog izvornog koda"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Licence otvorenog izvornog koda"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Verzija"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Opcije za razvojne programere"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Omogući USB TV prijemnik"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Da biste mogli čuti zvuk USB TV prijemnika, vaš televizor mora podržavati AC3 prolaz."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Mogućnost AC3 audioprolaza"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Vaš televizor podržava AC3 prolaz."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Vaš televizor ne podržava AC3 prolaz."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Pomogni poboljšati Kanale uživo"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Dijelite anonimne podatke o upotrebi i dijagnostici s Googleom kako bismo poboljšali TV kanale uživo i spriječili poteškoće kao što su rušenja i zamrzavanja."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Da biste gledali ovaj kanal, pritisnite desno pa unesite PIN"</string>
@@ -163,9 +157,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Program je blokiran"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Program ima ocjenu <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Samo zvuk"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Slab signal"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Nema internetske veze"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Bez naslova"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Kanal blokiran"</string>
- <string name="episode_format" msgid="4881195874563241096">"S. <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Novi"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Izvori"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -193,7 +188,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Traženje kanala nije uspjelo"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Nije pronađena nijedna aplikacija koja može provesti tu radnju."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Skriveni su kanali svih izvora.\nOdaberite barem jedan kanal za gledanje."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Nije dostupno zbog slabog videosignala"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Videozapis neočekivano nije dostupan"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Tipka NATRAG namijenjena je za povezani uređaj. Pritisnite tipku POČETNA za izlaz."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Kanali uživo nisu podržani na ovom uređaju s Androidom Lollipop."</string>
diff --git a/res/values-hu/arrays.xml b/res/values-hu/arrays.xml
index 3eba7e77..eb73d3ba 100644
--- a/res/values-hu/arrays.xml
+++ b/res/values-hu/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Teljes"</item>
<item msgid="8568284598210500589">"Nagyítás/kicsinyítés"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Összes csatorna"</item>
- <item msgid="928298872841713530">"Család/gyermekek"</item>
- <item msgid="2751606947569857164">"Sport"</item>
- <item msgid="7345749789651321496">"Vásárlás"</item>
- <item msgid="167201149441442173">"Filmek"</item>
- <item msgid="525966731464264290">"Vígjáték"</item>
- <item msgid="6096710741527327836">"Utazás"</item>
- <item msgid="2851882187117833883">"Dráma"</item>
- <item msgid="78492781188719038">"Oktatás"</item>
- <item msgid="7221999662426308394">"Állatok/vadvilág"</item>
- <item msgid="375300513250925001">"Hírműsor"</item>
- <item msgid="7746320336582330410">"Játék"</item>
- <item msgid="1255741860568329178">"Művészetek"</item>
- <item msgid="7603949681065702867">"Szórakozás"</item>
- <item msgid="4453821994746804366">"Életstílus"</item>
- <item msgid="3488534597567932843">"Zene"</item>
- <item msgid="7452153120614274095">"Premier"</item>
- <item msgid="8215762047341133299">"Technika/tudomány"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Minden csatorna"</item>
+ <item msgid="6897460857821394118">"Családi/gyermek"</item>
+ <item msgid="551257741825778215">"Sport"</item>
+ <item msgid="452133796804325879">"Vásárlás"</item>
+ <item msgid="3296058637230163031">"Filmek"</item>
+ <item msgid="1054540282883891201">"Vígjátékok"</item>
+ <item msgid="7900158429062595471">"Utazás"</item>
+ <item msgid="3768998587825611787">"Dráma"</item>
+ <item msgid="8340620094959282881">"Oktatás"</item>
+ <item msgid="7396447839483867269">"Állatok/vadvilág"</item>
+ <item msgid="4738043455148062673">"Hírek"</item>
+ <item msgid="7405041316051047427">"Játékok"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Minden csatorna"</item>
+ <item msgid="7909003973960375395">"Családi/gyermek"</item>
+ <item msgid="3185279732911635789">"Sport"</item>
+ <item msgid="4704858492065325964">"Vásárlás"</item>
+ <item msgid="6083795019290250078">"Filmek"</item>
+ <item msgid="8302638329222449550">"Vígjátékok"</item>
+ <item msgid="3803709976021475052">"Utazás"</item>
+ <item msgid="8116747365234169059">"Dráma"</item>
+ <item msgid="7356447541595315913">"Oktatás"</item>
+ <item msgid="7511135485827589547">"Állatok/vadvilág"</item>
+ <item msgid="6961248112238009967">"Hírek"</item>
+ <item msgid="6484685553679698447">"Játékok"</item>
+ <item msgid="2737158328243183190">"Művészetek"</item>
+ <item msgid="6577176952650166615">"Szórakozás"</item>
+ <item msgid="7886693831871777617">"Életstílus"</item>
+ <item msgid="8145832312485577062">"Zene"</item>
+ <item msgid="1345789204804308580">"Premier"</item>
+ <item msgid="2736680312770771994">"Technika/tudomány"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Élő csatornák"</item>
diff --git a/res/values-hu/rating_system_strings.xml b/res/values-hu/rating_system_strings.xml
index aca42d1a..9282b334 100644
--- a/res/values-hu/rating_system_strings.xml
+++ b/res/values-hu/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"A műsorok 15 éven aluliak számára nem megfelelő tartalommal bírhatnak, ezért esetükben szülői mérlegelés szükséges."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"A műsorok 19 éven aluliak számára nem megfelelő tartalommal bírhatnak, ezért nem alkalmasak megtekintésre a 19 évnél fiatalabbak számára."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Burkoltan szexuális jellegű párbeszéd"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Durva nyelvezet"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Szexuális tartalom"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index da1e9520..937d1c42 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Nyílt forráskódú licencek"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Nyílt forráskódú licencek"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Verzió"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Fejlesztői beállítások"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"USB-s tévétuner engedélyezése"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Ahhoz, hogy hallja az USB-s tévétuner hangját, a tévének támogatnia kell az AC3-átvitelt."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3-audiokompatibilitás"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"A tévé támogatja az AC3-átvitelt."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"A tévé nem támogatja az AC3-átvitelt."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Segítek az élő csatornák javításában"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Névtelen használati és diagnosztikai adatok megosztása a Google-lal, hogy továbbfejleszthessük az Élő csatornák szolgáltatást, és megelőzhessük a rendszer összeomlását és lefagyását."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"A csatorna megtekintéséhez nyomja meg a jobbra gombot, majd adja meg a PIN kódot"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"A műsor le van tiltva"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"A műsor besorolása: <xliff:g id="RATING">%1$s</xliff:g>."</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Csak hang"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Gyenge jel"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Nincs internetkapcsolat"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Nincs cím"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Csatorna letiltva"</string>
- <string name="episode_format" msgid="4881195874563241096">"<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>. évad, <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g>. rész: <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Új"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Források"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Tunerhiba"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Nincs megfelelő alkalmazás a művelet végrehajtásához."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Minden forráscsatorna rejtve van.\nVálasszon ki legalább egy csatornát, amelyet meg szeretne tekinteni."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"A gyenge videojel miatt nem érhető el."</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"A videó váratlanul nem érhető el."</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"A BACK gomb a csatlakoztatott eszközzel használható. A kilépéshez nyomja meg a HOME gombot."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Ezen az Android Lollipopot futtató eszközön nem támogatott az Élő csatornák alkalmazás."</string>
diff --git a/res/values-hy-rAM/arrays.xml b/res/values-hy-rAM/arrays.xml
index 5c6f0bfd..766c779e 100644
--- a/res/values-hy-rAM/arrays.xml
+++ b/res/values-hy-rAM/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Լիցքավորված"</item>
<item msgid="8568284598210500589">"Խոշորացնել"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Բոլոր ալիքները"</item>
- <item msgid="928298872841713530">"Ընտանիք/Երեխաներ"</item>
- <item msgid="2751606947569857164">"Սպորտ"</item>
- <item msgid="7345749789651321496">"Գնումներ"</item>
- <item msgid="167201149441442173">"Ֆիլմեր"</item>
- <item msgid="525966731464264290">"Կատակերգություն"</item>
- <item msgid="6096710741527327836">"Ճամփորդություն"</item>
- <item msgid="2851882187117833883">"Դրամա"</item>
- <item msgid="78492781188719038">"Կրթություն"</item>
- <item msgid="7221999662426308394">"Կենդ./Վայրի բնություն"</item>
- <item msgid="375300513250925001">"Նորություններ"</item>
- <item msgid="7746320336582330410">"Համակարգչային խաղեր"</item>
- <item msgid="1255741860568329178">"Արվեստ"</item>
- <item msgid="7603949681065702867">"Ժամանց"</item>
- <item msgid="4453821994746804366">"Ապրելակերպ"</item>
- <item msgid="3488534597567932843">"Երաժշտություն"</item>
- <item msgid="7452153120614274095">"Նորույթներ"</item>
- <item msgid="8215762047341133299">"Տեխնիկա/Գիտություն"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Բոլոր ալիքները"</item>
+ <item msgid="6897460857821394118">"Ընտանիք/Երեխաներ"</item>
+ <item msgid="551257741825778215">"Սպորտ"</item>
+ <item msgid="452133796804325879">"Գնումներ"</item>
+ <item msgid="3296058637230163031">"Ֆիլմեր"</item>
+ <item msgid="1054540282883891201">"Կատակերգություն"</item>
+ <item msgid="7900158429062595471">"Ճամփորդություն"</item>
+ <item msgid="3768998587825611787">"Դրամա"</item>
+ <item msgid="8340620094959282881">"Կրթություն"</item>
+ <item msgid="7396447839483867269">"Վայրի բնություն"</item>
+ <item msgid="4738043455148062673">"Նորություններ"</item>
+ <item msgid="7405041316051047427">"Համակարգչային խաղեր"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Բոլոր ալիքները"</item>
+ <item msgid="7909003973960375395">"Ընտանիք/Երեխաներ"</item>
+ <item msgid="3185279732911635789">"Սպորտ"</item>
+ <item msgid="4704858492065325964">"Գնումներ"</item>
+ <item msgid="6083795019290250078">"Ֆիլմեր"</item>
+ <item msgid="8302638329222449550">"Կատակերգություն"</item>
+ <item msgid="3803709976021475052">"Ճամփորդություն"</item>
+ <item msgid="8116747365234169059">"Դրամա"</item>
+ <item msgid="7356447541595315913">"Կրթություն"</item>
+ <item msgid="7511135485827589547">"Վայրի բնություն"</item>
+ <item msgid="6961248112238009967">"Նորություններ"</item>
+ <item msgid="6484685553679698447">"Համակարգչային խաղեր"</item>
+ <item msgid="2737158328243183190">"Արվեստ"</item>
+ <item msgid="6577176952650166615">"Ժամանց"</item>
+ <item msgid="7886693831871777617">"Ապրելակերպ"</item>
+ <item msgid="8145832312485577062">"Երաժշտություն"</item>
+ <item msgid="1345789204804308580">"Պրեմիերա"</item>
+ <item msgid="2736680312770771994">"Տեխնիկա/Գիտություն"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Ուղիղ եթեր"</item>
diff --git a/res/values-hy-rAM/rating_system_strings.xml b/res/values-hy-rAM/rating_system_strings.xml
index e1af0c05..7305b01f 100644
--- a/res/values-hy-rAM/rating_system_strings.xml
+++ b/res/values-hy-rAM/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Ծրագրերը կարող են պարունակել 15-ից փոքր լսարանի համար չնախատեսված նյութեր, այդ իսկ պատճառով դրանց դիտումը թույլատրվում է միայն ծնողի հայեցողությամբ:"</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Ծրագրերը կարող են պարունակել 19-ից փոքր լսարանի համար չնախատեսված նյութեր, այդ իսկ պատճառով դրանք պիտանի չեն 19 տարեկանը չլրացած երիտասարդների համար:"</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Անպարկեշտ խոսքեր"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Կոպիտ բառեր"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Սեռական բովանդակություն"</string>
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml
index 19d8a317..412902ee 100644
--- a/res/values-hy-rAM/strings.xml
+++ b/res/values-hy-rAM/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Բաց կոդով ծրագրակազմի արտոնագրեր"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Բաց կոդով ծրագրաշարի լիցենզիաներ"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Տարբերակ"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Մշակողի ընտրանքներ"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Միացնել USB հեռուստակարգավորիչը"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"USB հեռուստակարգավորիչի ձայնը լսելու համար անհրաժեշտ է, որ ձեր հեռուստացույցն աջակցի AC3 տարանցումը:"</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 ձայնի աջակցում"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Ձեր հեռուստացույցն աջակցում է AC3 տարանցումը:"</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Ձեր հեռուստացույցը չի աջակցում AC3 տարանցումը:"</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Օգնել բարելավել «Ուղիղ եթերը»"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Google-ին ուղարկեք օգտագործման և ախտորոշման անանուն տվյալներ, որոնք մենք կօգնեն կատարելագործել Live TV-ը և կանխել դրա հետ կապված խնդիրները, օրինակ՝ աշխատանքի խափանումը և սառեցումը:"</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Այս կապուղին դիտելու համար սեղմեք Աջ և մուտքագրեք ձեր PIN-ը"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Ծրագիրն արգելափակված է"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Այս ծրագրի վարկանիշը <xliff:g id="RATING">%1$s</xliff:g> է"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Միայն ձայն"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Թույլ ազդանշան"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Համացանցի կապակցում չկա"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Անվերնագիր"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Ալիքն արգելափակված է"</string>
- <string name="episode_format" msgid="4881195874563241096">"Ս<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Դրվ.՝ <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Նոր"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Աղբյուրներ"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Կարգավորումը չհաջողվեց"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Այս գործողությունը կատարելու համար ոչ մի հավելված չի գտնվել"</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Բոլոր ալիքները թաքնված են:\nԸնտրեք առնվազն մեկ ալիք:"</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Անհասանելի է թույլ տեսաազդանշանի պատճառով"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Անսպասելի սխալ առաջացավ տեսանյութի ցուցադրման ժամանակ"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"«Հետ» կոճակը միացված սարքի համար է: Դուս գալու համար սեղմեք «Գլխավոր» կոճակը:"</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Ուղիղ եթերը չի աջակցվում Android Lollipop համակարգով սարքի վրա:"</string>
diff --git a/res/values-in/arrays.xml b/res/values-in/arrays.xml
index 1248c942..e6d34476 100644
--- a/res/values-in/arrays.xml
+++ b/res/values-in/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Penuh"</item>
<item msgid="8568284598210500589">"Perbesar/Perkecil"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Semua saluran"</item>
- <item msgid="928298872841713530">"Keluarga/Anak-anak"</item>
- <item msgid="2751606947569857164">"Olahraga"</item>
- <item msgid="7345749789651321496">"Belanja"</item>
- <item msgid="167201149441442173">"Film"</item>
- <item msgid="525966731464264290">"Komedi"</item>
- <item msgid="6096710741527327836">"Perjalanan"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Pendidikan"</item>
- <item msgid="7221999662426308394">"Satwa/Alam Liar"</item>
- <item msgid="375300513250925001">"Berita"</item>
- <item msgid="7746320336582330410">"Game"</item>
- <item msgid="1255741860568329178">"Seni"</item>
- <item msgid="7603949681065702867">"Hiburan"</item>
- <item msgid="4453821994746804366">"Gaya Hidup"</item>
- <item msgid="3488534597567932843">"Musik"</item>
- <item msgid="7452153120614274095">"Tayang Perdana"</item>
- <item msgid="8215762047341133299">"Teknologi/Sains"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Semua saluran"</item>
+ <item msgid="6897460857821394118">"Keluarga/Anak-anak"</item>
+ <item msgid="551257741825778215">"Olahraga"</item>
+ <item msgid="452133796804325879">"Belanja"</item>
+ <item msgid="3296058637230163031">"Film"</item>
+ <item msgid="1054540282883891201">"Komedi"</item>
+ <item msgid="7900158429062595471">"Wisata"</item>
+ <item msgid="3768998587825611787">"Drama"</item>
+ <item msgid="8340620094959282881">"Pendidikan"</item>
+ <item msgid="7396447839483867269">"Satwa/Alam Liar"</item>
+ <item msgid="4738043455148062673">"Berita"</item>
+ <item msgid="7405041316051047427">"Game"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Semua saluran"</item>
+ <item msgid="7909003973960375395">"Keluarga/Anak-anak"</item>
+ <item msgid="3185279732911635789">"Olahraga"</item>
+ <item msgid="4704858492065325964">"Belanja"</item>
+ <item msgid="6083795019290250078">"Film"</item>
+ <item msgid="8302638329222449550">"Komedi"</item>
+ <item msgid="3803709976021475052">"Wisata"</item>
+ <item msgid="8116747365234169059">"Drama"</item>
+ <item msgid="7356447541595315913">"Pendidikan"</item>
+ <item msgid="7511135485827589547">"Satwa/Alam Liar"</item>
+ <item msgid="6961248112238009967">"Berita"</item>
+ <item msgid="6484685553679698447">"Game"</item>
+ <item msgid="2737158328243183190">"Seni"</item>
+ <item msgid="6577176952650166615">"Hiburan"</item>
+ <item msgid="7886693831871777617">"Gaya Hidup"</item>
+ <item msgid="8145832312485577062">"Musik"</item>
+ <item msgid="1345789204804308580">"Tayang Perdana"</item>
+ <item msgid="2736680312770771994">"Teknologi/Sains"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Saluran TV Langsung"</item>
diff --git a/res/values-in/rating_system_strings.xml b/res/values-in/rating_system_strings.xml
index 5606ef0b..b4023172 100644
--- a/res/values-in/rating_system_strings.xml
+++ b/res/values-in/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Program mungkin berisi materi yang tidak sesuai untuk pemirsa di bawah 15 tahun. Kebijaksanaan orang tua sangat dianjurkan."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Program mungkin berisi materi yang tidak sesuai untuk pemirsa di bawah 19 tahun sehingga tidak cocok untuk remaja di bawah 19 tahun."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Dialog yang merangsang"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Bahasa yang vulgar"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Konten seksual"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 82fbd5c9..6be2f7d6 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Lisensi sumber terbuka"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Lisensi sumber terbuka"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Versi"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Opsi pengembang"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Aktifkan penala TV USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Untuk mendengar suara penala TV USB, TV Anda harus mendukung passthrough AC3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Kemampuan audio AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"TV Anda mendukung passthrough AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"TV Anda tidak mendukung passthrough AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Bantu meningkatkan Saluran Siaran Langsung"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Bagikan data penggunaan dan diagnostik anonim kepada Google agar kami dapat membuat Saluran TV Langsung jadi lebih baik, serta mencegah masalah seperti aplikasi mogok dan berhenti tiba-tiba."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Untuk menonton saluran ini, tekan Kanan dan masukkan PIN"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Program diblokir"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Program ini diberi rating <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Audio saja"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Sinyal lemah"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Tidak ada sambungan internet"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Tanpa judul"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Saluran diblokir"</string>
- <string name="episode_format" msgid="4881195874563241096">"S<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Baru"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Sumber"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Penalaan gagal"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Tidak ditemukan aplikasi untuk menangani tindakan ini."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Semua saluran asal disembunyikan.\nPilih setidaknya satu saluran untuk ditonton."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Tidak tersedia karena sinyal video lemah"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Video tiba-tiba tidak tersedia"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Tombol KEMBALI adalah untuk perangkat yang tersambung. Tekan tombol UTAMA untuk keluar."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Saluran Langsung tidak didukung pada perangkat dengan Android Lollipop ini."</string>
diff --git a/res/values-is-rIS/arrays.xml b/res/values-is-rIS/arrays.xml
index 27cb8655..08200b56 100644
--- a/res/values-is-rIS/arrays.xml
+++ b/res/values-is-rIS/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Allur skjárinn"</item>
<item msgid="8568284598210500589">"Aðdráttur"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Allar rásir"</item>
- <item msgid="928298872841713530">"Fjölskylduefni/barnaefni"</item>
- <item msgid="2751606947569857164">"Íþróttir"</item>
- <item msgid="7345749789651321496">"Verslun"</item>
- <item msgid="167201149441442173">"Kvikmyndir"</item>
- <item msgid="525966731464264290">"Grín"</item>
- <item msgid="6096710741527327836">"Ferðalög"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Fræðsla"</item>
- <item msgid="7221999662426308394">"Dýr/dýralíf"</item>
- <item msgid="375300513250925001">"Fréttir"</item>
- <item msgid="7746320336582330410">"Leikir"</item>
- <item msgid="1255741860568329178">"List"</item>
- <item msgid="7603949681065702867">"Afþreying"</item>
- <item msgid="4453821994746804366">"Lífsstíll"</item>
- <item msgid="3488534597567932843">"Tónlist"</item>
- <item msgid="7452153120614274095">"Úrvalsefni"</item>
- <item msgid="8215762047341133299">"Tækni og vísindi"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Allar rásir"</item>
+ <item msgid="6897460857821394118">"Fjölskylduefni/barnaefni"</item>
+ <item msgid="551257741825778215">"Íþróttir"</item>
+ <item msgid="452133796804325879">"Verslun"</item>
+ <item msgid="3296058637230163031">"Kvikmyndir"</item>
+ <item msgid="1054540282883891201">"Grín"</item>
+ <item msgid="7900158429062595471">"Ferðalög"</item>
+ <item msgid="3768998587825611787">"Drama"</item>
+ <item msgid="8340620094959282881">"Fræðsla"</item>
+ <item msgid="7396447839483867269">"Dýr/dýralíf"</item>
+ <item msgid="4738043455148062673">"Fréttir"</item>
+ <item msgid="7405041316051047427">"Leikir"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Allar rásir"</item>
+ <item msgid="7909003973960375395">"Fjölskylduefni/barnaefni"</item>
+ <item msgid="3185279732911635789">"Íþróttir"</item>
+ <item msgid="4704858492065325964">"Verslun"</item>
+ <item msgid="6083795019290250078">"Kvikmyndir"</item>
+ <item msgid="8302638329222449550">"Grín"</item>
+ <item msgid="3803709976021475052">"Ferðalög"</item>
+ <item msgid="8116747365234169059">"Drama"</item>
+ <item msgid="7356447541595315913">"Fræðsla"</item>
+ <item msgid="7511135485827589547">"Dýr/dýralíf"</item>
+ <item msgid="6961248112238009967">"Fréttir"</item>
+ <item msgid="6484685553679698447">"Leikir"</item>
+ <item msgid="2737158328243183190">"List"</item>
+ <item msgid="6577176952650166615">"Afþreying"</item>
+ <item msgid="7886693831871777617">"Lífsstíll"</item>
+ <item msgid="8145832312485577062">"Tónlist"</item>
+ <item msgid="1345789204804308580">"Úrvalsefni"</item>
+ <item msgid="2736680312770771994">"Tækni og vísindi"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Beinar útsendingar"</item>
diff --git a/res/values-is-rIS/rating_system_strings.xml b/res/values-is-rIS/rating_system_strings.xml
index 24900216..7e3bfbb3 100644
--- a/res/values-is-rIS/rating_system_strings.xml
+++ b/res/values-is-rIS/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Efnið getur innihaldið atriði sem eru ekki við hæfi barna undir 15 ára aldri og foreldri skal því horfa með barninu."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Efnið getur innihaldið atriði sem eru ekki við hæfi einstaklinga undir 19 ára aldri."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Tvíræðar samræður"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Óheflað málfar"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Kynferðislegt efni"</string>
diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml
index e18a3139..1bfefeac 100644
--- a/res/values-is-rIS/strings.xml
+++ b/res/values-is-rIS/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Leyfi opins kóða"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Leyfi opins kóða"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Útgáfa"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Forritunarkostir"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Virkja USB-sjónvarpsmóttakara"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Til að það heyrist í USB-sjónvarpsmóttakaranum þarf sjónvarpið þitt að styðja AC3-tengingu."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3-hljóðeiginleiki"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Sjónvarpið þitt styður AC3-tengingu."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Sjónvarpið þitt styður ekki AC3-tengingu."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Hjálpa til við að bæta rásir í beinni"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Deila nafnlausum gögnum um notkun og greiningu með Google til að við getum gert beinar útsendingar betri og komið í veg fyrir vandamál eins og hrun og að mynd frjósi."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Til að horfa á þessa rás skaltu ýta til hægri og slá inn PIN-númerið þitt"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Þátturinn er læstur"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Þessi dagskrárliður er flokkaður sem <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Aðeins hljóð"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Lítill sendistyrkur"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Engin nettenging"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Ekkert heiti"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Rás læst"</string>
- <string name="episode_format" msgid="4881195874563241096">"S<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Þ. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Ný"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Veitur"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Stilling mistókst"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Ekkert forrit fannst sem getur framkvæmt þessa aðgerð."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Allar inntaksrásir eru faldar.\nVeldu minnst eina rás til að horfa á."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Ekki tiltækt vegna þess að sendistyrkur myndefnis er lítill"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Myndskeiðið er óvænt ekki tiltækt"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Til baka-lykillinn er fyrir tengd tæki. Ýttu á heimahnappinn til að hætta."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Rásir í beinni eru ekki studdar í þessu tæki með Android Lollipop."</string>
diff --git a/res/values-it/arrays.xml b/res/values-it/arrays.xml
index 8dd12954..e445f6ab 100644
--- a/res/values-it/arrays.xml
+++ b/res/values-it/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Intera"</item>
<item msgid="8568284598210500589">"Zoom"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Tutti i canali"</item>
- <item msgid="928298872841713530">"Famiglia/bambini"</item>
- <item msgid="2751606947569857164">"Sport"</item>
- <item msgid="7345749789651321496">"Shopping"</item>
- <item msgid="167201149441442173">"Film"</item>
- <item msgid="525966731464264290">"Commedie"</item>
- <item msgid="6096710741527327836">"Viaggi"</item>
- <item msgid="2851882187117833883">"Drammatici"</item>
- <item msgid="78492781188719038">"Istruzione"</item>
- <item msgid="7221999662426308394">"Animali/fauna selvatica"</item>
- <item msgid="375300513250925001">"Notizie"</item>
- <item msgid="7746320336582330410">"Giochi"</item>
- <item msgid="1255741860568329178">"Arte"</item>
- <item msgid="7603949681065702867">"Svago"</item>
- <item msgid="4453821994746804366">"Stile di vita"</item>
- <item msgid="3488534597567932843">"Musica"</item>
- <item msgid="7452153120614274095">"Il meglio"</item>
- <item msgid="8215762047341133299">"Scienza/tecnica"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Tutti i canali"</item>
+ <item msgid="6897460857821394118">"Famiglia/bambini"</item>
+ <item msgid="551257741825778215">"Sport"</item>
+ <item msgid="452133796804325879">"Acquisti"</item>
+ <item msgid="3296058637230163031">"Cinema"</item>
+ <item msgid="1054540282883891201">"Commedie"</item>
+ <item msgid="7900158429062595471">"Viaggi"</item>
+ <item msgid="3768998587825611787">"Drammatici"</item>
+ <item msgid="8340620094959282881">"Istruzione"</item>
+ <item msgid="7396447839483867269">"Animali/fauna selvatica"</item>
+ <item msgid="4738043455148062673">"Notizie"</item>
+ <item msgid="7405041316051047427">"Giochi"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Tutti i canali"</item>
+ <item msgid="7909003973960375395">"Famiglia/bambini"</item>
+ <item msgid="3185279732911635789">"Sport"</item>
+ <item msgid="4704858492065325964">"Acquisti"</item>
+ <item msgid="6083795019290250078">"Cinema"</item>
+ <item msgid="8302638329222449550">"Commedie"</item>
+ <item msgid="3803709976021475052">"Viaggi"</item>
+ <item msgid="8116747365234169059">"Drammatici"</item>
+ <item msgid="7356447541595315913">"Istruzione"</item>
+ <item msgid="7511135485827589547">"Animali/fauna selvatica"</item>
+ <item msgid="6961248112238009967">"Notizie"</item>
+ <item msgid="6484685553679698447">"Giochi"</item>
+ <item msgid="2737158328243183190">"Arte"</item>
+ <item msgid="6577176952650166615">"Divertimento"</item>
+ <item msgid="7886693831871777617">"Stile di vita"</item>
+ <item msgid="8145832312485577062">"Musica"</item>
+ <item msgid="1345789204804308580">"Il meglio"</item>
+ <item msgid="2736680312770771994">"Scienza/tecnica"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Dirette TV"</item>
diff --git a/res/values-it/rating_system_strings.xml b/res/values-it/rating_system_strings.xml
index f38dfbe0..1bc1f6b2 100644
--- a/res/values-it/rating_system_strings.xml
+++ b/res/values-it/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"I programmi potrebbero contenere materiale non adatto ai minori di 15 anni ed è consigliata quindi la supervisione dei genitori."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"I programmi potrebbero contenere materiale non adatto ai minori di 19 anni e pertanto la visione è sconsigliata a questo pubblico."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Dialoghi allusivi"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Linguaggio volgare"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Contenuti di natura sessuale"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 3310209d..56717ba0 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -146,12 +146,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Licenze open source"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Licenze open source"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Versione"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Opzioni sviluppatore"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Attiva sintonizzatore TV USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Per ascoltare l\'audio del sintonizzatore TV USB, la TV deve supportare il passthrough AC3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Funzionalità audio AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"La TV supporta il passthrough AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"La TV non supporta il passthrough AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Contribuisci a migliorare Live TV"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Condividi con Google dati anonimi sull\'utilizzo e sulla diagnostica per consentirci di migliorare l\'app Dirette TV ed di evitare problemi, quali arresti anomali e blocchi."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Per guardare questo canale, premi il pulsante destro e inserisci il PIN"</string>
@@ -163,9 +157,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Il programma è bloccato"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Questo programma è classificato come <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Solo audio"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Segnale debole"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Nessuna connessione Internet"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Senza titolo"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Canale bloccato"</string>
- <string name="episode_format" msgid="4881195874563241096">"Stag. <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Punt. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Nuove"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Fonti"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -192,7 +187,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Sintonizzazione non riuscita"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Nessuna app trovata per gestire questa azione."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Tutti i canali di origine sono nascosti.\nSeleziona almeno un canale da guardare."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Non disponibile a causa di segnale video debole"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Il video è improvvisamente non disponibile"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Il tasto INDIETRO è per il dispositivo connesso. Per uscire premi il pulsante HOME."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Live TV non è supportato su questo dispositivo con Android Lollipop."</string>
diff --git a/res/values-iw/arrays.xml b/res/values-iw/arrays.xml
index 17a73776..7748ed68 100644
--- a/res/values-iw/arrays.xml
+++ b/res/values-iw/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"מלאה"</item>
<item msgid="8568284598210500589">"שנה מרחק מתצוגה"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"כל הערוצים"</item>
- <item msgid="928298872841713530">"משפחה/ילדים"</item>
- <item msgid="2751606947569857164">"ספורט"</item>
- <item msgid="7345749789651321496">"קניות"</item>
- <item msgid="167201149441442173">"סרטים"</item>
- <item msgid="525966731464264290">"קומדיה"</item>
- <item msgid="6096710741527327836">"טיולים"</item>
- <item msgid="2851882187117833883">"דרמה"</item>
- <item msgid="78492781188719038">"חינוך"</item>
- <item msgid="7221999662426308394">"בעלי חיים/טבע"</item>
- <item msgid="375300513250925001">"חדשות"</item>
- <item msgid="7746320336582330410">"משחקים"</item>
- <item msgid="1255741860568329178">"אמנויות"</item>
- <item msgid="7603949681065702867">"בידור"</item>
- <item msgid="4453821994746804366">"החיים הטובים"</item>
- <item msgid="3488534597567932843">"מוזיקה"</item>
- <item msgid="7452153120614274095">"עילית"</item>
- <item msgid="8215762047341133299">"טכנולוגיה/מדע"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"כל הערוצים"</item>
+ <item msgid="6897460857821394118">"משפחה/ילדים"</item>
+ <item msgid="551257741825778215">"ספורט"</item>
+ <item msgid="452133796804325879">"קניות"</item>
+ <item msgid="3296058637230163031">"סרטים"</item>
+ <item msgid="1054540282883891201">"קומדיה"</item>
+ <item msgid="7900158429062595471">"טיולים"</item>
+ <item msgid="3768998587825611787">"דרמה"</item>
+ <item msgid="8340620094959282881">"חינוך"</item>
+ <item msgid="7396447839483867269">"בעלי חיים/חיות בר"</item>
+ <item msgid="4738043455148062673">"חדשות"</item>
+ <item msgid="7405041316051047427">"משחקים"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"כל הערוצים"</item>
+ <item msgid="7909003973960375395">"משפחה/ילדים"</item>
+ <item msgid="3185279732911635789">"ספורט"</item>
+ <item msgid="4704858492065325964">"קניות"</item>
+ <item msgid="6083795019290250078">"סרטים"</item>
+ <item msgid="8302638329222449550">"קומדיה"</item>
+ <item msgid="3803709976021475052">"טיולים"</item>
+ <item msgid="8116747365234169059">"דרמה"</item>
+ <item msgid="7356447541595315913">"חינוך"</item>
+ <item msgid="7511135485827589547">"בעלי חיים/חיות בר"</item>
+ <item msgid="6961248112238009967">"חדשות"</item>
+ <item msgid="6484685553679698447">"משחקים"</item>
+ <item msgid="2737158328243183190">"אמנות"</item>
+ <item msgid="6577176952650166615">"בידור"</item>
+ <item msgid="7886693831871777617">"סגנון חיים"</item>
+ <item msgid="8145832312485577062">"מוזיקה"</item>
+ <item msgid="1345789204804308580">"עילית"</item>
+ <item msgid="2736680312770771994">"טכנולוגיה/מדע"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"ערוצי שידורים חיים"</item>
diff --git a/res/values-iw/rating_system_strings.xml b/res/values-iw/rating_system_strings.xml
index caa83f4f..e682d2f6 100644
--- a/res/values-iw/rating_system_strings.xml
+++ b/res/values-iw/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"התוכניות עשויות להכיל תוכן בלתי הולם לקהלים מתחת לגיל 15, ולפיכך ההורים נדרשים להפעיל לגביהן שיקול דעת."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"התוכניות עשויות להכיל תוכן בלתי הולם לקהלים מתחת לגיל 19, ולפיכך אינן מתאימות לבני נוער מתחת לגיל 19."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"שיח בעל רמיזות מיניות"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"שפה גסה"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"תוכן מיני"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 7ef93abc..354c837c 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -148,12 +148,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"רישיונות קוד פתוח"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"רישיונות קוד פתוח"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"גרסה"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"אפשרויות למפתחים"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"‏הפעל את טיונר ה-USB שבטלוויזיה"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"‏כדי לשמוע צליל מטיונר ה-USB בטלוויזיה, יש צורך בתמיכה בחיבור AC3 בטלוויזיה."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"‏יכולת אודיו מסוג AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"‏הטלוויזיה שלך תומכת בחיבור AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"‏הטלוויזיה שלך לא תומכת בחיבור AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"עוזרים לשפר את הערוצים בשידור חי"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"‏שתף נתוני שימוש ואבחון אנונימיים עם Google כדי שנוכל לשפר את ערוצי השידורים החיים ולמנוע בעיות כמו קריסות וקפיאת תמונה."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"‏כדי לצפות בערוץ הזה, לחץ על \'ימין\' והזן את מספר ה-PIN"</string>
@@ -165,9 +159,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"התכנית חסומה"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"סיווג התכנית הזו הוא <xliff:g id="RATING">%1$s</xliff:g>."</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"אודיו בלבד"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"אות חלש"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"אין חיבור לאינטרנט"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"ללא כותרת"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"הערוץ חסום"</string>
- <string name="episode_format" msgid="4881195874563241096">"עונה <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: פרק <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"חדש"</string>
<string name="setup_category_done" msgid="4750902502852212319">"מקורות"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -196,7 +191,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"הכוונון נכשל"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"לא נמצאה אפליקציה שיכולה לטפל בפעולה הזו."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"כל ערוצי המקור מוסתרים.\nבחר ערוץ אחד לפחות לצפייה."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"לא זמין עקב אות וידאו חלש"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"מסיבה בלתי צפויה, הסרטון אינו זמין"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"מקש \'הקודם\' מיועד למכשירים מחוברים. לחץ על לחצן \'דף הבית\' כדי לצאת."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"‏Live TV אינו נתמך במכשיר זה עם Android Lollipop."</string>
diff --git a/res/values-ja/arrays.xml b/res/values-ja/arrays.xml
index 12d3b91a..52ac8f55 100644
--- a/res/values-ja/arrays.xml
+++ b/res/values-ja/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"フル"</item>
<item msgid="8568284598210500589">"ズーム"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"すべてのチャンネル"</item>
- <item msgid="928298872841713530">"ファミリー/子ども"</item>
- <item msgid="2751606947569857164">"スポーツ"</item>
- <item msgid="7345749789651321496">"ショッピング"</item>
- <item msgid="167201149441442173">"映画"</item>
- <item msgid="525966731464264290">"コメディー"</item>
- <item msgid="6096710741527327836">"旅行"</item>
- <item msgid="2851882187117833883">"ドラマ"</item>
- <item msgid="78492781188719038">"教育"</item>
- <item msgid="7221999662426308394">"動物/野生生物"</item>
- <item msgid="375300513250925001">"ニュース"</item>
- <item msgid="7746320336582330410">"ゲーム"</item>
- <item msgid="1255741860568329178">"芸術"</item>
- <item msgid="7603949681065702867">"エンターテインメント"</item>
- <item msgid="4453821994746804366">"ライフスタイル"</item>
- <item msgid="3488534597567932843">"音楽"</item>
- <item msgid="7452153120614274095">"プレミア"</item>
- <item msgid="8215762047341133299">"科学、技術"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"すべてのチャンネル"</item>
+ <item msgid="6897460857821394118">"ファミリー / 子ども"</item>
+ <item msgid="551257741825778215">"スポーツ"</item>
+ <item msgid="452133796804325879">"ショッピング"</item>
+ <item msgid="3296058637230163031">"映画"</item>
+ <item msgid="1054540282883891201">"コメディー"</item>
+ <item msgid="7900158429062595471">"旅行"</item>
+ <item msgid="3768998587825611787">"ドラマ"</item>
+ <item msgid="8340620094959282881">"教育"</item>
+ <item msgid="7396447839483867269">"動物 / 野生生物"</item>
+ <item msgid="4738043455148062673">"ニュース"</item>
+ <item msgid="7405041316051047427">"ゲーム"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"すべてのチャンネル"</item>
+ <item msgid="7909003973960375395">"ファミリー / 子ども"</item>
+ <item msgid="3185279732911635789">"スポーツ"</item>
+ <item msgid="4704858492065325964">"ショッピング"</item>
+ <item msgid="6083795019290250078">"映画"</item>
+ <item msgid="8302638329222449550">"コメディー"</item>
+ <item msgid="3803709976021475052">"旅行"</item>
+ <item msgid="8116747365234169059">"ドラマ"</item>
+ <item msgid="7356447541595315913">"教育"</item>
+ <item msgid="7511135485827589547">"動物 / 野生生物"</item>
+ <item msgid="6961248112238009967">"ニュース"</item>
+ <item msgid="6484685553679698447">"ゲーム"</item>
+ <item msgid="2737158328243183190">"芸術"</item>
+ <item msgid="6577176952650166615">"エンターテイメント"</item>
+ <item msgid="7886693831871777617">"ライフスタイル"</item>
+ <item msgid="8145832312485577062">"音楽"</item>
+ <item msgid="1345789204804308580">"プレミア"</item>
+ <item msgid="2736680312770771994">"科学、技術"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"ライブ チャンネル"</item>
diff --git a/res/values-ja/rating_system_strings.xml b/res/values-ja/rating_system_strings.xml
index 54446ace..69a38849 100644
--- a/res/values-ja/rating_system_strings.xml
+++ b/res/values-ja/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"番組は 15 歳未満のユーザーには不適切なコンテンツが含まれている可能性があるため、保護者の判断が求められます。"</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"番組は 19 歳未満のユーザーには不適切なコンテンツが含まれている可能性があるため、19 歳未満の青少年には推奨されません。"</string>
<string name="description_us_tv_d" msgid="12333789157204816">"性的なものを暗示する会話"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"下品な言葉遣い"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"性的な内容"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 626948fa..62b0f253 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"オープンソース ライセンス"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"オープンソースライセンス"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"バージョン"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"開発者向けオプション"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"USB テレビ チューナーを有効にする"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"USB テレビ チューナーの音声を再生するには、お使いのテレビが AC3 パススルーに対応している必要があります。"</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 オーディオ機能"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"お使いのテレビは AC3 パススルーに対応しています。"</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"お使いのテレビは AC3 パススルーに対応していません。"</string>
<string name="about_menu_improve" msgid="3712578027009311401">"ライブチャンネルの改善に協力する"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"クラッシュやフリーズの問題を防止し、ライブチャンネルの向上に役立てるため、匿名の診断情報や使用状況データをGoogleと共有してください。"</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"このチャンネルを視聴するには、右のボタンを押してPINを入力してください。"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"プログラムはブロックされています"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"このプログラムのレーティングは<xliff:g id="RATING">%1$s</xliff:g>です"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"音声のみ"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"信号強度が弱くなっています"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"インターネットに接続されていません"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"タイトルなし"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"チャンネルをブロック"</string>
- <string name="episode_format" msgid="4881195874563241096">"シーズン<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: エピソード<xliff:g id="EPISODE_NUMBER">%2$d</xliff:g>「<xliff:g id="EPISODE_TITLE">%3$s</xliff:g>」"</string>
<string name="setup_category_new" msgid="2899355289563443627">"新規"</string>
<string name="setup_category_done" msgid="4750902502852212319">"ソース"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"同調に失敗しました"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"この操作を行うアプリが見つかりませんでした。"</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"ソースチャンネルはすべて非表示になっています。\n視聴するチャンネルを1つ以上選択してください。"</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"動画の信号が弱いため再生できません"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"動画を再生できなくなりました"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"戻るキーは接続されたデバイス用です。終了するにはホームボタンを押してください。"</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"ライブチャンネルは、Android Lollipopを搭載するこの端末では利用できません。"</string>
diff --git a/res/values-ka-rGE/arrays.xml b/res/values-ka-rGE/arrays.xml
index 1d6659a6..4c911685 100644
--- a/res/values-ka-rGE/arrays.xml
+++ b/res/values-ka-rGE/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"სრული"</item>
<item msgid="8568284598210500589">"მასშტაბი"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"ყველა არხი"</item>
- <item msgid="928298872841713530">"ოჯახი/ბავშვები"</item>
- <item msgid="2751606947569857164">"სპორტი"</item>
- <item msgid="7345749789651321496">"საყიდლები"</item>
- <item msgid="167201149441442173">"ფილმები"</item>
- <item msgid="525966731464264290">"კომედია"</item>
- <item msgid="6096710741527327836">"მოგზაურობა"</item>
- <item msgid="2851882187117833883">"დრამატული"</item>
- <item msgid="78492781188719038">"განათლება"</item>
- <item msgid="7221999662426308394">"ცხოველები/ველური ბუნება"</item>
- <item msgid="375300513250925001">"ახალი ამბები"</item>
- <item msgid="7746320336582330410">"თამაშები"</item>
- <item msgid="1255741860568329178">"ხელოვნება"</item>
- <item msgid="7603949681065702867">"გართობა"</item>
- <item msgid="4453821994746804366">"ცხოვრების სტილი"</item>
- <item msgid="3488534597567932843">"მუსიკა"</item>
- <item msgid="7452153120614274095">"პრემიერა"</item>
- <item msgid="8215762047341133299">"ტექნოლოგიები/მეცნიერება"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"ყველა არხი"</item>
+ <item msgid="6897460857821394118">"ოჯახი/ბავშვები"</item>
+ <item msgid="551257741825778215">"სპორტი"</item>
+ <item msgid="452133796804325879">"საყიდლები"</item>
+ <item msgid="3296058637230163031">"ფილმები"</item>
+ <item msgid="1054540282883891201">"კომედია"</item>
+ <item msgid="7900158429062595471">"მოგზაურობა"</item>
+ <item msgid="3768998587825611787">"დრამა"</item>
+ <item msgid="8340620094959282881">"განათლება"</item>
+ <item msgid="7396447839483867269">"ცხოველები/ველური ბუნება"</item>
+ <item msgid="4738043455148062673">"ახალი ამბები"</item>
+ <item msgid="7405041316051047427">"თამაშები"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"ყველა არხი"</item>
+ <item msgid="7909003973960375395">"ოჯახი/ბავშვები"</item>
+ <item msgid="3185279732911635789">"სპორტი"</item>
+ <item msgid="4704858492065325964">"საყიდლები"</item>
+ <item msgid="6083795019290250078">"ფილმები"</item>
+ <item msgid="8302638329222449550">"კომედია"</item>
+ <item msgid="3803709976021475052">"მოგზაურობა"</item>
+ <item msgid="8116747365234169059">"დრამა"</item>
+ <item msgid="7356447541595315913">"განათლება"</item>
+ <item msgid="7511135485827589547">"ცხოველები/ველური ბუნება"</item>
+ <item msgid="6961248112238009967">"ახალი ამბები"</item>
+ <item msgid="6484685553679698447">"თამაშები"</item>
+ <item msgid="2737158328243183190">"ხელოვნება"</item>
+ <item msgid="6577176952650166615">"გართობა"</item>
+ <item msgid="7886693831871777617">"ცხოვრების სტილი"</item>
+ <item msgid="8145832312485577062">"მუსიკა"</item>
+ <item msgid="1345789204804308580">"პრემიერა"</item>
+ <item msgid="2736680312770771994">"ტექნოლოგიები/მეცნიერება"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"პირდაპირი არხები"</item>
diff --git a/res/values-ka-rGE/rating_system_strings.xml b/res/values-ka-rGE/rating_system_strings.xml
index c30c3de6..249bb557 100644
--- a/res/values-ka-rGE/rating_system_strings.xml
+++ b/res/values-ka-rGE/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"პროგრამების შიგთავსი შეიძლება შეუფერებელი იყოს 15 წლამდე მაყურებლებისთვის. აქედან გამომდინარე, მათი ყურება მშობლის თანხმობით უნდა მოხდეს."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"პროგრამების შიგთავსი შეიძლება შეუფერებელი იყოს 19 წლამდე მაყურებლებისთვის. აქედან გამომდინარე, მათი ყურება ამ აუდიტორიისთვის რეკომენდებული არ არის."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"მაცდუნებელი დიალოგი"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"უხამსი მეტყველება"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"სექსუალური ხასიათის შინაარსი"</string>
diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml
index 5b58347b..831cdd05 100644
--- a/res/values-ka-rGE/strings.xml
+++ b/res/values-ka-rGE/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"ღია კოდის ლიცენზიები"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"ღია კოდის ლიცენზიები"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"ვერსია"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"დეველოპერთა პარამეტრები"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"USB TV-ტუნერის ჩართვა"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"USB TV-ტუნერის ხმის მოსასმენად, თქვენი ტელევიზორის მიერ მხარდაჭერილი უნდა იყოს AC3 აუდიოს გატარება."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 აუდიოს გატარების შესაძლებლობა"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"თქვენი ტელევიზორის მიერ მხარდაჭერილია AC3 აუდიოს გატარება."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"თქვენი ტელევიზორის მიერ არ არის მხარდაჭერილი AC3 აუდიოს გატარება."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"მსურს პირდაპირი არხების გაუმჯობესების ხელშეწყობა"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"პირდაპირი არხების გაუმჯობესების მიზნით და ავარიულად გათიშვებისა თუ გაჭედვების თავიდან ასაცილებლად, გამოყენების და დიაგნოსტიკური მონაცემების Google-თან გაზიარება."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"ამ არხის საყურებლად დააჭირეთ „მარჯვენას“ და შეიყვანოთ თქვენი PIN"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"პროგრამა დაბლოკილია"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"ამ პროგრამის რეიტინგია <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"მხოლოდ აუდიო"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"სიგნალი სუსტია"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"ინტერნეტთან კავშირი არ არის"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"უსათაურო"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"არხი დაბლოკილია"</string>
- <string name="episode_format" msgid="4881195874563241096">"სეზ.<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: ეპ. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"ახალი"</string>
<string name="setup_category_done" msgid="4750902502852212319">"წყაროები"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"არხების დაჭერა ვერ მოხერხდა."</string>
<string name="msg_missing_app" msgid="8291542072400042076">"ამ მოქმედების შესასრულებლად აპი ვერ მოიძებნა."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"წყაროს ყველა არხი დამალულია.\nსაყურებლად აირჩიეთ მინიმუმ ერთი არხი."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"მიუწვდომელია სუსტი ვიდეო სიგნალის გამო."</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"ვიდეო მიუწვდომელი გახდა მოულოდნელად."</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"ღილაკი „უკან“ დაკავშირებული მოწყობილობისთვისაა. გამოსასვლელად დააჭირეთ ღილაკს „მთავარი“."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"პირდაპირი არხები არ არის მხარდაჭერილი ამ მოწყობილობაზე Android Lollipop-ით."</string>
diff --git a/res/values-kk-rKZ/arrays.xml b/res/values-kk-rKZ/arrays.xml
index bbb0282c..16324735 100644
--- a/res/values-kk-rKZ/arrays.xml
+++ b/res/values-kk-rKZ/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Толық"</item>
<item msgid="8568284598210500589">"Масштабтау"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Барлық арналар"</item>
- <item msgid="928298872841713530">"Отбасы/балалар"</item>
- <item msgid="2751606947569857164">"Спорт"</item>
- <item msgid="7345749789651321496">"Сатып алулар"</item>
- <item msgid="167201149441442173">"Фильмдер"</item>
- <item msgid="525966731464264290">"Комедия"</item>
- <item msgid="6096710741527327836">"Саяхат"</item>
- <item msgid="2851882187117833883">"Драма"</item>
- <item msgid="78492781188719038">"Білім"</item>
- <item msgid="7221999662426308394">"Жануарлар/жабайы табиғат"</item>
- <item msgid="375300513250925001">"Жаңалықтар"</item>
- <item msgid="7746320336582330410">"Ойындар"</item>
- <item msgid="1255741860568329178">"Өнер"</item>
- <item msgid="7603949681065702867">"Ойын-сауық"</item>
- <item msgid="4453821994746804366">"Өмір салты"</item>
- <item msgid="3488534597567932843">"Mузыка"</item>
- <item msgid="7452153120614274095">"Премьера"</item>
- <item msgid="8215762047341133299">"Технология/ғылым"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Барлық арналар"</item>
+ <item msgid="6897460857821394118">"Отбасы/балалар"</item>
+ <item msgid="551257741825778215">"Спорт"</item>
+ <item msgid="452133796804325879">"Сатып алулар"</item>
+ <item msgid="3296058637230163031">"Фильмдер"</item>
+ <item msgid="1054540282883891201">"Комедия"</item>
+ <item msgid="7900158429062595471">"Саяхат"</item>
+ <item msgid="3768998587825611787">"Драма"</item>
+ <item msgid="8340620094959282881">"Білім"</item>
+ <item msgid="7396447839483867269">"Жануарлар/жабайы табиғат"</item>
+ <item msgid="4738043455148062673">"Жаңалықтар"</item>
+ <item msgid="7405041316051047427">"Ойындар"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Барлық арналар"</item>
+ <item msgid="7909003973960375395">"Отбасы/балалар"</item>
+ <item msgid="3185279732911635789">"Спорт"</item>
+ <item msgid="4704858492065325964">"Сатып алулар"</item>
+ <item msgid="6083795019290250078">"Фильмдер"</item>
+ <item msgid="8302638329222449550">"Комедия"</item>
+ <item msgid="3803709976021475052">"Саяхат"</item>
+ <item msgid="8116747365234169059">"Драма"</item>
+ <item msgid="7356447541595315913">"Білім"</item>
+ <item msgid="7511135485827589547">"Жануарлар/жабайы табиғат"</item>
+ <item msgid="6961248112238009967">"Жаңалықтар"</item>
+ <item msgid="6484685553679698447">"Ойындар"</item>
+ <item msgid="2737158328243183190">"Өнер"</item>
+ <item msgid="6577176952650166615">"Ойын-сауық"</item>
+ <item msgid="7886693831871777617">"Өмір салты"</item>
+ <item msgid="8145832312485577062">"Музыка"</item>
+ <item msgid="1345789204804308580">"Премьера"</item>
+ <item msgid="2736680312770771994">"Технология/ғылым"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Live арналары"</item>
diff --git a/res/values-kk-rKZ/rating_system_strings.xml b/res/values-kk-rKZ/rating_system_strings.xml
index 520f73ba..20e66344 100644
--- a/res/values-kk-rKZ/rating_system_strings.xml
+++ b/res/values-kk-rKZ/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Бағдарламаларда 15 жасқа толмаған пайдаланушылардың көруіне болмайтын материалдар болуы мүмкін, сондықтан ата-ананың рұқсаты керек."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Бағдарламаларда 19 жасқа толмаған пайдаланушылардың көруіне болмайтын материалдар болуы мүмкін, сондықтан 19-ға дейінгі балаларға рұқсат етілмейді."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Ұятсыз әңгіме"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Дөрекі сөздер"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Жыныстық қатынасқа қатысты мазмұн"</string>
diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml
index 79833b29..56e72d5f 100644
--- a/res/values-kk-rKZ/strings.xml
+++ b/res/values-kk-rKZ/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Ашық бастапқы код лицензиялары"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Ашық бастапқы код лицензиялары"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Нұсқа"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Әзірлеуші опциялары"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"USB ТД тюнерін қосу"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"USB ТД тюнерінің дауысын есіту үшін теледидар AC3 транзиттік өткізуді қолдауы тиіс."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 аудио мүмкіндігі"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Теледидарыңыз AC3 транзиттік өткізуін қолдайды."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Теледидарыңыз AC3 транзиттік өткізуін қолдамайды."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Live TV қолданбасын жақсартуға көмектесу"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Анонимді пайдалану және диагностика деректерін Google жүйесімен бөлісіңіз, сонда Live TV қолданбасын жақсартып, жаңылыс не қатып қалу сияқты ақаулардың алдын аласыз."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Бұл арнаны көру үшін оңға түймесін басып, PIN кодын енгізіңіз"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Бағдарлама бөгелген"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Бұл бағдарламаға <xliff:g id="RATING">%1$s</xliff:g> бағасы қойылған"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Тек аудио"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Cигнал әлсіз"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Интернетпен байланыс жоқ"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Тақырыпсыз"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Арна бөгелген"</string>
- <string name="episode_format" msgid="4881195874563241096">"<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>-маусым: <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g>-эпизод <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Жаңа"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Дереккөздер"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Арнаға реттелмеді"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Бұл әрекетті орындайтын қолданба табылмады."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Барлық көз арналар жасырылған.\nКөру үшін кемінде бір арнаны таңдаңыз."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Әлсіз бейне сигналына байланысты қолжетімсіз"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Бейне белгісіз себеппен қолжетімсіз болып тұр"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"КЕРІ пернесі қосылған құрылғыға арналған. Шығу үшін НЕГІЗГІ пернесін басыңыз."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Android Lollipop жүйесі орнатылған құрылғыларда Live TV қолданбасына қолдау көрсетілмейді."</string>
diff --git a/res/values-km-rKH/arrays.xml b/res/values-km-rKH/arrays.xml
index e46312b4..0762e6ed 100644
--- a/res/values-km-rKH/arrays.xml
+++ b/res/values-km-rKH/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"ពេញ"</item>
<item msgid="8568284598210500589">"ពង្រីក"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"គ្រប់​ឆានែល"</item>
- <item msgid="928298872841713530">"គ្រួសារ/កុមារ"</item>
- <item msgid="2751606947569857164">"កីឡា"</item>
- <item msgid="7345749789651321496">"ទិញ​ទំនិញ"</item>
- <item msgid="167201149441442173">"ភាពយន្ត"</item>
- <item msgid="525966731464264290">"រឿង​កំប្លែង"</item>
- <item msgid="6096710741527327836">"ធ្វើដំណើរ"</item>
- <item msgid="2851882187117833883">"ល្ខោន"</item>
- <item msgid="78492781188719038">"ការអប់រំ"</item>
- <item msgid="7221999662426308394">"សត្វ​ពាហនៈ/សត្វព្រៃ"</item>
- <item msgid="375300513250925001">"ព័ត៌មាន"</item>
- <item msgid="7746320336582330410">"លេង​ហ្គេម​"</item>
- <item msgid="1255741860568329178">"សិល្បៈ"</item>
- <item msgid="7603949681065702867">"ការ​កម្សាន្ត"</item>
- <item msgid="4453821994746804366">"របៀបរស់នៅ"</item>
- <item msgid="3488534597567932843">"តន្ត្រី"</item>
- <item msgid="7452153120614274095">"បង្ហាញជូនលើដំបូង"</item>
- <item msgid="8215762047341133299">"បច្ចេក/វិទ្យាសាស្ត្រ"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"គ្រប់ប៉ុស្តិ៍"</item>
+ <item msgid="6897460857821394118">"គ្រួសារ/កុមារ"</item>
+ <item msgid="551257741825778215">"កីឡា"</item>
+ <item msgid="452133796804325879">"ទិញ​ទំនិញ"</item>
+ <item msgid="3296058637230163031">"ភាពយន្ត"</item>
+ <item msgid="1054540282883891201">"រឿង​កំប្លែង"</item>
+ <item msgid="7900158429062595471">"ធ្វើដំណើរ"</item>
+ <item msgid="3768998587825611787">"ល្ខោន"</item>
+ <item msgid="8340620094959282881">"ការអប់រំ"</item>
+ <item msgid="7396447839483867269">"សត្វ​ពាហនៈ/សត្វព្រៃ"</item>
+ <item msgid="4738043455148062673">"ព័ត៌មាន"</item>
+ <item msgid="7405041316051047427">"ការលេងហ្គេម"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"គ្រប់ប៉ុស្តិ៍"</item>
+ <item msgid="7909003973960375395">"គ្រួសារ/កុមារ"</item>
+ <item msgid="3185279732911635789">"កីឡា"</item>
+ <item msgid="4704858492065325964">"ទិញ​ទំនិញ"</item>
+ <item msgid="6083795019290250078">"ភាពយន្ត"</item>
+ <item msgid="8302638329222449550">"រឿង​កំប្លែង"</item>
+ <item msgid="3803709976021475052">"ធ្វើដំណើរ"</item>
+ <item msgid="8116747365234169059">"ល្ខោន"</item>
+ <item msgid="7356447541595315913">"ការអប់រំ"</item>
+ <item msgid="7511135485827589547">"សត្វ​ពាហនៈ/សត្វព្រៃ"</item>
+ <item msgid="6961248112238009967">"ព័ត៌មាន"</item>
+ <item msgid="6484685553679698447">"ការលេងហ្គេម"</item>
+ <item msgid="2737158328243183190">"សិល្បៈ"</item>
+ <item msgid="6577176952650166615">"កម្សាន្ត"</item>
+ <item msgid="7886693831871777617">"របៀបរស់នៅ"</item>
+ <item msgid="8145832312485577062">"តន្ត្រី"</item>
+ <item msgid="1345789204804308580">"បង្ហាញជូនលើដំបូង"</item>
+ <item msgid="2736680312770771994">"បច្ចេក/វិទ្យាសាស្ត្រ"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"ប៉ុស្តិ៍ផ្សាយផ្ទាល់"</item>
diff --git a/res/values-km-rKH/rating_system_strings.xml b/res/values-km-rKH/rating_system_strings.xml
index 3000a149..bf188cb5 100644
--- a/res/values-km-rKH/rating_system_strings.xml
+++ b/res/values-km-rKH/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"កម្មវិធីអាចមានអ្វីមួយដែលមិនសមរម្យសម្រាប់អ្នកទស្សនាដែលមានអាយុក្រោម 15 ឆ្នាំ ដូច្នេះការឃ្លាំមើលពីមាតាបិតាគួរត្រូវបានប្រើសម្រាប់ពួកគេ។"</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"កម្មវិធីអាចមានអ្វីមួយដែលមិនសមរម្យសម្រាប់អ្នកទស្សនាដែលមានអាយុក្រោម 19 ឆ្នាំ ដូច្នេះកម្មវិធីទាំងនោះមិនស័ក្កិសមនឹងយុវវ័យដែលមានអាយុក្រោម 19 ឆ្នាំទេ។"</string>
<string name="description_us_tv_d" msgid="12333789157204816">"ការពិភាក្សាផ្តល់យោបល់"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"ភាសាអសុរោះ"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"មាតិកាផ្លូវភេទ"</string>
diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml
index e0ba7b53..77f4edbd 100644
--- a/res/values-km-rKH/strings.xml
+++ b/res/values-km-rKH/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"អាជ្ញាប័ណ្ណប្រភពកូដបើកចំហ"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"អាជ្ញាប័ណ្ណប្រភពកូដចំហ"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"កំណែ"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"ជម្រើស​សម្រាប់​​អ្នក​អភិវឌ្ឍ"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"បើកដំណើរការឧបករណ៍ចាប់ប៉ុស្តិ៍ទូរទស្សន៍ USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"ដើម្បីឮសំឡេងឧបករណ៍ចាប់ប៉ុស្តិ៍ទូរទស្សន៍ USB ទូរទស្សន៍របស់អ្នកគួរតែអាចគាំទ្ររន្ធដោត AC3។"</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"សមត្ថភាពសំឡេង AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"ទូរទស្សន៍របស់អ្នកគាំទ្ររន្ធដោត AC3"</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"ទូរទស្សន៍របស់អ្នកមិនគាំទ្ររន្ធដោត AC3 ទេ"</string>
<string name="about_menu_improve" msgid="3712578027009311401">"ជួយធ្វើឲ្យប៉ុស្តិ៍ផ្សាយផ្ទាល់ប្រសើងជាងមុន"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"ចែករំលែកទិន្នន័យវិនិច្ឆ័យ និងទិន្នន័យប្រើប្រាស់អនាមិកជាមួយ Google នោះយើងនឹងអាចធ្វើឲ្យប៉ុស្តិ៍ផ្សាយបន្តផ្ទាល់ប្រសើរឡើង និងទប់ស្កាត់បញ្ហាដូចជា៖ ការគាំង និងការមិនដំណើរការជាដើម។"</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"ដើម្បី​មើល​ឆានែល​នេះ អ្នក​ត្រូវ​ចុច​កណ្ដុរស្ដាំ រួច​បញ្ចូល​កូដ PIN របស់​អ្នក"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"កម្មវិធី​នេះ​ត្រូវ​បាន​ទប់ស្កាត់"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"កម្មវិធីនេះត្រូវបានវាយតម្លៃ <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"តែ​អូឌីយ៉ូ​ប៉ុណ្ណោះ"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"សញ្ញា​ខ្សោយ"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"គ្មានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"គ្មាន​ចំណងជើង"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"បាន​ទប់​ស្កាត់​ឆានែល"</string>
- <string name="episode_format" msgid="4881195874563241096">"រដូវកាល <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>៖ វគ្គ <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"ថ្មី"</string>
<string name="setup_category_done" msgid="4750902502852212319">"ប្រភព"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"កាកែសម្រួលប៉ុស្តិ៍បានបរាជ័យ"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"រកមិនឃើញកម្មវិធីដើម្បីគ្រប់គ្រងសកម្មភាពនេះទេ។"</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"រាល់​​ឆានែល​ប្រភព​គឺ​​លាក់​។ \n ជ្រើស​ឆា​នែ​ល​យ៉ាង​ហោច​ណាស់​មួយ​​ដើម្បី​​មើល​។"</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"មិនអាចចាក់បានដោយសារសេវាវីដេអូខ្សោយ"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"មិនបានរំពឹងថាមិនមានវីដេអូនេះទេ"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"ប៊ូតុង​ថយក្រោយ​សម្រាប់​ឧបករណ៍​ដែល​បាន​ភ្ជាប់។ ចុច​ប៊ូតុង​ដើម ដើម្បី​ចាកចេញ។"</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"ប៉ុស្តិ៍ផ្សាយផ្ទាល់មិនត្រូវបានគាំទ្រលើឧបករណ៍ដែលដំណើរការដោយ Android Lollipop នេះទេ។"</string>
diff --git a/res/values-kn-rIN/arrays.xml b/res/values-kn-rIN/arrays.xml
index 1b1c650f..eb376e7b 100644
--- a/res/values-kn-rIN/arrays.xml
+++ b/res/values-kn-rIN/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"ಸಂಪೂರ್ಣ"</item>
<item msgid="8568284598210500589">"ಜೂಮ್"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"ಎಲ್ಲಾ ಚಾನಲ್‌ಗಳು"</item>
- <item msgid="928298872841713530">"ಕುಟುಂಬ/ಮಕ್ಕಳು"</item>
- <item msgid="2751606947569857164">"ಕ್ರೀಡೆಗಳು"</item>
- <item msgid="7345749789651321496">"ಶಾಪಿಂಗ್"</item>
- <item msgid="167201149441442173">"ಚಲನಚಿತ್ರಗಳು"</item>
- <item msgid="525966731464264290">"ಹಾಸ್ಯ ಪ್ರಧಾನ"</item>
- <item msgid="6096710741527327836">"ಪ್ರಯಾಣ"</item>
- <item msgid="2851882187117833883">"ನಾಟಕ"</item>
- <item msgid="78492781188719038">"ವಿದ್ಯಾಭ್ಯಾಸ"</item>
- <item msgid="7221999662426308394">"ಪ್ರಾಣಿ/ವನ್ಯಜೀವಿ"</item>
- <item msgid="375300513250925001">"ಸುದ್ದಿ"</item>
- <item msgid="7746320336582330410">"ಗೇಮಿಂಗ್"</item>
- <item msgid="1255741860568329178">"ಕಲೆ"</item>
- <item msgid="7603949681065702867">"ಮನರಂಜನೆ"</item>
- <item msgid="4453821994746804366">"ಲೈಫ್‌ಸ್ಟೈಲ್‌‌"</item>
- <item msgid="3488534597567932843">"ಸಂಗೀತ"</item>
- <item msgid="7452153120614274095">"ಪ್ರೀಮಿಯರ್"</item>
- <item msgid="8215762047341133299">"ತಂತ್ರಜ್ಞಾನ/ವಿಜ್ಞಾನ"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"ಎಲ್ಲಾ ಚಾನಲ್‌ಗಳು"</item>
+ <item msgid="6897460857821394118">"ಕುಟುಂಬ/ಮಕ್ಕಳು"</item>
+ <item msgid="551257741825778215">"ಕ್ರೀಡೆಗಳು"</item>
+ <item msgid="452133796804325879">"ಶಾಪಿಂಗ್"</item>
+ <item msgid="3296058637230163031">"ಚಲನಚಿತ್ರಗಳು"</item>
+ <item msgid="1054540282883891201">"ಹಾಸ್ಯ ಪ್ರಧಾನ"</item>
+ <item msgid="7900158429062595471">"ಪ್ರವಾಸ"</item>
+ <item msgid="3768998587825611787">"ನಾಟಕ"</item>
+ <item msgid="8340620094959282881">"ಶಿಕ್ಷಣ"</item>
+ <item msgid="7396447839483867269">"ಪ್ರಾಣಿ/ವನ್ಯಜೀವಿ"</item>
+ <item msgid="4738043455148062673">"ಸುದ್ದಿ"</item>
+ <item msgid="7405041316051047427">"ಗೇಮಿಂಗ್"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"ಎಲ್ಲಾ ಚಾನಲ್‌ಗಳು"</item>
+ <item msgid="7909003973960375395">"ಕುಟುಂಬ/ಮಕ್ಕಳು"</item>
+ <item msgid="3185279732911635789">"ಕ್ರೀಡೆಗಳು"</item>
+ <item msgid="4704858492065325964">"ಶಾಪಿಂಗ್"</item>
+ <item msgid="6083795019290250078">"ಚಲನಚಿತ್ರಗಳು"</item>
+ <item msgid="8302638329222449550">"ಹಾಸ್ಯ ಪ್ರಧಾನ"</item>
+ <item msgid="3803709976021475052">"ಪ್ರವಾಸ"</item>
+ <item msgid="8116747365234169059">"ನಾಟಕ"</item>
+ <item msgid="7356447541595315913">"ಶಿಕ್ಷಣ"</item>
+ <item msgid="7511135485827589547">"ಪ್ರಾಣಿ/ವನ್ಯಜೀವಿ"</item>
+ <item msgid="6961248112238009967">"ಸುದ್ದಿ"</item>
+ <item msgid="6484685553679698447">"ಗೇಮಿಂಗ್"</item>
+ <item msgid="2737158328243183190">"ಕಲೆ"</item>
+ <item msgid="6577176952650166615">"ಮನರಂಜನೆ"</item>
+ <item msgid="7886693831871777617">"ಜೀವನಶೈಲಿ"</item>
+ <item msgid="8145832312485577062">"ಸಂಗೀತ"</item>
+ <item msgid="1345789204804308580">"ಪ್ರೀಮಿಯರ್"</item>
+ <item msgid="2736680312770771994">"ತಂತ್ರಜ್ಞಾನ/ವಿಜ್ಞಾನ"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"ಲೈವ್‌ ಚಾನಲ್‌ಗಳು"</item>
diff --git a/res/values-kn-rIN/rating_system_strings.xml b/res/values-kn-rIN/rating_system_strings.xml
index 3b3b3c2c..7f3c869a 100644
--- a/res/values-kn-rIN/rating_system_strings.xml
+++ b/res/values-kn-rIN/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"ಕಾರ್ಯಕ್ರಮವು 15 ವರ್ಷದೊಳಗಿನ ಪ್ರೇಕ್ಷಕರಿಗೆ ಸೂಕ್ತವಲ್ಲದ ವಿಷಯವನ್ನು ಹೊಂದಿರಬಹುದು. ಆದ್ದರಿಂದಾಗಿ ಪೋಷಕರು ಅವರ ಸ್ವಂತ ವಿವೇಚನೆ ಬಳಸಬೇಕಾಗುತ್ತದೆ."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"ಕಾರ್ಯಕ್ರಮವು 19 ವರ್ಷದೊಳಗಿನ ಪ್ರೇಕ್ಷಪರಿಗೆ ಸೂಕ್ತವಲ್ಲದ ವಿಷಯವನ್ನು ಹೊಂದಿರಬಹುದು ಮತ್ತು ಇಂತಹವುಗಳು 19 ವರ್ಷದೊಳಗಿನ ಯುವಕರಿಗೆ ಸೂಕ್ತವಾಗಿರುವುದಿಲ್ಲ."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"ಸೂಚಿತ ಸಂಭಾಷಣೆ"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"ಒರಟಾದ ಭಾಷೆ"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"ಲೈಂಗಿಕ ವಿಷಯ"</string>
diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml
index bc008aa9..e9e0127c 100644
--- a/res/values-kn-rIN/strings.xml
+++ b/res/values-kn-rIN/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"ಮುಕ್ತ ಮೂಲ ಪರವಾನಗಿಗಳು"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"ಮುಕ್ತ ಮೂಲ ಪರವಾನಗಿಗಳು"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"ಆವೃತ್ತಿ"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"ಡೆವಲಪರ್ ಆಯ್ಕೆಗಳು"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"USB TV ಟ್ಯೂನರ್ ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"USB TV ಟ್ಯೂನರ್‌ನ ಧ್ವನಿ ಆಲಿಸಲು, ನಿಮ್ಮ TV ಯು AC3 ಪಾಸ್‌ಥ್ರೂ ಬೆಂಬಲಿಸಬೇಕು."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 ಆಡಿಯೋ ಸಾಮರ್ಥ್ಯ"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"ನಿಮ್ಮ TV ಯು AC3 ಪಾಸ್‌ಥ್ರೂ ಬೆಂಬಲಿಸುತ್ತದೆ."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"ನಿಮ್ಮ TV ಯು AC3 ಪಾಸ್‌ಥ್ರೂ ಅನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"ಲೈವ್ ಚಾನಲ್‌ಗಳನ್ನು ಸುಧಾರಿಸಲು ಸಹಾಯ ಮಾಡಿ"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"ಅನಾಮಧೇಯ ಬಳಕೆ ಮತ್ತು ಡಯಾಗ್ನೋಸ್ಟಿಕ್ಸ್ ಡೇಟಾವನ್ನು Google ಜೊತೆಗೆ ಹಂಚಿಕೊಳ್ಳಿ, ಈ ಮೂಲಕ ನಮಗೆ ಲೈವ್ ಚಾನಲ್‌ಗಳನ್ನು ಉತ್ತಮಗೊಳಿಸಲು ಹಾಗೂ ಕ್ರ್ಯಾಶಿಂಗ್ ಮತ್ತು ಫ್ರೀಜಿಂಗ್‌ನಂತಹ ಸಮಸ್ಯೆಗಳನ್ನು ತಡೆಯಲು ಸಾಧ್ಯವಾಗುತ್ತದೆ."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"ಈ ಚಾನಲ್ ಅನ್ನು ವೀಕ್ಷಿಸಲು, ಬಲಕ್ಕೆ ಒತ್ತಿ ಮತ್ತು ನಿಮ್ಮ PIN ಅನ್ನು ನಮೂದಿಸಿ"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"ಕಾರ್ಯಕ್ರಮವನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"ಈ ಕಾರ್ಯಕ್ರಮವನ್ನು <xliff:g id="RATING">%1$s</xliff:g> ಎಂದು ರೇಟ್‌ ಮಾಡಲಾಗಿದೆ."</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"ಆಡಿಯೊ ಮಾತ್ರ"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"ದುರ್ಬಲ ಸಿಗ್ನಲ್"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕವಿಲ್ಲ"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"ಯಾವುದೇ ಶೀರ್ಷಿಕೆಯಿಲ್ಲ"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"ಚಾನಲ್ ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ"</string>
- <string name="episode_format" msgid="4881195874563241096">"S<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: ಸಂ. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"ಹೊಸತು"</string>
<string name="setup_category_done" msgid="4750902502852212319">"ಮೂಲಗಳು"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"ಟ್ಯೂನ್ ವಿಫಲವಾಗಿದೆ"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"ಈ ಕ್ರಿಯೆಯನ್ನು ನಿರ್ವಹಿಸಲು ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್‌ ಕಂಡುಬಂದಿಲ್ಲ."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"ಎಲ್ಲ ಮೂಲ ಚಾನಲ್‌ಗಳನ್ನು ಮರೆಮಾಡಲಾಗಿದೆ.\nವೀಕ್ಷಿಸಲು ಕನಿಷ್ಠ ಒಂದು ಚಾನಲ್‌‌ ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"ದುರ್ಬಲ ವೀಡಿಯೊ ಸಿಗ್ನಲ್‌‌ನಿಂದಾಗಿ ಲಭ್ಯವಿಲ್ಲ"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"ವೀಡಿಯೊ ಅನಿರೀಕ್ಷಿತವಾಗಿ ಲಭ್ಯವಿಲ್ಲ"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"ಸಂಪರ್ಕಪಡಿಸಲಾದ ಸಾಧನಕ್ಕಾಗಿ ಹಿಂದೆ ಕೀ. ನಿರ್ಗಮಿಸಲು ಮುಖಪುಟ ಬಟನ್ ಒತ್ತಿರಿ."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Android Lollipop ಹೊಂದಿರುವ ಈ ಸಾಧನದಲ್ಲಿ ಲೈವ್ ಚಾನಲ್‌ಗಳು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."</string>
diff --git a/res/values-ko/arrays.xml b/res/values-ko/arrays.xml
index da945d72..cdf412c8 100644
--- a/res/values-ko/arrays.xml
+++ b/res/values-ko/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"전체"</item>
<item msgid="8568284598210500589">"확대/축소"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"모든 채널"</item>
- <item msgid="928298872841713530">"가족/어린이"</item>
- <item msgid="2751606947569857164">"스포츠"</item>
- <item msgid="7345749789651321496">"쇼핑"</item>
- <item msgid="167201149441442173">"영화"</item>
- <item msgid="525966731464264290">"코미디"</item>
- <item msgid="6096710741527327836">"여행"</item>
- <item msgid="2851882187117833883">"드라마"</item>
- <item msgid="78492781188719038">"교육"</item>
- <item msgid="7221999662426308394">"동물/야생 생물"</item>
- <item msgid="375300513250925001">"뉴스"</item>
- <item msgid="7746320336582330410">"게임"</item>
- <item msgid="1255741860568329178">"예술"</item>
- <item msgid="7603949681065702867">"예능"</item>
- <item msgid="4453821994746804366">"라이프스타일"</item>
- <item msgid="3488534597567932843">"음악"</item>
- <item msgid="7452153120614274095">"프리미엄"</item>
- <item msgid="8215762047341133299">"기술/과학"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"모든 채널"</item>
+ <item msgid="6897460857821394118">"가족/어린이"</item>
+ <item msgid="551257741825778215">"스포츠"</item>
+ <item msgid="452133796804325879">"쇼핑"</item>
+ <item msgid="3296058637230163031">"영화"</item>
+ <item msgid="1054540282883891201">"코미디"</item>
+ <item msgid="7900158429062595471">"여행"</item>
+ <item msgid="3768998587825611787">"드라마"</item>
+ <item msgid="8340620094959282881">"교육"</item>
+ <item msgid="7396447839483867269">"동물/야생 생물"</item>
+ <item msgid="4738043455148062673">"뉴스"</item>
+ <item msgid="7405041316051047427">"게임"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"모든 채널"</item>
+ <item msgid="7909003973960375395">"가족/어린이"</item>
+ <item msgid="3185279732911635789">"스포츠"</item>
+ <item msgid="4704858492065325964">"쇼핑"</item>
+ <item msgid="6083795019290250078">"영화"</item>
+ <item msgid="8302638329222449550">"코미디"</item>
+ <item msgid="3803709976021475052">"여행"</item>
+ <item msgid="8116747365234169059">"드라마"</item>
+ <item msgid="7356447541595315913">"교육"</item>
+ <item msgid="7511135485827589547">"동물/야생 생물"</item>
+ <item msgid="6961248112238009967">"뉴스"</item>
+ <item msgid="6484685553679698447">"게임"</item>
+ <item msgid="2737158328243183190">"예술"</item>
+ <item msgid="6577176952650166615">"예능"</item>
+ <item msgid="7886693831871777617">"라이프스타일"</item>
+ <item msgid="8145832312485577062">"음악"</item>
+ <item msgid="1345789204804308580">"프리미엄"</item>
+ <item msgid="2736680312770771994">"기술/과학"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"실시간 채널"</item>
diff --git a/res/values-ko/rating_system_strings.xml b/res/values-ko/rating_system_strings.xml
index c6af92a6..8ce3d58c 100644
--- a/res/values-ko/rating_system_strings.xml
+++ b/res/values-ko/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"프로그램에 만 15세 미만의 잠재고객에게 부적합한 콘텐츠가 포함되어 있을 수 있기 때문에 부모님의 신중한 판단이 필요합니다."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"프로그램에 만 19세 미만의 잠재고객에게 부적합한 콘텐츠가 포함되어 있을 수 있기 때문에 만 19세 미만의 청소년에게 적합하지 않습니다."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"외설적인 대화"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"거친 욕설"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"성적인 콘텐츠"</string>
diff --git a/common/res_leanback/values-af/strings.xml b/res/values-ko/rating_system_strings_ko.xml
index d683758b..891d55e9 100644
--- a/common/res_leanback/values-af/strings.xml
+++ b/res/values-ko/rating_system_strings_ko.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
+<!--
+ ~ Copyright (C) 2016 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -17,6 +17,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"BEGIN HIER"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"Volgende"</string>
+ <!-- TV content rating system strings for KR TV. These strings are from
+ http://www.law.go.kr/admRulLsInfoP.do?admRulSeq=2000000118507
+ This resources are tranlated only for Korean. -->
+ <string name="title_kr_tv_all">모든연령시청가</string>
+ <string name="title_kr_tv_7">7세이상시청가</string>
+ <string name="title_kr_tv_12">12세이상시청가</string>
+ <string name="title_kr_tv_15">15세이상시청가</string>
+ <string name="title_kr_tv_19">19세이상시청가</string>
</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 75983308..aa2a124a 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -146,12 +146,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"오픈소스 라이선스"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"오픈소스 라이선스"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"버전"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"개발자 옵션"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"USB TV 튜너 사용"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"USB TV 튜너 소리를 들으려면 TV에서 AC3 패스 스루를 지원해야 합니다."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 오디오 기능"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"TV에서 AC3 패스 스루를 지원합니다."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"TV에서 AC3 패스 스루를 지원하지 않습니다."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"실시간 채널 개선에 도움 주기"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Google에서 실시간 채널을 향상하고 다운과 정지 등의 문제를 방지할 수 있도록 익명의 사용 및 진단 데이터를 공유합니다."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"이 채널을 보려면 오른쪽을 누르고 PIN을 입력하세요."</string>
@@ -163,9 +157,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"차단된 프로그램입니다."</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"이 프로그램의 시청 등급은 <xliff:g id="RATING">%1$s</xliff:g>입니다."</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"오디오 전용"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"신호 약함"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"인터넷에 연결되지 않았습니다."</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"제목 없음"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"차단된 채널"</string>
- <string name="episode_format" msgid="4881195874563241096">"시즌 <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: 에피소드 <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"새 소스"</string>
<string name="setup_category_done" msgid="4750902502852212319">"소스"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -192,7 +187,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"조정에 실패했습니다."</string>
<string name="msg_missing_app" msgid="8291542072400042076">"이 작업을 처리하는 앱을 찾을 수 없습니다."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"모든 소스 채널이 숨겨져 있습니다.\n시청할 채널을 하나 이상 선택하세요."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"동영상 신호가 약해서 사용할 수 없습니다."</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"예기치 않게 동영상을 사용할 수 없습니다."</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"뒤로 키는 연결된 기기에 사용됩니다. 종료하려면 홈 버튼을 누르세요."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"실시간 채널은 이 Android Lollipop 기기에서 지원되지 않습니다."</string>
diff --git a/res/values-ky-rKG/arrays.xml b/res/values-ky-rKG/arrays.xml
index 835274e6..36bb78d4 100644
--- a/res/values-ky-rKG/arrays.xml
+++ b/res/values-ky-rKG/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Толук"</item>
<item msgid="8568284598210500589">"Ченөлчөм"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Бардык каналдар"</item>
- <item msgid="928298872841713530">"Үй-бүлө/Балдар"</item>
- <item msgid="2751606947569857164">"Спорт"</item>
- <item msgid="7345749789651321496">"Соода-сатык"</item>
- <item msgid="167201149441442173">"Тасмалар"</item>
- <item msgid="525966731464264290">"Комедия"</item>
- <item msgid="6096710741527327836">"Саякат"</item>
- <item msgid="2851882187117833883">"Драма"</item>
- <item msgid="78492781188719038">"Билим алуу"</item>
- <item msgid="7221999662426308394">"Жаныбарлар дүйнөсү"</item>
- <item msgid="375300513250925001">"Жаңылыктар"</item>
- <item msgid="7746320336582330410">"Оюндар"</item>
- <item msgid="1255741860568329178">"Көркөм өнөрчүлүк"</item>
- <item msgid="7603949681065702867">"Көңүл ачуу"</item>
- <item msgid="4453821994746804366">"Жашоо мүнөзү"</item>
- <item msgid="3488534597567932843">"Музыка"</item>
- <item msgid="7452153120614274095">"Бет ачар"</item>
- <item msgid="8215762047341133299">"Тех/Илим"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Бардык каналдар"</item>
+ <item msgid="6897460857821394118">"Үй-бүлө/Балдар"</item>
+ <item msgid="551257741825778215">"Спорт"</item>
+ <item msgid="452133796804325879">"Соода-сатык"</item>
+ <item msgid="3296058637230163031">"Тасмалар"</item>
+ <item msgid="1054540282883891201">"Комедия"</item>
+ <item msgid="7900158429062595471">"Саякат"</item>
+ <item msgid="3768998587825611787">"Драма"</item>
+ <item msgid="8340620094959282881">"Билим алуу"</item>
+ <item msgid="7396447839483867269">"Жаныбарлар дүйнөсү"</item>
+ <item msgid="4738043455148062673">"Жаңылыктар"</item>
+ <item msgid="7405041316051047427">"Оюн-зоок"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Бардык каналдар"</item>
+ <item msgid="7909003973960375395">"Үй-бүлө/Балдар"</item>
+ <item msgid="3185279732911635789">"Спорт"</item>
+ <item msgid="4704858492065325964">"Соода-сатык"</item>
+ <item msgid="6083795019290250078">"Тасмалар"</item>
+ <item msgid="8302638329222449550">"Комедия"</item>
+ <item msgid="3803709976021475052">"Саякат"</item>
+ <item msgid="8116747365234169059">"Драма"</item>
+ <item msgid="7356447541595315913">"Билим алуу"</item>
+ <item msgid="7511135485827589547">"Жаныбарлар дүйнөсү"</item>
+ <item msgid="6961248112238009967">"Жаңылыктар"</item>
+ <item msgid="6484685553679698447">"Оюн-зоок"</item>
+ <item msgid="2737158328243183190">"Көркөм өнөрчүлүк"</item>
+ <item msgid="6577176952650166615">"Көңүл ачуу"</item>
+ <item msgid="7886693831871777617">"Жашоо мүнөзү"</item>
+ <item msgid="8145832312485577062">"Музыка"</item>
+ <item msgid="1345789204804308580">"Бет ачар"</item>
+ <item msgid="2736680312770771994">"Техника/Илим"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Түз ободогу каналдар"</item>
diff --git a/res/values-ky-rKG/rating_system_strings.xml b/res/values-ky-rKG/rating_system_strings.xml
index 59531a4a..98672b74 100644
--- a/res/values-ky-rKG/rating_system_strings.xml
+++ b/res/values-ky-rKG/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Программаларда 15 жашка чыга электерге ылайыксыз материал камтылгандыктан, ата-энелердин уруксаты талап кылынат."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Программаларда 19 жашка чыга электерге ылайыксыз материал камтылгандыктан, ал куракка чейинки балдарга көрсөтүлбөшү керек."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Адепсиз диалог"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Сөгүнгөн сөздөр"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Сексуалдык мазмун"</string>
diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml
index 7fc4e56f..6526178b 100644
--- a/res/values-ky-rKG/strings.xml
+++ b/res/values-ky-rKG/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Ачык программа уруксаттамалары"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Ачык программа уруксаттамалары"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Версиясы"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Иштеп чыгуучунун параметрлери"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"USB сыналгы күүлөгүчүн иштетүү"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"USB сыналгы күүлөгүчүнүн добушун угуу үчүн, сыналгыңыз AC3 өткөрмөсүн колдоого алышы керек."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 аудио жөндөмү"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Сыналгыңыз AC3 өткөрмөсүн колдойт."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Сыналгыңыз AC3 өткөрмөсүн колдоого албайт."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Түз ободогу каналдарды жакшыртууга жардам бериңиз"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Түз обо каналдарын жакшыртып, бузулуу жана тоңдуруу сыяктуу маселелердин алдын алышыбыз үчүн, колдонуу жана дарт аныктоо дайындарын Google менен жашыруун бөлүшүңүз."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Бул каналды көрүү үчүн, Оңго басып, PIN-иңизди киргизиңиз"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Бул программа бөгөттөлгөн"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Бул программанын рейтинги – <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Аудио гана"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Начар сигнал"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Интернет туташуусу жок"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Аталышы жок"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Канал бөгөттөлдү"</string>
- <string name="episode_format" msgid="4881195874563241096">"Мезгил<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Эпизод <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Жаңы"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Булактар"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Жөндөлбөй калды"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Бул ишти аткара турган бир дагы колдонмо табылган жок."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Бардык булак каналдары жашырылган.\nКөрүү үчүн кеминде бир канал тандаңыз."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Үзүл-кесил байланыштан улам видео жеткиликсиз болуп калды"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Видео күтүүсүздөн жеткиликсиз болуп калды."</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"АРТКА баскычы – туташкан түзмөккө. Чыгуу үчүн БАШКЫ баскычты басыңыз."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Бул Android Lollipop орнотулган түзмөктө Түз ободогу каналдар колдоого алынбайт."</string>
diff --git a/res/values-lo-rLA/arrays.xml b/res/values-lo-rLA/arrays.xml
index 5e1c1e0c..71d977b4 100644
--- a/res/values-lo-rLA/arrays.xml
+++ b/res/values-lo-rLA/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"ເຕັມ"</item>
<item msgid="8568284598210500589">"ຊູມ"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"​ທຸກ​ຊ່ອງ"</item>
- <item msgid="928298872841713530">"ຄອບ​ຄົວ/ເດັກ​ນ້ອຍ"</item>
- <item msgid="2751606947569857164">"ກິລາ"</item>
- <item msgid="7345749789651321496">"ຊັອບປິງ"</item>
- <item msgid="167201149441442173">"ຮູບເງົາ"</item>
- <item msgid="525966731464264290">"ຄອມເມດີ"</item>
- <item msgid="6096710741527327836">"ການເດີນທາງ"</item>
- <item msgid="2851882187117833883">"ດຣາມ່າ"</item>
- <item msgid="78492781188719038">"ການສຶກສາ"</item>
- <item msgid="7221999662426308394">"ສັດ"</item>
- <item msgid="375300513250925001">"​ຂ່າວ"</item>
- <item msgid="7746320336582330410">"ເກມ"</item>
- <item msgid="1255741860568329178">"ສິ​ລະ​ປະ"</item>
- <item msgid="7603949681065702867">"ບັນເທີງ"</item>
- <item msgid="4453821994746804366">"ການດຳລົງຊີວິດ"</item>
- <item msgid="3488534597567932843">"ເພງ"</item>
- <item msgid="7452153120614274095">"ການ​ສະ​ແດງ"</item>
- <item msgid="8215762047341133299">"ເຕັກ​ໂນ​ໂລ​ຊີ/ວິ​ທະ​ຍາ​ສາດ"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"ທຸກຊ່ອງ"</item>
+ <item msgid="6897460857821394118">"ຄອບຄົວ/ເດັກນ້ອຍ"</item>
+ <item msgid="551257741825778215">"ກິລາ"</item>
+ <item msgid="452133796804325879">"ຊັອບປິ້ງ"</item>
+ <item msgid="3296058637230163031">"ຮູບເງົາ"</item>
+ <item msgid="1054540282883891201">"ຕະຫລົກ"</item>
+ <item msgid="7900158429062595471">"ການເດີນທາງ"</item>
+ <item msgid="3768998587825611787">"ດຣາມ່າ"</item>
+ <item msgid="8340620094959282881">"ການສຶກສາ"</item>
+ <item msgid="7396447839483867269">"ສັດ"</item>
+ <item msgid="4738043455148062673">"ຂ່າວ"</item>
+ <item msgid="7405041316051047427">"ເກມ"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"ທຸກຊ່ອງ"</item>
+ <item msgid="7909003973960375395">"ຄອບຄົວ/ເດັກນ້ອຍ"</item>
+ <item msgid="3185279732911635789">"ກິລາ"</item>
+ <item msgid="4704858492065325964">"ຊັອບປິ້ງ"</item>
+ <item msgid="6083795019290250078">"ຮູບເງົາ"</item>
+ <item msgid="8302638329222449550">"ຕະຫລົກ"</item>
+ <item msgid="3803709976021475052">"ການເດີນທາງ"</item>
+ <item msgid="8116747365234169059">"ດຣາມ່າ"</item>
+ <item msgid="7356447541595315913">"ການສຶກສາ"</item>
+ <item msgid="7511135485827589547">"ສັດ"</item>
+ <item msgid="6961248112238009967">"ຂ່າວ"</item>
+ <item msgid="6484685553679698447">"ເກມ"</item>
+ <item msgid="2737158328243183190">"ສິລະປະ"</item>
+ <item msgid="6577176952650166615">"ບັນເທີງ"</item>
+ <item msgid="7886693831871777617">"ການດຳລົງຊີວິດ"</item>
+ <item msgid="8145832312485577062">"ດົນຕີ"</item>
+ <item msgid="1345789204804308580">"ການສະແດງ"</item>
+ <item msgid="2736680312770771994">"ເຕັກໂນໂລຊີ/ວິທະຍາສາດ"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"ຊ່ອງ​ອອກອາກາດສົດ"</item>
diff --git a/res/values-lo-rLA/rating_system_strings.xml b/res/values-lo-rLA/rating_system_strings.xml
index 852e8d45..99d82728 100644
--- a/res/values-lo-rLA/rating_system_strings.xml
+++ b/res/values-lo-rLA/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"ລາຍການອາດມີເນື້ອຫາທີ່ບໍ່ເໝາະສົມສຳລັບຜູ້ມີອາຍຸຕໍ່າກວ່າ 15 ປີ, ສະນັ້ນ ຄວນໃຫ້ຜູ້ປົກຄອງໃຫ້ຄຳແນະນຳ."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"ລາຍການອາດມີເນື້ອຫາທີ່ບໍ່ເໝາະສົມສຳລັບຜູ້ມີອາຍຸຕໍ່າກວ່າ 19 ປີ ດັ່ງນັ້ນຈຶ່ງບໍ່ເໝາະສົມສຳລັບຜູ້ທີ່ມີອາຍຸຕໍ່າກວ່າ 19 ປີ."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"ການ​ສົນ​ທະ​ນາ​ຊັກ​ຊວນ"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"ພາ​ສາ​ຫຍາບຄາຍ"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"ເນື້ອ​ໃນກ່ຽວ​ກັບ​ເພດ"</string>
diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml
index 0101d73c..0f833d0a 100644
--- a/res/values-lo-rLA/strings.xml
+++ b/res/values-lo-rLA/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"​ໃບ​ອະ​ນຸ​ຍາດ​ Open source"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"​ໃບ​ອະ​ນຸ​ຍາດ​ແຫຼ່ງ​ເປີດ"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"ເວີຊັນ"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"ຕົວ​ເລືອກ​ນັກພັດທະນາ"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"​ເປີດ​ໃຊ້ຕົວ​ຮັບ​ສັນຍານ​ໂທລະພາບ​ແບບ USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"​ເພື່ອ​ຟັງສຽງ​ຂອງ​ຕົວ​ຮັບ​ສັນຍານ​ໂທລະພາບ​ແບບ USB, ​ໂທລະພາບ​ຂອງ​ທ່ານ​ຄວນ​ຮອງ​ຮັບ​ການ​ສົ່ງ​ຜ່ານ AC3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"ຄວາມ​ສາມາດ​ສຽງ AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"​ໂທລະພາບ​ຂອງ​ທ່ານ​​ຮອງ​ຮັບ​ການ​ສົ່ງ​ຜ່ານ​ AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"​ໂທລະພາບ​ຂອງ​ທ່ານ​ບໍ່​ຮອງ​ຮັບ​ການ​ສົ່ງ​ຜ່ານ​ຂອງ AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"ຊ່ວຍ​ປັບ​ປຸງ​ຊ່ອງ​ສົດ"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"ສະ​ແດງ​ການ​ໃຊ້​ນິ​ລະ​ນາມ ແລະ ຂໍ້​ມູນ​ວິ​ນິ​ໄສ​ກັບ Google ເພື່ອ​ທີ່​ພວກ​ເຮົາ​ຈະ​ສາ​ມາດ​ເຮັດ​ໃຫ້ຊ່ອງ​ສົດດີ​ຂຶ້ນ ແລະ ປ້ອງ​ກັນ​ບັນ​ຫາ​ຕ່າງໆ​ເຊັ່ນ: ການ​ຂັດ​ຂ້ອງ ແລະ ການ​ຈຶ້ງ​ໄວ້."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"​ເພື່ອ​ເບິ່ງ​ຊ່ອງ​ນີ້, ກົດ​ປຸ່ມ ຂວາ ແລະ ປ້ອນ​ລະ​ຫັດ PIN ຂອງ​ທ່ານ"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"​ລາຍ​ການ​ຖືກບລັອກ"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"​ລາຍ​ການ​ນີ້​ຖືກ​ຈັດ​ປະ​ເພດ <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"ສຽງເທົ່ານັ້ນ"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"ສັນຍານອ່ອນ"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"ບໍ່ມີຫົວຂໍ້"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"​ຊ່ອງ​ຖືກບລັອກ"</string>
- <string name="episode_format" msgid="4881195874563241096">"S<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: ຕອນ <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"ໃໝ່"</string>
<string name="setup_category_done" msgid="4750902502852212319">"ແຫຼ່ງທີ່ມາຂອງຊ່ອງ"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"ການ​ປັບ​ຊ່ອງ​ລົ້ມ​ເຫລວ"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"ບໍ່ພົບແອັບຯທີ່ໃຊ້ເພື່ອດຳເນີນການ."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"ຊ່ອງ​ແຫລ່ງ​ຂໍ້​ມູນ​ທັງ​ໝົດ​ຖືກ​ເຊື່ອງ​ໄວ້​ແລ້ວ.\nກະ​ລຸ​ນາ​ເລືອກ​ຢ່າງ​ໜ້ອຍ​ນຶ່ງ​ຊ່ອງ​ເພື່ອ​ເບິ່ງ."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"ບໍ່​ສາ​ມາດ​ໃຊ້​ໄດ້​ເນື່ອງ​ຈາກ​ສັນ​ຍານ​ວິ​ດີ​ໂອ​ອ່ອນ"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"ວິ​ດີ​ໂອບໍ່​ສາ​ມາດ​ເບິ່ງ​ໄດ້​ໂດຍບໍ່​ຄາດ​ຄິດ"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"ປຸ່ມ Back ແມ່ນ​ສຳ​ລັບ​ອຸ​ປະ​ກອນ​ທີ່​ເຊື່ອມ​ຕໍ່​ແລ້ວ. ໃຫ້​ກົດ​ປຸ່ມ Home ເພື່ອອອກ."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"ຊ່ອງ​ສົດ​ບໍ່​ຖືກ​ຮອງ​ຮັບ​ຢູ່​ໃນ​ອຸ​ປະ​ກອນ​ນີ້​ດ້ວຍ Android Lollipop."</string>
diff --git a/res/values-lt/arrays.xml b/res/values-lt/arrays.xml
index e1512c7b..cf74fa4b 100644
--- a/res/values-lt/arrays.xml
+++ b/res/values-lt/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Visas"</item>
<item msgid="8568284598210500589">"Mastelio keitimas"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Visi kanalai"</item>
- <item msgid="928298872841713530">"Šeima / vaikai"</item>
- <item msgid="2751606947569857164">"Sportas"</item>
- <item msgid="7345749789651321496">"Apsipirkimas"</item>
- <item msgid="167201149441442173">"Filmai"</item>
- <item msgid="525966731464264290">"Komedijos"</item>
- <item msgid="6096710741527327836">"Kelionės"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Švietimas"</item>
- <item msgid="7221999662426308394">"Gyvūn. / lauk. gamta"</item>
- <item msgid="375300513250925001">"Naujienos"</item>
- <item msgid="7746320336582330410">"Žaidimai"</item>
- <item msgid="1255741860568329178">"Menai"</item>
- <item msgid="7603949681065702867">"Pramogos"</item>
- <item msgid="4453821994746804366">"Gyvenimo būdas"</item>
- <item msgid="3488534597567932843">"Muzika"</item>
- <item msgid="7452153120614274095">"Premjera"</item>
- <item msgid="8215762047341133299">"Technol. / mokslas"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Visi kanalai"</item>
+ <item msgid="6897460857821394118">"Šeima / vaikai"</item>
+ <item msgid="551257741825778215">"Sportas"</item>
+ <item msgid="452133796804325879">"Apsipirkimas"</item>
+ <item msgid="3296058637230163031">"Filmai"</item>
+ <item msgid="1054540282883891201">"Komedijos"</item>
+ <item msgid="7900158429062595471">"Kelionės"</item>
+ <item msgid="3768998587825611787">"Dramos"</item>
+ <item msgid="8340620094959282881">"Išsilavinimas"</item>
+ <item msgid="7396447839483867269">"Gyvūnija ir augalija"</item>
+ <item msgid="4738043455148062673">"Naujienos"</item>
+ <item msgid="7405041316051047427">"Žaidimai"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Visi kanalai"</item>
+ <item msgid="7909003973960375395">"Šeima / vaikai"</item>
+ <item msgid="3185279732911635789">"Sportas"</item>
+ <item msgid="4704858492065325964">"Apsipirkimas"</item>
+ <item msgid="6083795019290250078">"Filmai"</item>
+ <item msgid="8302638329222449550">"Komedijos"</item>
+ <item msgid="3803709976021475052">"Kelionės"</item>
+ <item msgid="8116747365234169059">"Dramos"</item>
+ <item msgid="7356447541595315913">"Išsilavinimas"</item>
+ <item msgid="7511135485827589547">"Gyvūnija ir augalija"</item>
+ <item msgid="6961248112238009967">"Naujienos"</item>
+ <item msgid="6484685553679698447">"Žaidimai"</item>
+ <item msgid="2737158328243183190">"Menai"</item>
+ <item msgid="6577176952650166615">"Pramogos"</item>
+ <item msgid="7886693831871777617">"Gyvenimo būdas"</item>
+ <item msgid="8145832312485577062">"Muzika"</item>
+ <item msgid="1345789204804308580">"Geriausias turinys"</item>
+ <item msgid="2736680312770771994">"Technol. / mokslas"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Tiesioginiai kanalai"</item>
diff --git a/res/values-lt/rating_system_strings.xml b/res/values-lt/rating_system_strings.xml
index d4c396a3..ec5127f1 100644
--- a/res/values-lt/rating_system_strings.xml
+++ b/res/values-lt/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Programose gali būti jaunesnėms nei 15 metų auditorijoms netinkamo turinio ir jas turėtų įvertinti tėvai."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Programose gali būti jaunesnėms nei 19 metų auditorijoms netinkamo turinio, todėl jos nėra tinkamos jaunesniems nei 19 metų jaunuoliams."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Dviprasmiškas dialogas"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Nemandagi kalba"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Seksualinis turinys"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index a66d6451..9f198d30 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -148,12 +148,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Atvirojo šaltinio licencijos"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Atvirojo šaltinio licencijos"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Versija"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Kūrėjo parinktys"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Įgalinti USB TV imtuvą"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Kad galėtumėte girdėti USB TV imtuvo garsą, jūsų TV turi būti palaikomas AC3 perdavimas."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 garso geba"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Jūsų TV palaikomas AC3 perdavimas."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Jūsų TV nepalaikomas AC3 perdavimas."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Padėti tobulinti „Live TV“"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Bendrinkite anoniminius naudojimo ir diagnostikos duomenis su „Google“, kad galėtume patobulinti Tiesioginius kanalus ir apsaugoti nuo problemų, pvz., užstrigimo."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Jei norite žiūrėti šį kanalą, paspauskite „Tinkamas“ ir įveskite PIN kodą"</string>
@@ -165,9 +159,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Programa užblokuota"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Ši programa įvertinta kaip <xliff:g id="RATING">%1$s</xliff:g>."</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Tik garso įrašas"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Silpnas signalas"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Nėra interneto ryšio"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Nėra pavadinimo"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Kanalas užblokuotas"</string>
- <string name="episode_format" msgid="4881195874563241096">"<xliff:g id="SEASON_NUMBER">%1$d</xliff:g> sezonas: <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> serija „<xliff:g id="EPISODE_TITLE">%3$s</xliff:g>“"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Nauji"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Šaltiniai"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -196,7 +191,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Derinimas nepavyko"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Nerasta jokių programų šiam veiksmui apdoroti."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Visi šaltinio kanalai yra paslėpti.\nPasirinkite bent vieną norimą žiūrėti kanalą."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Negalimas dėl silpno vaizdo įrašo signalo"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Vaizdo įrašas netikėtai negalimas"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"GRĮŽIMO klavišas skirtas prijungtam įrenginiui. Paspauskite PAGRINDINIO PUSLAPIO mygtuką, kad išeitumėte."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Programa „Live TV“ nepalaikoma šiame įrenginyje, nes jame veikia „Lollipop“ versijos „Android“."</string>
diff --git a/res/values-lv/arrays.xml b/res/values-lv/arrays.xml
index 4e074e39..902751c3 100644
--- a/res/values-lv/arrays.xml
+++ b/res/values-lv/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Pilns"</item>
<item msgid="8568284598210500589">"Tālummaiņa"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Visi kanāli"</item>
- <item msgid="928298872841713530">"Ģimenei/bērniem"</item>
- <item msgid="2751606947569857164">"Sports"</item>
- <item msgid="7345749789651321496">"Iepirkšanās"</item>
- <item msgid="167201149441442173">"Filmas"</item>
- <item msgid="525966731464264290">"Komēdijas"</item>
- <item msgid="6096710741527327836">"Ceļojumi"</item>
- <item msgid="2851882187117833883">"Drāmas"</item>
- <item msgid="78492781188719038">"Izglītība"</item>
- <item msgid="7221999662426308394">"Dzīvnieki/daba"</item>
- <item msgid="375300513250925001">"Ziņas"</item>
- <item msgid="7746320336582330410">"Spēles"</item>
- <item msgid="1255741860568329178">"Māksla"</item>
- <item msgid="7603949681065702867">"Izklaide"</item>
- <item msgid="4453821994746804366">"Dzīvesveids"</item>
- <item msgid="3488534597567932843">"Mūzika"</item>
- <item msgid="7452153120614274095">"Svarīgākais"</item>
- <item msgid="8215762047341133299">"Tehnoloģijas/zinātne"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Visi kanāli"</item>
+ <item msgid="6897460857821394118">"Ģimenei/bērniem"</item>
+ <item msgid="551257741825778215">"Sports"</item>
+ <item msgid="452133796804325879">"Iepirkšanās"</item>
+ <item msgid="3296058637230163031">"Filmas"</item>
+ <item msgid="1054540282883891201">"Komēdijas"</item>
+ <item msgid="7900158429062595471">"Ceļojumi"</item>
+ <item msgid="3768998587825611787">"Drāmas"</item>
+ <item msgid="8340620094959282881">"Izglītība"</item>
+ <item msgid="7396447839483867269">"Dzīvnieki/daba"</item>
+ <item msgid="4738043455148062673">"Ziņas"</item>
+ <item msgid="7405041316051047427">"Spēles"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Visi kanāli"</item>
+ <item msgid="7909003973960375395">"Ģimenei/bērniem"</item>
+ <item msgid="3185279732911635789">"Sports"</item>
+ <item msgid="4704858492065325964">"Iepirkšanās"</item>
+ <item msgid="6083795019290250078">"Filmas"</item>
+ <item msgid="8302638329222449550">"Komēdijas"</item>
+ <item msgid="3803709976021475052">"Ceļojumi"</item>
+ <item msgid="8116747365234169059">"Drāmas"</item>
+ <item msgid="7356447541595315913">"Izglītība"</item>
+ <item msgid="7511135485827589547">"Dzīvnieki/daba"</item>
+ <item msgid="6961248112238009967">"Ziņas"</item>
+ <item msgid="6484685553679698447">"Spēles"</item>
+ <item msgid="2737158328243183190">"Māksla"</item>
+ <item msgid="6577176952650166615">"Izklaide"</item>
+ <item msgid="7886693831871777617">"Dzīvesveids"</item>
+ <item msgid="8145832312485577062">"Mūzika"</item>
+ <item msgid="1345789204804308580">"Svarīgākais"</item>
+ <item msgid="2736680312770771994">"Tehnoloģijas/zinātne"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Dzīvie kanāli"</item>
diff --git a/res/values-lv/rating_system_strings.xml b/res/values-lv/rating_system_strings.xml
index 8c8def23..e4a3959b 100644
--- a/res/values-lv/rating_system_strings.xml
+++ b/res/values-lv/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Programmas var ietvert materiālu, kas nav piemērots personām līdz 15 gadu vecumam, tādējādi bērni un pusaudži drīkst tās lietot pēc vecāku ieskatiem."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Programmas var ietvert materiālu, kas nav piemērots personām līdz 19 gadu vecumam, tādējādi programmas nav atbilstošas pusaudžiem, kas nav sasnieguši 19 gadu vecumu."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Divdomīgs dialogs"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Rupja valoda"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Seksuāla rakstura saturs"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 2d610e24..affcd230 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -146,12 +146,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Atklātā pirmkoda licences"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Atklātā pirmkoda licences"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Versija"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Izstrādātājiem paredzētās opcijas"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Iespējot USB TV kanālu meklētāju"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Jūsu TV ir jābūt atbalstītai AC3 tālāknodošanai, lai varētu dzirdēt USB TV kanālu meklētāja skaņu."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 audio atbalsts"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Jūsu TV tiek atbalstīta AC3 tālāknodošana."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Jūsu TV netiek atbalstīta AC3 tālāknodošana."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Palīdzēt uzlabot tiešraides kanālus"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Kopīgojiet anonīmus lietojuma un diagnostikas datus ar uzņēmumu Google, lai mēs varētu uzlabot lietotni Dzīvie kanāli un novērst tādas problēmas kā avarēšana vai lēna reaģēšana."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Lai skatītos šo kanālu, nospiediet pa labi vērsto bultiņu un ievadiet PIN kodu."</string>
@@ -163,9 +157,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Programma ir bloķēta."</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Šī programma ir novērtēta kā “<xliff:g id="RATING">%1$s</xliff:g>”."</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Tikai audio"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Vājš signāls"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Nav interneta savienojuma."</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Nav nosaukuma"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Kanāls bloķēts"</string>
- <string name="episode_format" msgid="4881195874563241096">"<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>. sezona, <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g>. sērija “<xliff:g id="EPISODE_TITLE">%3$s</xliff:g>”"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Jauni"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Avoti"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -193,7 +188,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Kanālu meklēšana neizdevās."</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Netika atrasta neviena lietotne šīs darbības veikšanai."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Visi avota kanāli ir paslēpti.\nAtlasiet vismaz vienu kanālu, ko skatīties."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Nav pieejams vāja video signāla dēļ."</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Videoklips negaidīti nav pieejams."</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Poga ATPAKAĻ ir paredzēta pievienotajai ierīcei. Lai izietu, nospiediet pogu SĀKUMS."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Šajā ierīcē, kurā instalēta operētājsistēma Android Lollipop, netiek atbalstīta lietotne Tiešraides kanāli."</string>
diff --git a/res/values-mk-rMK/arrays.xml b/res/values-mk-rMK/arrays.xml
index b1ae7467..007b18b1 100644
--- a/res/values-mk-rMK/arrays.xml
+++ b/res/values-mk-rMK/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Цел"</item>
<item msgid="8568284598210500589">"Зум"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Сите канали"</item>
- <item msgid="928298872841713530">"Семејство/деца"</item>
- <item msgid="2751606947569857164">"Спорт"</item>
- <item msgid="7345749789651321496">"Пазарење"</item>
- <item msgid="167201149441442173">"Филмови"</item>
- <item msgid="525966731464264290">"Комедија"</item>
- <item msgid="6096710741527327836">"Патување"</item>
- <item msgid="2851882187117833883">"Драма"</item>
- <item msgid="78492781188719038">"Образование"</item>
- <item msgid="7221999662426308394">"Животни/див свет"</item>
- <item msgid="375300513250925001">"Вести"</item>
- <item msgid="7746320336582330410">"Игри"</item>
- <item msgid="1255741860568329178">"Уметности"</item>
- <item msgid="7603949681065702867">"Забава"</item>
- <item msgid="4453821994746804366">"Стил на живеење"</item>
- <item msgid="3488534597567932843">"Музика"</item>
- <item msgid="7452153120614274095">"Главно"</item>
- <item msgid="8215762047341133299">"Техника/наука"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Сите канали"</item>
+ <item msgid="6897460857821394118">"Семејство/деца"</item>
+ <item msgid="551257741825778215">"Спорт"</item>
+ <item msgid="452133796804325879">"Пазарење"</item>
+ <item msgid="3296058637230163031">"Филмови"</item>
+ <item msgid="1054540282883891201">"Комедија"</item>
+ <item msgid="7900158429062595471">"Патување"</item>
+ <item msgid="3768998587825611787">"Драма"</item>
+ <item msgid="8340620094959282881">"Образование"</item>
+ <item msgid="7396447839483867269">"Животни/див свет"</item>
+ <item msgid="4738043455148062673">"Вести"</item>
+ <item msgid="7405041316051047427">"Игри"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Сите канали"</item>
+ <item msgid="7909003973960375395">"Семејство/деца"</item>
+ <item msgid="3185279732911635789">"Спорт"</item>
+ <item msgid="4704858492065325964">"Пазарење"</item>
+ <item msgid="6083795019290250078">"Филмови"</item>
+ <item msgid="8302638329222449550">"Комедија"</item>
+ <item msgid="3803709976021475052">"Патување"</item>
+ <item msgid="8116747365234169059">"Драма"</item>
+ <item msgid="7356447541595315913">"Образование"</item>
+ <item msgid="7511135485827589547">"Животни/див свет"</item>
+ <item msgid="6961248112238009967">"Вести"</item>
+ <item msgid="6484685553679698447">"Игри"</item>
+ <item msgid="2737158328243183190">"Уметности"</item>
+ <item msgid="6577176952650166615">"Забава"</item>
+ <item msgid="7886693831871777617">"Стил на живеење"</item>
+ <item msgid="8145832312485577062">"Музика"</item>
+ <item msgid="1345789204804308580">"Главно"</item>
+ <item msgid="2736680312770771994">"Техника/наука"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"ТВ канали во живо"</item>
diff --git a/res/values-mk-rMK/rating_system_strings.xml b/res/values-mk-rMK/rating_system_strings.xml
index c583f26c..36cd59d6 100644
--- a/res/values-mk-rMK/rating_system_strings.xml
+++ b/res/values-mk-rMK/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Програмите може да содржат материјали несоодветни за публика на возраст под 15 години и затоа треба да се користи родителски надзор."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Програмите може да содржат материјали несоодветни за публика на возраст под 19 години и затоа не се соодветни за таквата публика."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Сугестивен дијалог"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Непристоен говор"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Сексуална содржина"</string>
diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml
index 4a4550c9..db0f4c5d 100644
--- a/res/values-mk-rMK/strings.xml
+++ b/res/values-mk-rMK/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Лиценци за софтвер со отворен код"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Лиценци за отворен код"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Верзија"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Опции за програмери"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Овозможете TV приемник преку УСБ"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"За да го слушате звукот на TV приемникот преку УСБ, вашиот TV треба да поддржува премин AC3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Способност за AC3-аудио"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Вашиот TV поддржува премин AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Вашиот TV не поддржува премин AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Помогнете да се подобрат каналите во живо"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Споделувајте анонимни употреба и дијагностика со Google за да можеме да ги подобриме ТВ каналите во живо и да спречиме проблеми како падови и замрзнување."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"За да го гледате овој канал, притиснете Во ред и внесете го вашиот ПИН"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Програмата е блокирана"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Оваа програма е оценета <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Само звук"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Слаб сигнал"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Нема интернет-врска"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Без наслов"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Каналот е блокиран"</string>
- <string name="episode_format" msgid="4881195874563241096">"С<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Еп. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Нов"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Извори"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Бирањето не успеа"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Не е пронајдена апликација што ќе се справи со ова дејство."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Сите изворни канали се сокриени.\nИзберете барем еден канал за да го гледате."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Недостапно поради слаб видеосигнал"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Видеото е неочекувано недостапно"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Копчето НАЗАД е за поврзаниот уред. Притиснете на копчето ПОЧЕТНА СТРАНИЦА за да излезете."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Каналите во живо не се поддржани на уредов со Android Lollipop."</string>
diff --git a/res/values-ml-rIN/arrays.xml b/res/values-ml-rIN/arrays.xml
index 383837e7..3c772200 100644
--- a/res/values-ml-rIN/arrays.xml
+++ b/res/values-ml-rIN/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"നിറഞ്ഞു"</item>
<item msgid="8568284598210500589">"സൂം ചെയ്യുക"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"എല്ലാ ചാനലുകളും"</item>
- <item msgid="928298872841713530">"കുടുംബാംഗങ്ങൾ/കുട്ടികൾ"</item>
- <item msgid="2751606947569857164">"സ്‌പോർട്സ്"</item>
- <item msgid="7345749789651321496">"ഷോപ്പിംഗ്"</item>
- <item msgid="167201149441442173">"സിനിമകൾ"</item>
- <item msgid="525966731464264290">"ഹാസ്യം"</item>
- <item msgid="6096710741527327836">"യാത്ര"</item>
- <item msgid="2851882187117833883">"നാടകം"</item>
- <item msgid="78492781188719038">"വിദ്യാഭ്യാസം"</item>
- <item msgid="7221999662426308394">"മൃഗങ്ങൾ/വൈൽഡ്‌‌ലൈഫ്"</item>
- <item msgid="375300513250925001">"വാർത്ത"</item>
- <item msgid="7746320336582330410">"ഗെയിമിംഗ്"</item>
- <item msgid="1255741860568329178">"കലകള്‍"</item>
- <item msgid="7603949681065702867">"വിനോദം"</item>
- <item msgid="4453821994746804366">"ജീവിതശൈലി"</item>
- <item msgid="3488534597567932843">"സംഗീതം"</item>
- <item msgid="7452153120614274095">"പ്രീമിയർ"</item>
- <item msgid="8215762047341133299">"സാങ്കേതികവിദ്യ/ശാസ്‌ത്രം"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"എല്ലാ ചാനലുകളും"</item>
+ <item msgid="6897460857821394118">"കുടുംബം/കുട്ടികൾ"</item>
+ <item msgid="551257741825778215">"സ്‌പോർട്സ്"</item>
+ <item msgid="452133796804325879">"ഷോപ്പിംഗ്"</item>
+ <item msgid="3296058637230163031">"സിനിമകൾ"</item>
+ <item msgid="1054540282883891201">"തമാശ"</item>
+ <item msgid="7900158429062595471">"യാത്ര"</item>
+ <item msgid="3768998587825611787">"നാടകം"</item>
+ <item msgid="8340620094959282881">"വിദ്യാഭ്യാസം"</item>
+ <item msgid="7396447839483867269">"മൃഗങ്ങൾ/വൈൽഡ്‌‌ലൈഫ്"</item>
+ <item msgid="4738043455148062673">"വാർത്ത"</item>
+ <item msgid="7405041316051047427">"ഗെയിമിംഗ്"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"എല്ലാ ചാനലുകളും"</item>
+ <item msgid="7909003973960375395">"കുടുംബം/കുട്ടികൾ"</item>
+ <item msgid="3185279732911635789">"സ്‌പോർട്സ്"</item>
+ <item msgid="4704858492065325964">"ഷോപ്പിംഗ്"</item>
+ <item msgid="6083795019290250078">"സിനിമകൾ"</item>
+ <item msgid="8302638329222449550">"തമാശ"</item>
+ <item msgid="3803709976021475052">"യാത്ര"</item>
+ <item msgid="8116747365234169059">"നാടകം"</item>
+ <item msgid="7356447541595315913">"വിദ്യാഭ്യാസം"</item>
+ <item msgid="7511135485827589547">"മൃഗങ്ങൾ/വൈൽഡ്‌‌ലൈഫ്"</item>
+ <item msgid="6961248112238009967">"വാർത്ത"</item>
+ <item msgid="6484685553679698447">"ഗെയിമിംഗ്"</item>
+ <item msgid="2737158328243183190">"കലകൾ"</item>
+ <item msgid="6577176952650166615">"വിനോദം"</item>
+ <item msgid="7886693831871777617">"ജീവിതശൈലി"</item>
+ <item msgid="8145832312485577062">"സംഗീതം"</item>
+ <item msgid="1345789204804308580">"പ്രീമിയർ"</item>
+ <item msgid="2736680312770771994">"സാങ്കേതികം/ശാസ്‌ത്രം"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"തത്സമയ ചാനലുകൾ"</item>
diff --git a/res/values-ml-rIN/rating_system_strings.xml b/res/values-ml-rIN/rating_system_strings.xml
index 910a5e74..15e3a612 100644
--- a/res/values-ml-rIN/rating_system_strings.xml
+++ b/res/values-ml-rIN/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"പ്രോഗ്രാമുകളിൽ 15 വയസ്സിൽ താഴെയുള്ള പ്രേക്ഷകർക്ക് ഉചിതമായേക്കാത്ത ഉള്ളടക്കം അടങ്ങിയേക്കാമെന്നതിനാൽ അവർക്കായി രക്ഷാകർതൃ വിവേചനാധികാരം ഉപയോഗിക്കേണ്ടതാണ്."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"പ്രോഗ്രാമുകളിൽ 19 വയസ്സിൽ താഴെയുള്ള പ്രേക്ഷകർക്ക് ഉചിതമായേക്കാത്ത ഉള്ളടക്കം അടങ്ങിയേക്കാമെന്നതിനാൽ 19 വയസ്സിൽ താഴെയുള്ള പ്രായക്കാർക്ക് ഇവ അനുയോജ്യമല്ല."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"ലൈംഗികസ്‌പഷ്‌ടമായ സംഭാഷണം"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"മോശമായ ഭാഷ"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"ലൈംഗിക ഉള്ളടക്കം"</string>
diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml
index b23fe17e..3289f763 100644
--- a/res/values-ml-rIN/strings.xml
+++ b/res/values-ml-rIN/strings.xml
@@ -142,12 +142,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"ഓപ്പൺ സോഴ്‌സ് ലൈസൻസ്"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"ഓപ്പൺ സോഴ്‌സ് ലൈസൻസുകൾ"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"പതിപ്പ്"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"ഡെവലപ്പര്‍ ഓ‌പ്ഷനുകൾ"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"USB ടിവി ട്യൂണർ പ്രവർത്തനക്ഷമമാക്കുക"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"USB ടിവി ട്യൂണറിന്റെ ശബ്ദം കേൾക്കുന്നതിന്, AC3 പാസ്‌ത്രൂവിനെ നിങ്ങളുടെ ടിവി പിന്തുണയ്ക്കേണ്ടതുണ്ട്."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 ഓഡിയോ ശേഷി"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"AC3 പാസ്‌ത്രൂവിനെ നിങ്ങളുടെ ടിവി പിന്തുണയ്ക്കുന്നു."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"AC3 പാസ്‌ത്രൂവിനെ നിങ്ങളുടെ ടിവി പിന്തുണയ്ക്കുന്നില്ല."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"തത്സമയ ചാനലുകൾ മെച്ചപ്പെടുത്താൻ സഹായിക്കുക"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"പേരുവിവരങ്ങളില്ലാത്ത ഉപയോഗ വിവരങ്ങളും പ്രശ്നനിർണ്ണയ വിവരങ്ങളും Google-മായി പങ്കിടുക, അതുവഴി ഞങ്ങൾക്ക് തത്സമയ ചാനലുകൾ കൂടുതൽ മെച്ചപ്പെടുത്താനും ക്രാഷിംഗും ഫ്രീസിംഗും പോലുള്ള പ്രശ്നങ്ങൾ തടയാനും കഴിയും."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"ഈ ചാനൽ കാണുന്നതിന് വലതുവശത്ത് അമർത്തിക്കൊണ്ട് നിങ്ങളുടെ പിൻ നൽകുക"</string>
@@ -159,9 +153,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"പ്രോഗ്രാം തടഞ്ഞു"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"ഈ പ്രോഗ്രാമിനെ <xliff:g id="RATING">%1$s</xliff:g> എന്ന് റേറ്റുചെയ്‌തു"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"ഓഡിയോ മാത്രം"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"സിഗ്‌നൽ ദുർബലമാണ്"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"ഇന്റർനെറ്റ് കണക്ഷനില്ല"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"ശീർഷകമൊന്നുമില്ല"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"ചാനൽ തടഞ്ഞിരിക്കുന്നു"</string>
- <string name="episode_format" msgid="4881195874563241096">"സീസൺ<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: ഭാഗം. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"പുതിയത്"</string>
<string name="setup_category_done" msgid="4750902502852212319">"ഉറവിടങ്ങൾ"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -188,7 +183,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"ട്യൂൺ ചെയ്യൽ പരാജയപ്പെട്ടു"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"ഈ പ്രവർത്തനം കൈകാര്യം ചെയ്യാൻ ആപ്പുകളൊന്നും കണ്ടെത്തിയില്ല."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"എല്ലാ ഉറവിട ചാനലുകളും മറച്ചിരിക്കുന്നു.\nകാണാനായി ഒരു ചാനലെങ്കിലും തിരഞ്ഞെടുക്കുക."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"ദുർബലമായ വീഡിയോ സി‌ഗ്‌നൽ കാരണം ലഭ്യമല്ല"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"അപ്രതീക്ഷിതമായി വീഡിയോ ലഭ്യമല്ല"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"കണക്‌റ്റു‌ചെയ്‌തിരിക്കുന്ന ഉപകരണത്തിനുള്ളതാണ് മടങ്ങുക എന്ന കീ. പുറത്തുകടക്കാൻ ഹോം ബട്ടൺ അമർത്തുക."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Android Lollipop ഉള്ള ഈ ഉപകരണത്തിൽ തത്സമയ ചാനലുകൾക്ക് പിന്തുണയില്ല."</string>
diff --git a/res/values-mn-rMN/arrays.xml b/res/values-mn-rMN/arrays.xml
index 3d0b16ba..952ee618 100644
--- a/res/values-mn-rMN/arrays.xml
+++ b/res/values-mn-rMN/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Дүүрэн"</item>
<item msgid="8568284598210500589">"Томруулах"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Бүх суваг"</item>
- <item msgid="928298872841713530">"Гэр бүл/Хүүхдүүд"</item>
- <item msgid="2751606947569857164">"Спорт"</item>
- <item msgid="7345749789651321496">"Худалдаа"</item>
- <item msgid="167201149441442173">"Кино"</item>
- <item msgid="525966731464264290">"Инээдмийн"</item>
- <item msgid="6096710741527327836">"Аялал"</item>
- <item msgid="2851882187117833883">"Драма"</item>
- <item msgid="78492781188719038">"Боловсрол"</item>
- <item msgid="7221999662426308394">"Амьтад/Зэрлэг байгаль"</item>
- <item msgid="375300513250925001">"Мэдээ"</item>
- <item msgid="7746320336582330410">"Тоглоом"</item>
- <item msgid="1255741860568329178">"Урлаг"</item>
- <item msgid="7603949681065702867">"Цэнгээн"</item>
- <item msgid="4453821994746804366">"Амьдралын хэв маяг"</item>
- <item msgid="3488534597567932843">"Дуу хөгжим"</item>
- <item msgid="7452153120614274095">"Эхний"</item>
- <item msgid="8215762047341133299">"Технологи/ШУ"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Бүх суваг"</item>
+ <item msgid="6897460857821394118">"Гэр бүл/Хүүхдүүд"</item>
+ <item msgid="551257741825778215">"Спорт"</item>
+ <item msgid="452133796804325879">"Худалдаа"</item>
+ <item msgid="3296058637230163031">"Кино"</item>
+ <item msgid="1054540282883891201">"Инээдмийн"</item>
+ <item msgid="7900158429062595471">"Аялал"</item>
+ <item msgid="3768998587825611787">"Драма"</item>
+ <item msgid="8340620094959282881">"Боловсрол"</item>
+ <item msgid="7396447839483867269">"Амьтад/Зэрлэг байгаль"</item>
+ <item msgid="4738043455148062673">"Мэдээ"</item>
+ <item msgid="7405041316051047427">"Тоглоом"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Бүх суваг"</item>
+ <item msgid="7909003973960375395">"Гэр бүл/Хүүхдүүд"</item>
+ <item msgid="3185279732911635789">"Спорт"</item>
+ <item msgid="4704858492065325964">"Дэлгүүр"</item>
+ <item msgid="6083795019290250078">"Кино"</item>
+ <item msgid="8302638329222449550">"Инээдмийн"</item>
+ <item msgid="3803709976021475052">"Аялал"</item>
+ <item msgid="8116747365234169059">"Драма"</item>
+ <item msgid="7356447541595315913">"Боловсрол"</item>
+ <item msgid="7511135485827589547">"Амьтад/Зэрлэг байгаль"</item>
+ <item msgid="6961248112238009967">"Мэдээ"</item>
+ <item msgid="6484685553679698447">"Тоглоом"</item>
+ <item msgid="2737158328243183190">"Урлаг"</item>
+ <item msgid="6577176952650166615">"Энтертэйнмент"</item>
+ <item msgid="7886693831871777617">"Амьдралын хэв маяг"</item>
+ <item msgid="8145832312485577062">"Дуу хөгжим"</item>
+ <item msgid="1345789204804308580">"Эхний"</item>
+ <item msgid="2736680312770771994">"Технологи/шинжлэх ухаан"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Шууд суваг"</item>
diff --git a/res/values-mn-rMN/rating_system_strings.xml b/res/values-mn-rMN/rating_system_strings.xml
index 09052bf9..fc17077e 100644
--- a/res/values-mn-rMN/rating_system_strings.xml
+++ b/res/values-mn-rMN/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Хөтөлбөрт 15-с доош насныханд тохиромжгүй материал агуулсан тул эцэг эхийн хяналт шаардлагатай."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Хөтөлбөрт 19-с дээш насныханд зориулсан материал агуулсан тул 19-с доош насныхан үзэхэд тохиромжгүй."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Уруу татсан яриа"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Бүдүүлэг яриа"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Секс контент"</string>
diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml
index 8b636963..5fd5b644 100644
--- a/res/values-mn-rMN/strings.xml
+++ b/res/values-mn-rMN/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Нээлттэй эхийн лиценз"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Нээлттэй эхийн лиценз"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Хувилбар"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Хөгжүүлэгчийн тохиргоо"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"USB TV тохируулагчийг идэвхжүүлэх"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"USB ТV тохируулагчийн дууг сонсохын тулд таны ТV AC3 дамжуулалтыг дэмжих хэрэгтэй."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 аудио багтаамж"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Таны TV AC3 дамжуулалтыг дэмждэг."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Таны ТV AC3 дамжуулалтыг дэмждэггүй."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Шууд сувгийг сайжруулахад туслах"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Та хэрэглээ, оношлогооны мэдээг Google-д автоматаар хуваалцсанаар бид шууд сувгийг гэмтэх, зогсохоос сэргийлж, сайжруулах болно."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Энэ сувгийг үзэхийн тулд Баруун товчийг дараад өөрийн PIN-г оруулна уу"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Хөтөлбөр хориглогдсон"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Энэ хөтөлбөрийг <xliff:g id="RATING">%1$s</xliff:g>-ээр үнэлэгдсэн байна"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Зөвхөн аудио"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Дохио муу"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Интернэт холболт байхгүй"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Гарчиггүй"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Сувгийг хориглосон"</string>
- <string name="episode_format" msgid="4881195874563241096">"Бүлэг<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Анги. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Шинэ"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Эх сурвалж"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Дуу тааруулж чадсангүй."</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Энэ үйлдлийг гүйцэтгэх апп олдсонгүй."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Бүх эх сурвалж сувгуудыг нууцалсан.\nҮзэхийн тулд дор хаяж нэг суваг сонгоно уу."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Видео сигналь сул байгаа учир үзэх боломжгүй байна."</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Энэ видеог үзэх боломжгүй болсон."</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Буцах товчийг холбогдсон төхөөрөмжүүдэд ашиглана. Гарахын тулд Нүүр товчийг дарна уу."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Android Lollipop-той төхөөрөмжид шууд сувгийг дэмжихгүй."</string>
diff --git a/res/values-mr-rIN/arrays.xml b/res/values-mr-rIN/arrays.xml
index a91434f4..4272a4c2 100644
--- a/res/values-mr-rIN/arrays.xml
+++ b/res/values-mr-rIN/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"पूर्ण"</item>
<item msgid="8568284598210500589">"झूम करा"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"सर्व चॅनेल"</item>
- <item msgid="928298872841713530">"कुटुंब/मुले"</item>
- <item msgid="2751606947569857164">"क्रीडा"</item>
- <item msgid="7345749789651321496">"खरेदी"</item>
- <item msgid="167201149441442173">"चित्रपट"</item>
- <item msgid="525966731464264290">"विनोदी"</item>
- <item msgid="6096710741527327836">"प्रवास"</item>
- <item msgid="2851882187117833883">"नाटक"</item>
- <item msgid="78492781188719038">"शिक्षण"</item>
- <item msgid="7221999662426308394">"प्राणी/वन्यजीवन"</item>
- <item msgid="375300513250925001">"बातम्या"</item>
- <item msgid="7746320336582330410">"गेमिंग"</item>
- <item msgid="1255741860568329178">"कला"</item>
- <item msgid="7603949681065702867">"मनोरंजन"</item>
- <item msgid="4453821994746804366">"जीवनशैली"</item>
- <item msgid="3488534597567932843">"संगीत"</item>
- <item msgid="7452153120614274095">"प्रमुख"</item>
- <item msgid="8215762047341133299">"तंत्रज्ञान/विज्ञान"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"सर्व चॅनेल"</item>
+ <item msgid="6897460857821394118">"कुटुंब/मुले"</item>
+ <item msgid="551257741825778215">"क्रिडा"</item>
+ <item msgid="452133796804325879">"खरेदी"</item>
+ <item msgid="3296058637230163031">"चित्रपट"</item>
+ <item msgid="1054540282883891201">"विनोदी"</item>
+ <item msgid="7900158429062595471">"प्रवास"</item>
+ <item msgid="3768998587825611787">"नाटक"</item>
+ <item msgid="8340620094959282881">"शिक्षण"</item>
+ <item msgid="7396447839483867269">"प्राणी/वन्यजीवन"</item>
+ <item msgid="4738043455148062673">"बातम्या"</item>
+ <item msgid="7405041316051047427">"गेमिंग"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"सर्व चॅनेल"</item>
+ <item msgid="7909003973960375395">"कुटुंब/मुले"</item>
+ <item msgid="3185279732911635789">"क्रिडा"</item>
+ <item msgid="4704858492065325964">"खरेदी"</item>
+ <item msgid="6083795019290250078">"चित्रपट"</item>
+ <item msgid="8302638329222449550">"विनोदी"</item>
+ <item msgid="3803709976021475052">"प्रवास"</item>
+ <item msgid="8116747365234169059">"नाटक"</item>
+ <item msgid="7356447541595315913">"शिक्षण"</item>
+ <item msgid="7511135485827589547">"प्राणी/वन्यजीवन"</item>
+ <item msgid="6961248112238009967">"बातम्या"</item>
+ <item msgid="6484685553679698447">"गेमिंग"</item>
+ <item msgid="2737158328243183190">"कला"</item>
+ <item msgid="6577176952650166615">"मनोरंजन"</item>
+ <item msgid="7886693831871777617">"जीवनशैली"</item>
+ <item msgid="8145832312485577062">"संगीत"</item>
+ <item msgid="1345789204804308580">"प्रमुख"</item>
+ <item msgid="2736680312770771994">"तंत्रज्ञान/विज्ञान"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"थेट चॅनेल"</item>
diff --git a/res/values-mr-rIN/rating_system_strings.xml b/res/values-mr-rIN/rating_system_strings.xml
index e23e824d..14b98174 100644
--- a/res/values-mr-rIN/rating_system_strings.xml
+++ b/res/values-mr-rIN/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"प्रोग्राममध्ये 15 वर्षांखालील प्रेक्षकांसाठी अयोग्य असलेली सामग्री असू शकते आणि त्यामुळे त्यासाठी पालकांची विवेकबुद्धी वापरली जावी."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"प्रोग्राममध्ये 19 वर्षांखालील प्रेक्षकांसाठी अयोग्य असलेली सामग्री कदाचित असू शकते आणि त्यामुळे 19 वर्षांखालील प्रेक्षकांसाठी पालकांची विवेकबुद्धी वापरली जावी."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"अश्लील संवाद"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"असभ्य भाषा"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"लैंगिक सामग्री"</string>
diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml
index e804990e..2ddbeec9 100644
--- a/res/values-mr-rIN/strings.xml
+++ b/res/values-mr-rIN/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"मुक्त स्त्रोत परवाने"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"मुक्त स्त्रोत परवाने"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"आवृत्ती"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"विकासक पर्याय"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"USB टीव्ही ट्यूनर सक्षम करा"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"USB टीव्‍ही ट्यूनरचा ध्वनी ऐकण्‍यासाठी, आपल्या टीव्हीने AC3 पासथ्रूला समर्थन द्यावे."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 ऑडिओ क्षमता"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"आपला टीव्ही AC3 पासथ्रूला समर्थन देतो."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"आपला टीव्ही AC3 पासथ्रूला समर्थन देत नाही."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"थेट चॅनेल सुधारण्यात मदत करा"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Google सह अनामित वापर आणि निदान डेटा सामायिक करा जेणेकरून आम्‍ही थेट चॅनेल आणखी चांगले बनवू शकू आणि क्रॅश होणे आणि गोठविणे यासारख्‍या समस्या प्रतिबंधित करू शकू."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"हे चॅनेल पाहण्यासाठी, उजवे दाबा आणि आपला पिन प्रविष्‍ट करा"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"कार्यक्रम अवरोधित केला आहे"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"हा कार्यक्रम <xliff:g id="RATING">%1$s</xliff:g> रेट केला आहे"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"फक्त ऑडिओ"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"खराब सिग्नल"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"कोणतेही इंटरनेट कनेक्शन नाही"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"शीर्षक नाही"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"चॅनेल अवरोधित केले"</string>
- <string name="episode_format" msgid="4881195874563241096">"S<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: भाग. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"नवीन"</string>
<string name="setup_category_done" msgid="4750902502852212319">"स्त्रोत"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"ट्यून अयशस्वी झाले"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"ही क्रिया हाताळण्यासाठी कोणताही अ‍ॅप आढळला नाही."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"सर्व स्त्रोत चॅनेल लपविले आहेत.\nपाहण्‍यासाठी कमीतकमी एक चॅनेल निवडा."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"कमकुवत व्हिडिओ सिग्नलमुळे अनुपलब्ध"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"व्‍हिडिओ अनपेक्षितपणे अनुपलब्ध आहे"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"बॅक की कनेक्ट केलेल्या डिव्हाइससाठी आहे. बाहेर पडण्यासाठी मुख्यपृष्ठ बटण दाबा."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Android Lollipop सह थेट चॅनेल या डिव्हाइसवर समर्थित नाहीत."</string>
diff --git a/res/values-ms-rMY/arrays.xml b/res/values-ms-rMY/arrays.xml
index 16ccce64..9d285575 100644
--- a/res/values-ms-rMY/arrays.xml
+++ b/res/values-ms-rMY/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Penuh"</item>
<item msgid="8568284598210500589">"Zum"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Semua saluran"</item>
- <item msgid="928298872841713530">"Keluarga/Kanak-Kanak"</item>
- <item msgid="2751606947569857164">"Sukan"</item>
- <item msgid="7345749789651321496">"Beli-belah"</item>
- <item msgid="167201149441442173">"Filem"</item>
- <item msgid="525966731464264290">"Komedi"</item>
- <item msgid="6096710741527327836">"Pelancongan"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Pendidikan"</item>
- <item msgid="7221999662426308394">"Haiwan/Hidupan Liar"</item>
- <item msgid="375300513250925001">"Berita"</item>
- <item msgid="7746320336582330410">"Permainan"</item>
- <item msgid="1255741860568329178">"Seni"</item>
- <item msgid="7603949681065702867">"Hiburan"</item>
- <item msgid="4453821994746804366">"Gaya Hidup"</item>
- <item msgid="3488534597567932843">"Muzik"</item>
- <item msgid="7452153120614274095">"Perdana"</item>
- <item msgid="8215762047341133299">"Teknikal/Sains"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Semua saluran"</item>
+ <item msgid="6897460857821394118">"Keluarga/Kanak-Kanak"</item>
+ <item msgid="551257741825778215">"Sukan"</item>
+ <item msgid="452133796804325879">"Beli-belah"</item>
+ <item msgid="3296058637230163031">"Filem"</item>
+ <item msgid="1054540282883891201">"Komedi"</item>
+ <item msgid="7900158429062595471">"Pelancongan"</item>
+ <item msgid="3768998587825611787">"Drama"</item>
+ <item msgid="8340620094959282881">"Pendidikan"</item>
+ <item msgid="7396447839483867269">"Haiwan/Hidupan Liar"</item>
+ <item msgid="4738043455148062673">"Berita"</item>
+ <item msgid="7405041316051047427">"Permainan"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Semua saluran"</item>
+ <item msgid="7909003973960375395">"Keluarga/Kanak-Kanak"</item>
+ <item msgid="3185279732911635789">"Sukan"</item>
+ <item msgid="4704858492065325964">"Beli-belah"</item>
+ <item msgid="6083795019290250078">"Filem"</item>
+ <item msgid="8302638329222449550">"Komedi"</item>
+ <item msgid="3803709976021475052">"Pelancongan"</item>
+ <item msgid="8116747365234169059">"Drama"</item>
+ <item msgid="7356447541595315913">"Pendidikan"</item>
+ <item msgid="7511135485827589547">"Haiwan/Hidupan Liar"</item>
+ <item msgid="6961248112238009967">"Berita"</item>
+ <item msgid="6484685553679698447">"Permainan"</item>
+ <item msgid="2737158328243183190">"Seni"</item>
+ <item msgid="6577176952650166615">"Hiburan"</item>
+ <item msgid="7886693831871777617">"Gaya hidup"</item>
+ <item msgid="8145832312485577062">"Muzik"</item>
+ <item msgid="1345789204804308580">"Perdana"</item>
+ <item msgid="2736680312770771994">"Teknikal/Sains"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Saluran Langsung"</item>
diff --git a/res/values-ms-rMY/rating_system_strings.xml b/res/values-ms-rMY/rating_system_strings.xml
index 1cfeb1f3..92af90a3 100644
--- a/res/values-ms-rMY/rating_system_strings.xml
+++ b/res/values-ms-rMY/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Rancangan mungkin mengandungi bahan yang tidak sesuai untuk khalayak di bawah umur 15 tahun, maka budi bicara ibu bapa perlu digunakan."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Rancangan mungkin mengandungi bahan yang tidak sesuai untuk khalayak di bawah umur 19 tahun, maka rancangan itu tidak sesuai untuk golongan muda ini."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Dialog tidak senonoh"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Bahasa kasar"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Kandungan seksual"</string>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
index a21da27e..abcf09b1 100644
--- a/res/values-ms-rMY/strings.xml
+++ b/res/values-ms-rMY/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Lesen sumber terbuka"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Lesen sumber terbuka"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Versi"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Pilihan pembangun"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Dayakan penala TV USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"TV anda harus menyokong laluan AC3 untuk mendengar bunyi penala TV USB."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Keupayaan audio AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"TV anda menyokong laluan AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"TV anda tidak menyokong laluan AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Bantu meningkatkan Saluran Langsung"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Kongsi data penggunaan dan diagnostik awanama dengan Google supaya kami dapat menjadikan Saluran Langsung lebih baik dan menghalang isu seperti ranap dan pegun."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Untuk menonton saluran ini, tekan Kanan dan masukkan PIN anda"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Rancangan disekat"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Rancangan ini diberi rating <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Audio sahaja"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Isyarat lemah"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Tiada sambungan Internet"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Tiada tajuk"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Saluran disekat"</string>
- <string name="episode_format" msgid="4881195874563241096">"M<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Baharu"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Sumber"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Penalaan gagal"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Tiada apl ditemui untuk mengendalikan tindakan ini."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Semua saluran sumber disembunyikan.\nPilih sekurang-kurangnya satu saluran untuk ditonton."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Tidak tersedia disebabkan isyarat video lemah"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Video ini tidak tersedia tanpa dijangka"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Kekunci BACK adalah untuk peranti yang tersambung. Tekan butang HOME untuk keluar."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Saluran Langsung tidak disokong pada peranti ini dengan Android Lollipop."</string>
diff --git a/res/values-my-rMM/arrays.xml b/res/values-my-rMM/arrays.xml
index 384243da..944e116a 100644
--- a/res/values-my-rMM/arrays.xml
+++ b/res/values-my-rMM/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"အပြည့်"</item>
<item msgid="8568284598210500589">"ချဲ့ချုံ့ရန်"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"ချာနယ်များ အားလုံး"</item>
- <item msgid="928298872841713530">"မိသားစု/ကလေးများ"</item>
- <item msgid="2751606947569857164">"အားကစား"</item>
- <item msgid="7345749789651321496">"​ဈေးဝယ်ခြင်း"</item>
- <item msgid="167201149441442173">"ရုပ်ရှင်များ"</item>
- <item msgid="525966731464264290">"ဟာသ"</item>
- <item msgid="6096710741527327836">"ခရီးလှည့်မှု"</item>
- <item msgid="2851882187117833883">"ပြဇာတ်"</item>
- <item msgid="78492781188719038">"ပညာရေး"</item>
- <item msgid="7221999662426308394">"တိရိစ္ဆာန်/တောရိုင်း"</item>
- <item msgid="375300513250925001">"သတင်း"</item>
- <item msgid="7746320336582330410">"ဂိမ်းကစားခြင်း"</item>
- <item msgid="1255741860568329178">"အနုပညာများ"</item>
- <item msgid="7603949681065702867">"ဖြေဖျော်မှု"</item>
- <item msgid="4453821994746804366">"ဘဝပုံစံ"</item>
- <item msgid="3488534597567932843">"ဂီတ"</item>
- <item msgid="7452153120614274095">"ပွဲဦး"</item>
- <item msgid="8215762047341133299">"စက်မှု/သိပ္ပံ"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"ချန်နယ်များအားလုံး"</item>
+ <item msgid="6897460857821394118">"မိသားစု/ကလေးများ"</item>
+ <item msgid="551257741825778215">"အားကစား"</item>
+ <item msgid="452133796804325879">"​ဈေးဝယ်ခြင်း"</item>
+ <item msgid="3296058637230163031">"ရုပ်ရှင်များ"</item>
+ <item msgid="1054540282883891201">"ဟာသ"</item>
+ <item msgid="7900158429062595471">"ခရီးသွားခြင်း"</item>
+ <item msgid="3768998587825611787">"ဒရာမာ"</item>
+ <item msgid="8340620094959282881">"ပညာရေး"</item>
+ <item msgid="7396447839483867269">"တိရိစ္ဆာန်/တောရိုင်း"</item>
+ <item msgid="4738043455148062673">"News"</item>
+ <item msgid="7405041316051047427">"ဂိမ်းကစားခြင်း"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"ချန်နယ်များအားလုံး"</item>
+ <item msgid="7909003973960375395">"မိသားစု/ကလေးများ"</item>
+ <item msgid="3185279732911635789">"အားကစား"</item>
+ <item msgid="4704858492065325964">"​ဈေးဝယ်ခြင်း"</item>
+ <item msgid="6083795019290250078">"ရုပ်ရှင်များ"</item>
+ <item msgid="8302638329222449550">"ဟာသ"</item>
+ <item msgid="3803709976021475052">"ခရီးသွားခြင်း"</item>
+ <item msgid="8116747365234169059">"ဒရာမာ"</item>
+ <item msgid="7356447541595315913">"ပညာရေး"</item>
+ <item msgid="7511135485827589547">"တိရိစ္ဆာန်/တောရိုင်း"</item>
+ <item msgid="6961248112238009967">"News"</item>
+ <item msgid="6484685553679698447">"ဂိမ်းကစားခြင်း"</item>
+ <item msgid="2737158328243183190">"အနုပညာများ"</item>
+ <item msgid="6577176952650166615">"ဖျော်ဖြေမှု"</item>
+ <item msgid="7886693831871777617">"လူနေမှုဘဝပုံစံ"</item>
+ <item msgid="8145832312485577062">"သီချင်း"</item>
+ <item msgid="1345789204804308580">"ပရီးမီးယား"</item>
+ <item msgid="2736680312770771994">"စက်မှု/သိပ္ပံ"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"တိုက်ရိုက်လွှင့် ချန်နယ်များ"</item>
diff --git a/res/values-my-rMM/rating_system_strings.xml b/res/values-my-rMM/rating_system_strings.xml
index 51ce7a68..b2206835 100644
--- a/res/values-my-rMM/rating_system_strings.xml
+++ b/res/values-my-rMM/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"အစီအစဉ်များတွင် အသက် ၁၅ နှစ်အောက် ပရိသတ်များအတွက် မသင့်တော်သည့် အကြောင်းအရာများ ပါဝင်နိုင်သောကြောင့် ၎င်းတို့ကို မိဘအုပ်ထိန်းမှုအောက်တွင် ထားရှိသင့်ပါသည်။"</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"အစီအစဉ်များတွင် အသက် ၁၉ နှစ်အောက်ပရိသတ်များအတွက် မသင့်တော်သည့် အကြောင်းအရာများ ပါဝင်နိုင်သောကြောင့် အသက် ၁၉ နှစ်နှင့်အောက် လူငယ်များနှင့် ကိုက်ညီမှုမရှိပါ။"</string>
<string name="description_us_tv_d" msgid="12333789157204816">"အကြံပြုထားသည့် စကားပြောဆိုမှု"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"ကြမ်းတမ်းသော စကားလုံးများ"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"လိင်ဆက်ဆံမှု အကြောင်းအရာ"</string>
diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml
index a07bb709..13378d42 100644
--- a/res/values-my-rMM/strings.xml
+++ b/res/values-my-rMM/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"အခမဲ့အရင်းအမြစ်လိုင်စင်များ"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"အခမဲ့ ရင်းမြစ် လိုင်စင်များ"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"ဗားရှင်း"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"တီထွင်သူများရွေးစရာ"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"USB TV ချိန်ညှိကိရိယာကို ဖွင့်ပါ"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"USB TV ချိန်ညှိကိရိယာ၏ အသံကြားရန်၊ သင့် TV သည် AC3 passthrough ကိုထောက်ပံ့ရပါမည်။"</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 အသံ စွမ်းဆောင်နိုင်စွမ်း"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"သင့် TV သည် AC3 passthrough ကိုမထောက်ပံ့ပါ။"</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"သင့် TV သည် AC3 passthrough ကိုမထောက်ပံ့ပါ။"</string>
<string name="about_menu_improve" msgid="3712578027009311401">"တိုက်ရိုက်လွှင့်ချန်နယ်များ တိုးတက်အောင်ကူညီပါ"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"အသုံးပြုမှုနှင့် အမှားရှာဖွေမှုဒေတာများကို Google နှင့်မျှဝေခြင်းဖြင့် ပျက်စီးခြင်းနှင့် ရပ်တန့်သွားခြင်းကဲ့သို့သော ပြဿနာများကို ကာကွယ်ပြီး တိုက်ရိုက်လွှင့်နေသောချယ်နယ်များကို ပိုမိုကောင်းမွန်စေပါ။"</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"ဤချာနယ်ကို ကြည့်ရန်၊ ညာဘက် နှိပ်ပြီး PIN ရိုက်ထည့်ပါ"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"အစီအစဉ်ကို ပိတ်ဆို့ထား"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"ဤအစီအစဉ်သည် အဆင့် <xliff:g id="RATING">%1$s</xliff:g> ရှိ၏"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"အသံ သီးသန့်"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"လှိုင်းဆွဲအားနည်းသည်"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"အင်တာနက်ချိတ်ဆက်မှု မရှိပါ"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"ခေါင်းစဉ် မပါ"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"ချာနယ် ပိတ်ဆို့ထား"</string>
- <string name="episode_format" msgid="4881195874563241096">"ရာသီ<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: အပိုင်း − <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"အသစ်"</string>
<string name="setup_category_done" msgid="4750902502852212319">"အရင်းအမြစ်များ"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"ညှိမှု မအောင်မြင်ပါ"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"ဤလုပ်ဆောင်ချက်ကို ကိုင်တွယ်နိုင်သည့် အပလီကေးရှင်း မရှိပါ။"</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"ရင်းမြစ် ချာနယ် အားလုံးကို ဝှက်ထားသည်။ \n ကြည့်ရန် အနည်းဆုံး ချာနယ် တစ်ခုကို ရွေးပါ။"</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"ဗီဒီယိုအချက်ပြ အားနည်း၍ မရနိုင်ပါ။"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"ဗီဒီယို မမျှော်လင့်ဘဲ မရရှိနိုင်ပါ။"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"BACK ကီးသည် ချိတ်ဆက် စက်ကိရိယာ အတွက်ဖြစ်၏။ ထွက်ရန် HOME ခလုတ်ကို နှိပ်ပါ။"</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Android Lollipop ဗားရှင်းဖြင့်လည်ပတ်သော ဤကိရိယာတွင် တိုက်ရိုက်ထုတ်လွှင့်သောချန်နယ်လိုင်းများအား အသုံးမပြုနိင်ပါ။"</string>
diff --git a/res/values-nb/arrays.xml b/res/values-nb/arrays.xml
index 145dd560..14d83e84 100644
--- a/res/values-nb/arrays.xml
+++ b/res/values-nb/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Full"</item>
<item msgid="8568284598210500589">"Zoom"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Alle kanaler"</item>
- <item msgid="928298872841713530">"Familie/barn"</item>
- <item msgid="2751606947569857164">"Sport"</item>
- <item msgid="7345749789651321496">"Shopping"</item>
- <item msgid="167201149441442173">"Filmer"</item>
- <item msgid="525966731464264290">"Komedie"</item>
- <item msgid="6096710741527327836">"Reise"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Utdanning"</item>
- <item msgid="7221999662426308394">"Dyr/villmarksliv"</item>
- <item msgid="375300513250925001">"Nyheter"</item>
- <item msgid="7746320336582330410">"Spill"</item>
- <item msgid="1255741860568329178">"Kunst"</item>
- <item msgid="7603949681065702867">"Underholdning"</item>
- <item msgid="4453821994746804366">"Livsstil"</item>
- <item msgid="3488534597567932843">"Musikk"</item>
- <item msgid="7452153120614274095">"Premiere"</item>
- <item msgid="8215762047341133299">"Teknologi/vitenskap"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Alle kanaler"</item>
+ <item msgid="6897460857821394118">"Familie/barn"</item>
+ <item msgid="551257741825778215">"Sport"</item>
+ <item msgid="452133796804325879">"Shopping"</item>
+ <item msgid="3296058637230163031">"Filmer"</item>
+ <item msgid="1054540282883891201">"Komedie"</item>
+ <item msgid="7900158429062595471">"Reise"</item>
+ <item msgid="3768998587825611787">"Drama"</item>
+ <item msgid="8340620094959282881">"Utdanning"</item>
+ <item msgid="7396447839483867269">"Dyr/villmarksliv"</item>
+ <item msgid="4738043455148062673">"Nyheter"</item>
+ <item msgid="7405041316051047427">"Spill"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Alle kanaler"</item>
+ <item msgid="7909003973960375395">"Familie/barn"</item>
+ <item msgid="3185279732911635789">"Sport"</item>
+ <item msgid="4704858492065325964">"Shopping"</item>
+ <item msgid="6083795019290250078">"Filmer"</item>
+ <item msgid="8302638329222449550">"Komedie"</item>
+ <item msgid="3803709976021475052">"Reise"</item>
+ <item msgid="8116747365234169059">"Drama"</item>
+ <item msgid="7356447541595315913">"Utdanning"</item>
+ <item msgid="7511135485827589547">"Dyr/villmarksliv"</item>
+ <item msgid="6961248112238009967">"Nyheter"</item>
+ <item msgid="6484685553679698447">"Spill"</item>
+ <item msgid="2737158328243183190">"Kunst"</item>
+ <item msgid="6577176952650166615">"Underholdning"</item>
+ <item msgid="7886693831871777617">"Livsstil"</item>
+ <item msgid="8145832312485577062">"Musikk"</item>
+ <item msgid="1345789204804308580">"Premiere"</item>
+ <item msgid="2736680312770771994">"Teknologi/vitenskap"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Direkte-TV"</item>
diff --git a/res/values-nb/rating_system_strings.xml b/res/values-nb/rating_system_strings.xml
index fb2d8604..0b69ad23 100644
--- a/res/values-nb/rating_system_strings.xml
+++ b/res/values-nb/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Programmer kan inkludere innhold som er upassende for seere under 15 år. Foreldre bør bruke eget skjønn."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Programmer kan inkludere innhold som er upassende for seere under 19 år, og er derfor ikke passende for personer under 19 år."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Usømmelig dialog"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Grovt språk"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Seksuelt innhold"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index dd6123d1..f56156d8 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Lisenser for åpen kildekode"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Åpen kildekode-lisenser"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Versjon"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Utvikleralternativer"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Slå på USB-tuneren for TV"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"For å høre lyd via USB-tuneren for TV må TV-en din støtte AC3-gjennomgang."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3-lydfunksjonalitet"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"TV-en støtter AC3-gjennomgang."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"TV-en støtter ikke AC3-gjennomgang."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Bidra til å forbedre Live TV"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Del anonyme bruksdata og diagnostikkdata med Google sånn at vi kan gjøre Direkte-TV bedre og forhindre problemer (for eksempel når appen kræsjer eller fryser)."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"For å se på denne kanalen, trykk til høyre og skriv inn PIN-koden din"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Programmet er blokkert"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Dette programmet er vurdert som <xliff:g id="RATING">%1$s</xliff:g>."</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Bare lyd"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Svakt signal"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Ingen Internett-tilkobling"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Ingen tittel"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Kanalen er blokkert"</string>
- <string name="episode_format" msgid="4881195874563241096">"S<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Nye"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Kilder"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Justering mislyktes."</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Kunne ikke finne noen app som kan håndtere denne handlingen."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Alle kildekanalene er skjult.\nDu må velge minst én kanal å se på."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Utilgjengelig på grunn av svakt videosignal"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Videoen er utilgjengelig på grunn av en uventet feil"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"TILBAKE-tasten er for den tilkoblede enheten. Trykk på STARTSIDE-knappen for å avslutte."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Live TV støttes ikke på denne enheten med Android Lollipop."</string>
diff --git a/res/values-ne-rNP/arrays.xml b/res/values-ne-rNP/arrays.xml
index a6dc0066..711c21f8 100644
--- a/res/values-ne-rNP/arrays.xml
+++ b/res/values-ne-rNP/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"पूर्ण"</item>
<item msgid="8568284598210500589">"जूम"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"सबै च्यानलहरू"</item>
- <item msgid="928298872841713530">"पारिवार/बच्चाहरु"</item>
- <item msgid="2751606947569857164">"खेलकुदहरू"</item>
- <item msgid="7345749789651321496">"किनमेल"</item>
- <item msgid="167201149441442173">"चलचित्रहरू"</item>
- <item msgid="525966731464264290">"हास्य"</item>
- <item msgid="6096710741527327836">"यात्रा"</item>
- <item msgid="2851882187117833883">"नाटक"</item>
- <item msgid="78492781188719038">"शिक्षा"</item>
- <item msgid="7221999662426308394">"जनावर/वन्यजन्तु"</item>
- <item msgid="375300513250925001">"समाचार"</item>
- <item msgid="7746320336582330410">"खेल"</item>
- <item msgid="1255741860568329178">"कलाहरू"</item>
- <item msgid="7603949681065702867">"मनोरञ्जन"</item>
- <item msgid="4453821994746804366">"जीवन शैली"</item>
- <item msgid="3488534597567932843">"संगीत"</item>
- <item msgid="7452153120614274095">"प्रिमियर"</item>
- <item msgid="8215762047341133299">"प्रविधि/विज्ञान"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"सबै च्यानलहरू"</item>
+ <item msgid="6897460857821394118">"पारिवार/बालबालिकाहरू"</item>
+ <item msgid="551257741825778215">"खेलकुदहरू"</item>
+ <item msgid="452133796804325879">"किनमेल"</item>
+ <item msgid="3296058637230163031">"चलचित्रहरू"</item>
+ <item msgid="1054540282883891201">"हास्य"</item>
+ <item msgid="7900158429062595471">"यात्रा"</item>
+ <item msgid="3768998587825611787">"नाटक"</item>
+ <item msgid="8340620094959282881">"शिक्षा"</item>
+ <item msgid="7396447839483867269">"जनावर/वन्यजन्तु"</item>
+ <item msgid="4738043455148062673">"समाचार"</item>
+ <item msgid="7405041316051047427">"गेमिङ"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"सबै च्यानलहरू"</item>
+ <item msgid="7909003973960375395">"पारिवार/बालबालिकाहरू"</item>
+ <item msgid="3185279732911635789">"खेलकुदहरू"</item>
+ <item msgid="4704858492065325964">"किनमेल"</item>
+ <item msgid="6083795019290250078">"चलचित्रहरू"</item>
+ <item msgid="8302638329222449550">"हास्य"</item>
+ <item msgid="3803709976021475052">"यात्रा"</item>
+ <item msgid="8116747365234169059">"नाटक"</item>
+ <item msgid="7356447541595315913">"शिक्षा"</item>
+ <item msgid="7511135485827589547">"जनावर/वन्यजन्तु"</item>
+ <item msgid="6961248112238009967">"समाचार"</item>
+ <item msgid="6484685553679698447">"गेमिङ"</item>
+ <item msgid="2737158328243183190">"कलाहरू"</item>
+ <item msgid="6577176952650166615">"मनोरञ्जन"</item>
+ <item msgid="7886693831871777617">"जीवन शैली"</item>
+ <item msgid="8145832312485577062">"संगीत"</item>
+ <item msgid="1345789204804308580">"प्रिमियर"</item>
+ <item msgid="2736680312770771994">"प्रविधि/विज्ञान"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"लाइभ च्यानलहरू"</item>
diff --git a/res/values-ne-rNP/rating_system_strings.xml b/res/values-ne-rNP/rating_system_strings.xml
index b783b90a..211e4b3c 100644
--- a/res/values-ne-rNP/rating_system_strings.xml
+++ b/res/values-ne-rNP/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"कार्यक्रमहरूमा १५ वर्ष भन्दा मुनिका दर्शकहरूका लागि अनुपयुक्त सामग्री समावेश हुन सक्छ र त्यसैले तिनीहरूको लागि अभिभावकको स्वविवेक प्रयोग गरिनुपर्छ।"</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"कार्यक्रममा १९ वर्ष भन्दा मुनिका दर्शकहरूका लागि अनुपयुक्त सामग्री समावेश हुन सक्छ र त्यसैले यी १९ वर्ष भन्दा मुनिका युवाहरूका लागि उपयुक्त छैनन्।"</string>
<string name="description_us_tv_d" msgid="12333789157204816">"अश्लील संवाद"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"भद्दा भाषा"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"यौन सामग्री"</string>
diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml
index 8e997ba2..f8b6f4c7 100644
--- a/res/values-ne-rNP/strings.xml
+++ b/res/values-ne-rNP/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"स्रोतका इजाजतपत्रहरू खोल्नुहोस्"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"खुला स्रोत इजाजतपत्रहरू"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"संस्करण"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"विकासकर्ता विकल्पहरू"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"USB TV ट्युनर सक्रिय गर्नुहोस्"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"USB TV ट्युनर को ध्वनि सुन्‍न, तपाईँको TV ले AC3 passthrough समर्थन हुनुपर्छ।"</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 अडियो क्षमता"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"तपाईँको TV ले AC3 passthrough समर्थन गर्छ।"</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"तपाईँको TV ले AC3 passthrough समर्थन गर्दैन।"</string>
<string name="about_menu_improve" msgid="3712578027009311401">"लाइभ च्यानलहरु सुधार गर्न मद्दत गर्नुहोस्"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Google सँग अज्ञात प्रयोग डेटा र लक्षण पहिचान गर्न डेटा साझेदारी गर्नुहोस् ताकि हामी लाइभ च्यानलहरू राम्रो बनाउन र क्र्यास हुने तथा स्थिर हुने समस्याहरूलाई रोक्न सक्छौं।"</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"यो च्यानल हेर्न, दायाँ प्रेस गर्नुहोस् र आफ्नो पिन प्रविष्ट गर्नुहोस्"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"प्रोग्राम ब्लक गरिएको छ"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"यो कार्यक्रम मूल्याङ्कन <xliff:g id="RATING">%1$s</xliff:g> गरिएको छ"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"अडियो मात्र"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"सिग्नल कमजोर छ"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"इन्टरनेटमा जडान गर्न सकिएन"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"शीर्षक छैन"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"च्यानल अवरुद्ध"</string>
- <string name="episode_format" msgid="4881195874563241096">"संस्करण <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: भाग <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"नयाँ"</string>
<string name="setup_category_done" msgid="4750902502852212319">"स्रोतहरू"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"ट्युन गर्न असफल"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"यो कार्य सम्हाल्न कुनै पनि अनुप्रयोग भेटिएन।"</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"सबै स्रोत च्यानलहरू लुकेको छन्। \n हेर्नको लागि कम्तिमा एक च्यानल चयन गर्नुहोस्।"</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"कमजोर भिडियो संकेतका कारण अनुपलब्ध।"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"भिडियो अप्रत्याशित रूपमा अनुपलब्ध।"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"BACK कुञ्जी जडान भएका उपकरणका लागि हो। बाहिर निस्कनका लागि गृह बटन थिच्नुहोस्।"</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"लाइभ च्यानलहरु Android Lollipop भएको यस यन्त्रमा समर्थित छैन।"</string>
diff --git a/res/values-nl/arrays.xml b/res/values-nl/arrays.xml
index c2cd937e..32ff2095 100644
--- a/res/values-nl/arrays.xml
+++ b/res/values-nl/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Volledig"</item>
<item msgid="8568284598210500589">"Zoomen"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Alle kanalen"</item>
- <item msgid="928298872841713530">"Familie/kinderen"</item>
- <item msgid="2751606947569857164">"Sport"</item>
- <item msgid="7345749789651321496">"Winkelen"</item>
- <item msgid="167201149441442173">"Films"</item>
- <item msgid="525966731464264290">"Komedie"</item>
- <item msgid="6096710741527327836">"Reizen"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Onderwijs"</item>
- <item msgid="7221999662426308394">"Dieren/natuur"</item>
- <item msgid="375300513250925001">"Nieuws"</item>
- <item msgid="7746320336582330410">"Games"</item>
- <item msgid="1255741860568329178">"Kunst"</item>
- <item msgid="7603949681065702867">"Amusement"</item>
- <item msgid="4453821994746804366">"Lifestyle"</item>
- <item msgid="3488534597567932843">"Muziek"</item>
- <item msgid="7452153120614274095">"Premier"</item>
- <item msgid="8215762047341133299">"Techniek/wetenschap"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Alle kanalen"</item>
+ <item msgid="6897460857821394118">"Familie/kinderen"</item>
+ <item msgid="551257741825778215">"Sport"</item>
+ <item msgid="452133796804325879">"Winkelen"</item>
+ <item msgid="3296058637230163031">"Films"</item>
+ <item msgid="1054540282883891201">"Comedy"</item>
+ <item msgid="7900158429062595471">"Reizen"</item>
+ <item msgid="3768998587825611787">"Drama"</item>
+ <item msgid="8340620094959282881">"Onderwijs"</item>
+ <item msgid="7396447839483867269">"Dieren/natuur"</item>
+ <item msgid="4738043455148062673">"Nieuws"</item>
+ <item msgid="7405041316051047427">"Games"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Alle kanalen"</item>
+ <item msgid="7909003973960375395">"Familie/kinderen"</item>
+ <item msgid="3185279732911635789">"Sport"</item>
+ <item msgid="4704858492065325964">"Winkelen"</item>
+ <item msgid="6083795019290250078">"Films"</item>
+ <item msgid="8302638329222449550">"Comedy"</item>
+ <item msgid="3803709976021475052">"Reizen"</item>
+ <item msgid="8116747365234169059">"Drama"</item>
+ <item msgid="7356447541595315913">"Onderwijs"</item>
+ <item msgid="7511135485827589547">"Dieren/natuur"</item>
+ <item msgid="6961248112238009967">"Nieuws"</item>
+ <item msgid="6484685553679698447">"Games"</item>
+ <item msgid="2737158328243183190">"Kunst"</item>
+ <item msgid="6577176952650166615">"Entertainment"</item>
+ <item msgid="7886693831871777617">"Lifestyle"</item>
+ <item msgid="8145832312485577062">"Muziek"</item>
+ <item msgid="1345789204804308580">"Premier"</item>
+ <item msgid="2736680312770771994">"Techniek/wetenschap"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Live tv"</item>
diff --git a/res/values-nl/rating_system_strings.xml b/res/values-nl/rating_system_strings.xml
index ab074d64..d3892685 100644
--- a/res/values-nl/rating_system_strings.xml
+++ b/res/values-nl/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Programma\'s kunnen materiaal bevatten dat ongeschikt is voor een publiek jonger dan vijftien jaar. Ouderlijk toezicht is gewenst."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Programma\'s kunnen materiaal bevatten dat ongeschikt is voor een publiek jonger dan negentien jaar. Ze zijn daarom niet geschikt voor jongeren tot negentien jaar."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Suggestieve dialogen"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Grof taalgebruik"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Pornografische inhoud"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 81a4facc..151cbbcb 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -86,7 +86,7 @@
<string name="program_guide_content_locked" msgid="198056836554559553">"Dit programma is geblokkeerd"</string>
<string name="program_guide_content_locked_format" msgid="514915272862967389">"Dit programma is beoordeeld als <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="msg_no_setup_activity" msgid="7746893144640239857">"De invoer biedt geen ondersteuning voor automatisch scannen"</string>
- <string name="msg_unable_to_start_setup_activity" msgid="8402612466599977855">"Kan automatische scan voor \'<xliff:g id="TV_INPUT">%s</xliff:g>\' niet starten"</string>
+ <string name="msg_unable_to_start_setup_activity" msgid="8402612466599977855">"Kan automatisch zoeken naar \'<xliff:g id="TV_INPUT">%s</xliff:g>\' niet starten"</string>
<string name="msg_unable_to_start_system_captioning_settings" msgid="705242616044165668">"Kan de ondertitelingsvoorkeuren voor het hele systeem niet starten."</string>
<plurals name="msg_channel_added" formatted="false" msgid="5301526166755938705">
<item quantity="other">%1$d kanalen toegevoegd</item>
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Open-sourcelicenties"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Open-sourcelicenties"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Versie"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Opties voor ontwikkelaars"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"USB-tv-tuner inschakelen"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Als je het geluid van je USB-tv-tuner wilt horen, moet je tv ondersteuning bieden voor AC3-doorvoer."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3-audiomogelijkheden"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Je tv biedt ondersteuning voor AC3-doorvoer."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Je tv biedt geen ondersteuning voor AC3-doorvoer."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Helpen bij het verbeteren van Live kanalen"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Deel anonieme diagnostische en gebruiksgegevens met Google zodat we Live kanalen kunnen verbeteren en problemen, zoals crashen en vastlopen, kunnen voorkomen."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Als je dit kanaal wilt bekijken, druk je rechts en geef je je pincode op"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Programma is geblokkeerd"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Dit programma is beoordeeld als <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Alleen audio"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Zwak signaal"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Geen internetverbinding"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Geen titel"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Kanaal geblokkeerd"</string>
- <string name="episode_format" msgid="4881195874563241096">"S. <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: afl. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Nieuw"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Bronnen"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Afstemmen mislukt"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Er is geen app gevonden om deze actie uit te voeren."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Alle bronkanalen zijn verborgen.\nSelecteer ten minste één kanaal om te bekijken."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Niet beschikbaar wegens zwak videosignaal"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"De video is onverwacht niet beschikbaar"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"De toets TERUG is voor verbonden apparaten. Druk op de knop HOME om te sluiten."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"De app \'Live kanalen\' wordt niet ondersteund op dit apparaat met Android Lollipop."</string>
diff --git a/res/values-pl/arrays.xml b/res/values-pl/arrays.xml
index 4dc517b9..b8c3af9d 100644
--- a/res/values-pl/arrays.xml
+++ b/res/values-pl/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Pełny"</item>
<item msgid="8568284598210500589">"Powiększenie"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Wszystkie kanały"</item>
- <item msgid="928298872841713530">"Rodzina/dzieci"</item>
- <item msgid="2751606947569857164">"Sport"</item>
- <item msgid="7345749789651321496">"Zakupy"</item>
- <item msgid="167201149441442173">"Filmy"</item>
- <item msgid="525966731464264290">"Komedia"</item>
- <item msgid="6096710741527327836">"Podróże"</item>
- <item msgid="2851882187117833883">"Dramat"</item>
- <item msgid="78492781188719038">"Edukacja"</item>
- <item msgid="7221999662426308394">"Zwierzęta/natura"</item>
- <item msgid="375300513250925001">"Wiadomości"</item>
- <item msgid="7746320336582330410">"Gry"</item>
- <item msgid="1255741860568329178">"Sztuka"</item>
- <item msgid="7603949681065702867">"Rozrywka"</item>
- <item msgid="4453821994746804366">"Styl życia"</item>
- <item msgid="3488534597567932843">"Muzyka"</item>
- <item msgid="7452153120614274095">"Specjalne"</item>
- <item msgid="8215762047341133299">"Nauka i technika"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Wszystkie kanały"</item>
+ <item msgid="6897460857821394118">"Rodzina/dzieci"</item>
+ <item msgid="551257741825778215">"Sport"</item>
+ <item msgid="452133796804325879">"Zakupy"</item>
+ <item msgid="3296058637230163031">"Filmy"</item>
+ <item msgid="1054540282883891201">"Komedie"</item>
+ <item msgid="7900158429062595471">"Podróże"</item>
+ <item msgid="3768998587825611787">"Dramaty"</item>
+ <item msgid="8340620094959282881">"Edukacja"</item>
+ <item msgid="7396447839483867269">"Zwierzęta/natura"</item>
+ <item msgid="4738043455148062673">"Wiadomości"</item>
+ <item msgid="7405041316051047427">"Gry"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Wszystkie kanały"</item>
+ <item msgid="7909003973960375395">"Rodzina/dzieci"</item>
+ <item msgid="3185279732911635789">"Sport"</item>
+ <item msgid="4704858492065325964">"Zakupy"</item>
+ <item msgid="6083795019290250078">"Filmy"</item>
+ <item msgid="8302638329222449550">"Komedie"</item>
+ <item msgid="3803709976021475052">"Podróże"</item>
+ <item msgid="8116747365234169059">"Dramaty"</item>
+ <item msgid="7356447541595315913">"Edukacja"</item>
+ <item msgid="7511135485827589547">"Zwierzęta/natura"</item>
+ <item msgid="6961248112238009967">"Wiadomości"</item>
+ <item msgid="6484685553679698447">"Gry"</item>
+ <item msgid="2737158328243183190">"Sztuka"</item>
+ <item msgid="6577176952650166615">"Rozrywka"</item>
+ <item msgid="7886693831871777617">"Styl życia"</item>
+ <item msgid="8145832312485577062">"Muzyka"</item>
+ <item msgid="1345789204804308580">"Specjalne"</item>
+ <item msgid="2736680312770771994">"Nauka i technika"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Telewizja online"</item>
diff --git a/res/values-pl/rating_system_strings.xml b/res/values-pl/rating_system_strings.xml
index 20059093..cf0294bb 100644
--- a/res/values-pl/rating_system_strings.xml
+++ b/res/values-pl/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Programy mogą być nieodpowiednie dla odbiorców poniżej 15 roku życia, dlatego zalecany jest nadzór rodzicielski."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Programy mogą być nieodpowiednie dla odbiorców poniżej 19 roku życia, dlatego nie są dla nich przeznaczone."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Dwuznaczne dialogi"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Wulgarny język"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Treści pornograficzne"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 95f00248..ede1b713 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -148,12 +148,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Licencje open source"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Licencje open source"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Wersja"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Opcje programisty"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Włącz tuner TV USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Aby dźwięk z tunera TV USB był słyszalny, telewizor powinien obsługiwać przelotowe przesyłanie sygnału AC3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Obsługa dźwięku AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Twój telewizor obsługuje przelotowe przesyłanie sygnału AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Twój telewizor nie obsługuje przelotowego przesyłania sygnału AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Pomóż w ulepszaniu Telewizji online"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Udostępniaj Google anonimowe informacje o użytkowaniu i dane diagnostyczne, by pomóc w ulepszaniu Telewizji online i usuwaniu błędów takich jak np. zatrzymywanie obrazu."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Aby oglądać ten kanał, naciśnij Prawo i wpisz kod PIN"</string>
@@ -165,9 +159,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Program jest zablokowany"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Ten program ma ocenę <xliff:g id="RATING">%1$s</xliff:g>."</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Tylko dźwięk"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Słaby sygnał"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Brak połączenia z internetem"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Bez tytułu"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Kanał zablokowany"</string>
- <string name="episode_format" msgid="4881195874563241096">"S<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>E<xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> – <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Nowe"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Źródła"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -196,7 +191,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Strojenie nie powiodło się"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Nie znaleziono aplikacji do obsługi tego działania."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Wszystkie kanały źródłowe są ukryte.\nWybierz co najmniej jeden kanał do oglądania."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Niedostępne z powodu słabego sygnału wideo"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Sygnał wideo jest nieoczekiwanie niedostępny"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Klawisz BACK steruje podłączonym urządzeniem. Naciśnij przycisk HOME, by wyjść."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Telewizja online nie jest obsługiwana na tym urządzeniu z Androidem Lollipop."</string>
diff --git a/res/values-pt-rPT/arrays.xml b/res/values-pt-rPT/arrays.xml
index d2249a07..1514c1e0 100644
--- a/res/values-pt-rPT/arrays.xml
+++ b/res/values-pt-rPT/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Completo"</item>
<item msgid="8568284598210500589">"Zoom"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Todos os canais"</item>
- <item msgid="928298872841713530">"Família/infantil"</item>
- <item msgid="2751606947569857164">"Desporto"</item>
- <item msgid="7345749789651321496">"Compras"</item>
- <item msgid="167201149441442173">"Filmes"</item>
- <item msgid="525966731464264290">"Comédia"</item>
- <item msgid="6096710741527327836">"Viagens"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Educação"</item>
- <item msgid="7221999662426308394">"Animais/vida selv."</item>
- <item msgid="375300513250925001">"Notícias"</item>
- <item msgid="7746320336582330410">"Jogos"</item>
- <item msgid="1255741860568329178">"Arte"</item>
- <item msgid="7603949681065702867">"Entretenimento"</item>
- <item msgid="4453821994746804366">"Estilo de vida"</item>
- <item msgid="3488534597567932843">"Música"</item>
- <item msgid="7452153120614274095">"Estreia"</item>
- <item msgid="8215762047341133299">"Tecnologia/ciência"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Todos os canais"</item>
+ <item msgid="6897460857821394118">"Família/infantil"</item>
+ <item msgid="551257741825778215">"Desporto"</item>
+ <item msgid="452133796804325879">"Compras"</item>
+ <item msgid="3296058637230163031">"Filmes"</item>
+ <item msgid="1054540282883891201">"Comédia"</item>
+ <item msgid="7900158429062595471">"Viagens"</item>
+ <item msgid="3768998587825611787">"Drama"</item>
+ <item msgid="8340620094959282881">"Educação"</item>
+ <item msgid="7396447839483867269">"Animais/vida selv."</item>
+ <item msgid="4738043455148062673">"Notícias"</item>
+ <item msgid="7405041316051047427">"Jogos"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Todos os canais"</item>
+ <item msgid="7909003973960375395">"Família/infantil"</item>
+ <item msgid="3185279732911635789">"Desporto"</item>
+ <item msgid="4704858492065325964">"Compras"</item>
+ <item msgid="6083795019290250078">"Filmes"</item>
+ <item msgid="8302638329222449550">"Comédia"</item>
+ <item msgid="3803709976021475052">"Viagens"</item>
+ <item msgid="8116747365234169059">"Drama"</item>
+ <item msgid="7356447541595315913">"Educação"</item>
+ <item msgid="7511135485827589547">"Animais/vida selv."</item>
+ <item msgid="6961248112238009967">"Notícias"</item>
+ <item msgid="6484685553679698447">"Jogos"</item>
+ <item msgid="2737158328243183190">"Arte"</item>
+ <item msgid="6577176952650166615">"Entretenimento"</item>
+ <item msgid="7886693831871777617">"Estilo de vida"</item>
+ <item msgid="8145832312485577062">"Música"</item>
+ <item msgid="1345789204804308580">"Estreia"</item>
+ <item msgid="2736680312770771994">"Tecnologia/ciência"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Canais em direto"</item>
diff --git a/res/values-pt-rPT/rating_system_strings.xml b/res/values-pt-rPT/rating_system_strings.xml
index 84f6c811..8a01a6c7 100644
--- a/res/values-pt-rPT/rating_system_strings.xml
+++ b/res/values-pt-rPT/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Os programas podem conter material impróprio para um público-alvo com menos de 15 anos e, como tal, recomenda-se a supervisão parental."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Os programas podem conter material impróprio para um público-alvo com menos de 19 anos e, como tal, não são adequados para menores de 19 anos."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Diálogo sugestivo"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Linguagem grosseira"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Conteúdos de natureza sexual"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 0ff8cf01..61ec49d4 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Licenças de código aberto"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Licenças de código aberto"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Versão"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Opções de programador"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Ativar sintonizador de TV USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Para ouvir som do sintonizador de TV USB, a TV deve ser compatível com a passagem AC3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Capacidade de áudio AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"A sua TV é compatível com a passagem AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"A sua TV não é compatível com a passagem AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Ajudar a melhorar a aplicação Canais em direto"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Partilhar dados de diagnóstico e de utilização anónimos com a Google para melhorar os Canais em Direto e evitar problemas, como falhas e bloqueios."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Para ver este canal, prima Direito e introduza o PIN"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"O programa está bloqueado"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Este programa tem a classificação <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Apenas áudio"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Sinal fraco"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Sem ligação à Internet"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Sem título"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Canal bloqueado"</string>
- <string name="episode_format" msgid="4881195874563241096">"T<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: ep. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Nova"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Fontes"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Falha ao sintonizar"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Não foram encontradas aplicações para executar esta ação."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Todos os canais de origem estão ocultos.\nSelecione pelo menos um canal para ver."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Não disponível devido ao fraco sinal de vídeo"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"O vídeo encontra-se inesperadamente não disponível"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"A tecla RETROCEDER destina-se ao dispositivo ligado. Prima o botão INÍCIO para sair."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"A aplicação Canais em direto não é compatível com este dispositivo com o Android Lollipop."</string>
diff --git a/res/values-pt/arrays.xml b/res/values-pt/arrays.xml
index 7aa5b446..9755f7e8 100644
--- a/res/values-pt/arrays.xml
+++ b/res/values-pt/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Tela cheia"</item>
<item msgid="8568284598210500589">"Zoom"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Todos os canais"</item>
- <item msgid="928298872841713530">"Família/crianças"</item>
- <item msgid="2751606947569857164">"Esportes"</item>
- <item msgid="7345749789651321496">"Compras"</item>
- <item msgid="167201149441442173">"Filmes"</item>
- <item msgid="525966731464264290">"Comédia"</item>
- <item msgid="6096710741527327836">"Viagem"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Educação"</item>
- <item msgid="7221999662426308394">"Anim./vida selvagem"</item>
- <item msgid="375300513250925001">"Notícias"</item>
- <item msgid="7746320336582330410">"Jogos"</item>
- <item msgid="1255741860568329178">"Artes"</item>
- <item msgid="7603949681065702867">"Entretenimento"</item>
- <item msgid="4453821994746804366">"Estilo de vida"</item>
- <item msgid="3488534597567932843">"Música"</item>
- <item msgid="7452153120614274095">"Alto nível"</item>
- <item msgid="8215762047341133299">"Tecnologia/ciência"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Todos os canais"</item>
+ <item msgid="6897460857821394118">"Família/crianças"</item>
+ <item msgid="551257741825778215">"Esportes"</item>
+ <item msgid="452133796804325879">"Compras"</item>
+ <item msgid="3296058637230163031">"Filmes"</item>
+ <item msgid="1054540282883891201">"Comédia"</item>
+ <item msgid="7900158429062595471">"Viagem"</item>
+ <item msgid="3768998587825611787">"Drama"</item>
+ <item msgid="8340620094959282881">"Educação"</item>
+ <item msgid="7396447839483867269">"Anim./vida selvagem"</item>
+ <item msgid="4738043455148062673">"Notícias"</item>
+ <item msgid="7405041316051047427">"Jogos"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Todos os canais"</item>
+ <item msgid="7909003973960375395">"Família/crianças"</item>
+ <item msgid="3185279732911635789">"Esportes"</item>
+ <item msgid="4704858492065325964">"Compras"</item>
+ <item msgid="6083795019290250078">"Filmes"</item>
+ <item msgid="8302638329222449550">"Comédia"</item>
+ <item msgid="3803709976021475052">"Viagem"</item>
+ <item msgid="8116747365234169059">"Drama"</item>
+ <item msgid="7356447541595315913">"Educação"</item>
+ <item msgid="7511135485827589547">"Anim./vida selvagem"</item>
+ <item msgid="6961248112238009967">"Notícias"</item>
+ <item msgid="6484685553679698447">"Jogos"</item>
+ <item msgid="2737158328243183190">"Artes"</item>
+ <item msgid="6577176952650166615">"Entretenimento"</item>
+ <item msgid="7886693831871777617">"Estilo de vida"</item>
+ <item msgid="8145832312485577062">"Música"</item>
+ <item msgid="1345789204804308580">"Premier"</item>
+ <item msgid="2736680312770771994">"Tecnologia/Ciência"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Canais ao vivo"</item>
diff --git a/res/values-pt/rating_system_strings.xml b/res/values-pt/rating_system_strings.xml
index 18c21efb..6a45f113 100644
--- a/res/values-pt/rating_system_strings.xml
+++ b/res/values-pt/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Os programas podem conter material impróprio para públicos com menos de 15 anos, e a discrição dos responsáveis deve ser usada."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Os programas podem conter material impróprio para públicos com menos de 19 anos e, portanto, não são adequados para jovens nessa idade."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Diálogo sugestivo"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Linguagem vulgar"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Conteúdo sexual"</string>
diff --git a/common/res_leanback/values-am/strings.xml b/res/values-pt/rating_system_strings_pt.xml
index e70214dc..5242c7c8 100644
--- a/common/res_leanback/values-am/strings.xml
+++ b/res/values-pt/rating_system_strings_pt.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
+<!--
+ ~ Copyright (C) 2016 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -17,6 +17,13 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="lb_onboarding_get_started" msgid="1724006991213712878">"ይጀምሩ"</string>
- <string name="lb_onboarding_accessibility_next" msgid="113416166336573484">"ቀጣይ"</string>
+ <!-- TV content rating system strings for BR TV. These strings are from
+ http://www.justica.gov.br/seus-direitos/classificacao/guia-pratico/practical-guide.pdf/view
+ This resources are tranlated only for Portuguese. -->
+ <string name="title_br_tv_l">Livre</string>
+ <string name="title_br_tv_10">10 anos</string>
+ <string name="title_br_tv_12">12 anos</string>
+ <string name="title_br_tv_14">14 anos</string>
+ <string name="title_br_tv_16">16 anos</string>
+ <string name="title_br_tv_18">18 anos</string>
</resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 1dc878b5..9e1bb27f 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Licenças de código aberto"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Licenças de código aberto"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Versão"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Opções do desenvolvedor"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Ativar sintonizador de TV USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Para ouvir o som do sintonizador de TV USB, sua TV deve ser compatível com a passagem AC3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Recurso de áudio AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Sua TV é compatível com a passagem AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Sua TV não é compatível com a passagem AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Ajudar a melhorar os Canais ao vivo"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Compartilhar dados anônimos de uso e diagnóstico com o Google para que possamos melhorar os Canais ao vivo e evitar problemas, como falhas e congelamento."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Para assistir a este canal, pressione para a direita e digite o PIN"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Programa bloqueado"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Este programa foi classificado como <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Somente áudio"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Sinal fraco"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Sem conexão com a Internet"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Sem título"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Canal bloqueado"</string>
- <string name="episode_format" msgid="4881195874563241096">"T<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Novas"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Fontes"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Falha na sintonia"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Nenhum app foi encontrado para executar esta ação."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Todos os canais de origem estão ocultos.\nSelecione pelo menos um canal para assistir."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Indisponível devido a um sinal fraco de vídeo"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"O vídeo está inesperadamente indisponível"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"A tecla VOLTAR é para dispositivos conectados. Pressione o botão INÍCIO para sair."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"O app \"Canais ao vivo\" não é compatível com este dispositivo com Android Lollipop."</string>
diff --git a/res/values-ro/arrays.xml b/res/values-ro/arrays.xml
index 60827ee9..0c199ff9 100644
--- a/res/values-ro/arrays.xml
+++ b/res/values-ro/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Complet"</item>
<item msgid="8568284598210500589">"Zoom"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Toate canalele"</item>
- <item msgid="928298872841713530">"Familie/Copii"</item>
- <item msgid="2751606947569857164">"Sport"</item>
- <item msgid="7345749789651321496">"Cumpărături"</item>
- <item msgid="167201149441442173">"Filme"</item>
- <item msgid="525966731464264290">"Comedie"</item>
- <item msgid="6096710741527327836">"Călătorii"</item>
- <item msgid="2851882187117833883">"Dramă"</item>
- <item msgid="78492781188719038">"Educație"</item>
- <item msgid="7221999662426308394">"Animale/Faună"</item>
- <item msgid="375300513250925001">"Știri"</item>
- <item msgid="7746320336582330410">"Jocuri"</item>
- <item msgid="1255741860568329178">"Artă"</item>
- <item msgid="7603949681065702867">"Divertisment"</item>
- <item msgid="4453821994746804366">"Stil de viață"</item>
- <item msgid="3488534597567932843">"Muzică"</item>
- <item msgid="7452153120614274095">"Premiere"</item>
- <item msgid="8215762047341133299">"Tehnică/Știință"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Toate canalele"</item>
+ <item msgid="6897460857821394118">"Familie/Copii"</item>
+ <item msgid="551257741825778215">"Sport"</item>
+ <item msgid="452133796804325879">"Cumpărături"</item>
+ <item msgid="3296058637230163031">"Filme"</item>
+ <item msgid="1054540282883891201">"Comedie"</item>
+ <item msgid="7900158429062595471">"Călătorii"</item>
+ <item msgid="3768998587825611787">"Dramă"</item>
+ <item msgid="8340620094959282881">"Educație"</item>
+ <item msgid="7396447839483867269">"Animale/Faună"</item>
+ <item msgid="4738043455148062673">"Știri"</item>
+ <item msgid="7405041316051047427">"Jocuri"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Toate canalele"</item>
+ <item msgid="7909003973960375395">"Familie/Copii"</item>
+ <item msgid="3185279732911635789">"Sport"</item>
+ <item msgid="4704858492065325964">"Cumpărături"</item>
+ <item msgid="6083795019290250078">"Filme"</item>
+ <item msgid="8302638329222449550">"Comedie"</item>
+ <item msgid="3803709976021475052">"Călătorii"</item>
+ <item msgid="8116747365234169059">"Dramă"</item>
+ <item msgid="7356447541595315913">"Educație"</item>
+ <item msgid="7511135485827589547">"Animale/Faună"</item>
+ <item msgid="6961248112238009967">"Știri"</item>
+ <item msgid="6484685553679698447">"Jocuri"</item>
+ <item msgid="2737158328243183190">"Artă"</item>
+ <item msgid="6577176952650166615">"Divertisment"</item>
+ <item msgid="7886693831871777617">"Stil de viață"</item>
+ <item msgid="8145832312485577062">"Muzică"</item>
+ <item msgid="1345789204804308580">"Premiere"</item>
+ <item msgid="2736680312770771994">"Tehnică/Știință"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Canale live"</item>
diff --git a/res/values-ro/rating_system_strings.xml b/res/values-ro/rating_system_strings.xml
index a4cc56dc..67d3c309 100644
--- a/res/values-ro/rating_system_strings.xml
+++ b/res/values-ro/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Programele pot conține materiale neadecvate pentru persoanele sub 15 ani, motiv pentru care se recomandă supravegherea parentală."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Programele pot conține materiale neadecvate pentru persoanele sub 19 ani, motiv pentru care nu sunt recomandate tinerilor care nu au împlinit 19 ani."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Dialog sugestiv sexual"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Limbaj grosolan"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Conținut sexual"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index b012470e..82ff24ab 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -146,12 +146,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Licențe open source"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Licențe open source"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Versiune"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Opțiuni dezvoltator"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Activați tunerul TV USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Pentru a auzi sunetul tunerului TV USB, este necesar ca televizorul dvs. să accepte trecerea semnalelor AC3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Compatibilitate audio cu AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Televizorul dvs. acceptă trecerea semnalelor AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Televizorul dvs. nu acceptă trecerea semnalelor AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Contribuiți la îmbunătățirea aplicației „Canale live”"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Trimiteți la Google date anonime de utilizare și de diagnosticare pentru a îmbunătăți aplicația Canale live și pentru a preveni probleme cum ar blocarea."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Pentru a viziona acest canal, apăsați la dreapta și introduceți codul PIN"</string>
@@ -163,9 +157,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Programul este blocat"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Acest program este clasificat ca <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Numai conținut audio"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Semnal slab"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Fără conexiune la internet"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Fără titlu"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Canal blocat"</string>
- <string name="episode_format" msgid="4881195874563241096">"Sezonul <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>, episodul <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g>: <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Noi"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Surse"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -193,7 +188,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Eroare la optimizare"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Nu s-a găsit o aplicație care să îndeplinească această acțiune."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Toate canalele sursă sunt ascunse.\nSelectați cel puțin un canal pentru vizionare."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Indisponibil din cauza semnalului video slab"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Videoclipul este indisponibil în mod neașteptat"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Tasta BACK este pentru dispozitivul conectat. Apăsați pe butonul HOME pentru a ieși."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Aplicația „Canale live” nu este acceptată pe acest dispozitiv cu Android Lollipop."</string>
diff --git a/res/values-ru/arrays.xml b/res/values-ru/arrays.xml
index db7788bb..549e0309 100644
--- a/res/values-ru/arrays.xml
+++ b/res/values-ru/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Панорамный"</item>
<item msgid="8568284598210500589">"Увеличение"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Все каналы"</item>
- <item msgid="928298872841713530">"Для всей семьи"</item>
- <item msgid="2751606947569857164">"Спорт"</item>
- <item msgid="7345749789651321496">"Покупки"</item>
- <item msgid="167201149441442173">"Фильмы"</item>
- <item msgid="525966731464264290">"Юмор"</item>
- <item msgid="6096710741527327836">"Путешествия"</item>
- <item msgid="2851882187117833883">"Драма"</item>
- <item msgid="78492781188719038">"Образование"</item>
- <item msgid="7221999662426308394">"Природа и животные"</item>
- <item msgid="375300513250925001">"Новости"</item>
- <item msgid="7746320336582330410">"Компьютерные игры"</item>
- <item msgid="1255741860568329178">"Искусство"</item>
- <item msgid="7603949681065702867">"Развлечения"</item>
- <item msgid="4453821994746804366">"Стиль жизни"</item>
- <item msgid="3488534597567932843">"Музыка"</item>
- <item msgid="7452153120614274095">"Новинки"</item>
- <item msgid="8215762047341133299">"Наука и техника"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Все каналы"</item>
+ <item msgid="6897460857821394118">"Для всей семьи"</item>
+ <item msgid="551257741825778215">"Спорт"</item>
+ <item msgid="452133796804325879">"Покупки"</item>
+ <item msgid="3296058637230163031">"Фильмы"</item>
+ <item msgid="1054540282883891201">"Комедии"</item>
+ <item msgid="7900158429062595471">"Путешествия"</item>
+ <item msgid="3768998587825611787">"Драмы"</item>
+ <item msgid="8340620094959282881">"Образование"</item>
+ <item msgid="7396447839483867269">"Природа и животные"</item>
+ <item msgid="4738043455148062673">"Новости"</item>
+ <item msgid="7405041316051047427">"Игры"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Все каналы"</item>
+ <item msgid="7909003973960375395">"Для всей семьи"</item>
+ <item msgid="3185279732911635789">"Спорт"</item>
+ <item msgid="4704858492065325964">"Покупки"</item>
+ <item msgid="6083795019290250078">"Фильмы"</item>
+ <item msgid="8302638329222449550">"Комедии"</item>
+ <item msgid="3803709976021475052">"Путешествия"</item>
+ <item msgid="8116747365234169059">"Драмы"</item>
+ <item msgid="7356447541595315913">"Образование"</item>
+ <item msgid="7511135485827589547">"Природа и животные"</item>
+ <item msgid="6961248112238009967">"Новости"</item>
+ <item msgid="6484685553679698447">"Игры"</item>
+ <item msgid="2737158328243183190">"Искусство"</item>
+ <item msgid="6577176952650166615">"Развлечения"</item>
+ <item msgid="7886693831871777617">"Стиль жизни"</item>
+ <item msgid="8145832312485577062">"Музыка"</item>
+ <item msgid="1345789204804308580">"Новинки"</item>
+ <item msgid="2736680312770771994">"Наука и техника"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Каналы Live"</item>
diff --git a/res/values-ru/rating_system_strings.xml b/res/values-ru/rating_system_strings.xml
index 72946d31..e88eba47 100644
--- a/res/values-ru/rating_system_strings.xml
+++ b/res/values-ru/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Просмотр программ пользователями младше 15 лет возможен с разрешения родителей."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Просмотр программ пользователями младше 19 лет возможен с разрешения родителей."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Непристойные разговоры"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Нецензурная лексика"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Секс"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 906535e5..d7ac09e2 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -148,12 +148,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Лицензии открытого ПО"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Лицензии открытого ПО"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Версия"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Для разработчиков"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Включить USB-тюнер"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Для воспроизведения звука при использовании USB-тюнера телевизор должен поддерживать сквозную передачу в формате AC3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Поддержка AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Ваш телевизор поддерживает сквозную передачу в формате AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Ваш телевизор не поддерживает сквозную передачу в формате AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Не помогать улучшить \"Прямой эфир\""</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Разрешите отправку анонимных отчетов об использовании и данных диагностики в Google, чтобы мы могли улучшить работу Телеканалов и устранить сбои и зависания."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Чтобы смотреть этот канал, нажмите стрелку вправо и введите PIN-код."</string>
@@ -165,9 +159,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Программа заблокирована"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Возрастное ограничение этой программы: <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Только аудио"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Слабый сигнал"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Нет доступа к Интернету"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Без названия"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Канал заблокирован"</string>
- <string name="episode_format" msgid="4881195874563241096">"Сезон <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: серия <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g>, \"<xliff:g id="EPISODE_TITLE">%3$s</xliff:g>\""</string>
<string name="setup_category_new" msgid="2899355289563443627">"Новые"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Источники"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -196,7 +191,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Не удалось выполнить настройку"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Действие не поддерживается ни в одном приложении."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Все каналы скрыты.\nВыберите хотя бы один."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Видео недоступно из-за слабого сигнала"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Видео недоступно из-за непредвиденной ошибки"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Кнопка \"Назад\" управляет подключенным устройством. Чтобы выйти, нажмите \"Главный экран\"."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Прямой эфир не поддерживается на этом устройстве с Android 5.0."</string>
diff --git a/res/values-si-rLK/arrays.xml b/res/values-si-rLK/arrays.xml
index 657fb91d..363153c5 100644
--- a/res/values-si-rLK/arrays.xml
+++ b/res/values-si-rLK/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"පූර්ණ"</item>
<item msgid="8568284598210500589">"විශාලනය කරන්න"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"සියළුම නාලිකා"</item>
- <item msgid="928298872841713530">"පවුල/ළමයින්"</item>
- <item msgid="2751606947569857164">"ක්‍රීඩා"</item>
- <item msgid="7345749789651321496">"සාප්පුයාම"</item>
- <item msgid="167201149441442173">"චිත්‍රපට"</item>
- <item msgid="525966731464264290">"විකට"</item>
- <item msgid="6096710741527327836">"සංචාර"</item>
- <item msgid="2851882187117833883">"නාට්‍ය"</item>
- <item msgid="78492781188719038">"අධ්‍යාපනය"</item>
- <item msgid="7221999662426308394">"සතුන්/වනසතුන්"</item>
- <item msgid="375300513250925001">"ප්‍රවෘත්ති"</item>
- <item msgid="7746320336582330410">"වීඩියෝ ක්‍රීඩා"</item>
- <item msgid="1255741860568329178">"කලාව"</item>
- <item msgid="7603949681065702867">"විනෝදාස්වාදය"</item>
- <item msgid="4453821994746804366">"ජීවන රටාව"</item>
- <item msgid="3488534597567932843">"සංගීත"</item>
- <item msgid="7452153120614274095">"ප්‍රමුඛ"</item>
- <item msgid="8215762047341133299">"තාක්ෂණය/විද්‍යාව"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"සියලු නාලිකා"</item>
+ <item msgid="6897460857821394118">"පවුල/ළමයින්"</item>
+ <item msgid="551257741825778215">"ක්‍රීඩා"</item>
+ <item msgid="452133796804325879">"සාප්පු යාම"</item>
+ <item msgid="3296058637230163031">"චිත්‍රපට"</item>
+ <item msgid="1054540282883891201">"විකට"</item>
+ <item msgid="7900158429062595471">"සංචාර"</item>
+ <item msgid="3768998587825611787">"නාට්‍ය"</item>
+ <item msgid="8340620094959282881">"අධ්‍යාපනය"</item>
+ <item msgid="7396447839483867269">"සතුන්/වනසතුන්"</item>
+ <item msgid="4738043455148062673">"ප්‍රවෘත්ති"</item>
+ <item msgid="7405041316051047427">"ක්‍රීඩා"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"සියලු නාලිකා"</item>
+ <item msgid="7909003973960375395">"පවුල/ළමයින්"</item>
+ <item msgid="3185279732911635789">"ක්‍රීඩා"</item>
+ <item msgid="4704858492065325964">"සාප්පු යාම"</item>
+ <item msgid="6083795019290250078">"චිත්‍රපට"</item>
+ <item msgid="8302638329222449550">"විකට"</item>
+ <item msgid="3803709976021475052">"සංචාර"</item>
+ <item msgid="8116747365234169059">"නාට්‍ය"</item>
+ <item msgid="7356447541595315913">"අධ්‍යාපනය"</item>
+ <item msgid="7511135485827589547">"සතුන්/වනසතුන්"</item>
+ <item msgid="6961248112238009967">"ප්‍රවෘත්ති"</item>
+ <item msgid="6484685553679698447">"ක්‍රීඩා"</item>
+ <item msgid="2737158328243183190">"කලාව"</item>
+ <item msgid="6577176952650166615">"විනෝදාස්වාදය"</item>
+ <item msgid="7886693831871777617">"ජීවන රටාව"</item>
+ <item msgid="8145832312485577062">"සංගීතය"</item>
+ <item msgid="1345789204804308580">"ප්‍රිමියර්"</item>
+ <item msgid="2736680312770771994">"තාක්ෂණය/විද්‍යාව"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"සජීවී නාලිකා"</item>
diff --git a/res/values-si-rLK/rating_system_strings.xml b/res/values-si-rLK/rating_system_strings.xml
index 77666b1b..ebb26549 100644
--- a/res/values-si-rLK/rating_system_strings.xml
+++ b/res/values-si-rLK/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"වැඩසටහන්වල වයස අවුරුදු 15ට අඩු ප්‍රේක්ෂකයන්ට නොගැළපෙන ද්‍රව්‍ය අඩංගු විය හැකි අතර, එබැවින් ඒවා සඳහා මාපිය කැමැත්ත භාවිත කළ යුතුය."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"වැඩසටහන්වල වයස අවුරුදු 19ට අඩු ප්‍රේක්ෂකයන්ට නොගැළපෙන ද්‍රව්‍ය අඩංගු විය හැකි අතර, එබැවින් ඒවා වයස අවුරුදු 19ට වඩා අඩු අයට සුදුසු නොවේ."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"යෝජිත සංවාදය"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"රළු භාෂාව"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"ලිංගිකත්වය හඟවන අන්තර්ගතය"</string>
diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml
index ebdf4370..47b69c8e 100644
--- a/res/values-si-rLK/strings.xml
+++ b/res/values-si-rLK/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"විවෘත මූලාශ්‍ර බලපත්‍ර"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"විවෘත මූලාශ්‍ර වරපත්"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"අනුවාදය"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"සංවර්ධක විකල්ප"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"USB TV සුසරකය සබල කරන්න"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"USB TV සුසරකයේ හඬ ඇසීමට, AC3 passthrough සඳහා ඔබේ TV සහාය දැක්විය යුතුය."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 ශ්‍රව්‍ය හැකියාව"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"AC3 passthrough සඳහා ඔබේ TV සහාය දක්වයි."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"AC3 passthrough සඳහා ඔබේ TV සහාය නොදක්වයි."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"සජීවි නාලිකා වැඩි දියුණු කිරීමට උදව් කරන්න"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Google සමග නිර්නාමික භාවිත සහ දෝෂ නිර්ණ දත්ත බෙදා ගන්න, එවිට අපට සජීවී නාලිකා වඩාත් යහපත් කිරීමට සහ බිඳ වැටීම් සහ සිරවීම් වැනි ගැටලු වැළැක්වීමට හැකිය."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"මෙම නාලිකාව නැරඹිමට දකුණ ඔබා PIN එක ඇතුළු කරන්න"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"වැඩසටහන අවහිර කරන ලදි"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"මෙම වැඩසටහන <xliff:g id="RATING">%1$s</xliff:g> ලෙස අගයා ඇත"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"ශ්‍රව්‍ය පමණයි"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"දුර්වල සංඥාව"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"අන්තර්ජාල සබැඳුමක් නැත"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"මාතෘකාවක් නොමැත"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"නාලිකාව අවහිර කරන ලදි"</string>
- <string name="episode_format" msgid="4881195874563241096">"S<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"නව"</string>
<string name="setup_category_done" msgid="4750902502852212319">"මූලාශ්‍ර"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"සුසර කිරීම අසාර්ථක වුණි"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"මෙම ක්‍රියාව හැසිරවීමට යෙදුමක් සොයාගත්තේ නැත"</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"සියලුම මූල නාලිකා සඟවන ලදි.\nනැරඹීමට අඩුම තරමේ එක නාලිකාවක් වත් තෝරන්න."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"දුර්වල වීඩියෝ සංඥාව හේතුවෙන් නොතිබේ"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"වීඩියෝව බලාපොරොත්තු නොවූ ලෙස නොතිබේ"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"සම්බන්ධිත උපාංගය සඳහා BACK යතුර. පිටවීමට Home බොත්තම ඔබන්න."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Android Lollipop සමගින් සජීවී නාලිකා මෙම උපාංගය මත සහාය නොදක්වයි."</string>
diff --git a/res/values-sk/arrays.xml b/res/values-sk/arrays.xml
index 7c6b4d49..b71197c9 100644
--- a/res/values-sk/arrays.xml
+++ b/res/values-sk/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Úplné"</item>
<item msgid="8568284598210500589">"Priblíženie"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Všetky kanály"</item>
- <item msgid="928298872841713530">"Rodina a deti"</item>
- <item msgid="2751606947569857164">"Šport"</item>
- <item msgid="7345749789651321496">"Nákupy"</item>
- <item msgid="167201149441442173">"Filmy"</item>
- <item msgid="525966731464264290">"Komédie"</item>
- <item msgid="6096710741527327836">"Cestovanie"</item>
- <item msgid="2851882187117833883">"Dráma"</item>
- <item msgid="78492781188719038">"Vzdelávanie"</item>
- <item msgid="7221999662426308394">"Zvieratá a príroda"</item>
- <item msgid="375300513250925001">"Spravodajstvo"</item>
- <item msgid="7746320336582330410">"Hranie hier"</item>
- <item msgid="1255741860568329178">"Umenie"</item>
- <item msgid="7603949681065702867">"Zábava"</item>
- <item msgid="4453821994746804366">"Životný štýl"</item>
- <item msgid="3488534597567932843">"Hudba"</item>
- <item msgid="7452153120614274095">"Premiéra"</item>
- <item msgid="8215762047341133299">"Veda a technika"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Všetky kanály"</item>
+ <item msgid="6897460857821394118">"Rodina a deti"</item>
+ <item msgid="551257741825778215">"Šport"</item>
+ <item msgid="452133796804325879">"Nákupy"</item>
+ <item msgid="3296058637230163031">"Filmy"</item>
+ <item msgid="1054540282883891201">"Komédie"</item>
+ <item msgid="7900158429062595471">"Cestovanie"</item>
+ <item msgid="3768998587825611787">"Drámy"</item>
+ <item msgid="8340620094959282881">"Vzdelávanie"</item>
+ <item msgid="7396447839483867269">"Zvieratá a príroda"</item>
+ <item msgid="4738043455148062673">"Spravodajstvo"</item>
+ <item msgid="7405041316051047427">"Hry"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Všetky kanály"</item>
+ <item msgid="7909003973960375395">"Rodina a deti"</item>
+ <item msgid="3185279732911635789">"Šport"</item>
+ <item msgid="4704858492065325964">"Nákupy"</item>
+ <item msgid="6083795019290250078">"Filmy"</item>
+ <item msgid="8302638329222449550">"Komédie"</item>
+ <item msgid="3803709976021475052">"Cestovanie"</item>
+ <item msgid="8116747365234169059">"Drámy"</item>
+ <item msgid="7356447541595315913">"Vzdelávanie"</item>
+ <item msgid="7511135485827589547">"Zvieratá a príroda"</item>
+ <item msgid="6961248112238009967">"Spravodajstvo"</item>
+ <item msgid="6484685553679698447">"Hry"</item>
+ <item msgid="2737158328243183190">"Kultúra"</item>
+ <item msgid="6577176952650166615">"Zábava"</item>
+ <item msgid="7886693831871777617">"Životný štýl"</item>
+ <item msgid="8145832312485577062">"Hudba"</item>
+ <item msgid="1345789204804308580">"Premiéra"</item>
+ <item msgid="2736680312770771994">"Veda a technika"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Televízia online"</item>
diff --git a/res/values-sk/rating_system_strings.xml b/res/values-sk/rating_system_strings.xml
index a5c121d0..2f6e8d7e 100644
--- a/res/values-sk/rating_system_strings.xml
+++ b/res/values-sk/rating_system_strings.xml
@@ -17,7 +17,23 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="description_us_tv_d" msgid="12333789157204816">"Naznačujúce dialógy"</string>
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Programy môžu obsahovať materiál nevhodný pre publikum do 15 rokov, preto sa odporúča rodičovský dohľad."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Programy môžu obsahovať materiál nevhodný pre publikum do 19 rokov, a preto nie sú vhodné pre mladistvých do 19 rokov."</string>
+ <string name="description_us_tv_d" msgid="12333789157204816">"Dialógy so sexuálnym podtextom"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Hrubý jazyk"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Sexuálny obsah"</string>
<string name="description_us_tv_v" msgid="4799470820740236198">"Násilie"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 768b09ae..bc02f2f9 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -148,12 +148,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Licencie open source"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Licencie open source"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Verzia"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Možnosti pre vývojárov"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Povoliť TV tuner s rozhraním USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Ak chcete počuť zvuk TV tunera s rozhraním USB, váš televízor by mal podporovať formát AC3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Podpora zvuku AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Váš televízor podporuje formát AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Váš televízor nepodporuje formát AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Pomáhať s vylepšovaním aktívnych kanálov"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Zdieľať s Googlom anonymné štatistiky o používaní a diagnostické údaje, aby sme mohli Televíziu online zlepšiť a zabrániť problémom, ako sú zlyhania a zamŕzania."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Ak chcete sledovať tento kanál, stlačte šípku vpravo a zadajte kód PIN"</string>
@@ -165,9 +159,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Program je zablokovaný"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Hodnotenie tohto programu je <xliff:g id="RATING">%1$s</xliff:g>."</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Iba zvuk"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Slabý signál"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Žiadne pripojenie k internetu"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Bez názvu"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Kanál bol zablokovaný"</string>
- <string name="episode_format" msgid="4881195874563241096">"S <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: ep. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Nové"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Zdroje"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -196,7 +191,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Ladenie zlyhalo"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Aplikácia potrebná na spracovanie tejto akcie sa nenašla."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Všetky zdrojové kanály sú skryté.\nVyberte aspoň jeden kanál, ktorý chcete sledovať."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Nedostupné z dôvodu slabého signálu videa"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Video neočakávane prestalo byť dostupné"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Kláves Späť je určený pre pripojené zariadenie. Aplikáciu ukončite stlačením tlačidla Domov."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"V tomto zariadení so systémom Android Lollipop nie sú aktívne kanály podporované."</string>
diff --git a/res/values-sl/arrays.xml b/res/values-sl/arrays.xml
index 3867c26a..5c1ef578 100644
--- a/res/values-sl/arrays.xml
+++ b/res/values-sl/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"V celoti"</item>
<item msgid="8568284598210500589">"Povečava"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Vsi kanali"</item>
- <item msgid="928298872841713530">"Družina/otroci"</item>
- <item msgid="2751606947569857164">"Šport"</item>
- <item msgid="7345749789651321496">"Nakupovanje"</item>
- <item msgid="167201149441442173">"Filmi"</item>
- <item msgid="525966731464264290">"Komedija"</item>
- <item msgid="6096710741527327836">"Potovanja"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Izobraževanje"</item>
- <item msgid="7221999662426308394">"Živalski svet"</item>
- <item msgid="375300513250925001">"Novice"</item>
- <item msgid="7746320336582330410">"Igre"</item>
- <item msgid="1255741860568329178">"Umetnost"</item>
- <item msgid="7603949681065702867">"Razvedrilo"</item>
- <item msgid="4453821994746804366">"Življenjski slog"</item>
- <item msgid="3488534597567932843">"Glasba"</item>
- <item msgid="7452153120614274095">"Premiera"</item>
- <item msgid="8215762047341133299">"Tehnika/znanost"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Vsi kanali"</item>
+ <item msgid="6897460857821394118">"Družina/otroci"</item>
+ <item msgid="551257741825778215">"Šport"</item>
+ <item msgid="452133796804325879">"Nakupovanje"</item>
+ <item msgid="3296058637230163031">"Filmi"</item>
+ <item msgid="1054540282883891201">"Komedija"</item>
+ <item msgid="7900158429062595471">"Potovanja"</item>
+ <item msgid="3768998587825611787">"Drama"</item>
+ <item msgid="8340620094959282881">"Izobraževanje"</item>
+ <item msgid="7396447839483867269">"Živalski svet"</item>
+ <item msgid="4738043455148062673">"Novice"</item>
+ <item msgid="7405041316051047427">"Igre"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Vsi kanali"</item>
+ <item msgid="7909003973960375395">"Družina/otroci"</item>
+ <item msgid="3185279732911635789">"Šport"</item>
+ <item msgid="4704858492065325964">"Nakupovanje"</item>
+ <item msgid="6083795019290250078">"Filmi"</item>
+ <item msgid="8302638329222449550">"Komedija"</item>
+ <item msgid="3803709976021475052">"Potovanja"</item>
+ <item msgid="8116747365234169059">"Drama"</item>
+ <item msgid="7356447541595315913">"Izobraževanje"</item>
+ <item msgid="7511135485827589547">"Živalski svet"</item>
+ <item msgid="6961248112238009967">"Novice"</item>
+ <item msgid="6484685553679698447">"Igre"</item>
+ <item msgid="2737158328243183190">"Umetnost"</item>
+ <item msgid="6577176952650166615">"Zabava"</item>
+ <item msgid="7886693831871777617">"Življenjski slog"</item>
+ <item msgid="8145832312485577062">"Glasba"</item>
+ <item msgid="1345789204804308580">"Premiera"</item>
+ <item msgid="2736680312770771994">"Tehnika/znanost"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Kanali v živo"</item>
diff --git a/res/values-sl/rating_system_strings.xml b/res/values-sl/rating_system_strings.xml
index e88520c8..0db964ba 100644
--- a/res/values-sl/rating_system_strings.xml
+++ b/res/values-sl/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Vsebina lahko vključuje gradivo, neprimerno za osebe, mlajše od 15 let, zato je potreben starševski nadzor."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Vsebina lahko vključuje gradivo, neprimerno za osebe, mlajše od 19 let, zato ni primerno za mladostnike, mlajše od 19 let."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Sugestivni dialog"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Grobo izražanje"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Prizori spolnosti"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index d041e42c..962e9a29 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -148,12 +148,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Odprtokodne licence"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Odprtokodne licence"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Različica"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Možnosti za razvijalce"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Omogoči sprejemnik za TV-kanale USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Če želite slišati zvok sprejemnika za TV-kanale USB, moraš vaš televizor podpirati AC3-prepustnost."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Zmožnost prepustnosti zvoka AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Vaš televizor podpira AC3-prepustnost."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Vaš televizor ne podpira AC3-prepustnosti."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Pomoč pri izboljšanju kanalov v živo"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Deljenje anonimnih podatkov o uporabi in diagnostičnih podatkov z Googlom zaradi izboljšanja Televizije v živo in preprečevanja težav, kot so zrušitve in zmrzovanje."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Če želite gledati ta kanal, pritisnite v desno in vnesite kodo PIN"</string>
@@ -165,9 +159,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Oddaja je blokirana"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Ta program ima kategorijo vsebine <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Samo zvok"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Šibek signal"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Ni internetne povezave"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Brez naslova"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Kanal je blokiran"</string>
- <string name="episode_format" msgid="4881195874563241096">"<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>. sezona: <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g>. epizoda – <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Novo"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Viri"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -196,7 +191,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Nastavljanje kanalov ni uspelo"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Za to dejanje ni bilo mogoče najti nobene aplikacije."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Vsi kanali vira so skriti.\nIzberite vsaj en kanal za gledanje."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Ni na voljo zaradi šibkega signala za video"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Video nepričakovano ni na voljo"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Tipka NAZAJ je za priključena naprave. Pritisnite DOMOV za izhod."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Kanali v živo niso podprti v tej napravi s sistemom Android Lollipop."</string>
diff --git a/res/values-sr/arrays.xml b/res/values-sr/arrays.xml
index 3a96baf4..63b952ff 100644
--- a/res/values-sr/arrays.xml
+++ b/res/values-sr/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Пун"</item>
<item msgid="8568284598210500589">"Зумиран"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Сви канали"</item>
- <item msgid="928298872841713530">"Породица/деца"</item>
- <item msgid="2751606947569857164">"Спорт"</item>
- <item msgid="7345749789651321496">"Куповина"</item>
- <item msgid="167201149441442173">"Филмови"</item>
- <item msgid="525966731464264290">"Комедија"</item>
- <item msgid="6096710741527327836">"Путовања"</item>
- <item msgid="2851882187117833883">"Драма"</item>
- <item msgid="78492781188719038">"Образовање"</item>
- <item msgid="7221999662426308394">"Животиње/дивљи свет"</item>
- <item msgid="375300513250925001">"Вести"</item>
- <item msgid="7746320336582330410">"Видео игре"</item>
- <item msgid="1255741860568329178">"Уметност"</item>
- <item msgid="7603949681065702867">"Забава"</item>
- <item msgid="4453821994746804366">"Животни стил"</item>
- <item msgid="3488534597567932843">"Музика"</item>
- <item msgid="7452153120614274095">"Премијер"</item>
- <item msgid="8215762047341133299">"Технологија/наука"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Сви канали"</item>
+ <item msgid="6897460857821394118">"Породица/деца"</item>
+ <item msgid="551257741825778215">"Спорт"</item>
+ <item msgid="452133796804325879">"Куповина"</item>
+ <item msgid="3296058637230163031">"Филмови"</item>
+ <item msgid="1054540282883891201">"Комедија"</item>
+ <item msgid="7900158429062595471">"Путовања"</item>
+ <item msgid="3768998587825611787">"Драма"</item>
+ <item msgid="8340620094959282881">"Образовање"</item>
+ <item msgid="7396447839483867269">"Животиње/дивљи свет"</item>
+ <item msgid="4738043455148062673">"Вести"</item>
+ <item msgid="7405041316051047427">"Видео игре"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Сви канали"</item>
+ <item msgid="7909003973960375395">"Породица/деца"</item>
+ <item msgid="3185279732911635789">"Спорт"</item>
+ <item msgid="4704858492065325964">"Куповина"</item>
+ <item msgid="6083795019290250078">"Филмови"</item>
+ <item msgid="8302638329222449550">"Комедија"</item>
+ <item msgid="3803709976021475052">"Путовања"</item>
+ <item msgid="8116747365234169059">"Драма"</item>
+ <item msgid="7356447541595315913">"Образовање"</item>
+ <item msgid="7511135485827589547">"Животиње/дивљи свет"</item>
+ <item msgid="6961248112238009967">"Вести"</item>
+ <item msgid="6484685553679698447">"Видео игре"</item>
+ <item msgid="2737158328243183190">"Уметност"</item>
+ <item msgid="6577176952650166615">"Забава"</item>
+ <item msgid="7886693831871777617">"Стил живота"</item>
+ <item msgid="8145832312485577062">"Музика"</item>
+ <item msgid="1345789204804308580">"Премијер"</item>
+ <item msgid="2736680312770771994">"Технологија/наука"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Канали уживо"</item>
diff --git a/res/values-sr/rating_system_strings.xml b/res/values-sr/rating_system_strings.xml
index fc0a3fd2..d4285959 100644
--- a/res/values-sr/rating_system_strings.xml
+++ b/res/values-sr/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Програми могу да садрже материјал неприкладан за публику млађу од 15 година, па се препоручује родитељски надзор."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Програми могу да садрже материјал неприкладан за публику млађу од 19 година, па зато није прикладан за младе особе млађе од 19 година."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Сугестивни дијалог"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Непристојан језик"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Сексуални садржај"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index a75a21ad..ccba3012 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -146,12 +146,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Лиценце отвореног кода"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Лиценце отвореног кода"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Верзија"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Опције за програмера"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Омогући USB тјунер за ТВ"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Да бисте чули звук USB тјунера за ТВ, ТВ треба да подржава AC3 пропуштање."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 аудио функција"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"ТВ подржава AC3 пропуштање."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"ТВ не подржава AC3 пропуштање."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Шаљи податке ради побољшања Live TV-а"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Делите анонимне податке о коришћењу и дијагностици са Google-ом да бисмо побољшали Канале уживо и спречили проблемe, попут отказивања и неодазивања."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Да бисте гледали овај канал, притисните дугме Десно и унесите PIN"</string>
@@ -163,9 +157,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Програм је блокиран"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Овај програм има оцену <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Само аудио"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Слаб сигнал"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Нема интернет везе"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Нема наслова"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Канал је блокиран"</string>
- <string name="episode_format" msgid="4881195874563241096">"Серијал <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>, епизода <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g>, <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Ново"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Извори"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -193,7 +188,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Подешавање није успело"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Није пронађена ниједна апликација која би могла да обави ову радњу."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Сви изворни канали су сакривени.\nИзаберите бар један канал који хоћете да гледате."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Није доступно због слабог видео сигнала"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Видео је неочекивано недоступан"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Тастер за НАЗАД је за повезане уређаје. Притисните дугме ПОЧЕТНА да бисте изашли."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Канали уживо нису подржани на овом уређају који користи Android Lollipop."</string>
diff --git a/res/values-sv/arrays.xml b/res/values-sv/arrays.xml
index 46fde2cf..983c165b 100644
--- a/res/values-sv/arrays.xml
+++ b/res/values-sv/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Helskärm"</item>
<item msgid="8568284598210500589">"Zoom"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Alla kanaler"</item>
- <item msgid="928298872841713530">"Familj/barn"</item>
- <item msgid="2751606947569857164">"Sport"</item>
- <item msgid="7345749789651321496">"Shopping"</item>
- <item msgid="167201149441442173">"Filmer"</item>
- <item msgid="525966731464264290">"Komedi"</item>
- <item msgid="6096710741527327836">"Resor"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Utbildning"</item>
- <item msgid="7221999662426308394">"Djur/natur"</item>
- <item msgid="375300513250925001">"Nyheter"</item>
- <item msgid="7746320336582330410">"Spel"</item>
- <item msgid="1255741860568329178">"Konst"</item>
- <item msgid="7603949681065702867">"Underhållning"</item>
- <item msgid="4453821994746804366">"Livsstil"</item>
- <item msgid="3488534597567932843">"Musik"</item>
- <item msgid="7452153120614274095">"Premiär"</item>
- <item msgid="8215762047341133299">"Teknik/vetenskap"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Alla kanaler"</item>
+ <item msgid="6897460857821394118">"Familj/barn"</item>
+ <item msgid="551257741825778215">"Sport"</item>
+ <item msgid="452133796804325879">"Shopping"</item>
+ <item msgid="3296058637230163031">"Filmer"</item>
+ <item msgid="1054540282883891201">"Komedi"</item>
+ <item msgid="7900158429062595471">"Resor"</item>
+ <item msgid="3768998587825611787">"Drama"</item>
+ <item msgid="8340620094959282881">"Utbildning"</item>
+ <item msgid="7396447839483867269">"Djur/natur"</item>
+ <item msgid="4738043455148062673">"Nyheter"</item>
+ <item msgid="7405041316051047427">"Spel"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Alla kanaler"</item>
+ <item msgid="7909003973960375395">"Familj/barn"</item>
+ <item msgid="3185279732911635789">"Sport"</item>
+ <item msgid="4704858492065325964">"Shopping"</item>
+ <item msgid="6083795019290250078">"Filmer"</item>
+ <item msgid="8302638329222449550">"Komedi"</item>
+ <item msgid="3803709976021475052">"Resor"</item>
+ <item msgid="8116747365234169059">"Drama"</item>
+ <item msgid="7356447541595315913">"Utbildning"</item>
+ <item msgid="7511135485827589547">"Djur/natur"</item>
+ <item msgid="6961248112238009967">"Nyheter"</item>
+ <item msgid="6484685553679698447">"Spel"</item>
+ <item msgid="2737158328243183190">"Konst"</item>
+ <item msgid="6577176952650166615">"Underhållning"</item>
+ <item msgid="7886693831871777617">"Livsstil"</item>
+ <item msgid="8145832312485577062">"Musik"</item>
+ <item msgid="1345789204804308580">"Premiär"</item>
+ <item msgid="2736680312770771994">"Teknik/vetenskap"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Livekanaler"</item>
diff --git a/res/values-sv/rating_system_strings.xml b/res/values-sv/rating_system_strings.xml
index c00e378c..a02461a5 100644
--- a/res/values-sv/rating_system_strings.xml
+++ b/res/values-sv/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Programmen kan ha innehåll som är olämpligt för personer under 15 år och vårdnadshavare bör därför ta ställning till innehållet."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Det kan förekomma material som är olämpligt för personer under 19 år i programmen och de bör därför inte visas av personer under 19 år."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Provokativ dialog"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Grovt språk"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Sexuellt innehåll"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index c8f457da..4627c52d 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Licenser för öppen källkod"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Öppen källkod"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Version"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Utvecklaralternativ"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Aktivera USB-TV-mottagare"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"TV:n måste ha stöd för AC3-vidarebefordran om ljudet ska höras från USB-TV-mottagaren."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Stöd för AC3-ljud"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Din TV har stöd för AC3-vidarebefordran."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Din TV har inte stöd för AC3-vidarebefordran."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Hjälp till att förbättra Livekanaler"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Dela anonym information om användning och diagnostik med Google så att vi kan förbättra Livekanaler och förhindra problem som att appen fastnar eller kraschar."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Tryck till höger och ange pinkoden om du vill titta på den här kanalen"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Programmet har blockerats"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Det här programmet har kategoriserats som <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Endast ljud"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Svag signal"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Ingen internetanslutning"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Ingen titel"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Kanalen har blockerats"</string>
- <string name="episode_format" msgid="4881195874563241096">"Säsong <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Avsnitt <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> – <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Nytt"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Källor"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Kanaljusteringen misslyckades"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Ingen app som kan hantera åtgärden hittades"</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Alla källkanaler är dolda.\nVälj minst en kanal som du vill titta på."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Inte tillgänglig på grund av svag videosignal"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Videon är inte tillgänglig"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Knappen BACK (bakåt) gäller en ansluten enhet. Avsluta genom att trycka på knappen HOME (start)."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Livekanaler stöds inte på enheter med Android Lollipop."</string>
diff --git a/res/values-sw/arrays.xml b/res/values-sw/arrays.xml
index 72fdf8ee..081ecc8b 100644
--- a/res/values-sw/arrays.xml
+++ b/res/values-sw/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Kamili"</item>
<item msgid="8568284598210500589">"Kuza"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Vituo vyote"</item>
- <item msgid="928298872841713530">"Familia/Watoto"</item>
- <item msgid="2751606947569857164">"Michezo"</item>
- <item msgid="7345749789651321496">"Ununuzi"</item>
- <item msgid="167201149441442173">"Filamu"</item>
- <item msgid="525966731464264290">"Vichekesho"</item>
- <item msgid="6096710741527327836">"Usafiri"</item>
- <item msgid="2851882187117833883">"Uigizaji"</item>
- <item msgid="78492781188719038">"Elimu"</item>
- <item msgid="7221999662426308394">"Wanyama/Wanyamapori"</item>
- <item msgid="375300513250925001">"Habari"</item>
- <item msgid="7746320336582330410">"Kamari"</item>
- <item msgid="1255741860568329178">"Sanaa"</item>
- <item msgid="7603949681065702867">"Burudani"</item>
- <item msgid="4453821994746804366">"Mitindo ya maisha"</item>
- <item msgid="3488534597567932843">"Muziki"</item>
- <item msgid="7452153120614274095">"Zinazoongoza"</item>
- <item msgid="8215762047341133299">"Teknolojia/Sayansi"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Vituo vyote"</item>
+ <item msgid="6897460857821394118">"Familia/Watoto"</item>
+ <item msgid="551257741825778215">"Spoti"</item>
+ <item msgid="452133796804325879">"Ununuzi"</item>
+ <item msgid="3296058637230163031">"Filamu"</item>
+ <item msgid="1054540282883891201">"Vichekesho"</item>
+ <item msgid="7900158429062595471">"Usafiri"</item>
+ <item msgid="3768998587825611787">"Uigizaji"</item>
+ <item msgid="8340620094959282881">"Elimu"</item>
+ <item msgid="7396447839483867269">"Wanyama/Wanyamapori"</item>
+ <item msgid="4738043455148062673">"Habari"</item>
+ <item msgid="7405041316051047427">"Michezo ya video"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Vituo vyote"</item>
+ <item msgid="7909003973960375395">"Familia/Watoto"</item>
+ <item msgid="3185279732911635789">"Spoti"</item>
+ <item msgid="4704858492065325964">"Ununuzi"</item>
+ <item msgid="6083795019290250078">"Filamu"</item>
+ <item msgid="8302638329222449550">"Vichekesho"</item>
+ <item msgid="3803709976021475052">"Usafiri"</item>
+ <item msgid="8116747365234169059">"Uigizaji"</item>
+ <item msgid="7356447541595315913">"Elimu"</item>
+ <item msgid="7511135485827589547">"Wanyama/Wanyamapori"</item>
+ <item msgid="6961248112238009967">"Habari"</item>
+ <item msgid="6484685553679698447">"Michezo ya video"</item>
+ <item msgid="2737158328243183190">"Sanaa"</item>
+ <item msgid="6577176952650166615">"Burudani"</item>
+ <item msgid="7886693831871777617">"Mitindo ya maisha"</item>
+ <item msgid="8145832312485577062">"Muziki"</item>
+ <item msgid="1345789204804308580">"Zinazoongoza"</item>
+ <item msgid="2736680312770771994">"Teknolojia/Sayansi"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Televisheni Mtandaoni"</item>
diff --git a/res/values-sw/rating_system_strings.xml b/res/values-sw/rating_system_strings.xml
index 1893c5e2..ff3b0b2c 100644
--- a/res/values-sw/rating_system_strings.xml
+++ b/res/values-sw/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Huenda programu zikawa na maudhui ambayo hayafai hadhira ya watoto wa umri wa chini ya miaka 15. Kwa hivyo, hiari ya wazazi inahitajika."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Huenda programu zikawa na maudhui yasiyoruhusiwa kwa watazamaji wa umri wa chini ya miaka 19."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Mazungumzo yanayochochea ngono"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Lugha ya matusi"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Maudhui ya ngono"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 64fd515c..146202f6 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Leseni za programu huria"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Leseni za programu huria"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Toleo"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Chaguo za wasanidi programu"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Washa kitafuta vituo cha TV cha USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Ili kusikia sauti ya kitafuta vituo cha TV cha USB, TV yako inapaswa kutumia mawimbi ya AC3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Uwezo wa sauti za faili za AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"TV yako inatumia mawimbi ya AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"TV yako haitumii mawimbi ya AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Saidia kuboresha Vituo vya Moja kwa moja"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Shiriki data isiyokutambulisha ya matumizi na uchanganuzi wa matatizo kwa Google ili tuweze kuboresha programu ya Televisheni Mtandaoni na kuzuia matatizo ya kuacha kufanya kazi na kukwama."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Ili uangalie kituo hiki, bonyeza Kulia na uweke PIN"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Kipindi kimezuiwa"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Kipindi hiki kimekadiriwa <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Sauti pekee"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Mawimbi dhaifu"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Hakuna muunganisho wa Intaneti"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Kichwa hakijaongezwa"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Kituo kimezuiwa"</string>
- <string name="episode_format" msgid="4881195874563241096">"Msimu wa <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Kipindi cha <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g><xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Vipya"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Vyanzo"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Haikuweza kurekebisha"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Hakuna programu iliyopatikana inayoweza kushughulikia kitendo hiki."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Vituo chanzo vyote vimefichwa.\nChagua angalau kituo kimoja utazame."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Haipatikani kutokana na mawimbi dhaifu ya video"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Jambo lisilotarajiwa limetokea, video haipatikani"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Kitufe cha NYUMA ni kwa ajili ya kifaa kilichounganishwa. Bonyeza kitufe cha MWANZO ili uondoe."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Kipengele cha Vituo vya Moja kwa Moja hakiwezi kutumiwa kwenye kifaa hiki kwa kutumia Android Lollipop."</string>
diff --git a/res/values-ta-rIN/arrays.xml b/res/values-ta-rIN/arrays.xml
index 9d2c1f24..f824f4d9 100644
--- a/res/values-ta-rIN/arrays.xml
+++ b/res/values-ta-rIN/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"முழு"</item>
<item msgid="8568284598210500589">"பெரிதாக்கு"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"எல்லா சேனல்களும்"</item>
- <item msgid="928298872841713530">"குடும்பம்/சிறுவர்கள்"</item>
- <item msgid="2751606947569857164">"விளையாட்டு"</item>
- <item msgid="7345749789651321496">"ஷாப்பிங்"</item>
- <item msgid="167201149441442173">"மூவிகள்"</item>
- <item msgid="525966731464264290">"நகைச்சுவை"</item>
- <item msgid="6096710741527327836">"பயணம்"</item>
- <item msgid="2851882187117833883">"நாடகம்"</item>
- <item msgid="78492781188719038">"கல்வி"</item>
- <item msgid="7221999662426308394">"விலங்குகள்"</item>
- <item msgid="375300513250925001">"செய்திகள்"</item>
- <item msgid="7746320336582330410">"கேமிங்"</item>
- <item msgid="1255741860568329178">"கலைகள்"</item>
- <item msgid="7603949681065702867">"பொழுதுபோக்கு"</item>
- <item msgid="4453821994746804366">"வாழ்க்கை முறை"</item>
- <item msgid="3488534597567932843">"இசை"</item>
- <item msgid="7452153120614274095">"ப்ரீமியர்"</item>
- <item msgid="8215762047341133299">"தொழில்நுட்பம்/அறிவியல்"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"எல்லா சேனல்களும்"</item>
+ <item msgid="6897460857821394118">"குடும்பம்/குழந்தைகள்"</item>
+ <item msgid="551257741825778215">"விளையாட்டு"</item>
+ <item msgid="452133796804325879">"ஷாப்பிங்"</item>
+ <item msgid="3296058637230163031">"திரைப்படங்கள்"</item>
+ <item msgid="1054540282883891201">"நகைச்சுவை"</item>
+ <item msgid="7900158429062595471">"பயணம்"</item>
+ <item msgid="3768998587825611787">"நாடகம்"</item>
+ <item msgid="8340620094959282881">"கல்வி"</item>
+ <item msgid="7396447839483867269">"விலங்குகள்"</item>
+ <item msgid="4738043455148062673">"செய்திகள்"</item>
+ <item msgid="7405041316051047427">"கேமிங்"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"எல்லா சேனல்களும்"</item>
+ <item msgid="7909003973960375395">"குடும்பம்/குழந்தைகள்"</item>
+ <item msgid="3185279732911635789">"விளையாட்டு"</item>
+ <item msgid="4704858492065325964">"ஷாப்பிங்"</item>
+ <item msgid="6083795019290250078">"திரைப்படங்கள்"</item>
+ <item msgid="8302638329222449550">"நகைச்சுவை"</item>
+ <item msgid="3803709976021475052">"பயணம்"</item>
+ <item msgid="8116747365234169059">"நாடகம்"</item>
+ <item msgid="7356447541595315913">"கல்வி"</item>
+ <item msgid="7511135485827589547">"விலங்குகள்"</item>
+ <item msgid="6961248112238009967">"செய்திகள்"</item>
+ <item msgid="6484685553679698447">"கேமிங்"</item>
+ <item msgid="2737158328243183190">"கலைகள்"</item>
+ <item msgid="6577176952650166615">"பொழுதுபோக்கு"</item>
+ <item msgid="7886693831871777617">"வாழ்க்கை முறை"</item>
+ <item msgid="8145832312485577062">"இசை"</item>
+ <item msgid="1345789204804308580">"பிரீமியர்"</item>
+ <item msgid="2736680312770771994">"தொழில்நுட்பம்/அறிவியல்"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"நேரலைச் சேனல்கள்"</item>
diff --git a/res/values-ta-rIN/rating_system_strings.xml b/res/values-ta-rIN/rating_system_strings.xml
index cfbb23f1..1d554f06 100644
--- a/res/values-ta-rIN/rating_system_strings.xml
+++ b/res/values-ta-rIN/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"நிகழ்ச்சிகள் 15 வயதுக்குட்பட்ட பார்வையாளர்களுக்குப் பொருத்தமற்றதாக இருக்கலாம் என்பதால், பெற்றோர் முடிவு எடுக்க வேண்டும்."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"நிகழ்ச்சிகள் 19 வயதுக்குட்பட்ட பார்வையாளர்களுக்குப் பொருத்தமற்றதாக இருக்கலாம் என்பதால், அவை 19 வயதுக்குட்பட்டவர்களுக்கு ஏற்றதல்ல."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"பாலுணர்வைத் தூண்டும் உரையாடல்"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"கொச்சை மொழி"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"பாலியல் உள்ளடக்கம்"</string>
diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml
index 095a9661..c2585bcc 100644
--- a/res/values-ta-rIN/strings.xml
+++ b/res/values-ta-rIN/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"ஓப்பன் சோர்ஸ் உரிமங்கள்"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"ஓப்பன் சோர்ஸ் உரிமங்கள்"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"பதிப்பு"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"டெவெலப்பர் விருப்பங்கள்"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"USB டிவி ட்யூனரை இயக்கு"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"USB டிவி ட்யூனரின் ஒலியைக் கேட்க, டிவி AC3 அசல் ஒலியை ஆதரிக்க வேண்டும்."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 ஆடியோ திறன்"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"உங்கள் டிவி AC3 அசல் ஒலியை ஆதரிக்கிறது."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"உங்கள் டிவி AC3 அசல் ஒலியை ஆதரிக்காது."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"நேரலைச் சேனல்களை மேம்படுத்த உதவு"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"உபயோகம் மற்றும் பிழை ஆய்வுகள் தரவை அநாமதேயமாக Google உடன் பகிரும், இதன் மூலம் எங்களால் நேரலை சேனல்களை மேலும் சிறப்பாக்க முடியும் மற்றும் சிதைவு, செயல்படாமல் இருத்தல் போன்ற சிக்கல்களைத் தடுக்க முடியும்."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"இந்தச் சேனலைப் பார்க்க, வலது பக்கம் அழுத்தி, உங்கள் PINஐ உள்ளிடவும்"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"நிகழ்ச்சி தடுக்கப்பட்டுள்ளது"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"இந்த நிகழ்ச்சி <xliff:g id="RATING">%1$s</xliff:g> என மதிப்பிடப்பட்டுள்ளது"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"ஆடியோ மட்டும்"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"வலுவற்ற சிக்னல்"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"இணைய இணைப்பு இல்லை"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"தலைப்பு இல்லை"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"சேனல் தடுக்கப்பட்டது"</string>
- <string name="episode_format" msgid="4881195874563241096">"சீ<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: எபி. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"புதியவை"</string>
<string name="setup_category_done" msgid="4750902502852212319">"மூலங்கள்"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"ட்யூன் செய்ய முடியவில்லை"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"இந்தச் செயலைச் செய்வதற்கான பயன்பாடு எதுவுமில்லை."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"எல்லா சேனல்களும் மறைக்கப்பட்டுள்ளன.\nபார்க்க, ஒரு சேனலையாவது தேர்ந்தெடுக்கவும்."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"வலுவற்ற சிக்னலால் வீடியோ கிடைக்கவில்லை"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"வீடியோ கிடைக்கவில்லை"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Back விசை இணைத்த சாதனத்திற்கானது. வெளியேற, Home பட்டனை அழுத்துக."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Android Lollipop இல் இயங்கும் இந்தச் சாதனத்தில் நேரலைச் சேனல்களுக்கு ஆதரவில்லை."</string>
diff --git a/res/values-te-rIN/arrays.xml b/res/values-te-rIN/arrays.xml
index de6abf32..b09bfa4a 100644
--- a/res/values-te-rIN/arrays.xml
+++ b/res/values-te-rIN/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"పూర్తిగా"</item>
<item msgid="8568284598210500589">"జూమ్"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"అన్ని ఛానెల్‌లు"</item>
- <item msgid="928298872841713530">"కుటుంబం/పిల్లలు"</item>
- <item msgid="2751606947569857164">"క్రీడలు"</item>
- <item msgid="7345749789651321496">"షాపింగ్"</item>
- <item msgid="167201149441442173">"చలనచిత్రాలు"</item>
- <item msgid="525966731464264290">"హాస్యం"</item>
- <item msgid="6096710741527327836">"ప్రయాణం"</item>
- <item msgid="2851882187117833883">"నాటకం"</item>
- <item msgid="78492781188719038">"విద్య"</item>
- <item msgid="7221999662426308394">"జంతువు/వన్యప్రాణులు"</item>
- <item msgid="375300513250925001">"వార్తలు"</item>
- <item msgid="7746320336582330410">"గేమింగ్"</item>
- <item msgid="1255741860568329178">"కళలు"</item>
- <item msgid="7603949681065702867">"వినోదం"</item>
- <item msgid="4453821994746804366">"జీవన శైలి"</item>
- <item msgid="3488534597567932843">"సంగీతం"</item>
- <item msgid="7452153120614274095">"ప్రీమియర్"</item>
- <item msgid="8215762047341133299">"సాంకేతికం/శాస్త్రం"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"అన్ని ఛానెల్‌లు"</item>
+ <item msgid="6897460857821394118">"కుటుంబం/పిల్లలు"</item>
+ <item msgid="551257741825778215">"క్రీడలు"</item>
+ <item msgid="452133796804325879">"షాపింగ్"</item>
+ <item msgid="3296058637230163031">"చలనచిత్రాలు"</item>
+ <item msgid="1054540282883891201">"హాస్యం"</item>
+ <item msgid="7900158429062595471">"ప్రయాణం"</item>
+ <item msgid="3768998587825611787">"నాటకం"</item>
+ <item msgid="8340620094959282881">"విద్య"</item>
+ <item msgid="7396447839483867269">"జంతువు/వన్యప్రాణులు"</item>
+ <item msgid="4738043455148062673">"వార్తలు"</item>
+ <item msgid="7405041316051047427">"గేమ్‌లు ఆడటం"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"అన్ని ఛానెల్‌లు"</item>
+ <item msgid="7909003973960375395">"కుటుంబం/పిల్లలు"</item>
+ <item msgid="3185279732911635789">"క్రీడలు"</item>
+ <item msgid="4704858492065325964">"షాపింగ్"</item>
+ <item msgid="6083795019290250078">"చలనచిత్రాలు"</item>
+ <item msgid="8302638329222449550">"హాస్యం"</item>
+ <item msgid="3803709976021475052">"ప్రయాణం"</item>
+ <item msgid="8116747365234169059">"నాటకం"</item>
+ <item msgid="7356447541595315913">"విద్య"</item>
+ <item msgid="7511135485827589547">"జంతువు/వన్యప్రాణులు"</item>
+ <item msgid="6961248112238009967">"వార్తలు"</item>
+ <item msgid="6484685553679698447">"గేమ్‌లు ఆడటం"</item>
+ <item msgid="2737158328243183190">"కళలు"</item>
+ <item msgid="6577176952650166615">"వినోదం"</item>
+ <item msgid="7886693831871777617">"జీవనశైలి"</item>
+ <item msgid="8145832312485577062">"సంగీతం"</item>
+ <item msgid="1345789204804308580">"ప్రీమియర్"</item>
+ <item msgid="2736680312770771994">"సాంకేతికం/శాస్త్రం"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"ప్రత్యక్ష ప్రసార ఛానెల్‌లు"</item>
diff --git a/res/values-te-rIN/rating_system_strings.xml b/res/values-te-rIN/rating_system_strings.xml
index cd24f7b5..eed71aab 100644
--- a/res/values-te-rIN/rating_system_strings.xml
+++ b/res/values-te-rIN/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"కార్యక్రమాల్లో 15 ఏళ్లలోపు ప్రేక్షకులకు అనుచితమైన విషయాలు ఉండవచ్చు, కనుక తల్లిదండ్రులు వారి అభీష్టానుసారంగా నిర్ణయం తీసుకోవాలి."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"కార్యక్రమాల్లో 19 ఏళ్లలోపు ప్రేక్షకులకు అనుచితమైన విషయాలు ఉండవచ్చు, కనుక 19 ఏళ్లలోపు యువతకు తగినవి కావు."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"సరస శృంగార వ్యాఖ్యలు"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"అనాగరిక భాష"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"శృంగార విషయాలు"</string>
diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml
index 5505a8ac..0bc83633 100644
--- a/res/values-te-rIN/strings.xml
+++ b/res/values-te-rIN/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"ఓపెన్ సోర్స్ లైసెన్స్‌లు"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"ఓపెన్ సోర్స్ లైసెన్స్‌లు"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"సంస్కరణ"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"డెవలపర్ ఎంపికలు"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"USB టీవీ ట్యూనర్‌ను ప్రారంభించు"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"USB టీవీ ట్యూనర్ శబ్దం వినిపించడానికి, మీ టీవీ AC3 గుండా ప్రసరణకు మద్దతు ఇవ్వాలి."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 ఆడియో సామర్థ్యం"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"మీ టీవీ AC3 గుండా ప్రసరణకు మద్దతు ఇస్తుంది."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"మీ టీవీ AC3 గుండా ప్రసరణకు మద్దతు ఇవ్వదు."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"ప్రత్యక్ష ప్రసార ఛానెల్‌లను మెరుగుపరచడంలో సహాయపడండి"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"అనామక వినియోగ మరియు విశ్లేషణల డేటాను Googleతో భాగస్వామ్యం చేయండి, తద్వారా మేము లైవ్ ఛానెల్‌లను మెరుగ్గా చేయగలము అలాగే క్రాష్ కావడం మరియు స్తంభించడం వంటి సమస్యలను నిరోధించగలము."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"ఈ ఛానెల్‌ను చూడటానికి, కుడివైపు బటన్ నొక్కి, మీ పిన్‌ని నమోదు చేయండి"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"కార్యక్రమం బ్లాక్ చేయబడింది"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"ఈ కార్యక్రమం <xliff:g id="RATING">%1$s</xliff:g> అని రేట్ చేయబడింది"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"ఆడియో మాత్రమే"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"సిగ్నల్ బలహీనంగా ఉంది"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"ఇంటర్నెట్ కనెక్షన్ లేదు"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"శీర్షిక లేదు"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"ఛానెల్ బ్లాక్ చేయబడింది"</string>
- <string name="episode_format" msgid="4881195874563241096">"సీ.<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: ఎపి. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"కొత్తవి"</string>
<string name="setup_category_done" msgid="4750902502852212319">"మూలాలు"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"ట్యూన్ విఫలమైంది"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"ఈ చర్యను నిర్వహించడానికి అనువర్తనం ఏదీ కనుగొనబడలేదు."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"అన్ని మూల ఛానెల్‌లు దాచబడ్డాయి.\nచూడటానికి కనీసం ఒక ఛానెల్‌ను ఎంచుకోండి."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"వీడియో సిగ్నల్ బలహీనంగా ఉన్నందున అందుబాటులో లేదు"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"వీడియో ఊహించని విధంగా అందుబాటులో లేకుండా పోయింది"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"BACK కీ కనెక్ట్ చేయబడిన పరికరం కోసం ఉద్దేశించినది. నిష్క్రమించడానికి HOME బటన్‌ను నొక్కండి."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Android Lollipop అమలులో ఉన్న ఈ పరికరంలో ప్రత్యక్ష ప్రసార ఛానెల్‌లకు మద్దతు లేదు."</string>
diff --git a/res/values-th/arrays.xml b/res/values-th/arrays.xml
index 105725f8..e7e63988 100644
--- a/res/values-th/arrays.xml
+++ b/res/values-th/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"เต็มจอ"</item>
<item msgid="8568284598210500589">"ซูม"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"ช่องทั้งหมด"</item>
- <item msgid="928298872841713530">"ครอบครัว/เด็ก"</item>
- <item msgid="2751606947569857164">"กีฬา"</item>
- <item msgid="7345749789651321496">"ช็อปปิ้ง"</item>
- <item msgid="167201149441442173">"ภาพยนตร์"</item>
- <item msgid="525966731464264290">"ตลก"</item>
- <item msgid="6096710741527327836">"การเดินทาง"</item>
- <item msgid="2851882187117833883">"ดราม่า"</item>
- <item msgid="78492781188719038">"การศึกษา"</item>
- <item msgid="7221999662426308394">"สัตว์/สัตว์ป่า"</item>
- <item msgid="375300513250925001">"ข่าวสาร"</item>
- <item msgid="7746320336582330410">"เกม"</item>
- <item msgid="1255741860568329178">"ศิลปะ"</item>
- <item msgid="7603949681065702867">"บันเทิง"</item>
- <item msgid="4453821994746804366">"ไลฟ์สไตล์"</item>
- <item msgid="3488534597567932843">"เพลง"</item>
- <item msgid="7452153120614274095">"ปฐมทัศน์"</item>
- <item msgid="8215762047341133299">"วิทยาศาสตร์/เทคฯ"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"ช่องทั้งหมด"</item>
+ <item msgid="6897460857821394118">"ครอบครัว/เด็ก"</item>
+ <item msgid="551257741825778215">"กีฬา"</item>
+ <item msgid="452133796804325879">"ช็อปปิ้ง"</item>
+ <item msgid="3296058637230163031">"ภาพยนตร์"</item>
+ <item msgid="1054540282883891201">"ตลก"</item>
+ <item msgid="7900158429062595471">"ท่องเที่ยว"</item>
+ <item msgid="3768998587825611787">"ดราม่า"</item>
+ <item msgid="8340620094959282881">"การศึกษา"</item>
+ <item msgid="7396447839483867269">"สัตว์/สัตว์ป่า"</item>
+ <item msgid="4738043455148062673">"ข่าวสาร"</item>
+ <item msgid="7405041316051047427">"เกม"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"ช่องทั้งหมด"</item>
+ <item msgid="7909003973960375395">"ครอบครัว/เด็ก"</item>
+ <item msgid="3185279732911635789">"กีฬา"</item>
+ <item msgid="4704858492065325964">"ช็อปปิ้ง"</item>
+ <item msgid="6083795019290250078">"ภาพยนตร์"</item>
+ <item msgid="8302638329222449550">"ตลก"</item>
+ <item msgid="3803709976021475052">"ท่องเที่ยว"</item>
+ <item msgid="8116747365234169059">"ดราม่า"</item>
+ <item msgid="7356447541595315913">"การศึกษา"</item>
+ <item msgid="7511135485827589547">"สัตว์/สัตว์ป่า"</item>
+ <item msgid="6961248112238009967">"ข่าวสาร"</item>
+ <item msgid="6484685553679698447">"เกม"</item>
+ <item msgid="2737158328243183190">"ศิลปะ"</item>
+ <item msgid="6577176952650166615">"ความบันเทิง"</item>
+ <item msgid="7886693831871777617">"ไลฟ์สไตล์"</item>
+ <item msgid="8145832312485577062">"เพลง"</item>
+ <item msgid="1345789204804308580">"ปฐมทัศน์"</item>
+ <item msgid="2736680312770771994">"วิทยาศาสตร์/เทคฯ"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"รายการถ่ายทอดสด"</item>
diff --git a/res/values-th/rating_system_strings.xml b/res/values-th/rating_system_strings.xml
index f0b6715d..1e6fd44a 100644
--- a/res/values-th/rating_system_strings.xml
+++ b/res/values-th/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"โปรแกรมอาจมีเนื้อหาที่ไม่เหมาะสมสำหรับผู้ชมอายุต่ำกว่า 15 ปี ผู้ปกครองควรให้คำแนะนำ"</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"โปรแกรมอาจมีเนื้อหาที่ไม่เหมาะสมสำหรับผู้ชมอายุต่ำกว่า 19 ปี ดังนั้นจึงไม่เหมาะสำหรับเยาวชนอายุต่ำกว่า 19 ปี"</string>
<string name="description_us_tv_d" msgid="12333789157204816">"การสนทนาที่มีการชี้นำทางเพศ"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"ภาษาหยาบคาย"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"เนื้อหาเกี่ยวกับเรื่องเพศ"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 2b6d2f75..997d6fcb 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"ใบอนุญาตโอเพนซอร์ส"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"ใบอนุญาตโอเพนซอร์ส"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"เวอร์ชัน"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"ตัวเลือกสำหรับนักพัฒนา"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"เปิดใช้ตัวรับสัญญาณทีวีผ่าน USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"หากต้องการได้ยินเสียงของตัวรับสัญญาณทีวีผ่าน USB ทีวีของคุณต้องสนับสนุนการส่งผ่าน AC3"</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"ความสามารถเกี่ยวกับระบบเสียง AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"ทีวีของคุณสนับสนุนการส่งผ่าน AC3"</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"ทีวีของคุณไม่สนับสนุนการส่งผ่าน AC3"</string>
<string name="about_menu_improve" msgid="3712578027009311401">"ช่วยปรับปรุง Live TV"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"แชร์ข้อมูลการใช้งานและการวินิจฉัยที่ไม่ระบุชื่อกับ Google เพื่อให้เราสามารถทำให้รายการถ่ายทอดสดดีขึ้น และป้องกันปัญหาต่างๆ เช่น การขัดข้องและการค้าง"</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"หากต้องการดูช่องนี้ ให้กดขวาและป้อน PIN"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"ระบบบล็อกโปรแกรมไว้"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"โปรแกรมนี้เรต <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"เฉพาะเสียง"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"สัญญาณไม่ดี"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"ไม่มีการเชื่อมต่ออินเทอร์เน็ต"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"ไม่มีชื่อ"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"บล็อกช่องแล้ว"</string>
- <string name="episode_format" msgid="4881195874563241096">"ซีซัน <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: ตอนที่ <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"ใหม่"</string>
<string name="setup_category_done" msgid="4750902502852212319">"แหล่งที่มา"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"การรับสัญญาณล้มเหลว"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"ไม่พบแอปสำหรับการทำงานนี้"</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"ช่องแหล่งที่มาทั้งหมดซ่อนอยู่\nเลือกอย่างน้อย 1 ช่องเพื่อดู"</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"ไม่พร้อมใช้งานเนื่องจากสัญญาณวิดีโอไม่ดี"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"วิดีโอไม่พร้อมใช้งานด้วยเหตุบางประการ"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"แป้นกลับมีไว้สำหรับอุปกรณ์ที่เชื่อมต่อ กดปุ่มหน้าแรกเพื่อออก"</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Live TV ไม่ได้รับการสนับสนุนบนอุปกรณ์นี้ซึ่งมี Android Lollipop"</string>
diff --git a/res/values-tl/arrays.xml b/res/values-tl/arrays.xml
index 3c1bf603..8b31c423 100644
--- a/res/values-tl/arrays.xml
+++ b/res/values-tl/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Buo"</item>
<item msgid="8568284598210500589">"Mag-zoom"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Lahat ng channel"</item>
- <item msgid="928298872841713530">"Pampamilya/Pambata"</item>
- <item msgid="2751606947569857164">"Sports"</item>
- <item msgid="7345749789651321496">"Pamimili"</item>
- <item msgid="167201149441442173">"Mga Pelikula"</item>
- <item msgid="525966731464264290">"Komedya"</item>
- <item msgid="6096710741527327836">"Paglalakbay"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Edukasyon"</item>
- <item msgid="7221999662426308394">"Hayop/Wildlife"</item>
- <item msgid="375300513250925001">"Balita"</item>
- <item msgid="7746320336582330410">"Paglalaro"</item>
- <item msgid="1255741860568329178">"Sining"</item>
- <item msgid="7603949681065702867">"Entertainment"</item>
- <item msgid="4453821994746804366">"Estilo ng Pamumuhay"</item>
- <item msgid="3488534597567932843">"Musika"</item>
- <item msgid="7452153120614274095">"Premier"</item>
- <item msgid="8215762047341133299">"Teknolohiya/Agham"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Lahat ng channel"</item>
+ <item msgid="6897460857821394118">"Pampamilya/Pambata"</item>
+ <item msgid="551257741825778215">"Sports"</item>
+ <item msgid="452133796804325879">"Pamimili"</item>
+ <item msgid="3296058637230163031">"Mga Pelikula"</item>
+ <item msgid="1054540282883891201">"Komedya"</item>
+ <item msgid="7900158429062595471">"Paglalakbay"</item>
+ <item msgid="3768998587825611787">"Drama"</item>
+ <item msgid="8340620094959282881">"Edukasyon"</item>
+ <item msgid="7396447839483867269">"Hayop/Wildlife"</item>
+ <item msgid="4738043455148062673">"Balita"</item>
+ <item msgid="7405041316051047427">"Gaming"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Lahat ng channel"</item>
+ <item msgid="7909003973960375395">"Pampamilya/Pambata"</item>
+ <item msgid="3185279732911635789">"Sports"</item>
+ <item msgid="4704858492065325964">"Pamimili"</item>
+ <item msgid="6083795019290250078">"Mga Pelikula"</item>
+ <item msgid="8302638329222449550">"Komedya"</item>
+ <item msgid="3803709976021475052">"Paglalakbay"</item>
+ <item msgid="8116747365234169059">"Drama"</item>
+ <item msgid="7356447541595315913">"Edukasyon"</item>
+ <item msgid="7511135485827589547">"Hayop/Wildlife"</item>
+ <item msgid="6961248112238009967">"Balita"</item>
+ <item msgid="6484685553679698447">"Gaming"</item>
+ <item msgid="2737158328243183190">"Sining"</item>
+ <item msgid="6577176952650166615">"Entertainment"</item>
+ <item msgid="7886693831871777617">"Lifestyle"</item>
+ <item msgid="8145832312485577062">"Musika"</item>
+ <item msgid="1345789204804308580">"Premier"</item>
+ <item msgid="2736680312770771994">"Teknolohiya/Agham"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Mga Live na Channel"</item>
diff --git a/res/values-tl/rating_system_strings.xml b/res/values-tl/rating_system_strings.xml
index 5c95bf2e..e1f9f940 100644
--- a/res/values-tl/rating_system_strings.xml
+++ b/res/values-tl/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Ang mga programa ay maaaring may mga eksenang hindi naaangkop sa mga manonood na wala pang 15 taong gulang. Samakatuwid, dapat gabayan ng mga magulang ang kanilang mga anak sa panonood."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Ang mga programa ay maaaring may mga eksenang hindi naaangkop sa mga manonood na wala pang 19 taong gulang. Samakatuwid, ang mga ito ay hindi naaangkop sa mga kabataang wala pang 19 taong gulang."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Magpahiwatig na usapan"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Magaspang na pananalita"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Sekswal na content"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 7cacf4bf..a3645c0f 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Mga open source na lisensya"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Mga lisensyang open source"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Bersyon"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Mga opsyon ng developer"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"I-enable ang USB TV tuner"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Upang marinig ang tunog ng USB TV tuner, dapat suportahan ng iyong TV ang AC3 passthrough."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Kakayahang gumamit ng AC3 audio"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Sinusuportahan ng iyong TV ang AC3 passthrough."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Hindi sinusuportahan ng iyong TV ang AC3 passthrough."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Tulungang pahusayin ang Mga Live na Channel"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Magbahagi ng anonymous na data ng paggamit at mga diagnostic sa Google upang mas mapaganda namin ang Mga Live Channel at mapigilan ang mga isyu tulad ng pagka-crash at pagfi-freeze."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Upang mapanood ang channel na ito, pindutin ang Kanan at ilagay ang iyong PIN"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Naka-block ang programa"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Ang programang ito ay na-rate na <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Audio lang"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Mahinang signal"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Walang koneksyon sa internet"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Walang pamagat"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Naka-block ang channel"</string>
- <string name="episode_format" msgid="4881195874563241096">"S<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Bago"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Mga Pinagmumulan"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Hindi na-tune"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Walang nakitang app na gagawa sa aksyong ito."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Nakatago ang lahat ng pinagmumulang channel.\nPumili ng kahit isang channel lang na papanoorin."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Hindi available dahil sa mahinang signal ng video"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Hindi inaasahang hindi available ang video"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Ang BACK key ay para sa nakakonektang device. Pindutin ang HOME button upang lumabas."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Hindi sinusuportahan ang Live TV sa device na ito na may Android Lollipop."</string>
diff --git a/res/values-tr/arrays.xml b/res/values-tr/arrays.xml
index 3dfd8e76..5ef36030 100644
--- a/res/values-tr/arrays.xml
+++ b/res/values-tr/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Tam"</item>
<item msgid="8568284598210500589">"Zum"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Tüm kanallar"</item>
- <item msgid="928298872841713530">"Aile/Çocuk"</item>
- <item msgid="2751606947569857164">"Spor"</item>
- <item msgid="7345749789651321496">"Alışveriş"</item>
- <item msgid="167201149441442173">"Filmler"</item>
- <item msgid="525966731464264290">"Komedi"</item>
- <item msgid="6096710741527327836">"Seyahat"</item>
- <item msgid="2851882187117833883">"Dram"</item>
- <item msgid="78492781188719038">"Eğitim"</item>
- <item msgid="7221999662426308394">"Hayvanlar/Vahşi Yaşam"</item>
- <item msgid="375300513250925001">"Haberler"</item>
- <item msgid="7746320336582330410">"Oyun"</item>
- <item msgid="1255741860568329178">"Sanat"</item>
- <item msgid="7603949681065702867">"Eğlence"</item>
- <item msgid="4453821994746804366">"Yaşam Tarzı"</item>
- <item msgid="3488534597567932843">"Müzik"</item>
- <item msgid="7452153120614274095">"İlk Gösterim"</item>
- <item msgid="8215762047341133299">"Teknoloji/Bilim"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Tüm kanallar"</item>
+ <item msgid="6897460857821394118">"Aile/Çocuk"</item>
+ <item msgid="551257741825778215">"Spor"</item>
+ <item msgid="452133796804325879">"Alışveriş"</item>
+ <item msgid="3296058637230163031">"Filmler"</item>
+ <item msgid="1054540282883891201">"Komedi"</item>
+ <item msgid="7900158429062595471">"Seyahat"</item>
+ <item msgid="3768998587825611787">"Dram"</item>
+ <item msgid="8340620094959282881">"Eğitim"</item>
+ <item msgid="7396447839483867269">"Hayvanlar/Vahşi Doğa"</item>
+ <item msgid="4738043455148062673">"Haberler"</item>
+ <item msgid="7405041316051047427">"Oyun"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Tüm kanallar"</item>
+ <item msgid="7909003973960375395">"Aile/Çocuk"</item>
+ <item msgid="3185279732911635789">"Spor"</item>
+ <item msgid="4704858492065325964">"Alışveriş"</item>
+ <item msgid="6083795019290250078">"Filmler"</item>
+ <item msgid="8302638329222449550">"Komedi"</item>
+ <item msgid="3803709976021475052">"Seyahat"</item>
+ <item msgid="8116747365234169059">"Dram"</item>
+ <item msgid="7356447541595315913">"Eğitim"</item>
+ <item msgid="7511135485827589547">"Hayvanlar/Vahşi Doğa"</item>
+ <item msgid="6961248112238009967">"Haberler"</item>
+ <item msgid="6484685553679698447">"Oyun"</item>
+ <item msgid="2737158328243183190">"Sanat"</item>
+ <item msgid="6577176952650166615">"Eğlence"</item>
+ <item msgid="7886693831871777617">"Yaşam Tarzı"</item>
+ <item msgid="8145832312485577062">"Müzik"</item>
+ <item msgid="1345789204804308580">"İlk Gösterim"</item>
+ <item msgid="2736680312770771994">"Teknoloji/Bilim"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Canlı Yayın Kanalları"</item>
diff --git a/res/values-tr/rating_system_strings.xml b/res/values-tr/rating_system_strings.xml
index ebac04cc..232be2c8 100644
--- a/res/values-tr/rating_system_strings.xml
+++ b/res/values-tr/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Programlar 15 yaşından küçükler için uygunsuz malzemeler içerebileceğinden bu programları çocuklara izletip izletmemeye ebeveynler karar vermelidir."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Programlar 19 yaşından küçükler için uygunsuz malzemeler içerebileceğinden bu kitle için uygun değildir."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Müstehcen konuşma"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Kaba dil"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Cinsel içerik"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 9856bafc..92829fa3 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Açık kaynak lisansları"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Açık kaynak lisansları"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Sürüm"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Geliştirici seçenekleri"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"USB TV kanal ayarlayıcıyı etkinleştir"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"USB TV kanal ayarlayıcıdan ses alabilmek için TV\'nizin AC3 geçişini desteklemesi gerekir."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 ses özelliği"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"TV\'niz AC3 geçişini destekliyor."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"TV\'niz AC3 geçişini desteklemiyor."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Live TV\'ı iyileştirmeye yardımcı ol"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Canlı Kanallar\'ı daha iyi hale getirebilmemiz ve kilitlenme, donma gibi sorunları önleyebilmemeiz için anonim kullanım ve teşhis verilerini Google ile paylaşın."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Bu kanalı izlemek için Sağ tuşuna basın ve PIN\'inizi girin"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Program engellendi"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Bu program <xliff:g id="RATING">%1$s</xliff:g> olarak derecelendirilmiştir"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Yalnızca ses"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Zayıf sinyal"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"İnternet bağlantısı yok"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Başlıksız"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Kanal engellendi"</string>
- <string name="episode_format" msgid="4881195874563241096">"S<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Böl. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Yeni"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Kaynaklar"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Tarama işlemi başarısız oldu"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Bu işlemi gerçekleştirecek uygulama bulunamadı."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Tüm kaynak kanalları gizli.\nİzlemek için en az bir tane kanal seçin."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Zayıf video sinyali nedeniyle kullanılamıyor"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Video beklenmedik şekilde kullanılamıyor"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"GERİ tuşu bağlı cihazlar içindir. Çıkmak için ANA SAYFA düğmesine basın."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Bu cihazda Android Lollipop ile Canlı Kanallar desteklenmemektedir."</string>
diff --git a/res/values-uk/arrays.xml b/res/values-uk/arrays.xml
index 4dba1d71..2fa994ef 100644
--- a/res/values-uk/arrays.xml
+++ b/res/values-uk/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"На весь екран"</item>
<item msgid="8568284598210500589">"Масштабування"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Усі канали"</item>
- <item msgid="928298872841713530">"Сім’я та діти"</item>
- <item msgid="2751606947569857164">"Спорт"</item>
- <item msgid="7345749789651321496">"Покупки"</item>
- <item msgid="167201149441442173">"Фільми"</item>
- <item msgid="525966731464264290">"Комедія"</item>
- <item msgid="6096710741527327836">"Подорожі"</item>
- <item msgid="2851882187117833883">"Драма"</item>
- <item msgid="78492781188719038">"Освіта"</item>
- <item msgid="7221999662426308394">"Тварини та природа"</item>
- <item msgid="375300513250925001">"Новини"</item>
- <item msgid="7746320336582330410">"Ігри"</item>
- <item msgid="1255741860568329178">"Мистецтво"</item>
- <item msgid="7603949681065702867">"Розваги"</item>
- <item msgid="4453821994746804366">"Стиль життя"</item>
- <item msgid="3488534597567932843">"Музика"</item>
- <item msgid="7452153120614274095">"Популярне"</item>
- <item msgid="8215762047341133299">"Наука й техніка"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Усі канали"</item>
+ <item msgid="6897460857821394118">"Сім’я та діти"</item>
+ <item msgid="551257741825778215">"Спорт"</item>
+ <item msgid="452133796804325879">"Покупки"</item>
+ <item msgid="3296058637230163031">"Фільми"</item>
+ <item msgid="1054540282883891201">"Комедії"</item>
+ <item msgid="7900158429062595471">"Подорожі"</item>
+ <item msgid="3768998587825611787">"Драми"</item>
+ <item msgid="8340620094959282881">"Освіта"</item>
+ <item msgid="7396447839483867269">"Тварини та природа"</item>
+ <item msgid="4738043455148062673">"Новини"</item>
+ <item msgid="7405041316051047427">"Ігри"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Усі канали"</item>
+ <item msgid="7909003973960375395">"Сім’я та діти"</item>
+ <item msgid="3185279732911635789">"Спорт"</item>
+ <item msgid="4704858492065325964">"Покупки"</item>
+ <item msgid="6083795019290250078">"Фільми"</item>
+ <item msgid="8302638329222449550">"Комедії"</item>
+ <item msgid="3803709976021475052">"Подорожі"</item>
+ <item msgid="8116747365234169059">"Драми"</item>
+ <item msgid="7356447541595315913">"Освіта"</item>
+ <item msgid="7511135485827589547">"Тварини та природа"</item>
+ <item msgid="6961248112238009967">"Новини"</item>
+ <item msgid="6484685553679698447">"Ігри"</item>
+ <item msgid="2737158328243183190">"Мистецтво"</item>
+ <item msgid="6577176952650166615">"Розваги"</item>
+ <item msgid="7886693831871777617">"Стиль життя"</item>
+ <item msgid="8145832312485577062">"Музика"</item>
+ <item msgid="1345789204804308580">"Популярне"</item>
+ <item msgid="2736680312770771994">"Наука й техніка"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Телеканали"</item>
diff --git a/res/values-uk/rating_system_strings.xml b/res/values-uk/rating_system_strings.xml
index a26df0ba..27afc388 100644
--- a/res/values-uk/rating_system_strings.xml
+++ b/res/values-uk/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Телепередачі можуть містити матеріал, неприйнятний для користувачів віком до 15 років. Батьки самі вирішують, чи дозволяти перегляд."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Телепередачі можуть містити матеріал, неприйнятний для користувачів віком до 19 років. Батьки самі вирішують, чи дозволяти перегляд."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Непристойні діалоги"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Вульгарні висловлювання"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Вміст сексуального характеру"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index a15fb797..2b4dfddc 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -148,12 +148,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Ліцензії відкритого коду"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Ліцензії ПЗ з відкритим кодом"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Версія"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Параметри розробника"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Увімкнути ТВ-тюнер USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Щоб ваш телевізор сприймав звук ТВ-тюнера USB, на ньому має підтримуватися транзит AC3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Підтримка транзиту аудіо AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"На вашому телевізорі підтримується транзит AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"На вашому телевізорі не підтримується транзит AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Допомагати покращити додаток Live TV"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Надсилати в Google анонімні дані про використання та діагностику. Це допоможе нам покращити додаток Телеканали та вирішити такі проблеми, як аварійне завершення роботи й зависання."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Щоб дивитися цей канал, натисніть стрілку праворуч і введіть PIN-код"</string>
@@ -165,9 +159,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Телепередачу заблоковано"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Ця телепередача належить до категорії <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Лише аудіо"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Слабкий сигнал"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Немає з’єднання з Інтернетом"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Без назви"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Канал заблоковано"</string>
- <string name="episode_format" msgid="4881195874563241096">"Сезон <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>, серія <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g>: \"<xliff:g id="EPISODE_TITLE">%3$s</xliff:g>\""</string>
<string name="setup_category_new" msgid="2899355289563443627">"Нові джерела"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Джерела"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -184,7 +179,7 @@
<string name="new_sources_title" msgid="3878933676500061895">"Доступні нові джерела каналів"</string>
<string name="new_sources_description" msgid="749649005588426813">"Нові джерела пропонують канали.\nНалаштуйте їх зараз або зробіть це пізніше в налаштуваннях джерел каналів."</string>
<string name="new_sources_action_setup" msgid="177693761664016811">"Налаштувати"</string>
- <string name="new_sources_action_skip" msgid="2501296961258184330">"Зрозуміло"</string>
+ <string name="new_sources_action_skip" msgid="2501296961258184330">"OK"</string>
<!-- no translation found for intro_title (251772896916795556) -->
<skip />
<string name="intro_description" msgid="7806473686446937307"><b>"Натисніть \"ВИБРАТИ\""</b>", щоб відкрити меню телевізора."</string>
@@ -196,7 +191,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Не вдалося налаштувати"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Не знайдено додатка для цієї дії."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Усі канали джерела сховано.\nВиберіть принаймні один канал для перегляду."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Відео недоступне через слабкий сигнал"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Відео недоступне з невідомої причини"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Клавіша \"НАЗАД\" діє на підключеному пристрої. Натисніть \"ГОЛОВНИЙ ЕКРАН\", щоб вийти."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Додаток Live TV не підтримується на цьому пристрої з ОС Android Lollipop."</string>
diff --git a/res/values-ur-rPK/arrays.xml b/res/values-ur-rPK/arrays.xml
index f54b4914..1b581640 100644
--- a/res/values-ur-rPK/arrays.xml
+++ b/res/values-ur-rPK/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"مکمل"</item>
<item msgid="8568284598210500589">"زوم"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"سبھی چینلز"</item>
- <item msgid="928298872841713530">"خاندان/بچے"</item>
- <item msgid="2751606947569857164">"کھیل"</item>
- <item msgid="7345749789651321496">"خریداری"</item>
- <item msgid="167201149441442173">"موویز"</item>
- <item msgid="525966731464264290">"کامیڈی"</item>
- <item msgid="6096710741527327836">"سفر"</item>
- <item msgid="2851882187117833883">"ڈراما"</item>
- <item msgid="78492781188719038">"تعلیم"</item>
- <item msgid="7221999662426308394">"جانور/وائلڈ لائف"</item>
- <item msgid="375300513250925001">"خبریں"</item>
- <item msgid="7746320336582330410">"گیم سازی"</item>
- <item msgid="1255741860568329178">"فنون"</item>
- <item msgid="7603949681065702867">"تفریح"</item>
- <item msgid="4453821994746804366">"طرز زندگی"</item>
- <item msgid="3488534597567932843">"موسیقی"</item>
- <item msgid="7452153120614274095">"پریمیئر"</item>
- <item msgid="8215762047341133299">"ٹیک/سائنس"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"سبھی چینلز"</item>
+ <item msgid="6897460857821394118">"خاندان/بچے"</item>
+ <item msgid="551257741825778215">"کھیل"</item>
+ <item msgid="452133796804325879">"خریداری"</item>
+ <item msgid="3296058637230163031">"موویز"</item>
+ <item msgid="1054540282883891201">"کامیڈی"</item>
+ <item msgid="7900158429062595471">"سفر"</item>
+ <item msgid="3768998587825611787">"ڈرامہ"</item>
+ <item msgid="8340620094959282881">"تعلیم"</item>
+ <item msgid="7396447839483867269">"جانور/وائلڈ لائف"</item>
+ <item msgid="4738043455148062673">"خبریں"</item>
+ <item msgid="7405041316051047427">"گیمنگ"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"سبھی چینلز"</item>
+ <item msgid="7909003973960375395">"خاندان/بچے"</item>
+ <item msgid="3185279732911635789">"کھیل"</item>
+ <item msgid="4704858492065325964">"خریداری"</item>
+ <item msgid="6083795019290250078">"موویز"</item>
+ <item msgid="8302638329222449550">"کامیڈی"</item>
+ <item msgid="3803709976021475052">"سفر"</item>
+ <item msgid="8116747365234169059">"ڈرامہ"</item>
+ <item msgid="7356447541595315913">"تعلیم"</item>
+ <item msgid="7511135485827589547">"جانور/وائلڈ لائف"</item>
+ <item msgid="6961248112238009967">"خبریں"</item>
+ <item msgid="6484685553679698447">"گیمنگ"</item>
+ <item msgid="2737158328243183190">"فنون"</item>
+ <item msgid="6577176952650166615">"تفریح"</item>
+ <item msgid="7886693831871777617">"طرز زندگی"</item>
+ <item msgid="8145832312485577062">"موسیقی"</item>
+ <item msgid="1345789204804308580">"پریمیئر"</item>
+ <item msgid="2736680312770771994">"ٹیک/سائنس"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"لائیو چینلز"</item>
diff --git a/res/values-ur-rPK/rating_system_strings.xml b/res/values-ur-rPK/rating_system_strings.xml
index 901bb634..f5f48c4f 100644
--- a/res/values-ur-rPK/rating_system_strings.xml
+++ b/res/values-ur-rPK/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"پروگراموں میں ایسا مواد ہو سکتا ہے جو 15 سال سے کم عمر کے بچوں کیلئے غیر مناسب ہو، اس لئے ان کیلئے والدین کی صوابدید استعمال کرنی چاہئیے۔"</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"پروگراموں میں ایسا مواد ہو سکتا ہے جو 19 سال سے کم عمر افراد کیلئے غیر مناسب ہو، اور اس لئے یہ پروگرام 19 سال سے چھوٹے نوجوانوں کیلئے موزوں نہیں ہیں۔"</string>
<string name="description_us_tv_d" msgid="12333789157204816">"ہیجان خیز بات چیت"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"نامناسب زبان"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"جنسی مواد"</string>
diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml
index d49edc92..6633f45a 100644
--- a/res/values-ur-rPK/strings.xml
+++ b/res/values-ur-rPK/strings.xml
@@ -94,7 +94,7 @@
</plurals>
<string name="msg_no_channel_added" msgid="2882586037409921925">"کوئی چینلز شامل نہیں کیے گئے"</string>
<string name="input_selector_tuner_label" msgid="6631205039926880892">"ٹیونر"</string>
- <string name="menu_parental_controls" msgid="2474294054521345840">"والدین کے کنٹرولز"</string>
+ <string name="menu_parental_controls" msgid="2474294054521345840">"پیرنٹل کنٹرولز"</string>
<string name="option_toggle_parental_controls_on" msgid="9122851821454622696">"آن"</string>
<string name="option_toggle_parental_controls_off" msgid="7797910199040440618">"آف"</string>
<string name="option_channels_locked" msgid="5797855082297549907">"چینلز مسدود کر دیے"</string>
@@ -125,7 +125,7 @@
<string name="pin_enter_unlock_channel" msgid="4797922378296393173">"‏یہ چینل دیکھنے کیلئے اپنا PIN درج کریں"</string>
<string name="pin_enter_unlock_program" msgid="7311628843209871203">"‏یہ پروگرام دیکھنے کیلئے اپنا PIN درج کریں"</string>
<string name="pin_enter_pin" msgid="249314665028035038">"‏اپنا PIN درج کریں"</string>
- <string name="pin_enter_create_pin" msgid="3385754356793309946">"‏والدین کے کنٹرولز سیٹ کرنے کیلئے، ایک PIN بنائیں"</string>
+ <string name="pin_enter_create_pin" msgid="3385754356793309946">"‏پیرنٹل کنٹرولز سیٹ کرنے کیلئے، ایک PIN بنائیں"</string>
<string name="pin_enter_new_pin" msgid="1739471585849790384">"‏نیا PIN درج کریں"</string>
<string name="pin_enter_again" msgid="2618999754723090427">"‏اپنے PIN کی توثیق کریں"</string>
<string name="pin_enter_old_pin" msgid="4588282612931041919">"‏اپنا موجودہ PIN درج کریں"</string>
@@ -140,16 +140,10 @@
<string name="settings_channel_source_item_customize_channels_description" msgid="8966243790328235580">"اپنی پروگرام گائیڈ کیلئے چینلز منتخب کریں"</string>
<string name="settings_channel_source_item_setup" msgid="4566190088656419070">"چینل کے مآخذ"</string>
<string name="settings_channel_source_item_setup_new_inputs" msgid="4845822152617430787">"نئے چینلز دستیاب ہیں"</string>
- <string name="settings_parental_controls" msgid="5449397921700749317">"والدین کے کنٹرولز"</string>
+ <string name="settings_parental_controls" msgid="5449397921700749317">"پیرنٹل کنٹرولز"</string>
<string name="settings_menu_licenses" msgid="1257646083838406103">"اوپن سورس لائسنسز"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"اوپن سورس لائسنسز"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"ورژن"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"ڈیولپر کے اختیارات"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"‏USB TV ٹیونر فعال کریں"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"‏USB TV ٹیونر کی آواز سننے کیلئے، آپ کے TV کو AC3 پاس تھرو کی معاونت کرنی چاہئیے۔"</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"‏AC3 آڈیو صلاحیت"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"‏آپ کا TV‏ AC3 پاس تھرو کی معاونت کرتا ہے۔"</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"‏آپ کا TV‏ AC3 پاس تھرو کی معاونت نہیں کرتا۔"</string>
<string name="about_menu_improve" msgid="3712578027009311401">"لائیو چینلز بہتر بنانے میں مدد کریں"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"‏Google کے ساتھ گمنام استعمال اور تشخیصاتی ڈیٹا کا اشتراک کریں تاکہ ہم لائیو چینلز بہتر بنا اور کریشنگ اور فریزنگ جیسے مسائل کو روک سکیں۔"</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"‏یہ چینل دیکھنے کیلئے Right دبائیں اور اپنا PIN درج کریں"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"پروگرام مسدود ہے"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"اس پروگرام کی درجہ بندی <xliff:g id="RATING">%1$s</xliff:g> کی گئی ہے"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"صرف آڈیو"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"کمزور سگنل"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"کوئی انٹرنیٹ کنکشن نہیں ہے"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"کوئی عنوان نہیں ہے"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"چینل مسدود کر دیا گیا"</string>
- <string name="episode_format" msgid="4881195874563241096">"سیزن <xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: قسط <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"نئے"</string>
<string name="setup_category_done" msgid="4750902502852212319">"مآخذ"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"ٹیون کرنا ناکام ہوگیا"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"اس کارروائی کو نمٹانے کیلئے کوئی ایپ نہیں ملی۔"</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"سبھی ماخذ چینلز مخفی ہیں۔ \nدیکھنے کیلئے کم از کم ایک چینل منتخب کریں۔"</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"کمزور ویڈیو سگنل کی وجہ سے دستیاب نہیں ہے"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"ویڈیو غیر متوقع طور پر دستیاب نہیں ہے"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"‏BACK کلید منسلک آلہ کیلئے ہے۔ باہر نکلنے کیلئے HOME بٹن دبائیں۔"</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"‏Android Lollipop والے اس آلے پر لائیو چینلز کی معاونت نہیں ہے۔"</string>
diff --git a/res/values-uz-rUZ/arrays.xml b/res/values-uz-rUZ/arrays.xml
index 71619002..72a89932 100644
--- a/res/values-uz-rUZ/arrays.xml
+++ b/res/values-uz-rUZ/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"To‘liq"</item>
<item msgid="8568284598210500589">"Miqyos"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Barcha kanallar"</item>
- <item msgid="928298872841713530">"Oila va bolalar"</item>
- <item msgid="2751606947569857164">"Sport"</item>
- <item msgid="7345749789651321496">"Xarid"</item>
- <item msgid="167201149441442173">"Filmlar"</item>
- <item msgid="525966731464264290">"Komediya"</item>
- <item msgid="6096710741527327836">"Sayohat"</item>
- <item msgid="2851882187117833883">"Drama"</item>
- <item msgid="78492781188719038">"Ta’lim"</item>
- <item msgid="7221999662426308394">"Tabiat va jonivorlar"</item>
- <item msgid="375300513250925001">"Yangiliklar"</item>
- <item msgid="7746320336582330410">"O‘yinlar"</item>
- <item msgid="1255741860568329178">"San’at"</item>
- <item msgid="7603949681065702867">"Ko‘ngilochar"</item>
- <item msgid="4453821994746804366">"Turmush tarzi"</item>
- <item msgid="3488534597567932843">"Musiqa"</item>
- <item msgid="7452153120614274095">"Premyera"</item>
- <item msgid="8215762047341133299">"Fan va texnika"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Barcha kanallar"</item>
+ <item msgid="6897460857821394118">"Oila va bolalar"</item>
+ <item msgid="551257741825778215">"Sport"</item>
+ <item msgid="452133796804325879">"Xaridlar"</item>
+ <item msgid="3296058637230163031">"Filmlar"</item>
+ <item msgid="1054540282883891201">"Komediya"</item>
+ <item msgid="7900158429062595471">"Sayohat"</item>
+ <item msgid="3768998587825611787">"Dramalar"</item>
+ <item msgid="8340620094959282881">"Ta’lim"</item>
+ <item msgid="7396447839483867269">"Tabiat va jonivorlar"</item>
+ <item msgid="4738043455148062673">"Yangiliklar"</item>
+ <item msgid="7405041316051047427">"O‘yinlar"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Barcha kanallar"</item>
+ <item msgid="7909003973960375395">"Oila va bolalar"</item>
+ <item msgid="3185279732911635789">"Sport"</item>
+ <item msgid="4704858492065325964">"Xaridlar"</item>
+ <item msgid="6083795019290250078">"Filmlar"</item>
+ <item msgid="8302638329222449550">"Komediya"</item>
+ <item msgid="3803709976021475052">"Sayohat"</item>
+ <item msgid="8116747365234169059">"Dramalar"</item>
+ <item msgid="7356447541595315913">"Ta’lim"</item>
+ <item msgid="7511135485827589547">"Tabiat va jonivorlar"</item>
+ <item msgid="6961248112238009967">"Yangiliklar"</item>
+ <item msgid="6484685553679698447">"O‘yinlar"</item>
+ <item msgid="2737158328243183190">"San’at"</item>
+ <item msgid="6577176952650166615">"Hordiq"</item>
+ <item msgid="7886693831871777617">"Turmush tarzi"</item>
+ <item msgid="8145832312485577062">"Musiqa"</item>
+ <item msgid="1345789204804308580">"Premyera"</item>
+ <item msgid="2736680312770771994">"Fan va texnika"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Jonli efir"</item>
diff --git a/res/values-uz-rUZ/rating_system_strings.xml b/res/values-uz-rUZ/rating_system_strings.xml
index bfcb5723..470af93e 100644
--- a/res/values-uz-rUZ/rating_system_strings.xml
+++ b/res/values-uz-rUZ/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"15 yoshdan kichik foydalanuvchilarga dasturlarni ko‘rishi uchun ota-onasining ruxsat zarur."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"19 yoshdan kichik foydalanuvchilarga dasturlarni ko‘rishi uchun ota-onasining ruxsat zarur."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Behayo gaplar"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Qo‘pol nutq"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Shahvoniy tasvirlar"</string>
diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml
index 67a48ed6..e1343d55 100644
--- a/res/values-uz-rUZ/strings.xml
+++ b/res/values-uz-rUZ/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Ochiq kodli DT litsenziyalari"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Ochiq kodli DT litsenziyalari"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Versiyasi"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Dasturchi sozlamalari"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"USB TV-tyunerni yoqish"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"USB TV-tyuner ovozini eshitish uchun televizoringiz AC3 bevosita uzatmasini qo‘llab-quvvatlashi kerak."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 formatidagi audio imkoniyatiga ega"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"Televizoringiz AC3 bevosita uzatmasini qo‘llab-quvvatlaydi."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"Televizoringiz AC3 bevosita uzatmasini qo‘llab-quvvatlamaydi."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Jonli efir ilovasini yaxshilashga ko‘maklashish"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Jonli kanallar xizmatini yaxshilash va ishdan chiqish hamda qotib qolish muammolarining oldini olish uchun Google’ga anonim tarzda foydalanish to‘g‘risidagi va tashxis ma’lumotlarini yuboring."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Bu kanalni ko‘rish uchun o‘ngga qaragan chiziqni bosing va PIN kodni kiriting"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Dastur bloklangan"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Bu dastur uchun yosh cheklovi: <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Faqat audio"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Signal kuchsiz"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Internet aloqasi yo‘q"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Nomsiz"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Kanal bloklangan"</string>
- <string name="episode_format" msgid="4881195874563241096">"<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>-fasl <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g>-qism: <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Yangi"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Manbalar"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Sozlashni amalga oshirib bo‘lmadi."</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Bu amalni bajara oladigan ilova topilmadi."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Barcha kanallar berkitilgan.\nTomosha qilish uchun kamida bitta kanalni tanlang."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Videoni signal kuchsiz bo‘lganligi tufayli ko‘rib bo‘lmaydi"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Video kutilmaganda yo‘q bo‘lib qoldi."</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"ORQAGA tugmasi ulangan qurilmani boshqaradi. Chiqish uchun BOSHI tugmasini bosing."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Jonli efir Android Lollipop tizimi o‘rnatilgan mazkur qurilmada ishlamaydi."</string>
diff --git a/res/values-vi/arrays.xml b/res/values-vi/arrays.xml
index 202d287a..524ae9b9 100644
--- a/res/values-vi/arrays.xml
+++ b/res/values-vi/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Toàn màn hình"</item>
<item msgid="8568284598210500589">"Thu phóng"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Tất cả các kênh"</item>
- <item msgid="928298872841713530">"Gia đình/Trẻ em"</item>
- <item msgid="2751606947569857164">"Thể thao"</item>
- <item msgid="7345749789651321496">"Mua sắm"</item>
- <item msgid="167201149441442173">"Phim"</item>
- <item msgid="525966731464264290">"Hài kịch"</item>
- <item msgid="6096710741527327836">"Du lịch"</item>
- <item msgid="2851882187117833883">"Kịch"</item>
- <item msgid="78492781188719038">"Giáo dục"</item>
- <item msgid="7221999662426308394">"Động vật/Động vật hoang dã"</item>
- <item msgid="375300513250925001">"Tin tức"</item>
- <item msgid="7746320336582330410">"Trò chơi"</item>
- <item msgid="1255741860568329178">"Nghệ thuật"</item>
- <item msgid="7603949681065702867">"Giải trí"</item>
- <item msgid="4453821994746804366">"Lối sống"</item>
- <item msgid="3488534597567932843">"Âm nhạc"</item>
- <item msgid="7452153120614274095">"Cao cấp"</item>
- <item msgid="8215762047341133299">"Công nghệ/Khoa học"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Tất cả các kênh"</item>
+ <item msgid="6897460857821394118">"Gia đình/Trẻ em"</item>
+ <item msgid="551257741825778215">"Thể thao"</item>
+ <item msgid="452133796804325879">"Mua sắm"</item>
+ <item msgid="3296058637230163031">"Phim"</item>
+ <item msgid="1054540282883891201">"Hài kịch"</item>
+ <item msgid="7900158429062595471">"Du lịch"</item>
+ <item msgid="3768998587825611787">"Kịch"</item>
+ <item msgid="8340620094959282881">"Giáo dục"</item>
+ <item msgid="7396447839483867269">"Động vật/Thiên nhiên"</item>
+ <item msgid="4738043455148062673">"Tin tức"</item>
+ <item msgid="7405041316051047427">"Trò chơi"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Tất cả các kênh"</item>
+ <item msgid="7909003973960375395">"Gia đình/Trẻ em"</item>
+ <item msgid="3185279732911635789">"Thể thao"</item>
+ <item msgid="4704858492065325964">"Mua sắm"</item>
+ <item msgid="6083795019290250078">"Phim"</item>
+ <item msgid="8302638329222449550">"Hài kịch"</item>
+ <item msgid="3803709976021475052">"Du lịch"</item>
+ <item msgid="8116747365234169059">"Kịch"</item>
+ <item msgid="7356447541595315913">"Giáo dục"</item>
+ <item msgid="7511135485827589547">"Động vật/Thiên nhiên"</item>
+ <item msgid="6961248112238009967">"Tin tức"</item>
+ <item msgid="6484685553679698447">"Trò chơi"</item>
+ <item msgid="2737158328243183190">"Nghệ thuật"</item>
+ <item msgid="6577176952650166615">"Giải trí"</item>
+ <item msgid="7886693831871777617">"Lối sống"</item>
+ <item msgid="8145832312485577062">"Âm nhạc"</item>
+ <item msgid="1345789204804308580">"Cao cấp"</item>
+ <item msgid="2736680312770771994">"Công nghệ/Khoa học"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Kênh trực tiếp"</item>
diff --git a/res/values-vi/rating_system_strings.xml b/res/values-vi/rating_system_strings.xml
index 02216416..88b460f8 100644
--- a/res/values-vi/rating_system_strings.xml
+++ b/res/values-vi/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Các chương trình có thể chứa nội dung không phù hợp cho đối tượng dưới 15 tuổi và do đó cần phải có ý kiến của phụ huynh cho những đối tượng này"</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Các chương trình có thể chứa nội dung không phù hợp cho đối tượng dưới 19 tuổi và do đó không phù hợp người trẻ dưới 19 tuổi."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Ngôn từ khêu khợi"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Ngôn từ thô tục"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Nội dung khiêu dâm"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 6bc7dd76..449e2796 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Giấy phép nguồn mở"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Giấy phép nguồn mở"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Phiên bản"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Tùy chọn nhà phát triển"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Bật bộ dò TV USB"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Để nghe được âm thanh của bộ dò TV USB, TV của bạn cần hỗ trợ chuyển qua AC3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Chức năng âm thanh AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"TV của bạn hỗ trợ chuyển qua AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"TV của bạn không hỗ trợ chuyển qua AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Giúp cải tiến ứng dụng Kênh trực tiếp"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Chia sẻ ẩn danh dữ liệu sử dụng và chẩn đoán với Google để chúng tôi có thể làm cho Kênh trực tiếp tốt hơn và ngăn chặn các sự cố như bị lỗi và treo."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Để xem kênh này, hãy nhấn vào Quyền và nhập mã PIN của bạn"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Chương trình bị chặn"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Chương trình này được xếp hạng <xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Chỉ âm thanh"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Tín hiệu yếu"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Không có kết nối Internet"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Không có tiêu đề"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Đã chặn kênh"</string>
- <string name="episode_format" msgid="4881195874563241096">"Mùa<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: P<xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Mới"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Nguồn"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Không dò được"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Không tìm thấy ứng dụng nào để xử lý tác vụ này."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Tất cả các kênh nguồn đều bị ẩn.\nHãy chọn ít nhất một kênh để xem."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Không có do tín hiệu video yếu"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Video đột nhiên không có"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Phím QUAY LẠI dành cho thiết bị đã kết nối. Nhấn nút HOME để thoát."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Kênh trực tiếp không được hỗ trợ trên thiết bị chạy Android Lollipop này."</string>
diff --git a/res/values-zh-rCN/arrays.xml b/res/values-zh-rCN/arrays.xml
index 4bd6fa10..0dbbfe58 100644
--- a/res/values-zh-rCN/arrays.xml
+++ b/res/values-zh-rCN/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"全屏"</item>
<item msgid="8568284598210500589">"缩放"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"所有频道"</item>
- <item msgid="928298872841713530">"亲子"</item>
- <item msgid="2751606947569857164">"体育"</item>
- <item msgid="7345749789651321496">"购物"</item>
- <item msgid="167201149441442173">"电影"</item>
- <item msgid="525966731464264290">"喜剧"</item>
- <item msgid="6096710741527327836">"旅游"</item>
- <item msgid="2851882187117833883">"剧情类"</item>
- <item msgid="78492781188719038">"教育"</item>
- <item msgid="7221999662426308394">"动物/野生生物"</item>
- <item msgid="375300513250925001">"新闻"</item>
- <item msgid="7746320336582330410">"游戏"</item>
- <item msgid="1255741860568329178">"艺术"</item>
- <item msgid="7603949681065702867">"娱乐"</item>
- <item msgid="4453821994746804366">"生活时尚"</item>
- <item msgid="3488534597567932843">"音乐"</item>
- <item msgid="7452153120614274095">"首映"</item>
- <item msgid="8215762047341133299">"科技"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"所有频道"</item>
+ <item msgid="6897460857821394118">"亲子"</item>
+ <item msgid="551257741825778215">"体育"</item>
+ <item msgid="452133796804325879">"购物"</item>
+ <item msgid="3296058637230163031">"电影"</item>
+ <item msgid="1054540282883891201">"喜剧"</item>
+ <item msgid="7900158429062595471">"旅行"</item>
+ <item msgid="3768998587825611787">"戏剧"</item>
+ <item msgid="8340620094959282881">"教育"</item>
+ <item msgid="7396447839483867269">"动物/野生生物"</item>
+ <item msgid="4738043455148062673">"新闻"</item>
+ <item msgid="7405041316051047427">"游戏"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"所有频道"</item>
+ <item msgid="7909003973960375395">"亲子"</item>
+ <item msgid="3185279732911635789">"体育"</item>
+ <item msgid="4704858492065325964">"购物"</item>
+ <item msgid="6083795019290250078">"电影"</item>
+ <item msgid="8302638329222449550">"喜剧"</item>
+ <item msgid="3803709976021475052">"旅行"</item>
+ <item msgid="8116747365234169059">"戏剧"</item>
+ <item msgid="7356447541595315913">"教育"</item>
+ <item msgid="7511135485827589547">"动物/野生生物"</item>
+ <item msgid="6961248112238009967">"新闻"</item>
+ <item msgid="6484685553679698447">"游戏"</item>
+ <item msgid="2737158328243183190">"艺术"</item>
+ <item msgid="6577176952650166615">"娱乐"</item>
+ <item msgid="7886693831871777617">"生活时尚"</item>
+ <item msgid="8145832312485577062">"音乐"</item>
+ <item msgid="1345789204804308580">"首映"</item>
+ <item msgid="2736680312770771994">"科技"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"直播频道"</item>
diff --git a/res/values-zh-rCN/rating_system_strings.xml b/res/values-zh-rCN/rating_system_strings.xml
index 84cce4ab..93dcac1d 100644
--- a/res/values-zh-rCN/rating_system_strings.xml
+++ b/res/values-zh-rCN/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"节目可能包含不适合 15 周岁以下受众群体观看的内容,因此家长应酌情决定是否让子女观看。"</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"节目可能包含不适合 19 周岁以下受众群体观看的内容,因此不适合 19 岁以下的青少年观看。"</string>
<string name="description_us_tv_d" msgid="12333789157204816">"挑逗性对话"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"粗俗言语"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"色情内容"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index b4743f30..6a316495 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"开放源代码许可"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"开放源代码许可"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"版本"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"开发者选项"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"启用 USB 电视调谐器"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"要想听到 USB 电视调谐器的声音,您的电视需要支持 AC3 直通输出功能。"</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 音频功能"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"您的电视支持 AC3 直通输出功能。"</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"您的电视不支持 AC3 直通输出功能。"</string>
<string name="about_menu_improve" msgid="3712578027009311401">"帮助改进直播频道"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"将匿名使用情形和诊断数据提供给 Google,以协助我们改进直播频道并避免发生崩溃和死机等问题。"</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"要观看此频道,请按“向右”按钮,然后输入您的PIN码"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"节目已被屏蔽"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"此节目的分级为:<xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"仅提供音频"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"信号弱"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"未连接到互联网"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"无标题"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"频道已屏蔽"</string>
- <string name="episode_format" msgid="4881195874563241096">"第 <xliff:g id="SEASON_NUMBER">%1$d</xliff:g> 季第 <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> 集《<xliff:g id="EPISODE_TITLE">%3$s</xliff:g>》"</string>
<string name="setup_category_new" msgid="2899355289563443627">"新来源"</string>
<string name="setup_category_done" msgid="4750902502852212319">"来源"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"调谐失败"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"未找到可处理此操作的应用。"</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"所有来源频道均处于隐藏状态。\n请至少选择一个频道来观看。"</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"视频信号弱,因此无法播放"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"此视频出现异常,无法播放"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"“返回”键用于控制连接的设备。按“主屏幕”按钮即可退出。"</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"此 Android Lollipop 设备不支持直播频道。"</string>
diff --git a/res/values-zh-rHK/arrays.xml b/res/values-zh-rHK/arrays.xml
index 1fcac667..0951b0be 100644
--- a/res/values-zh-rHK/arrays.xml
+++ b/res/values-zh-rHK/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"全螢幕"</item>
<item msgid="8568284598210500589">"縮放"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"所有頻道"</item>
- <item msgid="928298872841713530">"家庭/兒童"</item>
- <item msgid="2751606947569857164">"體育"</item>
- <item msgid="7345749789651321496">"購物"</item>
- <item msgid="167201149441442173">"電影"</item>
- <item msgid="525966731464264290">"喜劇"</item>
- <item msgid="6096710741527327836">"旅遊"</item>
- <item msgid="2851882187117833883">"戲劇"</item>
- <item msgid="78492781188719038">"教育"</item>
- <item msgid="7221999662426308394">"動物/野生動物"</item>
- <item msgid="375300513250925001">"新聞"</item>
- <item msgid="7746320336582330410">"遊戲"</item>
- <item msgid="1255741860568329178">"藝術"</item>
- <item msgid="7603949681065702867">"娛樂"</item>
- <item msgid="4453821994746804366">"生活品味"</item>
- <item msgid="3488534597567932843">"音樂"</item>
- <item msgid="7452153120614274095">"首選"</item>
- <item msgid="8215762047341133299">"科技/科學"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"所有頻道"</item>
+ <item msgid="6897460857821394118">"家庭/兒童"</item>
+ <item msgid="551257741825778215">"體育"</item>
+ <item msgid="452133796804325879">"購物"</item>
+ <item msgid="3296058637230163031">"電影"</item>
+ <item msgid="1054540282883891201">"喜劇"</item>
+ <item msgid="7900158429062595471">"旅遊"</item>
+ <item msgid="3768998587825611787">"戲劇"</item>
+ <item msgid="8340620094959282881">"教育"</item>
+ <item msgid="7396447839483867269">"動物/野生動植物"</item>
+ <item msgid="4738043455148062673">"新聞"</item>
+ <item msgid="7405041316051047427">"遊戲"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"所有頻道"</item>
+ <item msgid="7909003973960375395">"家庭/兒童"</item>
+ <item msgid="3185279732911635789">"體育"</item>
+ <item msgid="4704858492065325964">"購物"</item>
+ <item msgid="6083795019290250078">"電影"</item>
+ <item msgid="8302638329222449550">"喜劇"</item>
+ <item msgid="3803709976021475052">"旅遊"</item>
+ <item msgid="8116747365234169059">"戲劇"</item>
+ <item msgid="7356447541595315913">"教育"</item>
+ <item msgid="7511135485827589547">"動物/野生動植物"</item>
+ <item msgid="6961248112238009967">"新聞"</item>
+ <item msgid="6484685553679698447">"遊戲"</item>
+ <item msgid="2737158328243183190">"藝術"</item>
+ <item msgid="6577176952650166615">"娛樂"</item>
+ <item msgid="7886693831871777617">"生活品味"</item>
+ <item msgid="8145832312485577062">"音樂"</item>
+ <item msgid="1345789204804308580">"首映"</item>
+ <item msgid="2736680312770771994">"科技/科學"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"直播頻道"</item>
diff --git a/res/values-zh-rHK/rating_system_strings.xml b/res/values-zh-rHK/rating_system_strings.xml
index 77f2a752..50a3db56 100644
--- a/res/values-zh-rHK/rating_system_strings.xml
+++ b/res/values-zh-rHK/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"節目內容可能不適合 15 歲以下觀眾,因此家長應酌情決定是否讓他們觀看。"</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"節目內容可能不適合 19 歲以下觀眾,因此 19 歲以下的青少年不宜觀看。"</string>
<string name="description_us_tv_d" msgid="12333789157204816">"性暗示對話"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"言語粗俗"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"色情內容"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 368b5cd9..b024e687 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"開放原始碼授權"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"開放原始碼授權"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"版本"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"開發人員選項"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"啟用 USB 電視調諧器"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"如要收聽 USB 電視調諧器的音訊,電視需要支援 AC3 直通輸出。"</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 音訊功能"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"您的電視支援 AC3 直通輸出。"</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"您的電視不支援 AC3 直通輸出。"</string>
<string name="about_menu_improve" msgid="3712578027009311401">"協助改善「直播頻道」"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"與 Google 分享匿名使用和診斷資料,以協助改善「直播頻道」,並防止當機問題發生。"</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"按向右鍵並輸入您的 PIN,以觀看這個頻道"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"節目被封鎖"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"這個節目的評級為<xliff:g id="RATING">%1$s</xliff:g>。"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"只限音效"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"訊號微弱"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"沒有互聯網連線"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"無標題"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"已封鎖的頻道"</string>
- <string name="episode_format" msgid="4881195874563241096">"第 <xliff:g id="SEASON_NUMBER">%1$d</xliff:g> 季:第 <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> 集 <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"最新"</string>
<string name="setup_category_done" msgid="4750902502852212319">"來源"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"調校失敗"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"找不到可以處理這項操作的應用程式。"</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"所有來源頻道均已隱藏。\n請選取至少一個要觀看的頻道。"</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"影片訊號微弱,因此無法播放"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"影片無法播放"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"[返回] 鍵適用於已連結的裝置。按一下 [主畫面] 按鈕即可結束。"</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"此 Android Lollipop 裝置不支援「直播頻道」。"</string>
diff --git a/res/values-zh-rTW/arrays.xml b/res/values-zh-rTW/arrays.xml
index 8647afb4..2576dafc 100644
--- a/res/values-zh-rTW/arrays.xml
+++ b/res/values-zh-rTW/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"16:9 模式"</item>
<item msgid="8568284598210500589">"縮放模式"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"所有頻道"</item>
- <item msgid="928298872841713530">"家庭/兒童"</item>
- <item msgid="2751606947569857164">"體育"</item>
- <item msgid="7345749789651321496">"購物"</item>
- <item msgid="167201149441442173">"電影"</item>
- <item msgid="525966731464264290">"喜劇"</item>
- <item msgid="6096710741527327836">"旅遊"</item>
- <item msgid="2851882187117833883">"劇情"</item>
- <item msgid="78492781188719038">"教育"</item>
- <item msgid="7221999662426308394">"動物/野生動物"</item>
- <item msgid="375300513250925001">"新聞"</item>
- <item msgid="7746320336582330410">"遊戲"</item>
- <item msgid="1255741860568329178">"藝術"</item>
- <item msgid="7603949681065702867">"娛樂"</item>
- <item msgid="4453821994746804366">"生活品味"</item>
- <item msgid="3488534597567932843">"音樂"</item>
- <item msgid="7452153120614274095">"首映"</item>
- <item msgid="8215762047341133299">"科技/科學"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"所有頻道"</item>
+ <item msgid="6897460857821394118">"家庭/兒童"</item>
+ <item msgid="551257741825778215">"體育"</item>
+ <item msgid="452133796804325879">"購物"</item>
+ <item msgid="3296058637230163031">"電影"</item>
+ <item msgid="1054540282883891201">"喜劇"</item>
+ <item msgid="7900158429062595471">"旅遊"</item>
+ <item msgid="3768998587825611787">"戲劇"</item>
+ <item msgid="8340620094959282881">"教育"</item>
+ <item msgid="7396447839483867269">"動物/野生動物"</item>
+ <item msgid="4738043455148062673">"新聞"</item>
+ <item msgid="7405041316051047427">"遊戲"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"所有頻道"</item>
+ <item msgid="7909003973960375395">"家庭/兒童"</item>
+ <item msgid="3185279732911635789">"體育"</item>
+ <item msgid="4704858492065325964">"購物"</item>
+ <item msgid="6083795019290250078">"電影"</item>
+ <item msgid="8302638329222449550">"喜劇"</item>
+ <item msgid="3803709976021475052">"旅遊"</item>
+ <item msgid="8116747365234169059">"戲劇"</item>
+ <item msgid="7356447541595315913">"教育"</item>
+ <item msgid="7511135485827589547">"動物/野生動物"</item>
+ <item msgid="6961248112238009967">"新聞"</item>
+ <item msgid="6484685553679698447">"遊戲"</item>
+ <item msgid="2737158328243183190">"藝術"</item>
+ <item msgid="6577176952650166615">"娛樂"</item>
+ <item msgid="7886693831871777617">"生活品味"</item>
+ <item msgid="8145832312485577062">"音樂"</item>
+ <item msgid="1345789204804308580">"首映"</item>
+ <item msgid="2736680312770771994">"科技/科學"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"直播頻道"</item>
diff --git a/res/values-zh-rTW/rating_system_strings.xml b/res/values-zh-rTW/rating_system_strings.xml
index b777019b..081edccb 100644
--- a/res/values-zh-rTW/rating_system_strings.xml
+++ b/res/values-zh-rTW/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"節目可能包含不適合 15 歲以下觀眾的內容,家長應自行斟酌。"</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"節目可能包含不適合 19 歲以下觀眾的內容,因此 19 歲以下觀眾不宜觀看。"</string>
<string name="description_us_tv_d" msgid="12333789157204816">"性暗示對話"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"粗暴言語"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"情色內容"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 0a3b4cc1..f69a96d8 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"開放原始碼授權"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"開放原始碼授權"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"版本"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"開發人員選項"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"啟用 USB 電視調諧器"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"您的電視必須支援 AC3 傳輸功能,才能播放 USB 電視調諧器的聲音。"</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"AC3 音訊功能"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"您的電視支援 AC3 傳輸功能。"</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"您的電視不支援 AC3 傳輸功能。"</string>
<string name="about_menu_improve" msgid="3712578027009311401">"協助改善直播頻道"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"將匿名的使用情形資料與診斷資料提供給 Google,協助我們改善直播頻道以及避免發生當機和畫面凍結等問題。"</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"如要觀看這個頻道,請按向右鍵並輸入您的 PIN"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"節目遭到封鎖"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"這個節目的分級是「<xliff:g id="RATING">%1$s</xliff:g>」。"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"僅限音訊"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"訊號微弱"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"沒有網際網路連線"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"無標題"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"頻道遭到封鎖"</string>
- <string name="episode_format" msgid="4881195874563241096">"第 <xliff:g id="SEASON_NUMBER">%1$d</xliff:g> 季:第 <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> 集「<xliff:g id="EPISODE_TITLE">%3$s</xliff:g>」"</string>
<string name="setup_category_new" msgid="2899355289563443627">"新的頻道來源"</string>
<string name="setup_category_done" msgid="4750902502852212319">"頻道來源"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"協調失敗"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"找不到可以處理這個動作的應用程式。"</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"所有來源頻道皆已隱藏。\n請選取至少一個要觀看的頻道。"</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"影片訊號微弱,因此無法播放"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"影片無法播放,原因不明"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"返回鍵適用於連線的裝置,按下主螢幕按鈕即可結束。"</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"這個搭載 Android Lollipop 的裝置不支援「直播頻道」。"</string>
diff --git a/res/values-zu/arrays.xml b/res/values-zu/arrays.xml
index 03f7be29..f1f9bd7f 100644
--- a/res/values-zu/arrays.xml
+++ b/res/values-zu/arrays.xml
@@ -22,25 +22,39 @@
<item msgid="2533030282864800794">"Kugcwele"</item>
<item msgid="8568284598210500589">"Sondeza"</item>
</string-array>
- <string-array name="genre_labels">
- <item msgid="3241959001790384341">"Zonke iziteshi"</item>
- <item msgid="928298872841713530">"Umndeni/abantwana"</item>
- <item msgid="2751606947569857164">"Ezemidlalo"</item>
- <item msgid="7345749789651321496">"Ukuthenga"</item>
- <item msgid="167201149441442173">"Ama-movie"</item>
- <item msgid="525966731464264290">"Awamahlaya"</item>
- <item msgid="6096710741527327836">"Ukuvakasha"</item>
- <item msgid="2851882187117833883">"Awomdlalo"</item>
- <item msgid="78492781188719038">"Imfundo"</item>
- <item msgid="7221999662426308394">"Izilwane/impilo yasendle"</item>
- <item msgid="375300513250925001">"Awezindaba"</item>
- <item msgid="7746320336582330410">"Awamageyimu"</item>
- <item msgid="1255741860568329178">"Ezobuciko"</item>
- <item msgid="7603949681065702867">"Ukuzijabulisa"</item>
- <item msgid="4453821994746804366">"Indlela yokuphila"</item>
- <item msgid="3488534597567932843">"Umculo"</item>
- <item msgid="7452153120614274095">"I-Premier"</item>
- <item msgid="8215762047341133299">"Ubuchwepheshe/isayensi"</item>
+ <string-array name="genre_labels_l">
+ <item msgid="4730006281716266127">"Zonke iziteshi"</item>
+ <item msgid="6897460857821394118">"Umndeni/abantwana"</item>
+ <item msgid="551257741825778215">"Ezemidlalo"</item>
+ <item msgid="452133796804325879">"Ukuthenga"</item>
+ <item msgid="3296058637230163031">"Ama-Movie"</item>
+ <item msgid="1054540282883891201">"Awamahlaya"</item>
+ <item msgid="7900158429062595471">"Ukuvakasha"</item>
+ <item msgid="3768998587825611787">"IDrama"</item>
+ <item msgid="8340620094959282881">"Imfundo"</item>
+ <item msgid="7396447839483867269">"Izilwane/impilo yasendle"</item>
+ <item msgid="4738043455148062673">"Izindaba"</item>
+ <item msgid="7405041316051047427">"Awamageyimu"</item>
+ </string-array>
+ <string-array name="genre_labels_l_mr1">
+ <item msgid="7173498629837517739">"Zonke iziteshi"</item>
+ <item msgid="7909003973960375395">"Umndeni/abantwana"</item>
+ <item msgid="3185279732911635789">"Ezemidlalo"</item>
+ <item msgid="4704858492065325964">"Ukuthenga"</item>
+ <item msgid="6083795019290250078">"Ama-Movie"</item>
+ <item msgid="8302638329222449550">"Awamahlaya"</item>
+ <item msgid="3803709976021475052">"Ukuvakasha"</item>
+ <item msgid="8116747365234169059">"IDrama"</item>
+ <item msgid="7356447541595315913">"Imfundo"</item>
+ <item msgid="7511135485827589547">"Izilwane/impilo yasendle"</item>
+ <item msgid="6961248112238009967">"Izindaba"</item>
+ <item msgid="6484685553679698447">"Awamageyimu"</item>
+ <item msgid="2737158328243183190">"Ezobuciko"</item>
+ <item msgid="6577176952650166615">"Okokuzijabulisa"</item>
+ <item msgid="7886693831871777617">"Indlela yokuphila"</item>
+ <item msgid="8145832312485577062">"Umculo"</item>
+ <item msgid="1345789204804308580">"I-Premier"</item>
+ <item msgid="2736680312770771994">"Ubuchwepheshe/isayensi"</item>
</string-array>
<string-array name="welcome_page_titles">
<item msgid="8322900543391231769">"Iziteshi ezibukhoma"</item>
diff --git a/res/values-zu/rating_system_strings.xml b/res/values-zu/rating_system_strings.xml
index bd19925c..404825f5 100644
--- a/res/values-zu/rating_system_strings.xml
+++ b/res/values-zu/rating_system_strings.xml
@@ -17,6 +17,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for description_fr_dvb_u (8232083238497153682) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_all (4821183217465284547) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_c (1367395312248365855) -->
+ <skip />
+ <!-- no translation found for description_es_dvb_x (438890727939582714) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_all (3048365195196820853) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_7 (5892929693766504059) -->
+ <skip />
+ <!-- no translation found for description_kr_tv_12 (1883893246941826597) -->
+ <skip />
+ <string name="description_kr_tv_15" msgid="5888141153573158322">"Iizinhlelo zingaqukatha umsebenzi ongalungele izethameli ezingaphansi kuka-15, ngakho-ke ukulawula komzali kufanele kusetshenzisiwe."</string>
+ <string name="description_kr_tv_19" msgid="9197085158053054550">"Izinhlelo zingahle ziqukathe okubalulekile okungalungele izethameli ezingaphansi kuka-19, ngakho-ke azilungele abancane abangaphansi kuka-19."</string>
<string name="description_us_tv_d" msgid="12333789157204816">"Ingxoxo ephakanyiswayo"</string>
<string name="description_us_tv_l" msgid="4105102855627964319">"Ulimi olunezithuko"</string>
<string name="description_us_tv_s" msgid="7552447251273237176">"Okuphathelene nezocansi"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index b59592fa..fc39708a 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -144,12 +144,6 @@
<string name="settings_menu_licenses" msgid="1257646083838406103">"Amalayisense womthombo ovulekile"</string>
<string name="dialog_title_licenses" msgid="4471754920475076623">"Amalayisense womthombo ovulekile"</string>
<string name="settings_menu_version" msgid="2604030372029921403">"Inguqulo"</string>
- <string name="side_panel_title_developer" msgid="7287627759979090359">"Izinketho zonjiniyela"</string>
- <string name="developer_menu_enable_usb_tv_tuner" msgid="8380174840125430895">"Nika amandla isishuni se-USB TV"</string>
- <string name="developer_menu_enable_usb_tv_tuner_description" msgid="7122833064850110134">"Ukuze uzwe umsindo wesishuni se-USB TV, i-TV yakho kufanele isekele ukuphuma kwe-AC3."</string>
- <string name="developer_menu_ac3_support" msgid="8542930057443915670">"Amandla omsindo we-AC3"</string>
- <string name="developer_menu_ac3_support_yes" msgid="8292133113564798078">"I-TV yakho isekela ukuphuma kwe-AC3."</string>
- <string name="developer_menu_ac3_support_no" msgid="6509302484099707809">"I-TV yakho ayisekeli ukuphuma kwe-AC3."</string>
<string name="about_menu_improve" msgid="3712578027009311401">"Siza ukuthuthukisa Iziteshi Ezibukhoma"</string>
<string name="about_menu_improve_summary" msgid="7548489011760588571">"Yabelana ngedatha yokusebenza yokungaziwa neyokuhlola ne-Google ukuze sikwazi ukwenza kangcono iziteshi ezibukhoma futhi sivikele izinkinga ezifana nokusaphazeka nokjema."</string>
<string name="tvview_channel_locked" msgid="6486375335718400728">"Ukuze ubuke lesi siteshi, cindezela Kwesokudla uphinde ufake i-PIN yakho"</string>
@@ -161,9 +155,10 @@
<string name="shrunken_tvview_content_locked" msgid="7686397981042364446">"Uhlelo luvinjiwe"</string>
<string name="shrunken_tvview_content_locked_format" msgid="3720284198877900916">"Lolu hlelo lulinganiselwe nge-<xliff:g id="RATING">%1$s</xliff:g>"</string>
<string name="tvview_msg_audio_only" msgid="1356866203687173329">"Umsindo kuphela"</string>
+ <string name="tvview_msg_weak_signal" msgid="1095050812622908976">"Isignali engaqinile"</string>
+ <string name="tvview_msg_no_internet_connection" msgid="7655994401188888231">"Alukho uxhumano lwe-intanethi"</string>
<string name="channel_banner_no_title" msgid="8660301979190693176">"Asikho isihloko"</string>
<string name="channel_banner_locked_channel_title" msgid="2006564967318945980">"Isiteshi sivinjiw"</string>
- <string name="episode_format" msgid="4881195874563241096">"S<xliff:g id="SEASON_NUMBER">%1$d</xliff:g>: Ep. <xliff:g id="EPISODE_NUMBER">%2$d</xliff:g> <xliff:g id="EPISODE_TITLE">%3$s</xliff:g>"</string>
<string name="setup_category_new" msgid="2899355289563443627">"Okusha"</string>
<string name="setup_category_done" msgid="4750902502852212319">"Imithombo"</string>
<plurals name="setup_input_channels" formatted="false" msgid="1695941684075602971">
@@ -190,7 +185,6 @@
<string name="msg_tune_failed" msgid="3277419551849972252">"Ukushuna kwehlulekile"</string>
<string name="msg_missing_app" msgid="8291542072400042076">"Alukho uhlelo lokusebenza olutholakalele ukuphatha lesi senzo."</string>
<string name="msg_all_channels_hidden" msgid="777397634062471936">"Zonke iziteshi zomthombo zifihliwe.\nKhetha okungenani isiteshi esisodwa ukuze ubuke."</string>
- <string name="msg_channel_unavailable_weak_signal" msgid="3935601939459171709">"Ayitholakali ngenxa yesignali yevidiyo engaqinile"</string>
<string name="msg_channel_unavailable_unknown" msgid="765586450831081871">"Ividiyo ayitholakali ngokungalindelekile"</string>
<string name="msg_back_key_guide" msgid="7404682718828721924">"Ukhiye we-EMUVA ungowedivayisi exhunyiwe. Cindezela kunkinobho ye-IKHAYA ukuze uphume."</string>
<string name="msg_not_supported_device" msgid="4469404625040284722">"Iziteshi ezibukhoma azisekelwa kule divayisi nge-Android Lollipop."</string>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 583ad51f..856e5cc4 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -26,11 +26,30 @@
<item>Zoom</item>
</string-array>
- <!-- The category strings to be displayed in the channel guide.
- The show_only_label should be synced with src/com/android/tv/data/ShowOnlyItems.java -->
+ <!-- The category strings to be displayed in the channel guide for LMP.
+ This list should be synced the data in src/com/android/tv/data/GenreItems.java -->
<eat-comment />
- <!-- Genre list [CHAR LIMIT=20] -->
- <string-array name="genre_labels" translatable="true">
+ <!-- Genre list for LMP [CHAR LIMIT=20] -->
+ <string-array name="genre_labels_l" translatable="true">
+ <item>All channels</item>
+ <item>Family/Kids</item>
+ <item>Sports</item>
+ <item>Shopping</item>
+ <item>Movies</item>
+ <item>Comedy</item>
+ <item>Travel</item>
+ <item>Drama</item>
+ <item>Education</item>
+ <item>Animal/Wildlife</item>
+ <item>News</item>
+ <item>Gaming</item>
+ </string-array>
+
+ <!-- The category strings to be displayed in the channel guide for LMP MR1.
+ This list should be synced the data in src/com/android/tv/data/GenreItems.java -->
+ <eat-comment />
+ <!-- Genre list for LMP MR1 [CHAR LIMIT=20] -->
+ <string-array name="genre_labels_l_mr1" translatable="true">
<item>All channels</item>
<item>Family/Kids</item>
<item>Sports</item>
diff --git a/res/values/attr.xml b/res/values/attr.xml
index 4592dbad..1261ea41 100644
--- a/res/values/attr.xml
+++ b/res/values/attr.xml
@@ -22,4 +22,4 @@
<declare-styleable name="ProgramTooWideState" >
<attr name="state_program_too_wide" format="boolean" />
</declare-styleable>
-</resources> \ No newline at end of file
+</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 73f04a4c..bdd77774 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -166,14 +166,16 @@
<dimen name="program_guide_table_margin_top">27dp</dimen>
<dimen name="program_guide_table_margin_bottom">27dp</dimen>
<dimen name="program_guide_table_margin_start">56dp</dimen>
- <dimen name="program_guide_table_header_column_width">204dp</dimen>
+ <dimen name="program_guide_table_header_column_width">232dp</dimen>
<dimen name="program_guide_table_header_column_padding_start">16dp</dimen>
<dimen name="program_guide_table_header_column_padding_end">16dp</dimen>
<dimen name="program_guide_table_header_column_channel_number_width">84dp</dimen>
<dimen name="program_guide_table_header_column_channel_number_large_font_size">32sp</dimen>
<dimen name="program_guide_table_header_column_channel_number_small_font_size">24sp</dimen>
+ <dimen name="program_guide_table_header_column_channel_number_margin_start">28dp</dimen>
<dimen name="program_guide_table_header_column_channel_name_width">88dp</dimen>
<dimen name="program_guide_table_header_column_channel_name_font_size">14sp</dimen>
+ <dimen name="program_guide_table_header_column_channel_name_margin_start">112dp</dimen>
<dimen name="program_guide_table_header_column_channel_logo_width">70dp</dimen>
<dimen name="program_guide_table_header_column_channel_logo_height">40dp</dimen>
<dimen name="program_guide_table_header_column_channel_logo_margin_top">12dp</dimen>
@@ -183,6 +185,10 @@
@dimen/program_guide_table_header_column_channel_logo_height</dimen>
<dimen name="program_guide_table_header_column_channel_block_margin_top">
@dimen/program_guide_table_header_column_channel_logo_margin_top</dimen>
+ <dimen name="program_guide_table_header_column_input_logo_width">18dp</dimen>
+ <dimen name="program_guide_table_header_column_input_logo_height">
+ @dimen/program_guide_table_header_column_input_logo_width</dimen>
+ <dimen name="program_guide_table_header_column_input_logo_margin_top">23dp</dimen>
<dimen name="program_guide_table_header_row_height">40dp</dimen>
<dimen name="program_guide_table_header_row_font_size">16sp</dimen>
<dimen name="program_guide_table_header_row_fade_length">24dp</dimen>
@@ -217,11 +223,11 @@
<!-- TV view -->
<dimen name="tvview_block_icon_width">200dp</dimen>
<dimen name="tvview_block_icon_height">120dp</dimen>
- <dimen name="tvview_block_text_padding_top">15dp</dimen>
+ <dimen name="tvview_block_vertical_spacing">15dp</dimen>
<dimen name="tvview_block_text_size">16sp</dimen>
<dimen name="shrunken_tvview_block_icon_width">60dp</dimen>
<dimen name="shrunken_tvview_block_icon_height">60dp</dimen>
- <dimen name="shrunken_tvview_block_text_padding_top">24dp</dimen>
+ <dimen name="shrunken_tvview_block_vertical_spacing">24dp</dimen>
<!-- PIP view -->
<dimen name="pipview_margin_horizontal">56dp</dimen>
@@ -311,4 +317,13 @@
+ lb_onboarding_welcome_shadow_margin_top(16dp)
+ margin from tv_container(70dp) -->
<dimen name="onboarding_welcome_arrow_margin_bottom">243dp</dimen>
+
+ <!-- DVR screens -->
+ <eat-comment />
+ <dimen name="dvr_card_layout_width">200dp</dimen>
+ <dimen name="dvr_card_layout_height">200dp</dimen>
+ <!-- card width - margin-->
+ <dimen name="dvr_card_image_layout_width">196dp</dimen>
+ <dimen name="dvr_card_image_layout_height">140dp</dimen>
+
</resources>
diff --git a/res/values/integers.xml b/res/values/integers.xml
index 08f55043..5aa59ffd 100644
--- a/res/values/integers.xml
+++ b/res/values/integers.xml
@@ -79,4 +79,9 @@
<integer name="max_recycled_view_pool_epg_table_row">15</integer>
<integer name="max_recycled_view_pool_epg_header_row_item">7</integer>
<integer name="max_recycled_view_pool_epg_side_panel_row">12</integer>
+
+ <!-- Half sized Dialog -->
+ <integer name="half_sized_dialog_anim_duration">250</integer>
+ <integer name="half_sized_dialog_enter_offset_y">32</integer>
+ <integer name="half_sized_dialog_exit_offset_y">32</integer>
</resources>
diff --git a/res/values/rating_system_strings.xml b/res/values/rating_system_strings.xml
index e3e0298f..2441f96f 100644
--- a/res/values/rating_system_strings.xml
+++ b/res/values/rating_system_strings.xml
@@ -60,12 +60,12 @@
<!-- TV content rating system strings for BR TV. These strings are from
http://www.justica.gov.br/seus-direitos/classificacao/guia-pratico/practical-guide.pdf/view -->
- <string name="title_br_tv_l" translatable="false">Livre</string>
- <string name="title_br_tv_10" translatable="false">10 anos</string>
- <string name="title_br_tv_12" translatable="false">12 anos</string>
- <string name="title_br_tv_14" translatable="false">14 anos</string>
- <string name="title_br_tv_16" translatable="false">16 anos</string>
- <string name="title_br_tv_18" translatable="false">18 anos</string>
+ <string name="title_br_tv_l" translatable="false">GA</string>
+ <string name="title_br_tv_10" translatable="false">PG-10</string>
+ <string name="title_br_tv_12" translatable="false">PG-12</string>
+ <string name="title_br_tv_14" translatable="false">PG-14</string>
+ <string name="title_br_tv_16" translatable="false">PG-16</string>
+ <string name="title_br_tv_18" translatable="false">PG-18</string>
<string name="description_br_tv_l" translatable="false">General audiences</string>
<string name="description_br_tv_10" translatable="false">Not recommended for ages under 10.</string>
<string name="description_br_tv_12" translatable="false">Not recommended for ages under 12.</string>
@@ -100,24 +100,33 @@
<string name="description_ca_tv_fr_18" translatable="false">The program is appropriate for viewing by persons 18 years of age or older. Programs reserved for adults most often deal primarily with the representation of explicit sexual encounters. They may also be extremely violent, showing scenes of hyperrealistic cruelty, torture and horror.</string>
<!-- TV content rating system strings for DVB -->
- <string name="description_fr_dvb_u" translatable="false">Recommended for all ages.</string>
- <string name="description_es_dvb_all" translatable="false">Recommended for all ages.</string>
- <string name="description_es_dvb_c" translatable="false">Recommended for children.</string>
- <string name="description_es_dvb_x" translatable="false">Recommended for adults.</string>
+ <!-- A TV content rating of DVB for all ages [CHAR LIMIT=NONE] -->
+ <string name="description_fr_dvb_u">Recommended for all ages.</string>
+ <!-- A TV content rating of DVB for all ages [CHAR LIMIT=NONE] -->
+ <string name="description_es_dvb_all">Recommended for all ages.</string>
+ <!-- A TV content rating of DVB for children [CHAR LIMIT=NONE] -->
+ <string name="description_es_dvb_c">Recommended for children.</string>
+ <!-- A TV content rating of DVB for adult [CHAR LIMIT=NONE] -->
+ <string name="description_es_dvb_x">Recommended for adults.</string>
<!-- TV content rating system strings for KR TV. These strings are from
http://www.law.go.kr/admRulLsInfoP.do?admRulSeq=2000000118507 but they are translated
from Korean to English. -->
- <string name="title_kr_tv_all" translatable="false">모든연령시청가</string>
- <string name="title_kr_tv_7" translatable="false">7세이상시청가</string>
- <string name="title_kr_tv_12" translatable="false">12세이상시청가</string>
- <string name="title_kr_tv_15" translatable="false">15세이상시청가</string>
- <string name="title_kr_tv_19" translatable="false">19세이상시청가</string>
- <string name="description_kr_tv_all" translatable="false">Programs do not contain material inappropriate for all ages, and thus are suitable for all ages.</string>
- <string name="description_kr_tv_7" translatable="false">Programs may contain material inappropriate for children under 7, and therefore parental discretion should be used for them.</string>
- <string name="description_kr_tv_12" translatable="false">Programs may contain material inappropriate for children under 12, and therefore parental discretion should be used for them.</string>
- <string name="description_kr_tv_15" translatable="false">Programs may contain material inappropriate for youngsters under 15, and therefore parental discretion should be used for them.</string>
- <string name="description_kr_tv_19" translatable="false">Programs may contain material inappropriate for youngsters under 19, and thus are not suitable for youngsters under 19.</string>
+ <string name="title_kr_tv_all" translatable="false">ALL</string>
+ <string name="title_kr_tv_7" translatable="false">7</string>
+ <string name="title_kr_tv_12" translatable="false">12</string>
+ <string name="title_kr_tv_15" translatable="false">15</string>
+ <string name="title_kr_tv_19" translatable="false">19</string>
+ <!-- A TV content rating of Korea for all ages [CHAR LIMIT=NONE] -->
+ <string name="description_kr_tv_all">Programs do not contain material inappropriate for all ages, and thus are suitable for all ages.</string>
+ <!-- A TV content rating of Korea for persons aged 7 and above [CHAR LIMIT=NONE] -->
+ <string name="description_kr_tv_7">Programs may contain material inappropriate for children under 7, and therefore parental discretion should be used for them.</string>
+ <!-- A TV content rating of Korea for persons aged 12 and above [CHAR LIMIT=NONE] -->
+ <string name="description_kr_tv_12">Programs may contain material inappropriate for children under 12, and therefore parental discretion should be used for them.</string>
+ <!-- A TV content rating of Korea for persons aged 15 and above [CHAR LIMIT=NONE] -->
+ <string name="description_kr_tv_15">Programs may contain material inappropriate for audiences under 15, and therefore parental discretion should be used for them.</string>
+ <!-- A TV content rating of Korea for persons aged 19 and above [CHAR LIMIT=NONE] -->
+ <string name="description_kr_tv_19">Programs may contain material inappropriate for audiences under 19, and thus are not suitable for youngsters under 19.</string>
<!-- TV content rating system strings for SG TV. These strings are from
http://www.mda.gov.sg/RegulationsAndLicensing/ContentStandardsAndClassification/FilmsAndVideos/Pages/default.aspx -->
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0bf026db..224aa298 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -392,21 +392,6 @@
<!-- Menu item that shows the application version(eg 1.2.03-final) as a second line below this title [CHAR LIMIT=35] -->
<string name="settings_menu_version">Version</string>
- <!-- menu for "Developer options" -->
- <eat-comment />
- <!-- Title of "Developer options" option. [CHAR LIMIT=30] -->
- <string name="side_panel_title_developer">Developer options</string>
- <!-- Menu switch to turn on/off USB TV tuner feature. [CHAR LIMIT=60] -->
- <string name="developer_menu_enable_usb_tv_tuner">Enable USB TV tuner</string>
- <!-- Menu switch description of USB TV tuner switch. [CHAR LIMIT=120] -->
- <string name="developer_menu_enable_usb_tv_tuner_description">To hear sound of USB TV tuner, your TV should support AC3 passthrough.</string>
- <!-- Menu item to shows AC3 passthrough capability. [CHAR LIMIT=60] -->
- <string name="developer_menu_ac3_support">AC3 audio capability</string>
- <!-- Menu item description when the user's TV supports AC3 passthrough. [CHAR LIMIT=120] -->
- <string name="developer_menu_ac3_support_yes">Your TV supports AC3 passthrough.</string>
- <!-- Menu item to shows that the user's TV doesn't support AC3 passthrough. [CHAR LIMIT=120] -->
- <string name="developer_menu_ac3_support_no">Your TV doesn\'t support AC3 passthrough.</string>
-
<!-- opt out preferences -->
<eat-comment />
@@ -439,6 +424,11 @@
<!-- The text message which is shown when the current program is audio only one.
i.e. The program doesn't have a video. [CHAR LIMIT=NONE] -->
<string name="tvview_msg_audio_only">Audio only</string>
+ <!-- Message displayed when current channel's signal is too weak to use. [CHAR LIMIT=NONE] -->
+ <string name="tvview_msg_weak_signal">Weak signal</string>
+ <!-- The text message which is shown when the internet connection fails.
+ This message is shown at the center of the screen. [CHAR LIMIT=NONE] -->
+ <string name="tvview_msg_no_internet_connection">No internet connection</string>
<!-- Channel Banner -->
<eat-comment />
@@ -446,8 +436,6 @@
<string name="channel_banner_no_title">No title</string>
<!-- The text used when a channel is locked and program title needs to be hidden. [CHAR LIMIT=NONE] -->
<string name="channel_banner_locked_channel_title">Channel blocked</string>
- <!-- The episode title format displayed on the info banner. For example, "S1: Ep. 1 Winter is coming". -->
- <string name="episode_format">S<xliff:g id="season_number" example="1">%1$d</xliff:g>: Ep. <xliff:g id="episode_number" example="1">%2$d</xliff:g> <xliff:g id="episode_title" example="Winder is coming">%3$s</xliff:g></string>
<!-- Setup Sources Fragment -->
<eat-comment />
@@ -514,8 +502,6 @@
<string name="msg_missing_app">No app was found to handle this action.</string>
<!-- Message when all channels are hidden. [CHAR LIMIT=NONE] -->
<string name="msg_all_channels_hidden">All source channels are hidden.\nSelect at least one channel to watch.</string>
- <!-- Message displayed when availability is changed by weak signal. [CHAR LIMIT=NONE] -->
- <string name="msg_channel_unavailable_weak_signal">Unavailable due to weak video signal</string>
<!-- Message displayed when availability is changed by unknown reason. [CHAR LIMIT=NONE] -->
<string name="msg_channel_unavailable_unknown">The video is unexpectedly unavailable</string>
<!-- Message to notify the different use of Back Button: Home Button(To exit) Back button
@@ -552,6 +538,14 @@
<!-- DVR TODO(DVR): make translatable true. -->
<eat-comment />
+ <!-- Dialog message to ask to remove the selected recording schedule. -->
+ <string name="epg_dvr_dialog_message_remove_recording_schedule" translatable="false">Remove the scheduled recording?</string>
+ <!-- Dialog message to ask to stop the current recording. -->
+ <string name="epg_dvr_dialog_message_stop_recording" translatable="false">Stop the recording?</string>
+ <!-- Dialog message to ask to schedule the recording of the selected program. -->
+ <string name="epg_dvr_dialog_message_schedule_recording" translatable="false">Schedule the program recording?</string>
+ <!-- Dialog message to ask to delecte the recorded program. -->
+ <string name="epg_dvr_dialog_message_delete_schedule" translatable="false">Delete the recording schedule?</string>
<!-- Item label to schedule program recording. -->
<string name="epg_dvr_record_program" translatable="false">Record program</string>
<!-- Item label to schedule whole season recording of a TV show. -->
@@ -561,7 +555,22 @@
<string name="epg_dvr_delete_program" translatable="false">Delete schedule</string>
<!-- Menu item label to start DVR manager UI. -->
- <string name="channels_item_dvr" translatable="false">DVR</string>
+ <string name="channels_item_dvr" translatable="false">Recorded programs</string>
+ <!-- Menu item label to start recording of the current channel. -->
+ <string name="channels_item_record_start" translatable="false">Start recording</string>
+ <!-- Menu item label to start recording of the current channel. -->
+ <string name="channels_item_record_stop" translatable="false">Stop recording</string>
+
+ <!-- Item of record start dialog item for 10 min recording. -->
+ <string name="recording_start_dialog_10_min_duration" translatable="false">10 min.</string>
+ <!-- Item of record start dialog item for 30 min recording. -->
+ <string name="recording_start_dialog_30_min_duration" translatable="false">30 min.</string>
+ <!-- Item of record start dialog item for 1 hour recording. -->
+ <string name="recording_start_dialog_1_hour_duration" translatable="false">1 hour</string>
+ <!-- Item of record start dialog item for 3 min recording. -->
+ <string name="recording_start_dialog_3_hours_duration" translatable="false">3 hours</string>
+ <!-- Item of record start dialog item until the end of the current program. -->
+ <string name="recording_start_dialog_till_end_of_program" translatable="false">End of program</string>
<!--TODO(DVR): decide if translation needed Name of DVR service [CHAR LIMIT=NONE] -->
<string name="dvr_service_name" translatable="false">DVR Service</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 04a7d422..82a5258e 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -88,4 +88,35 @@
<style name="TV.DoneButtonContainerStyle" parent="DoneButtonContainerStyle">
<item name="android:background">@color/setup_done_button_container_background</item>
</style>
+
+ <style name="TV.HalfSizedDialogAnim" parent="android:Animation">
+ <item name="android:windowEnterAnimation">@anim/half_sized_dialog_enter</item>
+ <item name="android:windowExitAnimation">@anim/half_sized_dialog_exit</item>
+ </style>
+
+ <style name="TV.Dvr.GuidanceTitleStyle" parent="Widget.Setup.GuidanceTitleStyle">
+ <item name="android:layout_toStartOf">@null</item>
+ <item name="android:layout_alignParentTop">true</item>
+ <item name="android:layout_marginTop">55dp</item>
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:textSize">34sp</item>
+ <item name="android:fontFamily">@string/light_font</item>
+ <item name="android:gravity">start</item>
+ </style>
+
+ <style name="TV.Dvr.GuidanceDescriptionStyle" parent="Widget.Setup.GuidanceDescriptionStyle">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:fontFamily">@string/light_font</item>
+ <item name="android:textColor">#ADEEEEEE</item>
+ <item name="android:gravity">start</item>
+ </style>
+
+ <style name="TV.Dvr.GuidedActionsListStyle" parent="Widget.Setup.GuidedActionsListStyle">
+ <item name="android:layout_marginTop">51dp</item>
+ </style>
+
+ <style name="TV.Dvr.GuidedActionItemContainerStyle" parent="Widget.Setup.GuidedActionItemContainerStyle">
+ <item name="android:layout_height">45dp</item>
+ <item name="android:layout_marginTop">7dp</item>
+ </style>
</resources>
diff --git a/res/values/themes.xml b/res/values/themes.xml
index 23b59a27..774d63ab 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -34,6 +34,12 @@
<item name="imageCardViewStyle">@style/Widget.Leanback.ImageCardViewStyle</item>
</style>
+ <style name="Theme.TV.MainActivity" parent="@style/Theme.TV">
+ <item name="setupCommonGuidanceBackground">@color/setup_background</item>
+ <item name="doneButtonContainerStyle">@style/TV.DoneButtonContainerStyle</item>
+ <item name="guidedStepTheme">@style/Theme.TV.GuidedStep</item>
+ </style>
+
<style name="Theme.TV.dialog.Fullscreen" parent="@style/Theme.TV">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
@@ -41,6 +47,14 @@
<item name="android:windowAnimationStyle">@null</item>
</style>
+ <style name="Theme.TV.dialog.HalfSizedDialog" parent="@style/Theme.TV">
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:backgroundDimEnabled">true</item>
+ <item name="android:colorBackgroundCacheHint">@null</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowAnimationStyle">@style/TV.HalfSizedDialogAnim</item>
+ </style>
+
<style name="Theme.SelectInputActivity" parent="@android:style/Theme.Material.NoActionBar">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
@@ -49,8 +63,17 @@
</style>
<style name="Theme.TV.GuidedStep" parent="Theme.Setup.GuidedStep">
- <item name="setupCommonGuidanceBackground">@color/setup_background</item>
<item name="guidedActionsBackground">@color/setup_actions_background</item>
- <item name="doneButtonContainerStyle">@style/TV.DoneButtonContainerStyle</item>
+ </style>
+
+ <style name="Theme.TV.Dvr.GuidedStep" parent="Theme.Setup.GuidedStep">
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="guidedStepBackground">@color/common_tv_background</item>
+ <item name="guidedActionsBackground">@color/common_tv_background</item>
+ <item name="guidanceTitleStyle">@style/TV.Dvr.GuidanceTitleStyle</item>
+ <item name="guidanceDescriptionStyle">@style/TV.Dvr.GuidanceDescriptionStyle</item>
+ <item name="guidedActionsListStyle">@style/TV.Dvr.GuidedActionsListStyle</item>
+ <item name="guidedActionItemContainerStyle">@style/TV.Dvr.GuidedActionItemContainerStyle</item>
+ <item name="guidedActionContentWidthWeight">@string/lb_guidedactions_width_weight</item>
</style>
</resources>
diff --git a/src/com/android/tv/ChannelTuner.java b/src/com/android/tv/ChannelTuner.java
index 0a000e9b..faa27bbd 100644
--- a/src/com/android/tv/ChannelTuner.java
+++ b/src/com/android/tv/ChannelTuner.java
@@ -22,12 +22,12 @@ import android.net.Uri;
import android.os.Handler;
import android.support.annotation.MainThread;
import android.support.annotation.Nullable;
+import android.util.ArraySet;
import android.util.Log;
-import com.android.tv.common.CollectionUtils;
+import com.android.tv.common.SoftPreconditions;
import com.android.tv.data.Channel;
import com.android.tv.data.ChannelDataManager;
-import com.android.tv.util.SoftPreconditions;
import com.android.tv.util.TvInputManagerHelper;
import java.util.ArrayList;
@@ -56,7 +56,7 @@ public class ChannelTuner {
private final Handler mHandler = new Handler();
private final ChannelDataManager mChannelDataManager;
- private final Set<Listener> mListeners = CollectionUtils.createSmallSet();
+ private final Set<Listener> mListeners = new ArraySet<>();
@Nullable
private Channel mCurrentChannel;
private final TvInputManagerHelper mInputManager;
diff --git a/src/com/android/tv/Features.java b/src/com/android/tv/Features.java
index 1a665506..6a78b632 100644
--- a/src/com/android/tv/Features.java
+++ b/src/com/android/tv/Features.java
@@ -16,20 +16,21 @@
package com.android.tv;
+import static com.android.tv.common.feature.EngOnlyFeature.ENG_ONLY_FEATURE;
+import static com.android.tv.common.feature.FeatureUtils.AND;
+import static com.android.tv.common.feature.FeatureUtils.ON;
+import static com.android.tv.common.feature.FeatureUtils.OR;
+
+import android.content.Context;
+import android.os.Build;
import android.support.annotation.VisibleForTesting;
+import android.support.v4.os.BuildCompat;
import com.android.tv.common.feature.Feature;
import com.android.tv.common.feature.GServiceFeature;
import com.android.tv.common.feature.PackageVersionFeature;
import com.android.tv.common.feature.PropertyFeature;
-import com.android.tv.common.feature.SharedPreferencesFeature;
-import com.android.tv.common.feature.TestableFeature;
-
-import static com.android.tv.common.feature.FeatureUtils.AND;
-import static com.android.tv.common.feature.FeatureUtils.ON;
-import static com.android.tv.common.feature.FeatureUtils.OR;
-import static com.android.tv.common.feature.TestableFeature.createTestableFeature;
-import static com.android.tv.common.feature.EngOnlyFeature.ENG_ONLY_FEATURE;
+import com.android.tv.util.PermissionUtils;
/**
* List of {@link Feature} for the Live TV App.
@@ -43,47 +44,65 @@ public final class Features {
* <p>Do not turn this on until the splash screen asking existing users to opt-in is launched.
* See <a href="http://b/20228119">b/20228119</a>
*/
- public static Feature ANALYTICS_OPT_IN = ENG_ONLY_FEATURE;
+ public static final Feature ANALYTICS_OPT_IN = ENG_ONLY_FEATURE;
/**
* Analytics that include sensitive information such as channel or program identifiers.
*
* <p>See <a href="http://b/22062676">b/22062676</a>
*/
- public static Feature ANALYTICS_V2 = AND(ON, ANALYTICS_OPT_IN);
+ public static final Feature ANALYTICS_V2 = AND(ON, ANALYTICS_OPT_IN);
- public static Feature EPG_SEARCH = new PropertyFeature("feature_tv_use_epg_search", false);
+ public static final Feature EPG_SEARCH =
+ new PropertyFeature("feature_tv_use_epg_search", false);
- public static SharedPreferencesFeature USB_TUNER = new SharedPreferencesFeature(
- "usb_tuner", true,
- OR(ENG_ONLY_FEATURE, new GServiceFeature("usbtuner_enabled", false)));
- public static Feature DEVELOPER_OPTION = OR(ENG_ONLY_FEATURE,
- new GServiceFeature("usbtuner_enabled", false));
+ public static final Feature USB_TUNER = new Feature() {
+
+ /**
+ * This is special handling just for USB Tuner.
+ * It does not require any N API's but relies on a improvements in N for AC3 support
+ * After release, change class to this to just be
+ * {@link BuildCompat#isAtLeastN()}.
+ */
+ @Override
+ public boolean isEnabled(Context context) {
+ return Build.VERSION.SDK_INT > Build.VERSION_CODES.M || BuildCompat.isAtLeastN();
+ }
+
+ };
private static final String PLAY_STORE_PACKAGE_NAME = "com.android.vending";
private static final int PLAY_STORE_ZIMA_VERSION_CODE = 80441186;
- private static Feature PLAY_STORE_LINK = new PackageVersionFeature(PLAY_STORE_PACKAGE_NAME,
- PLAY_STORE_ZIMA_VERSION_CODE);
+ private static final Feature PLAY_STORE_LINK =
+ new PackageVersionFeature(PLAY_STORE_PACKAGE_NAME, PLAY_STORE_ZIMA_VERSION_CODE);
- public static Feature ONBOARDING_PLAY_STORE = PLAY_STORE_LINK;
+ public static final Feature ONBOARDING_PLAY_STORE = PLAY_STORE_LINK;
/**
* A flag which indicates that the on-boarding experience is used or not.
*
* <p>See <a href="http://b/24070322">b/24070322</a>
*/
- public static Feature ONBOARDING_EXPERIENCE = ONBOARDING_PLAY_STORE;
+ public static final Feature ONBOARDING_EXPERIENCE = ONBOARDING_PLAY_STORE;
private static final String GSERVICE_KEY_UNHIDE = "live_channels_unhide";
/**
* A flag which indicates that LC app is unhidden even when there is no input.
*/
- public static Feature UNHIDE = AND(ONBOARDING_EXPERIENCE,
- new GServiceFeature(GSERVICE_KEY_UNHIDE, false));
+ public static final Feature UNHIDE = AND(ONBOARDING_EXPERIENCE,
+ OR(new GServiceFeature(GSERVICE_KEY_UNHIDE, false), new Feature() {
+ @Override
+ public boolean isEnabled(Context context) {
+ // If LC app runs as non-system app, we unhide the app.
+ return !PermissionUtils.hasAccessAllEpg(context);
+ }
+ }));
@VisibleForTesting
public static Feature TEST_FEATURE = new PropertyFeature("test_feature", false);
+ public static final Feature FETCH_EPG = new PropertyFeature("live_channels_fetch_epg", false);
+
private Features() {
}
}
diff --git a/src/com/android/tv/MainActivity.java b/src/com/android/tv/MainActivity.java
index 99bcb125..78fda42a 100644
--- a/src/com/android/tv/MainActivity.java
+++ b/src/com/android/tv/MainActivity.java
@@ -40,6 +40,7 @@ import android.media.tv.TvContract;
import android.media.tv.TvContract.Channels;
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager;
+import android.media.tv.TvInputManager.TvInputCallback;
import android.media.tv.TvTrackInfo;
import android.media.tv.TvView.OnUnhandledInputEventListener;
import android.net.Uri;
@@ -52,6 +53,7 @@ import android.provider.Settings;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.v4.os.BuildCompat;
import android.text.TextUtils;
import android.util.Log;
import android.view.Display;
@@ -73,10 +75,12 @@ import com.android.tv.analytics.SendConfigInfoRunnable;
import com.android.tv.analytics.Tracker;
import com.android.tv.common.BuildConfig;
import com.android.tv.common.MemoryManageable;
+import com.android.tv.common.SoftPreconditions;
import com.android.tv.common.TvCommonUtils;
import com.android.tv.common.TvContentRatingCache;
import com.android.tv.common.WeakHandler;
import com.android.tv.common.feature.CommonFeatures;
+import com.android.tv.common.recording.RecordedProgram;
import com.android.tv.data.Channel;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.OnCurrentProgramUpdatedListener;
@@ -89,7 +93,7 @@ import com.android.tv.dialog.SafeDismissDialogFragment;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.DvrPlayActivity;
-import com.android.tv.dvr.Recording;
+import com.android.tv.dvr.ScheduledRecording;
import com.android.tv.menu.Menu;
import com.android.tv.onboarding.OnboardingActivity;
import com.android.tv.parental.ContentRatingsManager;
@@ -125,11 +129,13 @@ import com.android.tv.util.PipInputManager.PipInput;
import com.android.tv.util.RecurringRunner;
import com.android.tv.util.SearchManagerHelper;
import com.android.tv.util.SetupUtils;
-import com.android.tv.util.SoftPreconditions;
import com.android.tv.util.SystemProperties;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.TvSettings;
import com.android.tv.util.TvSettings.PipSound;
+import com.android.usbtuner.UsbTunerPreferences;
+import com.android.usbtuner.setup.TunerSetupActivity;
+import com.android.usbtuner.tvinput.UsbTunerTvInputService;
import com.android.tv.util.TvTrackInfoUtils;
import com.android.tv.util.Utils;
@@ -140,7 +146,6 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
-import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
@@ -268,6 +273,7 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
private MediaSession mMediaSession;
private int mNowPlayingCardWidth;
private int mNowPlayingCardHeight;
+ private final MyOnTuneListener mOnTuneListener = new MyOnTuneListener();
private String mInputIdUnderSetup;
private boolean mIsSetupActivityCalledByPopup;
@@ -281,7 +287,7 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
private boolean mDebugNonFullSizeScreen;
private boolean mActivityResumed;
private boolean mActivityStarted;
- private boolean mLaunchedByLauncher;
+ private boolean mShouldTuneToTunerChannel;
private boolean mUseKeycodeBlacklist;
private boolean mShowLockedChannelsTemporarily;
private boolean mBackKeyPressed;
@@ -290,6 +296,8 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
private boolean mAc3PassthroughSupported;
private boolean mShowNewSourcesFragment = true;
private Uri mRecordingUri;
+ private String mUsbTunerInputId;
+ private boolean mOtherActivityLaunched;
private boolean mIsFilmModeSet;
private float mDefaultRefreshRate;
@@ -323,7 +331,7 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
// A caller which started this activity. (e.g. TvSearch)
private String mSource;
- private Handler mHandler = new MainActivityHandler(this);
+ private final Handler mHandler = new MainActivityHandler(this);
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -367,6 +375,7 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
Channel channel = mTvView.getCurrentChannel();
if (channel != null && channel.getId() == channelId) {
updateChannelBannerAndShowIfNeeded(UPDATE_CHANNEL_BANNER_REASON_UPDATE_INFO);
+ updateMediaSession();
}
}
};
@@ -413,6 +422,19 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
};
private ProgramGuideSearchFragment mSearchFragment;
+ private TvInputCallback mTvInputCallback = new TvInputCallback() {
+ @Override
+ public void onInputAdded(String inputId) {
+ if (mUsbTunerInputId.equals(inputId)
+ && UsbTunerPreferences.shouldShowSetupActivity(MainActivity.this)) {
+ Intent intent = TunerSetupActivity.createSetupActivity(MainActivity.this);
+ startActivity(intent);
+ UsbTunerPreferences.setShouldShowSetupActivity(MainActivity.this, false);
+ SetupUtils.getInstance(MainActivity.this).markAsKnownInput(mUsbTunerInputId);
+ }
+ }
+ };
+
private void applyParentalControlSettings() {
boolean parentalControlEnabled = mTvInputManagerHelper.getParentalControlSettings()
.isParentalControlsEnabled();
@@ -424,12 +446,19 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
protected void onCreate(Bundle savedInstanceState) {
if (DEBUG) Log.d(TAG,"onCreate()");
super.onCreate(savedInstanceState);
-
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M
+ && !PermissionUtils.hasAccessAllEpg(this)) {
+ Toast.makeText(this, R.string.msg_not_supported_device, Toast.LENGTH_LONG).show();
+ finish();
+ return;
+ }
+ boolean skipToShowOnboarding = getIntent().getAction() == Intent.ACTION_VIEW
+ && TvContract.isChannelUriForPassthroughInput(getIntent().getData());
if (Features.ONBOARDING_EXPERIENCE.isEnabled(this)
- && OnboardingUtils.needToShowOnboarding(this)
+ && OnboardingUtils.needToShowOnboarding(this) && !skipToShowOnboarding
&& !TvCommonUtils.isRunningInTest()) {
- // TODO: We turn off the new onboarding for test, because tests are broken by
- // the new onboarding. We need to enable the feature for tests later.
+ // TODO: The onboarding is turned off in test, because tests are broken by the
+ // onboarding. We need to enable the feature for tests later.
startActivity(OnboardingActivity.buildIntent(this, getIntent()));
finish();
return;
@@ -442,6 +471,8 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
}
mTracker = tvApplication.getTracker();
mTvInputManagerHelper = tvApplication.getTvInputManagerHelper();
+ mTvInputManagerHelper.addCallback(mTvInputCallback);
+ mUsbTunerInputId = UsbTunerTvInputService.getInputId(this);
mChannelDataManager = tvApplication.getChannelDataManager();
mProgramDataManager = tvApplication.getProgramDataManager();
mProgramDataManager.addOnCurrentProgramUpdatedListener(Channel.INVALID_ID,
@@ -455,7 +486,7 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
mMemoryManageables.add(mProgramDataManager);
mMemoryManageables.add(ImageCache.getInstance());
mMemoryManageables.add(TvContentRatingCache.getInstance());
- if(CommonFeatures.DVR.isEnabled(this)) {
+ if (CommonFeatures.DVR.isEnabled(this) && BuildCompat.isAtLeastN()) {
mDvrManager = tvApplication.getDvrManager();
mDvrDataManager = tvApplication.getDvrDataManager();
}
@@ -502,6 +533,7 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
new OnCurrentProgramUpdatedListener() {
@Override
public void onCurrentProgramUpdated(long channelId, Program program) {
+ updateMediaSession();
switch (mTimeShiftManager.getLastActionId()) {
case TimeShiftManager.TIME_SHIFT_ACTION_ID_REWIND:
case TimeShiftManager.TIME_SHIFT_ACTION_ID_FAST_FORWARD:
@@ -618,6 +650,10 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
mSendConfigInfoRecurringRunner.start();
mChannelStatusRecurringRunner = SendChannelStatusRunnable
.startChannelStatusRecurringRunner(this, mTracker, mChannelDataManager);
+
+ // To avoid not updating Rating systems when changing language.
+ mTvInputManagerHelper.getContentRatingsManager().update();
+
initForTest();
}
@@ -625,7 +661,12 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
if (requestCode == PERMISSIONS_REQUEST_READ_TV_LISTINGS) {
- if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ if (grantResults != null && grantResults.length > 0
+ && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ // Start reload of dependent data
+ mChannelDataManager.reload();
+ mProgramDataManager.reload();
+
// Restart live channels.
Intent intent = getIntent();
finish();
@@ -719,15 +760,11 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
protected void onResume() {
if (DEBUG) Log.d(TAG, "onResume()");
super.onResume();
- if (!PermissionUtils.hasAccessAllEpg(this)) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
- Toast.makeText(this, R.string.msg_not_supported_device, Toast.LENGTH_LONG).show();
- finish();
- } else if (checkSelfPermission(PERMISSION_READ_TV_LISTINGS)
+ if (!PermissionUtils.hasAccessAllEpg(this)
+ && checkSelfPermission(PERMISSION_READ_TV_LISTINGS)
!= PackageManager.PERMISSION_GRANTED) {
- requestPermissions(new String[]{PERMISSION_READ_TV_LISTINGS},
- PERMISSIONS_REQUEST_READ_TV_LISTINGS);
- }
+ requestPermissions(new String[]{PERMISSION_READ_TV_LISTINGS},
+ PERMISSIONS_REQUEST_READ_TV_LISTINGS);
}
mTracker.sendScreenView(SCREEN_NAME);
@@ -735,6 +772,7 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
mNeedShowBackKeyGuide = true;
mActivityResumed = true;
mShowNewSourcesFragment = true;
+ mOtherActivityLaunched = false;
int result = mAudioManager.requestAudioFocus(MainActivity.this,
AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
mAudioFocusStatus = (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) ?
@@ -798,7 +836,7 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
}
mBackKeyPressed = false;
mShowLockedChannelsTemporarily = false;
- mLaunchedByLauncher = false;
+ mShouldTuneToTunerChannel = false;
if (!mVisibleBehind) {
mAudioFocusStatus = AudioManager.AUDIOFOCUS_LOSS;
mAudioManager.abandonAudioFocus(this);
@@ -836,7 +874,7 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
private void resumeTvIfNeeded() {
if (DEBUG) Log.d(TAG, "resumeTvIfNeeded()");
if (!mTvView.isPlaying() || mInitChannelUri != null
- || (mLaunchedByLauncher && mChannelTuner.isCurrentChannelPassthrough())) {
+ || (mShouldTuneToTunerChannel && mChannelTuner.isCurrentChannelPassthrough())) {
if (TvContract.isChannelUriForPassthroughInput(mInitChannelUri)) {
// The target input may not be ready yet, especially, just after screen on.
String inputId = mInitChannelUri.getPathSegments().get(1);
@@ -1079,10 +1117,15 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
}
public Channel getCurrentChannel() {
- return mChannelTuner.getCurrentChannel();
+ return mTvView.isRecordingPlayback() ? mTvView.getCurrentChannel()
+ : mChannelTuner.getCurrentChannel();
}
public long getCurrentChannelId() {
+ if (mTvView.isRecordingPlayback()) {
+ Channel channel = mTvView.getCurrentChannel();
+ return channel == null ? Channel.INVALID_ID : channel.getId();
+ }
return mChannelTuner.getCurrentChannelId();
}
@@ -1099,7 +1142,31 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
* If the time shifting is available, it can be a past program.
*/
public Program getCurrentProgram() {
- if (mTimeShiftManager.isAvailable()) {
+ return getCurrentProgram(true);
+ }
+
+ /**
+ * Returns {@code true}, if this view is the recording playback mode.
+ */
+ public boolean isRecordingPlayback() {
+ return mTvView.isRecordingPlayback();
+ }
+
+ /**
+ * Returns the recording which is being played right now.
+ */
+ public RecordedProgram getPlayingRecordedProgram() {
+ return mTvView.getPlayingRecordedProgram();
+ }
+
+ /**
+ * Returns the current program which the user is watching right now.<p>
+ *
+ * @param applyTimeShifted If it is true and the time shifting is available, it can be
+ * a past program.
+ */
+ public Program getCurrentProgram(boolean applyTimeShifted) {
+ if (applyTimeShifted && mTimeShiftManager.isAvailable()) {
return mTimeShiftManager.getCurrentProgram();
}
return mProgramDataManager.getCurrentProgram(getCurrentChannelId());
@@ -1372,7 +1439,7 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
onKeyUp(keyCode, event);
return true;
}
- mLaunchedByLauncher = intent.getBooleanExtra(Utils.EXTRA_KEY_FROM_LAUNCHER, false);
+ mShouldTuneToTunerChannel = intent.getBooleanExtra(Utils.EXTRA_KEY_FROM_LAUNCHER, false);
mInitChannelUri = null;
String extraAction = intent.getStringExtra(Utils.EXTRA_KEY_ACTION);
@@ -1387,14 +1454,24 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
}
}
- if (CommonFeatures.DVR.isEnabled(this)) {
+ if (CommonFeatures.DVR.isEnabled(this) && BuildCompat.isAtLeastN()) {
mRecordingUri = intent.getParcelableExtra(Utils.EXTRA_KEY_RECORDING_URI);
if (mRecordingUri != null) {
return true;
}
}
- if (Intent.ACTION_VIEW.equals(intent.getAction())) {
+ // TODO: remove the checkState once N API is finalized.
+ SoftPreconditions.checkState(TvInputManager.ACTION_SETUP_INPUTS.equals(
+ "android.media.tv.action.SETUP_INPUTS"));
+ if (TvInputManager.ACTION_SETUP_INPUTS.equals(intent.getAction())) {
+ runAfterAttachedToWindow(new Runnable() {
+ @Override
+ public void run() {
+ mOverlayManager.showSetupFragment();
+ }
+ });
+ } else if (Intent.ACTION_VIEW.equals(intent.getAction())) {
Uri uri = intent.getData();
try {
mSource = uri.getQueryParameter(Utils.PARAM_SOURCE);
@@ -1416,6 +1493,7 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
if (Channels.CONTENT_URI.equals(mInitChannelUri)) {
// Tune to default channel.
mInitChannelUri = null;
+ mShouldTuneToTunerChannel = true;
return true;
}
if ((!Utils.isChannelUriForOneChannel(mInitChannelUri)
@@ -1483,6 +1561,7 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
}
private void setVolumeByAudioFocusStatus(TunableTvView tvView) {
+ SoftPreconditions.checkState(tvView == mTvView || tvView == mPipView);
if (tvView.isPlaying()) {
switch (mAudioFocusStatus) {
case AudioManager.AUDIOFOCUS_GAIN:
@@ -1497,6 +1576,15 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
break;
}
}
+ if (tvView == mTvView) {
+ if (mPipView != null && mPipView.isPlaying()) {
+ mPipView.setStreamVolume(AUDIO_MIN_VOLUME);
+ }
+ } else { // tvView == mPipView
+ if (mTvView != null && mTvView.isPlaying()) {
+ mTvView.setStreamVolume(AUDIO_MIN_VOLUME);
+ }
+ }
}
private void stopTv() {
@@ -1601,7 +1689,7 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
mPipView.setMain();
scheduleRestoreMainTvView();
mTvViewUiManager.onPipStart();
- mPipView.setStreamVolume(AUDIO_MIN_VOLUME);
+ setVolumeByAudioFocusStatus();
}
private void scheduleRestoreMainTvView() {
@@ -1633,27 +1721,9 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
}
private void playRecording(Uri recordingUri) {
- String inputId = recordingUri.getQueryParameter(Recording.PARAM_INPUT_ID);
- SoftPreconditions.checkNotNull(inputId);
- mTvView.playRecording(inputId, recordingUri, new OnTuneListener() {
- @Override
- public void onTuneFailed(Channel channel) { }
-
- @Override
- public void onUnexpectedStop(Channel channel) { }
-
- @Override
- public void onStreamInfoChanged(StreamInfo info) { }
-
- @Override
- public void onChannelRetuned(Uri channel) { }
-
- @Override
- public void onContentBlocked() { }
-
- @Override
- public void onContentAllowed() { }
- });
+ mTvView.playRecording(recordingUri, mOnTuneListener);
+ mOnTuneListener.onPlayRecording();
+ updateChannelBannerAndShowIfNeeded(UPDATE_CHANNEL_BANNER_REASON_TUNE);
}
private void tune() {
@@ -1668,75 +1738,73 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
return;
}
mTunePending = false;
- if (!mChannelTuner.isCurrentChannelPassthrough()
- && mTvInputManagerHelper.getTunerTvInputSize() == 0) {
- Toast.makeText(this, R.string.msg_no_input, Toast.LENGTH_SHORT).show();
- // TODO: Direct the user to a Play Store landing page for TvInputService apps.
- finish();
- return;
- }
- SetupUtils setupUtils = SetupUtils.getInstance(this);
- if (!mChannelTuner.isCurrentChannelPassthrough() && setupUtils.isFirstTune()) {
- if (!mChannelTuner.areAllChannelsLoaded()) {
- // tune() will be called, once all channels are loaded.
- stopTv("tune()", false);
- return;
- }
- if (mChannelDataManager.getChannelCount() > 0) {
- mOverlayManager.showIntroDialog();
- } else if (!Features.ONBOARDING_EXPERIENCE.isEnabled(this)) {
- mOverlayManager.showSetupFragment();
+ final Channel channel = mChannelTuner.getCurrentChannel();
+ if (!mChannelTuner.isCurrentChannelPassthrough()) {
+ if (mTvInputManagerHelper.getTunerTvInputSize() == 0) {
+ Toast.makeText(this, R.string.msg_no_input, Toast.LENGTH_SHORT).show();
+ // TODO: Direct the user to a Play Store landing page for TvInputService apps.
+ finish();
return;
}
- }
- if (!TvCommonUtils.isRunningInTest() && mShowNewSourcesFragment
- && setupUtils.hasUnrecognizedInput(mTvInputManagerHelper)) {
- // Show new channel sources fragment.
- runAfterAttachedToWindow(new Runnable() {
- @Override
- public void run() {
- mOverlayManager.runAfterOverlaysAreClosed(new Runnable() {
- @Override
- public void run() {
- mOverlayManager.showNewSourcesFragment();
- }
- });
+ SetupUtils setupUtils = SetupUtils.getInstance(this);
+ if (setupUtils.isFirstTune()) {
+ if (!mChannelTuner.areAllChannelsLoaded()) {
+ // tune() will be called, once all channels are loaded.
+ stopTv("tune()", false);
+ return;
+ }
+ if (mChannelDataManager.getChannelCount() > 0) {
+ mOverlayManager.showIntroDialog();
+ } else if (!Features.ONBOARDING_EXPERIENCE.isEnabled(this)) {
+ mOverlayManager.showSetupFragment();
+ return;
}
- });
- }
- mShowNewSourcesFragment = false;
- if (!mChannelTuner.isCurrentChannelPassthrough()
- && mChannelTuner.getBrowsableChannelCount() == 0
- && mChannelDataManager.getChannelCount() > 0
- && !mOverlayManager.getSideFragmentManager().isActive()) {
- if (!mChannelTuner.areAllChannelsLoaded()) {
- return;
}
- if (mTvInputManagerHelper.getTunerTvInputSize() == 1) {
- mOverlayManager.getSideFragmentManager().show(new CustomizeChannelListFragment());
- } else {
- showSettingsFragment();
+ if (!TvCommonUtils.isRunningInTest() && mShowNewSourcesFragment
+ && setupUtils.hasUnrecognizedInput(mTvInputManagerHelper)) {
+ // Show new channel sources fragment.
+ runAfterAttachedToWindow(new Runnable() {
+ @Override
+ public void run() {
+ mOverlayManager.runAfterOverlaysAreClosed(new Runnable() {
+ @Override
+ public void run() {
+ mOverlayManager.showNewSourcesFragment();
+ }
+ });
+ }
+ });
}
- return;
- }
- // TODO: need to refactor the following code to put in startTv.
- final Channel channel = mChannelTuner.getCurrentChannel();
- if (channel == null) {
- // There is no channel to tune to.
- stopTv("tune()", false);
- if (!mChannelDataManager.isDbLoadFinished()) {
- // Wait until channel data is loaded in order to know the number of channels.
- // tune() will be retried, once the channel data is loaded.
+ mShowNewSourcesFragment = false;
+ if (mChannelTuner.getBrowsableChannelCount() == 0
+ && mChannelDataManager.getChannelCount() > 0
+ && !mOverlayManager.getSideFragmentManager().isActive()) {
+ if (!mChannelTuner.areAllChannelsLoaded()) {
+ return;
+ }
+ if (mTvInputManagerHelper.getTunerTvInputSize() == 1) {
+ mOverlayManager.getSideFragmentManager().show(
+ new CustomizeChannelListFragment());
+ } else {
+ showSettingsFragment();
+ }
return;
}
- if (mOverlayManager.getSideFragmentManager().isActive()) {
+ // TODO: need to refactor the following code to put in startTv.
+ if (channel == null) {
+ // There is no channel to tune to.
+ stopTv("tune()", false);
+ if (!mChannelDataManager.isDbLoadFinished()) {
+ // Wait until channel data is loaded in order to know the number of channels.
+ // tune() will be retried, once the channel data is loaded.
+ return;
+ }
+ if (mOverlayManager.getSideFragmentManager().isActive()) {
+ return;
+ }
+ mOverlayManager.showSetupFragment();
return;
}
- mOverlayManager.showSetupFragment();
- return;
- }
-
- if (!channel.isPassthrough()) {
setupUtils.onTuned();
if (mTuneParams != null) {
Long initChannelId = mTuneParams.getLong(KEY_INIT_CHANNEL_ID);
@@ -1752,9 +1820,6 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
if (!isUnderShrunkenTvView()) {
mLastAllowedRatingForCurrentChannel = null;
}
- final boolean wasUnderShrunkenTvView = isUnderShrunkenTvView();
- final long streamInfoUpdateTimeThresholdMs =
- System.currentTimeMillis() + FIRST_STREAM_INFO_UPDATE_DELAY_MILLIS;
mHandler.removeMessages(MSG_UPDATE_CHANNEL_BANNER_BY_INFO_UPDATE);
if (mAccessibilityManager.isEnabled()) {
// For every tune, we need to inform the tuned channel or input to a user,
@@ -1774,105 +1839,9 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
mAccessibilityManager.sendAccessibilityEvent(event);
}
- boolean success = mTvView.tuneTo(channel, mTuneParams, new OnTuneListener() {
- boolean mUnlockAllowedRatingBeforeShrunken = true;
-
- @Override
- public void onUnexpectedStop(Channel channel) {
- stopTv();
- startTv(null);
- }
-
- @Override
- public void onTuneFailed(Channel channel) {
- Log.w(TAG, "Failed to tune to channel " + channel.getId()
- + "@" + channel.getInputId());
- if (mTvView.isFadedOut()) {
- mTvView.removeFadeEffect();
- }
- // TODO: show something to user about this error.
- }
+ boolean success = mTvView.tuneTo(channel, mTuneParams, mOnTuneListener);
+ mOnTuneListener.onTune(channel, isUnderShrunkenTvView());
- @Override
- public void onStreamInfoChanged(StreamInfo info) {
- if (info.isVideoAvailable() && mTuneDurationTimer.isRunning()) {
- mTracker.sendChannelTuneTime(info.getCurrentChannel(),
- mTuneDurationTimer.reset());
- }
- // If updateChannelBanner() is called without delay, the stream info seems flickering
- // when the channel is quickly changed.
- if (!mHandler.hasMessages(MSG_UPDATE_CHANNEL_BANNER_BY_INFO_UPDATE)
- && info.isVideoAvailable()) {
- if (System.currentTimeMillis() > streamInfoUpdateTimeThresholdMs) {
- updateChannelBannerAndShowIfNeeded(
- UPDATE_CHANNEL_BANNER_REASON_UPDATE_INFO);
- } else {
- mHandler.sendMessageDelayed(mHandler.obtainMessage(
- MSG_UPDATE_CHANNEL_BANNER_BY_INFO_UPDATE),
- streamInfoUpdateTimeThresholdMs - System.currentTimeMillis());
- }
- }
-
- applyDisplayRefreshRate(info.getVideoFrameRate());
- mTvViewUiManager.updateTvView();
- applyMultiAudio();
- applyClosedCaption();
- // TODO: Send command to TIS with checking the settings in TV and CaptionManager.
- mOverlayManager.getMenu().onStreamInfoChanged();
- if (mTvView.isVideoAvailable()) {
- mTvViewUiManager.fadeInTvView();
- }
- mHandler.removeCallbacks(mRestoreMainViewRunnable);
- restoreMainTvView();
- }
-
- @Override
- public void onChannelRetuned(Uri channel) {
- if (channel == null) {
- return;
- }
- Channel currentChannel =
- mChannelDataManager.getChannel(ContentUris.parseId(channel));
- if (currentChannel == null) {
- Log.e(TAG, "onChannelRetuned is called but can't find a channel with the URI "
- + channel);
- return;
- }
- if (isChannelChangeKeyDownReceived()) {
- // Ignore this message if the user is changing the channel.
- return;
- }
- mChannelTuner.setCurrentChannel(currentChannel);
- mTvView.setCurrentChannel(currentChannel);
- updateChannelBannerAndShowIfNeeded(UPDATE_CHANNEL_BANNER_REASON_TUNE);
- }
-
- @Override
- public void onContentBlocked() {
- mTuneDurationTimer.reset();
- TvContentRating rating = mTvView.getBlockedContentRating();
- // When tuneTo was called while TV view was shrunken, if the channel id is the same
- // with the channel watched before shrunken, we allow the rating which was allowed
- // before.
- if (wasUnderShrunkenTvView && mUnlockAllowedRatingBeforeShrunken
- && mChannelBeforeShrunkenTvView.equals(channel)
- && rating.equals(mAllowedRatingBeforeShrunken)) {
- mUnlockAllowedRatingBeforeShrunken = isUnderShrunkenTvView();
- mTvView.requestUnblockContent(rating);
- }
-
- updateChannelBannerAndShowIfNeeded(UPDATE_CHANNEL_BANNER_REASON_LOCK_OR_UNLOCK);
- mTvViewUiManager.fadeInTvView();
- }
-
- @Override
- public void onContentAllowed() {
- if (!isUnderShrunkenTvView()) {
- mUnlockAllowedRatingBeforeShrunken = false;
- }
- updateChannelBannerAndShowIfNeeded(UPDATE_CHANNEL_BANNER_REASON_LOCK_OR_UNLOCK);
- }
- });
mTuneParams = null;
if (!success) {
Toast.makeText(this, R.string.msg_tune_failed, Toast.LENGTH_SHORT).show();
@@ -1969,6 +1938,9 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
}
private void updateProgramPosterArt(Program program, @Nullable Bitmap posterArt) {
+ if (getCurrentChannel() == null) {
+ return;
+ }
if (posterArt != null) {
String cardTitleText = program == null ? null : program.getTitle();
if (TextUtils.isEmpty(cardTitleText)) {
@@ -1995,9 +1967,15 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
return;
}
- String cardTitleText = program == null ? null : program.getTitle();
- if (TextUtils.isEmpty(cardTitleText)) {
- cardTitleText = channel.getDisplayName();
+ String cardTitleText;
+ if (channel.isPassthrough()) {
+ TvInputInfo input = getTvInputManagerHelper().getTvInputInfo(channel.getInputId());
+ cardTitleText = Utils.loadLabel(this, input);
+ } else {
+ cardTitleText = program == null ? null : program.getTitle();
+ if (TextUtils.isEmpty(cardTitleText)) {
+ cardTitleText = channel.getDisplayName();
+ }
}
Bitmap posterArt = BitmapFactory.decodeResource(
@@ -2077,7 +2055,7 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
private void updateChannelBannerAndShowIfNeeded(@ChannelBannerUpdateReason int reason) {
if(DEBUG) Log.d(TAG, "updateChannelBannerAndShowIfNeeded(reason=" + reason + ")");
- if (!mChannelTuner.isCurrentChannelPassthrough()) {
+ if (!mChannelTuner.isCurrentChannelPassthrough() || mTvView.isRecordingPlayback()) {
int lockType = ChannelBannerView.LOCK_NONE;
if (mTvView.isScreenBlocked()) {
lockType = ChannelBannerView.LOCK_CHANNEL_INFO;
@@ -2321,6 +2299,9 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
mChannelStatusRecurringRunner.stop();
mChannelStatusRecurringRunner = null;
}
+ if (mTvInputManagerHelper != null) {
+ mTvInputManagerHelper.removeCallback(mTvInputCallback);
+ }
super.onDestroy();
}
@@ -2473,7 +2454,7 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
public void done(boolean success) {
if (success) {
mLastAllowedRatingForCurrentChannel = rating;
- mTvView.requestUnblockContent(rating);
+ mTvView.unblockContent(rating);
}
}
});
@@ -2500,7 +2481,8 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
updateChannelBannerAndShowIfNeeded(UPDATE_CHANNEL_BANNER_REASON_FORCE_SHOW);
}
if (keyCode != KeyEvent.KEYCODE_E) {
- mOverlayManager.showMenu(Menu.REASON_NONE);
+ mOverlayManager.showMenu(mTvView.isRecordingPlayback()
+ ? Menu.REASON_RECORDING_PLAYBACK : Menu.REASON_NONE);
}
return true;
case KeyEvent.KEYCODE_CHANNEL_UP:
@@ -2603,17 +2585,18 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
case KeyEvent.KEYCODE_PROG_YELLOW:
case KeyEvent.KEYCODE_BUTTON_Y:
case KeyEvent.KEYCODE_Y: {
- if (CommonFeatures.DVR.isEnabled(this)) {
+ if (CommonFeatures.DVR.isEnabled(this) && BuildCompat.isAtLeastN()) {
// TODO(DVR) only get finished recordings.
- List<Recording> recordings = mDvrDataManager.getRecordings();
- Log.d(TAG, "Found " + recordings.size() + " recordings");
- if (recordings.isEmpty()) {
+ List<RecordedProgram> recordedPrograms = mDvrDataManager
+ .getRecordedPrograms();
+ Log.d(TAG, "Found " + recordedPrograms.size() + " recordings");
+ if (recordedPrograms.isEmpty()) {
Toast.makeText(this, "No finished recording to play", Toast.LENGTH_LONG)
.show();
} else {
- Recording r = recordings.get(0);
+ RecordedProgram r = recordedPrograms.get(0);
Intent intent = new Intent(this, DvrPlayActivity.class);
- intent.putExtra(Recording.RECORDING_ID_EXTRA, r.getId());
+ intent.putExtra(ScheduledRecording.RECORDING_ID_EXTRA, r.getId());
startActivity(intent);
}
return true;
@@ -2664,6 +2647,20 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
}
}
+ @Override
+ public void enterPictureInPictureMode() {
+ // We need to hide overlay first, before moving the activity to PIP. If not, UI will
+ // be shown during PIP stack resizing, because UI and its animation is stuck during
+ // PIP resizing.
+ mOverlayManager.hideOverlays(TvOverlayManager.FLAG_HIDE_OVERLAYS_WITHOUT_ANIMATION);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ MainActivity.super.enterPictureInPictureMode();
+ }
+ });
+ }
+
public void togglePipView() {
enablePipView(!mPipEnabled, true);
mOverlayManager.getMenu().update();
@@ -2714,7 +2711,6 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
// Recover the stream volume of the main TV view, if needed.
if (mPipSound == TvSettings.PIP_SOUND_PIP_WINDOW) {
setVolumeByAudioFocusStatus(mTvView);
- mPipView.setStreamVolume(AUDIO_MIN_VOLUME);
mPipSound = TvSettings.PIP_SOUND_MAIN;
mTvOptionsManager.onPipSoundChanged(mPipSound);
}
@@ -2877,10 +2873,8 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
}
if (mPipSound == TvSettings.PIP_SOUND_MAIN) {
setVolumeByAudioFocusStatus(mTvView);
- mPipView.setStreamVolume(AUDIO_MIN_VOLUME);
} else { // mPipSound == TvSettings.PIP_SOUND_PIP_WINDOW
setVolumeByAudioFocusStatus(mPipView);
- mTvView.setStreamVolume(AUDIO_MIN_VOLUME);
}
mPipSwap = !mPipSwap;
mTvOptionsManager.onPipSwapChanged(mPipSwap);
@@ -2896,11 +2890,9 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
}
if (mPipSound == TvSettings.PIP_SOUND_MAIN) {
setVolumeByAudioFocusStatus(mPipView);
- mTvView.setStreamVolume(AUDIO_MIN_VOLUME);
mPipSound = TvSettings.PIP_SOUND_PIP_WINDOW;
} else { // mPipSound == TvSettings.PIP_SOUND_PIP_WINDOW
setVolumeByAudioFocusStatus(mTvView);
- mPipView.setStreamVolume(AUDIO_MIN_VOLUME);
mPipSound = TvSettings.PIP_SOUND_MAIN;
}
restoreMainTvView();
@@ -2929,9 +2921,26 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
}
stopPip();
mVisibleBehind = false;
+ if (!mOtherActivityLaunched && Build.VERSION.SDK_INT == Build.VERSION_CODES.M) {
+ // Workaround: in M, onStop is not called, even though it should be called after
+ // onVisibleBehindCanceled is called. As a workaround, we call finish().
+ finish();
+ }
super.onVisibleBehindCanceled();
}
+ @Override
+ public void startActivity(Intent intent) {
+ mOtherActivityLaunched = true;
+ super.startActivity(intent);
+ }
+
+ @Override
+ public void startActivityForResult(Intent intent, int requestCode) {
+ mOtherActivityLaunched = true;
+ super.startActivityForResult(intent, requestCode);
+ }
+
public List<TvTrackInfo> getTracks(int type) {
return mTvView.getTracks(type);
}
@@ -3015,10 +3024,8 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
case TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING:
case TvInputManager.VIDEO_UNAVAILABLE_REASON_BUFFERING:
case TvInputManager.VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY:
- return;
case TvInputManager.VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL:
- stringId = R.string.msg_channel_unavailable_weak_signal;
- break;
+ return;
case TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN:
default:
stringId = R.string.msg_channel_unavailable_unknown;
@@ -3119,4 +3126,123 @@ public class MainActivity extends Activity implements AudioManager.OnAudioFocusC
return CHANNEL_CHANGE_DELAY_MS_IN_NORMAL_SPEED;
}
}
+
+ private class MyOnTuneListener implements OnTuneListener {
+ boolean mUnlockAllowedRatingBeforeShrunken = true;
+ boolean mWasUnderShrunkenTvView;
+ long mStreamInfoUpdateTimeThresholdMs;
+ Channel mChannel;
+
+ public MyOnTuneListener() { }
+
+ private void onTune(Channel channel, boolean wasUnderShrukenTvView) {
+ mStreamInfoUpdateTimeThresholdMs =
+ System.currentTimeMillis() + FIRST_STREAM_INFO_UPDATE_DELAY_MILLIS;
+ mChannel = channel;
+ mWasUnderShrunkenTvView = wasUnderShrukenTvView;
+ }
+
+ private void onPlayRecording() {
+ mStreamInfoUpdateTimeThresholdMs =
+ System.currentTimeMillis() + FIRST_STREAM_INFO_UPDATE_DELAY_MILLIS;
+ mChannel = null;
+ mWasUnderShrunkenTvView = false;
+ }
+
+ @Override
+ public void onUnexpectedStop(Channel channel) {
+ stopTv();
+ startTv(null);
+ }
+
+ @Override
+ public void onTuneFailed(Channel channel) {
+ Log.w(TAG, "Failed to tune to channel " + channel.getId()
+ + "@" + channel.getInputId());
+ if (mTvView.isFadedOut()) {
+ mTvView.removeFadeEffect();
+ }
+ // TODO: show something to user about this error.
+ }
+
+ @Override
+ public void onStreamInfoChanged(StreamInfo info) {
+ if (info.isVideoAvailable() && mTuneDurationTimer.isRunning()) {
+ mTracker.sendChannelTuneTime(info.getCurrentChannel(),
+ mTuneDurationTimer.reset());
+ }
+ // If updateChannelBanner() is called without delay, the stream info seems flickering
+ // when the channel is quickly changed.
+ if (!mHandler.hasMessages(MSG_UPDATE_CHANNEL_BANNER_BY_INFO_UPDATE)
+ && info.isVideoAvailable()) {
+ if (System.currentTimeMillis() > mStreamInfoUpdateTimeThresholdMs) {
+ updateChannelBannerAndShowIfNeeded(
+ UPDATE_CHANNEL_BANNER_REASON_UPDATE_INFO);
+ } else {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ MSG_UPDATE_CHANNEL_BANNER_BY_INFO_UPDATE),
+ mStreamInfoUpdateTimeThresholdMs - System.currentTimeMillis());
+ }
+ }
+
+ applyDisplayRefreshRate(info.getVideoFrameRate());
+ mTvViewUiManager.updateTvView();
+ applyMultiAudio();
+ applyClosedCaption();
+ // TODO: Send command to TIS with checking the settings in TV and CaptionManager.
+ mOverlayManager.getMenu().onStreamInfoChanged();
+ if (mTvView.isVideoAvailable()) {
+ mTvViewUiManager.fadeInTvView();
+ }
+ mHandler.removeCallbacks(mRestoreMainViewRunnable);
+ restoreMainTvView();
+ }
+
+ @Override
+ public void onChannelRetuned(Uri channel) {
+ if (channel == null) {
+ return;
+ }
+ Channel currentChannel =
+ mChannelDataManager.getChannel(ContentUris.parseId(channel));
+ if (currentChannel == null) {
+ Log.e(TAG, "onChannelRetuned is called but can't find a channel with the URI "
+ + channel);
+ return;
+ }
+ if (isChannelChangeKeyDownReceived()) {
+ // Ignore this message if the user is changing the channel.
+ return;
+ }
+ mChannelTuner.setCurrentChannel(currentChannel);
+ mTvView.setCurrentChannel(currentChannel);
+ updateChannelBannerAndShowIfNeeded(UPDATE_CHANNEL_BANNER_REASON_TUNE);
+ }
+
+ @Override
+ public void onContentBlocked() {
+ mTuneDurationTimer.reset();
+ TvContentRating rating = mTvView.getBlockedContentRating();
+ // When tuneTo was called while TV view was shrunken, if the channel id is the same
+ // with the channel watched before shrunken, we allow the rating which was allowed
+ // before.
+ if (mWasUnderShrunkenTvView && mUnlockAllowedRatingBeforeShrunken
+ && mChannelBeforeShrunkenTvView.equals(mChannel)
+ && rating.equals(mAllowedRatingBeforeShrunken)) {
+ mUnlockAllowedRatingBeforeShrunken = isUnderShrunkenTvView();
+ mTvView.unblockContent(rating);
+ }
+
+ updateChannelBannerAndShowIfNeeded(UPDATE_CHANNEL_BANNER_REASON_LOCK_OR_UNLOCK);
+ mTvViewUiManager.fadeInTvView();
+ }
+
+ @Override
+ public void onContentAllowed() {
+ if (!isUnderShrunkenTvView()) {
+ mUnlockAllowedRatingBeforeShrunken = false;
+ }
+ updateChannelBannerAndShowIfNeeded(UPDATE_CHANNEL_BANNER_REASON_LOCK_OR_UNLOCK);
+ }
+ }
}
diff --git a/src/com/android/tv/MainActivityWrapper.java b/src/com/android/tv/MainActivityWrapper.java
index 94f11864..82e96d14 100644
--- a/src/com/android/tv/MainActivityWrapper.java
+++ b/src/com/android/tv/MainActivityWrapper.java
@@ -20,8 +20,8 @@ import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
+import android.util.ArraySet;
-import com.android.tv.common.CollectionUtils;
import com.android.tv.data.Channel;
import java.util.Set;
@@ -34,7 +34,7 @@ import java.util.Set;
public final class MainActivityWrapper {
private MainActivity mActivity;
- private final Set<OnCurrentChannelChangeListener> mListeners = CollectionUtils.createSmallSet();
+ private final Set<OnCurrentChannelChangeListener> mListeners = new ArraySet<>();
/**
* Returns the current main activity.
diff --git a/src/com/android/tv/SetupPassthroughActivity.java b/src/com/android/tv/SetupPassthroughActivity.java
index bdabf25b..e6373505 100644
--- a/src/com/android/tv/SetupPassthroughActivity.java
+++ b/src/com/android/tv/SetupPassthroughActivity.java
@@ -23,9 +23,9 @@ import android.media.tv.TvInputInfo;
import android.os.Bundle;
import android.util.Log;
+import com.android.tv.common.SoftPreconditions;
import com.android.tv.common.TvCommonConstants;
import com.android.tv.util.SetupUtils;
-import com.android.tv.util.SoftPreconditions;
import com.android.tv.util.TvInputManagerHelper;
/**
diff --git a/src/com/android/tv/TimeShiftManager.java b/src/com/android/tv/TimeShiftManager.java
index f96464e3..a231c29d 100644
--- a/src/com/android/tv/TimeShiftManager.java
+++ b/src/com/android/tv/TimeShiftManager.java
@@ -28,7 +28,9 @@ import android.util.Log;
import android.util.Range;
import com.android.tv.analytics.Tracker;
+import com.android.tv.common.SoftPreconditions;
import com.android.tv.common.WeakHandler;
+import com.android.tv.common.recording.RecordedProgram;
import com.android.tv.data.Channel;
import com.android.tv.data.OnCurrentProgramUpdatedListener;
import com.android.tv.data.Program;
@@ -179,7 +181,7 @@ public class TimeShiftManager {
tvView.setOnScreenBlockedListener(new TunableTvView.OnScreenBlockingChangedListener() {
@Override
public void onScreenBlockingChanged(boolean blocked) {
- onAvailabilityChanged();
+ mPlayController.onAvailabilityChanged();
}
});
}
@@ -195,7 +197,7 @@ public class TimeShiftManager {
* Checks if the trick play is available for the current channel.
*/
public boolean isAvailable() {
- return mPlayController.isAvailable();
+ return mPlayController.mAvailable;
}
/**
@@ -229,11 +231,6 @@ public class TimeShiftManager {
}
}
- public boolean isPlayForRecording() {
- // TODO: need to find better way to check if it's for recording playback.
- return mPlayController.mRecordEndTimeMs != CURRENT_TIME;
- }
-
/**
* Plays the media.
*
@@ -470,11 +467,18 @@ public class TimeShiftManager {
}
/**
+ * Checks whether the TV is playing the recorded content.
+ */
+ public boolean isRecordingPlayback() {
+ return mPlayController.mRecordingPlayback;
+ }
+
+ /**
* Returns {@code true} if the trick play is available and it's playing to the forward direction
* with normal speed, otherwise {@code false}.
*/
public boolean isNormalPlaying() {
- return mPlayController.isAvailable()
+ return mPlayController.mAvailable
&& mPlayController.mPlayStatus == PLAY_STATUS_PLAYING
&& mPlayController.mPlayDirection == PLAY_DIRECTION_FORWARD
&& mPlayController.mDisplayedPlaySpeed == PLAY_SPEED_1X;
@@ -484,7 +488,7 @@ public class TimeShiftManager {
* Checks if the trick play is available and it's playback status is paused.
*/
public boolean isPaused() {
- return mPlayController.isAvailable() && mPlayController.mPlayStatus == PLAY_STATUS_PAUSED;
+ return mPlayController.mAvailable && mPlayController.mPlayStatus == PLAY_STATUS_PAUSED;
}
/**
@@ -502,8 +506,9 @@ public class TimeShiftManager {
}
void onAvailabilityChanged() {
- mProgramManager.onAvailabilityChanged(mPlayController.isAvailable(),
- mPlayController.getCurrentChannel(), mPlayController.mRecordStartTimeMs);
+ mProgramManager.onAvailabilityChanged(mPlayController.mAvailable,
+ mPlayController.mRecordingPlayback ? null : mPlayController.getCurrentChannel(),
+ mPlayController.mRecordStartTimeMs);
updateActions();
// Availability change notification should be always sent
// even if mNotificationEnabled is false.
@@ -513,7 +518,7 @@ public class TimeShiftManager {
}
void onRecordTimeRangeChanged() {
- if (mPlayController.isAvailable()) {
+ if (mPlayController.mAvailable) {
mProgramManager.onRecordTimeRangeChanged(mPlayController.mRecordStartTimeMs,
mPlayController.mRecordEndTimeMs);
}
@@ -590,7 +595,6 @@ public class TimeShiftManager {
private class PlayController {
private final TunableTvView mTvView;
- private long mPossibleStartTimeMs;
private long mRecordStartTimeMs;
private long mRecordEndTimeMs;
@@ -598,6 +602,8 @@ public class TimeShiftManager {
@PlaySpeed private int mDisplayedPlaySpeed = PLAY_SPEED_1X;
@PlayDirection private int mPlayDirection = PLAY_DIRECTION_FORWARD;
private int mPlaybackSpeed;
+ private boolean mAvailable;
+ private boolean mRecordingPlayback;
/**
* Indicates that the trick play is not playing the current time position.
@@ -613,47 +619,11 @@ public class TimeShiftManager {
mTvView.setTimeShiftListener(new TimeShiftListener() {
@Override
public void onAvailabilityChanged() {
- // Do not send the notifications while the availability is changing,
- // because the variables are in the intermediate state.
- // For example, the current program can be null.
- mNotificationEnabled = false;
- mDisplayedPlaySpeed = PLAY_SPEED_1X;
- mPlaybackSpeed = 1;
- mPlayDirection = PLAY_DIRECTION_FORWARD;
- mIsPlayOffsetChanged = false;
- mPossibleStartTimeMs = System.currentTimeMillis();
- mRecordStartTimeMs = mPossibleStartTimeMs;
- mRecordEndTimeMs = CURRENT_TIME;
- mCurrentPositionMediator.initialize(mPossibleStartTimeMs);
- mHandler.removeMessages(MSG_GET_CURRENT_POSITION);
-
- if (isAvailable()) {
- // When the media availability message has come.
- mPlayController.setPlayStatus(PLAY_STATUS_PLAYING);
- mHandler.sendEmptyMessageDelayed(MSG_GET_CURRENT_POSITION,
- REQUEST_CURRENT_POSITION_INTERVAL);
- } else {
- // When the tune command is sent.
- mPlayController.setPlayStatus(PLAY_STATUS_PAUSED);
- }
- TimeShiftManager.this.onAvailabilityChanged();
- mNotificationEnabled = true;
+ PlayController.this.onAvailabilityChanged();
}
@Override
public void onRecordStartTimeChanged(long recordStartTimeMs) {
- if (mRecordEndTimeMs == CURRENT_TIME &&
- recordStartTimeMs < mPossibleStartTimeMs) {
- // Do not warn in this case because it can happen in normal cases.
- if (DEBUG) {
- Log.d(TAG, "Record start time is less then the time when it became "
- + "available. {availableStartTime="
- + Utils.toTimeString(mPossibleStartTimeMs)
- + ", recordStartTimeMs=" + Utils.toTimeString(recordStartTimeMs)
- + "}");
- }
- recordStartTimeMs = mPossibleStartTimeMs;
- }
if (mRecordStartTimeMs == recordStartTimeMs) {
return;
}
@@ -675,26 +645,48 @@ public class TimeShiftManager {
TimeShiftManager.this.play();
}
}
-
- @Override
- public void onRecordEndTimeChanged(long recordEndTimeMs) {
- if (mRecordEndTimeMs == recordEndTimeMs) {
- return;
- }
- mRecordEndTimeMs = recordEndTimeMs;
- TimeShiftManager.this.onRecordTimeRangeChanged();
-
- if (mPlayStatus == PLAY_STATUS_PLAYING &&
- mRecordEndTimeMs - getCurrentPositionMs()
- < RECORDING_BOUNDARY_THRESHOLD) {
- TimeShiftManager.this.pause();
- }
- }
});
}
- boolean isAvailable() {
- return mTvView.isTimeShiftAvailable() && !mTvView.isScreenBlocked();
+ void onAvailabilityChanged() {
+ boolean newAvailable = mTvView.isTimeShiftAvailable() && !mTvView.isScreenBlocked();
+ if (mAvailable == newAvailable) {
+ return;
+ }
+ mAvailable = newAvailable;
+ // Do not send the notifications while the availability is changing,
+ // because the variables are in the intermediate state.
+ // For example, the current program can be null.
+ mNotificationEnabled = false;
+ mDisplayedPlaySpeed = PLAY_SPEED_1X;
+ mPlaybackSpeed = 1;
+ mPlayDirection = PLAY_DIRECTION_FORWARD;
+ mRecordingPlayback = mTvView.isRecordingPlayback();
+ if (mRecordingPlayback) {
+ RecordedProgram recordedProgram = mTvView.getPlayingRecordedProgram();
+ SoftPreconditions.checkNotNull(recordedProgram);
+ mIsPlayOffsetChanged = true;
+ mRecordStartTimeMs = 0;
+ mRecordEndTimeMs = recordedProgram.getDurationMillis();
+ } else {
+ mIsPlayOffsetChanged = false;
+ mRecordStartTimeMs = System.currentTimeMillis();
+ mRecordEndTimeMs = CURRENT_TIME;
+ }
+ mCurrentPositionMediator.initialize(mRecordStartTimeMs);
+ mHandler.removeMessages(MSG_GET_CURRENT_POSITION);
+
+ if (mAvailable) {
+ // When the media availability message has come.
+ mPlayController.setPlayStatus(PLAY_STATUS_PLAYING);
+ mHandler.sendEmptyMessageDelayed(MSG_GET_CURRENT_POSITION,
+ REQUEST_CURRENT_POSITION_INTERVAL);
+ } else {
+ // When the tune command is sent.
+ mPlayController.setPlayStatus(PLAY_STATUS_PAUSED);
+ }
+ TimeShiftManager.this.onAvailabilityChanged();
+ mNotificationEnabled = true;
}
void handleGetCurrentPosition() {
@@ -855,18 +847,25 @@ public class TimeShiftManager {
private final List<Program> mPrograms = new ArrayList<>();
private final Queue<Range<Long>> mProgramLoadQueue = new LinkedList<>();
private LoadProgramsForCurrentChannelTask mProgramLoadTask = null;
+ private int mEmptyFetchCount = 0;
ProgramManager(ProgramDataManager programDataManager) {
mProgramDataManager = programDataManager;
}
void onAvailabilityChanged(boolean available, Channel channel, long currentPositionMs) {
+ if (DEBUG) {
+ Log.d(TAG, "onAvailabilityChanged(" + available + "+," + channel + ", "
+ + currentPositionMs + ")");
+ }
+
mProgramLoadQueue.clear();
if (mProgramLoadTask != null) {
mProgramLoadTask.cancel(true);
}
mHandler.removeMessages(MSG_PREFETCH_PROGRAM);
mPrograms.clear();
+ mEmptyFetchCount = 0;
mChannel = channel;
if (channel == null || channel.isPassthrough()) {
return;
@@ -1133,17 +1132,37 @@ public class TimeShiftManager {
}
Program lastValidProgram = getLastValidProgram();
if (DEBUG) Log.d(TAG, "Last valid program = " + lastValidProgram);
+ final long delay;
if (lastValidProgram != null) {
- long delay = lastValidProgram.getEndTimeUtcMillis()
+ delay = lastValidProgram.getEndTimeUtcMillis()
- PREFETCH_TIME_OFFSET_FROM_PROGRAM_END - System.currentTimeMillis();
- mHandler.sendEmptyMessageDelayed(MSG_PREFETCH_PROGRAM, delay);
- if (DEBUG) Log.d(TAG, "Scheduling with " + delay + "(ms) delays.");
} else {
- mHandler.sendEmptyMessage(MSG_PREFETCH_PROGRAM);
- if (DEBUG) Log.d(TAG, "Scheduling promptly.");
+ // Since there might not be any program data delay the retry 5 seconds,
+ // then 30 seconds then 5 minutes
+ switch (mEmptyFetchCount) {
+ case 0:
+ delay = 0;
+ break;
+ case 1:
+ delay = TimeUnit.SECONDS.toMillis(5);
+ break;
+ case 2:
+ delay = TimeUnit.SECONDS.toMillis(30);
+ break;
+ default:
+ delay = TimeUnit.MINUTES.toMillis(5);
+ break;
+ }
+ if (DEBUG) {
+ Log.d(TAG,
+ "No last valid program. Already tried " + mEmptyFetchCount + " times");
+ }
}
+ mHandler.sendEmptyMessageDelayed(MSG_PREFETCH_PROGRAM, delay);
+ if (DEBUG) Log.d(TAG, "Scheduling with " + delay + "(ms) delays.");
}
+ // Prefecth programs within PREFETCH_DURATION_FOR_NEXT from now.
private void prefetchPrograms() {
long startTimeMs;
Program lastValidProgram = getLastValidProgram();
@@ -1153,11 +1172,13 @@ public class TimeShiftManager {
startTimeMs = lastValidProgram.getEndTimeUtcMillis();
}
long endTimeMs = System.currentTimeMillis() + PREFETCH_DURATION_FOR_NEXT;
- if (DEBUG) {
- Log.d(TAG, "Prefetch task starts: {startTime=" + Utils.toTimeString(startTimeMs)
- + ", endTime=" + Utils.toTimeString(endTimeMs) + "}");
+ if (startTimeMs <= endTimeMs) {
+ if (DEBUG) {
+ Log.d(TAG, "Prefetch task starts: {startTime=" + Utils.toTimeString(startTimeMs)
+ + ", endTime=" + Utils.toTimeString(endTimeMs) + "}");
+ }
+ mProgramLoadQueue.add(Range.create(startTimeMs, endTimeMs));
}
- mProgramLoadQueue.add(Range.create(startTimeMs, endTimeMs));
startTaskIfNeeded();
}
@@ -1185,8 +1206,8 @@ public class TimeShiftManager {
it.remove();
}
}
- if (programs == null || programs.isEmpty() || mPrograms.isEmpty()) {
- mPrograms.addAll(programs);
+ if (programs == null || programs.isEmpty()) {
+ mEmptyFetchCount++;
if (addDummyPrograms(mPeriod)) {
TimeShiftManager.this.onProgramInfoChanged();
}
@@ -1194,19 +1215,22 @@ public class TimeShiftManager {
startNextLoadingIfNeeded();
return;
}
- removeDummyPrograms();
- removeOverlappedPrograms(programs);
- Program loadedProgram = programs.get(0);
- for (int i = 0; i < mPrograms.size() && !programs.isEmpty(); ++i) {
- Program program = mPrograms.get(i);
- while (program.getStartTimeUtcMillis() > loadedProgram
- .getStartTimeUtcMillis()) {
- mPrograms.add(i++, loadedProgram);
- programs.remove(0);
- if (programs.isEmpty()) {
- break;
+ mEmptyFetchCount = 0;
+ if(!mPrograms.isEmpty()) {
+ removeDummyPrograms();
+ removeOverlappedPrograms(programs);
+ Program loadedProgram = programs.get(0);
+ for (int i = 0; i < mPrograms.size() && !programs.isEmpty(); ++i) {
+ Program program = mPrograms.get(i);
+ while (program.getStartTimeUtcMillis() > loadedProgram
+ .getStartTimeUtcMillis()) {
+ mPrograms.add(i++, loadedProgram);
+ programs.remove(0);
+ if (programs.isEmpty()) {
+ break;
+ }
+ loadedProgram = programs.get(0);
}
- loadedProgram = programs.get(0);
}
}
mPrograms.addAll(programs);
diff --git a/src/com/android/tv/TvApplication.java b/src/com/android/tv/TvApplication.java
index 0cac4a3b..ef105c94 100644
--- a/src/com/android/tv/TvApplication.java
+++ b/src/com/android/tv/TvApplication.java
@@ -16,6 +16,7 @@
package com.android.tv;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Application;
import android.content.ComponentName;
@@ -23,14 +24,15 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.media.tv.TvContract;
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager;
import android.media.tv.TvInputManager.TvInputCallback;
+import android.os.Build;
import android.os.Bundle;
import android.os.StrictMode;
-import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-import android.support.annotation.UiThread;
+import android.support.v4.os.BuildCompat;
import android.util.Log;
import android.view.KeyEvent;
@@ -47,14 +49,17 @@ import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.ProgramDataManager;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrDataManagerImpl;
-import com.android.tv.dvr.DvrDataManagerInMemoryImpl;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.DvrRecordingService;
import com.android.tv.dvr.DvrSessionManager;
+import com.android.tv.util.Clock;
import com.android.tv.util.SetupUtils;
import com.android.tv.util.SystemProperties;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.Utils;
+import com.android.usbtuner.UsbTunerPreferences;
+import com.android.usbtuner.setup.TunerSetupActivity;
+import com.android.usbtuner.tvinput.UsbTunerTvInputService;
import java.util.List;
@@ -127,7 +132,7 @@ public class TvApplication extends Application implements ApplicationSingletons
handleInputCountChanged();
}
});
- if (CommonFeatures.DVR.isEnabled(this)) {
+ if (CommonFeatures.DVR.isEnabled(this) && BuildCompat.isAtLeastN()) {
mDvrManager = new DvrManager(this);
//NOTE: DvrRecordingService just keeps running.
DvrRecordingService.startService(this);
@@ -148,6 +153,7 @@ public class TvApplication extends Application implements ApplicationSingletons
}
@Override
+ @TargetApi(Build.VERSION_CODES.N)
public DvrSessionManager getDvrSessionManger() {
if (mDvrSessionManager == null) {
mDvrSessionManager = new DvrSessionManager(this);
@@ -199,16 +205,13 @@ public class TvApplication extends Application implements ApplicationSingletons
/**
* Returns {@link DvrDataManager}.
*/
+ @TargetApi(Build.VERSION_CODES.N)
@Override
public DvrDataManager getDvrDataManager() {
if (mDvrDataManager == null) {
- if(SystemProperties.USE_IN_MEMORY_DVR_DB.getValue()){
- mDvrDataManager = new DvrDataManagerInMemoryImpl(this);
- } else {
- DvrDataManagerImpl dvrDataManager = new DvrDataManagerImpl(this);
+ DvrDataManagerImpl dvrDataManager = new DvrDataManagerImpl(this, Clock.SYSTEM);
mDvrDataManager = dvrDataManager;
dvrDataManager.start();
- }
}
return mDvrDataManager;
}
@@ -256,7 +259,9 @@ public class TvApplication extends Application implements ApplicationSingletons
boolean hasTunerInput = false;
for (TvInputInfo input : tvInputs) {
if (input.isPassthroughInput()) {
- ++inputCount;
+ if (!input.isHidden(this)) {
+ ++inputCount;
+ }
} else if (!hasTunerInput) {
hasTunerInput = true;
++inputCount;
@@ -310,17 +315,38 @@ public class TvApplication extends Application implements ApplicationSingletons
* {@link SetupUtils}.
*/
public void handleInputCountChanged() {
+ handleInputCountChanged(false, false, false);
+ }
+
+ /**
+ * Checks the input counts and enable/disable TvActivity. Also updates the input list in
+ * {@link SetupUtils}.
+ *
+ * @param calledByTunerServiceChanged true if it is called when UsbTunerTvInputService
+ * is enabled or disabled.
+ * @param tunerServiceEnabled it's available only when calledByTunerServiceChanged is true.
+ * @param dontKillApp when TvActivity is enabled or disabled by this method, the app restarts
+ * by default. But, if dontKillApp is true, the app won't restart.
+ */
+ public void handleInputCountChanged(boolean calledByTunerServiceChanged,
+ boolean tunerServiceEnabled, boolean dontKillApp) {
TvInputManager inputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE);
- boolean enable = false;
- if (Features.UNHIDE.isEnabled(TvApplication.this)) {
- enable = true;
- } else {
+ boolean enable = (calledByTunerServiceChanged && tunerServiceEnabled)
+ || Features.UNHIDE.isEnabled(TvApplication.this);
+ if (!enable) {
List<TvInputInfo> inputs = inputManager.getTvInputList();
+ boolean skipTunerInputCheck = false;
// Enable the TvActivity only if there is at least one tuner type input.
- for (TvInputInfo input : inputs) {
- if (input.getType() == TvInputInfo.TYPE_TUNER) {
- enable = true;
- break;
+ if (!skipTunerInputCheck) {
+ for (TvInputInfo input : inputs) {
+ if (calledByTunerServiceChanged && !tunerServiceEnabled
+ && UsbTunerTvInputService.getInputId(this).equals(input.getId())) {
+ continue;
+ }
+ if (input.getType() == TvInputInfo.TYPE_TUNER) {
+ enable = true;
+ break;
+ }
}
}
if (DEBUG) Log.d(TAG, "Enable MainActivity: " + enable);
@@ -330,7 +356,8 @@ public class TvApplication extends Application implements ApplicationSingletons
int newState = enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
if (packageManager.getComponentEnabledSetting(name) != newState) {
- packageManager.setComponentEnabledSetting(name, newState, 0);
+ packageManager.setComponentEnabledSetting(name, newState,
+ dontKillApp ? PackageManager.DONT_KILL_APP : 0);
}
SetupUtils.getInstance(TvApplication.this).onInputListUpdated(inputManager);
}
diff --git a/src/com/android/tv/TvOptionsManager.java b/src/com/android/tv/TvOptionsManager.java
index 97b9d5fa..f104e75d 100644
--- a/src/com/android/tv/TvOptionsManager.java
+++ b/src/com/android/tv/TvOptionsManager.java
@@ -35,10 +35,11 @@ import java.util.Locale;
public class TvOptionsManager {
public static final int OPTION_CLOSED_CAPTIONS = 0;
public static final int OPTION_DISPLAY_MODE = 1;
- public static final int OPTION_PIP = 2;
- public static final int OPTION_MULTI_AUDIO = 3;
- public static final int OPTION_MORE_CHANNELS = 4;
- public static final int OPTION_SETTINGS = 5;
+ public static final int OPTION_IN_APP_PIP = 2;
+ public static final int OPTION_SYSTEMWIDE_PIP = 3;
+ public static final int OPTION_MULTI_AUDIO = 4;
+ public static final int OPTION_MORE_CHANNELS = 5;
+ public static final int OPTION_SETTINGS = 6;
public static final int OPTION_PIP_INPUT = 100;
public static final int OPTION_PIP_SWAP = 101;
@@ -75,7 +76,7 @@ public class TvOptionsManager {
.isDisplayModeAvailable(mDisplayMode)
? DisplayMode.getLabel(mDisplayMode, mContext)
: DisplayMode.getLabel(DisplayMode.MODE_NORMAL, mContext);
- case OPTION_PIP:
+ case OPTION_IN_APP_PIP:
return mContext.getString(
mPip ? R.string.options_item_pip_on : R.string.options_item_pip_off);
case OPTION_MULTI_AUDIO:
@@ -130,7 +131,7 @@ public class TvOptionsManager {
public void onPipChanged(boolean pip) {
mPip = pip;
- notifyOptionChanged(OPTION_PIP);
+ notifyOptionChanged(OPTION_IN_APP_PIP);
}
public void onMultiAudioChanged(String multiAudio) {
diff --git a/src/com/android/tv/analytics/SendConfigInfoRunnable.java b/src/com/android/tv/analytics/SendConfigInfoRunnable.java
index c2d5c5fb..41392a6d 100644
--- a/src/com/android/tv/analytics/SendConfigInfoRunnable.java
+++ b/src/com/android/tv/analytics/SendConfigInfoRunnable.java
@@ -26,8 +26,8 @@ import java.util.List;
* Sends ConfigurationInfo once a day.
*/
public class SendConfigInfoRunnable implements Runnable {
- private Tracker mTracker;
- private TvInputManagerHelper mTvInputManagerHelper;
+ private final Tracker mTracker;
+ private final TvInputManagerHelper mTvInputManagerHelper;
public SendConfigInfoRunnable(Tracker tracker, TvInputManagerHelper tvInputManagerHelper) {
this.mTracker = tracker;
diff --git a/src/com/android/tv/data/Channel.java b/src/com/android/tv/data/Channel.java
index ba3c59ba..86437ab2 100644
--- a/src/com/android/tv/data/Channel.java
+++ b/src/com/android/tv/data/Channel.java
@@ -33,7 +33,6 @@ import android.util.Log;
import com.android.tv.common.CollectionUtils;
import com.android.tv.common.TvCommonConstants;
-import com.android.tv.dvr.provider.DvrContract;
import com.android.tv.util.ImageLoader;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.Utils;
@@ -75,17 +74,17 @@ public final class Channel {
private static final String INVALID_PACKAGE_NAME = "packageName";
private static final String[] PROJECTION_BASE = {
- // Columns must match what is read in Channel.fromCursor()
- TvContract.Channels._ID,
- TvContract.Channels.COLUMN_PACKAGE_NAME,
- TvContract.Channels.COLUMN_INPUT_ID,
- TvContract.Channels.COLUMN_TYPE,
- TvContract.Channels.COLUMN_DISPLAY_NUMBER,
- TvContract.Channels.COLUMN_DISPLAY_NAME,
- TvContract.Channels.COLUMN_DESCRIPTION,
- TvContract.Channels.COLUMN_VIDEO_FORMAT,
- TvContract.Channels.COLUMN_BROWSABLE,
- TvContract.Channels.COLUMN_LOCKED,
+ // Columns must match what is read in Channel.fromCursor()
+ TvContract.Channels._ID,
+ TvContract.Channels.COLUMN_PACKAGE_NAME,
+ TvContract.Channels.COLUMN_INPUT_ID,
+ TvContract.Channels.COLUMN_TYPE,
+ TvContract.Channels.COLUMN_DISPLAY_NUMBER,
+ TvContract.Channels.COLUMN_DISPLAY_NAME,
+ TvContract.Channels.COLUMN_DESCRIPTION,
+ TvContract.Channels.COLUMN_VIDEO_FORMAT,
+ TvContract.Channels.COLUMN_BROWSABLE,
+ TvContract.Channels.COLUMN_LOCKED,
};
// Additional fields added in MNC.
@@ -110,15 +109,6 @@ public final class Channel {
}
/**
- * Use this projection if you want to create {@link Channel} object using
- * {@link #fromDvrCursor}.
- */
- public static final String[] PROJECTION_DVR = {
- // Columns must match what is read in Channel.fromDvrCursor()
- DvrContract.DvrChannels._ID
- };
-
- /**
* Creates {@code Channel} object from cursor.
*
* <p>The query that created the cursor MUST use {@link #PROJECTION}
@@ -264,6 +254,13 @@ public final class Channel {
}
/**
+ * Checks whether this channel is physical tuner channel or not.
+ */
+ public boolean isPhysicalTunerChannel() {
+ return !TextUtils.isEmpty(mType) && !TvContract.Channels.TYPE_OTHER.equals(mType);
+ }
+
+ /**
* Checks if two channels equal by checking ids.
*/
@Override
diff --git a/src/com/android/tv/data/ChannelDataManager.java b/src/com/android/tv/data/ChannelDataManager.java
index 82ac4b5a..84a16111 100644
--- a/src/com/android/tv/data/ChannelDataManager.java
+++ b/src/com/android/tv/data/ChannelDataManager.java
@@ -31,15 +31,15 @@ import android.os.Message;
import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
+import android.util.ArraySet;
import android.util.Log;
import android.util.MutableInt;
-import com.android.tv.common.CollectionUtils;
import com.android.tv.common.SharedPreferencesUtils;
+import com.android.tv.common.SoftPreconditions;
import com.android.tv.common.WeakHandler;
import com.android.tv.util.AsyncDbTask;
import com.android.tv.util.PermissionUtils;
-import com.android.tv.util.SoftPreconditions;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.Utils;
@@ -72,7 +72,7 @@ public class ChannelDataManager {
private QueryAllChannelsTask mChannelsUpdateTask;
private final List<Runnable> mPostRunnablesAfterChannelUpdate = new ArrayList<>();
- private final Set<Listener> mListeners = CollectionUtils.createSmallSet();
+ private final Set<Listener> mListeners = new ArraySet<>();
private final Map<Long, ChannelWrapper> mChannelWrapperMap = new HashMap<>();
private final Map<String, MutableInt> mChannelCountMap = new HashMap<>();
private final Channel.DefaultComparator mChannelComparator;
@@ -282,7 +282,7 @@ public class ChannelDataManager {
channels.add(channel);
}
}
- return Collections.unmodifiableList(channels);
+ return channels;
}
/**
@@ -508,6 +508,15 @@ public class ChannelDataManager {
mChannelsUpdateTask.executeOnDbThread();
}
+ /**
+ * Reloads channel data.
+ */
+ public void reload() {
+ if (mDbLoadFinished && !mHandler.hasMessages(MSG_UPDATE_CHANNELS)) {
+ mHandler.sendEmptyMessage(MSG_UPDATE_CHANNELS);
+ }
+ }
+
public interface Listener {
/**
* Called when data load is finished.
@@ -539,7 +548,7 @@ public class ChannelDataManager {
}
private class ChannelWrapper {
- final Set<ChannelListener> mChannelListeners = CollectionUtils.createSmallSet();
+ final Set<ChannelListener> mChannelListeners = new ArraySet<>();
final Channel mChannel;
boolean mBrowsableInDb;
boolean mLockedInDb;
diff --git a/src/com/android/tv/data/GenreItems.java b/src/com/android/tv/data/GenreItems.java
index 92e38809..b1110612 100644
--- a/src/com/android/tv/data/GenreItems.java
+++ b/src/com/android/tv/data/GenreItems.java
@@ -17,13 +17,11 @@
package com.android.tv.data;
import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
import android.content.Context;
import android.media.tv.TvContract.Programs.Genres;
import android.os.Build;
import com.android.tv.R;
-import com.android.tv.common.CollectionUtils;
public class GenreItems {
/**
@@ -31,7 +29,7 @@ public class GenreItems {
*/
public static final int ID_ALL_CHANNELS = 0;
- private static final String[] CANONICAL_GENRES_BASE = {
+ private static final String[] CANONICAL_GENRES_L = {
null, // All channels
Genres.FAMILY_KIDS,
Genres.SPORTS,
@@ -47,23 +45,34 @@ public class GenreItems {
};
@SuppressLint("InlinedApi")
- private static final String[] CANONICAL_GENRES_ADDED_IN_L_MR1 = {
- Genres.ARTS,
- Genres.ENTERTAINMENT,
- Genres.LIFE_STYLE,
- Genres.MUSIC,
- Genres.PREMIER,
- Genres.TECH_SCIENCE
+ private static final String[] CANONICAL_GENRES_L_MR1 = {
+ null, // All channels
+ Genres.FAMILY_KIDS,
+ Genres.SPORTS,
+ Genres.SHOPPING,
+ Genres.MOVIES,
+ Genres.COMEDY,
+ Genres.TRAVEL,
+ Genres.DRAMA,
+ Genres.EDUCATION,
+ Genres.ANIMAL_WILDLIFE,
+ Genres.NEWS,
+ Genres.GAMING,
+ Genres.ARTS,
+ Genres.ENTERTAINMENT,
+ Genres.LIFE_STYLE,
+ Genres.MUSIC,
+ Genres.PREMIER,
+ Genres.TECH_SCIENCE
};
private static final String[] CANONICAL_GENRES = createGenres();
private static String[] createGenres() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
- return CANONICAL_GENRES_BASE;
+ return CANONICAL_GENRES_L;
} else {
- return CollectionUtils
- .concatAll(CANONICAL_GENRES_BASE, CANONICAL_GENRES_ADDED_IN_L_MR1);
+ return CANONICAL_GENRES_L_MR1;
}
}
@@ -73,7 +82,9 @@ public class GenreItems {
* Returns array of all genre labels.
*/
public static String[] getLabels(Context context) {
- String[] items = context.getResources().getStringArray(R.array.genre_labels);
+ String[] items = Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1
+ ? context.getResources().getStringArray(R.array.genre_labels_l)
+ : context.getResources().getStringArray(R.array.genre_labels_l_mr1);
if (items.length != CANONICAL_GENRES.length) {
throw new IllegalArgumentException("Genre data mismatch");
}
diff --git a/src/com/android/tv/data/Program.java b/src/com/android/tv/data/Program.java
index b9c54aac..af5f93bb 100644
--- a/src/com/android/tv/data/Program.java
+++ b/src/com/android/tv/data/Program.java
@@ -22,13 +22,14 @@ import android.media.tv.TvContentRating;
import android.media.tv.TvContract;
import android.support.annotation.NonNull;
import android.support.annotation.UiThread;
+import android.support.v4.os.BuildCompat;
import android.text.TextUtils;
import android.util.Log;
import com.android.tv.R;
import com.android.tv.common.BuildConfig;
+import com.android.tv.common.CollectionUtils;
import com.android.tv.common.TvContentRatingCache;
-import com.android.tv.dvr.provider.DvrContract;
import com.android.tv.util.ImageLoader;
import com.android.tv.util.Utils;
@@ -43,33 +44,43 @@ public final class Program implements Comparable<Program> {
private static final boolean DEBUG_DUMP_DESCRIPTION = false;
private static final String TAG = "Program";
- public static final String[] PROJECTION = {
- // Columns must match what is read in Program.fromCursor()
- TvContract.Programs.COLUMN_CHANNEL_ID,
- TvContract.Programs.COLUMN_TITLE,
- TvContract.Programs.COLUMN_EPISODE_TITLE,
- TvContract.Programs.COLUMN_SEASON_NUMBER,
- TvContract.Programs.COLUMN_EPISODE_NUMBER,
- TvContract.Programs.COLUMN_SHORT_DESCRIPTION,
- TvContract.Programs.COLUMN_POSTER_ART_URI,
- TvContract.Programs.COLUMN_THUMBNAIL_URI,
- TvContract.Programs.COLUMN_CANONICAL_GENRE,
- TvContract.Programs.COLUMN_CONTENT_RATING,
- TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS,
- TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS,
- TvContract.Programs.COLUMN_VIDEO_WIDTH,
- TvContract.Programs.COLUMN_VIDEO_HEIGHT
+ private static final String[] PROJECTION_BASE = {
+ // Columns must match what is read in Program.fromCursor()
+ TvContract.Programs._ID,
+ TvContract.Programs.COLUMN_CHANNEL_ID,
+ TvContract.Programs.COLUMN_TITLE,
+ TvContract.Programs.COLUMN_EPISODE_TITLE,
+ TvContract.Programs.COLUMN_SHORT_DESCRIPTION,
+ TvContract.Programs.COLUMN_POSTER_ART_URI,
+ TvContract.Programs.COLUMN_THUMBNAIL_URI,
+ TvContract.Programs.COLUMN_CANONICAL_GENRE,
+ TvContract.Programs.COLUMN_CONTENT_RATING,
+ TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS,
+ TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS,
+ TvContract.Programs.COLUMN_VIDEO_WIDTH,
+ TvContract.Programs.COLUMN_VIDEO_HEIGHT
};
- /**
- * Use this projection if you want to create {@link Program} object using
- * {@link #fromDvrCursor}.
- */
- public static final String[] PROJECTION_DVR = {
- // Columns must match what is read in Channel.fromDvrCursor()
- DvrContract.DvrPrograms._ID
+ // Columns which is deprecated in NYC
+ private static final String[] PROJECTION_DEPRECATED_IN_NYC = {
+ TvContract.Programs.COLUMN_SEASON_NUMBER,
+ TvContract.Programs.COLUMN_EPISODE_NUMBER
+ };
+
+ private static final String[] PROJECTION_ADDED_IN_NYC = {
+ TvContract.Programs.COLUMN_SEASON_DISPLAY_NUMBER,
+ TvContract.Programs.COLUMN_SEASON_TITLE,
+ TvContract.Programs.COLUMN_EPISODE_DISPLAY_NUMBER
};
+ public static final String[] PROJECTION = createProjection();
+
+ private static String[] createProjection() {
+ return CollectionUtils
+ .concatAll(PROJECTION_BASE, BuildCompat.isAtLeastN() ? PROJECTION_ADDED_IN_NYC
+ : PROJECTION_DEPRECATED_IN_NYC);
+ }
+
/**
* Creates {@code Program} object from cursor.
*
@@ -79,39 +90,38 @@ public final class Program implements Comparable<Program> {
// Columns read must match the order of match {@link #PROJECTION}
Builder builder = new Builder();
int index = 0;
+ builder.setId(cursor.getLong(index++));
builder.setChannelId(cursor.getLong(index++));
builder.setTitle(cursor.getString(index++));
builder.setEpisodeTitle(cursor.getString(index++));
- builder.setSeasonNumber(cursor.getInt(index++));
- builder.setEpisodeNumber(cursor.getInt(index++));
builder.setDescription(cursor.getString(index++));
builder.setPosterArtUri(cursor.getString(index++));
builder.setThumbnailUri(cursor.getString(index++));
builder.setCanonicalGenres(cursor.getString(index++));
- builder.setContentRatings(TvContentRatingCache.getInstance()
- .getRatings(cursor.getString(index++)));
+ builder.setContentRatings(
+ TvContentRatingCache.getInstance().getRatings(cursor.getString(index++)));
builder.setStartTimeUtcMillis(cursor.getLong(index++));
builder.setEndTimeUtcMillis(cursor.getLong(index++));
builder.setVideoWidth((int) cursor.getLong(index++));
builder.setVideoHeight((int) cursor.getLong(index++));
+ if (BuildCompat.isAtLeastN()) {
+ builder.setSeasonNumber(cursor.getString(index++));
+ builder.setSeasonTitle(cursor.getString(index++));
+ builder.setEpisodeNumber(cursor.getString(index++));
+ } else {
+ builder.setSeasonNumber(cursor.getString(index++));
+ builder.setEpisodeNumber(cursor.getString(index++));
+ }
return builder.build();
}
- /**
- * Creates a {@link Program} object from the DVR database.
- */
- public static Program fromDvrCursor(Cursor c) {
- Program program = new Program();
- int index = -1;
- program.mDvrId = c.getLong(++index);
- return program;
- }
-
+ private long mId;
private long mChannelId;
private String mTitle;
private String mEpisodeTitle;
- private int mSeasonNumber;
- private int mEpisodeNumber;
+ private String mSeasonNumber;
+ private String mSeasonTitle;
+ private String mEpisodeNumber;
private long mStartTimeUtcMillis;
private long mEndTimeUtcMillis;
private String mDescription;
@@ -122,8 +132,6 @@ public final class Program implements Comparable<Program> {
private int[] mCanonicalGenreIds;
private TvContentRating[] mContentRatings;
- private long mDvrId;
-
/**
* TODO(DVR): Need to fill the following data.
*/
@@ -134,6 +142,10 @@ public final class Program implements Comparable<Program> {
// Do nothing.
}
+ public long getId() {
+ return mId;
+ }
+
public long getChannelId() {
return mChannelId;
}
@@ -161,13 +173,22 @@ public final class Program implements Comparable<Program> {
}
public String getEpisodeDisplayTitle(Context context) {
- if (mSeasonNumber > 0 && mEpisodeNumber > 0 && !TextUtils.isEmpty(mEpisodeTitle)) {
+ if (!TextUtils.isEmpty(mSeasonNumber) && !TextUtils.isEmpty(mEpisodeNumber)
+ && !TextUtils.isEmpty(mEpisodeTitle)) {
return String.format(context.getResources().getString(R.string.episode_format),
mSeasonNumber, mEpisodeNumber, mEpisodeTitle);
}
return mEpisodeTitle;
}
+ public String getSeasonNumber() {
+ return mSeasonNumber;
+ }
+
+ public String getEpisodeNumber() {
+ return mEpisodeNumber;
+ }
+
public long getStartTimeUtcMillis() {
return mStartTimeUtcMillis;
}
@@ -239,19 +260,12 @@ public final class Program implements Comparable<Program> {
return false;
}
- /**
- * Returns an ID in DVR database.
- */
- public long getDvrId() {
- return mDvrId;
- }
-
@Override
public int hashCode() {
return Objects.hash(mChannelId, mStartTimeUtcMillis, mEndTimeUtcMillis,
mTitle, mEpisodeTitle, mDescription, mVideoWidth, mVideoHeight,
mPosterArtUri, mThumbnailUri, Arrays.hashCode(mContentRatings),
- Arrays.hashCode(mCanonicalGenreIds), mSeasonNumber, mEpisodeNumber);
+ Arrays.hashCode(mCanonicalGenreIds), mSeasonNumber, mSeasonTitle, mEpisodeNumber);
}
@Override
@@ -272,8 +286,9 @@ public final class Program implements Comparable<Program> {
&& Objects.equals(mThumbnailUri, program.mThumbnailUri)
&& Arrays.equals(mContentRatings, program.mContentRatings)
&& Arrays.equals(mCanonicalGenreIds, program.mCanonicalGenreIds)
- && mSeasonNumber == program.mSeasonNumber
- && mEpisodeNumber == program.mEpisodeNumber;
+ && Objects.equals(mSeasonNumber, program.mSeasonNumber)
+ && Objects.equals(mSeasonTitle, program.mSeasonTitle)
+ && Objects.equals(mEpisodeNumber, program.mEpisodeNumber);
}
@Override
@@ -284,11 +299,12 @@ public final class Program implements Comparable<Program> {
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
- builder.append("Program{")
+ builder.append("Program[" + mId + "]{")
.append("channelId=").append(mChannelId)
.append(", title=").append(mTitle)
.append(", episodeTitle=").append(mEpisodeTitle)
.append(", seasonNumber=").append(mSeasonNumber)
+ .append(", seasonTitle=").append(mSeasonTitle)
.append(", episodeNumber=").append(mEpisodeNumber)
.append(", startTimeUtcSec=").append(Utils.toTimeString(mStartTimeUtcMillis))
.append(", endTimeUtcSec=").append(Utils.toTimeString(mEndTimeUtcMillis))
@@ -310,10 +326,12 @@ public final class Program implements Comparable<Program> {
return;
}
+ mId = other.mId;
mChannelId = other.mChannelId;
mTitle = other.mTitle;
mEpisodeTitle = other.mEpisodeTitle;
mSeasonNumber = other.mSeasonNumber;
+ mSeasonTitle = other.mSeasonTitle;
mEpisodeNumber = other.mEpisodeNumber;
mStartTimeUtcMillis = other.mStartTimeUtcMillis;
mEndTimeUtcMillis = other.mEndTimeUtcMillis;
@@ -328,17 +346,19 @@ public final class Program implements Comparable<Program> {
public static final class Builder {
private final Program mProgram;
+ private long mId;
public Builder() {
mProgram = new Program();
// Fill initial data.
mProgram.mChannelId = Channel.INVALID_ID;
- mProgram.mTitle = "title";
- mProgram.mSeasonNumber = -1;
- mProgram.mEpisodeNumber = -1;
+ mProgram.mTitle = null;
+ mProgram.mSeasonNumber = null;
+ mProgram.mSeasonTitle = null;
+ mProgram.mEpisodeNumber = null;
mProgram.mStartTimeUtcMillis = -1;
mProgram.mEndTimeUtcMillis = -1;
- mProgram.mDescription = "description";
+ mProgram.mDescription = null;
}
public Builder(Program other) {
@@ -346,6 +366,11 @@ public final class Program implements Comparable<Program> {
mProgram.copyFrom(other);
}
+ public Builder setId(long id) {
+ mProgram.mId = id;
+ return this;
+ }
+
public Builder setChannelId(long channelId) {
mProgram.mChannelId = channelId;
return this;
@@ -361,12 +386,17 @@ public final class Program implements Comparable<Program> {
return this;
}
- public Builder setSeasonNumber(int seasonNumber) {
+ public Builder setSeasonNumber(String seasonNumber) {
mProgram.mSeasonNumber = seasonNumber;
return this;
}
- public Builder setEpisodeNumber(int episodeNumber) {
+ public Builder setSeasonTitle(String seasonTitle) {
+ mProgram.mSeasonTitle = seasonTitle;
+ return this;
+ }
+
+ public Builder setEpisodeNumber(String episodeNumber) {
mProgram.mEpisodeNumber = episodeNumber;
return this;
}
@@ -473,11 +503,10 @@ public final class Program implements Comparable<Program> {
boolean isDuplicate = p1.getChannelId() == p2.getChannelId()
&& p1.getStartTimeUtcMillis() == p2.getStartTimeUtcMillis()
&& p1.getEndTimeUtcMillis() == p2.getEndTimeUtcMillis();
- if (BuildConfig.ENG && isDuplicate) {
+ if (DEBUG && BuildConfig.ENG && isDuplicate) {
Log.w(TAG, "Duplicate programs detected! - \"" + p1.getTitle() + "\" and \""
+ p2.getTitle() + "\"");
}
return isDuplicate;
}
-
}
diff --git a/src/com/android/tv/data/ProgramDataManager.java b/src/com/android/tv/data/ProgramDataManager.java
index 6c167238..88db91b9 100644
--- a/src/com/android/tv/data/ProgramDataManager.java
+++ b/src/com/android/tv/data/ProgramDataManager.java
@@ -28,16 +28,17 @@ import android.os.Looper;
import android.os.Message;
import android.support.annotation.MainThread;
import android.support.annotation.VisibleForTesting;
+import android.util.ArraySet;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.LruCache;
-import com.android.tv.common.CollectionUtils;
import com.android.tv.common.MemoryManageable;
+import com.android.tv.common.SoftPreconditions;
+import com.android.tv.data.epg.EpgFetcher;
import com.android.tv.util.AsyncDbTask;
import com.android.tv.util.Clock;
import com.android.tv.util.MultiLongSparseArray;
-import com.android.tv.util.SoftPreconditions;
import com.android.tv.util.Utils;
import java.util.ArrayList;
@@ -90,7 +91,7 @@ public class ProgramDataManager implements MemoryManageable {
private final MultiLongSparseArray<OnCurrentProgramUpdatedListener>
mChannelId2ProgramUpdatedListeners = new MultiLongSparseArray<>();
private final Handler mHandler;
- private final Set<Listener> mListeners = CollectionUtils.createSmallSet();
+ private final Set<Listener> mListeners = new ArraySet<>();
private final ContentObserver mProgramObserver;
@@ -108,8 +109,12 @@ public class ProgramDataManager implements MemoryManageable {
private boolean mPauseProgramUpdate = false;
private final LruCache<Long, Program> mZeroLengthProgramCache = new LruCache<>(10);
+ // TODO: Change to final.
+ private EpgFetcher mEpgFetcher;
+
public ProgramDataManager(Context context) {
this(context.getContentResolver(), Clock.SYSTEM, Looper.myLooper());
+ mEpgFetcher = new EpgFetcher(context);
}
@VisibleForTesting
@@ -128,8 +133,8 @@ public class ProgramDataManager implements MemoryManageable {
}
if (mPrefetchEnabled) {
// The delay time of an existing MSG_UPDATE_PREFETCH_PROGRAM could be quite long
- // up to PROGRAM_GUIDE_SNAP_TIME_MS. So we need to remove the existing message and
- // send MSG_UPDATE_PREFETCH_PROGRAM again.
+ // up to PROGRAM_GUIDE_SNAP_TIME_MS. So we need to remove the existing message
+ // and send MSG_UPDATE_PREFETCH_PROGRAM again.
mHandler.removeMessages(MSG_UPDATE_PREFETCH_PROGRAM);
mHandler.sendEmptyMessage(MSG_UPDATE_PREFETCH_PROGRAM);
}
@@ -169,6 +174,9 @@ public class ProgramDataManager implements MemoryManageable {
}
mContentResolver.registerContentObserver(Programs.CONTENT_URI,
true, mProgramObserver);
+ if (mEpgFetcher != null) {
+ mEpgFetcher.start();
+ }
}
/**
@@ -182,6 +190,9 @@ public class ProgramDataManager implements MemoryManageable {
}
mStarted = false;
+ if (mEpgFetcher != null) {
+ mEpgFetcher.stop();
+ }
mContentResolver.unregisterContentObserver(mProgramObserver);
mHandler.removeCallbacksAndMessages(null);
@@ -201,6 +212,18 @@ public class ProgramDataManager implements MemoryManageable {
}
/**
+ * Reloads program data.
+ */
+ public void reload() {
+ if (!mHandler.hasMessages(MSG_UPDATE_CURRENT_PROGRAMS)) {
+ mHandler.sendEmptyMessage(MSG_UPDATE_CURRENT_PROGRAMS);
+ }
+ if (mPrefetchEnabled && !mHandler.hasMessages(MSG_UPDATE_PREFETCH_PROGRAM)) {
+ mHandler.sendEmptyMessage(MSG_UPDATE_PREFETCH_PROGRAM);
+ }
+ }
+
+ /**
* A listener interface to receive notification on program data retrieval from DB.
*/
public interface Listener {
@@ -601,6 +624,22 @@ public class ProgramDataManager implements MemoryManageable {
}
}
+ /**
+ * Gets an single {@link Program} from {@link TvContract.Programs#CONTENT_URI}.
+ */
+ public static class QueryProgramTask extends AsyncDbTask.AsyncQueryItemTask<Program> {
+
+ public QueryProgramTask(ContentResolver contentResolver, long programId) {
+ super(contentResolver, TvContract.buildProgramUri(programId), Program.PROJECTION, null,
+ null, null);
+ }
+
+ @Override
+ protected Program fromCursor(Cursor c) {
+ return Program.fromCursor(c);
+ }
+ }
+
private class MyHandler extends Handler {
public MyHandler(Looper looper) {
super(looper);
diff --git a/src/com/android/tv/data/StreamInfo.java b/src/com/android/tv/data/StreamInfo.java
index 04f8258a..df842737 100644
--- a/src/com/android/tv/data/StreamInfo.java
+++ b/src/com/android/tv/data/StreamInfo.java
@@ -30,6 +30,7 @@ public interface StreamInfo {
int getVideoWidth();
int getVideoHeight();
float getVideoFrameRate();
+ float getVideoDisplayAspectRatio();
int getVideoDefinitionLevel();
int getAudioChannelCount();
boolean hasClosedCaption();
diff --git a/src/com/android/tv/data/WatchedHistoryManager.java b/src/com/android/tv/data/WatchedHistoryManager.java
index cff8cd5c..fc6672d2 100644
--- a/src/com/android/tv/data/WatchedHistoryManager.java
+++ b/src/com/android/tv/data/WatchedHistoryManager.java
@@ -3,10 +3,12 @@ package com.android.tv.data;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import android.util.Log;
@@ -42,8 +44,8 @@ public class WatchedHistoryManager {
private boolean mStarted;
private boolean mLoaded;
private SharedPreferences mSharedPreferences;
- private SharedPreferences.OnSharedPreferenceChangeListener mOnSharedPreferenceChangeListener =
- new SharedPreferences.OnSharedPreferenceChangeListener() {
+ private final OnSharedPreferenceChangeListener mOnSharedPreferenceChangeListener =
+ new OnSharedPreferenceChangeListener() {
@Override
@MainThread
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
@@ -80,7 +82,7 @@ public class WatchedHistoryManager {
private final Context mContext;
private Listener mListener;
private final int mMaxHistorySize;
- private Handler mHandler;
+ private final Handler mHandler;
public WatchedHistoryManager(Context context) {
this(context, MAX_HISTORY_SIZE);
@@ -197,6 +199,7 @@ public class WatchedHistoryManager {
* Returns watched history in the ascending order of time. In other words, the first element
* is the oldest and the last element is the latest record.
*/
+ @NonNull
public List<WatchedRecord> getWatchedHistory() {
return Collections.unmodifiableList(mWatchedHistory);
}
diff --git a/src/com/android/tv/data/epg/EpgFetcher.java b/src/com/android/tv/data/epg/EpgFetcher.java
new file mode 100644
index 00000000..9ff527d8
--- /dev/null
+++ b/src/com/android/tv/data/epg/EpgFetcher.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.data.epg;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.OperationApplicationException;
+import android.database.Cursor;
+import android.media.tv.TvContract;
+import android.media.tv.TvContract.Programs;
+import android.media.tv.TvInputInfo;
+import android.media.tv.TvInputManager.TvInputCallback;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.preference.PreferenceManager;
+import android.support.annotation.NonNull;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.tv.Features;
+import com.android.tv.TvApplication;
+import com.android.tv.common.WeakHandler;
+import com.android.tv.data.Channel;
+import com.android.tv.data.Program;
+import com.android.tv.util.RecurringRunner;
+import com.android.tv.util.TvInputManagerHelper;
+import com.android.tv.util.Utils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An utility class to fetch the EPG. This class isn't thread-safe.
+ */
+public class EpgFetcher {
+ private static final String TAG = "EpgFetcher";
+ private static final boolean DEBUG = false;
+
+ private static final int MSG_FETCH_EPG = 1;
+
+ private static final long EPG_PREFETCH_RECURRING_PERIOD_MS = TimeUnit.HOURS.toMillis(4);
+ private static final long EPG_READER_INIT_WAIT_MS = TimeUnit.MINUTES.toMillis(1);
+ private static final long PROGRAM_QUERY_DURATION = TimeUnit.DAYS.toMillis(30);
+
+ private static final int BATCH_OPERATION_COUNT = 100;
+
+ // Value: Long
+ private static final String KEY_LAST_UPDATED_EPG_TIMESTAMP =
+ "com.android.tv.data.epg.EpgFetcher.LastUpdatedEpgTimestamp";
+
+ private final Context mContext;
+ private final TvInputManagerHelper mInputHelper;
+ private final TvInputCallback mInputCallback;
+ private HandlerThread mHandlerThread;
+ private EpgFetcherHandler mHandler;
+ private RecurringRunner mRecurringRunner;
+
+ private long mLastEpgTimestamp = -1;
+
+ public EpgFetcher(Context context) {
+ mContext = context;
+ mInputHelper = TvApplication.getSingletons(mContext).getTvInputManagerHelper();
+ mInputCallback = new TvInputCallback() {
+ @Override
+ public void onInputAdded(String inputId) {
+ if (Utils.isInternalTvInput(mContext, inputId)) {
+ mHandler.removeMessages(MSG_FETCH_EPG);
+ mHandler.sendEmptyMessage(MSG_FETCH_EPG);
+ }
+ }
+ };
+ }
+
+ /**
+ * Starts fetching EPG.
+ */
+ public void start() {
+ if (DEBUG) Log.d(TAG, "Request to start fetching EPG.");
+ if (!Features.FETCH_EPG.isEnabled(mContext)) {
+ return;
+ }
+ if (mHandlerThread == null) {
+ mHandlerThread = new HandlerThread("EpgFetcher");
+ mHandlerThread.start();
+ mHandler = new EpgFetcherHandler(mHandlerThread.getLooper(), this);
+ mInputHelper.addCallback(mInputCallback);
+ mRecurringRunner = new RecurringRunner(mContext, EPG_PREFETCH_RECURRING_PERIOD_MS,
+ new Runnable() {
+ @Override
+ public void run() {
+ mHandler.removeMessages(MSG_FETCH_EPG);
+ mHandler.sendEmptyMessage(MSG_FETCH_EPG);
+ }
+ }, null);
+ mRecurringRunner.start();
+ }
+ }
+
+ /**
+ * Stops fetching EPG.
+ */
+ public void stop() {
+ if (mHandlerThread == null) {
+ return;
+ }
+ mRecurringRunner.stop();
+ mHandler.removeCallbacksAndMessages(null);
+ mHandler = null;
+ mHandlerThread.quit();
+ mHandlerThread = null;
+ }
+
+ private void onFetchEpg() {
+ if (DEBUG) Log.d(TAG, "Start fetching EPG.");
+ // Check for the internal inputs.
+ boolean hasInternalInput = false;
+ for (TvInputInfo input : mInputHelper.getTvInputInfos(true, true)) {
+ if (Utils.isInternalTvInput(mContext, input.getId())) {
+ hasInternalInput = true;
+ break;
+ }
+ }
+ if (!hasInternalInput) {
+ if (DEBUG) Log.d(TAG, "No internal input found.");
+ return;
+ }
+ // Check if EPG reader is available.
+ EpgReader epgReader = new StubEpgReader(mContext);
+ if (!epgReader.isAvailable()) {
+ if (DEBUG) Log.d(TAG, "EPG reader is not temporarily available.");
+ mHandler.removeMessages(MSG_FETCH_EPG);
+ mHandler.sendEmptyMessageDelayed(MSG_FETCH_EPG, EPG_READER_INIT_WAIT_MS);
+ return;
+ }
+ // Check the EPG Timestamp.
+ long epgTimestamp = epgReader.getEpgTimestamp();
+ if (epgTimestamp <= getLastUpdatedEpgTimestamp()) {
+ if (DEBUG) Log.d(TAG, "No new EPG.");
+ return;
+ }
+
+ List<Channel> channels = epgReader.getChannels();
+ for (Channel channel : channels) {
+ List<Program> programs = new ArrayList<>(epgReader.getPrograms(channel.getId()));
+ Collections.sort(programs);
+ if (DEBUG) {
+ Log.d(TAG, "Fetching " + programs.size() + " programs for channel " + channel);
+ }
+ updateEpg(channel.getId(), programs);
+ }
+
+ setLastUpdatedEpgTimestamp(epgTimestamp);
+ }
+
+ private long getLastUpdatedEpgTimestamp() {
+ if (mLastEpgTimestamp < 0) {
+ mLastEpgTimestamp = PreferenceManager.getDefaultSharedPreferences(mContext).getLong(
+ KEY_LAST_UPDATED_EPG_TIMESTAMP, 0);
+ }
+ return mLastEpgTimestamp;
+ }
+
+ private void setLastUpdatedEpgTimestamp(long timestamp) {
+ mLastEpgTimestamp = timestamp;
+ PreferenceManager.getDefaultSharedPreferences(mContext).edit().putLong(
+ KEY_LAST_UPDATED_EPG_TIMESTAMP, timestamp);
+ }
+
+ private void updateEpg(long channelId, List<Program> newPrograms) {
+ final int fetchedProgramsCount = newPrograms.size();
+ if (fetchedProgramsCount == 0) {
+ return;
+ }
+ long startTimeMs = System.currentTimeMillis();
+ long endTimeMs = startTimeMs + PROGRAM_QUERY_DURATION;
+ List<Program> oldPrograms = queryPrograms(mContext.getContentResolver(), channelId,
+ startTimeMs, endTimeMs);
+ Program currentOldProgram = oldPrograms.size() > 0 ? oldPrograms.get(0) : null;
+ int oldProgramsIndex = 0;
+ int newProgramsIndex = 0;
+ // Skip the past programs. They will be automatically removed by the system.
+ if (currentOldProgram != null) {
+ long oldStartTimeUtcMillis = currentOldProgram.getStartTimeUtcMillis();
+ for (Program program : newPrograms) {
+ if (program.getEndTimeUtcMillis() > oldStartTimeUtcMillis) {
+ break;
+ }
+ newProgramsIndex++;
+ }
+ }
+ // Compare the new programs with old programs one by one and update/delete the old one
+ // or insert new program if there is no matching program in the database.
+ ArrayList<ContentProviderOperation> ops = new ArrayList<>();
+ while (newProgramsIndex < fetchedProgramsCount) {
+ // TODO: Extract to method and make test.
+ Program oldProgram = oldProgramsIndex < oldPrograms.size()
+ ? oldPrograms.get(oldProgramsIndex) : null;
+ Program newProgram = newPrograms.get(newProgramsIndex);
+ boolean addNewProgram = false;
+ if (oldProgram != null) {
+ if (oldProgram.equals(newProgram)) {
+ // Exact match. No need to update. Move on to the next programs.
+ oldProgramsIndex++;
+ newProgramsIndex++;
+ } else if (isSameTitleAndOverlap(oldProgram, newProgram)) {
+ if (!oldProgram.equals(oldProgram)) {
+ // Partial match. Update the old program with the new one.
+ // NOTE: Use 'update' in this case instead of 'insert' and 'delete'. There
+ // could be application specific settings which belong to the old program.
+ ops.add(ContentProviderOperation.newUpdate(
+ TvContract.buildProgramUri(oldProgram.getId()))
+ .withValues(toContentValues(newProgram))
+ .build());
+ }
+ oldProgramsIndex++;
+ newProgramsIndex++;
+ } else if (oldProgram.getEndTimeUtcMillis()
+ < newProgram.getEndTimeUtcMillis()) {
+ // No match. Remove the old program first to see if the next program in
+ // {@code oldPrograms} partially matches the new program.
+ ops.add(ContentProviderOperation.newDelete(
+ TvContract.buildProgramUri(oldProgram.getId()))
+ .build());
+ oldProgramsIndex++;
+ } else {
+ // No match. The new program does not match any of the old programs. Insert
+ // it as a new program.
+ addNewProgram = true;
+ newProgramsIndex++;
+ }
+ } else {
+ // No old programs. Just insert new programs.
+ addNewProgram = true;
+ newProgramsIndex++;
+ }
+ if (addNewProgram) {
+ ops.add(ContentProviderOperation
+ .newInsert(TvContract.Programs.CONTENT_URI)
+ .withValues(toContentValues(newProgram))
+ .build());
+ }
+ // Throttle the batch operation not to cause TransactionTooLargeException.
+ if (ops.size() > BATCH_OPERATION_COUNT || newProgramsIndex >= fetchedProgramsCount) {
+ try {
+ if (DEBUG) {
+ int size = ops.size();
+ Log.d(TAG, "Running " + size + " operations for channel " + channelId);
+ for (int i = 0; i < size; ++i) {
+ Log.d(TAG, "Operation(" + i + "): " + ops.get(i));
+ }
+ }
+ mContext.getContentResolver().applyBatch(TvContract.AUTHORITY, ops);
+ } catch (RemoteException | OperationApplicationException e) {
+ Log.e(TAG, "Failed to insert programs.", e);
+ return;
+ }
+ ops.clear();
+ }
+ }
+ if (DEBUG) {
+ Log.d(TAG, "Fetched " + fetchedProgramsCount + " programs for channel " + channelId);
+ }
+ }
+
+ private List<Program> queryPrograms(ContentResolver contentResolver, long channelId,
+ long startTimeMs, long endTimeMs) {
+ try (Cursor c = mContext.getContentResolver().query(
+ TvContract.buildProgramsUriForChannel(channelId, startTimeMs, endTimeMs),
+ Program.PROJECTION, null, null, Programs.COLUMN_START_TIME_UTC_MILLIS)) {
+ if (c == null) {
+ return Collections.EMPTY_LIST;
+ }
+ ArrayList<Program> programs = new ArrayList<>();
+ while (c.moveToNext()) {
+ programs.add(Program.fromCursor(c));
+ }
+ return programs;
+ }
+ }
+
+ /**
+ * Returns {@code true} if the {@code oldProgram} program needs to be updated with the
+ * {@code newProgram} program.
+ */
+ private boolean isSameTitleAndOverlap(Program oldProgram, Program newProgram) {
+ // NOTE: Here, we update the old program if it has the same title and overlaps with the
+ // new program. The test logic is just an example and you can modify this. E.g. check
+ // whether the both programs have the same program ID if your EPG supports any ID for
+ // the programs.
+ return Objects.equals(oldProgram.getTitle(), newProgram.getTitle())
+ && oldProgram.getStartTimeUtcMillis() <= newProgram.getEndTimeUtcMillis()
+ && newProgram.getStartTimeUtcMillis() <= oldProgram.getEndTimeUtcMillis();
+ }
+
+ private static ContentValues toContentValues(Program program) {
+ ContentValues values = new ContentValues();
+ values.put(TvContract.Programs.COLUMN_CHANNEL_ID, program.getChannelId());
+ putValue(values, TvContract.Programs.COLUMN_TITLE, program.getTitle());
+ putValue(values, TvContract.Programs.COLUMN_EPISODE_TITLE, program.getEpisodeTitle());
+ putValue(values, TvContract.Programs.COLUMN_SEASON_NUMBER, program.getSeasonNumber());
+ putValue(values, TvContract.Programs.COLUMN_EPISODE_NUMBER, program.getEpisodeNumber());
+ putValue(values, TvContract.Programs.COLUMN_SHORT_DESCRIPTION, program.getDescription());
+ putValue(values, TvContract.Programs.COLUMN_POSTER_ART_URI, program.getPosterArtUri());
+ values.put(TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS,
+ program.getStartTimeUtcMillis());
+ values.put(TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS, program.getEndTimeUtcMillis());
+ return values;
+ }
+
+ private static void putValue(ContentValues contentValues, String key, String value) {
+ if (TextUtils.isEmpty(value)) {
+ contentValues.putNull(key);
+ } else {
+ contentValues.put(key, value);
+ }
+ }
+
+ private static class EpgFetcherHandler extends WeakHandler<EpgFetcher> {
+ public EpgFetcherHandler (@NonNull Looper looper, EpgFetcher ref) {
+ super(looper, ref);
+ }
+
+ @Override
+ public void handleMessage(Message msg, @NonNull EpgFetcher epgFetcher) {
+ switch (msg.what) {
+ case MSG_FETCH_EPG:
+ epgFetcher.onFetchEpg();
+ break;
+ default:
+ super.handleMessage(msg);
+ break;
+ }
+ }
+ }
+}
diff --git a/src/com/android/tv/data/epg/EpgReader.java b/src/com/android/tv/data/epg/EpgReader.java
new file mode 100644
index 00000000..1c7712f4
--- /dev/null
+++ b/src/com/android/tv/data/epg/EpgReader.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.data.epg;
+
+import android.support.annotation.WorkerThread;
+
+import com.android.tv.data.Channel;
+import com.android.tv.data.Program;
+
+import java.util.List;
+
+/**
+ * An interface used to retrieve the EPG data. This class should be used in worker thread.
+ */
+@WorkerThread
+public interface EpgReader {
+ /**
+ * Checks if the reader is available.
+ */
+ boolean isAvailable();
+
+ /**
+ * Returns the timestamp of the current EPG.
+ * The format should be YYYYMMDDHHmmSS as a long value. ex) 20160308141500
+ */
+ long getEpgTimestamp();
+
+ /**
+ * Returns the channels list.
+ */
+ List<Channel> getChannels();
+
+ /**
+ * Returns the programs for the given channel. The result is sorted by the start time.
+ * Note that the {@code Program} doesn't have valid program ID because it's not retrieved from
+ * TvProvider.
+ */
+ List<Program> getPrograms(long channelId);
+}
diff --git a/src/com/android/tv/data/epg/StubEpgReader.java b/src/com/android/tv/data/epg/StubEpgReader.java
new file mode 100644
index 00000000..2896e8e5
--- /dev/null
+++ b/src/com/android/tv/data/epg/StubEpgReader.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.data.epg;
+
+import android.content.Context;
+
+import com.android.tv.data.Channel;
+import com.android.tv.data.Program;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A stub class to read EPG.
+ */
+public class StubEpgReader implements EpgReader{
+ public StubEpgReader(Context context) {
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public long getEpgTimestamp() {
+ return 0;
+ }
+
+ @Override
+ public List<Channel> getChannels() {
+ return Collections.EMPTY_LIST;
+ }
+
+ @Override
+ public List<Program> getPrograms(long channelId) {
+ return Collections.EMPTY_LIST;
+ }
+}
diff --git a/src/com/android/tv/dialog/FullscreenDialogFragment.java b/src/com/android/tv/dialog/FullscreenDialogFragment.java
index eb84aaf9..d16202a1 100644
--- a/src/com/android/tv/dialog/FullscreenDialogFragment.java
+++ b/src/com/android/tv/dialog/FullscreenDialogFragment.java
@@ -48,7 +48,6 @@ public class FullscreenDialogFragment extends SafeDismissDialogFragment {
return f;
}
- private int mViewLayoutResId;
private String mTrackerLabel;
private DialogView mDialogView;
@@ -58,9 +57,9 @@ public class FullscreenDialogFragment extends SafeDismissDialogFragment {
new FullscreenDialog(getActivity(), R.style.Theme_TV_dialog_Fullscreen);
LayoutInflater inflater = LayoutInflater.from(getActivity());
Bundle args = getArguments();
- mViewLayoutResId = args.getInt(VIEW_LAYOUT_ID);
mTrackerLabel = args.getString(TRACKER_LABEL);
- View v = inflater.inflate(mViewLayoutResId, null);
+ int viewLayoutResId = args.getInt(VIEW_LAYOUT_ID);
+ View v = inflater.inflate(viewLayoutResId, null);
dialog.setContentView(v);
mDialogView = (DialogView) v;
mDialogView.initialize((MainActivity) getActivity(), dialog);
diff --git a/src/com/android/tv/dvr/BaseDvrDataManager.java b/src/com/android/tv/dvr/BaseDvrDataManager.java
index a98b5fa0..0fb469be 100644
--- a/src/com/android/tv/dvr/BaseDvrDataManager.java
+++ b/src/com/android/tv/dvr/BaseDvrDataManager.java
@@ -16,68 +16,155 @@
package com.android.tv.dvr;
+import android.annotation.TargetApi;
import android.content.Context;
+import android.os.Build;
import android.support.annotation.MainThread;
+import android.util.ArraySet;
import android.util.Log;
-import com.android.tv.common.CollectionUtils;
+import com.android.tv.common.SoftPreconditions;
import com.android.tv.common.feature.CommonFeatures;
-import com.android.tv.util.SoftPreconditions;
+import com.android.tv.common.recording.RecordedProgram;
+import com.android.tv.util.Clock;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Set;
/**
* Base implementation of @{link DataManagerInternal}.
*/
@MainThread
+@TargetApi(Build.VERSION_CODES.N)
public abstract class BaseDvrDataManager implements WritableDvrDataManager {
private final static String TAG = "BaseDvrDataManager";
private final static boolean DEBUG = false;
+ protected final Clock mClock;
- private final Set<DvrDataManager.Listener> mListeners = CollectionUtils.createSmallSet();
+ private final Set<ScheduledRecordingListener> mScheduledRecordingListeners = new ArraySet<>();
+ private final Set<RecordedProgramListener> mRecordedProgramListeners = new ArraySet<>();
- BaseDvrDataManager (Context context){
+ BaseDvrDataManager(Context context, Clock clock) {
SoftPreconditions.checkFeatureEnabled(context, CommonFeatures.DVR, TAG);
+ mClock = clock;
}
@Override
- public final void addListener(DvrDataManager.Listener listener) {
- mListeners.add(listener);
+ public final void addScheduledRecordingListener(ScheduledRecordingListener listener) {
+ mScheduledRecordingListeners.add(listener);
}
@Override
- public final void removeListener(DvrDataManager.Listener listener) {
- mListeners.remove(listener);
+ public final void removeScheduledRecordingListener(ScheduledRecordingListener listener) {
+ mScheduledRecordingListeners.remove(listener);
+ }
+
+ @Override
+ public final void addRecordedProgramListener(RecordedProgramListener listener) {
+ mRecordedProgramListeners.add(listener);
+ }
+
+ @Override
+ public final void removeRecordedProgramListener(RecordedProgramListener listener) {
+ mRecordedProgramListeners.remove(listener);
}
/**
- * Calls {@link DvrDataManager.Listener#onRecordingAdded(Recording)} for each current listener.
+ * Calls {@link RecordedProgramListener#onRecordedProgramAdded(RecordedProgram)}
+ * for each listener.
*/
- protected final void notifyRecordingAdded(Recording recording) {
- for (Listener l : mListeners) {
- if (DEBUG) Log.d(TAG, "notify " + l + "added recording " + recording);
- l.onRecordingAdded(recording);
+ protected final void notifyRecordedProgramAdded(RecordedProgram recordedProgram) {
+ for (RecordedProgramListener l : mRecordedProgramListeners) {
+ if (DEBUG) Log.d(TAG, "notify " + l + "added " + recordedProgram);
+ l.onRecordedProgramAdded(recordedProgram);
}
}
/**
- * Calls {@link DvrDataManager.Listener#onRecordingRemoved(Recording)} for each current listener.
+ * Calls {@link RecordedProgramListener#onRecordedProgramChanged(RecordedProgram)}
+ * for each listener.
*/
- protected final void notifyRecordingRemoved(Recording recording) {
- for (Listener l : mListeners) {
- if (DEBUG) Log.d(TAG, "notify " + l + "removed recording " + recording);
- l.onRecordingRemoved(recording);
+ protected final void notifyRecordedProgramChanged(RecordedProgram recordedProgram) {
+ for (RecordedProgramListener l : mRecordedProgramListeners) {
+ if (DEBUG) Log.d(TAG, "notify " + l + "changed " + recordedProgram);
+ l.onRecordedProgramChanged(recordedProgram);
}
}
/**
- * Calls {@link DvrDataManager.Listener#onRecordingStatusChanged(Recording)} for each current
- * listener.
+ * Calls {@link RecordedProgramListener#onRecordedProgramRemoved(RecordedProgram)}
+ * for each listener.
*/
- protected final void notifyRecordingStatusChanged(Recording recording) {
- for (Listener l : mListeners) {
- if (DEBUG) Log.d(TAG, "notify " + l + "changed recording " + recording);
- l.onRecordingStatusChanged(recording);
+ protected final void notifyRecordedProgramRemoved(RecordedProgram recordedProgram) {
+ for (RecordedProgramListener l : mRecordedProgramListeners) {
+ if (DEBUG) Log.d(TAG, "notify " + l + "removed " + recordedProgram);
+ l.onRecordedProgramRemoved(recordedProgram);
}
}
+
+ /**
+ * Calls {@link ScheduledRecordingListener#onScheduledRecordingAdded(ScheduledRecording)}
+ * for each listener.
+ */
+ protected final void notifyScheduledRecordingAdded(ScheduledRecording scheduledRecording) {
+ for (ScheduledRecordingListener l : mScheduledRecordingListeners) {
+ if (DEBUG) Log.d(TAG, "notify " + l + "added " + scheduledRecording);
+ l.onScheduledRecordingAdded(scheduledRecording);
+ }
+ }
+
+ /**
+ * Calls {@link ScheduledRecordingListener#onScheduledRecordingRemoved(ScheduledRecording)}
+ * for each listener.
+ */
+ protected final void notifyScheduledRecordingRemoved(ScheduledRecording scheduledRecording) {
+ for (ScheduledRecordingListener l : mScheduledRecordingListeners) {
+ if (DEBUG) {
+ Log.d(TAG, "notify " + l + "removed " + scheduledRecording);
+ }
+ l.onScheduledRecordingRemoved(scheduledRecording);
+ }
+ }
+
+ /**
+ * Calls
+ * {@link ScheduledRecordingListener#onScheduledRecordingStatusChanged(ScheduledRecording)}
+ * for each listener.
+ */
+ protected final void notifyScheduledRecordingStatusChanged(
+ ScheduledRecording scheduledRecording) {
+ for (ScheduledRecordingListener l : mScheduledRecordingListeners) {
+ if (DEBUG) Log.d(TAG, "notify " + l + "changed " + scheduledRecording);
+ l.onScheduledRecordingStatusChanged(scheduledRecording);
+ }
+ }
+
+ /**
+ * Returns a new list with only {@link ScheduledRecording} with a {@link
+ * ScheduledRecording#getEndTimeMs() endTime} after now.
+ */
+ private List<ScheduledRecording> filterEndTimeIsPast(List<ScheduledRecording> originals) {
+ List<ScheduledRecording> results = new ArrayList<>(originals.size());
+ for (ScheduledRecording r : originals) {
+ if (r.getEndTimeMs() > mClock.currentTimeMillis()) {
+ results.add(r);
+ }
+ }
+ return results;
+ }
+
+ @Override
+ public List<ScheduledRecording> getStartedRecordings() {
+ return filterEndTimeIsPast(
+ getRecordingsWithState(ScheduledRecording.STATE_RECORDING_IN_PROGRESS));
+ }
+
+ @Override
+ public List<ScheduledRecording> getNonStartedScheduledRecordings() {
+ return filterEndTimeIsPast(
+ getRecordingsWithState(ScheduledRecording.STATE_RECORDING_NOT_STARTED));
+ }
+
+ protected abstract List<ScheduledRecording> getRecordingsWithState(int state);
}
diff --git a/src/com/android/tv/dvr/DvrDataManager.java b/src/com/android/tv/dvr/DvrDataManager.java
index 4f8b0525..c96104e5 100644
--- a/src/com/android/tv/dvr/DvrDataManager.java
+++ b/src/com/android/tv/dvr/DvrDataManager.java
@@ -20,6 +20,8 @@ import android.support.annotation.MainThread;
import android.support.annotation.Nullable;
import android.util.Range;
+import com.android.tv.common.recording.RecordedProgram;
+
import java.util.List;
/**
@@ -32,24 +34,24 @@ public interface DvrDataManager {
boolean isInitialized();
/**
- * Returns recordings.
+ * Returns past recordings.
*/
- List<Recording> getRecordings();
+ List<RecordedProgram> getRecordedPrograms();
/**
- * Returns past recordings.
+ * Returns all {@link ScheduledRecording} regardless of state.
*/
- List<Recording> getFinishedRecordings();
+ List<ScheduledRecording> getAllScheduledRecordings();
/**
- * Returns started recordings.
+ * Returns started recordings that expired.
*/
- List<Recording> getStartedRecordings();
+ List<ScheduledRecording> getStartedRecordings();
/**
- * Returns scheduled recordings
+ * Returns scheduled but not started recordings that have not expired.
*/
- List<Recording> getScheduledRecordings();
+ List<ScheduledRecording> getNonStartedScheduledRecordings();
/**
* Returns season recordings.
@@ -73,27 +75,60 @@ public interface DvrDataManager {
*
* @param period a time period in milliseconds.
*/
- List<Recording> getRecordingsThatOverlapWith(Range<Long> period);
+ List<ScheduledRecording> getRecordingsThatOverlapWith(Range<Long> period);
+
+ /**
+ * Add a {@link ScheduledRecordingListener}.
+ */
+ void addScheduledRecordingListener(ScheduledRecordingListener scheduledRecordingListener);
+
+ /**
+ * Remove a {@link ScheduledRecordingListener}.
+ */
+ void removeScheduledRecordingListener(ScheduledRecordingListener scheduledRecordingListener);
+
+ /**
+ * Add a {@link RecordedProgramListener}.
+ */
+ void addRecordedProgramListener(RecordedProgramListener listener);
/**
- * Add a {@link Listener}.
+ * Remove a {@link RecordedProgramListener}.
*/
- void addListener(Listener listener);
+ void removeRecordedProgramListener(RecordedProgramListener listener);
/**
- * Remove a {@link Listener}.
+ * Returns the scheduled recording program with the given recordingId or null if is not found.
*/
- void removeListener(Listener listener);
+ @Nullable
+ ScheduledRecording getScheduledRecording(long recordingId);
+
+
+ /**
+ * Returns the scheduled recording program with the given programId or null if is not found.
+ */
+ @Nullable
+ ScheduledRecording getScheduledRecordingForProgramId(long programId);
/**
- * Returns the recording with the given recordingId or null if is not found
+ * Returns the recorded program with the given recordingId or null if is not found.
*/
@Nullable
- Recording getRecording(long recordingId);
+ RecordedProgram getRecordedProgram(long recordingId);
+
+ interface ScheduledRecordingListener {
+ void onScheduledRecordingAdded(ScheduledRecording scheduledRecording);
+
+ void onScheduledRecordingRemoved(ScheduledRecording scheduledRecording);
+
+ void onScheduledRecordingStatusChanged(ScheduledRecording scheduledRecording);
+ }
+
+ interface RecordedProgramListener {
+ void onRecordedProgramAdded(RecordedProgram recordedProgram);
+
+ void onRecordedProgramChanged(RecordedProgram recordedProgram);
- interface Listener {
- void onRecordingAdded(Recording recording);
- void onRecordingRemoved(Recording recording);
- void onRecordingStatusChanged(Recording recording);
+ void onRecordedProgramRemoved(RecordedProgram recordedProgram);
}
}
diff --git a/src/com/android/tv/dvr/DvrDataManagerImpl.java b/src/com/android/tv/dvr/DvrDataManagerImpl.java
index 647d9bd7..02c47750 100644
--- a/src/com/android/tv/dvr/DvrDataManagerImpl.java
+++ b/src/com/android/tv/dvr/DvrDataManagerImpl.java
@@ -16,95 +16,174 @@
package com.android.tv.dvr;
+import android.annotation.TargetApi;
+import android.content.ContentResolver;
+import android.content.ContentUris;
import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.media.tv.TvContract;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
import android.support.annotation.MainThread;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Range;
-import com.android.tv.dvr.Recording.RecordingState;
+import com.android.tv.common.SoftPreconditions;
+import com.android.tv.common.recording.RecordedProgram;
+import com.android.tv.dvr.ScheduledRecording.RecordingState;
import com.android.tv.dvr.provider.AsyncDvrDbTask;
import com.android.tv.dvr.provider.AsyncDvrDbTask.AsyncDvrQueryTask;
-import com.android.tv.util.SoftPreconditions;
+import com.android.tv.util.AsyncDbTask;
+import com.android.tv.util.Clock;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
+import java.util.Set;
/**
* DVR Data manager to handle recordings and schedules.
*/
@MainThread
+@TargetApi(Build.VERSION_CODES.N)
public class DvrDataManagerImpl extends BaseDvrDataManager {
private static final String TAG = "DvrDataManagerImpl";
+ private static final boolean DEBUG = false;
- private Context mContext;
- private boolean mLoadFinished;
- private final HashMap<Long, Recording> mRecordings = new HashMap<>();
- private AsyncDvrQueryTask mQueryTask;
+ private final HashMap<Long, ScheduledRecording> mScheduledRecordings = new HashMap<>();
+ private final HashMap<Long, ScheduledRecording> mProgramId2ScheduledRecordings =
+ new HashMap<>();
+ private final HashMap<Long, RecordedProgram> mRecordedPrograms = new HashMap<>();
- public DvrDataManagerImpl(Context context) {
- super(context);
+ private final Context mContext;
+ private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
+ private final ContentObserver mContentObserver = new ContentObserver(mMainThreadHandler) {
+
+ @Override
+ public void onChange(boolean selfChange) {
+ onChange(selfChange, null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, @Nullable final Uri uri) {
+ if (uri == null) {
+ // TODO reload everything.
+ }
+ AsyncRecordedProgramQueryTask task = new AsyncRecordedProgramQueryTask(
+ mContext.getContentResolver(), uri);
+ task.executeOnDbThread();
+ mPendingTasks.add(task);
+ }
+ };
+
+ private void onObservedChange(Uri uri, RecordedProgram recordedProgram) {
+ long id = ContentUris.parseId(uri);
+ if (DEBUG) {
+ Log.d(TAG, "changed recorded program #" + id + " to " + recordedProgram);
+ }
+ if (recordedProgram == null) {
+ RecordedProgram old = mRecordedPrograms.remove(id);
+ if (old != null) {
+ notifyRecordedProgramRemoved(old);
+ } else {
+ Log.w(TAG, "Could not find old version of deleted program #" + id);
+ }
+ } else {
+ RecordedProgram old = mRecordedPrograms.put(id, recordedProgram);
+ if (old == null) {
+ notifyRecordedProgramAdded(recordedProgram);
+ } else {
+ notifyRecordedProgramChanged(recordedProgram);
+ }
+ }
+ }
+
+ private boolean mDvrLoadFinished;
+ private boolean mRecordedProgramLoadFinished;
+ private final Set<AsyncTask> mPendingTasks = new ArraySet<>();
+
+ public DvrDataManagerImpl(Context context, Clock clock) {
+ super(context, clock);
mContext = context;
}
public void start() {
- mQueryTask = new AsyncDvrQueryTask(mContext) {
+ AsyncDvrQueryTask mDvrQueryTask = new AsyncDvrQueryTask(mContext) {
+
@Override
- protected void onPostExecute(List<Recording> result) {
- mQueryTask = null;
- mLoadFinished = true;
- for (Recording r : result) {
- mRecordings.put(r.getId(), r);
+ protected void onCancelled(List<ScheduledRecording> scheduledRecordings) {
+ mPendingTasks.remove(this);
+ }
+
+ @Override
+ protected void onPostExecute(List<ScheduledRecording> result) {
+ mPendingTasks.remove(this);
+ mDvrLoadFinished = true;
+ for (ScheduledRecording r : result) {
+ mScheduledRecordings.put(r.getId(), r);
}
}
};
- mQueryTask.executeOnDbThread();
+ mDvrQueryTask.executeOnDbThread();
+ mPendingTasks.add(mDvrQueryTask);
+ AsyncRecordedProgramsQueryTask mRecordedProgramQueryTask =
+ new AsyncRecordedProgramsQueryTask(mContext.getContentResolver());
+ mRecordedProgramQueryTask.executeOnDbThread();
+ ContentResolver cr = mContext.getContentResolver();
+ cr.registerContentObserver(TvContract.RecordedPrograms.CONTENT_URI, true, mContentObserver);
}
public void stop() {
- if (mQueryTask != null) {
- mQueryTask.cancel(true);
- mQueryTask = null;
+ ContentResolver cr = mContext.getContentResolver();
+ cr.unregisterContentObserver(mContentObserver);
+ Iterator<AsyncTask> i = mPendingTasks.iterator();
+ while (i.hasNext()) {
+ AsyncTask task = i.next();
+ i.remove();
+ task.cancel(true);
}
}
@Override
public boolean isInitialized() {
- return mLoadFinished;
+ return mDvrLoadFinished && mRecordedProgramLoadFinished;
}
- @Override
- public List<Recording> getRecordings() {
- if (!mLoadFinished) {
+ private List<ScheduledRecording> getScheduledRecordingsPrograms() {
+ if (!mDvrLoadFinished) {
return Collections.emptyList();
}
- ArrayList<Recording> list = new ArrayList<>(mRecordings.size());
- list.addAll(mRecordings.values());
- Collections.sort(list, Recording.START_TIME_COMPARATOR);
- return Collections.unmodifiableList(list);
- }
-
- @Override
- public List<Recording> getFinishedRecordings() {
- return getRecordingsWithState(Recording.STATE_RECORDING_FINISHED);
+ ArrayList<ScheduledRecording> list = new ArrayList<>(mScheduledRecordings.size());
+ list.addAll(mScheduledRecordings.values());
+ Collections.sort(list, ScheduledRecording.START_TIME_COMPARATOR);
+ return list;
}
@Override
- public List<Recording> getStartedRecordings() {
- return getRecordingsWithState(Recording.STATE_RECORDING_IN_PROGRESS);
+ public List<RecordedProgram> getRecordedPrograms() {
+ if (!mRecordedProgramLoadFinished) {
+ return Collections.emptyList();
+ }
+ return new ArrayList<>(mRecordedPrograms.values());
}
@Override
- public List<Recording> getScheduledRecordings() {
- return getRecordingsWithState(Recording.STATE_RECORDING_NOT_STARTED);
+ public List<ScheduledRecording> getAllScheduledRecordings() {
+ return new ArrayList<>(mScheduledRecordings.values());
}
- private List<Recording> getRecordingsWithState(@RecordingState int state) {
- List<Recording> result = new ArrayList<>();
- for (Recording r : mRecordings.values()) {
+ protected List<ScheduledRecording> getRecordingsWithState(@RecordingState int state) {
+ List<ScheduledRecording> result = new ArrayList<>();
+ for (ScheduledRecording r : mScheduledRecordings.values()) {
if (r.getState() == state) {
result.add(r);
}
@@ -120,29 +199,29 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
@Override
public long getNextScheduledStartTimeAfter(long startTime) {
- return getNextStartTimeAfter(getRecordings(), startTime);
+ return getNextStartTimeAfter(getScheduledRecordingsPrograms(), startTime);
}
@VisibleForTesting
- static long getNextStartTimeAfter(List<Recording> recordings, long startTime) {
+ static long getNextStartTimeAfter(List<ScheduledRecording> scheduledRecordings, long startTime) {
int start = 0;
- int end = recordings.size() - 1;
+ int end = scheduledRecordings.size() - 1;
while (start <= end) {
int mid = (start + end) / 2;
- if (recordings.get(mid).getStartTimeMs() <= startTime) {
+ if (scheduledRecordings.get(mid).getStartTimeMs() <= startTime) {
start = mid + 1;
} else {
end = mid - 1;
}
}
- return start < recordings.size() ? recordings.get(start).getStartTimeMs()
+ return start < scheduledRecordings.size() ? scheduledRecordings.get(start).getStartTimeMs()
: NEXT_START_TIME_NOT_FOUND;
}
@Override
- public List<Recording> getRecordingsThatOverlapWith(Range<Long> period) {
- List<Recording> result = new ArrayList<>();
- for (Recording r : mRecordings.values()) {
+ public List<ScheduledRecording> getRecordingsThatOverlapWith(Range<Long> period) {
+ List<ScheduledRecording> result = new ArrayList<>();
+ for (ScheduledRecording r : mScheduledRecordings.values()) {
if (r.isOverLapping(period)) {
result.add(r);
}
@@ -152,38 +231,56 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
@Nullable
@Override
- public Recording getRecording(long recordingId) {
- if (mLoadFinished) {
- return mRecordings.get(recordingId);
+ public ScheduledRecording getScheduledRecording(long recordingId) {
+ if (mDvrLoadFinished) {
+ return mScheduledRecordings.get(recordingId);
+ }
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public ScheduledRecording getScheduledRecordingForProgramId(long programId) {
+ if (mDvrLoadFinished) {
+ return mProgramId2ScheduledRecordings.get(programId);
}
return null;
}
+ @Nullable
+ @Override
+ public RecordedProgram getRecordedProgram(long recordingId) {
+ return mRecordedPrograms.get(recordingId);
+ }
+
@Override
- public void addRecording(final Recording recording) {
+ public void addScheduledRecording(final ScheduledRecording scheduledRecording) {
new AsyncDvrDbTask.AsyncAddRecordingTask(mContext) {
@Override
- protected void onPostExecute(List<Recording> recordings) {
- super.onPostExecute(recordings);
- SoftPreconditions.checkArgument(recordings.size() == 1);
- for (Recording r : recordings) {
+ protected void onPostExecute(List<ScheduledRecording> scheduledRecordings) {
+ super.onPostExecute(scheduledRecordings);
+ SoftPreconditions.checkArgument(scheduledRecordings.size() == 1);
+ for (ScheduledRecording r : scheduledRecordings) {
if (r.getId() != -1) {
- mRecordings.put(r.getId(), r);
- notifyRecordingAdded(r);
+ mScheduledRecordings.put(r.getId(), r);
+ if (r.getProgramId() != ScheduledRecording.ID_NOT_SET) {
+ mProgramId2ScheduledRecordings.put(r.getProgramId(), r);
+ }
+ notifyScheduledRecordingAdded(r);
} else {
Log.w(TAG, "Error adding " + r);
}
}
}
- }.executeOnDbThread(recording);
+ }.executeOnDbThread(scheduledRecording);
}
@Override
public void addSeasonRecording(SeasonRecording seasonRecording) { }
@Override
- public void removeRecording(final Recording recording) {
+ public void removeScheduledRecording(final ScheduledRecording scheduledRecording) {
new AsyncDvrDbTask.AsyncDeleteRecordingTask(mContext) {
@Override
protected void onPostExecute(List<Integer> counts) {
@@ -191,23 +288,27 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
SoftPreconditions.checkArgument(counts.size() == 1);
for (Integer c : counts) {
if (c == 1) {
- mRecordings.remove(recording.getId());
+ mScheduledRecordings.remove(scheduledRecording.getId());
+ if (scheduledRecording.getProgramId() != ScheduledRecording.ID_NOT_SET) {
+ mProgramId2ScheduledRecordings
+ .remove(scheduledRecording.getProgramId());
+ }
//TODO change to notifyRecordingUpdated
- notifyRecordingRemoved(recording);
+ notifyScheduledRecordingRemoved(scheduledRecording);
} else {
- Log.w(TAG, "Error removing " + recording);
+ Log.w(TAG, "Error removing " + scheduledRecording);
}
}
}
- }.executeOnDbThread(recording);
+ }.executeOnDbThread(scheduledRecording);
}
@Override
public void removeSeasonSchedule(SeasonRecording seasonSchedule) { }
@Override
- public void updateRecording(final Recording recording) {
+ public void updateScheduledRecording(final ScheduledRecording scheduledRecording) {
new AsyncDvrDbTask.AsyncUpdateRecordingTask(mContext) {
@Override
protected void onPostExecute(List<Integer> counts) {
@@ -215,15 +316,88 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
SoftPreconditions.checkArgument(counts.size() == 1);
for (Integer c : counts) {
if (c == 1) {
- mRecordings.put(recording.getId(), recording);
+ ScheduledRecording oldScheduledRecording = mScheduledRecordings
+ .put(scheduledRecording.getId(), scheduledRecording);
+ long programId = scheduledRecording.getProgramId();
+ if (oldScheduledRecording != null
+ && oldScheduledRecording.getProgramId() != programId
+ && oldScheduledRecording.getProgramId()
+ != ScheduledRecording.ID_NOT_SET) {
+ ScheduledRecording oldValueForProgramId = mProgramId2ScheduledRecordings
+ .get(oldScheduledRecording.getProgramId());
+ if (oldValueForProgramId.getId() == scheduledRecording.getId()) {
+ //Only remove the old ScheduledRecording if it has the same ID as
+ // the new one.
+ mProgramId2ScheduledRecordings
+ .remove(oldScheduledRecording.getProgramId());
+ }
+ }
+ if (programId != ScheduledRecording.ID_NOT_SET) {
+ mProgramId2ScheduledRecordings.put(programId, scheduledRecording);
+ }
//TODO change to notifyRecordingUpdated
- notifyRecordingStatusChanged(recording);
+ notifyScheduledRecordingStatusChanged(scheduledRecording);
} else {
- Log.w(TAG, "Error updating " + recording);
+ Log.w(TAG, "Error updating " + scheduledRecording);
}
}
+ }
+ }.executeOnDbThread(scheduledRecording);
+ }
+
+ private final class AsyncRecordedProgramsQueryTask
+ extends AsyncDbTask.AsyncQueryListTask<RecordedProgram> {
+ public AsyncRecordedProgramsQueryTask(ContentResolver contentResolver) {
+ super(contentResolver, TvContract.RecordedPrograms.CONTENT_URI,
+ RecordedProgram.PROJECTION, null, null, null);
+ }
+
+ @Override
+ protected RecordedProgram fromCursor(Cursor c) {
+ return RecordedProgram.fromCursor(c);
+ }
+
+ @Override
+ protected void onCancelled(List<RecordedProgram> scheduledRecordings) {
+ mPendingTasks.remove(this);
+ }
+ @Override
+ protected void onPostExecute(List<RecordedProgram> result) {
+ mPendingTasks.remove(this);
+ mRecordedProgramLoadFinished = true;
+ if (result != null) {
+ for (RecordedProgram r : result) {
+ mRecordedPrograms.put(r.getId(), r);
+ }
}
- }.executeOnDbThread(recording);
+ }
+ }
+
+ private final class AsyncRecordedProgramQueryTask
+ extends AsyncDbTask.AsyncQueryItemTask<RecordedProgram> {
+
+ private final Uri mUri;
+
+ public AsyncRecordedProgramQueryTask(ContentResolver contentResolver, Uri uri) {
+ super(contentResolver, uri, RecordedProgram.PROJECTION, null, null, null);
+ mUri = uri;
+ }
+
+ @Override
+ protected RecordedProgram fromCursor(Cursor c) {
+ return RecordedProgram.fromCursor(c);
+ }
+
+ @Override
+ protected void onCancelled(RecordedProgram recordedProgram) {
+ mPendingTasks.remove(this);
+ }
+
+ @Override
+ protected void onPostExecute(RecordedProgram recordedProgram) {
+ mPendingTasks.remove(this);
+ onObservedChange(mUri, recordedProgram);
+ }
}
}
diff --git a/src/com/android/tv/dvr/DvrDataManagerInMemoryImpl.java b/src/com/android/tv/dvr/DvrDataManagerInMemoryImpl.java
index 8a19cb29..95b342bb 100644
--- a/src/com/android/tv/dvr/DvrDataManagerInMemoryImpl.java
+++ b/src/com/android/tv/dvr/DvrDataManagerInMemoryImpl.java
@@ -23,7 +23,9 @@ import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.util.Range;
-import com.android.tv.util.SoftPreconditions;
+import com.android.tv.common.SoftPreconditions;
+import com.android.tv.common.recording.RecordedProgram;
+import com.android.tv.util.Clock;
import java.util.ArrayList;
import java.util.Collections;
@@ -40,11 +42,12 @@ import java.util.concurrent.atomic.AtomicLong;
public final class DvrDataManagerInMemoryImpl extends BaseDvrDataManager {
private final static String TAG = "DvrDataManagerInMemory";
private final AtomicLong mNextId = new AtomicLong(1);
- private final Map<Long, Recording> mRecordings = new HashMap<>();
- private List<SeasonRecording> mSeasonSchedule = new ArrayList<>();
+ private final Map<Long, ScheduledRecording> mScheduledRecordings = new HashMap<>();
+ private final Map<Long, RecordedProgram> mRecordedPrograms = new HashMap<>();
+ private final List<SeasonRecording> mSeasonSchedule = new ArrayList<>();
- public DvrDataManagerInMemoryImpl(Context context) {
- super(context);
+ public DvrDataManagerInMemoryImpl(Context context, Clock clock) {
+ super(context, clock);
}
@Override
@@ -52,27 +55,20 @@ public final class DvrDataManagerInMemoryImpl extends BaseDvrDataManager {
return true;
}
- @Override
- public List<Recording> getRecordings() {
- return new ArrayList(mRecordings.values());
- }
-
- @Override
- public List<Recording> getFinishedRecordings() {
- return getRecordingsWithState(Recording.STATE_RECORDING_FINISHED);
+ private List<ScheduledRecording> getScheduledRecordingsPrograms() {
+ return new ArrayList(mScheduledRecordings.values());
}
@Override
- public List<Recording> getStartedRecordings() {
- return getRecordingsWithState(Recording.STATE_RECORDING_IN_PROGRESS);
+ public List<RecordedProgram> getRecordedPrograms() {
+ return new ArrayList<>(mRecordedPrograms.values());
}
@Override
- public List<Recording> getScheduledRecordings() {
- return getRecordingsWithState(Recording.STATE_RECORDING_NOT_STARTED);
+ public List<ScheduledRecording> getAllScheduledRecordings() {
+ return new ArrayList<>(mScheduledRecordings.values());
}
- @Override
public List<SeasonRecording> getSeasonRecordings() {
return mSeasonSchedule;
}
@@ -80,9 +76,9 @@ public final class DvrDataManagerInMemoryImpl extends BaseDvrDataManager {
@Override
public long getNextScheduledStartTimeAfter(long startTime) {
- List<Recording> temp = getScheduledRecordings();
- Collections.sort(temp, Recording.START_TIME_COMPARATOR);
- for (Recording r : temp) {
+ List<ScheduledRecording> temp = getNonStartedScheduledRecordings();
+ Collections.sort(temp, ScheduledRecording.START_TIME_COMPARATOR);
+ for (ScheduledRecording r : temp) {
if (r.getStartTimeMs() > startTime) {
return r.getStartTimeMs();
}
@@ -91,10 +87,10 @@ public final class DvrDataManagerInMemoryImpl extends BaseDvrDataManager {
}
@Override
- public List<Recording> getRecordingsThatOverlapWith(Range<Long> period) {
- List<Recording> temp = getRecordings();
- List<Recording> result = new ArrayList<>();
- for (Recording r : temp) {
+ public List<ScheduledRecording> getRecordingsThatOverlapWith(Range<Long> period) {
+ List<ScheduledRecording> temp = getScheduledRecordingsPrograms();
+ List<ScheduledRecording> result = new ArrayList<>();
+ for (ScheduledRecording r : temp) {
if (r.isOverLapping(period)) {
result.add(r);
}
@@ -103,20 +99,56 @@ public final class DvrDataManagerInMemoryImpl extends BaseDvrDataManager {
}
/**
- * Add a new recording.
+ * Add a new scheduled recording.
*/
@Override
- public void addRecording(Recording recording) {
- addRecordingInternal(recording);
+ public void addScheduledRecording(ScheduledRecording scheduledRecording) {
+ addScheduledRecordingInternal(scheduledRecording);
+ }
+
+
+ public void addRecordedProgram(RecordedProgram recordedProgram) {
+ addRecordedProgramInternal(recordedProgram);
+ }
+
+ public void updateRecordedProgram(RecordedProgram r) {
+ long id = r.getId();
+ if (mRecordedPrograms.containsKey(id)) {
+ mRecordedPrograms.put(id, r);
+ notifyRecordedProgramChanged(r);
+ } else {
+ throw new IllegalArgumentException("Recording not found:" + r);
+ }
+ }
+
+ public void removeRecordedProgram(RecordedProgram scheduledRecording) {
+ mRecordedPrograms.remove(scheduledRecording.getId());
+ notifyRecordedProgramRemoved(scheduledRecording);
+ }
+
+
+ public ScheduledRecording addScheduledRecordingInternal(ScheduledRecording scheduledRecording) {
+ SoftPreconditions
+ .checkState(scheduledRecording.getId() == ScheduledRecording.ID_NOT_SET, TAG,
+ "expected id of " + ScheduledRecording.ID_NOT_SET + " but was "
+ + scheduledRecording);
+ scheduledRecording = ScheduledRecording.buildFrom(scheduledRecording)
+ .setId(mNextId.incrementAndGet())
+ .build();
+ mScheduledRecordings.put(scheduledRecording.getId(), scheduledRecording);
+ notifyScheduledRecordingAdded(scheduledRecording);
+ return scheduledRecording;
}
- public Recording addRecordingInternal(Recording recording) {
- SoftPreconditions.checkState(recording.getId() == Recording.ID_NOT_SET, TAG,
- "expected id of " + Recording.ID_NOT_SET + " but was " + recording);
- recording = Recording.buildFrom(recording).setId(mNextId.incrementAndGet()).build();
- mRecordings.put(recording.getId(), recording);
- notifyRecordingAdded(recording);
- return recording;
+ public RecordedProgram addRecordedProgramInternal(RecordedProgram recordedProgram) {
+ SoftPreconditions.checkState(recordedProgram.getId() == RecordedProgram.ID_NOT_SET, TAG,
+ "expected id of " + RecordedProgram.ID_NOT_SET + " but was " + recordedProgram);
+ recordedProgram = RecordedProgram.buildFrom(recordedProgram)
+ .setId(mNextId.incrementAndGet())
+ .build();
+ mRecordedPrograms.put(recordedProgram.getId(), recordedProgram);
+ notifyRecordedProgramAdded(recordedProgram);
+ return recordedProgram;
}
@Override
@@ -125,9 +157,9 @@ public final class DvrDataManagerInMemoryImpl extends BaseDvrDataManager {
}
@Override
- public void removeRecording(Recording recording) {
- mRecordings.remove(recording.getId());
- notifyRecordingRemoved(recording);
+ public void removeScheduledRecording(ScheduledRecording scheduledRecording) {
+ mScheduledRecordings.remove(scheduledRecording.getId());
+ notifyScheduledRecordingRemoved(scheduledRecording);
}
@Override
@@ -136,11 +168,11 @@ public final class DvrDataManagerInMemoryImpl extends BaseDvrDataManager {
}
@Override
- public void updateRecording(Recording r) {
+ public void updateScheduledRecording(ScheduledRecording r) {
long id = r.getId();
- if (mRecordings.containsKey(id)) {
- mRecordings.put(id, r);
- notifyRecordingStatusChanged(r);
+ if (mScheduledRecordings.containsKey(id)) {
+ mScheduledRecordings.put(id, r);
+ notifyScheduledRecordingStatusChanged(r);
} else {
throw new IllegalArgumentException("Recording not found:" + r);
}
@@ -148,14 +180,32 @@ public final class DvrDataManagerInMemoryImpl extends BaseDvrDataManager {
@Nullable
@Override
- public Recording getRecording(long id) {
- return mRecordings.get(id);
+ public ScheduledRecording getScheduledRecording(long id) {
+ return mScheduledRecordings.get(id);
}
+ @Nullable
+ @Override
+ public ScheduledRecording getScheduledRecordingForProgramId(long programId) {
+ for (ScheduledRecording r : mScheduledRecordings.values()) {
+ if (r.getProgramId() == programId) {
+ return r;
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public RecordedProgram getRecordedProgram(long recordingId) {
+ return mRecordedPrograms.get(recordingId);
+ }
+
+ @Override
@NonNull
- private List<Recording> getRecordingsWithState(int state) {
- ArrayList<Recording> result = new ArrayList<>();
- for (Recording r : mRecordings.values()) {
+ protected List<ScheduledRecording> getRecordingsWithState(int state) {
+ ArrayList<ScheduledRecording> result = new ArrayList<>();
+ for (ScheduledRecording r : mScheduledRecordings.values()) {
if(r.getState() == state){
result.add(r);
}
diff --git a/src/com/android/tv/dvr/DvrManager.java b/src/com/android/tv/dvr/DvrManager.java
index c62c564b..e3dc622e 100644
--- a/src/com/android/tv/dvr/DvrManager.java
+++ b/src/com/android/tv/dvr/DvrManager.java
@@ -16,24 +16,33 @@
package com.android.tv.dvr;
+import android.content.ContentResolver;
import android.content.Context;
+import android.media.tv.TvInputInfo;
+import android.os.Handler;
import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
+import android.support.annotation.WorkerThread;
import android.util.Log;
import android.util.Range;
+import android.widget.Toast;
import com.android.tv.ApplicationSingletons;
import com.android.tv.TvApplication;
+import com.android.tv.common.SoftPreconditions;
import com.android.tv.common.feature.CommonFeatures;
-import com.android.tv.common.recording.RecordingCapability;
+import com.android.tv.common.recording.RecordedProgram;
import com.android.tv.data.Channel;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.Program;
-import com.android.tv.util.SoftPreconditions;
+import com.android.tv.util.AsyncDbTask;
import com.android.tv.util.Utils;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
/**
* DVR manager class to add and remove recordings. UI can modify recording list through this class,
@@ -45,11 +54,15 @@ public class DvrManager {
private final WritableDvrDataManager mDataManager;
private final ChannelDataManager mChannelDataManager;
private final DvrSessionManager mDvrSessionManager;
+ // @GuardedBy("mListener")
+ private final Map<Listener, Handler> mListener = new HashMap<>();
+ private final Context mAppContext;
public DvrManager(Context context) {
SoftPreconditions.checkFeatureEnabled(context, CommonFeatures.DVR, TAG);
ApplicationSingletons appSingletons = TvApplication.getSingletons(context);
mDataManager = (WritableDvrDataManager) appSingletons.getDvrDataManager();
+ mAppContext = context.getApplicationContext();
mChannelDataManager = appSingletons.getChannelDataManager();
mDvrSessionManager = appSingletons.getDvrSessionManger();
}
@@ -59,17 +72,18 @@ public class DvrManager {
* @param program the program to record
* @param recordingsToOverride the possible empty list of recordings that will not be recorded
*/
- public void addSchedule(Program program, List<Recording> recordingsToOverride) {
+ public void addSchedule(Program program, List<ScheduledRecording> recordingsToOverride) {
Log.i(TAG,
"Adding scheduled recording of " + program + " instead of " + recordingsToOverride);
- Collections.sort(recordingsToOverride, Recording.PRIORITY_COMPARATOR);
+ Collections.sort(recordingsToOverride, ScheduledRecording.PRIORITY_COMPARATOR);
Channel c = mChannelDataManager.getChannel(program.getChannelId());
long priority = recordingsToOverride.isEmpty() ? Long.MAX_VALUE
: recordingsToOverride.get(0).getPriority() - 1;
- Recording r = Recording.builder(c, program)
+ ScheduledRecording r = ScheduledRecording.builder(program)
.setPriority(priority)
+ .setChannelId(c.getId())
.build();
- mDataManager.addRecording(r);
+ mDataManager.addScheduledRecording(r);
}
/**
@@ -79,8 +93,10 @@ public class DvrManager {
Log.i(TAG, "Adding scheduled recording of channel" + channel + " starting at " +
Utils.toTimeString(startTime) + " and ending at " + Utils.toTimeString(endTime));
//TODO: handle error cases
- Recording r = Recording.builder(channel, startTime, endTime).build();
- mDataManager.addRecording(r);
+ ScheduledRecording r = ScheduledRecording.builder(startTime, endTime)
+ .setChannelId(channel.getId())
+ .build();
+ mDataManager.addScheduledRecording(r);
}
/**
@@ -92,12 +108,45 @@ public class DvrManager {
}
/**
+ * Stops the currently recorded program
+ */
+ public void stopRecording(final ScheduledRecording recording) {
+ synchronized (mListener) {
+ for (final Entry<Listener, Handler> entry : mListener.entrySet()) {
+ entry.getValue().post(new Runnable() {
+ @Override
+ public void run() {
+ entry.getKey().onStopRecordingRequested(recording);
+ }
+ });
+ }
+ }
+ }
+
+ /**
* Removes a scheduled recording or an existing recording.
*/
- public void removeRecording(Recording recording) {
- Log.i(TAG, "Removing " + recording);
- // TODO(DVR): ask the TIS to delete the recording and respond to the result.
- mDataManager.removeRecording(recording);
+ public void removeScheduledRecording(ScheduledRecording scheduledRecording) {
+ Log.i(TAG, "Removing " + scheduledRecording);
+ mDataManager.removeScheduledRecording(scheduledRecording);
+ }
+
+ public void removeRecordedProgram(final RecordedProgram recordedProgram) {
+ // TODO(dvr): implement
+ Log.i(TAG, "To delete " + recordedProgram
+ + "\nyou should manually delete video data at"
+ + "\nadb shell rm -rf " + recordedProgram.getDataUri()
+ );
+ Toast.makeText(mAppContext, "Deleting recorded programs is not fully implemented yet",
+ Toast.LENGTH_SHORT).show();
+ new AsyncDbTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ ContentResolver resolver = mAppContext.getContentResolver();
+ resolver.delete(recordedProgram.getUri(), null, null);
+ return null;
+ }
+ }.execute();
}
/**
@@ -107,17 +156,21 @@ public class DvrManager {
* <p>Any empty list means there is no conflicts. If there is conflict the program must be
* scheduled to record with a Priority lower than the first Recording in the list returned.
*/
- public List<Recording> getScheduledRecordingsThatConflict(Program program) {
+ public List<ScheduledRecording> getScheduledRecordingsThatConflict(Program program) {
//TODO(DVR): move to scheduler.
//TODO(DVR): deal with more than one DvrInputService
- List<Recording> overLap = mDataManager.getRecordingsThatOverlapWith(getPeriod(program));
+ List<ScheduledRecording> overLap = mDataManager.getRecordingsThatOverlapWith(getPeriod(program));
if (!overLap.isEmpty()) {
// TODO(DVR): ignore shows that already won't record.
Channel channel = mChannelDataManager.getChannel(program.getChannelId());
if (channel != null) {
- RecordingCapability recordingCapability = mDvrSessionManager
- .getRecordingCapability(channel.getInputId());
- int remove = Math.max(0, recordingCapability.maxConcurrentTunedSessions - 1);
+ TvInputInfo info = mDvrSessionManager.getTvInputInfo(channel.getInputId());
+ if (info == null) {
+ Log.w(TAG,
+ "Could not find a recording TvInputInfo for " + channel.getInputId());
+ return overLap;
+ }
+ int remove = Math.max(0, info.getTunerCount() - 1);
if (remove >= overLap.size()) {
return Collections.EMPTY_LIST;
}
@@ -136,7 +189,7 @@ public class DvrManager {
* Checks whether {@code channel} can be tuned without any conflict with existing recordings
* in progress. If there is any conflict, {@code outConflictRecordings} will be filled.
*/
- public boolean canTuneTo(Channel channel, List<Recording> outConflictRecordings) {
+ public boolean canTuneTo(Channel channel, List<ScheduledRecording> outConflictScheduledRecordings) {
// TODO: implement
return true;
}
@@ -145,8 +198,29 @@ public class DvrManager {
* Returns true is the inputId supports recording.
*/
public boolean canRecord(String inputId) {
- RecordingCapability recordingCapability = mDvrSessionManager
- .getRecordingCapability(inputId);
- return recordingCapability != null && recordingCapability.maxConcurrentTunedSessions > 0;
+ TvInputInfo info = mDvrSessionManager.getTvInputInfo(inputId);
+ return info != null && info.getTunerCount() > 0;
+ }
+
+ @WorkerThread
+ void addListener(Listener listener, @NonNull Handler handler) {
+ SoftPreconditions.checkNotNull(handler);
+ synchronized (mListener) {
+ mListener.put(listener, handler);
+ }
+ }
+
+ @WorkerThread
+ void removeListener(Listener listener) {
+ synchronized (mListener) {
+ mListener.remove(listener);
+ }
+ }
+
+ /**
+ * Listener internally used inside dvr package.
+ */
+ interface Listener {
+ void onStopRecordingRequested(ScheduledRecording scheduledRecording);
}
}
diff --git a/src/com/android/tv/dvr/DvrPlayActivity.java b/src/com/android/tv/dvr/DvrPlayActivity.java
index 872e05bd..b117a7cf 100644
--- a/src/com/android/tv/dvr/DvrPlayActivity.java
+++ b/src/com/android/tv/dvr/DvrPlayActivity.java
@@ -24,7 +24,7 @@ import com.android.tv.R;
import com.android.tv.TvApplication;
/**
- * Simple Activity to play a {@link Recording}.
+ * Simple Activity to play a {@link ScheduledRecording}.
*/
public class DvrPlayActivity extends Activity {
@@ -35,11 +35,11 @@ public class DvrPlayActivity extends Activity {
DvrDataManager dvrDataManager = TvApplication.getSingletons(this).getDvrDataManager();
// TODO(DVR) handle errors.
- long recordingId = getIntent().getLongExtra(Recording.RECORDING_ID_EXTRA, 0);
- Recording recording = dvrDataManager.getRecording(recordingId);
+ long recordingId = getIntent().getLongExtra(ScheduledRecording.RECORDING_ID_EXTRA, 0);
+ ScheduledRecording scheduledRecording = dvrDataManager.getScheduledRecording(recordingId);
TextView textView = (TextView) findViewById(R.id.placeHolderText);
- if (recording != null) {
- textView.setText(recording.toString());
+ if (scheduledRecording != null) {
+ textView.setText(scheduledRecording.toString());
} else {
textView.setText(R.string.ut_result_not_found_title); // TODO(DVR) update error text
}
diff --git a/src/com/android/tv/dvr/DvrRecordingService.java b/src/com/android/tv/dvr/DvrRecordingService.java
index d0e86d50..2f3abccf 100644
--- a/src/com/android/tv/dvr/DvrRecordingService.java
+++ b/src/com/android/tv/dvr/DvrRecordingService.java
@@ -31,7 +31,8 @@ import com.android.tv.ApplicationSingletons;
import com.android.tv.TvApplication;
import com.android.tv.common.feature.CommonFeatures;
import com.android.tv.util.Clock;
-import com.android.tv.util.SoftPreconditions;
+import com.android.tv.util.RecurringRunner;
+import com.android.tv.common.SoftPreconditions;
/**
* DVR Scheduler service.
@@ -57,6 +58,8 @@ public class DvrRecordingService extends Service {
context.startService(dvrSchedulerIntent);
}
+ private final Clock mClock = Clock.SYSTEM;
+ private RecurringRunner mReaperRunner;
private WritableDvrDataManager mDataManager;
/**
@@ -86,14 +89,16 @@ public class DvrRecordingService extends Service {
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
// mScheduler may have been set for testing.
if (mScheduler == null) {
- DvrSessionManager sessionManager = singletons.getDvrSessionManger();
mHandlerThread = new HandlerThread(HANDLER_THREAD_NAME);
mHandlerThread.start();
- mScheduler = new Scheduler(mHandlerThread.getLooper(), sessionManager, mDataManager,
- this, Clock.SYSTEM,
- alarmManager);
+ mScheduler = new Scheduler(mHandlerThread.getLooper(), singletons.getDvrManager(),
+ singletons.getDvrSessionManger(), mDataManager,
+ singletons.getChannelDataManager(), this, mClock, alarmManager);
}
- mDataManager.addListener(mScheduler);
+ mDataManager.addScheduledRecordingListener(mScheduler);
+ mReaperRunner = new RecurringRunner(this, java.util.concurrent.TimeUnit.DAYS.toMillis(1),
+ new ScheduledProgramReaper(mDataManager, mClock), null);
+ mReaperRunner.start();
}
@Override
@@ -106,7 +111,8 @@ public class DvrRecordingService extends Service {
@Override
public void onDestroy() {
if (DEBUG) Log.d(TAG, "onDestroy");
- mDataManager.removeListener(mScheduler);
+ mReaperRunner.stop();
+ mDataManager.removeScheduledRecordingListener(mScheduler);
mScheduler = null;
if (mHandlerThread != null) {
mHandlerThread.quit();
diff --git a/src/com/android/tv/dvr/DvrSessionManager.java b/src/com/android/tv/dvr/DvrSessionManager.java
index 553001e2..fba05cb6 100644
--- a/src/com/android/tv/dvr/DvrSessionManager.java
+++ b/src/com/android/tv/dvr/DvrSessionManager.java
@@ -16,18 +16,21 @@
package com.android.tv.dvr;
-import android.content.ComponentName;
+import android.annotation.TargetApi;
import android.content.Context;
-import android.media.tv.TvContract;
+import android.media.tv.TvInputInfo;
+import android.media.tv.TvInputManager;
+import android.media.tv.TvRecordingClient;
+import android.os.Build;
+import android.os.Handler;
import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
import android.support.v4.util.ArrayMap;
+import android.util.Log;
+import com.android.tv.common.SoftPreconditions;
import com.android.tv.common.feature.CommonFeatures;
-import com.android.tv.common.recording.RecordingCapability;
-import com.android.tv.common.recording.TvRecording;
import com.android.tv.data.Channel;
-import com.android.tv.util.SoftPreconditions;
-import com.android.usbtuner.tvinput.UsbTunerTvInputService;
/**
* Manages Dvr Sessions.
@@ -37,57 +40,91 @@ import com.android.usbtuner.tvinput.UsbTunerTvInputService;
* <li>Manage capabilities (conflict)</li>
* </ul>
*/
-public class DvrSessionManager {
+@TargetApi(Build.VERSION_CODES.N)
+public class DvrSessionManager extends TvInputManager.TvInputCallback {
+ //consider moving all of this to TvInputManagerHelper
private final static String TAG = "DvrSessionManager";
+ private static final boolean DEBUG = false;
+
private final Context mContext;
- private TvRecording.TvRecordingClient mRecordingClient;
- private ArrayMap<String, RecordingCapability> mCapabilityMap = new ArrayMap<>();
+ private final TvInputManager mTvInputManager;
+ private final ArrayMap<String, TvInputInfo> mRecordingTvInputs = new ArrayMap<>();
public DvrSessionManager(Context context) {
+ this(context, (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE),
+ new Handler());
+ }
+
+ @VisibleForTesting
+ DvrSessionManager(Context context, TvInputManager tvInputManager, Handler handler) {
SoftPreconditions.checkFeatureEnabled(context, CommonFeatures.DVR, TAG);
+ mTvInputManager = tvInputManager;
mContext = context.getApplicationContext();
- // TODO(DVR): get a session to all clients, for now just get USB a TestInput
- final String inputId = TvContract
- .buildInputId(new ComponentName(context, UsbTunerTvInputService.class));
- mRecordingClient = acquireDvrSession(inputId, null);
- mRecordingClient.connect(inputId, new TvRecording.ClientCallback() {
- @Override
- public void onCapabilityReceived(RecordingCapability capability) {
- mCapabilityMap.put(inputId, capability);
- mRecordingClient.release();
- mRecordingClient = null;
+ for (TvInputInfo info : tvInputManager.getTvInputList()) {
+ if (DEBUG) {
+ Log.d(TAG, info + " canRecord=" + info.canRecord() + " tunerCount=" + info
+ .getTunerCount());
+ }
+ if (info.canRecord()) {
+ mRecordingTvInputs.put(info.getId(), info);
}
- });
- if (CommonFeatures.DVR.isEnabled(context)) { // STOPSHIP(DVR)
- String testInputId = "com.android.tv.testinput/.TestTvInputService";
- mCapabilityMap.put(testInputId,
- RecordingCapability.builder()
- .setInputId(testInputId)
- .setMaxConcurrentPlayingSessions(2)
- .setMaxConcurrentTunedSessions(2)
- .setMaxConcurrentSessionsOfAllTypes(3)
- .build());
-
}
+ tvInputManager.registerCallback(this, handler);
+
}
- public TvRecording.TvRecordingClient acquireDvrSession(String inputId, Channel channel) {
- // TODO(DVR): use input and channel or change API
- TvRecording.TvRecordingClient sessionClient = new TvRecording.TvRecordingClient(mContext);
- return sessionClient;
+ public TvRecordingClient createTvRecordingClient(String tag,
+ TvRecordingClient.RecordingCallback callback, Handler handler) {
+ return new TvRecordingClient(mContext, tag, callback, handler);
}
public boolean canAcquireDvrSession(String inputId, Channel channel) {
- // TODO(DVR): implement
- return true;
+ // TODO(DVR): implement checking tuner count etc.
+ TvInputInfo info = mRecordingTvInputs.get(inputId);
+ return info != null;
+ }
+
+ public void releaseTvRecordingClient(TvRecordingClient recordingClient) {
+ recordingClient.release();
}
- public void releaseDvrSession(TvRecording.TvRecordingClient session) {
- session.release();
+ @Override
+ public void onInputAdded(String inputId) {
+ super.onInputAdded(inputId);
+ TvInputInfo info = mTvInputManager.getTvInputInfo(inputId);
+ if (DEBUG) {
+ Log.d(TAG, "onInputAdded " + info.toString() + " canRecord=" + info.canRecord()
+ + " tunerCount=" + info.getTunerCount());
+ }
+ if (info.canRecord()) {
+ mRecordingTvInputs.put(inputId, info);
+ }
+ }
+
+ @Override
+ public void onInputRemoved(String inputId) {
+ super.onInputRemoved(inputId);
+ if (DEBUG) Log.d(TAG, "onInputRemoved " + inputId);
+ mRecordingTvInputs.remove(inputId);
+ }
+
+ @Override
+ public void onInputUpdated(String inputId) {
+ super.onInputUpdated(inputId);
+ TvInputInfo info = mTvInputManager.getTvInputInfo(inputId);
+ if (DEBUG) {
+ Log.d(TAG, "onInputUpdated " + info.toString() + " canRecord=" + info.canRecord()
+ + " tunerCount=" + info.getTunerCount());
+ }
+ if (info.canRecord()) {
+ mRecordingTvInputs.put(inputId, info);
+ } else {
+ mRecordingTvInputs.remove(inputId);
+ }
}
@Nullable
- public RecordingCapability getRecordingCapability(String inputId) {
- return mCapabilityMap.get(inputId);
+ public TvInputInfo getTvInputInfo(String inputId) {
+ return mRecordingTvInputs.get(inputId);
}
}
diff --git a/src/com/android/tv/dvr/RecordingTask.java b/src/com/android/tv/dvr/RecordingTask.java
index 3bed5e77..804485b3 100644
--- a/src/com/android/tv/dvr/RecordingTask.java
+++ b/src/com/android/tv/dvr/RecordingTask.java
@@ -16,6 +16,8 @@
package com.android.tv.dvr;
+import android.media.tv.TvContract;
+import android.media.tv.TvRecordingClient;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
@@ -24,10 +26,9 @@ import android.support.annotation.VisibleForTesting;
import android.support.annotation.WorkerThread;
import android.util.Log;
-import com.android.tv.common.recording.TvRecording;
+import com.android.tv.common.SoftPreconditions;
import com.android.tv.data.Channel;
import com.android.tv.util.Clock;
-import com.android.tv.util.SoftPreconditions;
import com.android.tv.util.Utils;
import java.util.concurrent.TimeUnit;
@@ -39,9 +40,10 @@ import java.util.concurrent.TimeUnit;
* There is only one looper so messages must be handled quickly or start a separate thread.
*/
@WorkerThread
-class RecordingTask extends TvRecording.ClientCallback implements Handler.Callback {
+class RecordingTask extends TvRecordingClient.RecordingCallback
+ implements Handler.Callback, DvrManager.Listener {
private static final String TAG = "RecordingTask";
- private static final boolean DEBUG = true; //STOPSHIP(DVR)
+ private static final boolean DEBUG = false;
@VisibleForTesting
static final int MESSAGE_INIT = 1;
@@ -51,11 +53,10 @@ class RecordingTask extends TvRecording.ClientCallback implements Handler.Callba
static final int MESSAGE_STOP_RECORDING = 3;
@VisibleForTesting
- static long MS_BEFORE_START = TimeUnit.SECONDS.toMillis(5);
+ static final long MS_BEFORE_START = TimeUnit.SECONDS.toMillis(5);
@VisibleForTesting
- static long MS_AFTER_END = TimeUnit.SECONDS.toMillis(5);
+ static final long MS_AFTER_END = TimeUnit.SECONDS.toMillis(5);
- //STOPSHIP(DVR) don't use enums.
@VisibleForTesting
enum State {
NOT_STARTED,
@@ -64,27 +65,33 @@ class RecordingTask extends TvRecording.ClientCallback implements Handler.Callba
CONNECTED,
RECORDING_START_REQUESTED,
RECORDING_STARTED,
+ RECORDING_STOP_REQUESTED,
ERROR,
RELEASED,
}
private final DvrSessionManager mSessionManager;
+ private final DvrManager mDvrManager;
private final WritableDvrDataManager mDataManager;
private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
- private TvRecording.TvRecordingClient mSession;
+ private TvRecordingClient mTvRecordingClient;
private Handler mHandler;
- private Recording mRecording;
+ private ScheduledRecording mScheduledRecording;
+ private final Channel mChannel;
private State mState = State.NOT_STARTED;
private final Clock mClock;
- RecordingTask(Recording recording, DvrSessionManager sessionManager,
+ RecordingTask(ScheduledRecording scheduledRecording, Channel channel,
+ DvrManager dvrManager, DvrSessionManager sessionManager,
WritableDvrDataManager dataManager, Clock clock) {
- mRecording = recording;
+ mScheduledRecording = scheduledRecording;
+ mChannel = channel;
mSessionManager = sessionManager;
mDataManager = dataManager;
mClock = clock;
+ mDvrManager = dvrManager;
- if (DEBUG) Log.d(TAG, "created recording task " + mRecording);
+ if (DEBUG) Log.d(TAG, "created recording task " + mScheduledRecording);
}
public void setHandler(Handler handler) {
@@ -118,60 +125,45 @@ class RecordingTask extends TvRecording.ClientCallback implements Handler.Callba
}
return true;
} catch (Exception e) {
- Log.w(TAG, "Error processing message " + msg + " for " + mRecording, e);
+ Log.w(TAG, "Error processing message " + msg + " for " + mScheduledRecording, e);
failAndQuit();
}
return false;
}
@Override
- public void onConnected() {
- if (DEBUG) Log.d(TAG, "onConnected");
- super.onConnected();
+ public void onTuned(Uri channelUri) {
+ if (DEBUG) {
+ Log.d(TAG, "onTuned");
+ }
+ super.onTuned(channelUri);
mState = State.CONNECTED;
+ if (mHandler == null || !sendEmptyMessageAtAbsoluteTime(MESSAGE_START_RECORDING,
+ mScheduledRecording.getStartTimeMs() - MS_BEFORE_START)) {
+ mState = State.ERROR;
+ return;
+ }
}
- @Override
- public void onDisconnected() {
- if (DEBUG) Log.d(TAG, "onDisconnected");
- super.onDisconnected();
- //Do nothing
- }
-
- @Override
- public void onRecordDeleted(Uri mediaUri) {
- if (DEBUG) Log.d(TAG, "onRecordDeleted " + mediaUri);
- super.onRecordDeleted(mediaUri);
- SoftPreconditions.checkState(false, TAG, "unexpected onRecordDeleted");
-
- }
-
- @Override
- public void onRecordDeleteFailed(Uri mediaUri, int reason) {
- if (DEBUG) Log.d(TAG, "onRecordDeleteFailed " + mediaUri + ", " + reason);
- super.onRecordDeleteFailed(mediaUri, reason);
- SoftPreconditions.checkState(false, TAG, "unexpected onRecordDeleteFailed");
- }
@Override
- public void onRecordStarted(Uri mediaUri) {
- if (DEBUG) Log.d(TAG, "onRecordStarted " + mediaUri);
- super.onRecordStarted(mediaUri);
- mState = State.RECORDING_STARTED;
- updateRecording(Recording.buildFrom(mRecording)
- .setState(Recording.STATE_RECORDING_IN_PROGRESS)
- .build());
+ public void onRecordingStopped(Uri recordedProgramUri) {
+ super.onRecordingStopped(recordedProgramUri);
+ mState = State.CONNECTED;
+ updateRecording(ScheduledRecording.buildFrom(mScheduledRecording)
+ .setState(ScheduledRecording.STATE_RECORDING_FINISHED).build());
+ sendRemove();
}
@Override
- public void onRecordStopped(Uri mediaUri, @TvRecording.RecordStopReason int reason) {
- if (DEBUG) Log.d(TAG, "onRecordStopped " + mediaUri + " reason " + reason);
- super.onRecordStopped(mediaUri, reason);
+ public void onError(int reason) {
+ if (DEBUG) Log.d(TAG, "onError reason " + reason);
+ super.onError(reason);
// TODO(dvr) handle success
switch (reason) {
default:
- updateRecording(Recording.buildFrom(mRecording)
- .setState(Recording.STATE_RECORDING_FAILED)
+ updateRecording(ScheduledRecording.buildFrom(mScheduledRecording)
+ .setState(ScheduledRecording.STATE_RECORDING_FAILED)
.build());
}
release();
@@ -179,67 +171,78 @@ class RecordingTask extends TvRecording.ClientCallback implements Handler.Callba
}
private void handleInit() {
+ if (DEBUG) Log.d(TAG, "handleInit " + mScheduledRecording);
//TODO check recording preconditions
- Channel channel = mRecording.getChannel();
- if (channel == null) {
- Log.w(TAG, "Null channel for " + mRecording);
+
+ if (mScheduledRecording.getEndTimeMs() < mClock.currentTimeMillis()) {
+ Log.w(TAG, "End time already past, not recording " + mScheduledRecording);
failAndQuit();
return;
}
- String inputId = channel.getInputId();
- if (mSessionManager.canAcquireDvrSession(inputId, channel)) {
- mSession = mSessionManager.acquireDvrSession(inputId, channel);
- mState = State.SESSION_ACQUIRED;
- } else {
- Log.w(TAG, "Unable to acquire a session for " + mRecording);
+ if (mChannel == null) {
+ Log.w(TAG, "Null channel for " + mScheduledRecording);
+ failAndQuit();
+ return;
+ }
+ if (mChannel.getId() != mScheduledRecording.getChannelId()) {
+ Log.w(TAG, "Channel" + mChannel + " does not match scheduled recording "
+ + mScheduledRecording);
failAndQuit();
return;
}
- mSession.connect(inputId, this);
- mState = State.CONNECTION_PENDING;
-
- if (mHandler == null || !sendEmptyMessageAtAbsoluteTime(MESSAGE_START_RECORDING,
- mRecording.getStartTimeMs() - MS_BEFORE_START)) {
- mState = State.ERROR;
+ String inputId = mChannel.getInputId();
+ if (mSessionManager.canAcquireDvrSession(inputId, mChannel)) {
+ mTvRecordingClient = mSessionManager
+ .createTvRecordingClient("recordingTask-" + mScheduledRecording.getId(), this,
+ mHandler);
+ mState = State.SESSION_ACQUIRED;
+ } else {
+ Log.w(TAG, "Unable to acquire a session for " + mScheduledRecording);
+ failAndQuit();
return;
}
+ mDvrManager.addListener(this, mHandler);
+ mTvRecordingClient.tune(inputId, mChannel.getUri());
+ mState = State.CONNECTION_PENDING;
}
private void failAndQuit() {
- updateRecordingState(Recording.STATE_RECORDING_FAILED);
+ if (DEBUG) Log.d(TAG, "failAndQuit");
+ updateRecordingState(ScheduledRecording.STATE_RECORDING_FAILED);
mState = State.ERROR;
sendRemove();
}
private void sendRemove() {
+ if (DEBUG) Log.d(TAG, "sendRemove");
if (mHandler != null) {
mHandler.sendEmptyMessage(Scheduler.HandlerWrapper.MESSAGE_REMOVE);
}
}
private void handleStartRecording() {
- if (DEBUG)Log.d(TAG, "handleStartRecording " + mRecording);
+ if (DEBUG) Log.d(TAG, "handleStartRecording " + mScheduledRecording);
// TODO(DVR) handle errors
- Channel channel = mRecording.getChannel();
- mSession.startRecord(channel.getUri(), getIdAsMediaUri(mRecording));
- mState= State.RECORDING_START_REQUESTED;
+ long programId = mScheduledRecording.getProgramId();
+ mTvRecordingClient.startRecording(programId == ScheduledRecording.ID_NOT_SET ? null
+ : TvContract.buildProgramUri(programId));
+ updateRecording(ScheduledRecording.buildFrom(mScheduledRecording)
+ .setState(ScheduledRecording.STATE_RECORDING_IN_PROGRESS).build());
+ mState = State.RECORDING_STARTED;
+
if (mHandler == null || !sendEmptyMessageAtAbsoluteTime(MESSAGE_STOP_RECORDING,
- mRecording.getEndTimeMs() + MS_AFTER_END)) {
+ mScheduledRecording.getEndTimeMs() + MS_AFTER_END)) {
mState = State.ERROR;
return;
}
}
private void handleStopRecording() {
- if (DEBUG)Log.d(TAG, "handleStopRecording " + mRecording);
- mSession.stopRecord();
- // TODO: once we add an API to notify successful completion of recording,
- // the following parts need to be moved to the listener implementation.
- updateRecording(Recording.buildFrom(mRecording)
- .setState(Recording.STATE_RECORDING_FINISHED).build());
- sendRemove();
+ if (DEBUG) Log.d(TAG, "handleStopRecording " + mScheduledRecording);
+ mTvRecordingClient.stopRecording();
+ mState = State.RECORDING_STOP_REQUESTED;
}
@VisibleForTesting
@@ -248,10 +251,10 @@ class RecordingTask extends TvRecording.ClientCallback implements Handler.Callba
}
private void release() {
- if (mSession != null) {
- mSession.release();
- mSessionManager.releaseDvrSession(mSession);
+ if (mTvRecordingClient != null) {
+ mSessionManager.releaseTvRecordingClient(mTvRecordingClient);
}
+ mDvrManager.removeListener(this);
}
private boolean sendEmptyMessageAtAbsoluteTime(int what, long when) {
@@ -264,28 +267,55 @@ class RecordingTask extends TvRecording.ClientCallback implements Handler.Callba
return mHandler.sendEmptyMessageDelayed(what, delay);
}
- private void updateRecordingState(@Recording.RecordingState int state) {
- updateRecording(Recording.buildFrom(mRecording).setState(state).build());
+ private void updateRecordingState(@ScheduledRecording.RecordingState int state) {
+ updateRecording(ScheduledRecording.buildFrom(mScheduledRecording).setState(state).build());
}
- @VisibleForTesting static Uri getIdAsMediaUri(Recording recording) {
+ @VisibleForTesting
+ static Uri getIdAsMediaUri(ScheduledRecording scheduledRecording) {
// TODO define the URI format
- return new Uri.Builder().appendPath(String.valueOf(recording.getId())).build();
+ return new Uri.Builder().appendPath(String.valueOf(scheduledRecording.getId())).build();
}
- private void updateRecording(Recording updatedRecording) {
- if (DEBUG) Log.d(TAG, "updateRecording " + updatedRecording);
- mRecording = updatedRecording;
+ private void updateRecording(ScheduledRecording updatedScheduledRecording) {
+ if (DEBUG) Log.d(TAG, "updateScheduledRecording " + updatedScheduledRecording);
+ mScheduledRecording = updatedScheduledRecording;
mMainThreadHandler.post(new Runnable() {
@Override
public void run() {
- mDataManager.updateRecording(mRecording);
+ mDataManager.updateScheduledRecording(mScheduledRecording);
}
});
}
@Override
+ public void onStopRecordingRequested(ScheduledRecording recording) {
+ if (recording.getId() != mScheduledRecording.getId()) {
+ return;
+ }
+ switch (mState) {
+ case RECORDING_STARTED:
+ mHandler.removeMessages(MESSAGE_STOP_RECORDING);
+ handleStopRecording();
+ break;
+ case RECORDING_STOP_REQUESTED:
+ // Do nothing
+ break;
+ case NOT_STARTED:
+ case SESSION_ACQUIRED:
+ case CONNECTION_PENDING:
+ case CONNECTED:
+ case RECORDING_START_REQUESTED:
+ case ERROR:
+ case RELEASED:
+ default:
+ sendRemove();
+ break;
+ }
+ }
+
+ @Override
public String toString() {
- return getClass().getName() + "(" + mRecording + ")";
+ return getClass().getName() + "(" + mScheduledRecording + ")";
}
}
diff --git a/src/com/android/tv/dvr/ScheduledProgramReaper.java b/src/com/android/tv/dvr/ScheduledProgramReaper.java
new file mode 100644
index 00000000..9053eaec
--- /dev/null
+++ b/src/com/android/tv/dvr/ScheduledProgramReaper.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.dvr;
+
+import android.support.annotation.MainThread;
+import android.support.annotation.VisibleForTesting;
+
+import com.android.tv.util.Clock;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Deletes {@link ScheduledRecording} older than {@value @DAYS} days.
+ */
+class ScheduledProgramReaper implements Runnable {
+
+ @VisibleForTesting
+ static final int DAYS = 2;
+ private final WritableDvrDataManager mDvrDataManager;
+ private final Clock mClock;
+
+ ScheduledProgramReaper(WritableDvrDataManager dvrDataManager, Clock clock) {
+ mDvrDataManager = dvrDataManager;
+ mClock = clock;
+ }
+
+ @Override
+ @MainThread
+ public void run() {
+ List<ScheduledRecording> recordings = mDvrDataManager.getAllScheduledRecordings();
+ long cutoff = mClock.currentTimeMillis() - TimeUnit.DAYS.toMillis(DAYS);
+ for (ScheduledRecording r : recordings) {
+ if (r.getEndTimeMs() < cutoff) {
+ mDvrDataManager.removeScheduledRecording(r);
+ }
+ }
+ }
+}
diff --git a/src/com/android/tv/dvr/Recording.java b/src/com/android/tv/dvr/ScheduledRecording.java
index 9ecda4da..01b00459 100644
--- a/src/com/android/tv/dvr/Recording.java
+++ b/src/com/android/tv/dvr/ScheduledRecording.java
@@ -16,49 +16,44 @@
package com.android.tv.dvr;
+import android.content.ContentValues;
import android.database.Cursor;
-import android.net.Uri;
import android.support.annotation.IntDef;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.util.Range;
+import com.android.tv.common.SoftPreconditions;
import com.android.tv.data.Channel;
import com.android.tv.data.Program;
import com.android.tv.dvr.provider.DvrContract;
-import com.android.tv.util.SoftPreconditions;
import com.android.tv.util.Utils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.Comparator;
-import java.util.List;
/**
* A data class for one recording contents.
*/
@VisibleForTesting
-public final class Recording {
+public final class ScheduledRecording {
private static final String TAG = "Recording";
- public static final String RECORDING_ID_EXTRA = "extra.dvr.recording.id";
+ public static final String RECORDING_ID_EXTRA = "extra.dvr.recording.id"; //TODO(DVR) move
public static final String PARAM_INPUT_ID = "input_id";
public static final long ID_NOT_SET = -1;
- public static final Comparator<Recording> START_TIME_COMPARATOR = new Comparator<Recording>() {
+ public static final Comparator<ScheduledRecording> START_TIME_COMPARATOR = new Comparator<ScheduledRecording>() {
@Override
- public int compare(Recording lhs, Recording rhs) {
+ public int compare(ScheduledRecording lhs, ScheduledRecording rhs) {
return Long.compare(lhs.mStartTimeMs, rhs.mStartTimeMs);
}
};
- public static final Comparator<Recording> PRIORITY_COMPARATOR = new Comparator<Recording>() {
+ public static final Comparator<ScheduledRecording> PRIORITY_COMPARATOR = new Comparator<ScheduledRecording>() {
@Override
- public int compare(Recording lhs, Recording rhs) {
+ public int compare(ScheduledRecording lhs, ScheduledRecording rhs) {
int value = Long.compare(lhs.mPriority, rhs.mPriority);
if (value == 0) {
value = Long.compare(lhs.mId, rhs.mId);
@@ -67,10 +62,10 @@ public final class Recording {
}
};
- public static final Comparator<Recording> START_TIME_THEN_PRIORITY_COMPARATOR
- = new Comparator<Recording>() {
+ public static final Comparator<ScheduledRecording> START_TIME_THEN_PRIORITY_COMPARATOR
+ = new Comparator<ScheduledRecording>() {
@Override
- public int compare(Recording lhs, Recording rhs) {
+ public int compare(ScheduledRecording lhs, ScheduledRecording rhs) {
int value = START_TIME_COMPARATOR.compare(lhs, rhs);
if (value == 0) {
value = PRIORITY_COMPARATOR.compare(lhs, rhs);
@@ -79,18 +74,15 @@ public final class Recording {
}
};
- public static Builder builder(Channel c, Program p) {
+ public static Builder builder(Program p) {
return new Builder()
- .setChannel(c)
- .setStartTime(p.getStartTimeUtcMillis())
- .setEndTime(p.getEndTimeUtcMillis())
- .setPrograms(Collections.singletonList(p))
+ .setStartTime(p.getStartTimeUtcMillis()).setEndTime(p.getEndTimeUtcMillis())
+ .setProgramId(p.getId())
.setType(TYPE_PROGRAM);
}
- public static Builder builder(Channel c, long startTime, long endTime) {
+ public static Builder builder(long startTime, long endTime) {
return new Builder()
- .setChannel(c)
.setStartTime(startTime)
.setEndTime(endTime)
.setType(TYPE_TIMED);
@@ -99,13 +91,11 @@ public final class Recording {
public static final class Builder {
private long mId = ID_NOT_SET;
private long mPriority = Long.MAX_VALUE;
- private Uri mUri;
- private Channel mChannel;
- private List<Program> mPrograms;
+ private long mChannelId;
+ private long mProgramId = ID_NOT_SET;
private @RecordingType int mType;
private long mStartTime;
private long mEndTime;
- private long mSize;
private @RecordingState int mState;
private SeasonRecording mParentSeasonRecording;
@@ -121,18 +111,13 @@ public final class Recording {
return this;
}
- private Builder setUri(Uri uri) {
- mUri = uri;
+ public Builder setChannelId(long channelId) {
+ mChannelId = channelId;
return this;
}
- private Builder setChannel(Channel channel) {
- mChannel = channel;
- return this;
- }
-
- public Builder setPrograms(List<Program> programs) {
- mPrograms = programs;
+ public Builder setProgramId(long programId) {
+ mProgramId = programId;
return this;
}
@@ -151,11 +136,6 @@ public final class Recording {
return this;
}
- public Builder setSize(long size) {
- mSize = size;
- return this;
- }
-
public Builder setState(@RecordingState int state) {
mState = state;
return this;
@@ -166,28 +146,21 @@ public final class Recording {
return this;
}
- public Recording build() {
- return new Recording(mId, mPriority, mUri, mChannel, mPrograms, mType, mStartTime,
- mEndTime, mSize,
- mState, mParentSeasonRecording);
+ public ScheduledRecording build() {
+ return new ScheduledRecording(mId, mPriority, mChannelId, mProgramId, mType, mStartTime,
+ mEndTime, mState, mParentSeasonRecording);
}
}
/**
* Creates {@link Builder} object from the given original {@code Recording}.
*/
- public static Builder buildFrom(Recording orig) {
+ public static Builder buildFrom(ScheduledRecording orig) {
return new Builder()
- .setId(orig.mId)
- .setChannel(orig.mChannel)
- .setEndTime(orig.mEndTimeMs)
- .setParentSeasonRecording(orig.mParentSeasonRecording)
- .setPrograms(orig.mPrograms)
- .setSize(orig.mMediaSize)
- .setStartTime(orig.mStartTimeMs)
- .setState(orig.mState)
- .setType(orig.mType)
- .setUri(orig.mUri);
+ .setId(orig.mId).setChannelId(orig.mChannelId)
+ .setEndTime(orig.mEndTimeMs).setParentSeasonRecording(orig.mParentSeasonRecording)
+ .setProgramId(orig.mProgramId)
+ .setStartTime(orig.mStartTimeMs).setState(orig.mState).setType(orig.mType);
}
@Retention(RetentionPolicy.SOURCE)
@@ -196,6 +169,7 @@ public final class Recording {
public @interface RecordingState {}
public static final int STATE_RECORDING_NOT_STARTED = 0;
public static final int STATE_RECORDING_IN_PROGRESS = 1;
+ @Deprecated // It is not used.
public static final int STATE_RECORDING_UNEXPECTEDLY_STOPPED = 2;
public static final int STATE_RECORDING_FINISHED = 3;
public static final int STATE_RECORDING_FAILED = 4;
@@ -215,20 +189,46 @@ public final class Recording {
@RecordingType private final int mType;
/**
- * Use this projection if you want to create {@link Recording} object using {@link #fromCursor}.
+ * Use this projection if you want to create {@link ScheduledRecording} object using {@link #fromCursor}.
*/
public static final String[] PROJECTION = {
- // Columns must match what is read in Recording.fromCursor()
- DvrContract.Recordings._ID,
- DvrContract.Recordings.COLUMN_PRIORITY,
- DvrContract.Recordings.COLUMN_TYPE,
- DvrContract.Recordings.COLUMN_URI,
- DvrContract.Recordings.COLUMN_CHANNEL_ID,
- DvrContract.Recordings.COLUMN_START_TIME_UTC_MILLIS,
- DvrContract.Recordings.COLUMN_END_TIME_UTC_MILLIS,
- DvrContract.Recordings.COLUMN_MEDIA_SIZE,
- DvrContract.Recordings.COLUMN_STATE
- };
+ // Columns must match what is read in Recording.fromCursor()
+ DvrContract.Recordings._ID,
+ DvrContract.Recordings.COLUMN_PRIORITY,
+ DvrContract.Recordings.COLUMN_TYPE,
+ DvrContract.Recordings.COLUMN_CHANNEL_ID,
+ DvrContract.Recordings.COLUMN_PROGRAM_ID,
+ DvrContract.Recordings.COLUMN_START_TIME_UTC_MILLIS,
+ DvrContract.Recordings.COLUMN_END_TIME_UTC_MILLIS,
+ DvrContract.Recordings.COLUMN_STATE};
+ /**
+ * Creates {@link ScheduledRecording} object from the given {@link Cursor}.
+ */
+ public static ScheduledRecording fromCursor(Cursor c) {
+ int index = -1;
+ return new Builder()
+ .setId(c.getLong(++index))
+ .setPriority(c.getLong(++index))
+ .setType(recordingType(c.getString(++index)))
+ .setChannelId(c.getLong(++index))
+ .setProgramId(c.getLong(++index))
+ .setStartTime(c.getLong(++index))
+ .setEndTime(c.getLong(++index))
+ .setState(recordingState(c.getString(++index)))
+ .build();
+ }
+
+ public static ContentValues toContentValues(ScheduledRecording r) {
+ ContentValues values = new ContentValues();
+ values.put(DvrContract.Recordings.COLUMN_CHANNEL_ID, r.getChannelId());
+ values.put(DvrContract.Recordings.COLUMN_PROGRAM_ID, r.getProgramId());
+ values.put(DvrContract.Recordings.COLUMN_PRIORITY, r.getPriority());
+ values.put(DvrContract.Recordings.COLUMN_START_TIME_UTC_MILLIS, r.getStartTimeMs());
+ values.put(DvrContract.Recordings.COLUMN_END_TIME_UTC_MILLIS, r.getEndTimeMs());
+ values.put(DvrContract.Recordings.COLUMN_STATE, r.getState());
+ values.put(DvrContract.Recordings.COLUMN_TYPE, r.getType());
+ return values;
+ }
/**
* The ID internal to Live TV
@@ -243,53 +243,30 @@ public final class Recording {
*/
private final long mPriority;
- /**
- * The {@link Uri} is used as its identifier with the TIS.
- * Note: If the state is STATE_RECORDING_NOT_STARTED, this might be {@code null}.
- */
- @Nullable
- private final Uri mUri;
+ private final long mChannelId;
/**
- * Note: mChannel and mPrograms should be loaded from a separate storage not
- * from TvProvider, because info from TvProvider can be removed or edited later.
- */
- @NonNull
- private final Channel mChannel;
- /**
- * Recorded program info. Its size is usually 1. But, when a channel is recorded by given time
- * range, multiple programs can be recorded in one recording.
+ * Optional id of the associated program.
+ *
*/
- @NonNull
- private final List<Program> mPrograms;
+ private final long mProgramId;
private final long mStartTimeMs;
private final long mEndTimeMs;
- private final long mMediaSize;
@RecordingState private final int mState;
private final SeasonRecording mParentSeasonRecording;
- private Recording(long id, long priority, Uri uri, Channel channel, List<Program> programs,
- @RecordingType int type, long startTime, long endTime, long size,
+ private ScheduledRecording(long id, long priority, long channelId, long programId,
+ @RecordingType int type, long startTime, long endTime,
@RecordingState int state, SeasonRecording parentSeasonRecording) {
mId = id;
mPriority = priority;
- if (uri == null && id >= 0 && channel != null) {
- uri = new Uri.Builder()
- .scheme("record")
- .authority("com.android.tv")
- .appendPath(Long.toString(mId))
- .appendQueryParameter(PARAM_INPUT_ID, channel.getInputId())
- .build();
- }
- mUri = uri;
- mChannel = channel;
- mPrograms = programs == null ? Collections.EMPTY_LIST : new ArrayList<>(programs);
+ mChannelId = channelId;
+ mProgramId = programId;
mType = type;
mStartTimeMs = startTime;
mEndTimeMs = endTime;
- mMediaSize = size;
mState = state;
mParentSeasonRecording = parentSeasonRecording;
}
@@ -304,24 +281,17 @@ public final class Recording {
}
/**
- * Returns {@link android.net.Uri} representing the recording.
- */
- public Uri getUri() {
- return mUri;
- }
-
- /**
* Returns recorded {@link Channel}.
*/
- public Channel getChannel() {
- return mChannel;
+ public long getChannelId() {
+ return mChannelId;
}
/**
- * Returns a list of recorded {@link Program}.
+ * Return the optional program id
*/
- public List<Program> getPrograms() {
- return mPrograms;
+ public long getProgramId() {
+ return mProgramId;
}
/**
@@ -346,13 +316,6 @@ public final class Recording {
}
/**
- * Returns file size which this record consumes.
- */
- public long getSize() {
- return mMediaSize;
- }
-
- /**
* Returns the state. The possible states are {@link #STATE_RECORDING_FINISHED},
* {@link #STATE_RECORDING_IN_PROGRESS} and {@link #STATE_RECORDING_UNEXPECTEDLY_STOPPED}.
*/
@@ -376,30 +339,6 @@ public final class Recording {
}
/**
- * Creates {@link Recording} object from the given {@link Cursor}.
- */
- public static Recording fromCursor(Cursor c, Channel channel, List<Program> programs) {
- Builder builder = new Builder();
- int index = -1;
- builder.setId(c.getLong(++index));
- builder.setPriority(c.getLong(++index));
- builder.setType(recordingType(c.getString(++index)));
- String uri = c.getString(++index);
- if (uri != null) {
- builder.setUri(Uri.parse(uri));
- }
- // Skip channel.
- ++index;
- builder.setStartTime(c.getLong(++index));
- builder.setEndTime(c.getLong(++index));
- builder.setSize(c.getLong(++index));
- builder.setState(recordingState(c.getString(++index)));
- builder.setChannel(channel);
- builder.setPrograms(programs);
- return builder.build();
- }
-
- /**
* Converts a string to a @RecordingType int, defaulting to {@link #TYPE_TIMED}.
*/
private static @RecordingType int recordingType(String type) {
@@ -459,7 +398,7 @@ public final class Recording {
@Override
public String toString() {
- return "Recording[" + mId
+ return "ScheduledRecording[" + mId
+ "]"
+ "(startTime=" + Utils.toIsoDateTimeString(mStartTimeMs)
+ ",endTime=" + Utils.toIsoDateTimeString(mEndTimeMs)
diff --git a/src/com/android/tv/dvr/Scheduler.java b/src/com/android/tv/dvr/Scheduler.java
index 8070f8a6..ff9bde68 100644
--- a/src/com/android/tv/dvr/Scheduler.java
+++ b/src/com/android/tv/dvr/Scheduler.java
@@ -28,6 +28,8 @@ import android.util.Log;
import android.util.LongSparseArray;
import android.util.Range;
+import com.android.tv.data.Channel;
+import com.android.tv.data.ChannelDataManager;
import com.android.tv.util.Clock;
import java.util.List;
@@ -37,7 +39,7 @@ import java.util.concurrent.TimeUnit;
* The core class to manage schedule and run actual recording.
*/
@VisibleForTesting
-public class Scheduler implements DvrDataManager.Listener {
+public class Scheduler implements DvrDataManager.ScheduledRecordingListener {
private static final String TAG = "Scheduler";
private static final boolean DEBUG = false;
@@ -51,9 +53,9 @@ public class Scheduler implements DvrDataManager.Listener {
public static final int MESSAGE_REMOVE = 999;
private final long mId;
- HandlerWrapper(Looper looper, Recording recording, RecordingTask recordingTask) {
+ HandlerWrapper(Looper looper, ScheduledRecording scheduledRecording, RecordingTask recordingTask) {
super(looper, recordingTask);
- mId = recording.getId();
+ mId = scheduledRecording.getId();
}
@Override
@@ -73,27 +75,32 @@ public class Scheduler implements DvrDataManager.Listener {
private final Looper mLooper;
private final DvrSessionManager mSessionManager;
private final WritableDvrDataManager mDataManager;
+ private final DvrManager mDvrManager;
+ private final ChannelDataManager mChannelDataManager;
private final Context mContext;
private final Clock mClock;
private final AlarmManager mAlarmManager;
- public Scheduler(Looper looper, DvrSessionManager sessionManager,
- WritableDvrDataManager dataManager, Context context, Clock clock,
+ public Scheduler(Looper looper, DvrManager dvrManager, DvrSessionManager sessionManager,
+ WritableDvrDataManager dataManager, ChannelDataManager channelDataManager,
+ Context context, Clock clock,
AlarmManager alarmManager) {
mLooper = looper;
+ mDvrManager = dvrManager;
mSessionManager = sessionManager;
mDataManager = dataManager;
+ mChannelDataManager = channelDataManager;
mContext = context;
mClock = clock;
mAlarmManager = alarmManager;
}
private void updatePendingRecordings() {
- List<Recording> recordings = mDataManager.getRecordingsThatOverlapWith(
+ List<ScheduledRecording> scheduledRecordings = mDataManager.getRecordingsThatOverlapWith(
new Range(mClock.currentTimeMillis(),
mClock.currentTimeMillis() + SOON_DURATION_IN_MS));
// TODO(DVR): handle removing and updating exiting recordings.
- for (Recording r : recordings) {
+ for (ScheduledRecording r : scheduledRecordings) {
scheduleRecordingSoon(r);
}
}
@@ -108,18 +115,18 @@ public class Scheduler implements DvrDataManager.Listener {
}
@Override
- public void onRecordingAdded(Recording recording) {
- if (DEBUG) Log.d(TAG, "added " + recording);
- if (startsWithin(recording, SOON_DURATION_IN_MS)) {
- scheduleRecordingSoon(recording);
+ public void onScheduledRecordingAdded(ScheduledRecording scheduledRecording) {
+ if (DEBUG) Log.d(TAG, "added " + scheduledRecording);
+ if (startsWithin(scheduledRecording, SOON_DURATION_IN_MS)) {
+ scheduleRecordingSoon(scheduledRecording);
} else {
updateNextAlarm();
}
}
@Override
- public void onRecordingRemoved(Recording recording) {
- long id = recording.getId();
+ public void onScheduledRecordingRemoved(ScheduledRecording scheduledRecording) {
+ long id = scheduledRecording.getId();
HandlerWrapper wrapper = mPendingRecordings.get(id);
if (wrapper != null) {
wrapper.removeCallbacksAndMessages(null);
@@ -130,16 +137,18 @@ public class Scheduler implements DvrDataManager.Listener {
}
@Override
- public void onRecordingStatusChanged(Recording recording) {
+ public void onScheduledRecordingStatusChanged(ScheduledRecording scheduledRecording) {
//TODO(DVR): implement
}
- private void scheduleRecordingSoon(Recording recording) {
- RecordingTask recordingTask = new RecordingTask(recording, mSessionManager, mDataManager,
- mClock);
- HandlerWrapper handlerWrapper = new HandlerWrapper(mLooper, recording, recordingTask);
+ private void scheduleRecordingSoon(ScheduledRecording scheduledRecording) {
+ Channel channel = mChannelDataManager.getChannel(scheduledRecording.getChannelId());
+ RecordingTask recordingTask = new RecordingTask(scheduledRecording, channel, mDvrManager,
+ mSessionManager, mDataManager, mClock);
+ HandlerWrapper handlerWrapper = new HandlerWrapper(mLooper, scheduledRecording,
+ recordingTask);
recordingTask.setHandler(handlerWrapper);
- mPendingRecordings.put(recording.getId(), handlerWrapper);
+ mPendingRecordings.put(scheduledRecording.getId(), handlerWrapper);
handlerWrapper.sendEmptyMessage(RecordingTask.MESSAGE_INIT);
}
@@ -164,7 +173,7 @@ public class Scheduler implements DvrDataManager.Listener {
}
@VisibleForTesting
- boolean startsWithin(Recording recording, long durationInMs) {
- return mClock.currentTimeMillis() >= recording.getStartTimeMs() - durationInMs;
+ boolean startsWithin(ScheduledRecording scheduledRecording, long durationInMs) {
+ return mClock.currentTimeMillis() >= scheduledRecording.getStartTimeMs() - durationInMs;
}
}
diff --git a/src/com/android/tv/dvr/SeasonRecording.java b/src/com/android/tv/dvr/SeasonRecording.java
index 074ef017..7f89e135 100644
--- a/src/com/android/tv/dvr/SeasonRecording.java
+++ b/src/com/android/tv/dvr/SeasonRecording.java
@@ -29,7 +29,7 @@ public class SeasonRecording {
*/
private static final int ALL_SEASON = -1;
- private List<Recording> mSchedule;
+ private List<ScheduledRecording> mSchedule;
private String mTitle;
private int mSeasonNumber;
}
diff --git a/src/com/android/tv/dvr/WritableDvrDataManager.java b/src/com/android/tv/dvr/WritableDvrDataManager.java
index 87809701..0b8a4c99 100644
--- a/src/com/android/tv/dvr/WritableDvrDataManager.java
+++ b/src/com/android/tv/dvr/WritableDvrDataManager.java
@@ -29,7 +29,7 @@ interface WritableDvrDataManager extends DvrDataManager {
/**
* Add a new recording.
*/
- void addRecording(Recording recording);
+ void addScheduledRecording(ScheduledRecording scheduledRecording);
/**
* Add a season recording/
@@ -39,7 +39,7 @@ interface WritableDvrDataManager extends DvrDataManager {
/**
* Remove a recording.
*/
- void removeRecording(Recording Recording);
+ void removeScheduledRecording(ScheduledRecording ScheduledRecording);
/**
* Remove a season schedule.
@@ -49,5 +49,5 @@ interface WritableDvrDataManager extends DvrDataManager {
/**
* Update an existing recording.
*/
- void updateRecording(Recording r);
+ void updateScheduledRecording(ScheduledRecording r);
}
diff --git a/src/com/android/tv/dvr/provider/AsyncDvrDbTask.java b/src/com/android/tv/dvr/provider/AsyncDvrDbTask.java
index 3fc6e4a9..6058aa54 100644
--- a/src/com/android/tv/dvr/provider/AsyncDvrDbTask.java
+++ b/src/com/android/tv/dvr/provider/AsyncDvrDbTask.java
@@ -21,19 +21,12 @@ import android.database.Cursor;
import android.os.AsyncTask;
import android.support.annotation.Nullable;
-import com.android.tv.data.Channel;
-import com.android.tv.data.Program;
-import com.android.tv.dvr.Recording;
-import com.android.tv.dvr.provider.DvrContract.DvrChannels;
-import com.android.tv.dvr.provider.DvrContract.DvrPrograms;
-import com.android.tv.dvr.provider.DvrContract.RecordingToPrograms;
+import com.android.tv.dvr.ScheduledRecording;
import com.android.tv.dvr.provider.DvrContract.Recordings;
import com.android.tv.util.NamedThreadFactory;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -87,14 +80,14 @@ public abstract class AsyncDvrDbTask<Params, Progress, Result>
* The id will be -1 if there was an error.
*/
public abstract static class AsyncAddRecordingTask
- extends AsyncDvrDbTask<Recording, Void, List<Recording>> {
+ extends AsyncDvrDbTask<ScheduledRecording, Void, List<ScheduledRecording>> {
public AsyncAddRecordingTask(Context context) {
super(context);
}
@Override
- protected final List<Recording> doInDvrBackground(Recording... params) {
+ protected final List<ScheduledRecording> doInDvrBackground(ScheduledRecording... params) {
return sDbHelper.insertRecordings(params);
}
}
@@ -106,13 +99,13 @@ public abstract class AsyncDvrDbTask<Params, Progress, Result>
* if no match was found. The count is expected to be exactly 1 for each recording.
*/
public abstract static class AsyncUpdateRecordingTask
- extends AsyncDvrDbTask<Recording, Void, List<Integer>> {
+ extends AsyncDvrDbTask<ScheduledRecording, Void, List<Integer>> {
public AsyncUpdateRecordingTask(Context context) {
super(context);
}
@Override
- protected final List<Integer> doInDvrBackground(Recording... params) {
+ protected final List<Integer> doInDvrBackground(ScheduledRecording... params) {
return sDbHelper.updateRecordings(params);
}
}
@@ -124,91 +117,43 @@ public abstract class AsyncDvrDbTask<Params, Progress, Result>
* if no match was found. The count is expected to be exactly 1 for each recording.
*/
public abstract static class AsyncDeleteRecordingTask
- extends AsyncDvrDbTask<Recording, Void, List<Integer>> {
+ extends AsyncDvrDbTask<ScheduledRecording, Void, List<Integer>> {
public AsyncDeleteRecordingTask(Context context) {
super(context);
}
@Override
- protected final List<Integer> doInDvrBackground(Recording... params) {
+ protected final List<Integer> doInDvrBackground(ScheduledRecording... params) {
return sDbHelper.deleteRecordings(params);
}
}
public abstract static class AsyncDvrQueryTask
- extends AsyncDvrDbTask<Void, Void, List<Recording>> {
+ extends AsyncDvrDbTask<Void, Void, List<ScheduledRecording>> {
public AsyncDvrQueryTask(Context context) {
super(context);
}
@Override
@Nullable
- protected final List<Recording> doInDvrBackground(Void... params) {
+ protected final List<ScheduledRecording> doInDvrBackground(Void... params) {
if (isCancelled()) {
return null;
}
- // Read Channels Table.
- Map<Long, Channel> channelMap = new HashMap<>();
- try (Cursor c = sDbHelper.query(DvrChannels.TABLE_NAME, Channel.PROJECTION_DVR)) {
- while (c.moveToNext() && !isCancelled()) {
- Channel channel = Channel.fromDvrCursor(c);
- channelMap.put(channel.getDvrId(), channel);
- }
- }
-
- if (isCancelled()) {
- return null;
- }
- // Read Programs Table.
- Map<Long, Program> programMap = new HashMap<>();
- try (Cursor c = sDbHelper.query(DvrPrograms.TABLE_NAME, Program.PROJECTION_DVR)) {
- while (c.moveToNext() && !isCancelled()) {
- Program program = Program.fromDvrCursor(c);
- programMap.put(program.getDvrId(), program);
- }
- }
if (isCancelled()) {
return null;
}
- // Read Mapping Table.
- Map<Long, List<Long>> recordingToProgramMap = new HashMap<>();
- try (Cursor c = sDbHelper.query(RecordingToPrograms.TABLE_NAME, new String[] {
- RecordingToPrograms.COLUMN_RECORDING_ID,
- RecordingToPrograms.COLUMN_PROGRAM_ID})) {
- while (c.moveToNext() && !isCancelled()) {
- long recordingId = c.getLong(0);
- List<Long> programList = recordingToProgramMap.get(recordingId);
- if (programList == null) {
- programList = new ArrayList<>();
- recordingToProgramMap.put(recordingId, programList);
- }
- programList.add(c.getLong(1));
- }
- }
-
if (isCancelled()) {
return null;
}
- List<Recording> recordings = new ArrayList<>();
- try (Cursor c = sDbHelper.query(Recordings.TABLE_NAME, Recording.PROJECTION)) {
- int idIndex = c.getColumnIndex(Recordings._ID);
- int channelIndex = c.getColumnIndex(Recordings.COLUMN_CHANNEL_ID);
+ List<ScheduledRecording> scheduledRecordings = new ArrayList<>();
+ try (Cursor c = sDbHelper.query(Recordings.TABLE_NAME, ScheduledRecording.PROJECTION)) {
while (c.moveToNext() && !isCancelled()) {
- Channel channel = channelMap.get(c.getLong(channelIndex));
- List<Program> programs = null;
- long recordingId = c.getLong(idIndex);
- List<Long> programIds = recordingToProgramMap.get(recordingId);
- if (programIds != null) {
- programs = new ArrayList<>();
- for (long programId : programIds) {
- programs.add(programMap.get(programId));
- }
- }
- recordings.add(Recording.fromCursor(c, channel, programs));
+ scheduledRecordings.add(ScheduledRecording.fromCursor(c));
}
}
- return recordings;
+ return scheduledRecordings;
}
}
}
diff --git a/src/com/android/tv/dvr/provider/DvrContract.java b/src/com/android/tv/dvr/provider/DvrContract.java
index e6ce4141..192cc17b 100644
--- a/src/com/android/tv/dvr/provider/DvrContract.java
+++ b/src/com/android/tv/dvr/provider/DvrContract.java
@@ -73,22 +73,23 @@ public final class DvrContract {
public static final String COLUMN_TYPE = "type";
/**
- * The URI string for the recorded media.
+ * The ID of the channel for recording.
*
- * <p>This field can be null if the media is not recorded yet.
+ * <p>This is a required field.
*
- * <p>Type: String
+ * <p>Type: INTEGER (long)
*/
- public static final String COLUMN_URI = "uri";
+ public static final String COLUMN_CHANNEL_ID = "channel_id";
+
/**
- * The ID of the channel for recording.
+ * The ID of the associated program for recording.
*
- * <p>This is a required field. It's not an ID in TvProvider, but in DVR database.
+ * <p>This is an optional field.
*
* <p>Type: INTEGER (long)
*/
- public static final String COLUMN_CHANNEL_ID = "channel_id";
+ public static final String COLUMN_PROGRAM_ID = "program_id";
/**
* The start time of this recording, in milliseconds since the epoch.
@@ -109,13 +110,6 @@ public final class DvrContract {
public static final String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
/**
- * The size of the stored media in bytes.
- *
- * <p>Type: INTEGER (long)
- */
- public static final String COLUMN_MEDIA_SIZE = "media_size";
-
- /**
* The state of this recording.
*
* <p>This value should be one of the followings: {@link #STATE_RECORDING_NOT_STARTED},
@@ -127,49 +121,9 @@ public final class DvrContract {
* <p>Type: String
*/
public static final String COLUMN_STATE = "state";
- }
- /**
- * Column definition for channels for recording.
- *
- * <p>This is the subset of {@link android.media.tv.TvContract.Channels}.
- */
- public static final class DvrChannels implements BaseColumns {
- /** The table name. */
- public static final String TABLE_NAME = "dvr_channels";
+ private Recordings() { }
}
- /**
- * Column definition for programs for recording.
- *
- * <p>This is the subset of {@link android.media.tv.TvContract.Programs}.
- */
- public static final class DvrPrograms implements BaseColumns {
- /** The table name. */
- public static final String TABLE_NAME = "dvr_programs";
- }
-
- /** Column definition for the mapping from recording to programs */
- public static final class RecordingToPrograms implements BaseColumns {
- /** The table name. */
- public static final String TABLE_NAME = "recording_to_programs";
-
- /**
- * The ID of the recording.
- *
- * <p>This is a required field.
- *
- * <p>Type: INTEGER (long)
- */
- public static final String COLUMN_RECORDING_ID = "recording_id";
-
- /**
- * The ID of the program.
- *
- * <p>This is a required field. It's not an ID in TvProvider, but in DVR database.
- *
- * <p>Type: INTEGER (long)
- */
- public static final String COLUMN_PROGRAM_ID = "program_id";
- }
+ private DvrContract() { }
}
diff --git a/src/com/android/tv/dvr/provider/DvrDatabaseHelper.java b/src/com/android/tv/dvr/provider/DvrDatabaseHelper.java
index 2445e935..bdba8ac3 100644
--- a/src/com/android/tv/dvr/provider/DvrDatabaseHelper.java
+++ b/src/com/android/tv/dvr/provider/DvrDatabaseHelper.java
@@ -24,11 +24,7 @@ import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.util.Log;
-import com.android.tv.data.Channel;
-import com.android.tv.dvr.Recording;
-import com.android.tv.dvr.provider.DvrContract.DvrChannels;
-import com.android.tv.dvr.provider.DvrContract.DvrPrograms;
-import com.android.tv.dvr.provider.DvrContract.RecordingToPrograms;
+import com.android.tv.dvr.ScheduledRecording;
import com.android.tv.dvr.provider.DvrContract.Recordings;
import java.util.ArrayList;
@@ -41,49 +37,22 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper {
private static final String TAG = "DvrDatabaseHelper";
private static final boolean DEBUG = true;
- private static final int DATABASE_VERSION = 2;
+ private static final int DATABASE_VERSION = 4;
private static final String DB_NAME = "dvr.db";
private static final String SQL_CREATE_RECORDINGS =
"CREATE TABLE " + Recordings.TABLE_NAME + "("
- + Recordings._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + Recordings.COLUMN_PRIORITY
- + " INTEGER DEFAULT " + Long.MAX_VALUE + ","
+ + Recordings._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + Recordings.COLUMN_PRIORITY + " INTEGER DEFAULT " + Long.MAX_VALUE + ","
+ Recordings.COLUMN_TYPE + " TEXT NOT NULL,"
- + Recordings.COLUMN_URI + " TEXT,"
+ Recordings.COLUMN_CHANNEL_ID + " INTEGER NOT NULL,"
+ + Recordings.COLUMN_PROGRAM_ID + " INTEGER ,"
+ Recordings.COLUMN_START_TIME_UTC_MILLIS + " INTEGER NOT NULL,"
+ Recordings.COLUMN_END_TIME_UTC_MILLIS + " INTEGER NOT NULL,"
- + Recordings.COLUMN_MEDIA_SIZE + " INTEGER,"
+ Recordings.COLUMN_STATE + " TEXT NOT NULL)";
- private static final String SQL_CREATE_DVR_CHANNELS =
- "CREATE TABLE " + DvrChannels.TABLE_NAME + "("
- + DvrChannels._ID + " INTEGER PRIMARY KEY AUTOINCREMENT)";
-
- private static final String SQL_CREATE_DVR_PROGRAMS =
- "CREATE TABLE " + DvrPrograms.TABLE_NAME + "("
- + DvrPrograms._ID + " INTEGER PRIMARY KEY AUTOINCREMENT)";
-
- private static final String SQL_CREATE_RECORDING_PROGRAMS =
- "CREATE TABLE " + RecordingToPrograms.TABLE_NAME + "("
- + RecordingToPrograms._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
- + RecordingToPrograms.COLUMN_RECORDING_ID + " INTEGER,"
- + RecordingToPrograms.COLUMN_PROGRAM_ID + " INTEGER,"
- + "FOREIGN KEY(" + RecordingToPrograms.COLUMN_RECORDING_ID
- + ") REFERENCES " + Recordings.TABLE_NAME + "(" + Recordings._ID
- + ") ON UPDATE CASCADE ON DELETE CASCADE,"
- + "FOREIGN KEY(" + RecordingToPrograms.COLUMN_PROGRAM_ID
- + ") REFERENCES " + DvrPrograms.TABLE_NAME + "(" + DvrPrograms._ID
- + ") ON UPDATE CASCADE ON DELETE CASCADE)";
-
private static final String SQL_DROP_RECORDINGS = "DROP TABLE IF EXISTS "
+ Recordings.TABLE_NAME;
- private static final String SQL_DROP_DVR_CHANNELS = "DROP TABLE IF EXISTS "
- + DvrChannels.TABLE_NAME;
- private static final String SQL_DROP_DVR_PROGRAMS = "DROP TABLE IF EXISTS "
- + DvrPrograms.TABLE_NAME;
- private static final String SQL_DROP_RECORDING_PROGRAMS = "DROP TABLE IF EXISTS "
- + RecordingToPrograms.TABLE_NAME;
public static final String WHERE_RECORDING_ID_EQUALS = Recordings._ID + " = ?";
public DvrDatabaseHelper(Context context) {
@@ -99,22 +68,10 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper {
public void onCreate(SQLiteDatabase db) {
if (DEBUG) Log.d(TAG, "Executing SQL: " + SQL_CREATE_RECORDINGS);
db.execSQL(SQL_CREATE_RECORDINGS);
- if (DEBUG) Log.d(TAG, "Executing SQL: " + SQL_CREATE_DVR_CHANNELS);
- db.execSQL(SQL_CREATE_DVR_CHANNELS);
- if (DEBUG) Log.d(TAG, "Executing SQL: " + SQL_CREATE_DVR_PROGRAMS);
- db.execSQL(SQL_CREATE_DVR_PROGRAMS);
- if (DEBUG) Log.d(TAG, "Executing SQL: " + SQL_CREATE_RECORDING_PROGRAMS);
- db.execSQL(SQL_CREATE_RECORDING_PROGRAMS);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- if (DEBUG) Log.d(TAG, "Executing SQL: " + SQL_DROP_RECORDING_PROGRAMS);
- db.execSQL(SQL_DROP_RECORDING_PROGRAMS);
- if (DEBUG) Log.d(TAG, "Executing SQL: " + SQL_DROP_DVR_PROGRAMS);
- db.execSQL(SQL_DROP_DVR_PROGRAMS);
- if (DEBUG) Log.d(TAG, "Executing SQL: " + SQL_DROP_DVR_CHANNELS);
- db.execSQL(SQL_DROP_DVR_CHANNELS);
if (DEBUG) Log.d(TAG, "Executing SQL: " + SQL_DROP_RECORDINGS);
db.execSQL(SQL_DROP_RECORDINGS);
onCreate(db);
@@ -135,15 +92,15 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper {
*
* @return The list of recordings with id set. The id will be -1 if there was an error.
*/
- public List<Recording> insertRecordings(Recording... recordings) {
- updateChannelsFromRecordings(recordings);
+ public List<ScheduledRecording> insertRecordings(ScheduledRecording... scheduledRecordings) {
+ updateChannelsFromRecordings(scheduledRecordings);
SQLiteDatabase db = getReadableDatabase();
- List<Recording> results = new ArrayList<>();
- for (Recording r : recordings) {
- ContentValues values = getContentValues(r);
+ List<ScheduledRecording> results = new ArrayList<>();
+ for (ScheduledRecording r : scheduledRecordings) {
+ ContentValues values = ScheduledRecording.toContentValues(r);
long id = db.insert(Recordings.TABLE_NAME, null, values);
- results.add(Recording.buildFrom(r).setId(id).build());
+ results.add(ScheduledRecording.buildFrom(r).setId(id).build());
}
return results;
}
@@ -154,13 +111,12 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper {
* @return The list of row update counts. The count will be -1 if there was an error or 0
* if no match was found. The count is expected to be exactly 1 for each recording.
*/
- public List<Integer> updateRecordings(Recording[] recordings) {
- updateChannelsFromRecordings(recordings);
+ public List<Integer> updateRecordings(ScheduledRecording[] scheduledRecordings) {
+ updateChannelsFromRecordings(scheduledRecordings);
SQLiteDatabase db = getWritableDatabase();
List<Integer> results = new ArrayList<>();
- long count = 0;
- for (Recording r : recordings) {
- ContentValues values = getContentValues(r);
+ for (ScheduledRecording r : scheduledRecordings) {
+ ContentValues values = ScheduledRecording.toContentValues(r);
int updated = db.update(Recordings.TABLE_NAME, values, Recordings._ID + " = ?",
new String[] {String.valueOf(r.getId())});
results.add(updated);
@@ -168,42 +124,21 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper {
return results;
}
- private void updateChannelsFromRecordings(Recording[] recordings) {
+ private void updateChannelsFromRecordings(ScheduledRecording[] scheduledRecordings) {
// TODO(DVR) implement/
// TODO(DVR) consider not deleting channels instead of keeping a separate table.
}
- private ContentValues getContentValues(Recording r) {
- ContentValues values = new ContentValues();
- // TODO(DVR): use DVR channel id instead
- Channel channel = r.getChannel();
- if (channel != null) {
- values.put(Recordings.COLUMN_CHANNEL_ID, channel.getId());
- }
- values.put(Recordings.COLUMN_PRIORITY, r.getPriority());
- values.put(Recordings.COLUMN_START_TIME_UTC_MILLIS, r.getStartTimeMs());
- values.put(Recordings.COLUMN_END_TIME_UTC_MILLIS, r.getEndTimeMs());
- values.put(Recordings.COLUMN_STATE, r.getState());
- values.put(Recordings.COLUMN_MEDIA_SIZE, r.getSize());
- values.put(Recordings.COLUMN_TYPE, r.getType());
- if (r.getUri() != null) {
- values.put(Recordings.COLUMN_URI, r.getUri().toString());
- }
- return values;
- }
-
/**
* Delete recordings.
*
* @return The list of row update counts. The count will be -1 if there was an error or 0
* if no match was found. The count is expected to be exactly 1 for each recording.
*/
- public List<Integer> deleteRecordings(Recording[] recordings) {
+ public List<Integer> deleteRecordings(ScheduledRecording[] scheduledRecordings) {
SQLiteDatabase db = getWritableDatabase();
List<Integer> results = new ArrayList<>();
- long count = 0;
- for (Recording r : recordings) {
- ContentValues values = getContentValues(r);
+ for (ScheduledRecording r : scheduledRecordings) {
int deleted = db.delete(Recordings.TABLE_NAME, WHERE_RECORDING_ID_EQUALS,
new String[] {String.valueOf(r.getId())});
results.add(deleted);
diff --git a/src/com/android/tv/dvr/ui/DvrBrowseFragment.java b/src/com/android/tv/dvr/ui/DvrBrowseFragment.java
index 87e47930..70e71cab 100644
--- a/src/com/android/tv/dvr/ui/DvrBrowseFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrBrowseFragment.java
@@ -20,26 +20,33 @@ import android.os.Bundle;
import android.support.annotation.IntDef;
import android.support.v17.leanback.app.BrowseFragment;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.ClassPresenterSelector;
import android.support.v17.leanback.widget.HeaderItem;
import android.support.v17.leanback.widget.ListRow;
import android.support.v17.leanback.widget.ListRowPresenter;
+import android.support.v17.leanback.widget.ObjectAdapter;
import android.util.Log;
import com.android.tv.R;
import com.android.tv.TvApplication;
+import com.android.tv.common.recording.RecordedProgram;
import com.android.tv.dvr.DvrDataManager;
-import com.android.tv.dvr.Recording;
+import com.android.tv.dvr.ScheduledRecording;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.LinkedHashMap;
-import java.util.List;
/**
* {@link BrowseFragment} for DVR functions.
*/
public class DvrBrowseFragment extends BrowseFragment {
private static final String TAG = "DvrBrowseFragment";
+ private static final boolean DEBUG = false;
+
+ private ScheduledRecordingsAdapter mRecordingsInProgressAdapter;
+ private ScheduledRecordingsAdapter mRecordingsNotStatedAdapter;
+ private RecordedProgramsAdapter mRecordedProgramsAdapter;
@IntDef({DVR_CURRENT_RECORDINGS, DVR_SCHEDULED_RECORDINGS, DVR_RECORDED_PROGRAMS, DVR_SETTINGS})
@Retention(RetentionPolicy.SOURCE)
@@ -49,27 +56,48 @@ public class DvrBrowseFragment extends BrowseFragment {
public static final int DVR_RECORDED_PROGRAMS = 2;
public static final int DVR_SETTINGS = 3;
- private static LinkedHashMap<Integer, Integer> sHeaders =
+ private static final LinkedHashMap<Integer, Integer> sHeaders =
new LinkedHashMap<Integer, Integer>() {{
put(DVR_CURRENT_RECORDINGS, R.string.dvr_main_current_recordings);
put(DVR_SCHEDULED_RECORDINGS, R.string.dvr_main_scheduled_recordings);
put(DVR_RECORDED_PROGRAMS, R.string.dvr_main_recorded_programs);
- put(DVR_SETTINGS, R.string.dvr_main_settings);
+ /* put(DVR_SETTINGS, R.string.dvr_main_settings); */ // TODO: Temporarily remove it for DP.
}};
private DvrDataManager mDvrDataManager;
private ArrayObjectAdapter mRowsAdapter;
@Override
- public void onActivityCreated(Bundle savedInstanceState) {
- Log.d(TAG, "onCreate");
- super.onActivityCreated(savedInstanceState);
+ public void onCreate(Bundle savedInstanceState) {
+ if (DEBUG) Log.d(TAG, "onCreate");
+ super.onCreate(savedInstanceState);
+ mDvrDataManager = TvApplication.getSingletons(getContext()).getDvrDataManager();
setupUiElements();
- setupAdapter();
+ setupAdapters();
+ mRecordingsInProgressAdapter.start();
+ mRecordingsNotStatedAdapter.start();
+ mRecordedProgramsAdapter.start();
+ initRows();
prepareEntranceTransition();
+ startEntranceTransition();
+ }
- // TODO: load asynchronously.
- loadData();
+ @Override
+ public void onStart() {
+ if (DEBUG) Log.d(TAG, "onStart");
+ super.onStart();
+ // TODO: It's a workaround for a bug that a progress bar isn't hidden.
+ // We need to remove it later.
+ getProgressBarManager().disableProgressBar();
+ }
+
+ @Override
+ public void onDestroy() {
+ if (DEBUG) Log.d(TAG, "onDestroy");
+ mRecordingsInProgressAdapter.stop();
+ mRecordingsNotStatedAdapter.stop();
+ mRecordedProgramsAdapter.stop();
+ super.onDestroy();
}
private void setupUiElements() {
@@ -77,43 +105,51 @@ public class DvrBrowseFragment extends BrowseFragment {
setHeadersTransitionOnBackEnabled(false);
}
- private void setupAdapter() {
- mDvrDataManager = TvApplication.getSingletons(getContext()).getDvrDataManager();
+ private void setupAdapters() {
mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
setAdapter(mRowsAdapter);
+ ClassPresenterSelector presenterSelector = new ClassPresenterSelector();
+ EmptyItemPresenter emptyItemPresenter = new EmptyItemPresenter(this);
+ ScheduledRecordingPresenter scheduledRecordingPresenter = new ScheduledRecordingPresenter(
+ getContext());
+ RecordedProgramPresenter recordedProgramPresenter = new RecordedProgramPresenter(
+ getContext());
+ presenterSelector.addClassPresenter(ScheduledRecording.class, scheduledRecordingPresenter);
+ presenterSelector.addClassPresenter(RecordedProgram.class, recordedProgramPresenter);
+ presenterSelector.addClassPresenter(EmptyHolder.class, emptyItemPresenter);
+ mRecordingsInProgressAdapter = new ScheduledRecordingsAdapter(mDvrDataManager,
+ ScheduledRecording.STATE_RECORDING_IN_PROGRESS, presenterSelector);
+ mRecordingsNotStatedAdapter = new ScheduledRecordingsAdapter(mDvrDataManager,
+ ScheduledRecording.STATE_RECORDING_NOT_STARTED, presenterSelector);
+ mRecordedProgramsAdapter = new RecordedProgramsAdapter(mDvrDataManager, presenterSelector);
}
- private void loadRow(ArrayObjectAdapter gridRowAdapter, List<Recording> recordings) {
- if (recordings == null || recordings.size() == 0) {
- gridRowAdapter.add(null);
- return;
- }
- for (Recording r : recordings) {
- gridRowAdapter.add(r);
- }
- }
-
- private void loadData() {
+ private void initRows() {
+ mRowsAdapter.clear();
for (@DVR_HEADERS_MODE int i : sHeaders.keySet()) {
HeaderItem gridHeader = new HeaderItem(i, getContext().getString(sHeaders.get(i)));
- GridItemPresenter gridPresenter = new GridItemPresenter(this);
- ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(gridPresenter);
+ ObjectAdapter gridRowAdapter = null;
switch (i) {
- case DVR_CURRENT_RECORDINGS:
- loadRow(gridRowAdapter, mDvrDataManager.getStartedRecordings());
+ case DVR_CURRENT_RECORDINGS: {
+ gridRowAdapter = mRecordingsInProgressAdapter;
break;
- case DVR_SCHEDULED_RECORDINGS:
- loadRow(gridRowAdapter, mDvrDataManager.getScheduledRecordings());
+ }
+ case DVR_SCHEDULED_RECORDINGS: {
+ gridRowAdapter = mRecordingsNotStatedAdapter;
+ }
break;
- case DVR_RECORDED_PROGRAMS:
- loadRow(gridRowAdapter, mDvrDataManager.getFinishedRecordings());
+ case DVR_RECORDED_PROGRAMS: {
+ gridRowAdapter = mRecordedProgramsAdapter;
+ }
break;
case DVR_SETTINGS:
+ gridRowAdapter = new ArrayObjectAdapter(new EmptyItemPresenter(this));
// TODO: provide setup rows.
break;
}
- mRowsAdapter.add(new ListRow(gridHeader, gridRowAdapter));
+ if (gridRowAdapter != null) {
+ mRowsAdapter.add(new ListRow(gridHeader, gridRowAdapter));
+ }
}
- startEntranceTransition();
}
}
diff --git a/src/com/android/tv/dvr/ui/DvrDialogFragment.java b/src/com/android/tv/dvr/ui/DvrDialogFragment.java
new file mode 100644
index 00000000..38de9d8d
--- /dev/null
+++ b/src/com/android/tv/dvr/ui/DvrDialogFragment.java
@@ -0,0 +1,50 @@
+package com.android.tv.dvr.ui;
+
+import android.app.FragmentManager;
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v17.leanback.app.GuidedStepFragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.tv.MainActivity;
+import com.android.tv.R;
+import com.android.tv.guide.ProgramGuide;
+
+public class DvrDialogFragment extends HalfSizedDialogFragment {
+ private final DvrGuidedStepFragment mDvrGuidedStepFragment;
+
+ public DvrDialogFragment(DvrGuidedStepFragment dvrGuidedStepFragment) {
+ mDvrGuidedStepFragment = dvrGuidedStepFragment;
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ ProgramGuide programGuide =
+ ((MainActivity) getActivity()).getOverlayManager().getProgramGuide();
+ if (programGuide != null && programGuide.isActive()) {
+ programGuide.cancelHide();
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = super.onCreateView(inflater, container, savedInstanceState);
+ FragmentManager fm = getChildFragmentManager();
+ GuidedStepFragment.add(fm, mDvrGuidedStepFragment, R.id.halfsized_dialog_host);
+ return view;
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ ProgramGuide programGuide =
+ ((MainActivity) getActivity()).getOverlayManager().getProgramGuide();
+ if (programGuide != null && programGuide.isActive()) {
+ programGuide.scheduleHide();
+ }
+ }
+}
diff --git a/src/com/android/tv/dvr/ui/DvrGuidedStepFragment.java b/src/com/android/tv/dvr/ui/DvrGuidedStepFragment.java
new file mode 100644
index 00000000..0854b91a
--- /dev/null
+++ b/src/com/android/tv/dvr/ui/DvrGuidedStepFragment.java
@@ -0,0 +1,73 @@
+package com.android.tv.dvr.ui;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v17.leanback.app.GuidedStepFragment;
+import android.support.v17.leanback.widget.GuidanceStylist;
+import android.support.v17.leanback.widget.VerticalGridView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.tv.MainActivity;
+import com.android.tv.TvApplication;
+import com.android.tv.dialog.SafeDismissDialogFragment;
+import com.android.tv.dvr.DvrManager;
+import com.android.tv.guide.ProgramManager.TableEntry;
+import com.android.tv.R;
+
+public class DvrGuidedStepFragment extends GuidedStepFragment {
+ private final TableEntry mEntry;
+ private DvrManager mDvrManager;
+
+ public DvrGuidedStepFragment(TableEntry entry) {
+ mEntry = entry;
+ }
+
+ protected TableEntry getEntry() {
+ return mEntry;
+ }
+
+ protected DvrManager getDvrManager() {
+ return mDvrManager;
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ mDvrManager = TvApplication.getSingletons(context).getDvrManager();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = super.onCreateView(inflater, container, savedInstanceState);
+ VerticalGridView gridView = getGuidedActionsStylist().getActionsGridView();
+ gridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE);
+ return view;
+ }
+
+ @Override
+ public GuidanceStylist onCreateGuidanceStylist() {
+ // Workaround: b/28448653
+ return new GuidanceStylist() {
+ @Override
+ public int onProvideLayoutId() {
+ return R.layout.halfsized_guidance;
+ }
+ };
+ }
+
+ @Override
+ public int onProvideTheme() {
+ return R.style.Theme_TV_Dvr_GuidedStep;
+ }
+
+ protected void dismissDialog() {
+ SafeDismissDialogFragment currentDialog =
+ ((MainActivity) getActivity()).getOverlayManager().getCurrentDialog();
+ if (currentDialog instanceof DvrDialogFragment) {
+ currentDialog.dismiss();
+ }
+ }
+}
diff --git a/src/com/android/tv/dvr/ui/DvrRecordConflictFragment.java b/src/com/android/tv/dvr/ui/DvrRecordConflictFragment.java
new file mode 100644
index 00000000..92052b5b
--- /dev/null
+++ b/src/com/android/tv/dvr/ui/DvrRecordConflictFragment.java
@@ -0,0 +1,82 @@
+package com.android.tv.dvr.ui;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.tv.MainActivity;
+import com.android.tv.R;
+
+import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
+import android.support.v17.leanback.widget.GuidedAction;
+
+import com.android.tv.data.Channel;
+import com.android.tv.data.ChannelDataManager;
+import com.android.tv.data.Program;
+import com.android.tv.dvr.ScheduledRecording;
+import com.android.tv.guide.ProgramManager.TableEntry;
+
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.List;
+
+public class DvrRecordConflictFragment extends DvrGuidedStepFragment {
+ private static final int DVR_EPG_RECORD = 1;
+ private static final int DVR_EPG_NOT_RECORD = 2;
+
+ private List<ScheduledRecording> mConflicts;
+
+ public DvrRecordConflictFragment(TableEntry entry) {
+ super(entry);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ mConflicts = getDvrManager().getScheduledRecordingsThatConflict(getEntry().program);
+ return super.onCreateView(inflater, container, savedInstanceState);
+ }
+
+ @Override
+ public Guidance onCreateGuidance(Bundle savedInstanceState) {
+ final MainActivity tvActivity = (MainActivity) getActivity();
+ final ChannelDataManager channelDataManager = tvActivity.getChannelDataManager();
+ StringBuilder sb = new StringBuilder();
+ for (ScheduledRecording r : mConflicts) {
+ Channel channel = channelDataManager.getChannel(r.getChannelId());
+ if (channel == null) {
+ continue;
+ }
+ sb.append(channel.getDisplayName())
+ .append(" : ")
+ .append(DateFormat.getDateTimeInstance().format(new Date(r.getStartTimeMs())))
+ .append("\n");
+ }
+ String title = getResources().getString(R.string.dvr_epg_conflict_dialog_title);
+ String description = sb.toString();
+ return new Guidance(title, description, null, null);
+ }
+
+ @Override
+ public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
+ Activity activity = getActivity();
+ actions.add(new GuidedAction.Builder(activity)
+ .id(DVR_EPG_RECORD)
+ .title(getResources().getString(R.string.dvr_epg_record))
+ .build());
+ actions.add(new GuidedAction.Builder(activity)
+ .id(DVR_EPG_NOT_RECORD)
+ .title(getResources().getString(R.string.dvr_epg_do_not_record))
+ .build());
+ }
+
+ @Override
+ public void onGuidedActionClicked(GuidedAction action) {
+ Program program = getEntry().program;
+ if (action.getId() == DVR_EPG_RECORD) {
+ getDvrManager().addSchedule(program, mConflicts);
+ }
+ dismissDialog();
+ }
+}
diff --git a/src/com/android/tv/dvr/ui/DvrRecordDeleteFragment.java b/src/com/android/tv/dvr/ui/DvrRecordDeleteFragment.java
new file mode 100644
index 00000000..d4d5cc41
--- /dev/null
+++ b/src/com/android/tv/dvr/ui/DvrRecordDeleteFragment.java
@@ -0,0 +1,48 @@
+package com.android.tv.dvr.ui;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
+import android.support.v17.leanback.widget.GuidedAction;
+
+import com.android.tv.R;
+import com.android.tv.guide.ProgramManager.TableEntry;
+
+import java.util.List;
+
+public class DvrRecordDeleteFragment extends DvrGuidedStepFragment {
+ private static final int ACTION_DELETE_YES = 1;
+ private static final int ACTION_DELETE_NO = 2;
+
+ public DvrRecordDeleteFragment(TableEntry entry) {
+ super(entry);
+ }
+
+ @Override
+ public Guidance onCreateGuidance(Bundle savedInstanceState) {
+ String title = getResources().getString(R.string.epg_dvr_dialog_message_delete_schedule);
+ return new Guidance(title, null, null, null);
+ }
+
+ @Override
+ public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
+ Activity activity = getActivity();
+ actions.add(new GuidedAction.Builder(activity)
+ .id(ACTION_DELETE_YES)
+ .title(getResources().getString(android.R.string.yes))
+ .build());
+ actions.add(new GuidedAction.Builder(activity)
+ .id(ACTION_DELETE_NO)
+ .title(getResources().getString(android.R.string.no))
+ .build());
+ }
+
+ @Override
+ public void onGuidedActionClicked(GuidedAction action) {
+ if (action.getId() == ACTION_DELETE_YES) {
+ getDvrManager().removeScheduledRecording(getEntry().scheduledRecording);
+ }
+ dismissDialog();
+ }
+}
diff --git a/src/com/android/tv/dvr/ui/DvrRecordScheduleFragment.java b/src/com/android/tv/dvr/ui/DvrRecordScheduleFragment.java
new file mode 100644
index 00000000..77e78ccc
--- /dev/null
+++ b/src/com/android/tv/dvr/ui/DvrRecordScheduleFragment.java
@@ -0,0 +1,70 @@
+package com.android.tv.dvr.ui;
+
+import android.app.Activity;
+import android.app.FragmentManager;
+import android.os.Bundle;
+
+import android.support.v17.leanback.app.GuidedStepFragment;
+import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
+import android.support.v17.leanback.widget.GuidedAction;
+
+import com.android.tv.data.Program;
+import com.android.tv.dialog.SafeDismissDialogFragment;
+import com.android.tv.dvr.ScheduledRecording;
+import com.android.tv.guide.ProgramManager.TableEntry;
+import com.android.tv.MainActivity;
+import com.android.tv.R;
+
+import java.util.List;
+
+public class DvrRecordScheduleFragment extends DvrGuidedStepFragment {
+ private static final int ACTION_RECORD_YES = 1;
+ private static final int ACTION_RECORD_NO = 2;
+
+ public DvrRecordScheduleFragment(TableEntry entry) {
+ super(entry);
+ }
+
+ @Override
+ public Guidance onCreateGuidance(Bundle savedInstanceState) {
+ String title = getResources().getString(R.string.epg_dvr_dialog_message_schedule_recording);
+ return new Guidance(title, null, null, null);
+ }
+
+ @Override
+ public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
+ Activity activity = getActivity();
+ actions.add(new GuidedAction.Builder(activity)
+ .id(ACTION_RECORD_YES)
+ .title(getResources().getString(android.R.string.yes))
+ .build());
+ actions.add(new GuidedAction.Builder(activity)
+ .id(ACTION_RECORD_NO)
+ .title(getResources().getString(android.R.string.no))
+ .build());
+ }
+
+ @Override
+ public void onGuidedActionClicked(GuidedAction action) {
+ TableEntry entry = getEntry();
+ Program program = entry.program;
+ final List<ScheduledRecording> conflicts =
+ getDvrManager().getScheduledRecordingsThatConflict(program);
+ if (action.getId() == ACTION_RECORD_YES) {
+ if (conflicts.isEmpty()) {
+ getDvrManager().addSchedule(program, conflicts);
+ dismissDialog();
+ } else {
+ DvrRecordConflictFragment dvrConflict = new DvrRecordConflictFragment(entry);
+ SafeDismissDialogFragment currentDialog =
+ ((MainActivity) getActivity()).getOverlayManager().getCurrentDialog();
+ if (currentDialog instanceof DvrDialogFragment) {
+ FragmentManager fm = currentDialog.getChildFragmentManager();
+ GuidedStepFragment.add(fm, dvrConflict, R.id.halfsized_dialog_host);
+ }
+ }
+ } else if (action.getId() == ACTION_RECORD_NO) {
+ dismissDialog();
+ }
+ }
+}
diff --git a/src/com/android/tv/dvr/ui/EmptyHolder.java b/src/com/android/tv/dvr/ui/EmptyHolder.java
new file mode 100644
index 00000000..45cd3a36
--- /dev/null
+++ b/src/com/android/tv/dvr/ui/EmptyHolder.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.dvr.ui;
+
+/**
+ * Special object meaning a row is empty;
+ */
+final class EmptyHolder {
+ static final EmptyHolder EMPTY_HOLDER = new EmptyHolder();
+
+ private EmptyHolder() {
+ }
+}
diff --git a/src/com/android/tv/dvr/ui/EmptyItemPresenter.java b/src/com/android/tv/dvr/ui/EmptyItemPresenter.java
new file mode 100644
index 00000000..c0305128
--- /dev/null
+++ b/src/com/android/tv/dvr/ui/EmptyItemPresenter.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.dvr.ui;
+
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.support.v17.leanback.widget.Presenter;
+import android.view.Gravity;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.android.tv.R;
+import com.android.tv.TvApplication;
+import com.android.tv.data.ChannelDataManager;
+import com.android.tv.util.Utils;
+
+/**
+ * Shows the item "NONE". Used for rows with now items.
+ */
+public class EmptyItemPresenter extends Presenter {
+
+ private final DvrBrowseFragment mMainFragment;
+
+ public EmptyItemPresenter(DvrBrowseFragment mainFragment) {
+ mMainFragment = mainFragment;
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent) {
+ TextView view = new TextView(parent.getContext());
+ Resources resources = view.getResources();
+ view.setLayoutParams(new ViewGroup.LayoutParams(
+ resources.getDimensionPixelSize(R.dimen.dvr_card_layout_width),
+ resources.getDimensionPixelSize(R.dimen.dvr_card_layout_width)));
+ view.setFocusable(true);
+ view.setFocusableInTouchMode(true);
+ view.setBackgroundColor(
+ Utils.getColor(mMainFragment.getResources(), R.color.setup_background));
+ view.setTextColor(Color.WHITE);
+ view.setGravity(Gravity.CENTER);
+ return new ViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder viewHolder, Object recording) {
+ ((TextView) viewHolder.view).setText(
+ viewHolder.view.getContext().getString(R.string.dvr_msg_no_recording_on_the_row));
+ }
+
+ @Override
+ public void onUnbindViewHolder(ViewHolder viewHolder) { }
+}
diff --git a/src/com/android/tv/dvr/ui/GridItemPresenter.java b/src/com/android/tv/dvr/ui/GridItemPresenter.java
deleted file mode 100644
index 099816d4..00000000
--- a/src/com/android/tv/dvr/ui/GridItemPresenter.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tv.dvr.ui;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.graphics.Color;
-import android.support.v17.leanback.widget.Presenter;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.tv.MainActivity;
-import com.android.tv.R;
-import com.android.tv.TvApplication;
-import com.android.tv.data.Channel;
-import com.android.tv.data.Program;
-import com.android.tv.dvr.DvrManager;
-import com.android.tv.dvr.Recording;
-import com.android.tv.util.Utils;
-
-import java.util.List;
-
-public class GridItemPresenter extends Presenter {
- private static final int GRID_ITEM_WIDTH = 200;
- private static final int GRID_ITEM_HEIGHT = 200;
-
- private final DvrBrowseFragment mainFragment;
-
- public GridItemPresenter(DvrBrowseFragment mainFragment) {
- this.mainFragment = mainFragment;
- }
-
- @Override
- public ViewHolder onCreateViewHolder(ViewGroup parent) {
- TextView view = new TextView(parent.getContext());
- view.setLayoutParams(new ViewGroup.LayoutParams(GRID_ITEM_WIDTH, GRID_ITEM_HEIGHT));
- view.setFocusable(true);
- view.setFocusableInTouchMode(true);
- view.setBackgroundColor(
- Utils.getColor(mainFragment.getResources(), R.color.setup_background));
- view.setTextColor(Color.WHITE);
- view.setGravity(Gravity.CENTER);
- return new ViewHolder(view);
- }
-
- @Override
- public void onBindViewHolder(ViewHolder viewHolder, Object recording) {
- if (recording == null) {
- ((TextView) viewHolder.view).setText(viewHolder.view.getContext()
- .getString(R.string.dvr_msg_no_recording_on_the_row));
- } else {
- final Recording r = (Recording) recording;
- StringBuilder sb = new StringBuilder();
- List<Program> programs = r.getPrograms();
- if (programs != null && programs.size() > 0) {
- sb.append(programs.get(0).getTitle());
- } else {
- sb.append(viewHolder.view.getContext()
- .getString(R.string.dvr_msg_program_title_unknown));
- }
- sb.append(" ");
- Channel channel = r.getChannel();
- if (channel != null) {
- sb.append(channel.getDisplayName());
- } else {
- sb.append(viewHolder.view.getContext().getString(R.string.dvr_msg_channel_unknown));
- }
- sb.append(" ").append(Utils.toIsoDateTimeString(r.getStartTimeMs()));
- ((TextView) viewHolder.view).setText(sb.toString());
- final Context context = viewHolder.view.getContext();
- viewHolder.view.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- switch (r.getState()) {
- case Recording.STATE_RECORDING_NOT_STARTED: {
- new AlertDialog.Builder(context)
- .setNegativeButton(R.string.dvr_detail_cancel,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Toast.makeText(context, "Not implemented yet",
- Toast.LENGTH_SHORT).show();
- }
- })
- .show();
- break;
- }
- case Recording.STATE_RECORDING_IN_PROGRESS: {
- new AlertDialog.Builder(context)
- .setNegativeButton(R.string.dvr_detail_stop_delete,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Toast.makeText(context, "Not implemented yet",
- Toast.LENGTH_SHORT).show();
- }
- })
- .setPositiveButton(R.string.dvr_detail_stop_keep,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Toast.makeText(context, "Not implemented yet",
- Toast.LENGTH_SHORT).show();
- }
- })
- .show();
- break;
- }
- case Recording.STATE_RECORDING_FINISHED: {
- new AlertDialog.Builder(context)
- .setNegativeButton(R.string.dvr_detail_delete,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- DvrManager dvrManager = TvApplication
- .getSingletons(mainFragment.getContext())
- .getDvrManager();
- // TODO(DVR) handle success/failure.
- dvrManager.removeRecording(r);
- }
- })
- .setPositiveButton(R.string.dvr_detail_play,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Intent intent = new Intent(context, MainActivity.class);
- intent.putExtra(Utils.EXTRA_KEY_RECORDING_URI,
- r.getUri());
- context.startActivity(intent);
- ((Activity) context).finish();
- }
- })
- .show();
- break;
- }
- }
- }
- });
- }
- }
-
- @Override
- public void onUnbindViewHolder(ViewHolder viewHolder) {
- }
-} \ No newline at end of file
diff --git a/src/com/android/tv/dvr/ui/HalfSizedDialogFragment.java b/src/com/android/tv/dvr/ui/HalfSizedDialogFragment.java
new file mode 100644
index 00000000..dc89a8e0
--- /dev/null
+++ b/src/com/android/tv/dvr/ui/HalfSizedDialogFragment.java
@@ -0,0 +1,30 @@
+package com.android.tv.dvr.ui;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.tv.dialog.SafeDismissDialogFragment;
+import com.android.tv.R;
+
+public class HalfSizedDialogFragment extends SafeDismissDialogFragment {
+ public static final String DIALOG_TAG = HalfSizedDialogFragment.class.getSimpleName();
+ public static final String TRACKER_LABEL = "Half sized dialog";
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.halfsized_dialog, null);
+ }
+
+ @Override
+ public int getTheme() {
+ return R.style.Theme_TV_dialog_HalfSizedDialog;
+ }
+
+ @Override
+ public String getTrackerLabel() {
+ return TRACKER_LABEL;
+ }
+}
diff --git a/src/com/android/tv/dvr/ui/RecordedProgramPresenter.java b/src/com/android/tv/dvr/ui/RecordedProgramPresenter.java
new file mode 100644
index 00000000..0b656bdc
--- /dev/null
+++ b/src/com/android/tv/dvr/ui/RecordedProgramPresenter.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.dvr.ui;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.media.tv.TvContract;
+import android.support.v17.leanback.widget.Presenter;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.List;
+
+import com.android.tv.MainActivity;
+import com.android.tv.R;
+import com.android.tv.TvApplication;
+import com.android.tv.common.recording.RecordedProgram;
+import com.android.tv.data.Channel;
+import com.android.tv.data.ChannelDataManager;
+import com.android.tv.dvr.DvrManager;
+import com.android.tv.ui.DialogUtils;
+import com.android.tv.util.Utils;
+
+/**
+ * Presents a {@link RecordedProgram} in the {@link DvrBrowseFragment}.
+ */
+public class RecordedProgramPresenter extends Presenter {
+ private final ChannelDataManager mChannelDataManager;
+
+ public RecordedProgramPresenter(Context context) {
+ mChannelDataManager = TvApplication.getSingletons(context).getChannelDataManager();
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent) {
+ Context context = parent.getContext();
+ RecordingCardView view = new RecordingCardView(context);
+ return new ViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder viewHolder, Object o) {
+ final RecordedProgram recording = (RecordedProgram) o;
+ final RecordingCardView cardView = (RecordingCardView) viewHolder.view;
+ final Context context = viewHolder.view.getContext();
+ final Resources resources = context.getResources();
+
+ Channel channel = mChannelDataManager.getChannel(recording.getChannelId());
+
+ if (!TextUtils.isEmpty(recording.getTitle())) {
+ cardView.setTitle(recording.getTitle());
+ } else {
+ cardView.setTitle(resources.getString(R.string.dvr_msg_program_title_unknown));
+ }
+ if (recording.getPosterArt() != null) {
+ cardView.setImageUri(recording.getPosterArt());
+ } else if (recording.getThumbnail() != null) {
+ cardView.setImageUri(recording.getThumbnail());
+ } else {
+ if (channel != null) {
+ cardView.setImageUri(TvContract.buildChannelLogoUri(channel.getId()).toString());
+ }
+ }
+ cardView.setContent(Utils.getDurationString(context, recording.getStartTimeUtcMillis(),
+ recording.getEndTimeUtcMillis(), true));
+ //TODO: replace with a detail card
+ viewHolder.view.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ DialogUtils.showListDialog(v.getContext(),
+ new int[] { R.string.dvr_detail_play, R.string.dvr_detail_delete },
+ new Runnable[] {
+ new Runnable() {
+ @Override
+ public void run() {
+ Intent intent = new Intent(context, MainActivity.class);
+ intent.putExtra(Utils.EXTRA_KEY_RECORDING_URI,
+ recording.getUri());
+ context.startActivity(intent);
+ ((Activity) context).finish();
+ }
+ },
+ new Runnable() {
+ @Override
+ public void run() {
+ DvrManager dvrManager = TvApplication
+ .getSingletons(context).getDvrManager();
+ dvrManager.removeRecordedProgram(recording);
+ }
+ },
+ });
+ }
+ });
+
+ }
+
+ @Override
+ public void onUnbindViewHolder(ViewHolder viewHolder) {
+ final RecordingCardView cardView = (RecordingCardView) viewHolder.view;
+ cardView.reset();
+ }
+}
diff --git a/src/com/android/tv/dvr/ui/RecordedProgramsAdapter.java b/src/com/android/tv/dvr/ui/RecordedProgramsAdapter.java
new file mode 100644
index 00000000..eeb26041
--- /dev/null
+++ b/src/com/android/tv/dvr/ui/RecordedProgramsAdapter.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.dvr.ui;
+
+import android.support.v17.leanback.widget.PresenterSelector;
+
+import com.android.tv.common.recording.RecordedProgram;
+import com.android.tv.dvr.DvrDataManager;
+
+/**
+ * Adapter for {@link RecordedProgram}.
+ */
+final class RecordedProgramsAdapter extends SortedArrayAdapter<RecordedProgram>
+ implements DvrDataManager.RecordedProgramListener {
+ private final DvrDataManager mDataManager;
+
+ RecordedProgramsAdapter(DvrDataManager dataManager, PresenterSelector presenterSelector) {
+ super(presenterSelector, RecordedProgram.START_TIME_THEN_ID_COMPARATOR);
+ mDataManager = dataManager;
+ }
+
+ public void start() {
+ clear();
+ addAll(mDataManager.getRecordedPrograms());
+ mDataManager.addRecordedProgramListener(this);
+ }
+
+ public void stop() {
+ mDataManager.removeRecordedProgramListener(this);
+ }
+
+ @Override
+ long getId(RecordedProgram item) {
+ return item.getId();
+ }
+
+ @Override // DvrDataManager.RecordedProgramListener
+ public void onRecordedProgramAdded(RecordedProgram recordedProgram) {
+ add(recordedProgram);
+ }
+
+ @Override // DvrDataManager.RecordedProgramListener
+ public void onRecordedProgramChanged(RecordedProgram recordedProgram) {
+ change(recordedProgram);
+ }
+
+ @Override // DvrDataManager.RecordedProgramListener
+ public void onRecordedProgramRemoved(RecordedProgram recordedProgram) {
+ remove(recordedProgram);
+ }
+}
diff --git a/src/com/android/tv/dvr/ui/RecordingCardView.java b/src/com/android/tv/dvr/ui/RecordingCardView.java
new file mode 100644
index 00000000..def11248
--- /dev/null
+++ b/src/com/android/tv/dvr/ui/RecordingCardView.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.dvr.ui;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.support.annotation.Nullable;
+import android.support.v17.leanback.widget.BaseCardView;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.tv.R;
+import com.android.tv.util.ImageLoader;
+
+/**
+ * A CardView for displaying info about a {@link com.android.tv.dvr.ScheduledRecording} or
+ * {@link com.android.tv.common.recording.RecordedProgram}
+ */
+class RecordingCardView extends BaseCardView {
+ private final ImageView mImageView;
+ private final int mImageWidth;
+ private final int mImageHeight;
+ private String mImageUri;
+ private final TextView mTitleView;
+ private final TextView mContentView;
+ private final Drawable mDefaultImage;
+
+ RecordingCardView(Context context) {
+ super(context);
+ //TODO(dvr): move these to the layout XML.
+ setCardType(BaseCardView.CARD_TYPE_INFO_UNDER_WITH_EXTRA);
+ setFocusable(true);
+ setFocusableInTouchMode(true);
+ mDefaultImage = getResources().getDrawable(R.drawable.default_now_card, null);
+
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ inflater.inflate(R.layout.dvr_recording_card_view, this);
+
+ mImageView = (ImageView) findViewById(R.id.image);
+ mImageWidth = getResources().getDimensionPixelSize(R.dimen.dvr_card_image_layout_width);
+ mImageHeight = getResources().getDimensionPixelSize(R.dimen.dvr_card_image_layout_width);
+ mTitleView = (TextView) findViewById(R.id.title);
+ mContentView = (TextView) findViewById(R.id.content);
+ }
+
+ void setTitle(CharSequence title) {
+ mTitleView.setText(title);
+ }
+
+ void setContent(CharSequence content) {
+ mContentView.setText(content);
+ }
+
+ void setImageUri(String uri) {
+ mImageUri = uri;
+ if (TextUtils.isEmpty(uri)) {
+ mImageView.setImageDrawable(mDefaultImage);
+ } else {
+ ImageLoader.loadBitmap(getContext(), uri, mImageWidth, mImageHeight,
+ new RecordingCardImageLoaderCallback(this, uri));
+ }
+ }
+
+ public void setImageUri(Uri uri) {
+ if (uri != null) {
+ setImageUri(uri.toString());
+ } else {
+ setImageUri("");
+ }
+ }
+
+ private static class RecordingCardImageLoaderCallback
+ extends ImageLoader.ImageLoaderCallback<RecordingCardView> {
+ private final String mUri;
+
+ RecordingCardImageLoaderCallback(RecordingCardView referent, String uri) {
+ super(referent);
+ mUri = uri;
+ }
+
+ @Override
+ public void onBitmapLoaded(RecordingCardView view, @Nullable Bitmap bitmap) {
+ if (bitmap == null || !mUri.equals(view.mImageUri)) {
+ view.mImageView.setImageDrawable(view.mDefaultImage);
+ } else {
+ view.mImageView.setImageDrawable(new BitmapDrawable(view.getResources(), bitmap));
+ }
+ }
+ }
+
+ public void reset() {
+ mTitleView.setText("");
+ mContentView.setText("");
+ mImageView.setImageDrawable(mDefaultImage);
+ }
+}
diff --git a/src/com/android/tv/dvr/ui/ScheduledRecordingPresenter.java b/src/com/android/tv/dvr/ui/ScheduledRecordingPresenter.java
new file mode 100644
index 00000000..533a4882
--- /dev/null
+++ b/src/com/android/tv/dvr/ui/ScheduledRecordingPresenter.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.dvr.ui;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.media.tv.TvContract;
+import android.support.annotation.Nullable;
+import android.support.v17.leanback.widget.Presenter;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import com.android.tv.ApplicationSingletons;
+import com.android.tv.R;
+import com.android.tv.TvApplication;
+import com.android.tv.data.Channel;
+import com.android.tv.data.ChannelDataManager;
+import com.android.tv.data.Program;
+import com.android.tv.data.ProgramDataManager;
+import com.android.tv.dvr.DvrManager;
+import com.android.tv.dvr.ScheduledRecording;
+import com.android.tv.util.Utils;
+
+/**
+ * Presents a {@link ScheduledRecording} in the {@link DvrBrowseFragment}.
+ */
+public class ScheduledRecordingPresenter extends Presenter {
+ private final ChannelDataManager mChannelDataManager;
+
+ private static final class ScheduledRecordingViewHolder extends ViewHolder {
+ private ProgramDataManager.QueryProgramTask mQueryProgramTask;
+
+ ScheduledRecordingViewHolder(RecordingCardView view) {
+ super(view);
+ }
+ }
+
+ public ScheduledRecordingPresenter(Context context) {
+ ApplicationSingletons singletons = TvApplication.getSingletons(context);
+ mChannelDataManager = singletons.getChannelDataManager();
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent) {
+ Context context = parent.getContext();
+ RecordingCardView view = new RecordingCardView(context);
+ return new ScheduledRecordingViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder baseHolder, Object o) {
+ ScheduledRecordingViewHolder viewHolder = (ScheduledRecordingViewHolder) baseHolder;
+ final ScheduledRecording recording = (ScheduledRecording) o;
+ final RecordingCardView cardView = (RecordingCardView) viewHolder.view;
+ final Context context = viewHolder.view.getContext();
+
+ long programId = recording.getProgramId();
+ if (programId == ScheduledRecording.ID_NOT_SET) {
+ setTitleAndImage(cardView, recording, null);
+ } else {
+ viewHolder.mQueryProgramTask = new ProgramDataManager.QueryProgramTask(
+ context.getContentResolver(), programId) {
+ @Override
+ protected void onPostExecute(Program program) {
+ super.onPostExecute(program);
+ setTitleAndImage(cardView, recording, program);
+ }
+ };
+ viewHolder.mQueryProgramTask.executeOnDbThread();
+
+ }
+ cardView.setContent(Utils.getDurationString(context, recording.getStartTimeMs(),
+ recording.getEndTimeMs(), true));
+ //TODO: replace with a detail card
+ View.OnClickListener clickListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ switch (recording.getState()) {
+ case ScheduledRecording.STATE_RECORDING_NOT_STARTED: {
+ showScheduledRecordingDialog(v.getContext(), recording);
+ break;
+ }
+ case ScheduledRecording.STATE_RECORDING_IN_PROGRESS: {
+ showCurrentlyRecordingDialog(v.getContext(), recording);
+ break;
+ }
+ }
+ }
+ };
+ baseHolder.view.setOnClickListener(clickListener);
+ }
+
+ private void setTitleAndImage(RecordingCardView cardView, ScheduledRecording recording,
+ @Nullable Program program) {
+ if (program != null) {
+ cardView.setTitle(program.getTitle());
+ cardView.setImageUri(program.getPosterArtUri());
+ } else {
+ cardView.setTitle(
+ cardView.getResources().getString(R.string.dvr_msg_program_title_unknown));
+ Channel channel = mChannelDataManager.getChannel(recording.getChannelId());
+ if (channel != null) {
+ cardView.setImageUri(TvContract.buildChannelLogoUri(channel.getId()).toString());
+ }
+ }
+ }
+
+ @Override
+ public void onUnbindViewHolder(ViewHolder baseHolder) {
+ ScheduledRecordingViewHolder viewHolder = (ScheduledRecordingViewHolder) baseHolder;
+ final RecordingCardView cardView = (RecordingCardView) viewHolder.view;
+ if (viewHolder.mQueryProgramTask != null) {
+ viewHolder.mQueryProgramTask.cancel(true);
+ viewHolder.mQueryProgramTask = null;
+ }
+ cardView.reset();
+ }
+
+ private void showScheduledRecordingDialog(final Context context,
+ final ScheduledRecording recording) {
+ DialogInterface.OnClickListener removeScheduleListener
+ = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // TODO(DVR) handle success/failure.
+ DvrManager dvrManager = TvApplication.getSingletons(context)
+ .getDvrManager();
+ dvrManager.removeScheduledRecording((ScheduledRecording) recording);
+ }
+ };
+ new AlertDialog.Builder(context)
+ .setMessage(R.string.epg_dvr_dialog_message_remove_recording_schedule)
+ .setNegativeButton(android.R.string.no, null)
+ .setPositiveButton(android.R.string.yes, removeScheduleListener)
+ .show();
+ }
+
+ private void showCurrentlyRecordingDialog(final Context context,
+ final ScheduledRecording recording) {
+ DialogInterface.OnClickListener stopRecordingListener
+ = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ DvrManager dvrManager = TvApplication.getSingletons(context)
+ .getDvrManager();
+ dvrManager.stopRecording((ScheduledRecording) recording);
+ }
+ };
+ new AlertDialog.Builder(context)
+ .setMessage(R.string.epg_dvr_dialog_message_stop_recording)
+ .setNegativeButton(android.R.string.no, null)
+ .setPositiveButton(android.R.string.yes, stopRecordingListener)
+ .show();
+ }
+}
diff --git a/src/com/android/tv/dvr/ui/ScheduledRecordingsAdapter.java b/src/com/android/tv/dvr/ui/ScheduledRecordingsAdapter.java
new file mode 100644
index 00000000..65955276
--- /dev/null
+++ b/src/com/android/tv/dvr/ui/ScheduledRecordingsAdapter.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.dvr.ui;
+
+import android.support.v17.leanback.widget.PresenterSelector;
+
+import com.android.tv.dvr.DvrDataManager;
+import com.android.tv.dvr.ScheduledRecording;
+
+/**
+ * Adapter for {@link ScheduledRecording} filtered by
+ * {@link com.android.tv.dvr.ScheduledRecording.RecordingState}.
+ */
+final class ScheduledRecordingsAdapter extends SortedArrayAdapter<ScheduledRecording>
+ implements DvrDataManager.ScheduledRecordingListener {
+ private final int mState;
+ private final DvrDataManager mDataManager;
+
+ ScheduledRecordingsAdapter(DvrDataManager dataManager, int state,
+ PresenterSelector presenterSelector) {
+ super(presenterSelector, ScheduledRecording.START_TIME_THEN_PRIORITY_COMPARATOR);
+ mDataManager = dataManager;
+ mState = state;
+ }
+
+ public void start() {
+ clear();
+ switch (mState) {
+ case ScheduledRecording.STATE_RECORDING_NOT_STARTED:
+ addAll(mDataManager.getNonStartedScheduledRecordings());
+ break;
+ case ScheduledRecording.STATE_RECORDING_IN_PROGRESS:
+ addAll(mDataManager.getStartedRecordings());
+ break;
+ default:
+ throw new IllegalStateException("Unknown recording state " + mState);
+
+ }
+ mDataManager.addScheduledRecordingListener(this);
+ }
+
+ public void stop() {
+ mDataManager.removeScheduledRecordingListener(this);
+ }
+
+ @Override
+ long getId(ScheduledRecording item) {
+ return item.getId();
+ }
+
+ @Override //DvrDataManager.ScheduledRecordingListener
+ public void onScheduledRecordingAdded(ScheduledRecording scheduledRecording) {
+ if (scheduledRecording.getState() == mState) {
+ add(scheduledRecording);
+ }
+ }
+
+ @Override //DvrDataManager.ScheduledRecordingListener
+ public void onScheduledRecordingRemoved(ScheduledRecording scheduledRecording) {
+ remove(scheduledRecording);
+ }
+
+ @Override //DvrDataManager.ScheduledRecordingListener
+ public void onScheduledRecordingStatusChanged(ScheduledRecording scheduledRecording) {
+ if (scheduledRecording.getState() == mState) {
+ change(scheduledRecording);
+ } else {
+ remove(scheduledRecording);
+ }
+ }
+}
diff --git a/src/com/android/tv/dvr/ui/SortedArrayAdapter.java b/src/com/android/tv/dvr/ui/SortedArrayAdapter.java
new file mode 100644
index 00000000..8a8bcdeb
--- /dev/null
+++ b/src/com/android/tv/dvr/ui/SortedArrayAdapter.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.dvr.ui;
+
+import android.support.v17.leanback.widget.ObjectAdapter;
+import android.support.v17.leanback.widget.PresenterSelector;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Keeps a set of {@code T} items sorted, but leaving a {@link EmptyHolder}
+ * if there is no items.
+ *
+ * <p>{@code T} must have stable IDs.
+ */
+abstract class SortedArrayAdapter<T> extends ObjectAdapter {
+ private final List<T> mItems = new ArrayList<>();
+ private final Comparator<T> mComparator;
+
+ SortedArrayAdapter(PresenterSelector presenterSelector, Comparator<T> comparator) {
+ super(presenterSelector);
+ mComparator = comparator;
+ setHasStableIds(true);
+ }
+
+ @Override
+ public final int size() {
+ return mItems.isEmpty() ? 1 : mItems.size();
+ }
+
+ @Override
+ public final Object get(int position) {
+ return isEmpty() ? EmptyHolder.EMPTY_HOLDER : getItem(position);
+ }
+
+ @Override
+ public final long getId(int position) {
+ if (isEmpty()) {
+ return NO_ID;
+ }
+ T item = mItems.get(position);
+ return item == null ? NO_ID : getId(item);
+ }
+
+ /**
+ * Returns the id of the the given {@code item}.
+ *
+ * The id must be stable.
+ */
+ abstract long getId(T item);
+
+ /**
+ * Returns the item at the given {@code position}.
+ *
+ * @throws IndexOutOfBoundsException if the position is out of range
+ * (<tt>position &lt; 0 || position &gt;= size()</tt>)
+ */
+ final T getItem(int position) {
+ return mItems.get(position);
+ }
+
+ /**
+ * Returns {@code true} if the list of items is empty.
+ *
+ * <p><b>NOTE</b> when the item list is empty the adapter has a size of 1 and
+ * {@link EmptyHolder#EMPTY_HOLDER} at position 0;
+ */
+ final boolean isEmpty() {
+ return mItems.isEmpty();
+ }
+
+ /**
+ * Removes all elements from the list.
+ *
+ * <p><b>NOTE</b> when the item list is empty the adapter has a size of 1 and
+ * {@link EmptyHolder#EMPTY_HOLDER} at position 0;
+ */
+ final void clear() {
+ mItems.clear();
+ notifyChanged();
+ }
+
+ /**
+ * Adds the objects in the given collection to the adapter keeping the elements sorted.
+ * If the index is >= {@link #size} an exception will be thrown.
+ *
+ * @param items A {@link Collection} of items to insert.
+ */
+ final void addAll(Collection<T> items) {
+ mItems.addAll(items);
+ Collections.sort(mItems, mComparator);
+ notifyChanged();
+ }
+
+ /**
+ * Adds an item in sorted order to the adapter.
+ *
+ * @param item The item to add in sorted order to the adapter.
+ */
+ final void add(T item) {
+ int i = findWhereToInsert(item);
+ mItems.add(i, item);
+ if (mItems.size() == 1) {
+ notifyItemRangeChanged(0, 1);
+ } else {
+ notifyItemRangeInserted(i, 1);
+ }
+ }
+
+ /**
+ * Remove an item from the list
+ *
+ * @param item The item to remove from the adapter.
+ */
+ final void remove(T item) {
+ int index = indexOf(item);
+ if (index != -1) {
+ mItems.remove(index);
+ if (mItems.isEmpty()) {
+ notifyItemRangeChanged(0, 1);
+ } else {
+ notifyItemRangeRemoved(index, 1);
+ }
+ }
+ }
+
+ /**
+ * Change an item in the list.
+ * @param item The item to change.
+ */
+ final void change(T item) {
+ int oldIndex = indexOf(item);
+ if (oldIndex != -1) {
+ T old = mItems.get(oldIndex);
+ if (mComparator.compare(old, item) == 0) {
+ mItems.set(oldIndex, item);
+ notifyItemRangeChanged(oldIndex, 1);
+ return;
+ }
+ mItems.remove(oldIndex);
+ }
+ int newIndex = findWhereToInsert(item);
+ mItems.add(newIndex, item);
+
+ if (oldIndex != -1) {
+ notifyItemRangeRemoved(oldIndex, 1);
+ }
+ if (newIndex != -1) {
+ notifyItemRangeInserted(newIndex, 1);
+ }
+ }
+
+ private int indexOf(T item) {
+ long id = getId(item);
+ for (int i = 0; i < mItems.size(); i++) {
+ T r = mItems.get(i);
+ if (getId(r) == id) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private int findWhereToInsert(T item) {
+ int i;
+ int size = mItems.size();
+ for (i = 0; i < size; i++) {
+ T r = mItems.get(i);
+ if (mComparator.compare(r, item) > 0) {
+ return i;
+ }
+ }
+ return size;
+ }
+}
diff --git a/src/com/android/tv/guide/ProgramGrid.java b/src/com/android/tv/guide/ProgramGrid.java
index 1339ddf8..77de5827 100644
--- a/src/com/android/tv/guide/ProgramGrid.java
+++ b/src/com/android/tv/guide/ProgramGrid.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
import android.support.v17.leanback.widget.VerticalGridView;
+import android.support.v7.widget.RecyclerView.LayoutManager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
@@ -66,6 +67,16 @@ public class ProgramGrid extends VerticalGridView {
}
};
+ private final ViewTreeObserver.OnPreDrawListener mPreDrawListener =
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ getViewTreeObserver().removeOnPreDrawListener(this);
+ updateInputLogo();
+ return true;
+ }
+ };
+
private ProgramManager mProgramManager;
private View mNextFocusByUpDown;
@@ -83,7 +94,7 @@ public class ProgramGrid extends VerticalGridView {
private boolean mKeepCurrentProgram;
private ChildFocusListener mChildFocusListener;
- private OnRepeatedKeyInterceptListener mOnRepeatedKeyInterceptListener;
+ private final OnRepeatedKeyInterceptListener mOnRepeatedKeyInterceptListener;
interface ChildFocusListener {
/**
@@ -332,6 +343,10 @@ public class ProgramGrid extends VerticalGridView {
return contains((View) v.getParent());
}
+ public void onItemSelectionReset() {
+ getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
+ }
+
@Override
public boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
if (mLastFocusedView != null && mLastFocusedView.isShown()) {
@@ -359,6 +374,49 @@ public class ProgramGrid extends VerticalGridView {
int maxY = (mSelectionRow + 1) * mRowHeight + mDetailHeight;
if (y > maxY) scrollBy(0, y - maxY);
}
+ updateInputLogo();
+ }
+
+ @Override
+ public void onViewRemoved(View view) {
+ // It is required to ensure input logo showing when the scroll is moved to most bottom.
+ updateInputLogo();
+ }
+
+ private int getFirstVisibleChildIndex() {
+ final LayoutManager mLayoutManager = getLayoutManager();
+ int top = mLayoutManager.getPaddingTop();
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View childView = getChildAt(i);
+ int childTop = mLayoutManager.getDecoratedTop(childView);
+ int childBottom = mLayoutManager.getDecoratedBottom(childView);
+ if ((childTop + childBottom) / 2 > top) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public void updateInputLogo() {
+ int childCount = getChildCount();
+ if (childCount == 0) {
+ return;
+ }
+ int firstVisibleChildIndex = getFirstVisibleChildIndex();
+ if (firstVisibleChildIndex == -1) {
+ return;
+ }
+ View childView = getChildAt(firstVisibleChildIndex);
+ int childAdapterPosition = getChildAdapterPosition(childView);
+ ((ProgramTableAdapter.ProgramRowHolder) getChildViewHolder(childView))
+ .updateInputLogo(childAdapterPosition, true);
+ for (int i = firstVisibleChildIndex + 1; i < childCount; i++) {
+ childView = getChildAt(i);
+ ((ProgramTableAdapter.ProgramRowHolder) getChildViewHolder(childView))
+ .updateInputLogo(childAdapterPosition, false);
+ childAdapterPosition = getChildAdapterPosition(childView);
+ }
}
private static void findFocusables(View v, ArrayList<View> outFocusable) {
diff --git a/src/com/android/tv/guide/ProgramGuide.java b/src/com/android/tv/guide/ProgramGuide.java
index 77a1146b..bfcb8b0d 100644
--- a/src/com/android/tv/guide/ProgramGuide.java
+++ b/src/com/android/tv/guide/ProgramGuide.java
@@ -31,6 +31,7 @@ import android.os.Message;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.v17.leanback.widget.OnChildSelectedListener;
import android.support.v17.leanback.widget.SearchOrbView;
import android.support.v17.leanback.widget.VerticalGridView;
@@ -52,6 +53,7 @@ import com.android.tv.common.WeakHandler;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.GenreItems;
import com.android.tv.data.ProgramDataManager;
+import com.android.tv.dvr.DvrDataManager;
import com.android.tv.ui.HardwareLayerAnimatorListenerAdapter;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.Utils;
@@ -160,12 +162,11 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
public ProgramGuide(MainActivity activity, ChannelTuner channelTuner,
TvInputManagerHelper tvInputManagerHelper, ChannelDataManager channelDataManager,
- ProgramDataManager programDataManager, Tracker tracker, Runnable preShowRunnable,
- Runnable postHideRunnable) {
+ ProgramDataManager programDataManager, @Nullable DvrDataManager dvrDataManager,
+ Tracker tracker, Runnable preShowRunnable, Runnable postHideRunnable) {
mActivity = activity;
- mProgramManager = new ProgramManager(tvInputManagerHelper,
- channelDataManager,
- programDataManager);
+ mProgramManager = new ProgramManager(tvInputManagerHelper, channelDataManager,
+ programDataManager, dvrDataManager);
mChannelTuner = channelTuner;
mTracker = tracker;
mPreShowRunnable = preShowRunnable;
@@ -245,7 +246,7 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
mTimelineRow.setAdapter(mTimeListAdapter);
ProgramTableAdapter programTableAdapter = new ProgramTableAdapter(mActivity,
- tvInputManagerHelper, mProgramManager, this);
+ mProgramManager, this);
programTableAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override
public void onChanged() {
@@ -590,7 +591,10 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
return mTimelineRow.getScrollOffset();
}
- private void cancelHide() {
+ /**
+ * Cancel hiding the program guide.
+ */
+ public void cancelHide() {
mHandler.removeCallbacks(mHideRunnable);
}
@@ -720,6 +724,7 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
Math.max(mProgramManager.getChannelIndex(mChannelTuner.getCurrentChannel()),
0));
mGrid.resetFocusState();
+ mGrid.onItemSelectionReset();
mIsDuringResetRowSelection = false;
}
diff --git a/src/com/android/tv/guide/ProgramItemView.java b/src/com/android/tv/guide/ProgramItemView.java
index 09a93037..172ee070 100644
--- a/src/com/android/tv/guide/ProgramItemView.java
+++ b/src/com/android/tv/guide/ProgramItemView.java
@@ -16,9 +16,7 @@
package com.android.tv.guide;
-import android.app.AlertDialog;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
@@ -26,6 +24,7 @@ import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.StateListDrawable;
import android.os.Handler;
import android.os.SystemClock;
+import android.support.v4.os.BuildCompat;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
@@ -43,15 +42,14 @@ import com.android.tv.TvApplication;
import com.android.tv.analytics.Tracker;
import com.android.tv.common.feature.CommonFeatures;
import com.android.tv.data.Channel;
-import com.android.tv.data.Program;
import com.android.tv.dvr.DvrManager;
-import com.android.tv.dvr.Recording;
+import com.android.tv.dvr.ui.DvrDialogFragment;
+import com.android.tv.dvr.ui.DvrRecordDeleteFragment;
+import com.android.tv.dvr.ui.DvrRecordScheduleFragment;
import com.android.tv.guide.ProgramManager.TableEntry;
import com.android.tv.util.Utils;
import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.List;
import java.util.concurrent.TimeUnit;
public class ProgramItemView extends TextView {
@@ -60,9 +58,6 @@ public class ProgramItemView extends TextView {
private static final long FOCUS_UPDATE_FREQUENCY = TimeUnit.SECONDS.toMillis(1);
private static final int MAX_PROGRESS = 10000; // From android.widget.ProgressBar.MAX_VALUE
- private static final int ACTION_RECORD_PROGRAM = 100;
- private static final int ACTION_RECORD_SEASON = 101;
-
// State indicating the focused program is the current program
private static final int[] STATE_CURRENT_PROGRAM = { R.attr.state_current_program };
@@ -89,6 +84,10 @@ public class ProgramItemView extends TextView {
@Override
public void onClick(final View view) {
TableEntry entry = ((ProgramItemView) view).mTableEntry;
+ if (entry == null) {
+ //do nothing
+ return;
+ }
ApplicationSingletons singletons = TvApplication.getSingletons(view.getContext());
Tracker tracker = singletons.getTracker();
tracker.sendEpgItemClicked();
@@ -105,78 +104,38 @@ public class ProgramItemView extends TextView {
}, entry.getWidth() > ((ProgramItemView) view).mMaxWidthForRipple ? 0
: view.getResources()
.getInteger(R.integer.program_guide_ripple_anim_duration));
- } else if (CommonFeatures.DVR.isEnabled(view.getContext())) {
+ } else if (CommonFeatures.DVR.isEnabled(view.getContext()) && BuildCompat
+ .isAtLeastN()) {
final MainActivity tvActivity = (MainActivity) view.getContext();
final DvrManager dvrManager = singletons.getDvrManager();
final Channel channel = tvActivity.getChannelDataManager()
.getChannel(entry.channelId);
- if (dvrManager.canRecord(channel.getInputId())) {
- showDvrDialog(view, entry, dvrManager);
+ if (dvrManager.canRecord(channel.getInputId()) && entry.program != null) {
+ if (entry.scheduledRecording == null) {
+ showDvrDialog(view, entry);
+ } else {
+ showRecordDeleteDialog(view, entry);
+ }
}
}
}
- private void showDvrDialog(final View view, TableEntry entry, final DvrManager dvrManager) {
- List<CharSequence> items = new ArrayList<>();
- final List<Integer> actions = new ArrayList<>();
- // TODO: the items can be changed by the state of the program. For example,
- // if the program is already added in scheduler, we need to show an item to
- // delete the recording schedule.
- items.add(view.getResources().getString(R.string.epg_dvr_record_program));
- actions.add(ACTION_RECORD_PROGRAM);
- items.add(view.getResources().getString(R.string.epg_dvr_record_season));
- actions.add(ACTION_RECORD_SEASON);
-
- final Program program = entry.program;
- final List<Recording> conflicts = dvrManager
- .getScheduledRecordingsThatConflict(program);
- // TODO: it is a tentative UI. Don't publish the UI.
- DialogInterface.OnClickListener onClickListener
- = new DialogInterface.OnClickListener() {
- @Override
- public void onClick(final DialogInterface dialog, int which) {
- if (actions.get(which) == ACTION_RECORD_PROGRAM) {
- if (conflicts.isEmpty()) {
- dvrManager.addSchedule(program, conflicts);
- } else {
- showConflictDialog(view, dvrManager, program, conflicts);
- }
- } else if (actions.get(which) == ACTION_RECORD_SEASON) {
- dvrManager.addSeasonSchedule(program);
- }
- dialog.dismiss();
- }
- };
- new AlertDialog.Builder(view.getContext())
- .setItems(items.toArray(new CharSequence[items.size()]), onClickListener)
- .create()
- .show();
+ private void showDvrDialog(final View view, TableEntry entry) {
+ Utils.showToastMessageForDeveloperFeature(view.getContext());
+ DvrRecordScheduleFragment dvrRecordScheduleFragment =
+ new DvrRecordScheduleFragment(entry);
+ DvrDialogFragment dvrDialogFragment = new DvrDialogFragment(dvrRecordScheduleFragment);
+ ((MainActivity) view.getContext()).getOverlayManager().showDialogFragment(
+ DvrDialogFragment.DIALOG_TAG, dvrDialogFragment, true, true);
}
- };
- private static void showConflictDialog(final View view, final DvrManager dvrManager,
- final Program program, final List<Recording> conflicts) {
- DialogInterface.OnClickListener conflictClickListener
- = new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (which == AlertDialog.BUTTON_POSITIVE) {
- dvrManager.addSchedule(program, conflicts);
- dialog.dismiss();
- }
- }
- };
- StringBuilder sb = new StringBuilder();
- for (Recording r : conflicts) {
- sb.append(r.toString()).append('\n');
+ private void showRecordDeleteDialog(final View view, final TableEntry entry) {
+ DvrRecordDeleteFragment recordDeleteDialogFragment = new DvrRecordDeleteFragment(entry);
+ DvrDialogFragment dvrDialogFragment = new DvrDialogFragment(recordDeleteDialogFragment);
+ ((MainActivity) view.getContext()).getOverlayManager().showDialogFragment(
+ DvrDialogFragment.DIALOG_TAG, dvrDialogFragment, true, true);
}
- new AlertDialog.Builder(view.getContext()).setTitle(R.string.dvr_epg_conflict_dialog_title)
- .setMessage(sb.toString())
- .setPositiveButton(R.string.dvr_epg_record, conflictClickListener)
- .setNegativeButton(R.string.dvr_epg_do_not_record, conflictClickListener)
- .create()
- .show();
- }
+ };
private static final View.OnFocusChangeListener ON_FOCUS_CHANGED =
new View.OnFocusChangeListener() {
@@ -198,6 +157,10 @@ public class ProgramItemView extends TextView {
public void run() {
refreshDrawableState();
TableEntry entry = mTableEntry;
+ if (entry == null) {
+ //do nothing
+ return;
+ }
if (entry.isCurrentProgram()) {
Drawable background = getBackground();
int progress = getProgress(entry.entryStartUtcMillis, entry.entryEndUtcMillis);
@@ -220,6 +183,8 @@ public class ProgramItemView extends TextView {
public ProgramItemView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+ setOnClickListener(ON_CLICKED);
+ setOnFocusChangeListener(ON_FOCUS_CHANGED);
}
private void initIfNeeded() {
@@ -282,11 +247,9 @@ public class ProgramItemView extends TextView {
return mTableEntry;
}
- public void onBind(TableEntry entry, ProgramListAdapter adapter) {
+ public void setValues(TableEntry entry, int selectedGenreId, long fromUtcMillis,
+ long toUtcMillis, String gapTitle) {
mTableEntry = entry;
- setOnClickListener(ON_CLICKED);
- setOnFocusChangeListener(ON_FOCUS_CHANGED);
- ProgramManager programManager = adapter.getProgramManager();
ViewGroup.LayoutParams layoutParams = getLayoutParams();
layoutParams.width = entry.getWidth();
@@ -303,16 +266,19 @@ public class ProgramItemView extends TextView {
setText(null);
} else {
if (entry.isGap()) {
- if (entry.isBlocked()) {
- title = adapter.getBlockedProgramTitle();
- } else {
- title = adapter.getNoInfoProgramTitle();
- }
+ title = gapTitle;
episode = null;
- } else if (entry.hasGenre(programManager.getSelectedGenreId())) {
+ } else if (entry.hasGenre(selectedGenreId)) {
titleStyle = sProgramTitleStyle;
episodeStyle = sEpisodeTitleStyle;
}
+ if (TextUtils.isEmpty(title)) {
+ title = getResources().getString(R.string.program_title_for_no_information);
+ }
+ if (mTableEntry.scheduledRecording != null) {
+ //TODO(dvr): use a proper icon for UI status.
+ title = "®" + title;
+ }
SpannableStringBuilder description = new SpannableStringBuilder();
description.append(title);
@@ -340,12 +306,11 @@ public class ProgramItemView extends TextView {
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
mTextWidth = getMeasuredWidth() - getPaddingStart() - getPaddingEnd();
int start = GuideUtils.convertMillisToPixel(entry.entryStartUtcMillis);
- int guideStart = GuideUtils.convertMillisToPixel(programManager.getFromUtcMillis());
+ int guideStart = GuideUtils.convertMillisToPixel(fromUtcMillis);
layoutVisibleArea(guideStart - start);
// Maximum width for us to use a ripple
- mMaxWidthForRipple = GuideUtils.convertMillisToPixel(
- programManager.getFromUtcMillis(), programManager.getToUtcMillis());
+ mMaxWidthForRipple = GuideUtils.convertMillisToPixel(fromUtcMillis, toUtcMillis);
}
/**
@@ -374,14 +339,13 @@ public class ProgramItemView extends TextView {
}
}
- public void onUnbind() {
+ public void clearValues() {
if (getHandler() != null) {
getHandler().removeCallbacks(mUpdateFocus);
}
setTag(null);
- setOnFocusChangeListener(null);
- setOnClickListener(null);
+ mTableEntry = null;
}
private static int getProgress(long start, long end) {
diff --git a/src/com/android/tv/guide/ProgramListAdapter.java b/src/com/android/tv/guide/ProgramListAdapter.java
index 88ba435e..03aea5ad 100644
--- a/src/com/android/tv/guide/ProgramListAdapter.java
+++ b/src/com/android/tv/guide/ProgramListAdapter.java
@@ -16,7 +16,6 @@
package com.android.tv.guide;
-import android.content.Context;
import android.content.res.Resources;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
@@ -33,30 +32,24 @@ import com.android.tv.guide.ProgramManager.TableEntry;
* Adapts a program list for a specific channel from {@link ProgramManager} to a row of the program
* guide table.
*/
-public class ProgramListAdapter extends
- RecyclerView.Adapter<ProgramListAdapter.ProgramViewHolder> implements
- TableEntriesUpdatedListener {
+public class ProgramListAdapter extends RecyclerView.Adapter<ProgramListAdapter.ProgramViewHolder>
+ implements TableEntriesUpdatedListener {
private static final String TAG = "ProgramListAdapter";
private static final boolean DEBUG = false;
- private final String mNoInfoProgramTitle;
- private final String mBlockedProgramTitle;
-
private final ProgramManager mProgramManager;
private final int mChannelIndex;
+ private final String mNoInfoProgramTitle;
+ private final String mBlockedProgramTitle;
private long mChannelId;
- public ProgramListAdapter(Context context, ProgramManager programManager,
- int channelIndex) {
- Resources res = context.getResources();
- mNoInfoProgramTitle = res.getString(
- R.string.program_title_for_no_information);
- mBlockedProgramTitle = res.getString(
- R.string.program_title_for_blocked_channel);
-
+ public ProgramListAdapter(Resources res, ProgramManager programManager, int channelIndex) {
+ setHasStableIds(true);
mProgramManager = programManager;
mChannelIndex = channelIndex;
+ mNoInfoProgramTitle = res.getString(R.string.program_title_for_no_information);
+ mBlockedProgramTitle = res.getString(R.string.program_title_for_blocked_channel);
onTableEntriesUpdated();
}
@@ -76,14 +69,6 @@ public class ProgramListAdapter extends
return mProgramManager;
}
- public String getNoInfoProgramTitle() {
- return mNoInfoProgramTitle;
- }
-
- public String getBlockedProgramTitle() {
- return mBlockedProgramTitle;
- }
-
@Override
public int getItemCount() {
return mProgramManager.getTableEntryCount(mChannelId);
@@ -95,8 +80,15 @@ public class ProgramListAdapter extends
}
@Override
+ public long getItemId(int position) {
+ return mProgramManager.getTableEntry(mChannelId, position).getId();
+ }
+
+ @Override
public void onBindViewHolder(ProgramViewHolder holder, int position) {
- holder.onBind(mProgramManager.getTableEntry(mChannelId, position), this);
+ TableEntry tableEntry = mProgramManager.getTableEntry(mChannelId, position);
+ String gapTitle = tableEntry.isBlocked() ? mBlockedProgramTitle : mNoInfoProgramTitle;
+ holder.onBind(tableEntry, this.getProgramManager(), gapTitle);
}
@Override
@@ -116,16 +108,16 @@ public class ProgramListAdapter extends
super(itemView);
}
- public void onBind(TableEntry entry, ProgramListAdapter adapter) {
+ public void onBind(TableEntry entry, ProgramManager programManager, String gapTitle) {
if (DEBUG) {
Log.d(TAG, "onBind. View = " + itemView + ", Entry = " + entry);
}
-
- ((ProgramItemView) itemView).onBind(entry, adapter);
+ ((ProgramItemView) itemView).setValues(entry, programManager.getSelectedGenreId(),
+ programManager.getFromUtcMillis(), programManager.getToUtcMillis(), gapTitle);
}
public void onUnbind() {
- ((ProgramItemView) itemView).onUnbind();
+ ((ProgramItemView) itemView).clearValues();
}
}
}
diff --git a/src/com/android/tv/guide/ProgramManager.java b/src/com/android/tv/guide/ProgramManager.java
index df52abbe..fe1a981f 100644
--- a/src/com/android/tv/guide/ProgramManager.java
+++ b/src/com/android/tv/guide/ProgramManager.java
@@ -17,14 +17,17 @@
package com.android.tv.guide;
import android.support.annotation.MainThread;
+import android.support.annotation.Nullable;
+import android.util.ArraySet;
import android.util.Log;
-import com.android.tv.common.CollectionUtils;
import com.android.tv.data.Channel;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.GenreItems;
import com.android.tv.data.Program;
import com.android.tv.data.ProgramDataManager;
+import com.android.tv.dvr.DvrDataManager;
+import com.android.tv.dvr.ScheduledRecording;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.Utils;
@@ -55,6 +58,7 @@ public class ProgramManager {
private final TvInputManagerHelper mTvInputManagerHelper;
private final ChannelDataManager mChannelDataManager;
private final ProgramDataManager mProgramDataManager;
+ private final DvrDataManager mDvrDataManager; // Only set if DVR is enabled
private long mStartUtcMillis;
private long mEndUtcMillis;
@@ -74,6 +78,8 @@ public class ProgramManager {
/** Program corresponding to the entry. {@code null} means that this entry is a gap. */
public final Program program;
+ public final ScheduledRecording scheduledRecording;
+
/** Start time of entry in UTC milliseconds. */
public final long entryStartUtcMillis;
@@ -82,34 +88,39 @@ public class ProgramManager {
private final boolean mIsBlocked;
- private TableEntry(long startUtcMillis, long endUtcMillis) {
- this(INVALID_ID, null, startUtcMillis, endUtcMillis, false);
- }
-
private TableEntry(long channelId, long startUtcMillis, long endUtcMillis) {
this(channelId, null, startUtcMillis, endUtcMillis, false);
}
private TableEntry(long channelId, long startUtcMillis, long endUtcMillis,
boolean blocked) {
- this(channelId, null, startUtcMillis, endUtcMillis, blocked);
+ this(channelId, null, null, startUtcMillis, endUtcMillis, blocked);
}
- private TableEntry(long channelId, Program program,
- long entryStartUtcMillis, long entryEndUtcMillis) {
- this(channelId, program, entryStartUtcMillis, entryEndUtcMillis, false);
+ private TableEntry(long channelId, Program program, long entryStartUtcMillis,
+ long entryEndUtcMillis, boolean isBlocked) {
+ this(channelId, program, null, entryStartUtcMillis, entryEndUtcMillis, isBlocked);
}
- private TableEntry(long channelId, Program program,
+ private TableEntry(long channelId, Program program, ScheduledRecording scheduledRecording,
long entryStartUtcMillis, long entryEndUtcMillis, boolean isBlocked) {
this.channelId = channelId;
this.program = program;
+ this.scheduledRecording = scheduledRecording;
this.entryStartUtcMillis = entryStartUtcMillis;
this.entryEndUtcMillis = entryEndUtcMillis;
mIsBlocked = isBlocked;
}
/**
+ * A stable id useful for {@link android.support.v7.widget.RecyclerView.Adapter}.
+ */
+ public long getId() {
+ // using a negative entryEndUtcMillis keeps it from conflicting with program Id
+ return program != null ? program.getId() : -entryEndUtcMillis;
+ }
+
+ /**
* Returns true if this is a gap.
*/
public boolean isGap() {
@@ -167,9 +178,10 @@ public class ProgramManager {
// Should be matched with mSelectedGenreId always.
private List<Channel> mFilteredChannels = mChannels;
- private final Set<Listener> mListeners = CollectionUtils.createSmallSet();
- private final Set<TableEntriesUpdatedListener> mTableEntriesUpdatedListeners = CollectionUtils
- .createSmallSet();
+ private final Set<Listener> mListeners = new ArraySet<>();
+ private final Set<TableEntriesUpdatedListener> mTableEntriesUpdatedListeners = new ArraySet<>();
+
+ private final Set<TableEntryChangedListener> mTableEntryChangedListeners = new ArraySet<>();
private final ChannelDataManager.Listener mChannelDataManagerListener =
new ChannelDataManager.Listener() {
@@ -197,12 +209,49 @@ public class ProgramManager {
}
};
+ private final DvrDataManager.ScheduledRecordingListener mScheduledRecordingListener =
+ new DvrDataManager.ScheduledRecordingListener() {
+ @Override
+ public void onScheduledRecordingAdded(ScheduledRecording scheduledRecording) {
+ TableEntry oldEntry = getTableEntry(scheduledRecording);
+ if (oldEntry != null) {
+ TableEntry newEntry = new TableEntry(oldEntry.channelId, oldEntry.program,
+ scheduledRecording, oldEntry.entryStartUtcMillis,
+ oldEntry.entryEndUtcMillis, oldEntry.isBlocked());
+ updateEntry(oldEntry, newEntry);
+ }
+ }
+
+ @Override
+ public void onScheduledRecordingRemoved(ScheduledRecording scheduledRecording) {
+ TableEntry oldEntry = getTableEntry(scheduledRecording);
+ if (oldEntry != null) {
+ TableEntry newEntry = new TableEntry(oldEntry.channelId, oldEntry.program, null,
+ oldEntry.entryStartUtcMillis, oldEntry.entryEndUtcMillis,
+ oldEntry.isBlocked());
+ updateEntry(oldEntry, newEntry);
+ }
+ }
+
+ @Override
+ public void onScheduledRecordingStatusChanged(ScheduledRecording scheduledRecording) {
+ TableEntry oldEntry = getTableEntry(scheduledRecording);
+ if (oldEntry != null) {
+ TableEntry newEntry = new TableEntry(oldEntry.channelId, oldEntry.program,
+ scheduledRecording, oldEntry.entryStartUtcMillis,
+ oldEntry.entryEndUtcMillis, oldEntry.isBlocked());
+ updateEntry(oldEntry, newEntry);
+ }
+ }
+ };
+
public ProgramManager(TvInputManagerHelper tvInputManagerHelper,
- ChannelDataManager channelDataManager,
- ProgramDataManager programDataManager) {
+ ChannelDataManager channelDataManager, ProgramDataManager programDataManager,
+ @Nullable DvrDataManager dvrDataManager) {
mTvInputManagerHelper = tvInputManagerHelper;
mChannelDataManager = channelDataManager;
mProgramDataManager = programDataManager;
+ mDvrDataManager = dvrDataManager;
}
public void programGuideVisibilityChanged(boolean visible) {
@@ -210,41 +259,61 @@ public class ProgramManager {
if (visible) {
mChannelDataManager.addListener(mChannelDataManagerListener);
mProgramDataManager.addListener(mProgramDataManagerListener);
+ if (mDvrDataManager != null) {
+ mDvrDataManager.addScheduledRecordingListener(mScheduledRecordingListener);
+ }
} else {
mChannelDataManager.removeListener(mChannelDataManagerListener);
mProgramDataManager.removeListener(mProgramDataManagerListener);
+ if (mDvrDataManager != null) {
+ mDvrDataManager.removeScheduledRecordingListener(mScheduledRecordingListener);
+ }
}
}
/**
- * Add a {@link Listener}.
+ * Adds a {@link Listener}.
*/
public void addListener(Listener listener) {
mListeners.add(listener);
}
/**
- * Register a listener to be invoked when table entries are updated.
+ * Registers a listener to be invoked when table entries are updated.
*/
public void addTableEntriesUpdatedListener(TableEntriesUpdatedListener listener) {
mTableEntriesUpdatedListeners.add(listener);
}
/**
- * Remove a {@link Listener}.
+ * Registers a listener to be invoked when a table entry is changed.
+ */
+ public void addTableEntryChangedListener(TableEntryChangedListener listener) {
+ mTableEntryChangedListeners.add(listener);
+ }
+
+ /**
+ * Removes a {@link Listener}.
*/
public void removeListener(Listener listener) {
mListeners.remove(listener);
}
/**
- * Remove a previously installed table entries update listener.
+ * Removes a previously installed table entries update listener.
*/
public void removeTableEntriesUpdatedListener(TableEntriesUpdatedListener listener) {
mTableEntriesUpdatedListeners.remove(listener);
}
/**
+ * Removes a previously installed table entry changed listener.
+ */
+ public void removeTableEntryChangedListener(TableEntryChangedListener listener) {
+ mTableEntryChangedListeners.remove(listener);
+ }
+
+ /**
* Build genre filters based on the current programs.
* This categories channels by its current program's canonical genres
* and subsequent @{link resetChannelListWithGenre(int)} calls will reset channel list
@@ -366,6 +435,7 @@ public class ProgramManager {
} else if (lastEntry.entryEndUtcMillis == Long.MAX_VALUE) {
entries.remove(entries.size() - 1);
entries.add(new TableEntry(lastEntry.channelId, lastEntry.program,
+ lastEntry.scheduledRecording,
lastEntry.entryStartUtcMillis, mEndUtcMillis,
lastEntry.mIsBlocked));
}
@@ -403,6 +473,37 @@ public class ProgramManager {
}
}
+ private void notifyTableEntryUpdated(TableEntry entry) {
+ for (TableEntryChangedListener listener : mTableEntryChangedListeners) {
+ listener.onTableEntryChanged(entry);
+ }
+ }
+
+ private void updateEntry(TableEntry old, TableEntry newEntry) {
+ List<TableEntry> entries = mChannelIdEntriesMap.get(old.channelId);
+ int index = entries.indexOf(old);
+ entries.set(index, newEntry);
+ notifyTableEntryUpdated(newEntry);
+ }
+
+ @Nullable
+ private TableEntry getTableEntry(ScheduledRecording scheduledRecording) {
+ return getTableEntry(scheduledRecording.getChannelId(), scheduledRecording.getProgramId());
+ }
+
+ @Nullable
+ private TableEntry getTableEntry(long channelId, long entryId) {
+ List<TableEntry> entries = mChannelIdEntriesMap.get(channelId);
+ if (entries != null) {
+ for (TableEntry entry : entries) {
+ if (entry.getId() == entryId) {
+ return entry;
+ }
+ }
+ }
+ return null;
+ }
+
/**
* Returns the start time of currently managed time range, in UTC millisecond.
*/
@@ -471,6 +572,14 @@ public class ProgramManager {
}
/**
+ * Returns the index of channel with {@code channelId} within the currently managed channels.
+ * Returns -1 if such a channel is not found.
+ */
+ public int getChannelIndex(long channelId) {
+ return getChannelIndex(mChannelDataManager.getChannel(channelId));
+ }
+
+ /**
* Returns the number of "entries", which lies within the currently managed time range, for a
* given {@code channelId}.
*/
@@ -511,8 +620,10 @@ public class ProgramManager {
lastProgramEndTime = programStartTime;
}
if (programEndTime > lastProgramEndTime) {
- entries.add(new TableEntry(channelId, program, lastProgramEndTime,
- programEndTime));
+ ScheduledRecording scheduledRecording = mDvrDataManager == null ? null
+ : mDvrDataManager.getScheduledRecordingForProgramId(program.getId());
+ entries.add(new TableEntry(channelId, program, scheduledRecording,
+ lastProgramEndTime, programEndTime, false));
lastProgramEndTime = programEndTime;
}
}
@@ -525,7 +636,8 @@ public class ProgramManager {
// the first entry from UI perspective. So we clip it out.
entries.remove(0);
entries.set(0, new TableEntry(secondEntry.channelId, secondEntry.program,
- mStartUtcMillis, secondEntry.entryEndUtcMillis));
+ secondEntry.scheduledRecording, mStartUtcMillis,
+ secondEntry.entryEndUtcMillis, secondEntry.mIsBlocked));
}
}
return entries;
@@ -555,6 +667,10 @@ public class ProgramManager {
void onTableEntriesUpdated();
}
+ public interface TableEntryChangedListener {
+ void onTableEntryChanged(TableEntry entry);
+ }
+
public static class ListenerAdapter implements Listener {
@Override
public void onGenresUpdated() { }
@@ -598,9 +714,24 @@ public class ProgramManager {
}
/**
- * Returns the program index of the program at {@code time}.
+ * Returns the program index of the program with {@code entryId} or -1 if not found.
+ */
+ public int getProgramIdIndex(long channelId, long entryId) {
+ List<TableEntry> entries = mChannelIdEntriesMap.get(channelId);
+ if (entries != null) {
+ for (int i = 0; i < entries.size(); i++) {
+ if (entries.get(i).getId() == entryId) {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the program index of the program at {@code time} or -1 if not found.
*/
- public int getProgramIndex(long channelId, long time) {
+ public int getProgramIndexAtTime(long channelId, long time) {
List<TableEntry> entries = mChannelIdEntriesMap.get(channelId);
for (int i = 0; i < entries.size(); ++i) {
TableEntry entry = entries.get(i);
diff --git a/src/com/android/tv/guide/ProgramRow.java b/src/com/android/tv/guide/ProgramRow.java
index 4f38b879..54b864db 100644
--- a/src/com/android/tv/guide/ProgramRow.java
+++ b/src/com/android/tv/guide/ProgramRow.java
@@ -306,7 +306,7 @@ public class ProgramRow extends TimelineGridView {
public void resetScroll(int scrollOffset) {
long startTime = GuideUtils.convertPixelToMillis(scrollOffset)
+ mProgramManager.getStartTime();
- int position = mChannel == null ? -1 : mProgramManager.getProgramIndex(
+ int position = mChannel == null ? -1 : mProgramManager.getProgramIndexAtTime(
mChannel.getId(), startTime);
if (position < 0) {
getLayoutManager().scrollToPosition(0);
diff --git a/src/com/android/tv/guide/ProgramTableAdapter.java b/src/com/android/tv/guide/ProgramTableAdapter.java
index a86c1332..83755b5f 100644
--- a/src/com/android/tv/guide/ProgramTableAdapter.java
+++ b/src/com/android/tv/guide/ProgramTableAdapter.java
@@ -26,7 +26,9 @@ import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.media.tv.TvContentRating;
+import android.media.tv.TvInputInfo;
import android.os.Handler;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.RecycledViewPool;
@@ -43,11 +45,15 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.android.tv.R;
+import com.android.tv.TvApplication;
import com.android.tv.data.Channel;
import com.android.tv.data.Program;
import com.android.tv.guide.ProgramManager.TableEntriesUpdatedListener;
import com.android.tv.parental.ParentalControlSettings;
import com.android.tv.ui.HardwareLayerAnimatorListenerAdapter;
+import com.android.tv.util.ImageCache;
+import com.android.tv.util.ImageLoader;
+import com.android.tv.util.ImageLoader.LoadTvInputLogoTask;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.Utils;
@@ -57,8 +63,8 @@ import java.util.List;
/**
* Adapts the {@link ProgramListAdapter} list to the body of the program guide table.
*/
-public class ProgramTableAdapter extends
- RecyclerView.Adapter<ProgramTableAdapter.ProgramRowHolder> {
+public class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.ProgramRowHolder>
+ implements ProgramManager.TableEntryChangedListener {
private static final String TAG = "ProgramTableAdapter";
private static final boolean DEBUG = false;
@@ -84,10 +90,10 @@ public class ProgramTableAdapter extends
private final int mDetailPadding;
private final TextAppearanceSpan mEpisodeTitleStyle;
- public ProgramTableAdapter(Context context, TvInputManagerHelper tvInputManagerHelper,
- ProgramManager programManager, ProgramGuide programGuide) {
+ public ProgramTableAdapter(Context context, ProgramManager programManager,
+ ProgramGuide programGuide) {
mContext = context;
- mTvInputManagerHelper = tvInputManagerHelper;
+ mTvInputManagerHelper = TvApplication.getSingletons(context).getTvInputManagerHelper();
mProgramManager = programManager;
mProgramGuide = programGuide;
@@ -140,6 +146,7 @@ public class ProgramTableAdapter extends
}
});
update();
+ mProgramManager.addTableEntryChangedListener(this);
}
private void update() {
@@ -149,7 +156,8 @@ public class ProgramTableAdapter extends
}
mProgramListAdapters.clear();
for (int i = 0; i < mProgramManager.getChannelCount(); i++) {
- ProgramListAdapter listAdapter = new ProgramListAdapter(mContext, mProgramManager, i);
+ ProgramListAdapter listAdapter = new ProgramListAdapter(mContext.getResources(),
+ mProgramManager, i);
mProgramManager.addTableEntriesUpdatedListener(listAdapter);
mProgramListAdapters.add(listAdapter);
}
@@ -179,6 +187,14 @@ public class ProgramTableAdapter extends
return new ProgramRowHolder(itemView);
}
+ @Override
+ public void onTableEntryChanged(ProgramManager.TableEntry tableEntry) {
+ int channelIndex = mProgramManager.getChannelIndex(tableEntry.channelId);
+ int pos = mProgramManager.getProgramIdIndex(tableEntry.channelId, tableEntry.getId());
+ if (DEBUG) Log.d(TAG, "update(" + channelIndex + ", " + pos + ")");
+ mProgramListAdapters.get(channelIndex).notifyItemChanged(pos, tableEntry);
+ }
+
// TODO: make it static
public class ProgramRowHolder extends RecyclerView.ViewHolder
implements ProgramRow.ChildFocusListener {
@@ -223,6 +239,9 @@ public class ProgramTableAdapter extends
private final TextView mChannelNameView;
private final ImageView mChannelLogoView;
private final ImageView mChannelBlockView;
+ private final ImageView mInputLogoView;
+
+ private boolean mIsInputLogoVisible;
public ProgramRowHolder(View itemView) {
super(itemView);
@@ -244,6 +263,7 @@ public class ProgramTableAdapter extends
mChannelNameView = (TextView) mContainer.findViewById(R.id.channel_name);
mChannelLogoView = (ImageView) mContainer.findViewById(R.id.channel_logo);
mChannelBlockView = (ImageView) mContainer.findViewById(R.id.channel_block);
+ mInputLogoView = (ImageView) mContainer.findViewById(R.id.input_logo);
}
public void onBind(int position) {
@@ -267,6 +287,8 @@ public class ProgramTableAdapter extends
if (DEBUG) Log.d(TAG, "onBindChannel " + channel);
mChannel = channel;
+ mInputLogoView.setVisibility(View.GONE);
+ mIsInputLogoVisible = false;
if (channel == null) {
mChannelNumberView.setVisibility(View.GONE);
mChannelNameView.setVisibility(View.GONE);
@@ -467,6 +489,43 @@ public class ProgramTableAdapter extends
}
}
+ /**
+ * Update tv input logo. It should be called when the visible child item in ProgramGrid
+ * changed.
+ */
+ public void updateInputLogo(int lastPosition, boolean forceShow) {
+ if (mChannel == null) {
+ mInputLogoView.setVisibility(View.GONE);
+ mIsInputLogoVisible = false;
+ return;
+ }
+
+ boolean showLogo = forceShow;
+ if (!showLogo) {
+ Channel lastChannel = mProgramManager.getChannel(lastPosition);
+ if (lastChannel == null
+ || !mChannel.getInputId().equals(lastChannel.getInputId())) {
+ showLogo = true;
+ }
+ }
+
+ if (showLogo) {
+ if (!mIsInputLogoVisible) {
+ mIsInputLogoVisible = true;
+ TvInputInfo info = mTvInputManagerHelper.getTvInputInfo(mChannel.getInputId());
+ if (info != null) {
+ LoadTvInputLogoTask task = new LoadTvInputLogoTask(
+ itemView.getContext(), ImageCache.getInstance(), info);
+ ImageLoader.loadBitmap(createTvInputLogoLoadedCallback(info, this), task);
+ }
+ }
+ } else {
+ mInputLogoView.setVisibility(View.GONE);
+ mInputLogoView.setImageDrawable(null);
+ mIsInputLogoVisible = false;
+ }
+ }
+
private void updateTextView(TextView textView, String text) {
if (!TextUtils.isEmpty(text)) {
textView.setVisibility(View.VISIBLE);
@@ -487,6 +546,14 @@ public class ProgramTableAdapter extends
mChannelLogoView.setVisibility(View.VISIBLE);
}
+ private void updateInputLogoInternal(@NonNull Bitmap tvInputLogo) {
+ if (!mIsInputLogoVisible) {
+ return;
+ }
+ mInputLogoView.setImageBitmap(tvInputLogo);
+ mInputLogoView.setVisibility(View.VISIBLE);
+ }
+
private void onHorizontalScrolled() {
if (mDetailInAnimator != null) {
mHandler.removeCallbacks(mDetailInStarter);
@@ -526,4 +593,17 @@ public class ProgramTableAdapter extends
}
};
}
+
+ private static ImageLoaderCallback<ProgramRowHolder> createTvInputLogoLoadedCallback(
+ final TvInputInfo info, ProgramRowHolder holder) {
+ return new ImageLoaderCallback<ProgramRowHolder>(holder) {
+ @Override
+ public void onBitmapLoaded(ProgramRowHolder holder, @Nullable Bitmap logo) {
+ if (logo != null && info.getId()
+ .equals(holder.mChannel.getInputId())) {
+ holder.updateInputLogoInternal(logo);
+ }
+ }
+ };
+ }
}
diff --git a/src/com/android/tv/guide/TimelineRow.java b/src/com/android/tv/guide/TimelineRow.java
index 891b14cd..3f0c8678 100644
--- a/src/com/android/tv/guide/TimelineRow.java
+++ b/src/com/android/tv/guide/TimelineRow.java
@@ -64,7 +64,9 @@ public class TimelineRow extends TimelineGridView {
public void onRtlPropertiesChanged(int layoutDirection) {
super.onRtlPropertiesChanged(layoutDirection);
// Reset scroll
- scrollTo(getScrollOffset(), false);
+ if (isAttachedToWindow()) {
+ scrollTo(getScrollOffset(), false);
+ }
}
@Override
diff --git a/src/com/android/tv/menu/ActionCardView.java b/src/com/android/tv/menu/ActionCardView.java
index 1848a3ce..2d72b06f 100644
--- a/src/com/android/tv/menu/ActionCardView.java
+++ b/src/com/android/tv/menu/ActionCardView.java
@@ -93,4 +93,7 @@ public class ActionCardView extends FrameLayout implements ItemListRowView.CardV
Log.d(TAG, "onDeselected: action=" + mLabelView.getText());
}
}
+
+ @Override
+ public void onRecycled() { }
}
diff --git a/src/com/android/tv/menu/BaseCardView.java b/src/com/android/tv/menu/BaseCardView.java
index 25d4e313..b4500dd1 100644
--- a/src/com/android/tv/menu/BaseCardView.java
+++ b/src/com/android/tv/menu/BaseCardView.java
@@ -85,6 +85,9 @@ public abstract class BaseCardView<T> extends LinearLayout implements ItemListRo
}
@Override
+ public void onRecycled() { }
+
+ @Override
public void onSelected() {
if (isAttachedToWindow() && getVisibility() == View.VISIBLE) {
startFocusAnimation(SCALE_FACTOR_1F);
diff --git a/src/com/android/tv/menu/ChannelsPosterPrefetcher.java b/src/com/android/tv/menu/ChannelsPosterPrefetcher.java
index 1e416e5b..f932d75d 100644
--- a/src/com/android/tv/menu/ChannelsPosterPrefetcher.java
+++ b/src/com/android/tv/menu/ChannelsPosterPrefetcher.java
@@ -25,11 +25,11 @@ import android.support.annotation.NonNull;
import android.util.Log;
import com.android.tv.R;
+import com.android.tv.common.SoftPreconditions;
import com.android.tv.common.WeakHandler;
import com.android.tv.data.Channel;
import com.android.tv.data.Program;
import com.android.tv.data.ProgramDataManager;
-import com.android.tv.util.SoftPreconditions;
import java.util.List;
diff --git a/src/com/android/tv/menu/ChannelsRowAdapter.java b/src/com/android/tv/menu/ChannelsRowAdapter.java
index 51867d0b..200f4ac0 100644
--- a/src/com/android/tv/menu/ChannelsRowAdapter.java
+++ b/src/com/android/tv/menu/ChannelsRowAdapter.java
@@ -18,7 +18,9 @@ package com.android.tv.menu;
import android.content.Context;
import android.content.Intent;
+import android.media.tv.TvInputInfo;
import android.os.Build;
+import android.support.v4.os.BuildCompat;
import android.view.View;
import com.android.tv.MainActivity;
@@ -29,6 +31,8 @@ import com.android.tv.common.feature.CommonFeatures;
import com.android.tv.data.Channel;
import com.android.tv.recommendation.Recommender;
import com.android.tv.util.SetupUtils;
+import com.android.tv.util.TvInputManagerHelper;
+import com.android.tv.util.Utils;
import java.util.ArrayList;
import java.util.List;
@@ -37,16 +41,16 @@ import java.util.List;
* An adapter of the Channels row.
*/
public class ChannelsRowAdapter extends ItemListRowView.ItemListAdapter<Channel> {
- // There are four special cards: guide, setup, dvr, applink.
- private static final int SIZE_OF_VIEW_TYPE = 4;
+ // There are four special cards: guide, setup, dvr, record, applink.
+ private static final int SIZE_OF_VIEW_TYPE = 5;
private final Context mContext;
private final Tracker mTracker;
private final Recommender mRecommender;
private final int mMaxCount;
private final int mMinCount;
- private boolean mShowDvrCard;
- private int[] mViewType = new int[SIZE_OF_VIEW_TYPE];
+ private final boolean mDvrFeatureEnabled;
+ private final int[] mViewType = new int[SIZE_OF_VIEW_TYPE];
private final View.OnClickListener mGuideOnClickListener = new View.OnClickListener() {
@Override
@@ -67,11 +71,28 @@ public class ChannelsRowAdapter extends ItemListRowView.ItemListAdapter<Channel>
private final View.OnClickListener mDvrOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
+ Utils.showToastMessageForDeveloperFeature(view.getContext());
mTracker.sendMenuClicked(R.string.channels_item_dvr);
getMainActivity().getOverlayManager().showDvrManager();
}
};
+ private final View.OnClickListener mRecordOnClickListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Utils.showToastMessageForDeveloperFeature(view.getContext());
+ RecordCardView v = ((RecordCardView) view);
+ boolean isRecording = v.isRecording();
+ mTracker.sendMenuClicked(isRecording ? R.string.channels_item_record_start
+ : R.string.channels_item_record_stop);
+ if (!isRecording) {
+ v.startRecording();
+ } else {
+ v.stopRecording();
+ }
+ }
+ };
+
private final View.OnClickListener mAppLinkOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
@@ -102,7 +123,7 @@ public class ChannelsRowAdapter extends ItemListRowView.ItemListAdapter<Channel>
mRecommender = recommender;
mMinCount = minCount;
mMaxCount = maxCount;
- mShowDvrCard = CommonFeatures.DVR.isEnabled(mContext);
+ mDvrFeatureEnabled = CommonFeatures.DVR.isEnabled(mContext) && BuildCompat.isAtLeastN();
}
@Override
@@ -131,6 +152,8 @@ public class ChannelsRowAdapter extends ItemListRowView.ItemListAdapter<Channel>
viewHolder.itemView.setOnClickListener(mAppLinkOnClickListener);
} else if (viewType == R.layout.menu_card_dvr) {
viewHolder.itemView.setOnClickListener(mDvrOnClickListener);
+ } else if (viewType == R.layout.menu_card_record) {
+ viewHolder.itemView.setOnClickListener(mRecordOnClickListener);
} else {
viewHolder.itemView.setTag(getItemList().get(position));
viewHolder.itemView.setOnClickListener(mChannelOnClickListener);
@@ -140,17 +163,33 @@ public class ChannelsRowAdapter extends ItemListRowView.ItemListAdapter<Channel>
@Override
public void update() {
List<Channel> channelList = new ArrayList<>();
- Channel dummyChannel = new Channel.Builder()
- .build();
+ Channel dummyChannel = new Channel.Builder().build();
// For guide item
channelList.add(dummyChannel);
// For setup item
- boolean showSetupCard = SetupUtils.getInstance(mContext)
- .hasNewInput(((MainActivity) mContext).getTvInputManagerHelper());
+ TvInputManagerHelper inputManager = TvApplication.getSingletons(mContext)
+ .getTvInputManagerHelper();
+ boolean showSetupCard = SetupUtils.getInstance(mContext).hasNewInput(inputManager);
Channel currentChannel = ((MainActivity) mContext).getCurrentChannel();
boolean showAppLinkCard = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& currentChannel != null
&& currentChannel.getAppLinkType(mContext) != Channel.APP_LINK_TYPE_NONE;
+ boolean showDvrCard = false;
+ boolean showRecordCard = false;
+ if (mDvrFeatureEnabled) {
+ for (TvInputInfo info : inputManager.getTvInputInfos(true, true)) {
+ if (info.canRecord()) {
+ showDvrCard = true;
+ break;
+ }
+ }
+ if (currentChannel != null && currentChannel.getInputId() != null) {
+ TvInputInfo inputInfo = inputManager.getTvInputInfo(currentChannel.getInputId());
+ if ((inputInfo.canRecord() && inputInfo.getTunerCount() > 1)) {
+ showRecordCard = true;
+ }
+ }
+ }
mViewType[0] = R.layout.menu_card_guide;
int index = 1;
@@ -158,10 +197,14 @@ public class ChannelsRowAdapter extends ItemListRowView.ItemListAdapter<Channel>
channelList.add(dummyChannel);
mViewType[index++] = R.layout.menu_card_setup;
}
- if (mShowDvrCard) {
+ if (showDvrCard) {
channelList.add(dummyChannel);
mViewType[index++] = R.layout.menu_card_dvr;
}
+ if (showRecordCard) {
+ channelList.add(currentChannel);
+ mViewType[index++] = R.layout.menu_card_record;
+ }
if (showAppLinkCard) {
channelList.add(currentChannel);
mViewType[index++] = R.layout.menu_card_app_link;
diff --git a/src/com/android/tv/menu/ItemListRowView.java b/src/com/android/tv/menu/ItemListRowView.java
index e9362a78..4919c595 100644
--- a/src/com/android/tv/menu/ItemListRowView.java
+++ b/src/com/android/tv/menu/ItemListRowView.java
@@ -41,6 +41,7 @@ public class ItemListRowView extends MenuRowView implements OnChildSelectedListe
public interface CardView<T> {
void onBind(T row, boolean selected);
+ void onRecycled();
void onSelected();
void onDeselected();
}
@@ -206,6 +207,13 @@ public class ItemListRowView extends MenuRowView implements OnChildSelectedListe
cardView.onBind(mItemList.get(position), cardView.equals(mItemListView.mSelectedCard));
}
+ @Override
+ public void onViewRecycled(MyViewHolder viewHolder) {
+ super.onViewRecycled(viewHolder);
+ CardView<T> cardView = (CardView<T>) viewHolder.itemView;
+ cardView.onRecycled();
+ }
+
public static class MyViewHolder extends RecyclerView.ViewHolder {
public MyViewHolder(View view) {
super(view);
diff --git a/src/com/android/tv/menu/Menu.java b/src/com/android/tv/menu/Menu.java
index 613e0d62..7bb0787e 100644
--- a/src/com/android/tv/menu/Menu.java
+++ b/src/com/android/tv/menu/Menu.java
@@ -56,7 +56,7 @@ public class Menu {
@IntDef({REASON_NONE, REASON_GUIDE, REASON_PLAY_CONTROLS_PLAY, REASON_PLAY_CONTROLS_PAUSE,
REASON_PLAY_CONTROLS_PLAY_PAUSE, REASON_PLAY_CONTROLS_REWIND,
REASON_PLAY_CONTROLS_FAST_FORWARD, REASON_PLAY_CONTROLS_JUMP_TO_PREVIOUS,
- REASON_PLAY_CONTROLS_JUMP_TO_NEXT})
+ REASON_PLAY_CONTROLS_JUMP_TO_NEXT, REASON_RECORDING_PLAYBACK})
public @interface MenuShowReason {}
public static final int REASON_NONE = 0;
public static final int REASON_GUIDE = 1;
@@ -67,6 +67,7 @@ public class Menu {
public static final int REASON_PLAY_CONTROLS_FAST_FORWARD = 6;
public static final int REASON_PLAY_CONTROLS_JUMP_TO_PREVIOUS = 7;
public static final int REASON_PLAY_CONTROLS_JUMP_TO_NEXT = 8;
+ public static final int REASON_RECORDING_PLAYBACK = 9;
private static final List<String> sRowIdListForReason = new ArrayList<>();
static {
@@ -79,6 +80,7 @@ public class Menu {
sRowIdListForReason.add(PlayControlsRow.ID); // REASON_PLAY_CONTROLS_FAST_FORWARD
sRowIdListForReason.add(PlayControlsRow.ID); // REASON_PLAY_CONTROLS_JUMP_TO_PREVIOUS
sRowIdListForReason.add(PlayControlsRow.ID); // REASON_PLAY_CONTROLS_JUMP_TO_NEXT
+ sRowIdListForReason.add(PlayControlsRow.ID); // REASON_RECORDING_PLAYBACK
}
private static final String SCREEN_NAME = "Menu";
diff --git a/src/com/android/tv/menu/MenuAction.java b/src/com/android/tv/menu/MenuAction.java
index b45e88c2..86153084 100644
--- a/src/com/android/tv/menu/MenuAction.java
+++ b/src/com/android/tv/menu/MenuAction.java
@@ -36,8 +36,11 @@ public class MenuAction {
public static final MenuAction SELECT_DISPLAY_MODE_ACTION =
new MenuAction(R.string.options_item_display_mode, TvOptionsManager.OPTION_DISPLAY_MODE,
R.drawable.ic_tvoption_aspect);
- public static final MenuAction PIP_ACTION =
- new MenuAction(R.string.options_item_pip, TvOptionsManager.OPTION_PIP,
+ public static final MenuAction PIP_IN_APP_ACTION =
+ new MenuAction(R.string.options_item_pip, TvOptionsManager.OPTION_IN_APP_PIP,
+ R.drawable.ic_tvoption_pip);
+ public static final MenuAction SYSTEMWIDE_PIP_ACTION =
+ new MenuAction(R.string.options_item_pip, TvOptionsManager.OPTION_SYSTEMWIDE_PIP,
R.drawable.ic_tvoption_pip);
public static final MenuAction SELECT_AUDIO_LANGUAGE_ACTION =
new MenuAction(R.string.options_item_multi_audio, TvOptionsManager.OPTION_MULTI_AUDIO,
diff --git a/src/com/android/tv/menu/MenuLayoutManager.java b/src/com/android/tv/menu/MenuLayoutManager.java
index 265ad840..1f377f54 100644
--- a/src/com/android/tv/menu/MenuLayoutManager.java
+++ b/src/com/android/tv/menu/MenuLayoutManager.java
@@ -35,7 +35,7 @@ import android.view.ViewGroup.MarginLayoutParams;
import android.widget.TextView;
import com.android.tv.R;
-import com.android.tv.util.SoftPreconditions;
+import com.android.tv.common.SoftPreconditions;
import com.android.tv.util.Utils;
import java.util.ArrayList;
@@ -318,6 +318,11 @@ public class MenuLayoutManager {
if (!indexValid) {
return;
}
+ MenuRow row = mMenuRows.get(position);
+ if (!row.isVisible()) {
+ Log.e(TAG, "Selecting invisible row: " + position);
+ return;
+ }
if (Utils.isIndexValid(mMenuRowViews, mSelectedPosition)) {
mMenuRowViews.get(mSelectedPosition).onDeselected();
}
@@ -360,6 +365,11 @@ public class MenuLayoutManager {
if (!newIndexValid) {
return;
}
+ MenuRow row = mMenuRows.get(position);
+ if (!row.isVisible()) {
+ Log.e(TAG, "Moving to the invisible row: " + position);
+ return;
+ }
if (mAnimatorSet != null) {
// Do not cancel the animation here. The property values should be set to the end values
// when the animation finishes.
@@ -787,9 +797,9 @@ public class MenuLayoutManager {
}
private static final class ViewPropertyValueHolder {
- public Property<View, Float> property;
- public View view;
- public float value;
+ public final Property<View, Float> property;
+ public final View view;
+ public final float value;
public ViewPropertyValueHolder(Property<View, Float> property, View view, float value) {
this.property = property;
diff --git a/src/com/android/tv/menu/MenuView.java b/src/com/android/tv/menu/MenuView.java
index df91ddf3..e012dfca 100644
--- a/src/com/android/tv/menu/MenuView.java
+++ b/src/com/android/tv/menu/MenuView.java
@@ -117,10 +117,11 @@ public class MenuView extends FrameLayout implements IMenuView {
}
initializeChildren();
update(true);
- if (rowIdToSelect == null) {
- rowIdToSelect = ChannelsRow.ID;
- }
int position = getItemPosition(rowIdToSelect);
+ if (position == -1 || !mMenuRows.get(position).isVisible()) {
+ // Channels row is always visible.
+ position = getItemPosition(ChannelsRow.ID);
+ }
setSelectedPosition(position);
// Change the visibility as late as possible to avoid the unnecessary animation.
setVisibility(VISIBLE);
diff --git a/src/com/android/tv/menu/PlayControlsRowView.java b/src/com/android/tv/menu/PlayControlsRowView.java
index f0853c40..058d5108 100644
--- a/src/com/android/tv/menu/PlayControlsRowView.java
+++ b/src/com/android/tv/menu/PlayControlsRowView.java
@@ -19,6 +19,7 @@ package com.android.tv.menu;
import android.content.Context;
import android.content.res.Resources;
import android.text.format.DateFormat;
+import android.text.format.DateUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
@@ -27,6 +28,7 @@ import android.widget.TextView;
import com.android.tv.R;
import com.android.tv.TimeShiftManager;
import com.android.tv.TimeShiftManager.TimeShiftActionId;
+import com.android.tv.common.SoftPreconditions;
import com.android.tv.data.Program;
import com.android.tv.menu.Menu.MenuShowReason;
@@ -249,9 +251,16 @@ public class PlayControlsRowView extends MenuRowView {
}
private void initializeTimeline() {
- Program program = mTimeShiftManager.getProgramAt(mTimeShiftManager.getCurrentPositionMs());
- mProgramStartTimeMs = program.getStartTimeUtcMillis();
- mProgramEndTimeMs = program.getEndTimeUtcMillis();
+ if (mTimeShiftManager.isRecordingPlayback()) {
+ mProgramStartTimeMs = mTimeShiftManager.getRecordStartTimeMs();
+ mProgramEndTimeMs = mTimeShiftManager.getRecordEndTimeMs();
+ } else {
+ Program program = mTimeShiftManager.getProgramAt(
+ mTimeShiftManager.getCurrentPositionMs());
+ mProgramStartTimeMs = program.getStartTimeUtcMillis();
+ mProgramEndTimeMs = program.getEndTimeUtcMillis();
+ }
+ SoftPreconditions.checkArgument(mProgramStartTimeMs <= mProgramEndTimeMs);
}
private void updateMenuVisibility() {
@@ -357,14 +366,6 @@ public class PlayControlsRowView extends MenuRowView {
mTimeIndicator.setVisibility(View.INVISIBLE);
return;
}
- if (mTimeShiftManager.isPlayForRecording()) {
- mProgramStartTimeMs = mTimeShiftManager.getRecordStartTimeMs();
- mProgramEndTimeMs = Math.max(mProgramStartTimeMs,
- mTimeShiftManager.getRecordEndTimeMs());
- if (mProgramStartTimeMs > mProgramEndTimeMs) {
- mProgramEndTimeMs = mProgramStartTimeMs;
- }
- }
long currentPositionMs = mTimeShiftManager.getCurrentPositionMs();
ViewGroup.MarginLayoutParams params =
(ViewGroup.MarginLayoutParams) mTimeText.getLayoutParams();
@@ -422,15 +423,18 @@ public class PlayControlsRowView extends MenuRowView {
private void updateRecTimeText() {
if (isEnabled()) {
- mProgramStartTimeText.setVisibility(View.VISIBLE);
+ if (mTimeShiftManager.isRecordingPlayback()) {
+ mProgramStartTimeText.setVisibility(View.GONE);
+ } else {
+ mProgramStartTimeText.setVisibility(View.VISIBLE);
+ mProgramStartTimeText.setText(getTimeString(mProgramStartTimeMs));
+ }
mProgramEndTimeText.setVisibility(View.VISIBLE);
+ mProgramEndTimeText.setText(getTimeString(mProgramEndTimeMs));
} else {
- mProgramStartTimeText.setVisibility(View.INVISIBLE);
- mProgramEndTimeText.setVisibility(View.INVISIBLE);
- return;
+ mProgramStartTimeText.setVisibility(View.GONE);
+ mProgramEndTimeText.setVisibility(View.GONE);
}
- mProgramStartTimeText.setText(getTimeString(mProgramStartTimeMs));
- mProgramEndTimeText.setText(getTimeString(mProgramEndTimeMs));
}
private void updateButtons() {
@@ -478,7 +482,9 @@ public class PlayControlsRowView extends MenuRowView {
}
private String getTimeString(long timeMs) {
- return mTimeFormat.format(timeMs);
+ return mTimeShiftManager.isRecordingPlayback()
+ ? DateUtils.formatElapsedTime(timeMs / 1000)
+ : mTimeFormat.format(timeMs);
}
private int convertDurationToPixel(long duration) {
diff --git a/src/com/android/tv/menu/RecordCardView.java b/src/com/android/tv/menu/RecordCardView.java
new file mode 100644
index 00000000..de30894e
--- /dev/null
+++ b/src/com/android/tv/menu/RecordCardView.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.menu;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.tv.MainActivity;
+import com.android.tv.R;
+import com.android.tv.TvApplication;
+import com.android.tv.data.Channel;
+import com.android.tv.data.Program;
+import com.android.tv.dvr.DvrDataManager;
+import com.android.tv.dvr.DvrManager;
+import com.android.tv.dvr.ScheduledRecording;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A view to render an item of TV options.
+ */
+public class RecordCardView extends SimpleCardView implements
+ DvrDataManager.ScheduledRecordingListener {
+ private static final String TAG = MenuView.TAG;
+ private static final boolean DEBUG = MenuView.DEBUG;
+ private static final long MIN_PROGRAM_RECORD_DURATION = TimeUnit.MINUTES.toMillis(5);
+
+ private ImageView mIconView;
+ private TextView mLabelView;
+ private Channel mCurrentChannel;
+ private final DvrManager mDvrManager;
+ private final DvrDataManager mDvrDataManager;
+ private boolean mIsRecording;
+ private ScheduledRecording mCurrentRecording;
+
+ public RecordCardView(Context context) {
+ this(context, null);
+ }
+
+ public RecordCardView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public RecordCardView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mDvrManager = TvApplication.getSingletons(context).getDvrManager();
+ mDvrDataManager = TvApplication.getSingletons(context).getDvrDataManager();
+ }
+
+ @Override
+ public void onBind(Channel channel, boolean selected) {
+ super.onBind(channel, selected);
+ mIconView = (ImageView) findViewById(R.id.record_icon);
+ mLabelView = (TextView) findViewById(R.id.record_label);
+ mCurrentChannel = channel;
+ mCurrentRecording = null;
+ for (ScheduledRecording recording : mDvrDataManager.getStartedRecordings()) {
+ if (recording.getChannelId() == channel.getId()) {
+ mIsRecording = true;
+ mCurrentRecording = recording;
+ }
+ }
+ mDvrDataManager.addScheduledRecordingListener(this);
+ updateCardView();
+ }
+
+ @Override
+ public void onRecycled() {
+ super.onRecycled();
+ mDvrDataManager.removeScheduledRecordingListener(this);
+ }
+
+ public boolean isRecording() {
+ return mIsRecording;
+ }
+
+ public void startRecording() {
+ showStartRecordingDialog();
+ }
+
+ public void stopRecording() {
+ mDvrManager.stopRecording(mCurrentRecording);
+ }
+
+ private void updateCardView() {
+ if (mIsRecording) {
+ mIconView.setImageResource(R.drawable.ic_record_stop);
+ mLabelView.setText(R.string.channels_item_record_stop);
+ } else {
+ mIconView.setImageResource(R.drawable.ic_record_start);
+ mLabelView.setText(R.string.channels_item_record_start);
+ }
+ }
+
+ private void showStartRecordingDialog() {
+ final long endOfProgram = -1;
+
+ final List<CharSequence> items = new ArrayList<>();
+ final List<Long> durations = new ArrayList<>();
+ Resources res = getResources();
+ items.add(res.getString(R.string.recording_start_dialog_10_min_duration));
+ durations.add(TimeUnit.MINUTES.toMillis(10));
+ items.add(res.getString(R.string.recording_start_dialog_30_min_duration));
+ durations.add(TimeUnit.MINUTES.toMillis(30));
+ items.add(res.getString(R.string.recording_start_dialog_1_hour_duration));
+ durations.add(TimeUnit.HOURS.toMillis(1));
+ items.add(res.getString(R.string.recording_start_dialog_3_hours_duration));
+ durations.add(TimeUnit.HOURS.toMillis(3));
+
+ Program currenProgram = ((MainActivity) getContext()).getCurrentProgram(false);
+ if (currenProgram != null) {
+ long duration = currenProgram.getEndTimeUtcMillis() - System.currentTimeMillis();
+ if (duration > MIN_PROGRAM_RECORD_DURATION) {
+ items.add(res.getString(R.string.recording_start_dialog_till_end_of_program));
+ durations.add(duration);
+ }
+ }
+
+ DialogInterface.OnClickListener onClickListener
+ = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(final DialogInterface dialog, int which) {
+ long startTime = System.currentTimeMillis();
+ long endTime = System.currentTimeMillis() + durations.get(which);
+ mDvrManager.addSchedule(mCurrentChannel, startTime, endTime);
+ dialog.dismiss();
+ }
+ };
+ new AlertDialog.Builder(getContext())
+ .setItems(items.toArray(new CharSequence[items.size()]), onClickListener)
+ .create()
+ .show();
+ }
+
+ @Override
+ public void onScheduledRecordingAdded(ScheduledRecording recording) {
+ }
+
+ @Override
+ public void onScheduledRecordingRemoved(ScheduledRecording recording) {
+ if (recording.getChannelId() != mCurrentChannel.getId()) {
+ return;
+ }
+ if (mIsRecording) {
+ mIsRecording = false;
+ mCurrentRecording = null;
+ updateCardView();
+ }
+ }
+
+ @Override
+ public void onScheduledRecordingStatusChanged(ScheduledRecording recording) {
+ if (recording.getChannelId() != mCurrentChannel.getId()) {
+ return;
+ }
+ int state = recording.getState();
+ if (state == ScheduledRecording.STATE_RECORDING_FAILED
+ || state == ScheduledRecording.STATE_RECORDING_FINISHED) {
+ mIsRecording = false;
+ mCurrentRecording = null;
+ updateCardView();
+ } else if (state == ScheduledRecording.STATE_RECORDING_IN_PROGRESS) {
+ mIsRecording = true;
+ mCurrentRecording = recording;
+ updateCardView();
+ }
+ }
+}
diff --git a/src/com/android/tv/menu/TvOptionsRowAdapter.java b/src/com/android/tv/menu/TvOptionsRowAdapter.java
index 82525456..ba84247b 100644
--- a/src/com/android/tv/menu/TvOptionsRowAdapter.java
+++ b/src/com/android/tv/menu/TvOptionsRowAdapter.java
@@ -19,6 +19,7 @@ package com.android.tv.menu;
import android.content.Context;
import android.media.tv.TvTrackInfo;
import android.support.annotation.VisibleForTesting;
+import android.support.v4.os.BuildCompat;
import com.android.tv.Features;
import com.android.tv.R;
@@ -39,10 +40,13 @@ import java.util.List;
*/
public class TvOptionsRowAdapter extends CustomizableOptionsRowAdapter {
private int mPositionPipAction;
- private boolean mHasPipAction = true;
+ // If mInAppPipAction is false, system-wide PIP is used.
+ private boolean mInAppPipAction = true;
+ private final Context mContext;
public TvOptionsRowAdapter(Context context, List<CustomAction> customActions) {
super(context, customActions);
+ mContext = context;
}
@Override
@@ -52,8 +56,8 @@ public class TvOptionsRowAdapter extends CustomizableOptionsRowAdapter {
setOptionChangedListener(MenuAction.SELECT_CLOSED_CAPTION_ACTION);
actionList.add(MenuAction.SELECT_DISPLAY_MODE_ACTION);
setOptionChangedListener(MenuAction.SELECT_DISPLAY_MODE_ACTION);
- actionList.add(MenuAction.PIP_ACTION);
- setOptionChangedListener(MenuAction.PIP_ACTION);
+ actionList.add(MenuAction.PIP_IN_APP_ACTION);
+ setOptionChangedListener(MenuAction.PIP_IN_APP_ACTION);
mPositionPipAction = actionList.size() - 1;
actionList.add(MenuAction.SELECT_AUDIO_LANGUAGE_ACTION);
setOptionChangedListener(MenuAction.SELECT_AUDIO_LANGUAGE_ACTION);
@@ -106,34 +110,39 @@ public class TvOptionsRowAdapter extends CustomizableOptionsRowAdapter {
// Case 1
PipInputManager pipInputManager = getMainActivity().getPipInputManager();
if (pipInputManager.getPipInputSize(false) < 2) {
- if (mHasPipAction) {
+ if (mInAppPipAction) {
removeAction(mPositionPipAction);
- mHasPipAction = false;
+ mInAppPipAction = false;
+ if (BuildCompat.isAtLeastN()) {
+ addAction(mPositionPipAction, MenuAction.SYSTEMWIDE_PIP_ACTION);
+ }
return true;
}
+ return false;
} else {
- if (!mHasPipAction) {
- addAction(mPositionPipAction, MenuAction.PIP_ACTION);
- mHasPipAction = true;
+ if (!mInAppPipAction) {
+ removeAction(mPositionPipAction);
+ addAction(mPositionPipAction, MenuAction.PIP_IN_APP_ACTION);
+ mInAppPipAction = true;
changed = true;
}
}
// Case 2
boolean isPipEnabled = getMainActivity().isPipEnabled();
- boolean oldEnabled = MenuAction.PIP_ACTION.isEnabled();
+ boolean oldEnabled = MenuAction.PIP_IN_APP_ACTION.isEnabled();
boolean newEnabled = pipInputManager.getPipInputSize(true) > 0;
if (oldEnabled != newEnabled) {
// Should not disable the item if the PIP is already turned on so that the user can
// force exit it.
if (newEnabled || !isPipEnabled) {
- MenuAction.PIP_ACTION.setEnabled(newEnabled);
+ MenuAction.PIP_IN_APP_ACTION.setEnabled(newEnabled);
changed = true;
}
}
// Case 3 & 4 - we just need to update the icon.
- MenuAction.PIP_ACTION.setDrawableResId(
+ MenuAction.PIP_IN_APP_ACTION.setDrawableResId(
isPipEnabled ? R.drawable.ic_tvoption_pip : R.drawable.ic_tvoption_pip_off);
return changed;
}
@@ -173,9 +182,12 @@ public class TvOptionsRowAdapter extends CustomizableOptionsRowAdapter {
getMainActivity().getOverlayManager().getSideFragmentManager().show(
new DisplayModeFragment());
break;
- case TvOptionsManager.OPTION_PIP:
+ case TvOptionsManager.OPTION_IN_APP_PIP:
getMainActivity().togglePipView();
break;
+ case TvOptionsManager.OPTION_SYSTEMWIDE_PIP:
+ getMainActivity().enterPictureInPictureMode();
+ break;
case TvOptionsManager.OPTION_MULTI_AUDIO:
getMainActivity().getOverlayManager().getSideFragmentManager().show(
new MultiAudioFragment());
diff --git a/src/com/android/tv/onboarding/OnboardingActivity.java b/src/com/android/tv/onboarding/OnboardingActivity.java
index 3ae80597..0685d14b 100644
--- a/src/com/android/tv/onboarding/OnboardingActivity.java
+++ b/src/com/android/tv/onboarding/OnboardingActivity.java
@@ -73,48 +73,55 @@ public class OnboardingActivity extends SetupActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- // Make the channels of the new inputs which have been setup outside Live TV
- // browsable.
- mChannelDataManager = TvApplication.getSingletons(this).getChannelDataManager();
- if (mChannelDataManager.isDbLoadFinished()) {
- SetupUtils.getInstance(this).markNewChannelsBrowsable();
- } else {
- mChannelDataManager.addListener(mChannelListener);
+ if (!PermissionUtils.hasAccessAllEpg(this)) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+ Toast.makeText(this, R.string.msg_not_supported_device, Toast.LENGTH_LONG).show();
+ finish();
+ return;
+ } else if (checkSelfPermission(PERMISSION_READ_TV_LISTINGS)
+ != PackageManager.PERMISSION_GRANTED) {
+ requestPermissions(new String[]{PERMISSION_READ_TV_LISTINGS},
+ PERMISSIONS_REQUEST_READ_TV_LISTINGS);
+ }
}
}
@Override
protected void onDestroy() {
- mChannelDataManager.removeListener(mChannelListener);
+ if (mChannelDataManager != null) {
+ mChannelDataManager.removeListener(mChannelListener);
+ }
super.onDestroy();
}
@Override
protected Fragment onCreateInitialFragment() {
- return OnboardingUtils.isFirstRunWithCurrentVersion(this) ? new WelcomeFragment()
- : new SetupSourcesFragment();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- if (!PermissionUtils.hasAccessAllEpg(this)) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
- Toast.makeText(this, R.string.msg_not_supported_device, Toast.LENGTH_LONG).show();
- finish();
- } else if (checkSelfPermission(PERMISSION_READ_TV_LISTINGS)
- != PackageManager.PERMISSION_GRANTED) {
- requestPermissions(new String[]{PERMISSION_READ_TV_LISTINGS},
- PERMISSIONS_REQUEST_READ_TV_LISTINGS);
+ if (PermissionUtils.hasAccessAllEpg(this) || PermissionUtils.hasReadTvListings(this)) {
+ // Make the channels of the new inputs which have been setup outside Live TV
+ // browsable.
+ mChannelDataManager = TvApplication.getSingletons(this).getChannelDataManager();
+ if (mChannelDataManager.isDbLoadFinished()) {
+ SetupUtils.getInstance(this).markNewChannelsBrowsable();
+ } else {
+ mChannelDataManager.addListener(mChannelListener);
}
+ return OnboardingUtils.isFirstRunWithCurrentVersion(this) ? new WelcomeFragment()
+ : new SetupSourcesFragment();
}
+ return null;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
if (requestCode == PERMISSIONS_REQUEST_READ_TV_LISTINGS) {
- if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
+ if (grantResults != null && grantResults.length > 0
+ && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ finish();
+ Intent intentForNextActivity = getIntent().getParcelableExtra(
+ KEY_INTENT_AFTER_COMPLETION);
+ startActivity(buildIntent(this, intentForNextActivity));
+ } else {
Toast.makeText(this, R.string.msg_read_tv_listing_permission_denied,
Toast.LENGTH_LONG).show();
finish();
diff --git a/src/com/android/tv/onboarding/SetupSourcesFragment.java b/src/com/android/tv/onboarding/SetupSourcesFragment.java
index ebf32d00..23145503 100644
--- a/src/com/android/tv/onboarding/SetupSourcesFragment.java
+++ b/src/com/android/tv/onboarding/SetupSourcesFragment.java
@@ -30,7 +30,6 @@ import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
import android.support.v17.leanback.widget.GuidedAction;
import android.support.v17.leanback.widget.GuidedActionsStylist;
import android.support.v17.leanback.widget.VerticalGridView;
-import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -60,16 +59,14 @@ import java.util.List;
* A fragment for channel source info/setup.
*/
public class SetupSourcesFragment extends SetupMultiPaneFragment {
+ private static final String TAG = "SetupSourcesFragment";
+
public static final String ACTION_CATEGORY =
"com.android.tv.onboarding.SetupSourcesFragment";
public static final int ACTION_PLAY_STORE = 1;
- public static final int DEFAULT_THEME = -1;
-
private static final String SETUP_TRACKER_LABEL = "Setup fragment";
- private static int sTheme = DEFAULT_THEME;
-
private InputSetupRunnable mInputSetupRunnable;
private ContentFragment mContentFragment;
@@ -77,12 +74,7 @@ public class SetupSourcesFragment extends SetupMultiPaneFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- LayoutInflater localInflater = inflater;
- if (sTheme != -1) {
- ContextThemeWrapper themeWrapper = new ContextThemeWrapper(getActivity(), sTheme);
- localInflater = inflater.cloneInContext(themeWrapper);
- }
- View view = super.onCreateView(localInflater, container, savedInstanceState);
+ View view = super.onCreateView(inflater, container, savedInstanceState);
TvApplication.getSingletons(getActivity()).getTracker().sendScreenView(SETUP_TRACKER_LABEL);
return view;
}
@@ -97,10 +89,10 @@ public class SetupSourcesFragment extends SetupMultiPaneFragment {
@Override
protected SetupGuidedStepFragment onCreateContentFragment() {
mContentFragment = new ContentFragment();
+ mContentFragment.setParentFragment(this);
Bundle arguments = new Bundle();
arguments.putBoolean(SetupGuidedStepFragment.KEY_THREE_PANE, true);
mContentFragment.setArguments(arguments);
- mContentFragment.setParentFragment(this);
return mContentFragment;
}
@@ -110,13 +102,6 @@ public class SetupSourcesFragment extends SetupMultiPaneFragment {
}
/**
- * Sets the custom theme dynamically.
- */
- public static void setTheme(int theme) {
- sTheme = theme;
- }
-
- /**
* Call this method to run customized input setup.
*
* @param runnable runnable to be called when the input setup is necessary.
@@ -173,6 +158,11 @@ public class SetupSourcesFragment extends SetupMultiPaneFragment {
handleInputChanged();
}
+ @Override
+ public void onInputUpdated(String inputId) {
+ handleInputChanged();
+ }
+
private void handleInputChanged() {
// The actions created while enter transition is running will not be included in the
// fragment transition.
@@ -395,11 +385,6 @@ public class SetupSourcesFragment extends SetupMultiPaneFragment {
updateActions();
}
- @Override
- public int onProvideTheme() {
- return sTheme == DEFAULT_THEME ? super.onProvideTheme() : sTheme;
- }
-
void executePendingAction() {
switch (mPendingAction) {
case PENDING_ACTION_INPUT_CHANGED:
diff --git a/src/com/android/tv/onboarding/WelcomeFragment.java b/src/com/android/tv/onboarding/WelcomeFragment.java
index ed85df68..00f7fe8d 100644
--- a/src/com/android/tv/onboarding/WelcomeFragment.java
+++ b/src/com/android/tv/onboarding/WelcomeFragment.java
@@ -20,8 +20,11 @@ import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
+import android.app.Activity;
+import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
+import android.support.v17.leanback.app.OnboardingFragment;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -31,7 +34,6 @@ import android.widget.ImageView;
import com.android.tv.R;
import com.android.tv.common.ui.setup.SetupActionHelper;
import com.android.tv.common.ui.setup.animation.SetupAnimationHelper;
-import com.android.tv.common.ui.setup.leanback.OnboardingFragment;
import java.util.ArrayList;
import java.util.List;
@@ -580,7 +582,6 @@ public class WelcomeFragment extends OnboardingFragment {
private ImageView mArrowView;
private Animator mAnimator;
- private boolean mNeedToEndAnimator;
public WelcomeFragment() {
setExitTransition(new SetupAnimationHelper.TransitionBuilder()
@@ -589,16 +590,63 @@ public class WelcomeFragment extends OnboardingFragment {
.build());
}
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ initialize();
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ initialize();
+ }
+
+ private void initialize() {
+ if (mPageTitles == null) {
+ mPageTitles = getResources().getStringArray(R.array.welcome_page_titles);
+ mPageDescriptions = getResources().getStringArray(R.array.welcome_page_descriptions);
+ }
+ }
+
@Nullable
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- mPageTitles = getResources().getStringArray(R.array.welcome_page_titles);
- mPageDescriptions = getResources().getStringArray(R.array.welcome_page_descriptions);
- return super.onCreateView(inflater, container, savedInstanceState);
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = super.onCreateView(inflater, container, savedInstanceState);
+ setLogoResourceId(R.drawable.splash_logo);
+ if (savedInstanceState != null) {
+ switch (getCurrentPageIndex()) {
+ case 0:
+ mTvContentView.setImageResource(
+ TV_FRAMES_1_START[TV_FRAMES_1_START.length - 1]);
+ break;
+ case 1:
+ mTvContentView.setImageResource(
+ TV_FRAMES_2_START[TV_FRAMES_2_START.length - 1]);
+ break;
+ case 2:
+ mTvContentView.setImageResource(
+ TV_FRAMES_3_ORANGE_START[TV_FRAMES_3_ORANGE_START.length - 1]);
+ mArrowView.setImageResource(TV_FRAMES_3_BLUE_ARROW[0]);
+ break;
+ case 3:
+ default:
+ mTvContentView.setImageResource(
+ TV_FRAMES_4_START[TV_FRAMES_4_START.length - 1]);
+ break;
+ }
+ }
+ return view;
+ }
+
+ @Override
+ public int onProvideTheme() {
+ return R.style.Theme_Leanback_Onboarding;
}
@Override
- protected void onStartEnterAnimation() {
+ protected Animator onCreateEnterAnimation() {
List<Animator> animators = new ArrayList<>();
// Cloud 1
View view = getActivity().findViewById(R.id.cloud1);
@@ -640,9 +688,7 @@ public class WelcomeFragment extends OnboardingFragment {
animators.add(animator);
AnimatorSet set = new AnimatorSet();
set.playTogether(animators);
- mAnimator = set;
- mAnimator.start();
- mNeedToEndAnimator = true;
+ return set;
}
@Nullable
@@ -683,23 +729,14 @@ public class WelcomeFragment extends OnboardingFragment {
}
@Override
- protected int getLogoResourceId() {
- return R.drawable.splash_logo;
- }
-
- @Override
protected void onFinishFragment() {
SetupActionHelper.onActionClick(WelcomeFragment.this, ACTION_CATEGORY, ACTION_NEXT);
}
@Override
- protected void onStartPageChangeAnimation(int previousPage) {
+ protected void onPageChanged(int newPage, int previousPage) {
if (mAnimator != null) {
- if (mNeedToEndAnimator) {
- mAnimator.end();
- } else {
- mAnimator.cancel();
- }
+ mAnimator.cancel();
}
mArrowView.setVisibility(View.GONE);
// TV screen hiding animator.
@@ -710,7 +747,7 @@ public class WelcomeFragment extends OnboardingFragment {
// TV screen showing animator.
AnimatorSet animatorSet = new AnimatorSet();
int firstFrame;
- switch (getCurrentPageIndex()) {
+ switch (newPage) {
case 0:
animatorSet.playSequentially(hideAnimator,
SetupAnimationHelper.createFrameAnimator(mTvContentView,
@@ -762,6 +799,5 @@ public class WelcomeFragment extends OnboardingFragment {
});
mAnimator = SetupAnimationHelper.applyAnimationTimeScale(animatorSet);
mAnimator.start();
- mNeedToEndAnimator = false;
}
}
diff --git a/src/com/android/tv/parental/ContentRatingSystem.java b/src/com/android/tv/parental/ContentRatingSystem.java
index 6c00ee11..6b5d6635 100644
--- a/src/com/android/tv/parental/ContentRatingSystem.java
+++ b/src/com/android/tv/parental/ContentRatingSystem.java
@@ -490,6 +490,19 @@ public class ContentRatingSystem {
mRatingOrder = ratingOrder;
}
+ /**
+ * Returns index of the rating in this order.
+ * Returns -1 if this order doesn't contain the rating.
+ */
+ public int getRatingIndex(Rating rating) {
+ for (int i = 0; i < mRatingOrder.size(); i++) {
+ if (mRatingOrder.get(i).getName().equals(rating.getName())) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
public static class Builder {
private final List<String> mRatingNames = new ArrayList<>();
diff --git a/src/com/android/tv/receiver/BootCompletedReceiver.java b/src/com/android/tv/receiver/BootCompletedReceiver.java
index 3cd6186c..da88f70d 100644
--- a/src/com/android/tv/receiver/BootCompletedReceiver.java
+++ b/src/com/android/tv/receiver/BootCompletedReceiver.java
@@ -21,6 +21,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.support.v4.os.BuildCompat;
import android.util.Log;
import com.android.tv.Features;
@@ -72,8 +73,7 @@ public class BootCompletedReceiver extends BroadcastReceiver {
}
}
- // DVR
- if (CommonFeatures.DVR.isEnabled(context)) {
+ if (CommonFeatures.DVR.isEnabled(context) && BuildCompat.isAtLeastN()) {
DvrRecordingService.startService(context);
}
}
diff --git a/src/com/android/tv/receiver/GlobalKeyReceiver.java b/src/com/android/tv/receiver/GlobalKeyReceiver.java
index bd81cee3..2e19c089 100644
--- a/src/com/android/tv/receiver/GlobalKeyReceiver.java
+++ b/src/com/android/tv/receiver/GlobalKeyReceiver.java
@@ -19,6 +19,7 @@ package com.android.tv.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.media.tv.TvContract;
import android.util.Log;
import android.view.KeyEvent;
@@ -39,10 +40,22 @@ public class GlobalKeyReceiver extends BroadcastReceiver {
if (DEBUG) Log.d(TAG, "onReceive: " + event);
int keyCode = event.getKeyCode();
int action = event.getAction();
- if (keyCode == KeyEvent.KEYCODE_TV && action == KeyEvent.ACTION_UP) {
- ((TvApplication) context.getApplicationContext()).handleTvKey();
- } else if (keyCode == KeyEvent.KEYCODE_TV_INPUT && action == KeyEvent.ACTION_UP) {
- ((TvApplication) context.getApplicationContext()).handleTvInputKey();
+ if (action == KeyEvent.ACTION_UP) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_GUIDE:
+ context.startActivity(
+ new Intent(Intent.ACTION_VIEW, TvContract.Programs.CONTENT_URI));
+ break;
+ case KeyEvent.KEYCODE_TV:
+ ((TvApplication) context.getApplicationContext()).handleTvKey();
+ break;
+ case KeyEvent.KEYCODE_TV_INPUT:
+ ((TvApplication) context.getApplicationContext()).handleTvInputKey();
+ break;
+ default:
+ // Do nothing
+ break;
+ }
}
}
}
diff --git a/src/com/android/tv/receiver/PackageIntentsReceiver.java b/src/com/android/tv/receiver/PackageIntentsReceiver.java
index 67f0529f..4c850402 100644
--- a/src/com/android/tv/receiver/PackageIntentsReceiver.java
+++ b/src/com/android/tv/receiver/PackageIntentsReceiver.java
@@ -17,59 +17,18 @@
package com.android.tv.receiver;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
-import com.android.tv.TvActivity;
import com.android.tv.TvApplication;
-import com.android.usbtuner.setup.TunerSetupActivity;
-import com.android.usbtuner.UsbTunerPreferences;
-import com.android.usbtuner.tvinput.UsbTunerTvInputService;
/**
* A class for handling the broadcast intents from PackageManager.
*/
public class PackageIntentsReceiver extends BroadcastReceiver {
- private PackageManager mPackageManager;
- private ComponentName mTvActivityComponentName;
- private ComponentName mUsbTunerComponentName;
-
- private void init(Context context) {
- mPackageManager = context.getPackageManager();
- mTvActivityComponentName = new ComponentName(context, TvActivity.class);
- mUsbTunerComponentName = new ComponentName(context, UsbTunerTvInputService.class);
- }
@Override
public void onReceive(Context context, Intent intent) {
- if (mPackageManager == null) {
- init(context);
- }
((TvApplication) context.getApplicationContext()).handleInputCountChanged();
- // Check the component status of UsbTunerTvInputService and TvActivity here to make sure
- // start the setup activity of USB tuner TV input service only when those components are
- // enabled.
- if (UsbTunerPreferences.shouldShowSetupActivity(context)
- && Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())
- && mPackageManager.getComponentEnabledSetting(mTvActivityComponentName)
- == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
- && mPackageManager.getComponentEnabledSetting(mUsbTunerComponentName)
- == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
- startUsbTunerSetupActivity(context);
- UsbTunerPreferences.setShouldShowSetupActivity(context, false);
- }
- }
-
- /**
- * Launches the setup activity of USB tuner TV input service.
- *
- * @param context {@link Context} instance
- */
- private static void startUsbTunerSetupActivity(Context context) {
- Intent intent = TunerSetupActivity.createSetupActivity(context);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(intent);
}
}
diff --git a/src/com/android/tv/recommendation/NotificationService.java b/src/com/android/tv/recommendation/NotificationService.java
index c6a0c3f6..0095482d 100644
--- a/src/com/android/tv/recommendation/NotificationService.java
+++ b/src/com/android/tv/recommendation/NotificationService.java
@@ -28,6 +28,7 @@ import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.media.tv.TvInputInfo;
+import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -51,6 +52,7 @@ import com.android.tv.data.Program;
import com.android.tv.util.BitmapUtils;
import com.android.tv.util.BitmapUtils.ScaledBitmapInfo;
import com.android.tv.util.ImageLoader;
+import com.android.tv.util.PermissionUtils;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.Utils;
@@ -127,6 +129,12 @@ public class NotificationService extends Service implements Recommender.Listener
public void onCreate() {
if (DEBUG) Log.d(TAG, "onCreate");
super.onCreate();
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M
+ && !PermissionUtils.hasAccessAllEpg(this)) {
+ Log.w(TAG, "Live TV requires the system permission on this platform.");
+ stopSelf();
+ return;
+ }
mCurrentNotificationCount = 0;
mNotificationChannels = new long[NOTIFICATION_COUNT];
diff --git a/src/com/android/tv/recommendation/RecommendationDataManager.java b/src/com/android/tv/recommendation/RecommendationDataManager.java
index 66dd9fe4..a7d4c46d 100644
--- a/src/com/android/tv/recommendation/RecommendationDataManager.java
+++ b/src/com/android/tv/recommendation/RecommendationDataManager.java
@@ -16,7 +16,6 @@
package com.android.tv.recommendation;
-import android.content.ContentUris;
import android.content.Context;
import android.content.UriMatcher;
import android.database.ContentObserver;
@@ -35,8 +34,10 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
+import com.android.tv.TvApplication;
import com.android.tv.common.WeakHandler;
import com.android.tv.data.Channel;
+import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.Program;
import com.android.tv.data.WatchedHistoryManager;
import com.android.tv.util.PermissionUtils;
@@ -51,8 +52,6 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class RecommendationDataManager implements WatchedHistoryManager.Listener {
- private static final String TAG = "RecommendationDataManager";
-
private static final UriMatcher sUriMatcher;
private static final int MATCH_CHANNEL = 1;
private static final int MATCH_CHANNEL_ID = 2;
@@ -66,19 +65,15 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
private static final int MSG_START = 1000;
private static final int MSG_STOP = 1001;
- private static final int MSG_UPDATE_CHANNEL = 1002;
- private static final int MSG_UPDATE_CHANNELS = 1003;
- private static final int MSG_UPDATE_WATCH_HISTORY = 1004;
- private static final int MSG_NOTIFY_CHANNEL_RECORD_MAP_LOADED = 1005;
- private static final int MSG_NOTIFY_CHANNEL_RECORD_MAP_CHANGED = 1006;
+ private static final int MSG_UPDATE_CHANNELS = 1002;
+ private static final int MSG_UPDATE_WATCH_HISTORY = 1003;
+ private static final int MSG_NOTIFY_CHANNEL_RECORD_MAP_LOADED = 1004;
+ private static final int MSG_NOTIFY_CHANNEL_RECORD_MAP_CHANGED = 1005;
private static final int MSG_FIRST = MSG_START;
private static final int MSG_LAST = MSG_NOTIFY_CHANNEL_RECORD_MAP_CHANGED;
- private static final int INVALID_INDEX = -1;
-
private static RecommendationDataManager sManager;
- private final static Object sListenerLock = new Object();
private final ContentObserver mContentObserver;
private final Map<Long, ChannelRecord> mChannelRecordMap = new ConcurrentHashMap<>();
private final Map<Long, ChannelRecord> mAvailableChannelRecordMap = new ConcurrentHashMap<>();
@@ -98,10 +93,33 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
private final HandlerThread mHandlerThread;
private final Handler mHandler;
+ private final Handler mMainHandler;
@Nullable
private WatchedHistoryManager mWatchedHistoryManager;
+ private final ChannelDataManager mChannelDataManager;
+ private final ChannelDataManager.Listener mChannelDataListener =
+ new ChannelDataManager.Listener() {
+ @Override
+ @MainThread
+ public void onLoadFinished() {
+ updateChannelData();
+ }
- private final List<ListenerRecord> mListeners = new ArrayList<>();
+ @Override
+ @MainThread
+ public void onChannelListUpdated() {
+ updateChannelData();
+ }
+
+ @Override
+ @MainThread
+ public void onChannelBrowsableChanged() {
+ updateChannelData();
+ }
+ };
+
+ // For thread safety, this variable is handled only on main thread.
+ private final List<Listener> mListeners = new ArrayList<>();
/**
* Gets instance of RecommendationDataManager, and adds a {@link Listener}.
@@ -112,25 +130,11 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
public synchronized static RecommendationDataManager acquireManager(
Context context, @NonNull Listener listener) {
if (sManager == null) {
- sManager = new RecommendationDataManager(context);
+ sManager = new RecommendationDataManager(context, listener);
}
- sManager.addListener(listener);
- sManager.start();
return sManager;
}
- /**
- * Removes the {@link Listener}, and releases RecommendationDataManager
- * if there are no listeners remained.
- */
- public void release(@NonNull Listener listener) {
- removeListener(listener);
- synchronized (sListenerLock) {
- if (mListeners.size() == 0) {
- stop();
- }
- }
- }
private final TvInputCallback mInternalCallback =
new TvInputCallback() {
@Override
@@ -187,12 +191,37 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
public void onInputUpdated(String inputId) { }
};
- private RecommendationDataManager(Context context) {
+ private RecommendationDataManager(Context context, final Listener listener) {
mContext = context.getApplicationContext();
mHandlerThread = new HandlerThread("RecommendationDataManager");
mHandlerThread.start();
mHandler = new RecommendationHandler(mHandlerThread.getLooper(), this);
+ mMainHandler = new RecommendationMainHandler(Looper.getMainLooper(), this);
mContentObserver = new RecommendationContentObserver(mHandler);
+ mChannelDataManager = TvApplication.getSingletons(mContext).getChannelDataManager();
+ runOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ addListener(listener);
+ start();
+ }
+ });
+ }
+
+ /**
+ * Removes the {@link Listener}, and releases RecommendationDataManager
+ * if there are no listeners remained.
+ */
+ public void release(@NonNull final Listener listener) {
+ runOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ removeListener(listener);
+ if (mListeners.size() == 0) {
+ stop();
+ }
+ }
+ });
}
/**
@@ -216,54 +245,48 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
return Collections.unmodifiableCollection(mAvailableChannelRecordMap.values());
}
+ @MainThread
private void start() {
mHandler.sendEmptyMessage(MSG_START);
+ mChannelDataManager.addListener(mChannelDataListener);
+ if (mChannelDataManager.isDbLoadFinished()) {
+ updateChannelData();
+ }
}
+ @MainThread
private void stop() {
for (int what = MSG_FIRST; what <= MSG_LAST; ++what) {
mHandler.removeMessages(what);
}
+ mChannelDataManager.removeListener(mChannelDataListener);
mHandler.sendEmptyMessage(MSG_STOP);
mHandlerThread.quitSafely();
+ mMainHandler.removeCallbacksAndMessages(null);
sManager = null;
}
- private int getListenerIndexLocked(Listener listener) {
- for (int i = 0; i < mListeners.size(); ++i) {
- if (mListeners.get(i).mListener == listener) {
- return i;
- }
- }
- return INVALID_INDEX;
+ @MainThread
+ private void updateChannelData() {
+ mHandler.removeMessages(MSG_UPDATE_CHANNELS);
+ mHandler.obtainMessage(MSG_UPDATE_CHANNELS, mChannelDataManager.getBrowsableChannelList())
+ .sendToTarget();
}
+ @MainThread
private void addListener(Listener listener) {
- synchronized (sListenerLock) {
- if (getListenerIndexLocked(listener) == INVALID_INDEX) {
- mListeners.add((new ListenerRecord(listener)));
- }
- }
+ mListeners.add(listener);
}
+ @MainThread
private void removeListener(Listener listener) {
- synchronized (sListenerLock) {
- int idx = getListenerIndexLocked(listener);
- if (idx != INVALID_INDEX) {
- ListenerRecord record = mListeners.remove(idx);
- record.mListener = null;
- }
- }
+ mListeners.remove(listener);
}
private void onStart() {
if (!mStarted) {
mStarted = true;
mCancelLoadTask = false;
- mContext.getContentResolver().registerContentObserver(
- TvContract.Channels.CONTENT_URI, true, mContentObserver);
- mHandler.obtainMessage(MSG_UPDATE_CHANNELS, TvContract.Channels.CONTENT_URI)
- .sendToTarget();
if (!PermissionUtils.hasAccessWatchedHistory(mContext)) {
mWatchedHistoryManager = new WatchedHistoryManager(mContext);
mWatchedHistoryManager.setListener(this);
@@ -297,42 +320,7 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
}
@WorkerThread
- private void onUpdateChannel(Uri uri) {
- Channel channel = null;
- try (Cursor cursor = mContext.getContentResolver().query(uri, Channel.PROJECTION,
- null, null, null)) {
- if (cursor != null && cursor.moveToFirst()) {
- channel = Channel.fromCursor(cursor);
- }
- }
- boolean isChannelRecordMapChanged = false;
- if (channel == null) {
- long channelId = ContentUris.parseId(uri);
- mChannelRecordMap.remove(channelId);
- isChannelRecordMapChanged = mAvailableChannelRecordMap.remove(channelId) != null;
- } else if (updateChannelRecordMapFromChannel(channel)) {
- isChannelRecordMapChanged = true;
- }
- if (isChannelRecordMapChanged && mChannelRecordMapLoaded
- && !mHandler.hasMessages(MSG_NOTIFY_CHANNEL_RECORD_MAP_CHANGED)) {
- mHandler.sendEmptyMessage(MSG_NOTIFY_CHANNEL_RECORD_MAP_CHANGED);
- }
- }
-
- @WorkerThread
- private void onUpdateChannels(Uri uri) {
- List<Channel> channels = new ArrayList<>();
- try (Cursor cursor = mContext.getContentResolver().query(uri, Channel.PROJECTION,
- null, null, null)) {
- if (cursor != null) {
- while (cursor.moveToNext()) {
- if (mCancelLoadTask) {
- return;
- }
- channels.add(Channel.fromCursor(cursor));
- }
- }
- }
+ private void onUpdateChannels(List<Channel> channels) {
boolean isChannelRecordMapChanged = false;
Set<Long> removedChannelIdSet = new HashSet<>(mChannelRecordMap.keySet());
// Builds removedChannelIdSet.
@@ -374,11 +362,14 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
final ChannelRecord channelRecord =
updateChannelRecordFromWatchedProgram(watchedProgram);
if (mChannelRecordMapLoaded && channelRecord != null) {
- synchronized (sListenerLock) {
- for (ListenerRecord l : mListeners) {
- l.postNewWatchLog(channelRecord);
+ runOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ for (Listener l : mListeners) {
+ l.onNewWatchLog(channelRecord);
+ }
}
- }
+ });
}
}
if (!mChannelRecordMapLoaded) {
@@ -410,14 +401,17 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
@Override
public void onNewRecordAdded(WatchedHistoryManager.WatchedRecord watchedRecord) {
- ChannelRecord channelRecord = updateChannelRecordFromWatchedProgram(
+ final ChannelRecord channelRecord = updateChannelRecordFromWatchedProgram(
convertFromWatchedHistoryManagerRecords(watchedRecord));
if (mChannelRecordMapLoaded && channelRecord != null) {
- synchronized (sListenerLock) {
- for (ListenerRecord l : mListeners) {
- l.postNewWatchLog(channelRecord);
+ runOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ for (Listener l : mListeners) {
+ l.onNewWatchLog(channelRecord);
+ }
}
- }
+ });
}
}
@@ -452,19 +446,25 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
private void onNotifyChannelRecordMapLoaded() {
mChannelRecordMapLoaded = true;
- synchronized (sListenerLock) {
- for (ListenerRecord l : mListeners) {
- l.postChannelRecordLoaded();
+ runOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ for (Listener l : mListeners) {
+ l.onChannelRecordLoaded();
+ }
}
- }
+ });
}
private void onNotifyChannelRecordMapChanged() {
- synchronized (sListenerLock) {
- for (ListenerRecord l : mListeners) {
- l.postChannelRecordChanged();
+ runOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ for (Listener l : mListeners) {
+ l.onChannelRecordChanged();
+ }
}
- }
+ });
}
/**
@@ -511,15 +511,6 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
@Override
public void onChange(final boolean selfChange, final Uri uri) {
switch (sUriMatcher.match(uri)) {
- case MATCH_CHANNEL:
- if (!mHandler.hasMessages(MSG_UPDATE_CHANNELS, TvContract.Channels.CONTENT_URI)) {
- mHandler.obtainMessage(MSG_UPDATE_CHANNELS, TvContract.Channels.CONTENT_URI)
- .sendToTarget();
- }
- break;
- case MATCH_CHANNEL_ID:
- mHandler.obtainMessage(MSG_UPDATE_CHANNEL, uri).sendToTarget();
- break;
case MATCH_WATCHED_PROGRAM_ID:
if (!mHandler.hasMessages(MSG_UPDATE_WATCH_HISTORY,
TvContract.WatchedPrograms.CONTENT_URI)) {
@@ -530,6 +521,14 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
}
}
+ private void runOnMainThread(Runnable r) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ r.run();
+ } else {
+ mMainHandler.post(r);
+ }
+ }
+
/**
* A listener interface to receive notification about the recommendation data.
*
@@ -561,55 +560,6 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
void onChannelRecordChanged();
}
- private static class ListenerRecord {
- private Listener mListener;
- private final Handler mHandler;
-
- public ListenerRecord(Listener listener) {
- mHandler = new Handler();
- mListener = listener;
- }
-
- public void postChannelRecordLoaded() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- synchronized (sListenerLock) {
- if (mListener != null) {
- mListener.onChannelRecordLoaded();
- }
- }
- }
- });
- }
-
- public void postNewWatchLog(final ChannelRecord channelRecord) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- synchronized (sListenerLock) {
- if (mListener != null) {
- mListener.onNewWatchLog(channelRecord);
- }
- }
- }
- });
- }
-
- public void postChannelRecordChanged() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- synchronized (sListenerLock) {
- if (mListener != null) {
- mListener.onChannelRecordChanged();
- }
- }
- }
- });
- }
- }
-
private static class RecommendationHandler extends WeakHandler<RecommendationDataManager> {
public RecommendationHandler(@NonNull Looper looper, RecommendationDataManager ref) {
super(looper, ref);
@@ -626,14 +576,9 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
dataManager.onStop();
}
break;
- case MSG_UPDATE_CHANNEL:
- if (dataManager.mStarted) {
- dataManager.onUpdateChannel((Uri) msg.obj);
- }
- break;
case MSG_UPDATE_CHANNELS:
if (dataManager.mStarted) {
- dataManager.onUpdateChannels((Uri) msg.obj);
+ dataManager.onUpdateChannels((List<Channel>) msg.obj);
}
break;
case MSG_UPDATE_WATCH_HISTORY:
@@ -654,4 +599,13 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
}
}
}
+
+ private static class RecommendationMainHandler extends WeakHandler<RecommendationDataManager> {
+ public RecommendationMainHandler(@NonNull Looper looper, RecommendationDataManager ref) {
+ super(looper, ref);
+ }
+
+ @Override
+ protected void handleMessage(Message msg, @NonNull RecommendationDataManager referent) { }
+ }
}
diff --git a/src/com/android/tv/recommendation/Recommender.java b/src/com/android/tv/recommendation/Recommender.java
index 0561449e..82c2893d 100644
--- a/src/com/android/tv/recommendation/Recommender.java
+++ b/src/com/android/tv/recommendation/Recommender.java
@@ -145,7 +145,7 @@ public class Recommender implements RecommendationDataManager.Listener {
mChannelSortKey.put(records.get(i).first.getId(), String.format(sortKeyFormat, i));
results.add(records.get(i).first);
}
- return Collections.unmodifiableList(results);
+ return results;
}
/**
diff --git a/src/com/android/tv/recommendation/RoutineWatchEvaluator.java b/src/com/android/tv/recommendation/RoutineWatchEvaluator.java
index 694da6bf..5ff7cae9 100644
--- a/src/com/android/tv/recommendation/RoutineWatchEvaluator.java
+++ b/src/com/android/tv/recommendation/RoutineWatchEvaluator.java
@@ -16,7 +16,9 @@
package com.android.tv.recommendation;
+import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
import com.android.tv.data.Program;
@@ -36,7 +38,6 @@ public class RoutineWatchEvaluator extends Recommender.Evaluator {
private static final double TIME_MATCH_WEIGHT = 1 - TITLE_MATCH_WEIGHT;
private static final long DIFF_MS_TOLERANCE_FOR_OLD_PROGRAM = TimeUnit.DAYS.toMillis(14);
private static final long MAX_DIFF_MS_FOR_OLD_PROGRAM = TimeUnit.DAYS.toMillis(56);
- private static final String REGULAR_EXPRESSION_FOR_WHITE_SPACES = "\\s+";
@Override
public double evaluateChannel(long channelId) {
@@ -91,8 +92,8 @@ public class RoutineWatchEvaluator extends Recommender.Evaluator {
return maxScore;
}
- private double calculateRoutineWatchScore(
- Program currentProgram, Program watchedProgram, long watchedDurationMs) {
+ private static double calculateRoutineWatchScore(Program currentProgram, Program watchedProgram,
+ long watchedDurationMs) {
double timeMatchScore = calculateTimeMatchScore(currentProgram, watchedProgram);
double titleMatchScore = calculateTitleMatchScore(
currentProgram.getTitle(), watchedProgram.getTitle());
@@ -107,10 +108,16 @@ public class RoutineWatchEvaluator extends Recommender.Evaluator {
* watchDurationScore * multiplierForOldProgram;
}
- private double calculateTitleMatchScore(String title1, String title2) {
+ @VisibleForTesting
+ static double calculateTitleMatchScore(@Nullable String title1, @Nullable String title2) {
+ if (TextUtils.isEmpty(title1) || TextUtils.isEmpty(title2)) {
+ return 0;
+ }
List<String> wordList1 = splitTextToWords(title1);
List<String> wordList2 = splitTextToWords(title2);
-
+ if (wordList1.isEmpty() || wordList2.isEmpty()) {
+ return 0;
+ }
int maxMatchedWordSeqLen = calculateMaximumMatchedWordSequenceLength(
wordList1, wordList2);
@@ -121,8 +128,8 @@ public class RoutineWatchEvaluator extends Recommender.Evaluator {
}
@VisibleForTesting
- int calculateMaximumMatchedWordSequenceLength(
- List<String> toSearchWords, List<String> toMatchWords) {
+ static int calculateMaximumMatchedWordSequenceLength(List<String> toSearchWords,
+ List<String> toMatchWords) {
int[] matchedWordSeqLen = new int[toMatchWords.size()];
int maxMatchedWordSeqLen = 0;
for (String word : toSearchWords) {
@@ -142,7 +149,7 @@ public class RoutineWatchEvaluator extends Recommender.Evaluator {
return maxMatchedWordSeqLen;
}
- private double calculateTimeMatchScore(Program p1, Program p2) {
+ private static double calculateTimeMatchScore(Program p1, Program p2) {
ProgramTime t1 = ProgramTime.createFromProgram(p1);
ProgramTime t2 = ProgramTime.createFromProgram(p2);
@@ -155,7 +162,7 @@ public class RoutineWatchEvaluator extends Recommender.Evaluator {
}
@VisibleForTesting
- double calculateOverlappedIntervalScore(ProgramTime t1, ProgramTime t2) {
+ static double calculateOverlappedIntervalScore(ProgramTime t1, ProgramTime t2) {
if (t1.dayChanged && !t2.dayChanged) {
// Swap two values.
return calculateOverlappedIntervalScore(t2, t1);
@@ -181,7 +188,7 @@ public class RoutineWatchEvaluator extends Recommender.Evaluator {
return score;
}
- private double calculateWatchDurationScore(Program program, long durationMs) {
+ private static double calculateWatchDurationScore(Program program, long durationMs) {
return (double) durationMs
/ (program.getEndTimeUtcMillis() - program.getStartTimeUtcMillis());
}
diff --git a/src/com/android/tv/ui/AppLayerTvView.java b/src/com/android/tv/ui/AppLayerTvView.java
index befa004c..c7b94a15 100644
--- a/src/com/android/tv/ui/AppLayerTvView.java
+++ b/src/com/android/tv/ui/AppLayerTvView.java
@@ -16,9 +16,8 @@
package com.android.tv.ui;
-import com.android.tv.common.recording.PlaybackTvView;
-
import android.content.Context;
+import android.media.tv.TvView;
import android.util.AttributeSet;
/**
@@ -30,7 +29,7 @@ import android.util.AttributeSet;
* TODO: remove this class once the TvView.setMain() is revisited.
* </p>
*/
-public class AppLayerTvView extends PlaybackTvView {
+public class AppLayerTvView extends TvView {
public AppLayerTvView(Context context) {
super(context);
}
diff --git a/src/com/android/tv/ui/BlockScreenView.java b/src/com/android/tv/ui/BlockScreenView.java
new file mode 100644
index 00000000..52b9389d
--- /dev/null
+++ b/src/com/android/tv/ui/BlockScreenView.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.ui;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.tv.R;
+import com.android.tv.ui.TunableTvView.BlockScreenType;
+
+public class BlockScreenView extends LinearLayout {
+ private View mContainerView;
+ private View mImageContainer;
+ private ImageView mNormalImageView;
+ private ImageView mShrunkenImageView;
+ private View mSpace;
+ private TextView mTextView;
+
+ private final int mSpacingNormal;
+ private final int mSpacingShrunken;
+
+ // Animators used for fade in/out of block screen icon.
+ private Animator mFadeIn;
+ private Animator mFadeOut;
+
+ public BlockScreenView(Context context) {
+ this(context, null, 0);
+ }
+
+ public BlockScreenView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public BlockScreenView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mSpacingNormal = getResources().getDimensionPixelOffset(
+ R.dimen.tvview_block_vertical_spacing);
+ mSpacingShrunken = getResources().getDimensionPixelOffset(
+ R.dimen.shrunken_tvview_block_vertical_spacing);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mContainerView = findViewById(R.id.block_screen_container);
+ mImageContainer = findViewById(R.id.image_container);
+ mNormalImageView = (ImageView) findViewById(R.id.block_screen_icon);
+ mShrunkenImageView = (ImageView) findViewById(R.id.block_screen_shrunken_icon);
+ mSpace = findViewById(R.id.space);
+ mTextView = (TextView) findViewById(R.id.block_screen_text);
+ mFadeIn = AnimatorInflater.loadAnimator(getContext(),
+ R.animator.tvview_block_screen_fade_in);
+ mFadeIn.setTarget(mContainerView);
+ mFadeOut = AnimatorInflater.loadAnimator(getContext(),
+ R.animator.tvview_block_screen_fade_out);
+ mFadeOut.setTarget(mContainerView);
+ mFadeOut.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mContainerView.setVisibility(GONE);
+ mContainerView.setAlpha(1f);
+ }
+ });
+ }
+
+ /**
+ * Sets the normal image.
+ */
+ public void setImage(int resId) {
+ mNormalImageView.setImageResource(resId);
+ updateSpaceVisibility();
+ }
+
+ /**
+ * Sets the scale type of the normal image.
+ */
+ public void setScaleType(ScaleType scaleType) {
+ mNormalImageView.setScaleType(scaleType);
+ updateSpaceVisibility();
+ }
+
+ /**
+ * Sets the shrunken image.
+ */
+ public void setShrunkenImage(int resId) {
+ mShrunkenImageView.setImageResource(resId);
+ updateSpaceVisibility();
+ }
+
+ /**
+ * Show or hide the image of this view.
+ */
+ public void setImageVisibility(boolean visible) {
+ mImageContainer.setVisibility(visible ? VISIBLE : GONE);
+ updateSpaceVisibility();
+ }
+
+ /**
+ * Sets the text message.
+ */
+ public void setText(int resId) {
+ mTextView.setText(resId);
+ updateSpaceVisibility();
+ }
+
+ /**
+ * Sets the text message.
+ */
+ public void setText(String text) {
+ mTextView.setText(text);
+ updateSpaceVisibility();
+ }
+
+ private void updateSpaceVisibility() {
+ if (isImageViewVisible() && isTextViewVisible(mTextView)) {
+ mSpace.setVisibility(VISIBLE);
+ } else {
+ mSpace.setVisibility(GONE);
+ }
+ }
+
+ private boolean isImageViewVisible() {
+ return mImageContainer.getVisibility() == VISIBLE
+ && (isImageViewVisible(mNormalImageView) || isImageViewVisible(mShrunkenImageView));
+ }
+
+ private static boolean isImageViewVisible(ImageView imageView) {
+ return imageView.getVisibility() != GONE && imageView.getDrawable() != null;
+ }
+
+ private static boolean isTextViewVisible(TextView textView) {
+ return textView.getVisibility() != GONE && !TextUtils.isEmpty(textView.getText());
+ }
+
+ /**
+ * Changes the spacing between the image view and the text view according to the
+ * {@code blockScreenType}.
+ */
+ public void setSpacing(@BlockScreenType int blockScreenType) {
+ mSpace.getLayoutParams().height =
+ blockScreenType == TunableTvView.BLOCK_SCREEN_TYPE_SHRUNKEN_TV_VIEW
+ ? mSpacingShrunken : mSpacingNormal;
+ requestLayout();
+ }
+
+ /**
+ * Changes the view layout according to the {@code blockScreenType}.
+ */
+ public void onBlockStatusChanged(@BlockScreenType int blockScreenType, boolean withAnimation) {
+ if (!withAnimation) {
+ switch (blockScreenType) {
+ case TunableTvView.BLOCK_SCREEN_TYPE_NO_UI:
+ mContainerView.setVisibility(GONE);
+ break;
+ case TunableTvView.BLOCK_SCREEN_TYPE_SHRUNKEN_TV_VIEW:
+ mNormalImageView.setVisibility(GONE);
+ mShrunkenImageView.setVisibility(VISIBLE);
+ mContainerView.setVisibility(VISIBLE);
+ break;
+ case TunableTvView.BLOCK_SCREEN_TYPE_NORMAL:
+ mNormalImageView.setVisibility(VISIBLE);
+ mShrunkenImageView.setVisibility(GONE);
+ mContainerView.setVisibility(VISIBLE);
+ break;
+ }
+ } else {
+ switch (blockScreenType) {
+ case TunableTvView.BLOCK_SCREEN_TYPE_NO_UI:
+ if (mContainerView.getVisibility() == VISIBLE) {
+ mFadeOut.start();
+ }
+ break;
+ case TunableTvView.BLOCK_SCREEN_TYPE_SHRUNKEN_TV_VIEW:
+ mNormalImageView.setVisibility(GONE);
+ mShrunkenImageView.setVisibility(VISIBLE);
+ mContainerView.setVisibility(VISIBLE);
+ if (mContainerView.getVisibility() == GONE) {
+ mFadeIn.start();
+ }
+ break;
+ case TunableTvView.BLOCK_SCREEN_TYPE_NORMAL:
+ mNormalImageView.setVisibility(VISIBLE);
+ mShrunkenImageView.setVisibility(GONE);
+ mContainerView.setVisibility(VISIBLE);
+ if (mContainerView.getVisibility() == GONE) {
+ mFadeIn.start();
+ }
+ break;
+ }
+ }
+ updateSpaceVisibility();
+ }
+
+ /**
+ * Scales the contents view by the given {@code scale}.
+ */
+ public void scaleContainerView(float scale) {
+ mContainerView.setScaleX(scale);
+ mContainerView.setScaleY(scale);
+ }
+
+ public void addFadeOutAnimationListener(AnimatorListener listener) {
+ mFadeOut.addListener(listener);
+ }
+
+ /**
+ * Ends the currently running animations.
+ */
+ public void endAnimations() {
+ if (mFadeIn != null && mFadeIn.isRunning()) {
+ mFadeIn.end();
+ }
+ if (mFadeOut != null && mFadeOut.isRunning()) {
+ mFadeOut.end();
+ }
+ }
+}
diff --git a/src/com/android/tv/ui/ChannelBannerView.java b/src/com/android/tv/ui/ChannelBannerView.java
index 17ac8f3b..a36ba83c 100644
--- a/src/com/android/tv/ui/ChannelBannerView.java
+++ b/src/com/android/tv/ui/ChannelBannerView.java
@@ -16,8 +16,6 @@
package com.android.tv.ui;
-import static com.android.tv.util.ImageLoader.ImageLoaderCallback;
-
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
@@ -35,8 +33,10 @@ import android.support.annotation.Nullable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
+import android.text.format.DateUtils;
import android.text.style.TextAppearanceSpan;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
@@ -50,11 +50,13 @@ import android.widget.TextView;
import com.android.tv.MainActivity;
import com.android.tv.R;
+import com.android.tv.common.recording.RecordedProgram;
import com.android.tv.data.Channel;
import com.android.tv.data.Program;
import com.android.tv.data.StreamInfo;
import com.android.tv.util.ImageCache;
import com.android.tv.util.ImageLoader;
+import com.android.tv.util.ImageLoader.ImageLoaderCallback;
import com.android.tv.util.ImageLoader.LoadTvInputLogoTask;
import com.android.tv.util.Utils;
@@ -66,6 +68,8 @@ import java.util.Objects;
* A view to render channel banner.
*/
public class ChannelBannerView extends FrameLayout implements TvTransitionManager.TransitionLayout {
+ private static final String TAG = "ChannelBannerView";
+ private static final boolean DEBUG = false;
/**
* Show all information at the channel banner.
@@ -111,6 +115,7 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
private View mAnchorView;
private Channel mCurrentChannel;
private Program mLastUpdatedProgram;
+ private RecordedProgram mLastUpdatedRecordedProgram;
private final Handler mHandler = new Handler();
private int mLockType;
@@ -235,6 +240,7 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
@Override
protected void onAttachedToWindow() {
+ if (DEBUG) Log.d(TAG, "onAttachedToWindow");
super.onAttachedToWindow();
getContext().getContentResolver().registerContentObserver(TvContract.Programs.CONTENT_URI,
true, mProgramUpdateObserver);
@@ -242,6 +248,7 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
@Override
protected void onDetachedFromWindow() {
+ if (DEBUG) Log.d(TAG, "onDetachedToWindow");
getContext().getContentResolver().unregisterContentObserver(mProgramUpdateObserver);
super.onDetachedFromWindow();
}
@@ -329,8 +336,6 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
/**
* Update channel banner view.
- * Note that this only updates the channel banner contents,
- * and use onBeforeShow() or onAfterHide() for showing/hiding.
*
* @param info A StreamInfo that includes stream information.
* If it's {@code null}, only program information will be updated.
@@ -342,19 +347,19 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
scheduleHide();
}
mCurrentChannel = channel;
- if (channel == null) {
- mLastUpdatedProgram = null;
- updateProgramInfo(null);
- return;
- }
mChannelView.setVisibility(VISIBLE);
if (info != null) {
// If the current channels between ChannelTuner and TvView are different,
// the stream information should not be seen.
- updateStreamInfo(channel.equals(info.getCurrentChannel()) ? info : null);
+ updateStreamInfo(channel != null && channel.equals(info.getCurrentChannel()) ? info
+ : null);
updateChannelInfo();
}
- updateProgramInfo(mMainActivity.getCurrentProgram());
+ if (mMainActivity.isRecordingPlayback()) {
+ updateProgramInfo(mMainActivity.getPlayingRecordedProgram());
+ } else {
+ updateProgramInfo(mMainActivity.getCurrentProgram());
+ }
}
private void updateStreamInfo(StreamInfo info) {
@@ -363,7 +368,7 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
updateText(mClosedCaptionTextView, info.hasClosedCaption() ? sClosedCaptionMark
: EMPTY_STRING);
updateText(mAspectRatioTextView,
- Utils.getAspectRatioString(info.getVideoWidth(), info.getVideoHeight()));
+ Utils.getAspectRatioString(info.getVideoDisplayAspectRatio()));
updateText(mResolutionTextView,
Utils.getVideoDefinitionLevelString(
mMainActivity, info.getVideoDefinitionLevel()));
@@ -380,11 +385,24 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
private void updateChannelInfo() {
// Update static information for a channel.
- String displayNumber = mCurrentChannel.getDisplayNumber();
- if (displayNumber == null) {
- displayNumber = EMPTY_STRING;
+ String displayNumber = EMPTY_STRING;
+ String displayName = EMPTY_STRING;
+ if (mCurrentChannel != null) {
+ displayNumber = mCurrentChannel.getDisplayNumber();
+ if (displayNumber == null) {
+ displayNumber = EMPTY_STRING;
+ }
+ displayName = mCurrentChannel.getDisplayName();
+ if (displayName == null) {
+ displayName = EMPTY_STRING;
+ }
}
+ if (displayNumber.isEmpty()) {
+ mChannelNumberTextView.setVisibility(GONE);
+ } else {
+ mChannelNumberTextView.setVisibility(VISIBLE);
+ }
if (displayNumber.length() <= 3) {
updateTextView(
mChannelNumberTextView,
@@ -402,14 +420,9 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
R.dimen.channel_banner_channel_number_small_margin_top);
}
mChannelNumberTextView.setText(displayNumber);
- String displayName = mCurrentChannel.getDisplayName();
- if (displayName == null) {
- displayName = EMPTY_STRING;
- }
mChannelNameTextView.setText(displayName);
-
TvInputInfo info = mMainActivity.getTvInputManagerHelper().getTvInputInfo(
- mCurrentChannel.getInputId());
+ getCurrentInputId());
if (info == null || !ImageLoader.loadBitmap(createTvInputLogoLoaderCallback(info, this),
new LoadTvInputLogoTask(getContext(), ImageCache.getInstance(), info))) {
mTvInputLogoImageView.setVisibility(View.GONE);
@@ -417,9 +430,24 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
}
mChannelLogoImageView.setImageBitmap(null);
mChannelLogoImageView.setVisibility(View.GONE);
- mCurrentChannel.loadBitmap(getContext(), Channel.LOAD_IMAGE_TYPE_CHANNEL_LOGO,
- mChannelLogoImageViewWidth, mChannelLogoImageViewHeight,
- createChannelLogoCallback(this, mCurrentChannel));
+ if (mCurrentChannel != null) {
+ mCurrentChannel.loadBitmap(getContext(), Channel.LOAD_IMAGE_TYPE_CHANNEL_LOGO,
+ mChannelLogoImageViewWidth, mChannelLogoImageViewHeight,
+ createChannelLogoCallback(this, mCurrentChannel));
+ }
+ }
+
+ private String getCurrentInputId() {
+ Channel channel = mMainActivity.getCurrentChannel();
+ if (channel != null) {
+ return channel.getInputId();
+ } else if (mMainActivity.isRecordingPlayback()) {
+ RecordedProgram recordedProgram = mMainActivity.getPlayingRecordedProgram();
+ if (recordedProgram != null) {
+ return recordedProgram.getInputId();
+ }
+ }
+ return null;
}
private void updateTvInputLogo(Bitmap bitmap) {
@@ -432,8 +460,8 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
return new ImageLoaderCallback<ChannelBannerView>(channelBannerView) {
@Override
public void onBitmapLoaded(ChannelBannerView channelBannerView, Bitmap bitmap) {
- if (bitmap != null && info.getId()
- .equals(channelBannerView.mCurrentChannel.getInputId())) {
+ if (bitmap != null && channelBannerView.mCurrentChannel != null
+ && info.getId().equals(channelBannerView.mCurrentChannel.getInputId())) {
channelBannerView.updateTvInputLogo(bitmap);
}
}
@@ -510,7 +538,7 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
if (mLastUpdatedProgram == null
|| !TextUtils.equals(program.getTitle(), mLastUpdatedProgram.getTitle())
|| !TextUtils.equals(program.getEpisodeDisplayTitle(getContext()),
- mLastUpdatedProgram.getEpisodeDisplayTitle(getContext()))) {
+ mLastUpdatedProgram.getEpisodeDisplayTitle(getContext()))) {
updateProgramTextView(program);
}
updateProgramTimeInfo(program);
@@ -519,7 +547,7 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
// cancel the animation.
boolean isProgramChanged = !Objects.equals(mLastUpdatedProgram, program);
if (mResizeAnimator != null && isProgramChanged) {
- mLastUpdatedProgram = program;
+ setLastUpdatedProgram(program);
mProgramInfoUpdatePendingByResizing = true;
mResizeAnimator.cancel();
} else if (mResizeAnimator == null) {
@@ -537,16 +565,73 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
} else {
mProgramInfoUpdatePendingByResizing = true;
}
- mLastUpdatedProgram = program;
+ setLastUpdatedProgram(program);
+ }
+
+ private void updateProgramInfo(RecordedProgram recordedProgram) {
+ if (mLockType == LOCK_CHANNEL_INFO) {
+ updateProgramInfo(sLockedChannelProgram);
+ return;
+ } else if (recordedProgram == null) {
+ updateProgramInfo(sNoProgram);
+ return;
+ }
+
+ if (mLastUpdatedRecordedProgram == null
+ || !TextUtils.equals(recordedProgram.getTitle(),
+ mLastUpdatedRecordedProgram.getTitle())
+ || !TextUtils.equals(recordedProgram.getEpisodeDisplayTitle(getContext()),
+ mLastUpdatedRecordedProgram.getEpisodeDisplayTitle(getContext()))) {
+ updateProgramTextView(recordedProgram);
+ }
+ updateProgramTimeInfo(recordedProgram);
+
+ // When the program is changed, but the previous resize animation has not ended yet,
+ // cancel the animation.
+ boolean isProgramChanged = !Objects.equals(mLastUpdatedRecordedProgram, recordedProgram);
+ if (mResizeAnimator != null && isProgramChanged) {
+ setLastUpdatedRecordedProgram(recordedProgram);
+ mProgramInfoUpdatePendingByResizing = true;
+ mResizeAnimator.cancel();
+ } else if (mResizeAnimator == null) {
+ if (mLockType != LOCK_NONE
+ || TextUtils.isEmpty(recordedProgram.getShortDescription())) {
+ mProgramDescriptionTextView.setVisibility(GONE);
+ mProgramDescriptionText = "";
+ } else {
+ mProgramDescriptionTextView.setVisibility(VISIBLE);
+ mProgramDescriptionText = recordedProgram.getShortDescription();
+ }
+ String description = mProgramDescriptionTextView.getText().toString();
+ boolean needFadeAnimation = isProgramChanged
+ || !description.equals(mProgramDescriptionText);
+ updateBannerHeight(needFadeAnimation);
+ } else {
+ mProgramInfoUpdatePendingByResizing = true;
+ }
+ setLastUpdatedRecordedProgram(recordedProgram);
}
private void updateProgramTextView(Program program) {
if (program == null) {
return;
}
+ updateProgramTextView(program == sLockedChannelProgram, program.getTitle(),
+ program.getEpisodeTitle(), program.getEpisodeDisplayTitle(getContext()));
+ }
+
+ private void updateProgramTextView(RecordedProgram recordedProgram) {
+ if (recordedProgram == null) {
+ return;
+ }
+ updateProgramTextView(false, recordedProgram.getTitle(), recordedProgram.getEpisodeTitle(),
+ recordedProgram.getEpisodeDisplayTitle(getContext()));
+ }
+ private void updateProgramTextView(boolean dimText, String title, String episodeTitle,
+ String episodeDisplayTitle) {
mProgramTextView.setVisibility(View.VISIBLE);
- if (program == sLockedChannelProgram) {
+ if (dimText) {
mProgramTextView.setTextColor(mChannelBannerDimTextColor);
} else {
mProgramTextView.setTextColor(mChannelBannerTextColor);
@@ -554,17 +639,15 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
updateTextView(mProgramTextView,
R.dimen.channel_banner_program_large_text_size,
R.dimen.channel_banner_program_large_margin_top);
- if (TextUtils.isEmpty(program.getEpisodeTitle())) {
- mProgramTextView.setText(program.getTitle());
+ if (TextUtils.isEmpty(episodeTitle)) {
+ mProgramTextView.setText(title);
} else {
- String title = program.getTitle();
- String episodeTitle = program.getEpisodeDisplayTitle(getContext());
- String fullTitle = title + " " + episodeTitle;
+ String fullTitle = title + " " + episodeDisplayTitle;
SpannableString text = new SpannableString(fullTitle);
text.setSpan(new TextAppearanceSpan(getContext(),
R.style.text_appearance_channel_banner_episode_title),
- fullTitle.length() - episodeTitle.length(), fullTitle.length(),
+ fullTitle.length() - episodeDisplayTitle.length(), fullTitle.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mProgramTextView.setText(text);
}
@@ -617,6 +700,38 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
}
}
+ private void updateProgramTimeInfo(RecordedProgram recordedProgram) {
+ long durationMs = recordedProgram.getDurationMillis();
+ if (mLockType != LOCK_CHANNEL_INFO && durationMs > 0) {
+ mProgramTimeTextView.setVisibility(View.VISIBLE);
+ mRemainingTimeView.setVisibility(View.VISIBLE);
+
+ mProgramTimeTextView.setText(DateUtils.formatElapsedTime(durationMs / 1000));
+
+ long currTimeMs = mMainActivity.getCurrentPlayingPosition();
+ if (currTimeMs <= 0) {
+ mRemainingTimeView.setProgress(0);
+ } else if (currTimeMs >= durationMs) {
+ mRemainingTimeView.setProgress(100);
+ } else {
+ mRemainingTimeView.setProgress((int) (100 * currTimeMs / durationMs));
+ }
+ } else {
+ mProgramTimeTextView.setVisibility(View.GONE);
+ mRemainingTimeView.setVisibility(View.GONE);
+ }
+ }
+
+ private void setLastUpdatedProgram(Program program) {
+ mLastUpdatedProgram = program;
+ mLastUpdatedRecordedProgram = null;
+ }
+
+ private void setLastUpdatedRecordedProgram(RecordedProgram recordedProgram) {
+ mLastUpdatedProgram = null;
+ mLastUpdatedRecordedProgram = recordedProgram;
+ }
+
private void updateBannerHeight(boolean needFadeAnimation) {
Assert.assertNull(mResizeAnimator);
// Need to measure the layout height with the new description text.
diff --git a/src/com/android/tv/ui/DialogUtils.java b/src/com/android/tv/ui/DialogUtils.java
new file mode 100644
index 00000000..acbaf8c8
--- /dev/null
+++ b/src/com/android/tv/ui/DialogUtils.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.ui;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Resources;
+
+import com.android.tv.common.SoftPreconditions;
+
+public final class DialogUtils {
+
+ /**
+ * Shows a list in a Dialog.
+ *
+ * @param itemResIds String resource id for each item
+ * @param runnables Runnable for each item
+ */
+ public static void showListDialog(Context context, int[] itemResIds,
+ final Runnable[] runnables) {
+ int size = itemResIds.length;
+ SoftPreconditions.checkState(size == runnables.length);
+ DialogInterface.OnClickListener onClickListener
+ = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(final DialogInterface dialog, int which) {
+ Runnable runnable = runnables[which];
+ if (runnable != null) {
+ runnable.run();
+ }
+ dialog.dismiss();
+ }
+ };
+ CharSequence[] items = new CharSequence[itemResIds.length];
+ Resources res = context.getResources();
+ for (int i = 0; i < size; ++i) {
+ items[i] = res.getString(itemResIds[i]);
+ }
+ new AlertDialog.Builder(context)
+ .setItems(items, onClickListener)
+ .create()
+ .show();
+ }
+
+ private DialogUtils() { }
+}
diff --git a/src/com/android/tv/ui/KeypadChannelSwitchView.java b/src/com/android/tv/ui/KeypadChannelSwitchView.java
index cf43fc9b..abc05bad 100644
--- a/src/com/android/tv/ui/KeypadChannelSwitchView.java
+++ b/src/com/android/tv/ui/KeypadChannelSwitchView.java
@@ -41,9 +41,9 @@ import com.android.tv.R;
import com.android.tv.TvApplication;
import com.android.tv.analytics.DurationTimer;
import com.android.tv.analytics.Tracker;
+import com.android.tv.common.SoftPreconditions;
import com.android.tv.data.Channel;
import com.android.tv.data.ChannelNumber;
-import com.android.tv.util.SoftPreconditions;
import java.util.ArrayList;
import java.util.List;
diff --git a/src/com/android/tv/ui/OnRepeatedKeyInterceptListener.java b/src/com/android/tv/ui/OnRepeatedKeyInterceptListener.java
index afea9ba5..63ee199d 100644
--- a/src/com/android/tv/ui/OnRepeatedKeyInterceptListener.java
+++ b/src/com/android/tv/ui/OnRepeatedKeyInterceptListener.java
@@ -35,8 +35,8 @@ public class OnRepeatedKeyInterceptListener implements VerticalGridView.OnKeyInt
private static final int[] MAX_SKIPPED_VIEW_COUNT = { 1, 4 };
private static final int MSG_MOVE_FOCUS = 1000;
- private VerticalGridView mView;
- private MyHandler mHandler = new MyHandler(this);
+ private final VerticalGridView mView;
+ private final MyHandler mHandler = new MyHandler(this);
private int mDirection;
private boolean mFocusAccelerated;
private long mRepeatedKeyInterval;
diff --git a/src/com/android/tv/ui/SelectInputView.java b/src/com/android/tv/ui/SelectInputView.java
index 032782bd..646f9159 100644
--- a/src/com/android/tv/ui/SelectInputView.java
+++ b/src/com/android/tv/ui/SelectInputView.java
@@ -249,14 +249,16 @@ public class SelectInputView extends VerticalGridView implements
boolean foundTuner = false;
for (TvInputInfo input : mTvInputManagerHelper.getTvInputInfos(false, false)) {
if (input.isPassthroughInput()) {
- mInputList.add(input);
- inputMap.put(input.getId(), input);
+ if (!input.isHidden(getContext())) {
+ mInputList.add(input);
+ inputMap.put(input.getId(), input);
+ }
} else if (!foundTuner) {
foundTuner = true;
mInputList.add(input);
}
}
- // Do not show an AVR if an HDMI device is connected to it.
+ // Do not show HDMI ports if a CEC device is directly connected to the port.
for (TvInputInfo input : inputMap.values()) {
if (input.getParentId() != null && !input.isConnectedToHdmiSwitch()) {
mInputList.remove(inputMap.get(input.getParentId()));
diff --git a/src/com/android/tv/ui/TunableTvView.java b/src/com/android/tv/ui/TunableTvView.java
index 286bc1f9..6d3d62aa 100644
--- a/src/com/android/tv/ui/TunableTvView.java
+++ b/src/com/android/tv/ui/TunableTvView.java
@@ -17,11 +17,11 @@
package com.android.tv.ui;
import android.animation.Animator;
-import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
import android.animation.TimeInterpolator;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
+import android.content.ContentUris;
import android.content.Context;
import android.content.pm.PackageManager;
import android.media.PlaybackParams;
@@ -29,13 +29,18 @@ import android.media.tv.TvContentRating;
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager;
import android.media.tv.TvTrackInfo;
+import android.media.tv.TvView;
import android.media.tv.TvView.OnUnhandledInputEventListener;
import android.media.tv.TvView.TvInputCallback;
+import android.net.ConnectivityManager;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
+import android.support.annotation.UiThread;
+import android.support.v4.os.BuildCompat;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
@@ -46,50 +51,57 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
-import android.widget.TextView;
import com.android.tv.ApplicationSingletons;
import com.android.tv.R;
import com.android.tv.TvApplication;
import com.android.tv.analytics.DurationTimer;
import com.android.tv.analytics.Tracker;
-import com.android.tv.common.recording.PlaybackTvView;
-import com.android.tv.common.recording.RecordingUtils;
+import com.android.tv.common.feature.CommonFeatures;
+import com.android.tv.common.recording.RecordedProgram;
import com.android.tv.data.Channel;
+import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.StreamInfo;
import com.android.tv.data.WatchedHistoryManager;
+import com.android.tv.dvr.DvrDataManager;
import com.android.tv.parental.ContentRatingsManager;
import com.android.tv.recommendation.NotificationService;
+import com.android.tv.util.NetworkUtils;
import com.android.tv.util.PermissionUtils;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.Utils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.List;
public class TunableTvView extends FrameLayout implements StreamInfo {
private static final boolean DEBUG = false;
private static final String TAG = "TunableTvView";
- public static final String PERMISSION_RECEIVE_INPUT_EVENT =
- "com.android.tv.permission.RECEIVE_INPUT_EVENT";
-
public static final int VIDEO_UNAVAILABLE_REASON_NOT_TUNED = -1;
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({BLOCK_SCREEN_TYPE_NO_UI, BLOCK_SCREEN_TYPE_SHRUNKEN_TV_VIEW, BLOCK_SCREEN_TYPE_NORMAL})
+ public @interface BlockScreenType {}
public static final int BLOCK_SCREEN_TYPE_NO_UI = 0;
public static final int BLOCK_SCREEN_TYPE_SHRUNKEN_TV_VIEW = 1;
public static final int BLOCK_SCREEN_TYPE_NORMAL = 2;
+ private static final String PERMISSION_RECEIVE_INPUT_EVENT =
+ "com.android.tv.permission.RECEIVE_INPUT_EVENT";
+
@Retention(RetentionPolicy.SOURCE)
@IntDef({ TIME_SHIFT_STATE_NONE, TIME_SHIFT_STATE_PLAY, TIME_SHIFT_STATE_PAUSE,
- TIME_SHIFT_STATE_REWIND, TIME_SHIFT_STATE_FAST_FORWARD })
- public @interface TimeShiftState {}
- public static final int TIME_SHIFT_STATE_NONE = 0;
- public static final int TIME_SHIFT_STATE_PLAY = 1;
- public static final int TIME_SHIFT_STATE_PAUSE = 2;
- public static final int TIME_SHIFT_STATE_REWIND = 3;
- public static final int TIME_SHIFT_STATE_FAST_FORWARD = 4;
+ TIME_SHIFT_STATE_REWIND, TIME_SHIFT_STATE_FAST_FORWARD })
+ private @interface TimeShiftState {}
+ private static final int TIME_SHIFT_STATE_NONE = 0;
+ private static final int TIME_SHIFT_STATE_PLAY = 1;
+ private static final int TIME_SHIFT_STATE_PAUSE = 2;
+ private static final int TIME_SHIFT_STATE_REWIND = 3;
+ private static final int TIME_SHIFT_STATE_FAST_FORWARD = 4;
private static final int FADED_IN = 0;
private static final int FADED_OUT = 1;
@@ -103,6 +115,7 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
private AppLayerTvView mTvView;
private Channel mCurrentChannel;
+ private RecordedProgram mRecordedProgram;
private TvInputManagerHelper mInputManagerHelper;
private ContentRatingsManager mContentRatingsManager;
@Nullable
@@ -114,6 +127,7 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
private int mVideoHeight;
private int mVideoFormat = StreamInfo.VIDEO_DEFINITION_LEVEL_UNKNOWN;
private float mVideoFrameRate;
+ private float mVideoDisplayAspectRatio;
private int mAudioChannelCount = StreamInfo.AUDIO_CHANNEL_COUNT_UNKNOWN;
private boolean mHasClosedCaption = false;
private boolean mVideoAvailable;
@@ -130,7 +144,7 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
private boolean mIsPip;
private int mScreenHeight;
private int mShrunkenTvViewHeight;
- private boolean mCanModifyParentalControls;
+ private final boolean mCanModifyParentalControls;
@TimeShiftState private int mTimeShiftState = TIME_SHIFT_STATE_NONE;
private TimeShiftListener mTimeShiftListener;
@@ -139,22 +153,14 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
private final Tracker mTracker;
private final DurationTimer mChannelViewTimer = new DurationTimer();
+ private InternetCheckTask mInternetCheckTask;
// A block screen view which has lock icon with black background.
// This indicates that user's action is needed to play video.
- private final View mBlockScreenView;
-
- private final View mBlockScreenDescriptionView;
- private final ImageView mBlockScreenIconView;
- private final View mBlockScreenShrunkenIconView;
- private final TextView mBlockScreenTextView;
-
- // Animators used for fade in/out of block screen icon.
- private final Animator mBlockScreenDescriptionFadeIn;
- private final Animator mBlockScreenDescriptionFadeOut;
+ private final BlockScreenView mBlockScreenView;
// A View to hide screen when there's problem in video playback.
- private final TextView mHideScreenView;
+ private final BlockScreenView mHideScreenView;
// A View to block screen until onContentAllowed is received if parental control is on.
private final View mBlockScreenForTuneView;
@@ -167,7 +173,11 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
private int mFadeState = FADED_IN;
private Runnable mActionAfterFade;
- private int mBlockScreenType;
+ @BlockScreenType private int mBlockScreenType;
+
+ private final DvrDataManager mDvrDataManager;
+ private final ChannelDataManager mChannelDataManager;
+ private final ConnectivityManager mConnectivityManager;
private final TvInputCallback mCallback =
new TvInputCallback() {
@@ -239,6 +249,7 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
mVideoHeight = 0;
mVideoFormat = StreamInfo.VIDEO_DEFINITION_LEVEL_UNKNOWN;
mVideoFrameRate = 0f;
+ mVideoDisplayAspectRatio = 0f;
} else if (type == TvTrackInfo.TYPE_AUDIO) {
mAudioChannelCount = StreamInfo.AUDIO_CHANNEL_COUNT_UNKNOWN;
}
@@ -254,6 +265,18 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
mVideoFormat = Utils.getVideoDefinitionLevelFromSize(
mVideoWidth, mVideoHeight);
mVideoFrameRate = track.getVideoFrameRate();
+ if (mVideoWidth <= 0 || mVideoHeight <= 0) {
+ mVideoDisplayAspectRatio = 0.0f;
+ } else if (android.os.Build.VERSION.SDK_INT >=
+ android.os.Build.VERSION_CODES.M) {
+ float VideoPixelAspectRatio =
+ track.getVideoPixelAspectRatio();
+ mVideoDisplayAspectRatio = VideoPixelAspectRatio
+ * mVideoWidth / mVideoHeight;
+ } else {
+ mVideoDisplayAspectRatio = mVideoWidth
+ / (float) mVideoHeight;
+ }
} else if (type == TvTrackInfo.TYPE_AUDIO) {
mAudioChannelCount = track.getAudioChannelCount();
}
@@ -315,7 +338,8 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
@Override
@TargetApi(Build.VERSION_CODES.M)
public void onTimeShiftStatusChanged(String inputId, int status) {
- setTimeShiftAvailable(status == TvInputManager.TIME_SHIFT_STATUS_AVAILABLE);
+ boolean available = status == TvInputManager.TIME_SHIFT_STATUS_AVAILABLE;
+ setTimeShiftAvailable(available);
}
};
@@ -336,53 +360,32 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
inflate(getContext(), R.layout.tunable_tv_view, this);
ApplicationSingletons appSingletons = TvApplication.getSingletons(context);
+ mDvrDataManager = CommonFeatures.DVR.isEnabled(context) && BuildCompat.isAtLeastN()
+ ? appSingletons.getDvrDataManager()
+ : null;
+ mChannelDataManager = appSingletons.getChannelDataManager();
+ mConnectivityManager = (ConnectivityManager) context
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
mCanModifyParentalControls = PermissionUtils.hasModifyParentalControls(context);
mTracker = appSingletons.getTracker();
mBlockScreenType = BLOCK_SCREEN_TYPE_NORMAL;
- mBlockScreenView = findViewById(R.id.block_screen);
- mBlockScreenDescriptionView = findViewById(R.id.block_screen_description);
-
- mBlockScreenIconView = (ImageView) mBlockScreenView.findViewById(R.id.block_screen_icon);
+ mBlockScreenView = (BlockScreenView) findViewById(R.id.block_screen);
if (!mCanModifyParentalControls) {
- mBlockScreenIconView.setImageResource(R.drawable.ic_message_lock_no_permission);
- mBlockScreenIconView.setScaleType(ImageView.ScaleType.CENTER);
+ mBlockScreenView.setImage(R.drawable.ic_message_lock_no_permission);
+ mBlockScreenView.setScaleType(ImageView.ScaleType.CENTER);
+ } else {
+ mBlockScreenView.setImage(R.drawable.ic_message_lock);
}
- mBlockScreenShrunkenIconView = mBlockScreenView.findViewById(
- R.id.block_screen_shrunken_icon);
- mBlockScreenTextView = (TextView) mBlockScreenView.findViewById(R.id.block_screen_text);
-
- mBlockScreenDescriptionFadeIn = AnimatorInflater.loadAnimator(context,
- R.animator.tvview_block_screen_fade_in);
- mBlockScreenDescriptionFadeIn.setTarget(mBlockScreenDescriptionView);
- mBlockScreenDescriptionFadeIn.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- switch (mBlockScreenType) {
- case BLOCK_SCREEN_TYPE_NORMAL:
- mBlockScreenIconView.setVisibility(VISIBLE);
- mBlockScreenShrunkenIconView.setVisibility(GONE);
- break;
- case BLOCK_SCREEN_TYPE_SHRUNKEN_TV_VIEW:
- mBlockScreenIconView.setVisibility(GONE);
- mBlockScreenShrunkenIconView.setVisibility(VISIBLE);
- break;
- }
- mBlockScreenDescriptionView.setVisibility(VISIBLE);
- }
- });
- mBlockScreenDescriptionFadeOut = AnimatorInflater.loadAnimator(context,
- R.animator.tvview_block_screen_fade_out);
- mBlockScreenDescriptionFadeOut.setTarget(mBlockScreenDescriptionView);
- mBlockScreenDescriptionFadeOut.addListener(new AnimatorListenerAdapter() {
+ mBlockScreenView.setShrunkenImage(R.drawable.ic_message_lock_preview);
+ mBlockScreenView.addFadeOutAnimationListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- mBlockScreenDescriptionView.setVisibility(GONE);
- mBlockScreenDescriptionView.setAlpha(1f);
- updateBlockScreenTextView();
+ adjustBlockScreenSpacingAndText();
}
});
- mHideScreenView = (TextView) findViewById(R.id.hide_screen);
+ mHideScreenView = (BlockScreenView) findViewById(R.id.hide_screen);
+ mHideScreenView.setImageVisibility(false);
mBufferingSpinnerView = findViewById(R.id.buffering_spinner);
mBlockScreenForTuneView = findViewById(R.id.block_screen_for_tune);
mDimScreenView = findViewById(R.id.dim);
@@ -441,6 +444,7 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
public void reset() {
mTvView.reset();
mCurrentChannel = null;
+ mRecordedProgram = null;
mInputInfo = null;
mCanReceiveInputEvent = false;
mOnTuneListener = null;
@@ -471,15 +475,82 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
}
/**
+ * Returns {@code true}, if this view is the recording playback mode.
+ */
+ public boolean isRecordingPlayback() {
+ return mRecordedProgram != null;
+ }
+
+ /**
+ * Returns the recording which is being played right now.
+ */
+ public RecordedProgram getPlayingRecordedProgram() {
+ return mRecordedProgram;
+ }
+
+ /**
* Plays a recording.
*/
- public boolean playRecording(String inputId, Uri recordingUri, OnTuneListener listener) {
- // Create a dummy channel.
- Channel channel = new Channel.Builder()
- .setId(0)
- .setInputId(inputId)
- .build();
- return tuneTo(channel, RecordingUtils.buildMediaUri(recordingUri), listener);
+ public boolean playRecording(Uri recordingUri, OnTuneListener listener) {
+ if (!mStarted) {
+ throw new IllegalStateException("TvView isn't started");
+ }
+ if (!CommonFeatures.DVR.isEnabled(getContext()) || !BuildCompat.isAtLeastN()) {
+ return false;
+ }
+ if (DEBUG) Log.d(TAG, "playRecording " + recordingUri);
+ long recordingId = ContentUris.parseId(recordingUri);
+ mRecordedProgram = mDvrDataManager.getRecordedProgram(recordingId);
+ if (mRecordedProgram == null) {
+ Log.w(TAG, "No recorded program (Uri=" + recordingUri + ")");
+ return false;
+ }
+ String inputId = mRecordedProgram.getInputId();
+ TvInputInfo inputInfo = mInputManagerHelper.getTvInputInfo(inputId);
+ if (inputInfo == null) {
+ return false;
+ }
+ mOnTuneListener = listener;
+ // mCurrentChannel can be null.
+ mCurrentChannel = mChannelDataManager.getChannel(mRecordedProgram.getChannelId());
+ // For recording playback, input event should not be sent.
+ mCanReceiveInputEvent = false;
+ boolean needSurfaceSizeUpdate = false;
+ if (!inputInfo.equals(mInputInfo)) {
+ mInputInfo = inputInfo;
+ if (DEBUG) {
+ Log.d(TAG, "Input \'" + mInputInfo.getId() + "\' can receive input event: "
+ + mCanReceiveInputEvent);
+ }
+ needSurfaceSizeUpdate = true;
+ }
+ mChannelViewTimer.start();
+ mVideoWidth = 0;
+ mVideoHeight = 0;
+ mVideoFormat = StreamInfo.VIDEO_DEFINITION_LEVEL_UNKNOWN;
+ mVideoFrameRate = 0f;
+ mVideoDisplayAspectRatio = 0f;
+ mAudioChannelCount = StreamInfo.AUDIO_CHANNEL_COUNT_UNKNOWN;
+ mHasClosedCaption = false;
+ mTvView.setCallback(mCallback);
+ mTimeShiftCurrentPositionMs = INVALID_TIME;
+ mTvView.setTimeShiftPositionCallback(null);
+ setTimeShiftAvailable(false);
+ mTvView.timeShiftPlay(inputId, recordingUri);
+ if (needSurfaceSizeUpdate && mFixedSurfaceWidth > 0 && mFixedSurfaceHeight > 0) {
+ // When the input is changed, TvView recreates its SurfaceView internally.
+ // So we need to call SurfaceHolder.setFixedSize for the new SurfaceView.
+ getSurfaceView().getHolder().setFixedSize(mFixedSurfaceWidth, mFixedSurfaceHeight);
+ }
+ hideScreenByVideoAvailability(TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING);
+ unblockScreenByContentRating();
+ if (mParentControlEnabled) {
+ mBlockScreenForTuneView.setVisibility(View.VISIBLE);
+ }
+ if (mOnTuneListener != null) {
+ mOnTuneListener.onStreamInfoChanged(this);
+ }
+ return true;
}
/**
@@ -508,6 +579,7 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
}
mOnTuneListener = listener;
mCurrentChannel = channel;
+ mRecordedProgram = null;
boolean tunedByRecommendation = params != null
&& params.getString(NotificationService.TUNE_PARAMS_RECOMMENDATION_TYPE) != null;
boolean needSurfaceSizeUpdate = false;
@@ -528,6 +600,7 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
mVideoHeight = 0;
mVideoFormat = StreamInfo.VIDEO_DEFINITION_LEVEL_UNKNOWN;
mVideoFrameRate = 0f;
+ mVideoDisplayAspectRatio = 0f;
mAudioChannelCount = StreamInfo.AUDIO_CHANNEL_COUNT_UNKNOWN;
mHasClosedCaption = false;
mTvView.setCallback(mCallback);
@@ -590,8 +663,7 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
* Note: Once {@link android.view.SurfaceHolder#setFixedSize} is called,
* {@link android.view.SurfaceView} and its underlying window can be misaligned, when the size
* of {@link android.view.SurfaceView} is changed without changing either left position or top
- * position. For detail, please refer the codes of {@link android.view.SurfaceView#updateWindow}
- * .
+ * position. For detail, please refer the codes of android.view.SurfaceView.updateWindow().
*/
public void setFixedSurfaceSize(int width, int height) {
mFixedSurfaceWidth = width;
@@ -636,8 +708,18 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
void onContentAllowed();
}
- public void requestUnblockContent(TvContentRating rating) {
- mTvView.unblockContent(rating);
+ public void unblockContent(TvContentRating rating) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+ try {
+ Method method = TvView.class.getMethod("requestUnblockContent",
+ TvContentRating.class);
+ method.invoke(mTvView, rating);
+ } catch (NoSuchMethodException|IllegalAccessException|InvocationTargetException e) {
+ e.printStackTrace();
+ }
+ } else {
+ mTvView.unblockContent(rating);
+ }
}
@Override
@@ -660,6 +742,14 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
return mVideoFrameRate;
}
+ /**
+ * Returns displayed aspect ratio (video width / video height * pixel ratio).
+ */
+ @Override
+ public float getVideoDisplayAspectRatio() {
+ return mVideoDisplayAspectRatio;
+ }
+
@Override
public int getAudioChannelCount() {
return mAudioChannelCount;
@@ -683,7 +773,7 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
/**
* Returns the {@link android.view.SurfaceView} of the {@link android.media.tv.TvView}.
*/
- public SurfaceView getSurfaceView() {
+ private SurfaceView getSurfaceView() {
return (SurfaceView) mTvView.getChildAt(0);
}
@@ -757,8 +847,7 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
scale = height * PIP_BLOCK_SCREEN_SCALE_FACTOR / mScreenHeight;
}
// TODO: need to get UX confirmation.
- mBlockScreenDescriptionView.setScaleX(scale);
- mBlockScreenDescriptionView.setScaleY(scale);
+ mBlockScreenView.scaleContainerView(scale);
}
}
@@ -817,7 +906,7 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
*
* @param type The type of block screen to set.
*/
- public void setBlockScreenType(int type) {
+ public void setBlockScreenType(@BlockScreenType int type) {
// TODO: need to support the transition from NORMAL to SHRUNKEN and vice verse.
if (mBlockScreenType != type) {
mBlockScreenType = type;
@@ -826,12 +915,7 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
}
private void updateBlockScreenUI(boolean animation) {
- if (mBlockScreenDescriptionFadeIn.isRunning()) {
- mBlockScreenDescriptionFadeIn.end();
- }
- if (mBlockScreenDescriptionFadeOut.isRunning()) {
- mBlockScreenDescriptionFadeOut.end();
- }
+ mBlockScreenView.endAnimations();
if (!mScreenBlocked && mBlockedContentRating == null) {
mBlockScreenView.setVisibility(GONE);
@@ -839,101 +923,72 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
}
mBlockScreenView.setVisibility(VISIBLE);
- if (!animation) {
- updateBlockScreenTextView();
- switch (mBlockScreenType) {
- case BLOCK_SCREEN_TYPE_NO_UI:
- mBlockScreenIconView.setVisibility(GONE);
- mBlockScreenShrunkenIconView.setVisibility(GONE);
- mBlockScreenDescriptionView.setVisibility(GONE);
- break;
- case BLOCK_SCREEN_TYPE_SHRUNKEN_TV_VIEW:
- mBlockScreenIconView.setVisibility(GONE);
- mBlockScreenShrunkenIconView.setVisibility(VISIBLE);
- mBlockScreenDescriptionView.setVisibility(VISIBLE);
- break;
- case BLOCK_SCREEN_TYPE_NORMAL:
- mBlockScreenIconView.setVisibility(VISIBLE);
- mBlockScreenShrunkenIconView.setVisibility(GONE);
- mBlockScreenDescriptionView.setVisibility(VISIBLE);
- break;
- }
- } else {
- switch (mBlockScreenType) {
- case BLOCK_SCREEN_TYPE_NO_UI:
- if (mBlockScreenDescriptionView.getVisibility() == VISIBLE) {
- mBlockScreenDescriptionFadeOut.start();
- }
- break;
- case BLOCK_SCREEN_TYPE_SHRUNKEN_TV_VIEW:
- case BLOCK_SCREEN_TYPE_NORMAL:
- updateBlockScreenTextView();
- if (mBlockScreenDescriptionView.getVisibility() == GONE) {
- mBlockScreenDescriptionFadeIn.start();
- }
- break;
- }
+ if (!animation || mBlockScreenType != TunableTvView.BLOCK_SCREEN_TYPE_NO_UI) {
+ adjustBlockScreenSpacingAndText();
}
+ mBlockScreenView.onBlockStatusChanged(mBlockScreenType, animation);
}
- private void updateBlockScreenTextView() {
+ private void adjustBlockScreenSpacingAndText() {
// TODO: need to add animation for padding change when the block screen type is changed
// NORMAL to SHRUNKEN and vice verse.
- mBlockScreenTextView.setPadding(0,
- getResources().getDimensionPixelOffset(
- mBlockScreenType == BLOCK_SCREEN_TYPE_SHRUNKEN_TV_VIEW
- ? R.dimen.shrunken_tvview_block_text_padding_top
- : R.dimen.tvview_block_text_padding_top),
- 0, 0);
+ mBlockScreenView.setSpacing(mBlockScreenType);
+ String text = getBlockScreenText();
+ if (text != null) {
+ mBlockScreenView.setText(text);
+ }
+ }
+ /**
+ * Returns the block screen text corresponding to the current status.
+ * Note that returning {@code null} value means that the current text should not be changed.
+ */
+ private String getBlockScreenText() {
if (mScreenBlocked) {
switch (mBlockScreenType) {
case BLOCK_SCREEN_TYPE_NO_UI:
case BLOCK_SCREEN_TYPE_SHRUNKEN_TV_VIEW:
- mBlockScreenTextView.setText("");
- break;
+ return "";
case BLOCK_SCREEN_TYPE_NORMAL:
if (mCanModifyParentalControls) {
- mBlockScreenTextView.setText(R.string.tvview_channel_locked);
+ return getResources().getString(R.string.tvview_channel_locked);
} else {
- mBlockScreenTextView.setText(R.string.tvview_channel_locked_no_permission);
+ return getResources().getString(
+ R.string.tvview_channel_locked_no_permission);
}
- break;
}
} else if (mBlockedContentRating != null) {
String name = mContentRatingsManager.getDisplayNameForRating(mBlockedContentRating);
switch (mBlockScreenType) {
case BLOCK_SCREEN_TYPE_NO_UI:
- mBlockScreenTextView.setText("");
- break;
+ return "";
case BLOCK_SCREEN_TYPE_SHRUNKEN_TV_VIEW:
if (TextUtils.isEmpty(name)) {
- mBlockScreenTextView.setText(R.string.shrunken_tvview_content_locked);
+ return getResources().getString(R.string.shrunken_tvview_content_locked);
} else {
- mBlockScreenTextView.setText(getContext().getString(
- R.string.shrunken_tvview_content_locked_format, name));
+ return getContext().getString(
+ R.string.shrunken_tvview_content_locked_format, name);
}
- break;
case BLOCK_SCREEN_TYPE_NORMAL:
if (TextUtils.isEmpty(name)) {
if (mCanModifyParentalControls) {
- mBlockScreenTextView.setText(R.string.tvview_content_locked);
+ return getResources().getString(R.string.tvview_content_locked);
} else {
- mBlockScreenTextView.setText(
+ return getResources().getString(
R.string.tvview_content_locked_no_permission);
}
} else {
if (mCanModifyParentalControls) {
- mBlockScreenTextView.setText(getContext().getString(
- R.string.tvview_content_locked_format, name));
+ return getContext().getString(
+ R.string.tvview_content_locked_format, name);
} else {
- mBlockScreenTextView.setText(getContext().getString(
- R.string.tvview_content_locked_format_no_permission, name));
+ return getContext().getString(
+ R.string.tvview_content_locked_format_no_permission, name);
}
}
- break;
}
}
+ return null;
}
private void checkBlockScreenAndMuteNeeded() {
@@ -968,10 +1023,18 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
checkBlockScreenAndMuteNeeded();
}
+ @UiThread
private void hideScreenByVideoAvailability(int reason) {
+ mVideoAvailable = false;
+ mVideoUnavailableReason = reason;
+ if (mInternetCheckTask != null) {
+ mInternetCheckTask.cancel(true);
+ mInternetCheckTask = null;
+ }
switch (reason) {
case TvInputManager.VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY:
mHideScreenView.setVisibility(VISIBLE);
+ mHideScreenView.setImageVisibility(false);
mHideScreenView.setText(R.string.tvview_msg_audio_only);
mBufferingSpinnerView.setVisibility(GONE);
unmuteIfPossible();
@@ -980,19 +1043,33 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
mBufferingSpinnerView.setVisibility(VISIBLE);
mute();
break;
- case TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN:
- case TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING:
case TvInputManager.VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL:
+ mHideScreenView.setVisibility(VISIBLE);
+ mHideScreenView.setText(R.string.tvview_msg_weak_signal);
+ mBufferingSpinnerView.setVisibility(GONE);
+ mute();
+ break;
+ case TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING:
case VIDEO_UNAVAILABLE_REASON_NOT_TUNED:
+ mHideScreenView.setVisibility(VISIBLE);
+ mHideScreenView.setImageVisibility(false);
+ mHideScreenView.setText(null);
+ mBufferingSpinnerView.setVisibility(GONE);
+ mute();
+ break;
+ case TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN:
default:
mHideScreenView.setVisibility(VISIBLE);
+ mHideScreenView.setImageVisibility(false);
mHideScreenView.setText(null);
mBufferingSpinnerView.setVisibility(GONE);
mute();
+ if (mCurrentChannel != null && !mCurrentChannel.isPhysicalTunerChannel()) {
+ mInternetCheckTask = new InternetCheckTask();
+ mInternetCheckTask.execute();
+ }
break;
}
- mVideoAvailable = false;
- mVideoUnavailableReason = reason;
}
private void unhideScreenByVideoAvailability() {
@@ -1094,7 +1171,7 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
}
mTimeShiftAvailable = isTimeShiftAvailable;
if (isTimeShiftAvailable) {
- mTvView.setTimeShiftPositionCallback(new PlaybackTvView.TimeShiftPositionCallback2() {
+ mTvView.setTimeShiftPositionCallback(new TvView.TimeShiftPositionCallback() {
@Override
public void onTimeShiftStartPositionChanged(String inputId, long timeMs) {
if (mTimeShiftListener != null && mCurrentChannel != null
@@ -1107,14 +1184,6 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
public void onTimeShiftCurrentPositionChanged(String inputId, long timeMs) {
mTimeShiftCurrentPositionMs = timeMs;
}
-
- @Override
- public void onTimeShiftEndPositionChanged(String inputId, long timeMs) {
- if (mTimeShiftListener != null && mCurrentChannel != null
- && mCurrentChannel.getInputId().equals(inputId)) {
- mTimeShiftListener.onRecordEndTimeChanged(timeMs);
- }
- }
});
} else {
mTvView.setTimeShiftPositionCallback(null);
@@ -1265,11 +1334,6 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
* Called when the record start time has been changed.
*/
public abstract void onRecordStartTimeChanged(long recordStartTimeMs);
-
- /**
- * Called when the record end time has been changed.
- */
- public abstract void onRecordEndTimeChanged(long recordEndTimeMs);
}
/**
@@ -1281,4 +1345,22 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
*/
public abstract void onScreenBlockingChanged(boolean blocked);
}
+
+ public class InternetCheckTask extends AsyncTask<Void, Void, Boolean> {
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ return NetworkUtils.isNetworkAvailable(mConnectivityManager);
+ }
+
+ @Override
+ protected void onPostExecute(Boolean networkAvailable) {
+ mInternetCheckTask = null;
+ if (!mVideoAvailable && !networkAvailable && isAttachedToWindow()
+ && mVideoUnavailableReason == TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN) {
+ mHideScreenView.setImageVisibility(true);
+ mHideScreenView.setImage(R.drawable.ic_sad_cloud);
+ mHideScreenView.setText(R.string.tvview_msg_no_internet_connection);
+ }
+ }
+ }
}
diff --git a/src/com/android/tv/ui/TvOverlayManager.java b/src/com/android/tv/ui/TvOverlayManager.java
index 124f3393..94f9b0f9 100644
--- a/src/com/android/tv/ui/TvOverlayManager.java
+++ b/src/com/android/tv/ui/TvOverlayManager.java
@@ -29,6 +29,7 @@ import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
+import android.support.v4.os.BuildCompat;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
@@ -46,6 +47,7 @@ import com.android.tv.TimeShiftManager;
import com.android.tv.TvApplication;
import com.android.tv.analytics.Tracker;
import com.android.tv.common.WeakHandler;
+import com.android.tv.common.feature.CommonFeatures;
import com.android.tv.common.ui.setup.OnActionClickListener;
import com.android.tv.common.ui.setup.SetupFragment;
import com.android.tv.common.ui.setup.SetupMultiPaneFragment;
@@ -54,7 +56,9 @@ import com.android.tv.dialog.FullscreenDialogFragment;
import com.android.tv.dialog.PinDialogFragment;
import com.android.tv.dialog.RecentlyWatchedDialogFragment;
import com.android.tv.dialog.SafeDismissDialogFragment;
+import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.ui.DvrActivity;
+import com.android.tv.dvr.ui.HalfSizedDialogFragment;
import com.android.tv.guide.ProgramGuide;
import com.android.tv.menu.Menu;
import com.android.tv.menu.Menu.MenuShowReason;
@@ -133,6 +137,7 @@ public class TvOverlayManager {
AVAILABLE_DIALOG_TAGS.add(FullscreenDialogFragment.DIALOG_TAG);
AVAILABLE_DIALOG_TAGS.add(SettingsFragment.LicenseActionItem.DIALOG_TAG);
AVAILABLE_DIALOG_TAGS.add(RatingsFragment.AttributionItem.DIALOG_TAG);
+ AVAILABLE_DIALOG_TAGS.add(HalfSizedDialogFragment.DIALOG_TAG);
}
private final MainActivity mMainActivity;
@@ -155,7 +160,7 @@ public class TvOverlayManager {
private @TvOverlayType int mOpenedOverlays;
- private List<Runnable> mPendingActions = new ArrayList<>();
+ private final List<Runnable> mPendingActions = new ArrayList<>();
public TvOverlayManager(MainActivity mainActivity, ChannelTuner channelTuner,
KeypadChannelSwitchView keypadChannelSwitchView,
@@ -227,9 +232,13 @@ public class TvOverlayManager {
onOverlayClosed(OVERLAY_TYPE_GUIDE);
}
};
+ DvrDataManager dvrDataManager =
+ CommonFeatures.DVR.isEnabled(mainActivity) && BuildCompat.isAtLeastN() ? singletons
+ .getDvrDataManager() : null;
mProgramGuide = new ProgramGuide(mainActivity, channelTuner,
singletons.getTvInputManagerHelper(), mChannelDataManager,
- singletons.getProgramDataManager(), singletons.getTracker(), preShowRunnable,
+ singletons.getProgramDataManager(), dvrDataManager, singletons.getTracker(),
+ preShowRunnable,
postHideRunnable);
mSetupFragment = new SetupSourcesFragment();
mSetupFragment.setOnActionClickListener(new OnActionClickListener() {
@@ -346,10 +355,18 @@ public class TvOverlayManager {
*/
public void showDialogFragment(String tag, SafeDismissDialogFragment dialog,
boolean keepSidePanelHistory) {
+ showDialogFragment(tag, dialog, keepSidePanelHistory, false);
+ }
+
+ public void showDialogFragment(String tag, SafeDismissDialogFragment dialog,
+ boolean keepSidePanelHistory, boolean keepProgramGuide) {
int flags = FLAG_HIDE_OVERLAYS_KEEP_DIALOG;
if (keepSidePanelHistory) {
flags |= FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANEL_HISTORY;
}
+ if (keepProgramGuide) {
+ flags |= FLAG_HIDE_OVERLAYS_KEEP_PROGRAM_GUIDE;
+ }
hideOverlays(flags);
// A tag for dialog must be added to AVAILABLE_DIALOG_TAGS to make it launchable from TV.
if (!AVAILABLE_DIALOG_TAGS.contains(tag)) {
@@ -422,7 +439,6 @@ public class TvOverlayManager {
public void showSetupFragment() {
if (DEBUG) Log.d(TAG, "showSetupFragment");
mSetupFragmentActive = true;
- SetupSourcesFragment.setTheme(R.style.Theme_TV_GuidedStep);
mSetupFragment.enableFragmentTransition(SetupFragment.FRAGMENT_ENTER_TRANSITION
| SetupFragment.FRAGMENT_EXIT_TRANSITION | SetupFragment.FRAGMENT_RETURN_TRANSITION
| SetupFragment.FRAGMENT_REENTER_TRANSITION);
@@ -437,7 +453,6 @@ public class TvOverlayManager {
return;
}
mSetupFragmentActive = false;
- SetupSourcesFragment.setTheme(SetupSourcesFragment.DEFAULT_THEME);
closeFragment(removeFragment ? mSetupFragment : null);
if (mChannelDataManager.getChannelCount() == 0) {
mMainActivity.finish();
diff --git a/src/com/android/tv/ui/TvTransitionManager.java b/src/com/android/tv/ui/TvTransitionManager.java
index 444b5c0c..52e96cc0 100644
--- a/src/com/android/tv/ui/TvTransitionManager.java
+++ b/src/com/android/tv/ui/TvTransitionManager.java
@@ -33,6 +33,7 @@ import android.widget.FrameLayout.LayoutParams;
import com.android.tv.MainActivity;
import com.android.tv.R;
+import com.android.tv.data.Channel;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -89,15 +90,21 @@ public class TvTransitionManager extends TransitionManager {
}
initIfNeeded();
if (withAnimation) {
+ mEmptyView.setAlpha(1.0f);
transitionTo(mEmptyScene);
} else {
TransitionManager.go(mEmptyScene, null);
+ // When transition is null, transition got stuck without calling endTransitions.
+ TransitionManager.endTransitions(mEmptyScene.getSceneRoot());
+ // Since Fade.OUT transition doesn't run, we need to set alpha manually.
+ mEmptyView.setAlpha(0);
}
}
public void goToChannelBannerScene() {
initIfNeeded();
- if (mMainActivity.getCurrentChannel().isPassthrough()) {
+ Channel channel = mMainActivity.getCurrentChannel();
+ if (channel != null && channel.isPassthrough()) {
if (mCurrentScene != mInputBannerScene) {
// Show the input banner instead.
LayoutParams lp = (LayoutParams) mInputBannerView.getLayoutParams();
@@ -152,7 +159,7 @@ public class TvTransitionManager extends TransitionManager {
mExitAnimator = AnimatorInflater.loadAnimator(mMainActivity,
R.animator.channel_banner_exit);
- mEmptyScene = new Scene(mSceneContainer, mEmptyView);
+ mEmptyScene = new Scene(mSceneContainer, (View) mEmptyView);
mEmptyScene.setEnterAction(new Runnable() {
@Override
public void run() {
diff --git a/src/com/android/tv/ui/TvViewUiManager.java b/src/com/android/tv/ui/TvViewUiManager.java
index d767906b..5ad89bfa 100644
--- a/src/com/android/tv/ui/TvViewUiManager.java
+++ b/src/com/android/tv/ui/TvViewUiManager.java
@@ -59,6 +59,7 @@ public class TvViewUiManager {
private static final boolean DEBUG = false;
private static final float DISPLAY_MODE_EPSILON = 0.001f;
+ private static final float DISPLAY_ASPECT_RATIO_EPSILON = 0.01f;
private final Context mContext;
private final Resources mResources;
@@ -71,8 +72,8 @@ public class TvViewUiManager {
private final int mTvViewShrunkenEndMargin;
private final int mTvViewPapStartMargin;
private final int mTvViewPapEndMargin;
- private final int mScreenWidth;
- private final int mScreenHeight;
+ private int mWindowWidth;
+ private int mWindowHeight;
private final int mPipViewHorizontalMargin;
private final int mPipViewTopMargin;
private final int mPipViewBottomMargin;
@@ -101,28 +102,28 @@ public class TvViewUiManager {
private ObjectAnimator mBackgroundAnimator;
private int mBackgroundColor;
private int mAppliedDisplayedMode = DisplayMode.MODE_NOT_DEFINED;
- private int mAppliedVideoWidth;
- private int mAppliedVideoHeight;
private int mAppliedTvViewStartMargin;
private int mAppliedTvViewEndMargin;
+ private float mAppliedVideoDisplayAspectRatio;
public TvViewUiManager(Context context, TunableTvView tvView, TunableTvView pipView,
FrameLayout contentView, TvOptionsManager tvOptionManager) {
mContext = context;
- mResources = context.getResources();
+ mResources = mContext.getResources();
mTvView = tvView;
mPipView = pipView;
mContentView = contentView;
mTvOptionsManager = tvOptionManager;
- DisplayManager displayManager = (DisplayManager) context
+ DisplayManager displayManager = (DisplayManager) mContext
.getSystemService(Context.DISPLAY_SERVICE);
Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
Point size = new Point();
display.getSize(size);
- mScreenWidth = size.x;
- mScreenHeight = size.y;
+ mWindowWidth = size.x;
+ mWindowHeight = size.y;
+ // Have an assumption that PIP and TvView Shrinking happens only in full screen.
mTvViewShrunkenStartMargin = mResources
.getDimensionPixelOffset(R.dimen.shrunken_tvview_margin_start);
mTvViewShrunkenEndMargin =
@@ -131,7 +132,7 @@ public class TvViewUiManager {
int papMarginHorizontal = mResources
.getDimensionPixelOffset(R.dimen.papview_margin_horizontal);
int papSpacing = mResources.getDimensionPixelOffset(R.dimen.papview_spacing);
- mTvViewPapWidth = (mScreenWidth - papSpacing) / 2 - papMarginHorizontal;
+ mTvViewPapWidth = (mWindowWidth - papSpacing) / 2 - papMarginHorizontal;
mTvViewPapStartMargin = papMarginHorizontal + mTvViewPapWidth + papSpacing;
mTvViewPapEndMargin = papMarginHorizontal;
mTvViewFrame = createMarginLayoutParams(0, 0, 0, 0);
@@ -147,6 +148,21 @@ public class TvViewUiManager {
.getDimensionPixelOffset(R.dimen.pipview_margin_horizontal);
mPipViewTopMargin = mResources.getDimensionPixelOffset(R.dimen.pipview_margin_top);
mPipViewBottomMargin = mResources.getDimensionPixelOffset(R.dimen.pipview_margin_bottom);
+ mContentView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ int windowWidth = right - left;
+ int windowHeight = bottom - top;
+ if (windowWidth > 0 && windowHeight > 0) {
+ if (mWindowWidth != windowWidth || mWindowHeight != windowHeight) {
+ mWindowWidth = windowWidth;
+ mWindowHeight = windowHeight;
+ applyDisplayMode(mTvView.getVideoDisplayAspectRatio(), false, true);
+ }
+ }
+ }
+ });
}
/**
@@ -170,7 +186,7 @@ public class TvViewUiManager {
mTvViewEndMarginBeforeShrunken = mTvViewEndMargin;
if (mPipStarted && getPipLayout() == TvSettings.PIP_LAYOUT_SIDE_BY_SIDE) {
float sidePanelWidth = mResources.getDimensionPixelOffset(R.dimen.side_panel_width);
- float factor = 1.0f - sidePanelWidth / mScreenWidth;
+ float factor = 1.0f - sidePanelWidth / mWindowWidth;
int startMargin = (int) (mTvViewPapStartMargin * factor);
int endMargin = (int) (mTvViewPapEndMargin * factor + sidePanelWidth);
setTvViewMargin(startMargin, endMargin);
@@ -209,25 +225,21 @@ public class TvViewUiManager {
int viewWidth = mContentView.getWidth();
int viewHeight = mContentView.getHeight();
- int videoWidth = mTvView.getVideoWidth();
- int videoHeight = mTvView.getVideoHeight();
-
- if (viewWidth <= 0 || viewHeight <= 0 || videoWidth <= 0 || videoHeight <= 0) {
+ float videoDisplayAspectRatio = mTvView.getVideoDisplayAspectRatio();
+ if (viewWidth <= 0 || viewHeight <= 0 || videoDisplayAspectRatio <= 0f) {
Log.w(TAG, "Video size is currently unavailable");
if (DEBUG) {
Log.d(TAG, "isDisplayModeAvailable: "
+ "viewWidth=" + viewWidth
+ ", viewHeight=" + viewHeight
- + ", videoWidth=" + videoWidth
- + ", videoHeight="+ videoHeight
+ + ", videoDisplayAspectRatio=" + videoDisplayAspectRatio
);
}
return false;
}
float viewRatio = viewWidth / (float) viewHeight;
- float videoRatio = videoWidth / (float) videoHeight;
- return Math.abs(viewRatio - videoRatio) >= DISPLAY_MODE_EPSILON;
+ return Math.abs(viewRatio - videoDisplayAspectRatio) >= DISPLAY_MODE_EPSILON;
}
/**
@@ -251,7 +263,7 @@ public class TvViewUiManager {
if (storeInPreference) {
mSharedPreferences.edit().putInt(TvSettings.PREF_DISPLAY_MODE, displayMode).apply();
}
- applyDisplayMode(mTvView.getVideoWidth(), mTvView.getVideoHeight(), animate);
+ applyDisplayMode(mTvView.getVideoDisplayAspectRatio(), animate, false);
return prev;
}
@@ -268,7 +280,7 @@ public class TvViewUiManager {
* Updates TvView. It is called when video resolution is updated.
*/
public void updateTvView() {
- applyDisplayMode(mTvView.getVideoWidth(), mTvView.getVideoHeight(), false);
+ applyDisplayMode(mTvView.getVideoDisplayAspectRatio(), false, false);
if (mTvView.isVideoAvailable() && mTvView.isFadedOut()) {
mTvView.fadeIn(mResources.getInteger(R.integer.tvview_fade_in_duration),
mFastOutLinearIn, null);
@@ -507,11 +519,11 @@ public class TvViewUiManager {
@Override
public void run() {
if (DEBUG) {
- Log.d(TAG, "setFixedSize: w=" + mTvView.getWidth() + " h=" + mTvView
- .getHeight());
+ Log.d(TAG, "setFixedSize: w=" + layoutParams.width + " h="
+ + layoutParams.height);
}
mTvView.setLayoutParams(layoutParams);
- mTvView.setFixedSurfaceSize(mTvView.getWidth(), mTvView.getHeight());
+ mTvView.setFixedSurfaceSize(layoutParams.width, layoutParams.height);
}
});
} else {
@@ -541,15 +553,14 @@ public class TvViewUiManager {
if (mPipLayout == TvSettings.PIP_LAYOUT_SIDE_BY_SIDE) {
gravity = Gravity.CENTER_VERTICAL | Gravity.START;
height = tvViewFrame.height;
- int pipVideoWidth = mPipView.getVideoWidth();
- int pipVideoHeight = mPipView.getVideoHeight();
- if (pipVideoWidth > 0 && pipVideoHeight > 0) {
- width = height * pipVideoWidth / pipVideoHeight;
+ float videoDisplayAspectRatio = mPipView.getVideoDisplayAspectRatio();
+ if (videoDisplayAspectRatio <= 0f) {
+ width = tvViewFrame.width;
+ } else {
+ width = (int) (height * videoDisplayAspectRatio);
if (width > tvViewFrame.width) {
width = tvViewFrame.width;
}
- } else {
- width = tvViewFrame.width;
}
startMargin = mResources.getDimensionPixelOffset(R.dimen.papview_margin_horizontal)
* tvViewFrame.width / mTvViewPapWidth + (tvViewFrame.width - width) / 2;
@@ -563,8 +574,8 @@ public class TvViewUiManager {
int tvEndMargin = tvViewFrame.getMarginEnd();
int tvTopMargin = tvViewFrame.topMargin;
int tvBottomMargin = tvViewFrame.bottomMargin;
- float horizontalScaleFactor = (float) tvViewWidth / mScreenWidth;
- float verticalScaleFactor = (float) tvViewHeight / mScreenHeight;
+ float horizontalScaleFactor = (float) tvViewWidth / mWindowWidth;
+ float verticalScaleFactor = (float) tvViewHeight / mWindowHeight;
int maxWidth;
if (mPipSize == TvSettings.PIP_SIZE_SMALL) {
@@ -580,15 +591,14 @@ public class TvViewUiManager {
} else {
throw new IllegalArgumentException("Invalid PIP size: " + mPipSize);
}
- int pipVideoWidth = mPipView.getVideoWidth();
- int pipVideoHeight = mPipView.getVideoHeight();
- if (pipVideoWidth > 0 && pipVideoHeight > 0) {
- width = height * pipVideoWidth / pipVideoHeight;
+ float videoDisplayAspectRatio = mPipView.getVideoDisplayAspectRatio();
+ if (videoDisplayAspectRatio <= 0f) {
+ width = maxWidth;
+ } else {
+ width = (int) (height * videoDisplayAspectRatio);
if (width > maxWidth) {
width = maxWidth;
}
- } else {
- width = maxWidth;
}
startMargin = tvStartMargin + (int) (mPipViewHorizontalMargin * horizontalScaleFactor);
@@ -703,31 +713,29 @@ public class TvViewUiManager {
});
}
- private void applyDisplayMode(int videoWidth, int videoHeight, boolean animate) {
+ private void applyDisplayMode(float videoDisplayAspectRatio, boolean animate,
+ boolean forceUpdate) {
if (mAppliedDisplayedMode == mDisplayMode
- && mAppliedVideoWidth == videoWidth
- && mAppliedVideoHeight == videoHeight
&& mAppliedTvViewStartMargin == mTvViewStartMargin
- && mAppliedTvViewEndMargin == mTvViewEndMargin) {
- return;
+ && mAppliedTvViewEndMargin == mTvViewEndMargin
+ && Math.abs(mAppliedVideoDisplayAspectRatio - videoDisplayAspectRatio) <
+ DISPLAY_ASPECT_RATIO_EPSILON) {
+ if (!forceUpdate) {
+ return;
+ }
} else {
mAppliedDisplayedMode = mDisplayMode;
- mAppliedVideoHeight = videoHeight;
- mAppliedVideoWidth = videoWidth;
mAppliedTvViewStartMargin = mTvViewStartMargin;
mAppliedTvViewEndMargin = mTvViewEndMargin;
+ mAppliedVideoDisplayAspectRatio = videoDisplayAspectRatio;
}
- int availableAreaWidth = mScreenWidth - mTvViewStartMargin - mTvViewEndMargin;
- int availableAreaHeight = availableAreaWidth * mScreenHeight / mScreenWidth;
+ int availableAreaWidth = mWindowWidth - mTvViewStartMargin - mTvViewEndMargin;
+ int availableAreaHeight = availableAreaWidth * mWindowHeight / mWindowWidth;
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(0, 0,
((FrameLayout.LayoutParams) mTvView.getLayoutParams()).gravity);
int displayMode = mDisplayMode;
double availableAreaRatio = 0;
double videoRatio = 0;
- if (videoWidth <= 0 || videoHeight <= 0) {
- videoWidth = mScreenWidth;
- videoHeight = mScreenHeight;
- }
if (availableAreaWidth <= 0 || availableAreaHeight <= 0) {
displayMode = DisplayMode.MODE_FULL;
Log.w(TAG, "Some resolution info is missing during applyDisplayMode. ("
@@ -735,10 +743,14 @@ public class TvViewUiManager {
+ availableAreaHeight + ")");
} else {
availableAreaRatio = (double) availableAreaWidth / availableAreaHeight;
- videoRatio = (double) videoWidth / videoHeight;
+ if (videoDisplayAspectRatio <= 0f) {
+ videoRatio = (double) mWindowWidth / mWindowHeight;
+ } else {
+ videoRatio = videoDisplayAspectRatio;
+ }
}
- int tvViewFrameTop = (mScreenHeight - availableAreaHeight) / 2;
+ int tvViewFrameTop = (mWindowHeight - availableAreaHeight) / 2;
MarginLayoutParams tvViewFrame = createMarginLayoutParams(
mTvViewStartMargin, mTvViewEndMargin, tvViewFrameTop, tvViewFrameTop);
layoutParams.width = availableAreaWidth;
@@ -777,7 +789,7 @@ public class TvViewUiManager {
int marginStart = mTvViewStartMargin + (availableAreaWidth - layoutParams.width) / 2;
layoutParams.setMarginStart(marginStart);
// Set marginEnd as well because setTvViewPosition uses both start/end margin.
- layoutParams.setMarginEnd(mScreenWidth - layoutParams.width - marginStart);
+ layoutParams.setMarginEnd(mWindowWidth - layoutParams.width - marginStart);
setBackgroundColor(Utils.getColor(mResources, isTvViewFullScreen()
? R.color.tvactivity_background : R.color.tvactivity_background_on_shrunken_tvview),
@@ -810,8 +822,8 @@ public class TvViewUiManager {
lp.setMarginEnd(endMargin);
lp.topMargin = topMargin;
lp.bottomMargin = bottomMargin;
- lp.width = mScreenWidth - startMargin - endMargin;
- lp.height = mScreenHeight - topMargin - bottomMargin;
+ lp.width = mWindowWidth - startMargin - endMargin;
+ lp.height = mWindowHeight - topMargin - bottomMargin;
return lp;
}
}
diff --git a/src/com/android/tv/ui/sidepanel/CustomizeChannelListFragment.java b/src/com/android/tv/ui/sidepanel/CustomizeChannelListFragment.java
index 85050dc4..b52302b6 100644
--- a/src/com/android/tv/ui/sidepanel/CustomizeChannelListFragment.java
+++ b/src/com/android/tv/ui/sidepanel/CustomizeChannelListFragment.java
@@ -37,6 +37,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.Iterator;
public class CustomizeChannelListFragment extends SideFragment {
private static final int GROUP_BY_SOURCE = 0;
@@ -157,6 +158,21 @@ public class CustomizeChannelListFragment extends SideFragment {
return mItems;
}
+ private void cleanUpOneChannelGroupItem(List<Item> items) {
+ Iterator<Item> iter = items.iterator();
+ while (iter.hasNext()) {
+ Item item = iter.next();
+ if (item instanceof SelectGroupItem) {
+ SelectGroupItem selectGroupItem = (SelectGroupItem) item;
+ if (selectGroupItem.mChannelItemsInGroup.size() == 1) {
+ ((ChannelItem) selectGroupItem.mChannelItemsInGroup.get(0))
+ .mSelectGroupItem = null;
+ iter.remove();
+ }
+ }
+ }
+ }
+
private void addItemForGroupBySource(List<Item> items) {
items.add(new GroupBySubMenu(getString(R.string.edit_channels_group_by_sources)));
SelectGroupItem selectGroupItem = null;
@@ -177,6 +193,7 @@ public class CustomizeChannelListFragment extends SideFragment {
items.add(channelItem);
selectGroupItem.addChannelItem(channelItem);
}
+ cleanUpOneChannelGroupItem(items);
}
private void addItemForGroupByHdSd(List<Item> items) {
@@ -211,6 +228,7 @@ public class CustomizeChannelListFragment extends SideFragment {
items.add(channelItem);
selectGroupItem.addChannelItem(channelItem);
}
+ cleanUpOneChannelGroupItem(items);
}
private static boolean isHdChannel(Channel channel) {
@@ -275,7 +293,7 @@ public class CustomizeChannelListFragment extends SideFragment {
}
private class ChannelItem extends ChannelCheckItem {
- private final SelectGroupItem mSelectGroupItem;
+ private SelectGroupItem mSelectGroupItem;
public ChannelItem(Channel channel, SelectGroupItem selectGroupItem) {
super(channel, getChannelDataManager(), getProgramDataManager());
@@ -292,7 +310,9 @@ public class CustomizeChannelListFragment extends SideFragment {
protected void onSelected() {
super.onSelected();
getChannelDataManager().updateBrowsable(getChannel().getId(), isChecked());
- mSelectGroupItem.notifyUpdated();
+ if (mSelectGroupItem != null) {
+ mSelectGroupItem.notifyUpdated();
+ }
}
@Override
diff --git a/src/com/android/tv/ui/sidepanel/DeveloperFragment.java b/src/com/android/tv/ui/sidepanel/DeveloperFragment.java
deleted file mode 100644
index 44b4d452..00000000
--- a/src/com/android/tv/ui/sidepanel/DeveloperFragment.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tv.ui.sidepanel;
-
-import android.app.Activity;
-import android.content.Context;
-import android.view.View;
-
-import com.android.tv.Features;
-import com.android.tv.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Shows developer options like enabling USB TV tuner.
- */
-public class DeveloperFragment extends SideFragment {
- private static final String TRACKER_LABEL = "developer options";
-
- /**
- * Sets USB TV tuner enabled.
- */
- private static final class UsbTvTunerItem extends SwitchItem {
- Context mContext;
-
- public UsbTvTunerItem(Context context) {
- super(context.getResources().getString(R.string.developer_menu_enable_usb_tv_tuner),
- context.getResources().getString(R.string.developer_menu_enable_usb_tv_tuner),
- context.getResources().getString(
- R.string.developer_menu_enable_usb_tv_tuner_description));
- mContext = context;
- }
-
- @Override
- protected void onBind(View view) {
- super.onBind(view);
- setChecked(Features.USB_TUNER.isEnabled(view.getContext()));
- }
-
- @Override
- public void setChecked(boolean checked) {
- super.setChecked(checked);
- Features.USB_TUNER.setEnabled(mContext, checked);
- }
- }
-
- @Override
- protected String getTitle() {
- return getResources().getString(R.string.side_panel_title_developer);
- }
-
- @Override
- public String getTrackerLabel() {
- return TRACKER_LABEL;
- }
-
- @Override
- protected List<Item> getItemList() {
- List<Item> items = new ArrayList<>();
- Activity activity = getActivity();
- items.add(new UsbTvTunerItem(activity));
- boolean ac3Support = getMainActivity().isAc3PassthroughSupported();
- // Show AC3 passthrough availability.
- items.add(new SimpleItem(getString(R.string.developer_menu_ac3_support),
- getString(ac3Support ? R.string.developer_menu_ac3_support_yes
- : R.string.developer_menu_ac3_support_no)));
- return items;
- }
-}
diff --git a/src/com/android/tv/ui/sidepanel/PipInputSelectorFragment.java b/src/com/android/tv/ui/sidepanel/PipInputSelectorFragment.java
index 06415c21..dec017a8 100644
--- a/src/com/android/tv/ui/sidepanel/PipInputSelectorFragment.java
+++ b/src/com/android/tv/ui/sidepanel/PipInputSelectorFragment.java
@@ -156,8 +156,11 @@ public class PipInputSelectorFragment extends SideFragment {
// If this input shares the same parent with the current main input, you cannot select
// it. (E.g. two HDMI CEC devices that are connected to HDMI port 1 through an A/V
// receiver.)
- TvInputInfo mainInputInfo = mPipInputManager.getPipInput(
- getMainActivity().getCurrentChannel()).getInputInfo();
+ PipInput pipInput = mPipInputManager.getPipInput(getMainActivity().getCurrentChannel());
+ if (pipInput == null) {
+ return false;
+ }
+ TvInputInfo mainInputInfo = pipInput.getInputInfo();
TvInputInfo pipInputInfo = mPipInput.getInputInfo();
return mainInputInfo == null || pipInputInfo == null
|| !TextUtils.equals(mainInputInfo.getId(), pipInputInfo.getId())
diff --git a/src/com/android/tv/ui/sidepanel/SettingsFragment.java b/src/com/android/tv/ui/sidepanel/SettingsFragment.java
index 6b5b2584..6d606014 100644
--- a/src/com/android/tv/ui/sidepanel/SettingsFragment.java
+++ b/src/com/android/tv/ui/sidepanel/SettingsFragment.java
@@ -16,13 +16,9 @@
package com.android.tv.ui.sidepanel;
-import android.content.res.Resources;
-import android.os.Build;
-import android.provider.Settings;
import android.view.View;
import android.widget.Toast;
-import com.android.tv.Features;
import com.android.tv.MainActivity;
import com.android.tv.R;
import com.android.tv.TvApplication;
@@ -155,19 +151,6 @@ public class SettingsFragment extends SideFragment {
if (LicenseUtils.hasLicenses(activity.getAssets())) {
items.add(new LicenseActionItem(activity));
}
- boolean developerOptionEnabled = Settings.Secure.getInt(getActivity().getContentResolver(),
- Settings.Global.DEVELOPMENT_SETTINGS_ENABLED , 0) != 0;
- if (Features.DEVELOPER_OPTION.isEnabled(getActivity()) && developerOptionEnabled
- && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- Resources res = getActivity().getResources();
- items.add(new ActionItem(res.getString(R.string.side_panel_title_developer)) {
- @Override
- protected void onSelected() {
- getMainActivity().getOverlayManager().getSideFragmentManager().show(
- new DeveloperFragment());
- }
- });
- }
// Show version.
items.add(new SimpleItem(getString(R.string.settings_menu_version),
((TvApplication) activity.getApplicationContext()).getVersionName()));
diff --git a/src/com/android/tv/ui/sidepanel/parentalcontrols/RatingsFragment.java b/src/com/android/tv/ui/sidepanel/parentalcontrols/RatingsFragment.java
index 7ec28bb8..6bc47939 100644
--- a/src/com/android/tv/ui/sidepanel/parentalcontrols/RatingsFragment.java
+++ b/src/com/android/tv/ui/sidepanel/parentalcontrols/RatingsFragment.java
@@ -18,6 +18,7 @@ package com.android.tv.ui.sidepanel.parentalcontrols;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
+import android.util.ArrayMap;
import android.util.SparseIntArray;
import android.view.View;
import android.widget.CompoundButton;
@@ -41,6 +42,7 @@ import com.android.tv.util.TvSettings.ContentRatingLevel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
public class RatingsFragment extends SideFragment {
private static final SparseIntArray sLevelResourceIdMap;
@@ -73,6 +75,9 @@ public class RatingsFragment extends SideFragment {
}
private final List<RatingLevelItem> mRatingLevelItems = new ArrayList<>();
+ // A map from the rating system ID string to RatingItem objects.
+ private final Map<String, List<RatingItem>> mContentRatingSystemItemMap = new ArrayMap<>();
+ private ParentalControlSettings mParentalControlSettings;
public static String getDescription(MainActivity tvActivity) {
@ContentRatingLevel int currentLevel =
@@ -104,18 +109,32 @@ public class RatingsFragment extends SideFragment {
updateRatingLevels();
items.addAll(mRatingLevelItems);
+ mContentRatingSystemItemMap.clear();
+
List<ContentRatingSystem> contentRatingSystems =
getMainActivity().getContentRatingsManager().getContentRatingSystems();
Collections.sort(contentRatingSystems, ContentRatingSystem.DISPLAY_NAME_COMPARATOR);
for (ContentRatingSystem s : contentRatingSystems) {
- if (getMainActivity().getParentalControlSettings().isContentRatingSystemEnabled(s)) {
+ if (mParentalControlSettings.isContentRatingSystemEnabled(s)) {
+ List<RatingItem> ratingItems = new ArrayList<>();
+ boolean hasSubRating = false;
items.add(new DividerItem(s.getDisplayName()));
for (Rating rating : s.getRatings()) {
- RatingItem item = rating.getSubRatings().size() == 0 ?
+ RatingItem item = rating.getSubRatings().isEmpty() ?
new RatingItem(s, rating) :
new RatingWithSubItem(s, rating);
items.add(item);
+ if (rating.getSubRatings().isEmpty()) {
+ ratingItems.add(item);
+ } else {
+ hasSubRating = true;
+ }
+ }
+ // Only include rating systems that don't contain any sub ratings in the map for
+ // simplicity.
+ if (!hasSubRating) {
+ mContentRatingSystemItemMap.put(s.getId(), ratingItems);
}
}
}
@@ -131,7 +150,8 @@ public class RatingsFragment extends SideFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- getMainActivity().getParentalControlSettings().loadRatings();
+ mParentalControlSettings = getMainActivity().getParentalControlSettings();
+ mParentalControlSettings.loadRatings();
}
@Override
@@ -146,13 +166,27 @@ public class RatingsFragment extends SideFragment {
}
private void updateRatingLevels() {
- @ContentRatingLevel int ratingLevel =
- getMainActivity().getParentalControlSettings().getContentRatingLevel();
+ @ContentRatingLevel int ratingLevel = mParentalControlSettings.getContentRatingLevel();
for (RatingLevelItem ratingLevelItem : mRatingLevelItems) {
ratingLevelItem.setChecked(ratingLevel == ratingLevelItem.mRatingLevel);
}
}
+ private void updateDependentRatingItems(ContentRatingSystem.Order order,
+ int selectedRatingOrderIndex, String contentRatingSystemId, boolean isChecked) {
+ List<RatingItem> ratingItems = mContentRatingSystemItemMap.get(contentRatingSystemId);
+ if (ratingItems != null) {
+ for (RatingItem item : ratingItems) {
+ int ratingOrderIndex = item.getRatingOrderIndex(order);
+ if (ratingOrderIndex != -1
+ && ((ratingOrderIndex > selectedRatingOrderIndex && isChecked)
+ || (ratingOrderIndex < selectedRatingOrderIndex && !isChecked))) {
+ item.setRatingBlocked(isChecked);
+ }
+ }
+ }
+ }
+
private class RatingLevelItem extends RadioButtonItem {
private final int mRatingLevel;
@@ -166,7 +200,7 @@ public class RatingsFragment extends SideFragment {
@Override
protected void onSelected() {
super.onSelected();
- getMainActivity().getParentalControlSettings().setContentRatingLevel(
+ mParentalControlSettings.setContentRatingLevel(
getMainActivity().getContentRatingsManager(), mRatingLevel);
notifyItemsChanged(mRatingLevelItems.size());
}
@@ -177,12 +211,21 @@ public class RatingsFragment extends SideFragment {
protected final Rating mRating;
private final Drawable mIcon;
private CompoundButton mCompoundButton;
+ private final List<ContentRatingSystem.Order> mOrders = new ArrayList<>();
+ private final List<Integer> mOrderIndexes = new ArrayList<>();
private RatingItem(ContentRatingSystem contentRatingSystem, Rating rating) {
super(rating.getTitle(), rating.getDescription());
mContentRatingSystem = contentRatingSystem;
mRating = rating;
mIcon = rating.getIcon();
+ for (ContentRatingSystem.Order order : mContentRatingSystem.getOrders()) {
+ int orderIndex = order.getRatingIndex(mRating);
+ if (orderIndex != -1) {
+ mOrders.add(order);
+ mOrderIndexes.add(orderIndex);
+ }
+ }
}
@Override
@@ -211,17 +254,21 @@ public class RatingsFragment extends SideFragment {
protected void onUpdate() {
super.onUpdate();
mCompoundButton.setButtonDrawable(getButtonDrawable());
- setChecked(getMainActivity().getParentalControlSettings().isRatingBlocked(
- mContentRatingSystem, mRating));
+ setChecked(mParentalControlSettings.isRatingBlocked(mContentRatingSystem, mRating));
}
@Override
protected void onSelected() {
super.onSelected();
- if (getMainActivity().getParentalControlSettings()
- .setRatingBlocked(mContentRatingSystem, mRating, isChecked())) {
+ if (mParentalControlSettings.setRatingBlocked(
+ mContentRatingSystem, mRating, isChecked())) {
updateRatingLevels();
}
+ // Automatically check/uncheck dependent ratings.
+ for (int i = 0; i < mOrders.size(); i++) {
+ updateDependentRatingItems(mOrders.get(i), mOrderIndexes.get(i),
+ mContentRatingSystem.getId(), isChecked());
+ }
}
@Override
@@ -232,6 +279,19 @@ public class RatingsFragment extends SideFragment {
protected int getButtonDrawable() {
return R.drawable.btn_lock_material_anim;
}
+
+ private int getRatingOrderIndex(ContentRatingSystem.Order order) {
+ int orderIndex = mOrders.indexOf(order);
+ return orderIndex == -1 ? -1 : mOrderIndexes.get(orderIndex);
+ }
+
+ private void setRatingBlocked(boolean isChecked) {
+ if (isChecked() == isChecked) {
+ return;
+ }
+ mParentalControlSettings.setRatingBlocked(mContentRatingSystem, mRating, isChecked);
+ notifyUpdated();
+ }
}
private class RatingWithSubItem extends RatingItem {
@@ -247,7 +307,7 @@ public class RatingsFragment extends SideFragment {
@Override
protected int getButtonDrawable() {
- int blockedStatus = getMainActivity().getParentalControlSettings().getBlockedStatus(
+ int blockedStatus = mParentalControlSettings.getBlockedStatus(
mContentRatingSystem, mRating);
if (blockedStatus == ParentalControlSettings.RATING_BLOCKED) {
return R.drawable.btn_lock_material;
diff --git a/src/com/android/tv/util/AsyncDbTask.java b/src/com/android/tv/util/AsyncDbTask.java
index 9f440533..7ac293fc 100644
--- a/src/com/android/tv/util/AsyncDbTask.java
+++ b/src/com/android/tv/util/AsyncDbTask.java
@@ -22,10 +22,12 @@ import android.media.tv.TvContract;
import android.net.Uri;
import android.os.AsyncTask;
import android.support.annotation.MainThread;
+import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import android.util.Log;
import android.util.Range;
+import com.android.tv.common.SoftPreconditions;
import com.android.tv.data.Channel;
import com.android.tv.data.Program;
@@ -40,6 +42,10 @@ import java.util.concurrent.RejectedExecutionException;
*
* <p>Instances of this class should only be executed this using {@link
* #executeOnDbThread(Object[])}.
+ *
+ * @param <Params> the type of the parameters sent to the task upon execution.
+ * @param <Progress> the type of the progress units published during the background computation.
+ * @param <Result> the type of the result of the background computation.
*/
public abstract class AsyncDbTask<Params, Progress, Result>
extends AsyncTask<Params, Progress, Result> {
@@ -79,7 +85,7 @@ public abstract class AsyncDbTask<Params, Progress, Result>
* <p> {@link #doInBackground(Void...)} executes the query on call {@link #onQuery(Cursor)}
* which is implemented by subclasses.
*
- * @param <Result> The type of result returned by {@link #onQuery(Cursor)}
+ * @param <Result> the type of result returned by {@link #onQuery(Cursor)}
*/
public abstract static class AsyncQueryTask<Result> extends AsyncDbTask<Void, Void, Result> {
private final ContentResolver mContentResolver;
@@ -103,9 +109,10 @@ public abstract class AsyncDbTask<Params, Progress, Result>
@Override
protected final Result doInBackground(Void... params) {
if (!THREAD_FACTORY.namedWithPrefix(Thread.currentThread())) {
- IllegalStateException e = new IllegalStateException(
- this + " should only be executed using executeOnDbThread, " +
- "but it was called on thread " + Thread.currentThread());
+ IllegalStateException e = new IllegalStateException(this
+ + " should only be executed using executeOnDbThread, "
+ + "but it was called on thread "
+ + Thread.currentThread());
Log.w(TAG, e);
if (DEBUG) {
throw e;
@@ -137,8 +144,8 @@ public abstract class AsyncDbTask<Params, Progress, Result>
}
return null;
}
- } catch (SecurityException e) {
- Log.d(TAG, "Security exception during query", e);
+ } catch (Exception e) {
+ SoftPreconditions.warn(TAG, null, "Error querying " + this, e);
return null;
}
}
@@ -161,8 +168,10 @@ public abstract class AsyncDbTask<Params, Progress, Result>
* Returns the result of a query as an {@link List} of {@code T}.
*
* <p>Subclasses must implement {@link #fromCursor(Cursor)}.
+ *
+ * @param <T> the type of result returned in a list by {@link #onQuery(Cursor)}
*/
- public static abstract class AsyncQueryListTask<T> extends AsyncQueryTask<List<T>> {
+ public abstract static class AsyncQueryListTask<T> extends AsyncQueryTask<List<T>> {
public AsyncQueryListTask(ContentResolver contentResolver, Uri uri, String[] projection,
String selection, String[] selectionArgs, String orderBy) {
@@ -201,9 +210,56 @@ public abstract class AsyncDbTask<Params, Progress, Result>
}
/**
+ * Returns the result of a query as a single instance of {@code T}.
+ *
+ * <p>Subclasses must implement {@link #fromCursor(Cursor)}.
+ */
+ public abstract static class AsyncQueryItemTask<T> extends AsyncQueryTask<T> {
+
+ public AsyncQueryItemTask(ContentResolver contentResolver, Uri uri, String[] projection,
+ String selection, String[] selectionArgs, String orderBy) {
+ super(contentResolver, uri, projection, selection, selectionArgs, orderBy);
+ }
+
+ @Override
+ protected final T onQuery(Cursor c) {
+ if (c.moveToNext()) {
+ if (isCancelled()) {
+ // This is guaranteed to never call onPostExecute because the task is canceled.
+ return null;
+ }
+ T result = fromCursor(c);
+ if (c.moveToNext()) {
+ Log.w(TAG, "More than one result for found for " + this);
+ }
+ return result;
+ } else {
+ if (DEBUG) {
+ Log.v(TAG, "No result for found for " + this);
+ }
+ return null;
+ }
+
+ }
+
+ /**
+ * Return a single instance of {@code T} from the cursor.
+ *
+ * <p><b>NOTE</b> Do not move the cursor or close it, that is handled by {@link
+ * #onQuery(Cursor)}.
+ *
+ * <p><b>Note</b> This is executed on the DB thread by {@link #onQuery(Cursor)}
+ *
+ * @param c The cursor with the values to create T from.
+ */
+ @WorkerThread
+ protected abstract T fromCursor(Cursor c);
+ }
+
+ /**
* Gets an {@link List} of {@link Channel}s from {@link TvContract.Channels#CONTENT_URI}.
*/
- public static abstract class AsyncChannelQueryTask extends AsyncQueryListTask<Channel> {
+ public abstract static class AsyncChannelQueryTask extends AsyncQueryListTask<Channel> {
public AsyncChannelQueryTask(ContentResolver contentResolver) {
super(contentResolver, TvContract.Channels.CONTENT_URI, Channel.PROJECTION,
@@ -227,16 +283,19 @@ public abstract class AsyncDbTask<Params, Progress, Result>
/**
* Gets an {@link List} of {@link Program}s for a given channel and period {@link
- * TvContract#buildProgramsUriForChannel(long, long, long)}.
+ * TvContract#buildProgramsUriForChannel(long, long, long)}. If the {@code period} is
+ * {@code null}, then all the programs is queried.
*/
public static class LoadProgramsForChannelTask extends AsyncQueryListTask<Program> {
protected final Range<Long> mPeriod;
protected final long mChannelId;
public LoadProgramsForChannelTask(ContentResolver contentResolver, long channelId,
- Range<Long> period) {
- super(contentResolver, TvContract
- .buildProgramsUriForChannel(channelId, period.getLower(), period.getUpper()),
+ @Nullable Range<Long> period) {
+ super(contentResolver, period == null
+ ? TvContract.buildProgramsUriForChannel(channelId)
+ : TvContract.buildProgramsUriForChannel(channelId, period.getLower(),
+ period.getUpper()),
Program.PROJECTION, null, null, null);
mPeriod = period;
mChannelId = channelId;
diff --git a/src/com/android/tv/util/ImageLoader.java b/src/com/android/tv/util/ImageLoader.java
index 59c4983b..ed0fd54d 100644
--- a/src/com/android/tv/util/ImageLoader.java
+++ b/src/com/android/tv/util/ImageLoader.java
@@ -28,18 +28,23 @@ import android.support.annotation.MainThread;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.annotation.WorkerThread;
+import android.util.ArraySet;
import android.util.Log;
import com.android.tv.R;
-import com.android.tv.common.CollectionUtils;
import com.android.tv.util.BitmapUtils.ScaledBitmapInfo;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
/**
* This class wraps up completing some arbitrary long running work when loading a bitmap. It
@@ -49,6 +54,39 @@ public final class ImageLoader {
private static final String TAG = "ImageLoader";
private static final boolean DEBUG = false;
+ private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
+ // We want at least 2 threads and at most 4 threads in the core pool,
+ // preferring to have 1 less than the CPU count to avoid saturating
+ // the CPU with background work
+ private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
+ private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
+ private static final int KEEP_ALIVE_SECONDS = 30;
+
+ private static final ThreadFactory sThreadFactory = new NamedThreadFactory("ImageLoader");
+
+ private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(
+ 128);
+
+ /**
+ * An private {@link Executor} that can be used to execute tasks in parallel.
+ *
+ * <p>{@code IMAGE_THREAD_POOL_EXECUTOR} setting are copied from {@link AsyncTask}
+ * Since we do a lot of concurrent image loading we can exhaust a thread pool.
+ * ImageLoader catches the error, and just leaves the image blank.
+ * However other tasks will fail and crash the application.
+ *
+ * <p>Using a separate thread pool prevents image loading from causing other tasks to fail.
+ */
+ private static final Executor IMAGE_THREAD_POOL_EXECUTOR;
+
+ static {
+ ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
+ MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, sPoolWorkQueue,
+ sThreadFactory);
+ threadPoolExecutor.allowCoreThreadTimeOut(true);
+ IMAGE_THREAD_POOL_EXECUTOR = threadPoolExecutor;
+ }
+
private static Handler sMainHandler;
/**
@@ -148,7 +186,7 @@ public final class ImageLoader {
Log.d(TAG, "loadBitmap() " + uriString);
}
return doLoadBitmap(context, uriString, maxWidth, maxHeight, callback,
- AsyncTask.THREAD_POOL_EXECUTOR);
+ IMAGE_THREAD_POOL_EXECUTOR);
}
private static boolean doLoadBitmap(Context context, String uriString,
@@ -179,7 +217,7 @@ public final class ImageLoader {
if (DEBUG) {
Log.d(TAG, "loadBitmap() " + loadBitmapTask);
}
- return doLoadBitmap(callback, AsyncTask.THREAD_POOL_EXECUTOR, loadBitmapTask);
+ return doLoadBitmap(callback, IMAGE_THREAD_POOL_EXECUTOR, loadBitmapTask);
}
/**
@@ -226,7 +264,7 @@ public final class ImageLoader {
protected final Context mAppContext;
protected final int mMaxWidth;
protected final int mMaxHeight;
- private final Set<ImageLoaderCallback> mCallbacks = CollectionUtils.createSmallSet();
+ private final Set<ImageLoaderCallback> mCallbacks = new ArraySet<>();
private final ImageCache mImageCache;
private final String mKey;
diff --git a/src/com/android/tv/util/MultiLongSparseArray.java b/src/com/android/tv/util/MultiLongSparseArray.java
index 7ed72d61..1d5fa80b 100644
--- a/src/com/android/tv/util/MultiLongSparseArray.java
+++ b/src/com/android/tv/util/MultiLongSparseArray.java
@@ -17,10 +17,9 @@
package com.android.tv.util;
import android.support.annotation.VisibleForTesting;
+import android.util.ArraySet;
import android.util.LongSparseArray;
-import com.android.tv.common.CollectionUtils;
-
import java.util.Collections;
import java.util.Set;
@@ -105,7 +104,7 @@ public class MultiLongSparseArray<T> {
private Set<T> getEmptySet() {
if (mEmptyIndex < 0) {
- return CollectionUtils.createSmallSet();
+ return new ArraySet<>();
}
Set<T> emptySet = mEmptySets[mEmptyIndex];
mEmptySets[mEmptyIndex--] = null;
diff --git a/src/com/android/tv/util/NetworkUtils.java b/src/com/android/tv/util/NetworkUtils.java
new file mode 100644
index 00000000..ed3ce383
--- /dev/null
+++ b/src/com/android/tv/util/NetworkUtils.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.util;
+
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.support.annotation.Nullable;
+import android.support.annotation.WorkerThread;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+/**
+ * A utility class to check the connectivity.
+ */
+@WorkerThread
+public class NetworkUtils {
+ private static final String GENERATE_204 = "http://clients3.google.com/generate_204";
+
+ /**
+ * Checks if the internet connection is available.
+ */
+ public static boolean isNetworkAvailable(@Nullable ConnectivityManager connectivityManager) {
+ if (connectivityManager == null) {
+ return false;
+ }
+ NetworkInfo info = connectivityManager.getActiveNetworkInfo();
+ if (info == null || !info.isConnected()) {
+ return false;
+ }
+ HttpURLConnection connection = null;
+ try {
+ connection = (HttpURLConnection) new URL(GENERATE_204).openConnection();
+ connection.setInstanceFollowRedirects(false);
+ connection.setDefaultUseCaches(false);
+ connection.setUseCaches(false);
+ if (connection.getResponseCode() == HttpURLConnection.HTTP_NO_CONTENT) {
+ return true;
+ }
+ } catch (IOException e) {
+ // Does nothing.
+ } finally {
+ if (connection != null) {
+ connection.disconnect();
+ }
+ }
+ return false;
+ }
+
+ private NetworkUtils() { }
+}
diff --git a/src/com/android/tv/util/OnboardingUtils.java b/src/com/android/tv/util/OnboardingUtils.java
index 582a0c9f..3dcc324d 100644
--- a/src/com/android/tv/util/OnboardingUtils.java
+++ b/src/com/android/tv/util/OnboardingUtils.java
@@ -37,7 +37,7 @@ public final class OnboardingUtils {
private static final int ONBOARDING_VERSION = 1;
private static final String MERCHANT_COLLECTION_URL_STRING =
- "https://play.google.com/store/apps/collection/promotion_3001bf9_ATV_livechannels";
+ "TODO: put a market link to show TV input apps";
/**
* Intent to show merchant collection in play store.
*/
@@ -101,7 +101,7 @@ public final class OnboardingUtils {
ContentResolver resolver = context.getContentResolver();
try (Cursor c = resolver.query(Channels.CONTENT_URI, new String[] {Channels._ID}, null,
null, null)) {
- return c.getCount() != 0;
+ return c != null && c.getCount() != 0;
}
}
diff --git a/src/com/android/tv/util/PipInputManager.java b/src/com/android/tv/util/PipInputManager.java
index dddc82b6..03bdc681 100644
--- a/src/com/android/tv/util/PipInputManager.java
+++ b/src/com/android/tv/util/PipInputManager.java
@@ -20,11 +20,11 @@ import android.content.Context;
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager;
import android.media.tv.TvInputManager.TvInputCallback;
+import android.util.ArraySet;
import android.util.Log;
import com.android.tv.ChannelTuner;
import com.android.tv.R;
-import com.android.tv.common.CollectionUtils;
import com.android.tv.data.Channel;
import java.util.ArrayList;
@@ -37,6 +37,7 @@ import java.util.Set;
/**
* A class that manages inputs for PIP. All tuner inputs are represented to one tuner input for PIP.
+ * Hidden inputs should not be visible to the users.
*/
public class PipInputManager {
private static final String TAG = "PipInputManager";
@@ -50,7 +51,7 @@ public class PipInputManager {
private final ChannelTuner mChannelTuner;
private boolean mStarted;
private final Map<String, PipInput> mPipInputMap = new HashMap<>(); // inputId -> PipInput
- private final Set<Listener> mListeners = CollectionUtils.createSmallSet();
+ private final Set<Listener> mListeners = new ArraySet<>();
private final TvInputCallback mTvInputCallback = new TvInputCallback() {
@Override
@@ -182,40 +183,53 @@ public class PipInputManager {
/**
* Gets the size of inputs for PIP.
*
- * @param availableOnly If true, it counts only available PIP inputs. Please see {@link
+ * <p>The hidden inputs are not counted.
+ *
+ * @param availableOnly If {@code true}, it counts only available PIP inputs. Please see {@link
* PipInput#isAvailable()} for the details of availability.
*/
public int getPipInputSize(boolean availableOnly) {
- if (availableOnly) {
- int count = 0;
- for (PipInput pipInput : mPipInputMap.values()) {
- if (pipInput.isAvailable()) {
- ++count;
+ int count = 0;
+ for (PipInput pipInput : mPipInputMap.values()) {
+ if (!pipInput.isHidden() && (!availableOnly || pipInput.mAvailable)) {
+ ++count;
+ }
+ if (pipInput.isPassthrough()) {
+ TvInputInfo info = pipInput.getInputInfo();
+ // Do not count HDMI ports if a CEC device is directly connected to the port.
+ if (info.getParentId() != null && !info.isConnectedToHdmiSwitch()) {
+ --count;
}
}
- return count;
- } else {
- return mPipInputMap.size();
}
+ return count;
}
/**
- * Gets the list of inputs for PIP.
+ * Gets the list of inputs for PIP..
+ *
+ * <p>The hidden inputs are excluded.
*
* @param availableOnly If true, it returns only available PIP inputs. Please see {@link
* PipInput#isAvailable()} for the details of availability.
*/
public List<PipInput> getPipInputList(boolean availableOnly) {
- List<PipInput> pipInputs;
- if (availableOnly) {
- pipInputs = new ArrayList<>();
- for (PipInput pipInput : mPipInputMap.values()) {
- if (pipInput.mAvailable) {
- pipInputs.add(pipInput);
+ List<PipInput> pipInputs = new ArrayList<>();
+ List<PipInput> removeInputs = new ArrayList<>();
+ for (PipInput pipInput : mPipInputMap.values()) {
+ if (!pipInput.isHidden() && (!availableOnly || pipInput.mAvailable)) {
+ pipInputs.add(pipInput);
+ }
+ if (pipInput.isPassthrough()) {
+ TvInputInfo info = pipInput.getInputInfo();
+ // Do not show HDMI ports if a CEC device is directly connected to the port.
+ if (info.getParentId() != null && !info.isConnectedToHdmiSwitch()) {
+ removeInputs.add(mPipInputMap.get(info.getParentId()));
}
}
- } else {
- pipInputs = new ArrayList<>(mPipInputMap.values());
+ }
+ if (!removeInputs.isEmpty()) {
+ pipInputs.removeAll(removeInputs);
}
Collections.sort(pipInputs, new Comparator<PipInput>() {
@Override
@@ -325,9 +339,9 @@ public class PipInputManager {
}
/**
- * Returns true, if the input is available for PIP. If a channel of an input is already
- * played or an input is not connected state or there is no browsable channel, the input
- * is unavailable.
+ * Returns {@code true}, if the input is available for PIP. If a channel of an input is
+ * already played or an input is not connected state or there is no browsable channel, the
+ * input is unavailable.
*/
public boolean isAvailable() {
return mAvailable;
@@ -407,5 +421,10 @@ public class PipInputManager {
}
}
}
+
+ private boolean isHidden() {
+ // mInputInfo is null for the tuner input and it's always visible.
+ return mInputInfo != null && mInputInfo.isHidden(mContext);
+ }
}
}
diff --git a/src/com/android/tv/util/RecurringRunner.java b/src/com/android/tv/util/RecurringRunner.java
index 2a006f9e..5e65715e 100644
--- a/src/com/android/tv/util/RecurringRunner.java
+++ b/src/com/android/tv/util/RecurringRunner.java
@@ -24,6 +24,7 @@ import android.support.annotation.WorkerThread;
import android.util.Log;
import com.android.tv.common.SharedPreferencesUtils;
+import com.android.tv.common.SoftPreconditions;
import java.util.Date;
diff --git a/src/com/android/tv/util/SetupUtils.java b/src/com/android/tv/util/SetupUtils.java
index d337139b..6d24d5bd 100644
--- a/src/com/android/tv/util/SetupUtils.java
+++ b/src/com/android/tv/util/SetupUtils.java
@@ -27,11 +27,13 @@ import android.os.Build;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
+import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Log;
import com.android.tv.ApplicationSingletons;
import com.android.tv.TvApplication;
-import com.android.tv.common.CollectionUtils;
+import com.android.tv.common.SoftPreconditions;
import com.android.tv.data.Channel;
import com.android.tv.data.ChannelDataManager;
@@ -67,13 +69,13 @@ public class SetupUtils {
private SetupUtils(TvApplication tvApplication) {
mTvApplication = tvApplication;
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(tvApplication);
- mSetUpInputs = CollectionUtils.createSmallSet();
+ mSetUpInputs = new ArraySet<>();
mSetUpInputs.addAll(mSharedPreferences.getStringSet(PREF_KEY_SET_UP_INPUTS,
Collections.<String>emptySet()));
- mKnownInputs = CollectionUtils.createSmallSet();
+ mKnownInputs = new ArraySet<>();
mKnownInputs.addAll(mSharedPreferences.getStringSet(PREF_KEY_KNOWN_INPUTS,
Collections.<String>emptySet()));
- mRecognizedInputs = CollectionUtils.createSmallSet();
+ mRecognizedInputs = new ArraySet<>();
mRecognizedInputs.addAll(mSharedPreferences.getStringSet(PREF_KEY_RECOGNIZED_INPUTS,
mKnownInputs));
mIsFirstTune = mSharedPreferences.getBoolean(PREF_KEY_IS_FIRST_TUNE, true);
@@ -262,29 +264,26 @@ public class SetupUtils {
* @param context The Context used for granting permission.
*/
public static void grantEpgPermissionToSetUpPackages(Context context) {
- // TvProvider allows granting of Uri permissions starting from MNC.
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- SharedPreferences sharedPreferences =
- PreferenceManager.getDefaultSharedPreferences(context);
- Set<String> setUpInputs = new HashSet<>(sharedPreferences.getStringSet(
- PREF_KEY_SET_UP_INPUTS, new HashSet<String>()));
- Set<String> setUpPackages = new HashSet<>();
- for (String input : setUpInputs) {
- ComponentName componentName = null;
- try {
- componentName = ComponentName.unflattenFromString(input);
- } catch (Exception e) {
- Log.w(TAG, "Failed to unflatten string to component name (" + input + ")", e);
- }
- if (componentName == null) {
- continue;
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+ // Can't grant permission.
+ return;
+ }
+
+ // Find all already-verified packages.
+ Set<String> setUpPackages = new HashSet<>();
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+ for (String input : sp.getStringSet(PREF_KEY_SET_UP_INPUTS, Collections.EMPTY_SET)) {
+ if (!TextUtils.isEmpty(input)) {
+ ComponentName componentName = ComponentName.unflattenFromString(input);
+ if (componentName != null) {
+ setUpPackages.add(componentName.getPackageName());
}
- setUpPackages.add(componentName.getPackageName());
- }
- for (String packageName : setUpPackages) {
- grantEpgPermission(context, packageName);
}
}
+
+ for (String packageName : setUpPackages) {
+ grantEpgPermission(context, packageName);
+ }
}
/**
@@ -346,7 +345,7 @@ public class SetupUtils {
}
mSharedPreferences.edit().putStringSet(PREF_KEY_SET_UP_INPUTS, mSetUpInputs)
.putStringSet(PREF_KEY_KNOWN_INPUTS, mKnownInputs)
- .putStringSet(PREF_KEY_RECOGNIZED_INPUTS, mKnownInputs).apply();
+ .putStringSet(PREF_KEY_RECOGNIZED_INPUTS, mRecognizedInputs).apply();
}
}
@@ -360,7 +359,7 @@ public class SetupUtils {
if (!mRecognizedInputs.contains(inputId)) {
Log.i(TAG, "An unrecognized input's setup has been done. inputId=" + inputId);
mRecognizedInputs.add(inputId);
- mSharedPreferences.edit().putStringSet(PREF_KEY_RECOGNIZED_INPUTS, mKnownInputs)
+ mSharedPreferences.edit().putStringSet(PREF_KEY_RECOGNIZED_INPUTS, mRecognizedInputs)
.apply();
}
if (!mKnownInputs.contains(inputId)) {
diff --git a/src/com/android/tv/util/SystemProperties.java b/src/com/android/tv/util/SystemProperties.java
index 1dc70fd5..235161b6 100644
--- a/src/com/android/tv/util/SystemProperties.java
+++ b/src/com/android/tv/util/SystemProperties.java
@@ -58,13 +58,6 @@ public final class SystemProperties {
public static final BooleanSystemProperty USE_TRACKER = new BooleanSystemProperty(
"tv_use_tracker", true);
- /**
- * Selects using {@link com.android.tv.dvr.DvrDataManagerInMemoryImpl}
- * instead of {@link com.android.tv.dvr.DvrDataManagerImpl}
- */
- public static final BooleanSystemProperty USE_IN_MEMORY_DVR_DB = new BooleanSystemProperty(
- "tv_use_in_memory_dvr_db", false); // STOPSHIP(DVR)
-
static {
updateSystemProperties();
}
diff --git a/src/com/android/tv/util/TvInputManagerHelper.java b/src/com/android/tv/util/TvInputManagerHelper.java
index 250ca430..b4149637 100644
--- a/src/com/android/tv/util/TvInputManagerHelper.java
+++ b/src/com/android/tv/util/TvInputManagerHelper.java
@@ -26,6 +26,7 @@ import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import android.util.Log;
+import com.android.tv.common.SoftPreconditions;
import com.android.tv.parental.ContentRatingsManager;
import com.android.tv.parental.ParentalControlSettings;
diff --git a/src/com/android/tv/util/Utils.java b/src/com/android/tv/util/Utils.java
index 44d601c5..a763fe58 100644
--- a/src/com/android/tv/util/Utils.java
+++ b/src/com/android/tv/util/Utils.java
@@ -24,10 +24,10 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.database.Cursor;
-import android.media.tv.TvContentRating;
import android.media.tv.TvContract;
import android.media.tv.TvContract.Channels;
import android.media.tv.TvInputInfo;
@@ -42,18 +42,16 @@ import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.View;
+import android.widget.Toast;
-import com.android.tv.Features;
import com.android.tv.R;
import com.android.tv.TvApplication;
import com.android.tv.data.Channel;
import com.android.tv.data.Program;
import com.android.tv.data.StreamInfo;
-import com.android.usbtuner.tvinput.UsbTunerTvInputService;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
@@ -336,6 +334,19 @@ public class Utils {
return "";
}
+ public static String getAspectRatioString(float videoDisplayAspectRatio) {
+ if (videoDisplayAspectRatio <= 0) {
+ return "";
+ }
+
+ for (AspectRatio ratio : AspectRatio.values()) {
+ if (Math.abs((float) ratio.width / ratio.height - videoDisplayAspectRatio) < 0.05f) {
+ return ratio.toString();
+ }
+ }
+ return "";
+ }
+
public static int getVideoDefinitionLevelFromSize(int width, int height) {
if (width >= VIDEO_ULTRA_HD_WIDTH && height >= VIDEO_ULTRA_HD_HEIGHT) {
return StreamInfo.VIDEO_DEFINITION_LEVEL_ULTRA_HD;
@@ -609,67 +620,46 @@ public class Utils {
}
/**
- * Returns input ID of {@link UsbTunerTvInputService}.
+ * Returns a localized version of the text resource specified by resourceId.
*/
- @Nullable
- public static String getUsbTunerInputId(Context context) {
- if (!Features.USB_TUNER.isEnabled(context)) {
- return null;
+ public static CharSequence getTextForLocale(Context context, Locale locale, int resourceId) {
+ if (locale.equals(context.getResources().getConfiguration().locale)) {
+ return context.getText(resourceId);
}
- return TvContract.buildInputId(new ComponentName(context.getPackageName(),
- UsbTunerTvInputService.class.getName()));
+ Configuration config = new Configuration(context.getResources().getConfiguration());
+ config.setLocale(locale);
+ return context.createConfigurationContext(config).getText(resourceId);
}
/**
- * Returns {@link TvInputInfo} object of {@link UsbTunerTvInputService}.
+ * Returns the internal TV inputs.
*/
- @Nullable
- public static TvInputInfo getUsbTunerInputInfo(Context context) {
- if (!Features.USB_TUNER.isEnabled(context)) {
- return null;
+ public static List<TvInputInfo> getInternalTvInputs(Context context, boolean tunerInputOnly) {
+ List<TvInputInfo> inputs = new ArrayList<>();
+ String contextPackageName = context.getPackageName();
+ for (TvInputInfo input : TvApplication.getSingletons(context).getTvInputManagerHelper()
+ .getTvInputInfos(true, tunerInputOnly)) {
+ if (contextPackageName.equals(ComponentName.unflattenFromString(input.getId())
+ .getPackageName())) {
+ inputs.add(input);
+ }
}
- TvInputManagerHelper helper = TvApplication.getSingletons(context)
- .getTvInputManagerHelper();
- return helper.getTvInputInfo(getUsbTunerInputId(context));
+ return inputs;
}
- private static final class SyncRunnable implements Runnable {
- private final Runnable mTarget;
- private boolean mComplete;
-
- public SyncRunnable(Runnable target) {
- mTarget = target;
- }
-
- @Override
- public void run() {
- try {
- mTarget.run();
- } finally {
- synchronized (this) {
- mComplete = true;
- notifyAll();
- }
- }
- }
+ /**
+ * Checks whether the input is internal or not.
+ */
+ public static boolean isInternalTvInput(Context context, String inputId) {
+ return context.getPackageName().equals(ComponentName.unflattenFromString(inputId)
+ .getPackageName());
+ }
- public void waitForComplete() {
- boolean interrupted = false;
- synchronized (this) {
- try {
- while (!mComplete) {
- try {
- wait();
- } catch (InterruptedException e) {
- interrupted = true;
- }
- }
- } finally {
- if (interrupted) {
- Thread.currentThread().interrupt();
- }
- }
- }
- }
+ /**
+ * Shows a toast message to notice that the current feature is a developer feature.
+ */
+ public static void showToastMessageForDeveloperFeature(Context context) {
+ Toast.makeText(context, "This feature is for developer preview.", Toast.LENGTH_SHORT)
+ .show();
}
}
diff --git a/src/com/android/usbtuner/UsbInputController.java b/src/com/android/usbtuner/UsbInputController.java
index 6d6fccc2..f0982eb5 100644
--- a/src/com/android/usbtuner/UsbInputController.java
+++ b/src/com/android/usbtuner/UsbInputController.java
@@ -23,10 +23,14 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
+import android.media.tv.TvInputInfo;
+import android.media.tv.TvInputManager;
+import android.media.tv.TvInputService;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.support.v4.os.BuildCompat;
import android.util.Log;
import com.android.tv.Features;
@@ -44,7 +48,7 @@ import java.util.Map;
* to update the connection status of the supported USB TV tuners.
*/
public class UsbInputController extends BroadcastReceiver {
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
private static final String TAG = "UsbInputController";
private static final TunerDevice[] TUNER_DEVICES = {
@@ -58,7 +62,7 @@ public class UsbInputController extends BroadcastReceiver {
private static final long DVB_DRIVER_CHECK_DELAY_MS = 300;
private DvbDeviceAccessor mDvbDeviceAccessor;
- private Handler mHandler = new Handler(Looper.getMainLooper()) {
+ private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -155,6 +159,10 @@ public class UsbInputController extends BroadcastReceiver {
PackageManager pm = context.getPackageManager();
ComponentName USBTUNER = new ComponentName(context, UsbTunerTvInputService.class);
+ // Don't kill app by enabling/disabling TvActivity. If LC is killed by enabling/disabling
+ // TvActivity, the following pm.setComponentEnabledSetting doesn't work.
+ ((TvApplication) context.getApplicationContext()).handleInputCountChanged(
+ true, enabled, true);
// Since PackageManager.DONT_KILL_APP delays the operation by 10 seconds
// (PackageManagerService.BROADCAST_DELAY), we'd better avoid using it. It is used only
// when the LiveChannels app is active since we don't want to kill the running app.
@@ -165,10 +173,17 @@ public class UsbInputController extends BroadcastReceiver {
if (newState != pm.getComponentEnabledSetting(USBTUNER)) {
// Send/cancel the USB tuner TV input setup recommendation card.
TunerSetupActivity.onTvInputEnabled(context, enabled);
-
// Enable/disable the USB tuner TV input.
pm.setComponentEnabledSetting(USBTUNER, newState, flags);
if (DEBUG) Log.d(TAG, "Status updated:" + enabled);
}
+ if (enabled && BuildCompat.isAtLeastN()) {
+ TvInputInfo info = mDvbDeviceAccessor.buildTvInputInfo(context);
+ if (info != null) {
+ Log.i(TAG, "TvInputInfo updated: " + info.toString());
+ ((TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE))
+ .updateTvInputInfo(info);
+ }
+ }
}
}
diff --git a/tests/common/src/com/android/tv/testing/ChannelInfo.java b/tests/common/src/com/android/tv/testing/ChannelInfo.java
index 28ba4a64..99430461 100644
--- a/tests/common/src/com/android/tv/testing/ChannelInfo.java
+++ b/tests/common/src/com/android/tv/testing/ChannelInfo.java
@@ -57,6 +57,7 @@ public final class ChannelInfo {
public final int originalNetworkId;
public final int videoWidth;
public final int videoHeight;
+ public final float videoPixelAspectRatio;
public final int audioChannel;
public final int audioLanguageCount;
public final boolean hasClosedCaption;
@@ -109,15 +110,17 @@ public final class ChannelInfo {
}
private ChannelInfo(String number, String name, String logoUrl, int originalNetworkId,
- int videoWidth, int videoHeight, int audioChannel, int audioLanguageCount,
- boolean hasClosedCaption, ProgramInfo program, String appLinkText, int appLinkColor,
- String appLinkIconUri, String appLinkPosterArtUri, String appLinkIntentUri) {
+ int videoWidth, int videoHeight, float videoPixelAspectRatio, int audioChannel,
+ int audioLanguageCount, boolean hasClosedCaption, ProgramInfo program,
+ String appLinkText, int appLinkColor, String appLinkIconUri, String appLinkPosterArtUri,
+ String appLinkIntentUri) {
this.number = number;
this.name = name;
this.logoUrl = logoUrl;
this.originalNetworkId = originalNetworkId;
this.videoWidth = videoWidth;
this.videoHeight = videoHeight;
+ this.videoPixelAspectRatio = videoPixelAspectRatio;
this.audioChannel = audioChannel;
this.audioLanguageCount = audioLanguageCount;
this.hasClosedCaption = hasClosedCaption;
@@ -193,6 +196,7 @@ public final class ChannelInfo {
private int mOriginalNetworkId;
private int mVideoWidth = 1920; // Width for HD video.
private int mVideoHeight = 1080; // Height for HD video.
+ private float mVideoPixelAspectRatio = 1.0f; //default value
private int mAudioChannel;
private int mAudioLanguageCount;
private boolean mHasClosedCaption;
@@ -213,6 +217,7 @@ public final class ChannelInfo {
mOriginalNetworkId = other.originalNetworkId;
mVideoWidth = other.videoWidth;
mVideoHeight = other.videoHeight;
+ mVideoPixelAspectRatio = other.videoPixelAspectRatio;
mAudioChannel = other.audioChannel;
mAudioLanguageCount = other.audioLanguageCount;
mHasClosedCaption = other.hasClosedCaption;
@@ -249,6 +254,11 @@ public final class ChannelInfo {
return this;
}
+ public Builder setVideoPixelAspectRatio(float videoPixelAspectRatio) {
+ mVideoPixelAspectRatio = videoPixelAspectRatio;
+ return this;
+ }
+
public Builder setAudioChannel(int audioChannel) {
mAudioChannel = audioChannel;
return this;
@@ -296,8 +306,8 @@ public final class ChannelInfo {
public ChannelInfo build() {
return new ChannelInfo(mNumber, mName, mLogoUrl, mOriginalNetworkId,
- mVideoWidth, mVideoHeight, mAudioChannel, mAudioLanguageCount,
- mHasClosedCaption, mProgram, mAppLinkText, mAppLinkColor,
+ mVideoWidth, mVideoHeight, mVideoPixelAspectRatio, mAudioChannel,
+ mAudioLanguageCount, mHasClosedCaption, mProgram, mAppLinkText, mAppLinkColor,
mAppLinkIconUri, mAppLinkPosterArtUri, mAppLinkIntentUri);
}
diff --git a/tests/common/src/com/android/tv/testing/FakeClock.java b/tests/common/src/com/android/tv/testing/FakeClock.java
index 372a7569..d88e53a8 100644
--- a/tests/common/src/com/android/tv/testing/FakeClock.java
+++ b/tests/common/src/com/android/tv/testing/FakeClock.java
@@ -81,7 +81,7 @@ public class FakeClock implements Clock {
* @param unit The time unit to increment by.
* @param amount The amount of time units to increment by.
*/
- private void increment(TimeUnit unit, long amount) {
+ public void increment(TimeUnit unit, long amount) {
mCurrentTimeMillis += unit.toMillis(amount);
}
diff --git a/tests/common/src/com/android/tv/testing/dvr/RecordingTestUtils.java b/tests/common/src/com/android/tv/testing/dvr/RecordingTestUtils.java
index 458c9f2c..ab3bb043 100644
--- a/tests/common/src/com/android/tv/testing/dvr/RecordingTestUtils.java
+++ b/tests/common/src/com/android/tv/testing/dvr/RecordingTestUtils.java
@@ -16,43 +16,48 @@
package com.android.tv.testing.dvr;
-import android.support.annotation.RequiresPermission;
-
-import com.android.tv.data.Channel;
-import com.android.tv.dvr.Recording;
+import com.android.tv.dvr.DvrDataManagerInMemoryImpl;
+import com.android.tv.dvr.ScheduledRecording;
import junit.framework.Assert;
-import java.util.Collections;
-
/**
- * Static utils for using {@link Recording} in tests.
+ * Static utils for using {@link ScheduledRecording} in tests.
*/
public final class RecordingTestUtils {
- public static Recording createTestRecordingWithIdAndPeriod(long id, long startTime,
- long endTime) {
- return Recording.builder(new Channel.Builder().build(), startTime, endTime)
+ public static ScheduledRecording createTestRecordingWithIdAndPeriod(long id, long channelId,
+ long startTime, long endTime) {
+ return ScheduledRecording.builder(startTime, endTime)
.setId(id)
- .setPrograms(Collections.EMPTY_LIST)
+ .setChannelId(channelId)
.build();
}
- public static Recording createTestRecordingWithPeriod(long startTime, long endTime) {
- return createTestRecordingWithIdAndPeriod(Recording.ID_NOT_SET, startTime, endTime);
+ public static ScheduledRecording createTestRecordingWithPeriod(long channelId, long startTime,
+ long endTime) {
+ return createTestRecordingWithIdAndPeriod(ScheduledRecording.ID_NOT_SET, channelId,
+ startTime, endTime);
+ }
+
+ public static ScheduledRecording addScheduledRecording(
+ DvrDataManagerInMemoryImpl dvrDataManager, long channelId, long startTime,
+ long endTime) {
+ ScheduledRecording recording = createTestRecordingWithPeriod(channelId, startTime, endTime);
+ recording = dvrDataManager.addScheduledRecordingInternal(recording);
+ return recording;
}
- public static Recording normalizePriority(Recording orig){
- return Recording.buildFrom(orig).setPriority(orig.getId()).build();
+ public static ScheduledRecording normalizePriority(ScheduledRecording orig){
+ return ScheduledRecording.buildFrom(orig).setPriority(orig.getId()).build();
}
- public static void assertRecordingEquals(Recording expected, Recording actual) {
+ public static void assertRecordingEquals(ScheduledRecording expected, ScheduledRecording actual) {
Assert.assertEquals("id", expected.getId(), actual.getId());
- Assert.assertEquals("uri", expected.getUri(), actual.getUri());
- Assert.assertEquals("channel", expected.getChannel(), actual.getChannel());
- Assert.assertEquals("programs", expected.getPrograms(), actual.getPrograms());
+ Assert.assertEquals("channel", expected.getChannelId(), actual.getChannelId());
+ Assert.assertEquals("programId", expected.getProgramId(), actual.getProgramId());
+ Assert.assertEquals("priority", expected.getPriority(), actual.getPriority());
Assert.assertEquals("start time", expected.getStartTimeMs(), actual.getStartTimeMs());
Assert.assertEquals("end time", expected.getEndTimeMs(), actual.getEndTimeMs());
- Assert.assertEquals("media size", expected.getSize(), actual.getSize());
Assert.assertEquals("state", expected.getState(), actual.getState());
Assert.assertEquals("parent season recording", expected.getParentSeasonRecording(),
actual.getParentSeasonRecording());
diff --git a/tests/common/src/com/android/tv/testing/uihelper/SidePanelHelper.java b/tests/common/src/com/android/tv/testing/uihelper/SidePanelHelper.java
index 66edd4bb..2d4f9b2f 100644
--- a/tests/common/src/com/android/tv/testing/uihelper/SidePanelHelper.java
+++ b/tests/common/src/com/android/tv/testing/uihelper/SidePanelHelper.java
@@ -42,6 +42,10 @@ public class SidePanelHelper extends BaseUiDeviceHelper {
.hasDescendant(ByResource.text(mTargetResources, titleResId));
}
+ public BySelector byViewText(int textResId) {
+ return By.hasDescendant(ByResource.text(mTargetResources, textResId));
+ }
+
public UiObject2 assertNavigateToItem(int resId) {
String title = mTargetResources.getString(resId);
return assertNavigateToItem(title);
diff --git a/tests/func/Android.mk b/tests/func/Android.mk
index 2fd68b08..e89ba25b 100644
--- a/tests/func/Android.mk
+++ b/tests/func/Android.mk
@@ -17,5 +17,6 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
LOCAL_INSTRUMENTATION_FOR := LiveTv
LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 23 # M
include $(BUILD_PACKAGE)
diff --git a/tests/func/src/com/android/tv/tests/ui/ParentalControlsTest.java b/tests/func/src/com/android/tv/tests/ui/ParentalControlsTest.java
new file mode 100644
index 00000000..19861c1f
--- /dev/null
+++ b/tests/func/src/com/android/tv/tests/ui/ParentalControlsTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.tests.ui;
+
+import static com.android.tv.testing.uihelper.UiDeviceAsserts.assertWaitForCondition;
+
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.tv.R;
+import com.android.tv.testing.uihelper.ByResource;
+import com.android.tv.testing.uihelper.DialogHelper;
+
+@SmallTest
+public class ParentalControlsTest extends LiveChannelsTestCase {
+
+ private BySelector mBySettingsSidePanel;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mLiveChannelsHelper.assertAppStarted();
+ mBySettingsSidePanel = mSidePanelHelper.bySidePanelTitled(
+ R.string.side_panel_title_settings);
+ prepareParentalControl();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ switchParentalControl(R.string.option_toggle_parental_controls_on);
+ super.tearDown();
+ }
+
+ public void testRatingDependentSelect() {
+ // Show ratings fragment.
+ BySelector bySidePanel = mSidePanelHelper.bySidePanelTitled(
+ R.string.option_program_restrictions);
+ assertWaitForCondition(mDevice, Until.hasObject(bySidePanel));
+ mSidePanelHelper.assertNavigateToItem(R.string.option_ratings);
+ mDevice.pressDPadCenter();
+ // Block rating 6 and rating 12. Check if dependent select works well.
+ bySidePanel = mSidePanelHelper.bySidePanelTitled(R.string.option_ratings);
+ assertWaitForCondition(mDevice, Until.hasObject(bySidePanel));
+ // Test on blocking and unblocking Japanese rating.
+ int blockAge = 6;
+ int unBlockAge = 12;
+ int maxAge = 20;
+ int minAge = 4;
+ for (int age = minAge; age <= maxAge; age++) {
+ UiObject2 ratingCheckBox = mSidePanelHelper.assertNavigateToItem(String.valueOf(age))
+ .findObject(ByResource.id(mTargetResources, R.id.check_box));
+ if (ratingCheckBox.isChecked()) {
+ mDevice.pressDPadCenter();
+ }
+ }
+ mSidePanelHelper.assertNavigateToItem(String.valueOf(blockAge));
+ mDevice.pressDPadCenter();
+ assertRatingViewIsChecked(minAge, maxAge, blockAge, true);
+ mSidePanelHelper.assertNavigateToItem(String.valueOf(unBlockAge));
+ mDevice.pressDPadCenter();
+ assertRatingViewIsChecked(minAge, maxAge, unBlockAge, false);
+ mDevice.pressBack();
+ mDevice.pressBack();
+ getInstrumentation().waitForIdleSync();
+ }
+
+ private void assertRatingViewIsChecked(int minAge, int maxAge, int selectedAge,
+ boolean expectedValue) {
+ for (int age = minAge; age <= maxAge; age++) {
+ UiObject2 ratingCheckBox = mSidePanelHelper.assertNavigateToItem(String.valueOf(age))
+ .findObject(ByResource.id(mTargetResources, R.id.check_box));
+ if (age < selectedAge) {
+ assertTrue("The lower rating age should be unblocked", !ratingCheckBox.isChecked());
+ } else if (age > selectedAge) {
+ assertTrue("The higher rating age should be blocked", ratingCheckBox.isChecked());
+ } else {
+ assertEquals("The rating for age " + selectedAge + " isBlocked ", expectedValue,
+ ratingCheckBox.isChecked());
+ }
+ }
+ }
+
+ /**
+ * Prepare the need for testRatingDependentSelect.
+ * 1. Turn on parental control if it's off.
+ * 2. Make sure Japan rating system is selected.
+ */
+ private void prepareParentalControl() {
+ showParentalControl();
+ switchParentalControl(R.string.option_toggle_parental_controls_off);
+ // Show all rating systems.
+ mSidePanelHelper.assertNavigateToItem(R.string.option_program_restrictions);
+ mDevice.pressDPadCenter();
+ BySelector bySidePanel = mSidePanelHelper.bySidePanelTitled(
+ R.string.option_program_restrictions);
+ assertWaitForCondition(mDevice, Until.hasObject(bySidePanel));
+ mSidePanelHelper.assertNavigateToItem(R.string.option_country_rating_systems);
+ mDevice.pressDPadCenter();
+ bySidePanel = mSidePanelHelper.bySidePanelTitled(R.string.option_country_rating_systems);
+ assertWaitForCondition(mDevice,Until.hasObject(bySidePanel));
+ mSidePanelHelper.assertNavigateToItem(R.string.option_see_all_rating_systems);
+ mDevice.pressDPadCenter();
+ // Make sure Japan rating system is selected.
+ UiObject2 ratingSystemCheckBox = mSidePanelHelper.assertNavigateToItem("Japan")
+ .findObject(ByResource.id(mTargetResources, R.id.check_box));
+ if (!ratingSystemCheckBox.isChecked()) {
+ mDevice.pressDPadCenter();
+ getInstrumentation().waitForIdleSync();
+ }
+ mDevice.pressBack();
+ }
+
+ private void switchParentalControl(int oppositeStateResId) {
+ BySelector bySidePanel = mSidePanelHelper.byViewText(oppositeStateResId);
+ if (mDevice.hasObject(bySidePanel)) {
+ mSidePanelHelper.assertNavigateToItem(oppositeStateResId);
+ mDevice.pressDPadCenter();
+ getInstrumentation().waitForIdleSync();
+ }
+ }
+
+ private void showParentalControl() {
+ // Show menu and select parental controls.
+ mMenuHelper.showMenu();
+ mMenuHelper.assertPressOptionsSettings();
+ assertWaitForCondition(mDevice, Until.hasObject(mBySettingsSidePanel));
+ mSidePanelHelper.assertNavigateToItem(R.string.settings_parental_controls);
+ mDevice.pressDPadCenter();
+ // Enter pin code.
+ DialogHelper dialogHelper = new DialogHelper(mDevice, mTargetResources);
+ dialogHelper.assertWaitForPinDialogOpen();
+ dialogHelper.enterPinCodes();
+ dialogHelper.assertWaitForPinDialogClose();
+ BySelector bySidePanel = mSidePanelHelper.bySidePanelTitled(
+ R.string.menu_parental_controls);
+ assertWaitForCondition(mDevice, Until.hasObject(bySidePanel));
+ }
+}
diff --git a/tests/input/Android.mk b/tests/input/Android.mk
index ea1d0f71..2efd32b9 100644
--- a/tests/input/Android.mk
+++ b/tests/input/Android.mk
@@ -8,6 +8,7 @@ LOCAL_MODULE_TAGS := optional
LOCAL_PROGUARD_ENABLED := disabled
# Overlay view related functionality requires system APIs.
LOCAL_SDK_VERSION := system_current
+LOCAL_MIN_SDK_VERSION := 23 # M
LOCAL_STATIC_JAVA_LIBRARIES := \
tv-test-common \
diff --git a/tests/jank/Android.mk b/tests/jank/Android.mk
index 3d0b3bfd..b71ea1b7 100644
--- a/tests/jank/Android.mk
+++ b/tests/jank/Android.mk
@@ -18,5 +18,6 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
LOCAL_INSTRUMENTATION_FOR := LiveTv
LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 23 # M
include $(BUILD_PACKAGE)
diff --git a/tests/unit/Android.mk b/tests/unit/Android.mk
index 55ee5276..3808cdc2 100644
--- a/tests/unit/Android.mk
+++ b/tests/unit/Android.mk
@@ -17,5 +17,6 @@ LOCAL_PACKAGE_NAME := TVUnitTests
LOCAL_INSTRUMENTATION_FOR := LiveTv
LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 23 # M
include $(BUILD_PACKAGE)
diff --git a/tests/unit/src/com/android/tv/common/ui/setup/leanback/PagingIndicatorTest.java b/tests/unit/src/com/android/tv/common/ui/setup/leanback/PagingIndicatorTest.java
deleted file mode 100644
index b342de66..00000000
--- a/tests/unit/src/com/android/tv/common/ui/setup/leanback/PagingIndicatorTest.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.tv.common.ui.setup.leanback;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.tv.testing.Utils;
-
-/**
- * Tests for {@link PagingIndicator}.
- */
-@SmallTest
-public class PagingIndicatorTest extends AndroidTestCase {
- private PagingIndicator mIndicator;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- Utils.runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mIndicator = new PagingIndicator(getContext());
- }
- });
- }
-
- public void testDotPosition() {
- mIndicator.setPageCount(3);
- assertDotPosition();
- mIndicator.setPageCount(6);
- assertDotPosition();
- mIndicator.setPageCount(9);
- assertDotPosition();
- }
-
- private void assertDotPosition() {
- assertSymmetry();
- assertDistance();
- }
-
- private void assertSymmetry() {
- int pageCount = mIndicator.getPageCount();
- int mid = pageCount / 2;
- int[] selectedX = mIndicator.getDotSelectedX();
- int sum = selectedX[0] + selectedX[pageCount - 1];
- for (int i = 1; i <= mid; ++i) {
- assertEquals("Selected dots are not symmetric", sum,
- selectedX[i] + selectedX[pageCount - i - 1]);
- }
- int[] leftX = mIndicator.getDotSelectedLeftX();
- int[] rightX = mIndicator.getDotSelectedRightX();
- sum = leftX[0] + rightX[pageCount - 1];
- for (int i = 1; i < pageCount - 1; ++i) {
- assertEquals("Deselected dots are not symmetric", sum,
- leftX[i] + rightX[pageCount - i - 1]);
- }
- }
-
- private void assertDistance() {
- int pageCount = mIndicator.getPageCount();
- int[] selectedX = mIndicator.getDotSelectedX();
- int[] leftX = mIndicator.getDotSelectedLeftX();
- int[] rightX = mIndicator.getDotSelectedRightX();
- int distance = selectedX[1] - selectedX[0];
- for (int i = 2; i < pageCount; ++i) {
- assertEquals("Gaps between selected dots are not even", distance,
- selectedX[i] - selectedX[i - 1]);
- }
- distance = leftX[1] - leftX[0];
- for (int i = 2; i < pageCount - 1; ++i) {
- assertEquals("Gaps between left dots are not even", distance,
- leftX[i] - leftX[i - 1]);
- }
- distance = rightX[2] - rightX[1];
- for (int i = 3; i < pageCount; ++i) {
- assertEquals("Gaps between right dots are not even", distance,
- rightX[i] - rightX[i - 1]);
- }
- }
-}
diff --git a/tests/unit/src/com/android/tv/data/ChannelDataManagerTest.java b/tests/unit/src/com/android/tv/data/ChannelDataManagerTest.java
index 4dc91ce3..574dac8d 100644
--- a/tests/unit/src/com/android/tv/data/ChannelDataManagerTest.java
+++ b/tests/unit/src/com/android/tv/data/ChannelDataManagerTest.java
@@ -206,6 +206,8 @@ public class ChannelDataManagerTest extends AndroidTestCase {
channelListener.reset();
// Test {@link ChannelDataManager#applyUpdatedValuesToDb}
+ // Disable the update notification to avoid the unwanted call of "onLoadFinished".
+ mContentResolver.mNotifyDisabled = true;
mChannelDataManager.applyUpdatedValuesToDb();
restart();
browsableChannelList = mChannelDataManager.getBrowsableChannelList();
@@ -240,6 +242,8 @@ public class ChannelDataManagerTest extends AndroidTestCase {
assertFalse(browsableChannelList.contains(channel2));
// Test {@link ChannelDataManager#applyUpdatedValuesToDb}
+ // Disable the update notification to avoid the unwanted call of "onLoadFinished".
+ mContentResolver.mNotifyDisabled = true;
mChannelDataManager.applyUpdatedValuesToDb();
restart();
browsableChannelList = mChannelDataManager.getBrowsableChannelList();
@@ -270,6 +274,8 @@ public class ChannelDataManagerTest extends AndroidTestCase {
assertTrue(mChannelDataManager.getChannel(channel.getId()).isLocked());
// Test {@link ChannelDataManager#applyUpdatedValuesToDb}.
+ // Disable the update notification to avoid the unwanted call of "onLoadFinished".
+ mContentResolver.mNotifyDisabled = true;
mChannelDataManager.applyUpdatedValuesToDb();
restart();
assertTrue(mChannelDataManager.getChannel(channel.getId()).isLocked());
@@ -343,11 +349,17 @@ public class ChannelDataManagerTest extends AndroidTestCase {
}
private class FakeContentResolver extends MockContentResolver {
+ boolean mNotifyDisabled;
+
@Override
public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
super.notifyChange(uri, observer, syncToNetwork);
if (DEBUG) {
- Log.d(TAG, "onChanged(uri=" + uri + ", observer=" + observer + ")");
+ Log.d(TAG, "onChanged(uri=" + uri + ", observer=" + observer + ") - Notification "
+ + (mNotifyDisabled ? "disabled" : "enabled"));
+ }
+ if (mNotifyDisabled) {
+ return;
}
// Do not call {@link ContentObserver#onChange} directly to run it on the correct
// thread.
diff --git a/tests/unit/src/com/android/tv/data/GenreItemTest.java b/tests/unit/src/com/android/tv/data/GenreItemTest.java
new file mode 100644
index 00000000..643768f8
--- /dev/null
+++ b/tests/unit/src/com/android/tv/data/GenreItemTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.data;
+
+import android.media.tv.TvContract.Programs.Genres;
+import android.os.Build;
+import android.support.test.filters.SdkSuppress;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/**
+ * Tests for {@link Channel}.
+ */
+@SmallTest
+public class GenreItemTest extends AndroidTestCase {
+ private static final String INVALID_GENRE = "INVALID GENRE";
+
+ public void testGetLabels() {
+ // Checks if no exception is thrown.
+ GenreItems.getLabels(getContext());
+ }
+
+ public void testGetCanonicalGenre() {
+ int count = GenreItems.getGenreCount();
+ assertNull(GenreItems.getCanonicalGenre(GenreItems.ID_ALL_CHANNELS));
+ for (int i = 1; i < count; ++i) {
+ assertNotNull(GenreItems.getCanonicalGenre(i));
+ }
+ }
+
+ public void testGetId_base() {
+ int count = GenreItems.getGenreCount();
+ assertEquals(GenreItems.ID_ALL_CHANNELS, GenreItems.getId(null));
+ assertEquals(GenreItems.ID_ALL_CHANNELS, GenreItems.getId(INVALID_GENRE));
+ assertInRange(GenreItems.getId(Genres.FAMILY_KIDS), 1, count - 1);
+ assertInRange(GenreItems.getId(Genres.SPORTS), 1, count - 1);
+ assertInRange(GenreItems.getId(Genres.SHOPPING), 1, count - 1);
+ assertInRange(GenreItems.getId(Genres.MOVIES), 1, count - 1);
+ assertInRange(GenreItems.getId(Genres.COMEDY), 1, count - 1);
+ assertInRange(GenreItems.getId(Genres.TRAVEL), 1, count - 1);
+ assertInRange(GenreItems.getId(Genres.DRAMA), 1, count - 1);
+ assertInRange(GenreItems.getId(Genres.EDUCATION), 1, count - 1);
+ assertInRange(GenreItems.getId(Genres.ANIMAL_WILDLIFE), 1, count - 1);
+ assertInRange(GenreItems.getId(Genres.NEWS), 1, count - 1);
+ assertInRange(GenreItems.getId(Genres.GAMING), 1, count - 1);
+ }
+
+ public void testGetId_lmp_mr1() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
+ assertEquals(GenreItems.ID_ALL_CHANNELS, GenreItems.getId(Genres.ARTS));
+ assertEquals(GenreItems.ID_ALL_CHANNELS, GenreItems.getId(Genres.ENTERTAINMENT));
+ assertEquals(GenreItems.ID_ALL_CHANNELS, GenreItems.getId(Genres.LIFE_STYLE));
+ assertEquals(GenreItems.ID_ALL_CHANNELS, GenreItems.getId(Genres.MUSIC));
+ assertEquals(GenreItems.ID_ALL_CHANNELS, GenreItems.getId(Genres.PREMIER));
+ assertEquals(GenreItems.ID_ALL_CHANNELS, GenreItems.getId(Genres.TECH_SCIENCE));
+ } else {
+ int count = GenreItems.getGenreCount();
+ assertInRange(GenreItems.getId(Genres.ARTS), 1, count - 1);
+ assertInRange(GenreItems.getId(Genres.ENTERTAINMENT), 1, count - 1);
+ assertInRange(GenreItems.getId(Genres.LIFE_STYLE), 1, count - 1);
+ assertInRange(GenreItems.getId(Genres.MUSIC), 1, count - 1);
+ assertInRange(GenreItems.getId(Genres.PREMIER), 1, count - 1);
+ assertInRange(GenreItems.getId(Genres.TECH_SCIENCE), 1, count - 1);
+ }
+ }
+
+ private void assertInRange(int value, int lower, int upper) {
+ assertTrue(value >= lower && value <= upper);
+ }
+}
diff --git a/tests/unit/src/com/android/tv/data/TvInputNewComparatorTest.java b/tests/unit/src/com/android/tv/data/TvInputNewComparatorTest.java
index 0914f804..6b2bc8e5 100644
--- a/tests/unit/src/com/android/tv/data/TvInputNewComparatorTest.java
+++ b/tests/unit/src/com/android/tv/data/TvInputNewComparatorTest.java
@@ -16,10 +16,12 @@
package com.android.tv.data;
+import android.annotation.SuppressLint;
import android.content.pm.ResolveInfo;
import android.media.tv.TvInputInfo;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
import android.util.Pair;
import com.android.tv.testing.ComparatorTester;
@@ -40,6 +42,7 @@ import java.util.LinkedHashMap;
*/
@SmallTest
public class TvInputNewComparatorTest extends AndroidTestCase {
+ @Suppress // http://b/26903987
public void testComparator() throws Exception {
final LinkedHashMap<String, Pair<Boolean, Boolean>> INPUT_ID_TO_NEW_INPUT =
new LinkedHashMap<>();
diff --git a/tests/unit/src/com/android/tv/dvr/BaseDvrDataManagerTest.java b/tests/unit/src/com/android/tv/dvr/BaseDvrDataManagerTest.java
new file mode 100644
index 00000000..3df9ab97
--- /dev/null
+++ b/tests/unit/src/com/android/tv/dvr/BaseDvrDataManagerTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.dvr;
+
+import android.support.annotation.NonNull;
+import android.support.test.filters.SmallTest;
+import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
+
+import com.android.tv.testing.FakeClock;
+import com.android.tv.testing.dvr.RecordingTestUtils;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for {@link BaseDvrDataManager} using {@link DvrDataManagerInMemoryImpl}.
+ */
+@SmallTest
+public class BaseDvrDataManagerTest extends AndroidTestCase {
+ private static final int CHANNEL_ID = 273;
+
+ private DvrDataManagerInMemoryImpl mDvrDataManager;
+ private FakeClock mFakeClock;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mFakeClock = FakeClock.createWithCurrentTime();
+ mDvrDataManager = new DvrDataManagerInMemoryImpl(getContext(), mFakeClock);
+ }
+
+ public void testGetNonStartedScheduledRecordings() {
+ ScheduledRecording recording = mDvrDataManager
+ .addScheduledRecordingInternal(createNewScheduledRecordingStartingNow());
+ List<ScheduledRecording> result = mDvrDataManager.getNonStartedScheduledRecordings();
+ MoreAsserts.assertContentsInAnyOrder(result, recording);
+ }
+
+ public void testGetNonStartedScheduledRecordings_past() {
+ mDvrDataManager.addScheduledRecordingInternal(createNewScheduledRecordingStartingNow());
+ mFakeClock.increment(TimeUnit.MINUTES, 6);
+ List<ScheduledRecording> result = mDvrDataManager.getNonStartedScheduledRecordings();
+ MoreAsserts.assertContentsInAnyOrder(result);
+ }
+
+ @NonNull
+ private ScheduledRecording createNewScheduledRecordingStartingNow() {
+ return ScheduledRecording.buildFrom(RecordingTestUtils
+ .createTestRecordingWithIdAndPeriod(
+ -1,
+ CHANNEL_ID,
+ mFakeClock.currentTimeMillis(),
+ mFakeClock.currentTimeMillis() + TimeUnit.MINUTES.toMillis(5)))
+ .setState(ScheduledRecording.STATE_RECORDING_NOT_STARTED)
+ .build();
+ }
+}
diff --git a/tests/unit/src/com/android/tv/dvr/DvrDataManagerImplTest.java b/tests/unit/src/com/android/tv/dvr/DvrDataManagerImplTest.java
index 204f7cec..a9c5d390 100644
--- a/tests/unit/src/com/android/tv/dvr/DvrDataManagerImplTest.java
+++ b/tests/unit/src/com/android/tv/dvr/DvrDataManagerImplTest.java
@@ -26,36 +26,44 @@ import java.util.ArrayList;
import java.util.List;
/**
- * Tests for {@link DvrDataManagerImplTest}
+ * Tests for {@link DvrDataManagerImpl}
*/
@SmallTest
public class DvrDataManagerImplTest extends TestCase {
+ private static final int CHANNEL_ID = 273;
+
public void testGetNextScheduledStartTimeAfter() throws Exception {
long id = 1;
- List<Recording> recordings = new ArrayList<>();
- assertNextStartTime(recordings, 0L, DvrDataManager.NEXT_START_TIME_NOT_FOUND);
- recordings.add(RecordingTestUtils.createTestRecordingWithIdAndPeriod(id++, 10L, 20L));
- assertNextStartTime(recordings, 9L, 10L);
- assertNextStartTime(recordings, 10L, DvrDataManager.NEXT_START_TIME_NOT_FOUND);
- recordings.add(RecordingTestUtils.createTestRecordingWithIdAndPeriod(id++, 20L, 30L));
- assertNextStartTime(recordings, 9L, 10L);
- assertNextStartTime(recordings, 10L, 20L);
- assertNextStartTime(recordings, 20L, DvrDataManager.NEXT_START_TIME_NOT_FOUND);
- recordings.add(RecordingTestUtils.createTestRecordingWithIdAndPeriod(id++, 30L, 40L));
- assertNextStartTime(recordings, 9L, 10L);
- assertNextStartTime(recordings, 10L, 20L);
- assertNextStartTime(recordings, 20L, 30L);
- assertNextStartTime(recordings, 30L, DvrDataManager.NEXT_START_TIME_NOT_FOUND);
- recordings.clear();
- recordings.add(RecordingTestUtils.createTestRecordingWithIdAndPeriod(id++, 10L, 20L));
- recordings.add(RecordingTestUtils.createTestRecordingWithIdAndPeriod(id++, 10L, 20L));
- recordings.add(RecordingTestUtils.createTestRecordingWithIdAndPeriod(id++, 10L, 20L));
- assertNextStartTime(recordings, 9L, 10L);
- assertNextStartTime(recordings, 10L, DvrDataManager.NEXT_START_TIME_NOT_FOUND);
+ List<ScheduledRecording> scheduledRecordings = new ArrayList<>();
+ assertNextStartTime(scheduledRecordings, 0L, DvrDataManager.NEXT_START_TIME_NOT_FOUND);
+ scheduledRecordings.add(RecordingTestUtils
+ .createTestRecordingWithIdAndPeriod(id++, CHANNEL_ID, 10L, 20L));
+ assertNextStartTime(scheduledRecordings, 9L, 10L);
+ assertNextStartTime(scheduledRecordings, 10L, DvrDataManager.NEXT_START_TIME_NOT_FOUND);
+ scheduledRecordings.add(RecordingTestUtils
+ .createTestRecordingWithIdAndPeriod(id++, CHANNEL_ID, 20L, 30L));
+ assertNextStartTime(scheduledRecordings, 9L, 10L);
+ assertNextStartTime(scheduledRecordings, 10L, 20L);
+ assertNextStartTime(scheduledRecordings, 20L, DvrDataManager.NEXT_START_TIME_NOT_FOUND);
+ scheduledRecordings.add(RecordingTestUtils
+ .createTestRecordingWithIdAndPeriod(id++, CHANNEL_ID, 30L, 40L));
+ assertNextStartTime(scheduledRecordings, 9L, 10L);
+ assertNextStartTime(scheduledRecordings, 10L, 20L);
+ assertNextStartTime(scheduledRecordings, 20L, 30L);
+ assertNextStartTime(scheduledRecordings, 30L, DvrDataManager.NEXT_START_TIME_NOT_FOUND);
+ scheduledRecordings.clear();
+ scheduledRecordings.add(RecordingTestUtils
+ .createTestRecordingWithIdAndPeriod(id++, CHANNEL_ID, 10L, 20L));
+ scheduledRecordings.add(RecordingTestUtils
+ .createTestRecordingWithIdAndPeriod(id++, CHANNEL_ID, 10L, 20L));
+ scheduledRecordings.add(RecordingTestUtils
+ .createTestRecordingWithIdAndPeriod(id++, CHANNEL_ID, 10L, 20L));
+ assertNextStartTime(scheduledRecordings, 9L, 10L);
+ assertNextStartTime(scheduledRecordings, 10L, DvrDataManager.NEXT_START_TIME_NOT_FOUND);
}
- private void assertNextStartTime(List<Recording> recordings, long startTime, long expected) {
+ private void assertNextStartTime(List<ScheduledRecording> scheduledRecordings, long startTime, long expected) {
assertEquals("getNextScheduledStartTimeAfter()", expected,
- DvrDataManagerImpl.getNextStartTimeAfter(recordings, startTime));
+ DvrDataManagerImpl.getNextStartTimeAfter(scheduledRecordings, startTime));
}
} \ No newline at end of file
diff --git a/tests/unit/src/com/android/tv/dvr/DvrRecordingServiceTest.java b/tests/unit/src/com/android/tv/dvr/DvrRecordingServiceTest.java
index 418caa7e..292233a2 100644
--- a/tests/unit/src/com/android/tv/dvr/DvrRecordingServiceTest.java
+++ b/tests/unit/src/com/android/tv/dvr/DvrRecordingServiceTest.java
@@ -19,6 +19,8 @@ package com.android.tv.dvr;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.os.Build;
+import android.support.test.filters.SdkSuppress;
import android.test.ServiceTestCase;
import android.test.suitebuilder.annotation.SmallTest;
@@ -26,6 +28,7 @@ import com.android.tv.ApplicationSingletons;
import com.android.tv.MockTvApplication;
import com.android.tv.common.feature.CommonFeatures;
import com.android.tv.common.feature.TestableFeature;
+import com.android.tv.testing.FakeClock;
import org.mockito.Mock;
import org.mockito.Mockito;
@@ -35,6 +38,7 @@ import org.mockito.MockitoAnnotations;
* Tests for {@link DvrRecordingService}.
*/
@SmallTest
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
public class DvrRecordingServiceTest extends ServiceTestCase<DvrRecordingService> {
@Mock Scheduler mMockScheduler;
@@ -42,13 +46,14 @@ public class DvrRecordingServiceTest extends ServiceTestCase<DvrRecordingService
private final TestableFeature mDvrFeature = CommonFeatures.DVR;
private DvrDataManagerInMemoryImpl mDataManager;
private DvrRecordingService mService;
+ private FakeClock mFakeClock = FakeClock.createWithCurrentTime();
@Override
protected void setUp() throws Exception {
super.setUp();
mDvrFeature.enableForTest();
MockitoAnnotations.initMocks(this);
- mDataManager = new DvrDataManagerInMemoryImpl(getContext());
+ mDataManager = new DvrDataManagerInMemoryImpl(getContext(), mFakeClock);
setApplication(new MockTvApplication(mApplicationSingletons));
when(mApplicationSingletons.getDvrDataManager()).thenReturn(mDataManager);
setupService();
diff --git a/tests/unit/src/com/android/tv/dvr/RecordingTaskTest.java b/tests/unit/src/com/android/tv/dvr/RecordingTaskTest.java
index 8e3b4fd4..2fb0228b 100644
--- a/tests/unit/src/com/android/tv/dvr/RecordingTaskTest.java
+++ b/tests/unit/src/com/android/tv/dvr/RecordingTaskTest.java
@@ -16,7 +16,6 @@
package com.android.tv.dvr;
-import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.argThat;
@@ -26,6 +25,8 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import android.media.tv.TvRecordingClient;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -34,8 +35,8 @@ import android.support.test.filters.SdkSuppress;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
-import com.android.tv.common.recording.TvRecording;
import com.android.tv.data.Channel;
+import com.android.tv.data.Program;
import com.android.tv.dvr.RecordingTask.State;
import com.android.tv.testing.FakeClock;
import com.android.tv.testing.dvr.RecordingTestUtils;
@@ -52,19 +53,18 @@ import java.util.concurrent.TimeUnit;
* Tests for {@link RecordingTask}.
*/
@SmallTest
-@SdkSuppress(minSdkVersion = 23)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
public class RecordingTaskTest extends AndroidTestCase {
private static final long DURATION = TimeUnit.MINUTES.toMillis(30);
private static final long START_OFFSET = Scheduler.MS_TO_WAKE_BEFORE_START;
+ public static final int CHANNEL_ID = 273;
private FakeClock mFakeClock;
private DvrDataManagerInMemoryImpl mDataManager;
- @Mock
- Handler mMockHandler;
- @Mock
- DvrSessionManager mMockSessionManager;
- @Mock
- TvRecording.TvRecordingClient mMockTvRecordingClient;
+ @Mock Handler mMockHandler;
+ @Mock DvrManager mDvrManager;
+ @Mock DvrSessionManager mMockSessionManager;
+ @Mock TvRecordingClient mMockTvRecordingClient;
@Override
protected void setUp() throws Exception {
@@ -74,17 +74,16 @@ public class RecordingTaskTest extends AndroidTestCase {
}
MockitoAnnotations.initMocks(this);
mFakeClock = FakeClock.createWithCurrentTime();
- mDataManager = new DvrDataManagerInMemoryImpl(getContext());
+ mDataManager = new DvrDataManagerInMemoryImpl(getContext(), mFakeClock);
}
-
public void testHandle_init() {
- Recording r = createRecording();
- RecordingTask task = createRecordingTask(r);
- Channel channel = r.getChannel();
+ Channel channel = createTestChannel();
+ ScheduledRecording r = createRecording(channel);
+ RecordingTask task = createRecordingTask(r, channel);
String inputId = channel.getInputId();
when(mMockSessionManager.canAcquireDvrSession(inputId, channel)).thenReturn(true);
- when(mMockSessionManager.acquireDvrSession(inputId, channel))
+ when(mMockSessionManager.createTvRecordingClient("tag", task, null))
.thenReturn(mMockTvRecordingClient);
when(mMockHandler.sendEmptyMessageDelayed(anyInt(), anyLong())).thenReturn(true);
@@ -94,49 +93,59 @@ public class RecordingTaskTest extends AndroidTestCase {
assertEquals(State.CONNECTION_PENDING, task.getState());
verify(mMockSessionManager).canAcquireDvrSession(inputId, channel);
- verify(mMockSessionManager).acquireDvrSession(inputId, channel);
- verify(mMockTvRecordingClient).connect(eq(inputId), any(TvRecording.ClientCallback.class));
+ verify(mMockSessionManager).createTvRecordingClient("tag", task, null);
+ verify(mMockTvRecordingClient).tune(eq(inputId), eq(channel.getUri()));
verifySendMessageAt(RecordingTask.MESSAGE_START_RECORDING, uptime + delay);
verifyNoMoreInteractions(mMockHandler, mMockTvRecordingClient, mMockSessionManager);
}
+ private static Channel createTestChannel() {
+ return new Channel.Builder().setId(CHANNEL_ID).setDisplayName("Test Ch " + CHANNEL_ID)
+ .build();
+ }
public void testHandle_init_cannotAcquireSession() {
- Recording r = createRecording();
- r = mDataManager.addRecordingInternal(r);
- RecordingTask task = createRecordingTask(r);
+ Channel channel = createTestChannel();
+ ScheduledRecording r = createRecording(channel);
+ r = mDataManager.addScheduledRecordingInternal(r);
+ RecordingTask task = createRecordingTask(r, channel);
- when(mMockSessionManager.canAcquireDvrSession(r.getChannel().getInputId(), r.getChannel()))
+ when(mMockSessionManager.canAcquireDvrSession(channel.getInputId(), channel))
.thenReturn(false);
assertTrue(task.handleMessage(createMessage(RecordingTask.MESSAGE_INIT)));
assertEquals(State.ERROR, task.getState());
verifySendMessage(Scheduler.HandlerWrapper.MESSAGE_REMOVE);
- Recording updatedRecording = mDataManager.getRecording(r.getId());
- assertEquals("status", Recording.STATE_RECORDING_FAILED, updatedRecording.getState());
+ ScheduledRecording updatedScheduledRecording = mDataManager
+ .getScheduledRecording(r.getId());
+ assertEquals("status", ScheduledRecording.STATE_RECORDING_FAILED,
+ updatedScheduledRecording.getState());
}
public void testOnConnected() {
- Recording r = createRecording();
- mDataManager.addRecording(r);
- RecordingTask task = createRecordingTask(r);
+ Channel channel = createTestChannel();
+ ScheduledRecording r = createRecording(channel);
+ mDataManager.addScheduledRecording(r);
+ RecordingTask task = createRecordingTask(r, channel);
- task.onConnected();
+ task.onTuned(channel.getUri());
assertEquals(State.CONNECTED, task.getState());
}
- private Recording createRecording() {
+ private ScheduledRecording createRecording(Channel c) {
long startTime = mFakeClock.currentTimeMillis() + START_OFFSET;
long endTime = startTime + DURATION;
- return RecordingTestUtils.createTestRecordingWithPeriod(startTime, endTime);
+ return RecordingTestUtils.createTestRecordingWithPeriod(c.getId(), startTime, endTime);
}
- private RecordingTask createRecordingTask(Recording r) {
- RecordingTask recordingTask = new RecordingTask(r, mMockSessionManager, mDataManager,
- mFakeClock);
+ private RecordingTask createRecordingTask(ScheduledRecording r, Channel channel) {
+ Program p = r.getProgramId() == ScheduledRecording.ID_NOT_SET ? null
+ : new Program.Builder().setId(r.getId()).build();
+ RecordingTask recordingTask = new RecordingTask(r, channel, mDvrManager,
+ mMockSessionManager, mDataManager, mFakeClock);
recordingTask.setHandler(mMockHandler);
return recordingTask;
}
diff --git a/tests/unit/src/com/android/tv/dvr/ScheduledProgramReaperTest.java b/tests/unit/src/com/android/tv/dvr/ScheduledProgramReaperTest.java
new file mode 100644
index 00000000..6210f464
--- /dev/null
+++ b/tests/unit/src/com/android/tv/dvr/ScheduledProgramReaperTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.dvr;
+
+import android.test.MoreAsserts;
+
+import com.android.tv.testing.FakeClock;
+import com.android.tv.testing.dvr.RecordingTestUtils;
+
+import junit.framework.TestCase;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for {@link ScheduledProgramReaper}.
+ */
+public class ScheduledProgramReaperTest extends TestCase {
+ public static final int CHANNEL_ID = 273;
+ public static final long DURATION = TimeUnit.HOURS.toMillis(1);
+
+ private ScheduledProgramReaper mReaper;
+ private FakeClock mFakeClock;
+ private DvrDataManagerInMemoryImpl mDvrDataManager;
+ private ScheduledRecording mScheduledRecordingDay1;
+
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mFakeClock = FakeClock.createWithTimeOne();
+ mDvrDataManager = new DvrDataManagerInMemoryImpl(null, mFakeClock);
+ mReaper = new ScheduledProgramReaper(mDvrDataManager, mFakeClock);
+ }
+
+
+ public void testRun_noRecordings() {
+ MoreAsserts.assertContentsInAnyOrder(mDvrDataManager.getAllScheduledRecordings());
+ mReaper.run();
+ MoreAsserts.assertContentsInAnyOrder(mDvrDataManager.getAllScheduledRecordings());
+ }
+
+ public void testRun_oneRecordingsTomorrow() {
+ ScheduledRecording recording = addNewScheduledRecordingForTomorrow();
+ MoreAsserts
+ .assertContentsInAnyOrder(mDvrDataManager.getAllScheduledRecordings(), recording);
+ mReaper.run();
+ MoreAsserts
+ .assertContentsInAnyOrder(mDvrDataManager.getAllScheduledRecordings(), recording);
+ }
+
+ public void testRun_oneRecordingsStarted() {
+ ScheduledRecording recording = addNewScheduledRecordingForTomorrow();
+ MoreAsserts
+ .assertContentsInAnyOrder(mDvrDataManager.getAllScheduledRecordings(), recording);
+ mFakeClock.increment(TimeUnit.DAYS);
+ mReaper.run();
+ MoreAsserts
+ .assertContentsInAnyOrder(mDvrDataManager.getAllScheduledRecordings(), recording);
+ }
+
+ public void testRun_oneRecordingsFinished() {
+ ScheduledRecording recording = addNewScheduledRecordingForTomorrow();
+ MoreAsserts
+ .assertContentsInAnyOrder(mDvrDataManager.getAllScheduledRecordings(), recording);
+ mFakeClock.increment(TimeUnit.DAYS);
+ mFakeClock.increment(TimeUnit.MINUTES, 2);
+ mReaper.run();
+ MoreAsserts
+ .assertContentsInAnyOrder(mDvrDataManager.getAllScheduledRecordings(), recording);
+ }
+
+ public void testRun_oneRecordingsExpired() {
+ ScheduledRecording recording = addNewScheduledRecordingForTomorrow();
+ MoreAsserts
+ .assertContentsInAnyOrder(mDvrDataManager.getAllScheduledRecordings(), recording);
+ mFakeClock.increment(TimeUnit.DAYS, 1 + ScheduledProgramReaper.DAYS);
+ mFakeClock.increment(TimeUnit.MILLISECONDS, DURATION);
+ // After the cutoff and enough so we can see on the clock
+ mFakeClock.increment(TimeUnit.SECONDS, 1);
+
+ mReaper.run();
+ MoreAsserts.assertContentsInAnyOrder(
+ "Recordings after reaper at " + com.android.tv.util.Utils
+ .toIsoDateTimeString(mFakeClock.currentTimeMillis()),
+ mDvrDataManager.getAllScheduledRecordings());
+ }
+
+ private ScheduledRecording addNewScheduledRecordingForTomorrow() {
+ long startTime = mFakeClock.currentTimeMillis() + TimeUnit.DAYS.toMillis(1);
+ return RecordingTestUtils.addScheduledRecording(mDvrDataManager, CHANNEL_ID, startTime,
+ startTime + DURATION);
+ }
+}
diff --git a/tests/unit/src/com/android/tv/dvr/RecordingTest.java b/tests/unit/src/com/android/tv/dvr/ScheduledRecordingTest.java
index e5ffaa3b..1aee6d33 100644
--- a/tests/unit/src/com/android/tv/dvr/RecordingTest.java
+++ b/tests/unit/src/com/android/tv/dvr/ScheduledRecordingTest.java
@@ -35,12 +35,14 @@ import java.util.Collections;
import java.util.List;
/**
- * Tests for {@link RecordingTest}
+ * Tests for {@link ScheduledRecordingTest}
*/
@SmallTest
-public class RecordingTest extends TestCase {
+public class ScheduledRecordingTest extends TestCase {
+ private static final int CHANNEL_ID = 273;
+
public void testIsOverLapping() throws Exception {
- Recording r = createTestRecordingWithIdAndPeriod(1, 10L, 20L);
+ ScheduledRecording r = createTestRecordingWithIdAndPeriod(1, CHANNEL_ID, 10L, 20L);
assertOverLapping(false, 1L, 9L, r);
assertOverLapping(true, 1L, 20L, r);
@@ -57,41 +59,44 @@ public class RecordingTest extends TestCase {
public void testBuildProgram() {
Channel c = new Channel.Builder().build();
Program p = new Program.Builder().build();
- Recording actual = Recording.builder(c, p).build();
- assertEquals("type", Recording.TYPE_PROGRAM, actual.getType());
+ ScheduledRecording actual = ScheduledRecording.builder(p).setChannelId(c.getId()).build();
+ assertEquals("type", ScheduledRecording.TYPE_PROGRAM, actual.getType());
}
public void testBuildTime() {
- Recording actual = createTestRecordingWithIdAndPeriod(1, 10L, 20L);
- assertEquals("type", Recording.TYPE_TIMED, actual.getType());
+ ScheduledRecording actual = createTestRecordingWithIdAndPeriod(1, CHANNEL_ID, 10L, 20L);
+ assertEquals("type", ScheduledRecording.TYPE_TIMED, actual.getType());
}
public void testBuildFrom() {
- Recording expected = createTestRecordingWithIdAndPeriod(1, 10L, 20L);
- Recording actual = Recording.buildFrom(expected).build();
+ ScheduledRecording expected = createTestRecordingWithIdAndPeriod(1, CHANNEL_ID, 10L, 20L);
+ ScheduledRecording actual = ScheduledRecording.buildFrom(expected).build();
RecordingTestUtils.assertRecordingEquals(expected, actual);
}
public void testBuild_priority() {
- Recording a = normalizePriority(createTestRecordingWithIdAndPeriod(1, 10L, 20L));
- Recording b = normalizePriority(createTestRecordingWithIdAndPeriod(2, 10L, 20L));
- Recording c = normalizePriority(createTestRecordingWithIdAndPeriod(3, 10L, 20L));
+ ScheduledRecording a = normalizePriority(
+ createTestRecordingWithIdAndPeriod(1, CHANNEL_ID, 10L, 20L));
+ ScheduledRecording b = normalizePriority(
+ createTestRecordingWithIdAndPeriod(2, CHANNEL_ID, 10L, 20L));
+ ScheduledRecording c = normalizePriority(
+ createTestRecordingWithIdAndPeriod(3, CHANNEL_ID, 10L, 20L));
// default priority
MoreAsserts.assertContentsInOrder(sortByPriority(c,b,a), a, b, c);
// make C preferred over B
- c = Recording.buildFrom(c).setPriority(b.getPriority() - 1).build();
+ c = ScheduledRecording.buildFrom(c).setPriority(b.getPriority() - 1).build();
MoreAsserts.assertContentsInOrder(sortByPriority(c,b,a), a, c, b);
}
- public Collection<Recording> sortByPriority(Recording a, Recording b, Recording c) {
- List<Recording> list = Arrays.asList(a, b, c);
- Collections.sort(list, Recording.PRIORITY_COMPARATOR);
+ public Collection<ScheduledRecording> sortByPriority(ScheduledRecording a, ScheduledRecording b, ScheduledRecording c) {
+ List<ScheduledRecording> list = Arrays.asList(a, b, c);
+ Collections.sort(list, ScheduledRecording.PRIORITY_COMPARATOR);
return list;
}
- private void assertOverLapping(boolean expected, long lower, long upper, Recording r) {
+ private void assertOverLapping(boolean expected, long lower, long upper, ScheduledRecording r) {
assertEquals("isOverlapping(Range(" + lower + "," + upper + "), recording " + r, expected,
r.isOverLapping(new Range<Long>(lower, upper)));
}
diff --git a/tests/unit/src/com/android/tv/dvr/SchedulerTest.java b/tests/unit/src/com/android/tv/dvr/SchedulerTest.java
index 6748eddb..140d9091 100644
--- a/tests/unit/src/com/android/tv/dvr/SchedulerTest.java
+++ b/tests/unit/src/com/android/tv/dvr/SchedulerTest.java
@@ -24,11 +24,13 @@ import static org.mockito.Mockito.verifyZeroInteractions;
import android.app.AlarmManager;
import android.app.PendingIntent;
+import android.os.Build;
import android.os.Looper;
import android.support.test.filters.SdkSuppress;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.tv.data.ChannelDataManager;
import com.android.tv.testing.FakeClock;
import com.android.tv.testing.dvr.RecordingTestUtils;
@@ -41,22 +43,27 @@ import java.util.concurrent.TimeUnit;
* Tests for {@link Scheduler}.
*/
@SmallTest
-@SdkSuppress(minSdkVersion = 23)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
public class SchedulerTest extends AndroidTestCase {
- private FakeClock mClock;
+ private static final int CHANNEL_ID = 273;
+
+ private FakeClock mFakeClock;
private DvrDataManagerInMemoryImpl mDataManager;
private Scheduler mScheduler;
+ @Mock DvrManager mDvrManager;
@Mock DvrSessionManager mSessionManager;
@Mock AlarmManager mMockAlarmManager;
+ @Mock
+ ChannelDataManager mChannelDataManager;
@Override
protected void setUp() throws Exception {
super.setUp();
MockitoAnnotations.initMocks(this);
- mClock = FakeClock.createWithCurrentTime();
- mDataManager = new DvrDataManagerInMemoryImpl(getContext());
- mScheduler = new Scheduler(Looper.myLooper(), mSessionManager, mDataManager, getContext(),
- mClock, mMockAlarmManager);
+ mFakeClock = FakeClock.createWithCurrentTime();
+ mDataManager = new DvrDataManagerInMemoryImpl(getContext(), mFakeClock);
+ mScheduler = new Scheduler(Looper.myLooper(), mDvrManager, mSessionManager, mDataManager,
+ mChannelDataManager, getContext(), mFakeClock, mMockAlarmManager);
}
public void testUpdate_none() throws Exception {
@@ -65,11 +72,12 @@ public class SchedulerTest extends AndroidTestCase {
}
public void testUpdate_nextIn12Hours() throws Exception {
- long now = mClock.currentTimeMillis();
+ long now = mFakeClock.currentTimeMillis();
long startTime = now + TimeUnit.HOURS.toMillis(12);
- Recording r = RecordingTestUtils.createTestRecordingWithPeriod(startTime,
+ ScheduledRecording r = RecordingTestUtils
+ .createTestRecordingWithPeriod(CHANNEL_ID, startTime,
startTime + TimeUnit.HOURS.toMillis(1));
- mDataManager.addRecording(r);
+ mDataManager.addScheduledRecording(r);
mScheduler.update();
verify(mMockAlarmManager).set(
eq(AlarmManager.RTC_WAKEUP),
@@ -78,10 +86,10 @@ public class SchedulerTest extends AndroidTestCase {
}
public void testStartsWithin() throws Exception {
- long now = mClock.currentTimeMillis();
+ long now = mFakeClock.currentTimeMillis();
long startTime = now + 3;
- Recording r = RecordingTestUtils
- .createTestRecordingWithPeriod(startTime, startTime + 100);
+ ScheduledRecording r = RecordingTestUtils
+ .createTestRecordingWithPeriod(273, startTime, startTime + 100);
assertFalse(mScheduler.startsWithin(r, 2));
assertTrue(mScheduler.startsWithin(r, 3));
}
diff --git a/tests/unit/src/com/android/tv/dvr/ui/SortedArrayAdapterTest.java b/tests/unit/src/com/android/tv/dvr/ui/SortedArrayAdapterTest.java
new file mode 100644
index 00000000..5195c57d
--- /dev/null
+++ b/tests/unit/src/com/android/tv/dvr/ui/SortedArrayAdapterTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.dvr.ui;
+
+import android.support.test.filters.SmallTest;
+import android.support.v17.leanback.widget.ClassPresenterSelector;
+import android.support.v17.leanback.widget.ObjectAdapter;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Objects;
+
+/**
+ * Tests for {@link SortedArrayAdapter}.
+ */
+@SmallTest
+public class SortedArrayAdapterTest extends TestCase {
+
+ public static final TestData P1 = TestData.create(1, "one");
+ public static final TestData P2 = TestData.create(2, "before");
+ public static final TestData P3 = TestData.create(3, "other");
+ private TestSortedArrayAdapter mAdapter;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mAdapter = new TestSortedArrayAdapter();
+ }
+
+ public void testContents_empty() {
+ assertEmpty();
+ }
+
+ public void testAdd_one() {
+ mAdapter.add(P1);
+ assertNotEmpty();
+ assertContentsInOrder(mAdapter, P1);
+ }
+
+ public void testAdd_two() {
+ mAdapter.add(P1);
+ mAdapter.add(P2);
+ assertNotEmpty();
+ assertContentsInOrder(mAdapter, P2, P1);
+ }
+
+ public void testAddAll_two() {
+ mAdapter.addAll(Arrays.asList(P1, P2));
+ assertNotEmpty();
+ assertContentsInOrder(mAdapter, P2, P1);
+ }
+
+ public void testRemove() {
+ mAdapter.add(P1);
+ mAdapter.add(P2);
+ assertNotEmpty();
+ assertContentsInOrder(mAdapter, P2, P1);
+ mAdapter.remove(P3);
+ assertContentsInOrder(mAdapter, P2, P1);
+ mAdapter.remove(P2);
+ assertContentsInOrder(mAdapter, P1);
+ mAdapter.remove(P1);
+ assertEmpty();
+ }
+
+ public void testChange_sorting() {
+ TestData p2_changed = TestData.create(2, "z changed");
+ mAdapter.add(P1);
+ mAdapter.add(P2);
+ assertNotEmpty();
+ assertContentsInOrder(mAdapter, P2, P1);
+ mAdapter.change(p2_changed);
+ assertContentsInOrder(mAdapter, P1, p2_changed);
+ }
+
+ public void testChange_new() {
+ mAdapter.change(P1);
+ assertNotEmpty();
+ assertContentsInOrder(mAdapter, P1);
+ }
+
+ private void assertEmpty() {
+ assertEquals("empty", true, mAdapter.isEmpty());
+ assertContentsInOrder(mAdapter, EmptyHolder.EMPTY_HOLDER);
+ }
+
+ private void assertNotEmpty() {
+ assertEquals("empty", false, mAdapter.isEmpty());
+ }
+
+ private static void assertContentsInOrder(ObjectAdapter adapter, Object... contents) {
+ int ex = contents.length;
+ assertEquals("size", ex, adapter.size());
+ for (int i = 0; i < ex; i++) {
+ assertEquals("element " + 1, contents[i], adapter.get(i));
+ }
+ }
+
+ private static class TestData {
+ @Override
+ public String toString() {
+ return "TestData[" + mId + "]{" + mText + '}';
+ }
+
+ static TestData create(long first, String text) {
+ return new TestData(first, text);
+ }
+
+ private final long mId;
+ private final String mText;
+
+ private TestData(long id, String second) {
+ this.mId = id;
+ this.mText = second;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof TestData)) return false;
+ TestData that = (TestData) o;
+ return mId == that.mId && Objects.equals(mText, that.mText);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mId, mText);
+ }
+ }
+
+ private static class TestSortedArrayAdapter extends SortedArrayAdapter<TestData> {
+
+ private static final Comparator<TestData> TEXT_COMPARATOR = new Comparator<TestData>() {
+ @Override
+ public int compare(TestData lhs, TestData rhs) {
+ return lhs.mText.compareTo(rhs.mText);
+ }
+ };
+
+ TestSortedArrayAdapter() {
+ super(new ClassPresenterSelector(), TEXT_COMPARATOR);
+ }
+
+ @Override
+ long getId(TestData item) {
+ return item.mId;
+ }
+
+ }
+}
diff --git a/tests/unit/src/com/android/tv/recommendation/RoutineWatchEvaluatorTest.java b/tests/unit/src/com/android/tv/recommendation/RoutineWatchEvaluatorTest.java
index 2511094e..8427b19f 100644
--- a/tests/unit/src/com/android/tv/recommendation/RoutineWatchEvaluatorTest.java
+++ b/tests/unit/src/com/android/tv/recommendation/RoutineWatchEvaluatorTest.java
@@ -22,12 +22,39 @@ import android.test.suitebuilder.annotation.SmallTest;
import com.android.tv.data.Program;
import com.android.tv.recommendation.RoutineWatchEvaluator.ProgramTime;
+import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
+import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
@SmallTest
public class RoutineWatchEvaluatorTest extends EvaluatorTestCase<RoutineWatchEvaluator> {
+ private static class ScoredItem implements Comparable<ScoredItem> {
+ private final String mBase;
+ private final String mText;
+ private final double mScore;
+
+ private ScoredItem(String base, String text) {
+ this.mBase = base;
+ this.mText = text;
+ this.mScore = RoutineWatchEvaluator.calculateTitleMatchScore(base, text);
+ }
+
+ @Override
+ public int compareTo(ScoredItem scoredItem) {
+ return Double.compare(mScore, scoredItem.mScore);
+ }
+
+ @Override
+ public String toString() {
+ return mBase + " scored with " + mText + " is " + mScore;
+ }
+ }
+
+ private static ScoredItem score(String t1, String t2) {
+ return new ScoredItem(t1, t2);
+ }
@Override
public RoutineWatchEvaluator createEvaluator() {
@@ -55,6 +82,34 @@ public class RoutineWatchEvaluatorTest extends EvaluatorTestCase<RoutineWatchEva
assertMaximumMatchedWordSequenceLength(0, "Dexter", "Friends");
}
+ public void testCalculateTitleMatchScore_empty() {
+ assertEquals(0.0, RoutineWatchEvaluator.calculateTitleMatchScore("", ""));
+ assertEquals(0.0, RoutineWatchEvaluator.calculateTitleMatchScore("foo", ""));
+ assertEquals(0.0, RoutineWatchEvaluator.calculateTitleMatchScore("", "foo"));
+ }
+
+ public void testCalculateTitleMatchScore_spaces() {
+ assertEquals(0.0, RoutineWatchEvaluator.calculateTitleMatchScore(" ", " "));
+ assertEquals(0.0, RoutineWatchEvaluator.calculateTitleMatchScore("foo", " "));
+ assertEquals(0.0, RoutineWatchEvaluator.calculateTitleMatchScore(" ", "foo"));
+ }
+
+
+ public void testCalculateTitleMatchScore_null() {
+ assertEquals(0.0, RoutineWatchEvaluator.calculateTitleMatchScore(null, null));
+ assertEquals(0.0, RoutineWatchEvaluator.calculateTitleMatchScore("foo", null));
+ assertEquals(0.0, RoutineWatchEvaluator.calculateTitleMatchScore(null, "foo"));
+ }
+
+ public void testCalculateTitleMatchScore_longerMatchIsBetter() {
+ String base = "foo bar baz";
+ assertInOrder(
+ score(base, ""),
+ score(base, "bar"),
+ score(base, "foo bar"),
+ score(base, "foo bar baz"));
+ }
+
public void testProgramTime_createFromProgram() {
Calendar time = Calendar.getInstance();
int todayDayOfWeek = time.get(Calendar.DAY_OF_WEEK);
@@ -110,18 +165,18 @@ public class RoutineWatchEvaluatorTest extends EvaluatorTestCase<RoutineWatchEva
// Check intersection time and commutative law in all cases.
int oneHourInSec = hourMinuteToSec(1, 0);
- assertOverlappedIntervalScore(2 * oneHourInSec, true,
- programTimeToday2100_2400, programTimeToday2200_0100);
- assertOverlappedIntervalScore(0, false,
- programTimeToday2100_2400, programTimeTomorrow0000_0300);
- assertOverlappedIntervalScore(2 * oneHourInSec, false,
- programTimeToday2100_2400, programTimeTomorrow2000_2300);
- assertOverlappedIntervalScore(oneHourInSec, true,
- programTimeToday2200_0100, programTimeTomorrow0000_0300);
- assertOverlappedIntervalScore(oneHourInSec, false,
- programTimeToday2200_0100, programTimeTomorrow2000_2300);
- assertOverlappedIntervalScore(0, false,
- programTimeTomorrow0000_0300, programTimeTomorrow2000_2300);
+ assertOverlappedIntervalScore(2 * oneHourInSec, true, programTimeToday2100_2400,
+ programTimeToday2200_0100);
+ assertOverlappedIntervalScore(0, false, programTimeToday2100_2400,
+ programTimeTomorrow0000_0300);
+ assertOverlappedIntervalScore(2 * oneHourInSec, false, programTimeToday2100_2400,
+ programTimeTomorrow2000_2300);
+ assertOverlappedIntervalScore(oneHourInSec, true, programTimeToday2200_0100,
+ programTimeTomorrow0000_0300);
+ assertOverlappedIntervalScore(oneHourInSec, false, programTimeToday2200_0100,
+ programTimeTomorrow2000_2300);
+ assertOverlappedIntervalScore(0, false, programTimeTomorrow0000_0300,
+ programTimeTomorrow2000_2300);
}
public void testGetTimeOfDayInSec() {
@@ -139,8 +194,8 @@ public class RoutineWatchEvaluatorTest extends EvaluatorTestCase<RoutineWatchEva
MoreAsserts.assertContentsInOrder(wordList, words);
}
- private void assertMaximumMatchedWordSequenceLength(int expectedLength,
- String text1, String text2) {
+ private void assertMaximumMatchedWordSequenceLength(int expectedLength, String text1,
+ String text2) {
List<String> wordList1 = RoutineWatchEvaluator.splitTextToWords(text1);
List<String> wordList2 = RoutineWatchEvaluator.splitTextToWords(text2);
assertEquals("MaximumMatchedWordSequenceLength", expectedLength,
@@ -152,10 +207,10 @@ public class RoutineWatchEvaluatorTest extends EvaluatorTestCase<RoutineWatchEva
private void assertProgramTime(int expectedWeekDay, int expectedStartTimeOfDayInSec,
int expectedEndTimeOfDayInSec, ProgramTime actualProgramTime) {
assertEquals("Weekday", expectedWeekDay, actualProgramTime.weekDay);
- assertEquals("StartTimeOfDayInSec",
- expectedStartTimeOfDayInSec, actualProgramTime.startTimeOfDayInSec);
- assertEquals("EndTimeOfDayInSec",
- expectedEndTimeOfDayInSec, actualProgramTime.endTimeOfDayInSec);
+ assertEquals("StartTimeOfDayInSec", expectedStartTimeOfDayInSec,
+ actualProgramTime.startTimeOfDayInSec);
+ assertEquals("EndTimeOfDayInSec", expectedEndTimeOfDayInSec,
+ actualProgramTime.endTimeOfDayInSec);
}
private void assertOverlappedIntervalScore(int expectedSeconds, boolean overlappedOnSameDay,
@@ -165,10 +220,10 @@ public class RoutineWatchEvaluatorTest extends EvaluatorTestCase<RoutineWatchEva
score *= RoutineWatchEvaluator.MULTIPLIER_FOR_UNMATCHED_DAY_OF_WEEK;
}
// Two tests for testing commutative law.
- assertEquals("OverlappedIntervalScore",
- score, mEvaluator.calculateOverlappedIntervalScore(t1, t2));
- assertEquals("OverlappedIntervalScore",
- score, mEvaluator.calculateOverlappedIntervalScore(t2, t1));
+ assertEquals("OverlappedIntervalScore", score,
+ mEvaluator.calculateOverlappedIntervalScore(t1, t2));
+ assertEquals("OverlappedIntervalScore", score,
+ mEvaluator.calculateOverlappedIntervalScore(t2, t1));
}
private int hourMinuteToSec(int hour, int minute) {
@@ -200,9 +255,12 @@ public class RoutineWatchEvaluatorTest extends EvaluatorTestCase<RoutineWatchEva
private Program createDummyProgram(Calendar startTime, long programDurationMs) {
long startTimeMs = startTime.getTimeInMillis();
- return new Program.Builder()
- .setStartTimeUtcMillis(startTimeMs)
- .setEndTimeUtcMillis(startTimeMs + programDurationMs)
- .build();
+ return new Program.Builder().setStartTimeUtcMillis(startTimeMs)
+ .setEndTimeUtcMillis(startTimeMs + programDurationMs).build();
+ }
+
+ private static <T> void assertInOrder(T... items) {
+ TreeSet<T> copy = new TreeSet<>(Arrays.asList(items));
+ MoreAsserts.assertContentsInOrder(copy, items);
}
}
diff --git a/tests/unit/src/com/android/tv/util/TestUtils.java b/tests/unit/src/com/android/tv/util/TestUtils.java
index 09d32779..f4befaa8 100644
--- a/tests/unit/src/com/android/tv/util/TestUtils.java
+++ b/tests/unit/src/com/android/tv/util/TestUtils.java
@@ -20,6 +20,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.media.tv.TvInputInfo;
import android.os.Build;
+import android.support.v4.os.BuildCompat;
import java.lang.reflect.Constructor;
@@ -34,6 +35,8 @@ public class TestUtils {
// Note that mockito doesn't support mock/spy on final object.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return createTvInputInfoForLmp(service, id, parentId, type);
+ } else if (BuildCompat.isAtLeastN()) {
+ new RuntimeException("TOOD(dvr): implement"); // http://b/26903987
}
return createTvInputInfoForMnc(service, id, parentId, type, isHardwareInput);
}
@@ -54,6 +57,14 @@ public class TestUtils {
return constructor.newInstance(service, id, parentId, type, isHardwareInput);
}
+ private static TvInputInfo createTvInputInfoForNpreview(ResolveInfo service, String id,
+ String parentId, int type) throws Exception {
+ Constructor<TvInputInfo> constructor = TvInputInfo.class.getDeclaredConstructor(
+ new Class[]{ResolveInfo.class, String.class, String.class, int.class});
+ constructor.setAccessible(true);
+ return constructor.newInstance(service, id, parentId, type);
+ }
+
public static ResolveInfo createResolveInfo(String packageName, String name) {
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.serviceInfo = new ServiceInfo();
diff --git a/tests/unit/src/com/android/tv/util/TvInputManagerHelperTest.java b/tests/unit/src/com/android/tv/util/TvInputManagerHelperTest.java
index 2f06de59..120e23a4 100644
--- a/tests/unit/src/com/android/tv/util/TvInputManagerHelperTest.java
+++ b/tests/unit/src/com/android/tv/util/TvInputManagerHelperTest.java
@@ -20,6 +20,7 @@ import android.content.pm.ResolveInfo;
import android.media.tv.TvInputInfo;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
import com.android.tv.testing.ComparatorTester;
@@ -34,6 +35,7 @@ import java.util.LinkedHashMap;
*/
@SmallTest
public class TvInputManagerHelperTest extends AndroidTestCase {
+ @Suppress // http://b/26903987
public void testComparator() throws Exception {
final LinkedHashMap<String, Boolean> INPUT_ID_TO_PARTNER_INPUT = new LinkedHashMap<>();
INPUT_ID_TO_PARTNER_INPUT.put("2_partner_input", true);
diff --git a/usbtuner/Android.mk b/usbtuner/Android.mk
index 37b65ed0..8e5669b2 100644
--- a/usbtuner/Android.mk
+++ b/usbtuner/Android.mk
@@ -19,9 +19,10 @@ LOCAL_MODULE_TAGS := optional
# It's not required but keep it for a compatibility with the previous version.
LOCAL_PRIVILEGED_MODULE := true
LOCAL_SDK_VERSION := system_current
+LOCAL_MIN_SDK_VERSION := 23 # M
LOCAL_STATIC_JAVA_LIBRARIES := \
- lib-tv-exoplayer \
+ lib-exoplayer \
usbtuner-tvinput
LOCAL_RESOURCE_DIR := \
@@ -61,7 +62,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-v7-recyclerview \
android-support-v17-leanback \
icu4j-usbtuner \
- lib-tv-exoplayer \
+ lib-exoplayer \
libprotobuf-java-nano \
tv-common
@@ -97,17 +98,21 @@ LOCAL_SRC_FILES := \
LOCAL_SDK_VERSION := system_current
include $(BUILD_STATIC_JAVA_LIBRARY)
-
#############################################################
# Pre-built dependency jars
#############################################################
+# --------------------------------------------------------------
+# ExoPlayer library version 1.5.6
+# https://github.com/google/ExoPlayer/archive/r1.5.6.zip
+# TODO: Add ExoPlayer source code to external/ android repository.
+
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \
- lib-tv-exoplayer:libs/tv-exoplayer.jar \
+ lib-exoplayer:libs/exoplayer_1.5.6.jar
include $(BUILD_MULTI_PREBUILT)
diff --git a/usbtuner/AndroidManifest.xml b/usbtuner/AndroidManifest.xml
index a763ed2d..200334e8 100644
--- a/usbtuner/AndroidManifest.xml
+++ b/usbtuner/AndroidManifest.xml
@@ -21,8 +21,8 @@
android:versionName="0.1" >
<uses-sdk
- android:minSdkVersion="21"
- android:targetSdkVersion="21" />
+ android:minSdkVersion="23"
+ android:targetSdkVersion="23" />
<uses-permission android:name="android.permission.DVB_DEVICE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
diff --git a/usbtuner/icu/icu4j/main/classes/core/src/com/ibm/icu/text/UnicodeDecompressor.java b/usbtuner/icu/icu4j/main/classes/core/src/com/ibm/icu/text/UnicodeDecompressor.java
index a9af2b32..e799ea14 100644
--- a/usbtuner/icu/icu4j/main/classes/core/src/com/ibm/icu/text/UnicodeDecompressor.java
+++ b/usbtuner/icu/icu4j/main/classes/core/src/com/ibm/icu/text/UnicodeDecompressor.java
@@ -1,6 +1,6 @@
/*
*******************************************************************************
- * Copyright (C) 1996-2009, International Business Machines Corporation and *
+ * Copyright (C) 1996-2016, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
@@ -55,7 +55,7 @@ package com.ibm.icu.text;
* // update the no. of chars written
* totalCharsWritten += charsWritten;
*
-* } while(totalBytesDecompressed < len);
+* } while(totalBytesDecompressed &lt; len);
*
* myDecompressor.reset(); // reuse decompressor
* </PRE>
diff --git a/usbtuner/libs/exoplayer_1.5.6.jar b/usbtuner/libs/exoplayer_1.5.6.jar
new file mode 100644
index 00000000..a0b311c9
--- /dev/null
+++ b/usbtuner/libs/exoplayer_1.5.6.jar
Binary files differ
diff --git a/usbtuner/libs/tv-exoplayer.jar b/usbtuner/libs/tv-exoplayer.jar
deleted file mode 100644
index a8ca7b2a..00000000
--- a/usbtuner/libs/tv-exoplayer.jar
+++ /dev/null
Binary files differ
diff --git a/usbtuner/res/xml/ut_tvinputservice.xml b/usbtuner/res/xml/ut_tvinputservice.xml
index 039d848f..54f56878 100644
--- a/usbtuner/res/xml/ut_tvinputservice.xml
+++ b/usbtuner/res/xml/ut_tvinputservice.xml
@@ -34,4 +34,6 @@
-->
<tv-input xmlns:android="http://schemas.android.com/apk/res/android"
- android:setupActivity="com.android.usbtuner.setup.TunerSetupActivity" />
+ android:setupActivity="com.android.usbtuner.setup.TunerSetupActivity"
+ android:canRecord="true"
+ android:tunerCount="1" />
diff --git a/usbtuner/src/com/android/usbtuner/DvbDeviceAccessor.java b/usbtuner/src/com/android/usbtuner/DvbDeviceAccessor.java
index 61185f59..1e8f1698 100644
--- a/usbtuner/src/com/android/usbtuner/DvbDeviceAccessor.java
+++ b/usbtuner/src/com/android/usbtuner/DvbDeviceAccessor.java
@@ -16,15 +16,27 @@
package com.android.usbtuner;
+import android.annotation.TargetApi;
+import android.content.ComponentName;
import android.content.Context;
+import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager;
+import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.os.BuildCompat;
import android.util.Log;
+import com.android.tv.common.SoftPreconditions;
+import com.android.tv.common.feature.CommonFeatures;
import com.android.tv.common.recording.RecordingCapability;
+import com.android.usbtuner.tvinput.UsbTunerTvInputService;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationTargetException;
@@ -128,6 +140,27 @@ public class DvbDeviceAccessor {
.build();
}
+ @Nullable
+ @TargetApi(Build.VERSION_CODES.N)
+ public TvInputInfo buildTvInputInfo(Context context) {
+ List<DvbDeviceInfoWrapper> deviceList = getDvbDeviceList();
+ TvInputInfo.Builder builder = new TvInputInfo.Builder(context, new ComponentName(context,
+ UsbTunerTvInputService.class));
+ try {
+ if (deviceList.size() > 0) {
+ return builder.setCanRecord(
+ CommonFeatures.DVR.isEnabled(context) && BuildCompat.isAtLeastN())
+ .setTunerCount(deviceList.size())
+ .build();
+ } else {
+ return null;
+ }
+ } catch (IOException | XmlPullParserException e) {
+ SoftPreconditions.warn(TAG, null, "Failed to update TvInputInfo", e);
+ return null;
+ }
+ }
+
public static class DvbDeviceInfoWrapper implements Comparable<DvbDeviceInfoWrapper> {
private static Method sGetAdapterIdMethod;
private static Method sGetDeviceIdMethod;
diff --git a/usbtuner/src/com/android/usbtuner/FileDataSource.java b/usbtuner/src/com/android/usbtuner/FileDataSource.java
index 163d1048..af831e2f 100644
--- a/usbtuner/src/com/android/usbtuner/FileDataSource.java
+++ b/usbtuner/src/com/android/usbtuner/FileDataSource.java
@@ -48,6 +48,7 @@ public class FileDataSource extends MediaDataSource implements InputStreamSource
private static final int CIRCULAR_BUFFER_SIZE = MIN_READ_UNIT * 4000; // ~ 8MB
private static final int PADDING_SIZE = MIN_READ_UNIT * 1000; // ~2MB
private static final int READ_TIMEOUT_MS = 10000; // 10 secs.
+ private static final int BUFFER_UNDERRUN_SLEEP_MS = 10;
private static final String FILE_DIR =
new File(Environment.getExternalStorageDirectory(), "Streams").getAbsolutePath();
@@ -137,6 +138,7 @@ public class FileDataSource extends MediaDataSource implements InputStreamSource
mStreamingThread.join();
}
} catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
}
}
@@ -262,6 +264,7 @@ public class FileDataSource extends MediaDataSource implements InputStreamSource
mCircularBufferMonitor.wait(READ_TIMEOUT_MS);
} catch (InterruptedException e) {
// Wait again.
+ Thread.currentThread().interrupt();
}
if (initialBytesFetched == mBytesFetched) {
Log.w(TAG, "No data update for " + READ_TIMEOUT_MS + "ms. returning -1.");
@@ -350,6 +353,7 @@ public class FileDataSource extends MediaDataSource implements InputStreamSource
mCircularBufferMonitor.wait();
} catch (InterruptedException e) {
// Wait again.
+ Thread.currentThread().interrupt();
}
}
if (!mStreaming) {
@@ -359,6 +363,13 @@ public class FileDataSource extends MediaDataSource implements InputStreamSource
int bytesWritten = mSource.read(dataBuffer);
if (bytesWritten <= 0) {
+ try {
+ // When buffer is underrun, we sleep for short time to prevent
+ // unnecessary CPU draining.
+ sleep(BUFFER_UNDERRUN_SLEEP_MS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
continue;
}
diff --git a/usbtuner/src/com/android/usbtuner/TunerHal.java b/usbtuner/src/com/android/usbtuner/TunerHal.java
index 102b60fd..fc04b2da 100644
--- a/usbtuner/src/com/android/usbtuner/TunerHal.java
+++ b/usbtuner/src/com/android/usbtuner/TunerHal.java
@@ -61,13 +61,19 @@ public abstract class TunerHal implements AutoCloseable {
System.loadLibrary("tunertvinput_jni");
}
- public static TunerHal getInstance(Context context) {
+ /**
+ * Creates a TunerHal instance.
+ * @param context context for creating the TunerHal instance
+ * @return the TunerHal instance
+ */
+ public static TunerHal createInstance(Context context) {
TunerHal tunerHal;
if (TisConfiguration.isPackagedWithLiveChannels(context)) {
tunerHal = new UsbTunerHal(context);
} else {
tunerHal = new InternalTunerHal(context);
- } if (tunerHal.openFirstAvailable()) {
+ }
+ if (tunerHal.openFirstAvailable()) {
return tunerHal;
}
return null;
diff --git a/usbtuner/src/com/android/usbtuner/UsbTunerDataSource.java b/usbtuner/src/com/android/usbtuner/UsbTunerDataSource.java
index 34558b99..0d139311 100644
--- a/usbtuner/src/com/android/usbtuner/UsbTunerDataSource.java
+++ b/usbtuner/src/com/android/usbtuner/UsbTunerDataSource.java
@@ -19,7 +19,6 @@ package com.android.usbtuner;
import android.media.MediaDataSource;
import android.util.Log;
-import com.google.android.exoplayer.upstream.DataSource;
import com.android.usbtuner.ChannelScanFileParser.ScanChannel;
import com.android.usbtuner.data.Channel;
import com.android.usbtuner.data.TunerChannel;
@@ -32,8 +31,8 @@ import java.util.Locale;
import java.util.concurrent.atomic.AtomicLong;
/**
- * A {@link DataSource} implementation which provides the mpeg2ts stream from the tuner device to
- * demux.
+ * A {@link MediaDataSource} implementation which provides the mpeg2ts stream from the tuner device
+ * to {@link MediaExtractor}.
*/
public class UsbTunerDataSource extends MediaDataSource implements InputStreamSource {
private static final String TAG = "UsbTunerDataSource";
@@ -43,6 +42,7 @@ public class UsbTunerDataSource extends MediaDataSource implements InputStreamSo
private static final int CIRCULAR_BUFFER_SIZE = MIN_READ_UNIT * 20000; // ~ 30MB
private static final int READ_TIMEOUT_MS = 5000; // 5 secs.
+ private static final int BUFFER_UNDERRUN_SLEEP_MS = 10;
private static final int CACHE_KEY_VERSION = 1;
@@ -138,6 +138,9 @@ public class UsbTunerDataSource extends MediaDataSource implements InputStreamSo
mStreamingThread.join();
}
} catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ } finally {
+ mTunerHal.stopTune();
}
}
@@ -154,7 +157,6 @@ public class UsbTunerDataSource extends MediaDataSource implements InputStreamSo
}
private class StreamingThread extends Thread {
-
@Override
public void run() {
// Buffers for streaming data from the tuner and the internal buffer.
@@ -169,6 +171,13 @@ public class UsbTunerDataSource extends MediaDataSource implements InputStreamSo
int bytesWritten = mTunerHal.readTsStream(dataBuffer, dataBuffer.length);
if (bytesWritten <= 0) {
+ try {
+ // When buffer is underrun, we sleep for short time to prevent
+ // unnecessary CPU draining.
+ sleep(BUFFER_UNDERRUN_SLEEP_MS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
continue;
}
@@ -214,6 +223,7 @@ public class UsbTunerDataSource extends MediaDataSource implements InputStreamSo
mCircularBufferMonitor.wait(READ_TIMEOUT_MS);
} catch (InterruptedException e) {
// Wait again.
+ Thread.currentThread().interrupt();
}
if (initialBytesFetched == mBytesFetched) {
Log.w(TAG, "No data update for " + READ_TIMEOUT_MS + "ms. returning -1.");
@@ -260,7 +270,8 @@ public class UsbTunerDataSource extends MediaDataSource implements InputStreamSo
@Override
public void close() {
- mTunerHal.stopTune();
+ // Called from system MediaExtractor. All the resource should be closed
+ // in stopStream() already.
}
@Override
diff --git a/usbtuner/src/com/android/usbtuner/UsbTunerPreferences.java b/usbtuner/src/com/android/usbtuner/UsbTunerPreferences.java
index 2f4a4764..0394648d 100644
--- a/usbtuner/src/com/android/usbtuner/UsbTunerPreferences.java
+++ b/usbtuner/src/com/android/usbtuner/UsbTunerPreferences.java
@@ -21,14 +21,20 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import com.android.tv.common.SoftPreconditions;
import com.android.usbtuner.UsbTunerPreferenceProvider.Preferences;
import com.android.usbtuner.util.TisConfiguration;
/**
* A helper class for the USB tuner preferences.
*/
+// TODO: Change this class to run on the worker thread.
public class UsbTunerPreferences {
+ private static final String TAG = "UsbTunerPreferences";
+
private static final String PREFS_KEY_CHANNEL_DATA_VERSION = "channel_data_version";
private static final String PREFS_KEY_SCANNED_CHANNEL_COUNT = "scanned_channel_count";
private static final String PREFS_KEY_SCAN_DONE = "scan_done";
@@ -36,6 +42,8 @@ public class UsbTunerPreferences {
private static final String SHARED_PREFS_NAME = "com.android.usbtuner.preferences";
+ private static final Bundle PREFERENCE_VALUES = new Bundle();
+
private static boolean useContentProvider(Context context) {
// If TIS is a part of LC, it should use ContentProvider to resolve multiple process access.
return TisConfiguration.isPackagedWithLiveChannels(context);
@@ -132,11 +140,16 @@ public class UsbTunerPreferences {
if (cursor != null && cursor.moveToFirst()) {
return cursor.getString(0);
}
+ } catch (Exception e) {
+ SoftPreconditions.warn(TAG, "getPreference", "Error querying preference values", e);
}
return null;
}
private static int getPreferenceInt(Context context, String key) {
+ if (PREFERENCE_VALUES.containsKey(key)) {
+ return PREFERENCE_VALUES.getInt(key);
+ }
try {
return Integer.parseInt(getPreference(context, key));
} catch (NumberFormatException e) {
@@ -145,22 +158,38 @@ public class UsbTunerPreferences {
}
private static boolean getPreferenceBoolean(Context context, String key) {
+ if (PREFERENCE_VALUES.containsKey(key)) {
+ return PREFERENCE_VALUES.getBoolean(key);
+ }
return Boolean.valueOf(getPreference(context, key));
}
- private static void setPreference(Context context, String key, String value) {
- ContentResolver resolver = context.getContentResolver();
- ContentValues values = new ContentValues();
- values.put(Preferences.COLUMN_KEY, key);
- values.put(Preferences.COLUMN_VALUE, value);
- resolver.insert(Preferences.CONTENT_URI, values);
+ private static void setPreference(final Context context, final String key, final String value) {
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ ContentResolver resolver = context.getContentResolver();
+ ContentValues values = new ContentValues();
+ values.put(Preferences.COLUMN_KEY, key);
+ values.put(Preferences.COLUMN_VALUE, value);
+ try {
+ resolver.insert(Preferences.CONTENT_URI, values);
+ } catch (Exception e) {
+ SoftPreconditions.warn(TAG, "setPreference", "Error writing preference values",
+ e);
+ }
+ return null;
+ }
+ }.execute();
}
private static void setPreference(Context context, String key, int value) {
+ PREFERENCE_VALUES.putInt(key, value);
setPreference(context, key, Integer.toString(value));
}
private static void setPreference(Context context, String key, boolean value) {
+ PREFERENCE_VALUES.putBoolean(key, value);
setPreference(context, key, Boolean.toString(value));
}
}
diff --git a/usbtuner/src/com/android/usbtuner/UsbTunerTsScannerSource.java b/usbtuner/src/com/android/usbtuner/UsbTunerTsScannerSource.java
index afb1ee31..fd7f8838 100644
--- a/usbtuner/src/com/android/usbtuner/UsbTunerTsScannerSource.java
+++ b/usbtuner/src/com/android/usbtuner/UsbTunerTsScannerSource.java
@@ -49,7 +49,7 @@ public class UsbTunerTsScannerSource implements InputStreamSource {
private final AtomicLong mBytesFetched = new AtomicLong();
public UsbTunerTsScannerSource(Context context, EventListener eventListener) {
- mTunerHal = TunerHal.getInstance(context);
+ mTunerHal = TunerHal.createInstance(context);
if (mTunerHal == null) {
throw new RuntimeException("Failed to open a DVB device");
}
diff --git a/usbtuner/src/com/android/usbtuner/cc/CaptionWindowLayout.java b/usbtuner/src/com/android/usbtuner/cc/CaptionWindowLayout.java
index 7446b014..26b92493 100644
--- a/usbtuner/src/com/android/usbtuner/cc/CaptionWindowLayout.java
+++ b/usbtuner/src/com/android/usbtuner/cc/CaptionWindowLayout.java
@@ -91,6 +91,7 @@ public class CaptionWindowLayout extends RelativeLayout implements View.OnLayout
private CaptionLayout mCaptionLayout;
private CaptionStyleCompat mCaptionStyleCompat;
+ // TODO: Replace SubtitleView to {@link com.google.android.exoplayer.text.SubtitleLayout}.
private final SubtitleView mSubtitleView;
private int mRowLimit = 0;
private final SpannableStringBuilder mBuilder = new SpannableStringBuilder();
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/CachedSampleSourceExtractor.java b/usbtuner/src/com/android/usbtuner/exoplayer/CachedSampleSourceExtractor.java
deleted file mode 100644
index 654a9f92..00000000
--- a/usbtuner/src/com/android/usbtuner/exoplayer/CachedSampleSourceExtractor.java
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.usbtuner.exoplayer;
-
-import android.media.MediaCodec;
-import android.media.MediaDataSource;
-import android.os.ConditionVariable;
-import android.os.SystemClock;
-import android.util.Log;
-
-import com.google.android.exoplayer.C;
-import com.google.android.exoplayer.SampleHolder;
-import com.google.android.exoplayer.SampleSource;
-import com.android.usbtuner.tvinput.PlaybackCacheListener;
-
-import junit.framework.Assert;
-
-import java.io.IOException;
-import java.util.Locale;
-import java.util.Random;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Extracts samples from {@link MediaDataSource} and stores them on the disk, which enables
- * trickplay.
- */
-public class CachedSampleSourceExtractor extends BaseSampleSourceExtractor implements
- CacheManager.EvictListener {
- private static final String TAG = "CachedSampleSourceExt";
- private static final boolean DEBUG = false;
-
- public static final long CHUNK_DURATION_US = TimeUnit.MILLISECONDS.toMicros(500);
-
- private static final long LIVE_THRESHOLD_US = TimeUnit.SECONDS.toMicros(1);
- private static final long CACHE_WRITE_TIMEOUT_MS = 10 * 1000; // 10 seconds
-
- private final CacheManager mCacheManager;
- private final String mId;
-
- private final PlaybackCacheListener mCacheListener;
- private long[] mCacheEndPositionUs;
- private SampleCache[] mSampleCaches;
- private CachedSampleQueue[] mPlayingSampleQueues;
- private final SamplePool mSamplePool = new SamplePool();
- private long mLastBufferedPositionUs = C.UNKNOWN_TIME_US;
- private long mCurrentPlaybackPositionUs = 0;
-
- private class CachedSampleQueue extends SampleQueue {
- private SampleCache mCache = null;
-
- public CachedSampleQueue(SamplePool samplePool) {
- super(samplePool);
- }
-
- public void setSource(SampleCache newCache) {
- for (SampleCache cache = mCache; cache != null; cache = cache.getNext()) {
- cache.clear();
- cache.close();
- }
- mCache = newCache;
- for (SampleCache cache = mCache; cache != null; cache = cache.getNext()) {
- cache.resetRead();
- }
- }
-
- public boolean maybeReadSample() {
- if (isDurationGreaterThan(CHUNK_DURATION_US)) {
- return false;
- }
- SampleHolder sample = mCache.maybeReadSample();
- if (sample == null) {
- if (!mCache.canReadMore() && mCache.getNext() != null) {
- mCache.clear();
- mCache.close();
- mCache = mCache.getNext();
- mCache.resetRead();
- return maybeReadSample();
- } else {
- return false;
- }
- } else {
- queueSample(sample);
- return true;
- }
- }
-
- public int dequeueSample(SampleHolder sample) {
- maybeReadSample();
- return super.dequeueSample(sample);
- }
-
- @Override
- public void clear() {
- super.clear();
- for (SampleCache cache = mCache; cache != null; cache = cache.getNext()) {
- cache.clear();
- cache.close();
- }
- mCache = null;
- }
-
- public long getSourceStartPositionUs() {
- return mCache == null ? -1 : mCache.getStartPositionUs();
- }
- }
-
- public CachedSampleSourceExtractor(MediaDataSource source, CacheManager cacheManager,
- PlaybackCacheListener cacheListener) {
- super(source);
- mCacheManager = cacheManager;
- mCacheListener = cacheListener;
- mId = Long.toHexString(new Random().nextLong());
- cacheListener.onCacheStateChanged(true); // Enable trickplay
- }
-
- @Override
- public void queueSample(int index, SampleHolder sample, ConditionVariable conditionVariable)
- throws IOException {
- long writeStartTimeNs = SystemClock.elapsedRealtimeNanos();
- synchronized (this) {
- SampleCache cache = mSampleCaches[index];
- if ((sample.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) {
- if (sample.timeUs >= mCacheEndPositionUs[index]) {
- try {
- SampleCache nextCache = mCacheManager.createNewWriteFile(
- getTrackId(index), mCacheEndPositionUs[index], mSamplePool);
- cache.finishWrite(nextCache);
- mSampleCaches[index] = cache = nextCache;
- mCacheEndPositionUs[index] =
- ((sample.timeUs / CHUNK_DURATION_US) + 1) * CHUNK_DURATION_US;
- } catch (IOException e) {
- cache.finishWrite(null);
- throw e;
- }
- }
- }
- cache.writeSample(sample, conditionVariable);
- }
- if (!conditionVariable.block(CACHE_WRITE_TIMEOUT_MS)) {
- Log.e(TAG, "Error: Serious delay on writing cache");
- conditionVariable.block();
- }
-
- // Check if the storage has enough bandwidth for trickplay. Otherwise we disable it
- // and notify the slowness through the playback cache listener.
- mCacheManager.addWriteStat(sample.size,
- SystemClock.elapsedRealtimeNanos() - writeStartTimeNs);
- if (mCacheManager.isWriteSlow()) {
- Log.w(TAG, "Disk is too slow for trickplay. Disable trickplay.");
- mCacheManager.disable();
- mCacheListener.onDiskTooSlow();
- }
- }
-
- private String getTrackId(int index) {
- return String.format(Locale.ENGLISH, "%s_%x", mId, index);
- }
-
- @Override
- public void initOnPrepareLocked(int trackCount) throws IOException {
- mSampleCaches = new SampleCache[trackCount];
- mPlayingSampleQueues = new CachedSampleQueue[trackCount];
- mCacheEndPositionUs = new long[trackCount];
- for (int i = 0; i < trackCount; i++) {
- mSampleCaches[i] = mCacheManager.createNewWriteFile(getTrackId(i), 0, mSamplePool);
- mPlayingSampleQueues[i] = null;
- mCacheEndPositionUs[i] = CHUNK_DURATION_US;
- }
- }
-
- @Override
- public void selectTrack(int index) {
- synchronized (this) {
- if (mPlayingSampleQueues[index] == null) {
- String trackId = getTrackId(index);
- mPlayingSampleQueues[index] = new CachedSampleQueue(mSamplePool);
- mCacheManager.registerEvictListener(trackId, this);
- seekIndividualTrackLocked(index, mCurrentPlaybackPositionUs,
- isLiveLocked(mCurrentPlaybackPositionUs));
- mPlayingSampleQueues[index].maybeReadSample();
- }
- }
- }
-
- @Override
- public void deselectTrack(int index) {
- synchronized (this) {
- if (mPlayingSampleQueues[index] != null) {
- mPlayingSampleQueues[index].clear();
- mPlayingSampleQueues[index] = null;
- mCacheManager.unregisterEvictListener(getTrackId(index));
- }
- }
- }
-
- @Override
- public long getBufferedPositionUs() {
- synchronized (this) {
- Long result = null;
- for (CachedSampleQueue queue : mPlayingSampleQueues) {
- if (queue == null) {
- continue;
- }
- Long bufferedPositionUs = queue.getEndPositionUs();
- if (bufferedPositionUs == null) {
- continue;
- }
- if (result == null || result > bufferedPositionUs) {
- result = bufferedPositionUs;
- }
- }
- if (result == null) {
- return mLastBufferedPositionUs;
- } else {
- return (mLastBufferedPositionUs = result);
- }
- }
- }
-
- @Override
- public void seekTo(long positionUs) {
- synchronized (this) {
- boolean isLive = isLiveLocked(positionUs);
-
- // Seek video track first
- for (int i = 0; i < mPlayingSampleQueues.length; ++i) {
- CachedSampleQueue queue = mPlayingSampleQueues[i];
- if (queue == null) {
- continue;
- }
- seekIndividualTrackLocked(i, positionUs, isLive);
- if (DEBUG) {
- Log.d(TAG, "start time = " + queue.getSourceStartPositionUs());
- }
- }
- mLastBufferedPositionUs = positionUs;
- }
- }
-
- private boolean isLiveLocked(long positionUs) {
- Long livePositionUs = null;
- for (SampleCache cache : mSampleCaches) {
- if (livePositionUs == null || livePositionUs < cache.getEndPositionUs()) {
- livePositionUs = cache.getEndPositionUs();
- }
- }
- return (livePositionUs == null
- || Math.abs(livePositionUs - positionUs) < LIVE_THRESHOLD_US);
- }
-
- private void seekIndividualTrackLocked(int index, long positionUs, boolean isLive) {
- CachedSampleQueue queue = mPlayingSampleQueues[index];
- if (queue == null) {
- return;
- }
- queue.clear();
- if (isLive) {
- queue.setSource(mSampleCaches[index]);
- } else {
- queue.setSource(mCacheManager.getReadFile(getTrackId(index), positionUs));
- }
- queue.maybeReadSample();
- }
-
- @Override
- public int readSample(int track, SampleHolder sampleHolder) {
- synchronized (this) {
- CachedSampleQueue queue = mPlayingSampleQueues[track];
- Assert.assertNotNull(queue);
- queue.maybeReadSample();
- int result = queue.dequeueSample(sampleHolder);
- if (result != SampleSource.SAMPLE_READ && getEos()) {
- return SampleSource.END_OF_STREAM;
- }
- return result;
- }
- }
-
- @Override
- public void cleanUpImpl() {
- if (mSampleCaches == null) {
- return;
- }
- for (int i = 0; i < mSampleCaches.length; ++i) {
- mSampleCaches[i].finishWrite(null);
- mCacheManager.unregisterEvictListener(getTrackId(i));
- }
- for (int i = 0; i < mSampleCaches.length; ++i) {
- mCacheManager.clearTrack(getTrackId(i));
- }
- }
-
- @Override
- public boolean continueBuffering(long positionUs) {
- synchronized (this) {
- boolean hasSamples = true;
- mCurrentPlaybackPositionUs = positionUs;
- for (CachedSampleQueue queue : mPlayingSampleQueues) {
- if (queue == null) {
- continue;
- }
- queue.maybeReadSample();
- if (queue.isEmpty()) {
- hasSamples = false;
- }
- }
- return hasSamples;
- }
- }
-
- // CacheEvictListener
- @Override
- public void onCacheEvicted(String id, long createdTimeMs) {
- mCacheListener.onCacheStartTimeChanged(
- createdTimeMs + TimeUnit.MICROSECONDS.toMillis(CHUNK_DURATION_US));
- }
-}
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/Cea708TextTrackRenderer.java b/usbtuner/src/com/android/usbtuner/exoplayer/Cea708TextTrackRenderer.java
index 3fdfb34a..a391db50 100644
--- a/usbtuner/src/com/android/usbtuner/exoplayer/Cea708TextTrackRenderer.java
+++ b/usbtuner/src/com/android/usbtuner/exoplayer/Cea708TextTrackRenderer.java
@@ -19,10 +19,13 @@ package com.android.usbtuner.exoplayer;
import android.util.Log;
import com.google.android.exoplayer.ExoPlaybackException;
+import com.google.android.exoplayer.MediaClock;
+import com.google.android.exoplayer.MediaFormat;
+import com.google.android.exoplayer.MediaFormatHolder;
import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource;
-import com.google.android.exoplayer.TrackInfo;
import com.google.android.exoplayer.TrackRenderer;
+import com.google.android.exoplayer.util.Assertions;
import com.android.usbtuner.cc.Cea708Parser;
import com.android.usbtuner.cc.Cea708Parser.OnCea708ParserListener;
import com.android.usbtuner.data.Cea708Data.CaptionEvent;
@@ -41,10 +44,10 @@ public class Cea708TextTrackRenderer extends TrackRenderer implements OnCea708Pa
// According to CEA-708B, the maximum value of closed caption bandwidth is 9600bps.
private static final int DEFAULT_INPUT_BUFFER_SIZE = 9600 / 8;
- private SampleSource mSource;
+ private SampleSource.SampleSourceReader mSource;
private SampleHolder mSampleHolder;
+ private MediaFormatHolder mFormatHolder;
private int mServiceNumber;
- private boolean mSourceStateReady;
private boolean mInputStreamEnded;
private long mCurrentPositionUs;
private long mPresentationTimeUs;
@@ -58,14 +61,16 @@ public class Cea708TextTrackRenderer extends TrackRenderer implements OnCea708Pa
}
public Cea708TextTrackRenderer(SampleSource source) {
- mSource = source;
+ mSource = source.register();
+ mTrackIndex = -1;
mSampleHolder = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_DIRECT);
- mSampleHolder.replaceBuffer(DEFAULT_INPUT_BUFFER_SIZE);
+ mSampleHolder.ensureSpaceForWrite(DEFAULT_INPUT_BUFFER_SIZE);
+ mFormatHolder = new MediaFormatHolder();
}
@Override
- protected boolean isTimeSource() {
- return false;
+ protected MediaClock getMediaClock() {
+ return null;
}
private boolean handlesMimeType(String mimeType) {
@@ -73,31 +78,28 @@ public class Cea708TextTrackRenderer extends TrackRenderer implements OnCea708Pa
}
@Override
- protected int doPrepare(long positionUs) throws ExoPlaybackException {
- try {
- boolean sourcePrepared = mSource.prepare(positionUs);
- if (!sourcePrepared) {
- return TrackRenderer.STATE_UNPREPARED;
- }
- } catch (IOException e) {
- throw new ExoPlaybackException(e);
+ protected boolean doPrepare(long positionUs) throws ExoPlaybackException {
+ boolean sourcePrepared = mSource.prepare(positionUs);
+ if (!sourcePrepared) {
+ return false;
}
int trackCount = mSource.getTrackCount();
for (int i = 0; i < trackCount; ++i) {
- TrackInfo trackInfo = mSource.getTrackInfo(i);
- if (handlesMimeType(trackInfo.mimeType)) {
+ MediaFormat trackFormat = mSource.getFormat(i);
+ if (handlesMimeType(trackFormat.mimeType)) {
mTrackIndex = i;
clearDecodeState();
- return TrackRenderer.STATE_PREPARED;
+ return true;
}
}
- return TrackRenderer.STATE_IGNORE;
+ // TODO: Check this case. (Source do not have the proper mime type.)
+ return true;
}
@Override
- protected void onEnabled(long positionUs, boolean joining) {
+ protected void onEnabled(int track, long positionUs, boolean joining) {
+ Assertions.checkArgument(mTrackIndex != -1 && track == 0);
mSource.enable(mTrackIndex, positionUs);
- mSourceStateReady = false;
mInputStreamEnded = false;
mPresentationTimeUs = positionUs;
mCurrentPositionUs = Long.MIN_VALUE;
@@ -121,19 +123,34 @@ public class Cea708TextTrackRenderer extends TrackRenderer implements OnCea708Pa
@Override
protected boolean isReady() {
- return mSourceStateReady;
+ // Since this track will be fed by {@link VideoTrackRenderer},
+ // it is not required to control transition between ready state and buffering state.
+ return true;
+ }
+
+ @Override
+ protected int getTrackCount() {
+ return mTrackIndex < 0 ? 0 : 1;
+ }
+
+ @Override
+ protected MediaFormat getFormat(int track) {
+ Assertions.checkArgument(mTrackIndex != -1 && track == 0);
+ return mSource.getFormat(mTrackIndex);
+ }
+
+ @Override
+ protected void maybeThrowError() throws ExoPlaybackException {
+ try {
+ mSource.maybeThrowError();
+ } catch (IOException e) {
+ throw new ExoPlaybackException(e);
+ }
}
@Override
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
try {
- boolean continueBuffering = mSource.continueBuffering(positionUs);
- if (mSourceStateReady != continueBuffering) {
- mSourceStateReady = continueBuffering;
- if (DEBUG) {
- Log.d(TAG, "mSourceStateReady: " + mSourceStateReady);
- }
- }
mPresentationTimeUs = positionUs;
if (!mInputStreamEnded) {
processOutput();
@@ -155,22 +172,25 @@ public class Cea708TextTrackRenderer extends TrackRenderer implements OnCea708Pa
if (mInputStreamEnded) {
return false;
}
+ long discontinuity = mSource.readDiscontinuity(mTrackIndex);
+ if (discontinuity != SampleSource.NO_DISCONTINUITY) {
+ if (DEBUG) {
+ Log.d(TAG, "Read discontinuity happened");
+ }
+
+ // TODO: handle input discontinuity for trickplay.
+ clearDecodeState();
+ mPresentationTimeUs = discontinuity;
+ return false;
+ }
mSampleHolder.data.clear();
mSampleHolder.size = 0;
- int result = mSource.readData(mTrackIndex, mPresentationTimeUs, null, mSampleHolder, false);
+ int result = mSource.readData(mTrackIndex, mPresentationTimeUs,
+ mFormatHolder, mSampleHolder);
switch (result) {
case SampleSource.NOTHING_READ: {
return false;
}
- case SampleSource.DISCONTINUITY_READ: {
- if (DEBUG) {
- Log.d(TAG, "Read discontinuity happened");
- }
-
- // TODO: handle input discontinuity for trickplay.
- clearDecodeState();
- return true;
- }
case SampleSource.FORMAT_READ: {
if (DEBUG) {
Log.i(TAG, "Format was read again");
@@ -203,26 +223,17 @@ public class Cea708TextTrackRenderer extends TrackRenderer implements OnCea708Pa
@Override
protected long getDurationUs() {
- return mSource.getTrackInfo(mTrackIndex).durationUs;
- }
-
- @Override
- protected long getCurrentPositionUs() {
- mCurrentPositionUs = Math.max(mCurrentPositionUs, mPresentationTimeUs);
- return mCurrentPositionUs;
+ return mSource.getFormat(mTrackIndex).durationUs;
}
@Override
protected long getBufferedPositionUs() {
- long positionUs = mSource.getBufferedPositionUs();
- return positionUs == UNKNOWN_TIME_US || positionUs == END_OF_TRACK_US
- ? positionUs : Math.max(positionUs, getCurrentPositionUs());
+ return mSource.getBufferedPositionUs();
}
@Override
protected void seekTo(long currentPositionUs) throws ExoPlaybackException {
mSource.seekToUs(currentPositionUs);
- mSourceStateReady = false;
mInputStreamEnded = false;
mPresentationTimeUs = currentPositionUs;
mCurrentPositionUs = Long.MIN_VALUE;
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/DefaultSampleSource.java b/usbtuner/src/com/android/usbtuner/exoplayer/DefaultSampleSource.java
deleted file mode 100644
index 6800d644..00000000
--- a/usbtuner/src/com/android/usbtuner/exoplayer/DefaultSampleSource.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.usbtuner.exoplayer;
-
-import com.google.android.exoplayer.C;
-import com.google.android.exoplayer.MediaFormatHolder;
-import com.google.android.exoplayer.SampleHolder;
-import com.google.android.exoplayer.SampleSource;
-import com.google.android.exoplayer.TrackInfo;
-import com.google.android.exoplayer.util.Assertions;
-
-import java.io.IOException;
-
-/** {@link SampleSource} that extracts sample data using a {@link SampleExtractor}. */
-public final class DefaultSampleSource implements SampleSource {
-
- private static final int TRACK_STATE_DISABLED = 0;
- private static final int TRACK_STATE_ENABLED = 1;
- private static final int TRACK_STATE_FORMAT_SENT = 2;
-
- private final SampleExtractor sampleExtractor;
-
- private TrackInfo[] trackInfos;
- private boolean prepared;
- private int remainingReleaseCount;
- private int[] trackStates;
- private boolean[] pendingDiscontinuities;
-
- private long seekPositionUs;
-
- /**
- * Creates a new sample source that extracts samples using {@code sampleExtractor}. Specifies the
- * {@code downstreamRendererCount} to ensure that the sample source is released only when all
- * downstream renderers have been released.
- *
- * @param sampleExtractor a sample extractor for accessing media samples
- * @param downstreamRendererCount the number of track renderers dependent on this sample source
- */
- public DefaultSampleSource(SampleExtractor sampleExtractor, int downstreamRendererCount) {
- this.sampleExtractor = Assertions.checkNotNull(sampleExtractor);
- this.remainingReleaseCount = downstreamRendererCount;
- }
-
- @Override
- public boolean prepare(long positionUs) throws IOException {
- if (prepared) {
- return true;
- }
-
- if (sampleExtractor.prepare()) {
- prepared = true;
- trackInfos = sampleExtractor.getTrackInfos();
- trackStates = new int[trackInfos.length];
- pendingDiscontinuities = new boolean[trackInfos.length];
- }
-
- return prepared;
- }
-
- @Override
- public int getTrackCount() {
- Assertions.checkState(prepared);
- return trackInfos.length;
- }
-
- @Override
- public TrackInfo getTrackInfo(int track) {
- Assertions.checkState(prepared);
- return trackInfos[track];
- }
-
- @Override
- public void enable(int track, long positionUs) {
- Assertions.checkState(prepared);
- Assertions.checkState(trackStates[track] == TRACK_STATE_DISABLED);
- trackStates[track] = TRACK_STATE_ENABLED;
- sampleExtractor.selectTrack(track);
- seekToUsInternal(positionUs, positionUs != 0);
- }
-
- @Override
- public void disable(int track) {
- Assertions.checkState(prepared);
- Assertions.checkState(trackStates[track] != TRACK_STATE_DISABLED);
- sampleExtractor.deselectTrack(track);
- pendingDiscontinuities[track] = false;
- trackStates[track] = TRACK_STATE_DISABLED;
- }
-
- @Override
- public boolean continueBuffering(long positionUs) throws IOException {
- return sampleExtractor.continueBuffering(positionUs);
- }
-
- @Override
- public int readData(int track, long positionUs, MediaFormatHolder formatHolder,
- SampleHolder sampleHolder, boolean onlyReadDiscontinuity) throws IOException {
- Assertions.checkState(prepared);
- Assertions.checkState(trackStates[track] != TRACK_STATE_DISABLED);
- if (pendingDiscontinuities[track]) {
- pendingDiscontinuities[track] = false;
- return DISCONTINUITY_READ;
- }
- if (onlyReadDiscontinuity) {
- return NOTHING_READ;
- }
- if (trackStates[track] != TRACK_STATE_FORMAT_SENT) {
- sampleExtractor.getTrackMediaFormat(track, formatHolder);
- trackStates[track] = TRACK_STATE_FORMAT_SENT;
- return FORMAT_READ;
- }
-
- seekPositionUs = C.UNKNOWN_TIME_US;
- return sampleExtractor.readSample(track, sampleHolder);
- }
-
- @Override
- public void seekToUs(long positionUs) {
- Assertions.checkState(prepared);
- seekToUsInternal(positionUs, false);
- }
-
- @Override
- public long getBufferedPositionUs() {
- Assertions.checkState(prepared);
- return sampleExtractor.getBufferedPositionUs();
- }
-
- @Override
- public void release() {
- Assertions.checkState(remainingReleaseCount > 0);
- if (--remainingReleaseCount == 0) {
- sampleExtractor.release();
- }
- }
-
- private void seekToUsInternal(long positionUs, boolean force) {
- // Unless forced, avoid duplicate calls to the underlying extractor's seek method in the case
- // that there have been no interleaving calls to readSample.
- if (force || seekPositionUs != positionUs) {
- seekPositionUs = positionUs;
- sampleExtractor.seekTo(positionUs);
- for (int i = 0; i < trackStates.length; ++i) {
- if (trackStates[i] != TRACK_STATE_DISABLED) {
- pendingDiscontinuities[i] = true;
- }
- }
- }
- }
-}
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/MediaCodecVideoTrackRenderer.java b/usbtuner/src/com/android/usbtuner/exoplayer/MediaCodecVideoTrackRenderer.java
deleted file mode 100644
index 19a8ff49..00000000
--- a/usbtuner/src/com/android/usbtuner/exoplayer/MediaCodecVideoTrackRenderer.java
+++ /dev/null
@@ -1,609 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.usbtuner.exoplayer;
-
-import android.annotation.TargetApi;
-import android.media.MediaCodec;
-import android.media.MediaCrypto;
-import android.os.Handler;
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.Surface;
-
-import com.google.android.exoplayer.ExoPlaybackException;
-import com.google.android.exoplayer.MediaCodecTrackRenderer;
-import com.google.android.exoplayer.MediaFormat;
-import com.google.android.exoplayer.MediaFormatHolder;
-import com.google.android.exoplayer.SampleSource;
-import com.google.android.exoplayer.TrackRenderer;
-import com.google.android.exoplayer.drm.DrmSessionManager;
-import com.google.android.exoplayer.util.MimeTypes;
-import com.google.android.exoplayer.util.TraceUtil;
-import com.google.android.exoplayer.util.Util;
-import com.android.usbtuner.tvinput.UsbTunerDebug;
-
-import java.nio.ByteBuffer;
-
-/**
- * Decodes and renders video using {@link MediaCodec}.
- */
-@TargetApi(16)
-public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
- private static final String TAG = "MediaCodecVideoTrackRen";
- private static final boolean DEBUG = false;
-
- /**
- * Interface definition for a callback to be notified of {@link MediaCodecVideoTrackRenderer}
- * events.
- */
- public interface EventListener extends MediaCodecTrackRenderer.EventListener {
-
- /**
- * Invoked to report the number of frames dropped by the renderer. Dropped frames are
- * reported whenever the renderer is stopped having dropped frames, and optionally, whenever
- * the count reaches a specified threshold whilst the renderer is started.
- *
- * @param count the number of dropped frames
- * @param elapsed a duration in milliseconds over which the frames were dropped. This
- * duration is timed from when the renderer was started or from when dropped
- * frames were last reported (whichever was more recent), and not from when the
- * first of the reported drops occurred.
- */
- void onDroppedFrames(int count, long elapsed);
-
- /**
- * Invoked each time there's a change in the size of the video being rendered.
- *
- * @param width the video width in pixels
- * @param height the video height in pixels
- * @param pixelWidthHeightRatio the width to height ratio of each pixel. For the normal case
- * of square pixels this will be equal to 1.0. Different values are indicative of
- * anamorphic content.
- */
- void onVideoSizeChanged(int width, int height, float pixelWidthHeightRatio);
-
- /**
- * Invoked when a frame is rendered to a {@link Surface} for the first time following that
- * {@link Surface} having been set as the target for the renderer.
- *
- * @param surface a {@link Surface} to which a first frame has been rendered
- */
- void onDrawnToSurface(Surface surface);
-
- }
-
- /**
- * An interface for fine-grained adjustment of frame release times.
- */
- public interface FrameReleaseTimeHelper {
-
- /**
- * Enables the helper.
- */
- void enable();
-
- /**
- * Disables the helper.
- */
- void disable();
-
- /**
- * Called to make a fine-grained adjustment to a frame release time.
- *
- * @param framePresentationTimeUs the frame's media presentation time, in microseconds
- * @param unadjustedReleaseTimeNs the frame's unadjusted release time, in nanoseconds and in
- * the same time base as {@link System#nanoTime()}
- * @return an adjusted release time for the frame, in nanoseconds and in the same time base
- * as {@link System#nanoTime()}
- */
- long adjustReleaseTime(long framePresentationTimeUs, long unadjustedReleaseTimeNs);
-
- }
-
- // TODO: Use MediaFormat constants if these get exposed through the API.
- private static final String KEY_CROP_LEFT = "crop-left";
- private static final String KEY_CROP_RIGHT = "crop-right";
- private static final String KEY_CROP_BOTTOM = "crop-bottom";
- private static final String KEY_CROP_TOP = "crop-top";
-
- /**
- * The type of a message that can be passed to an instance of this class via
- * {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object
- * should be the target {@link Surface}, or null.
- */
- public static final int MSG_SET_SURFACE = 1;
-
- private final FrameReleaseTimeHelper frameReleaseTimeHelper;
- private final EventListener eventListener;
- private final long allowedJoiningTimeUs;
- private final int videoScalingMode;
- private final int maxDroppedFrameCountToNotify;
-
- private Surface surface;
- private boolean reportedDrawnToSurface;
- private boolean renderedFirstFrame;
- private long joiningDeadlineUs;
- private long droppedFrameAccumulationStartTimeMs;
- private int droppedFrameCount;
-
- private int currentWidth;
- private int currentHeight;
- private float currentPixelWidthHeightRatio;
- private int lastReportedWidth;
- private int lastReportedHeight;
- private float lastReportedPixelWidthHeightRatio;
-
- /**
- * @param source the upstream source from which the renderer obtains samples
- * @param videoScalingMode the scaling mode to pass to
- * {@link MediaCodec#setVideoScalingMode(int)}
- */
- public MediaCodecVideoTrackRenderer(SampleSource source, int videoScalingMode) {
- this(source, null, true, videoScalingMode);
- }
-
- /**
- * @param source the upstream source from which the renderer obtains samples
- * @param drmSessionManager for use with encrypted content. May be null if support for encrypted
- * content is not required.
- * @param playClearSamplesWithoutKeys encrypted media may contain clear (un-encrypted) regions.
- * For example a media file may start with a short clear region so as to allow
- * playback to begin in parallel with key acquisition. This parameter specifies
- * whether the renderer is permitted to play clear regions of encrypted media files
- * before {@code drmSessionManager} has obtained the keys necessary to decrypt
- * encrypted regions of the media.
- * @param videoScalingMode the scaling mode to pass to
- * {@link MediaCodec#setVideoScalingMode(int)}
- */
- public MediaCodecVideoTrackRenderer(SampleSource source, DrmSessionManager drmSessionManager,
- boolean playClearSamplesWithoutKeys, int videoScalingMode) {
- this(source, drmSessionManager, playClearSamplesWithoutKeys, videoScalingMode, 0);
- }
-
- /**
- * @param source the upstream source from which the renderer obtains samples
- * @param videoScalingMode the scaling mode to pass to
- * {@link MediaCodec#setVideoScalingMode(int)}
- * @param allowedJoiningTimeMs the maximum duration in milliseconds for which this video
- * renderer can attempt to seamlessly join an ongoing playback
- */
- public MediaCodecVideoTrackRenderer(SampleSource source, int videoScalingMode,
- long allowedJoiningTimeMs) {
- this(source, null, true, videoScalingMode, allowedJoiningTimeMs);
- }
-
- /**
- * @param source the upstream source from which the renderer obtains samples
- * @param drmSessionManager for use with encrypted content. May be null if support for encrypted
- * content is not required.
- * @param playClearSamplesWithoutKeys encrypted media may contain clear (un-encrypted) regions.
- * For example a media file may start with a short clear region so as to allow
- * playback to begin in parallel with key acquisition. This parameter specifies
- * whether the renderer is permitted to play clear regions of encrypted media files
- * before {@code drmSessionManager} has obtained the keys necessary to decrypt
- * encrypted regions of the media.
- * @param videoScalingMode the scaling mode to pass to
- * {@link MediaCodec#setVideoScalingMode(int)}
- * @param allowedJoiningTimeMs the maximum duration in milliseconds for which this video
- * renderer can attempt to seamlessly join an ongoing playback
- */
- public MediaCodecVideoTrackRenderer(SampleSource source, DrmSessionManager drmSessionManager,
- boolean playClearSamplesWithoutKeys, int videoScalingMode, long allowedJoiningTimeMs) {
- this(source, drmSessionManager, playClearSamplesWithoutKeys, videoScalingMode,
- allowedJoiningTimeMs, null, null, null, -1);
- }
-
- /**
- * @param source the upstream source from which the renderer obtains samples
- * @param videoScalingMode the scaling mode to pass to
- * {@link MediaCodec#setVideoScalingMode(int)}
- * @param allowedJoiningTimeMs the maximum duration in milliseconds for which this video
- * renderer can attempt to seamlessly join an ongoing playback
- * @param eventHandler a handler to use when delivering events to {@code eventListener}. May be
- * null if delivery of events is not required.
- * @param eventListener a listener of events. May be null if delivery of events is not required.
- * @param maxDroppedFrameCountToNotify the maximum number of frames that can be dropped between
- * invocations of {@link EventListener#onDroppedFrames(int, long)}
- */
- public MediaCodecVideoTrackRenderer(SampleSource source, int videoScalingMode,
- long allowedJoiningTimeMs, Handler eventHandler, EventListener eventListener,
- int maxDroppedFrameCountToNotify) {
- this(source, null, true, videoScalingMode, allowedJoiningTimeMs, null, eventHandler,
- eventListener, maxDroppedFrameCountToNotify);
- }
-
- /**
- * @param source the upstream source from which the renderer obtains samples
- * @param drmSessionManager for use with encrypted content. May be null if support for encrypted
- * content is not required.
- * @param playClearSamplesWithoutKeys encrypted media may contain clear (un-encrypted) regions.
- * For example a media file may start with a short clear region so as to allow
- * playback to begin in parallel with key acquisition. This parameter specifies
- * whether the renderer is permitted to play clear regions of encrypted media files
- * before {@code drmSessionManager} has obtained the keys necessary to decrypt
- * encrypted regions of the media.
- * @param videoScalingMode the scaling mode to pass to
- * {@link MediaCodec#setVideoScalingMode(int)}
- * @param allowedJoiningTimeMs the maximum duration in milliseconds for which this video
- * renderer can attempt to seamlessly join an ongoing playback
- * @param frameReleaseTimeHelper an optional helper to make fine-grained adjustments to frame
- * release times. May be null.
- * @param eventHandler a handler to use when delivering events to {@code eventListener}. May be
- * null if delivery of events is not required.
- * @param eventListener a listener of events. May be null if delivery of events is not required.
- * @param maxDroppedFrameCountToNotify the maximum number of frames that can be dropped between
- * invocations of {@link EventListener#onDroppedFrames(int, long)}.
- */
- public MediaCodecVideoTrackRenderer(SampleSource source, DrmSessionManager drmSessionManager,
- boolean playClearSamplesWithoutKeys, int videoScalingMode, long allowedJoiningTimeMs,
- FrameReleaseTimeHelper frameReleaseTimeHelper, Handler eventHandler,
- EventListener eventListener, int maxDroppedFrameCountToNotify) {
- super(source, drmSessionManager, playClearSamplesWithoutKeys, eventHandler, eventListener);
- this.videoScalingMode = videoScalingMode;
- this.allowedJoiningTimeUs = allowedJoiningTimeMs * 1000;
- this.frameReleaseTimeHelper = frameReleaseTimeHelper;
- this.eventListener = eventListener;
- this.maxDroppedFrameCountToNotify = maxDroppedFrameCountToNotify;
- joiningDeadlineUs = -1;
- currentWidth = -1;
- currentHeight = -1;
- currentPixelWidthHeightRatio = -1;
- lastReportedWidth = -1;
- lastReportedHeight = -1;
- lastReportedPixelWidthHeightRatio = -1;
- }
-
- @Override
- protected boolean handlesMimeType(String mimeType) {
- return MimeTypes.isVideo(mimeType) && super.handlesMimeType(mimeType);
- }
-
- @Override
- protected void onEnabled(long positionUs, boolean joining) {
- super.onEnabled(positionUs, joining);
- renderedFirstFrame = false;
- if (joining && allowedJoiningTimeUs > 0) {
- joiningDeadlineUs = SystemClock.elapsedRealtime() * 1000L + allowedJoiningTimeUs;
- }
- if (frameReleaseTimeHelper != null) {
- frameReleaseTimeHelper.enable();
- }
- }
-
- @Override
- protected void seekTo(long positionUs) throws ExoPlaybackException {
- super.seekTo(positionUs);
- renderedFirstFrame = false;
- joiningDeadlineUs = -1;
- }
-
- @Override
- protected boolean isReady() {
- if (super.isReady() && (renderedFirstFrame || !codecInitialized()
- || getSourceState() == SOURCE_STATE_READY_READ_MAY_FAIL)) {
- // Ready. If we were joining then we've now joined, so clear the joining deadline.
- joiningDeadlineUs = -1;
- return true;
- } else if (joiningDeadlineUs == -1) {
- // Not joining.
- return false;
- } else if (SystemClock.elapsedRealtime() * 1000 < joiningDeadlineUs) {
- // Joining and still within the joining deadline.
- return true;
- } else {
- // The joining deadline has been exceeded. Give up and clear the deadline.
- joiningDeadlineUs = -1;
- return false;
- }
- }
-
- @Override
- protected void onStarted() {
- super.onStarted();
- droppedFrameCount = 0;
- droppedFrameAccumulationStartTimeMs = SystemClock.elapsedRealtime();
- }
-
- @Override
- protected void onStopped() {
- joiningDeadlineUs = -1;
- maybeNotifyDroppedFrameCount();
- super.onStopped();
- }
-
- @Override
- public void onDisabled() {
- currentWidth = -1;
- currentHeight = -1;
- currentPixelWidthHeightRatio = -1;
- lastReportedWidth = -1;
- lastReportedHeight = -1;
- lastReportedPixelWidthHeightRatio = -1;
- if (frameReleaseTimeHelper != null) {
- frameReleaseTimeHelper.disable();
- }
- super.onDisabled();
- }
-
- @Override
- public void handleMessage(int messageType, Object message) throws ExoPlaybackException {
- if (messageType == MSG_SET_SURFACE) {
- setSurface((Surface) message);
- } else {
- super.handleMessage(messageType, message);
- }
- }
-
- /**
- * @param surface a {@link Surface} to set
- * @throws {@link ExoPlaybackException}
- */
- private void setSurface(Surface surface) throws ExoPlaybackException {
- if (this.surface == surface) {
- return;
- }
- this.surface = surface;
- this.reportedDrawnToSurface = false;
- int state = getState();
- if (state == TrackRenderer.STATE_ENABLED || state == TrackRenderer.STATE_STARTED) {
- releaseCodec();
- maybeInitCodec();
- }
- }
-
- @Override
- protected boolean shouldInitCodec() {
- return super.shouldInitCodec() && surface != null && surface.isValid();
- }
-
- // Override configureCodec to provide the {@link Surface}.
- @Override
- protected void configureCodec(MediaCodec codec, String codecName,
- android.media.MediaFormat format,
- MediaCrypto crypto) {
- Log.d(TAG, "configureCodec " + format);
- codec.configure(format, surface, crypto, 0);
- codec.setVideoScalingMode(videoScalingMode);
-
- // This method is also called from onInputFormatChanged(). For an earlier notification of
- // video stream information, invoke updateVideoSize() here.
- updateVideoSize(format);
- }
-
- @Override
- protected void onInputFormatChanged(MediaFormatHolder holder) throws ExoPlaybackException {
- super.onInputFormatChanged(holder);
-
- // TODO: Ideally this would be read in onOutputFormatChanged, but there doesn't seem
- // to be a way to pass a custom key/value pair value through to the output format.
- currentPixelWidthHeightRatio = holder.format.pixelWidthHeightRatio == MediaFormat.NO_VALUE
- ? 1 : holder.format.pixelWidthHeightRatio;
- }
-
- @Override
- protected void onOutputFormatChanged(MediaFormat inputFormat,
- android.media.MediaFormat format) {
- Log.d(TAG, "onOutputFormatChanged " + format);
- updateVideoSize(format);
- }
-
- private void updateVideoSize(android.media.MediaFormat format) {
- boolean hasCrop = format.containsKey(KEY_CROP_RIGHT) && format.containsKey(KEY_CROP_LEFT)
- && format.containsKey(KEY_CROP_BOTTOM) && format.containsKey(KEY_CROP_TOP);
- currentWidth = hasCrop
- ? format.getInteger(KEY_CROP_RIGHT) - format.getInteger(KEY_CROP_LEFT) + 1
- : format.getInteger(android.media.MediaFormat.KEY_WIDTH);
- currentHeight = hasCrop
- ? format.getInteger(KEY_CROP_BOTTOM) - format.getInteger(KEY_CROP_TOP) + 1
- : format.getInteger(android.media.MediaFormat.KEY_HEIGHT);
-
- maybeNotifyVideoSizeChanged();
- }
-
- @Override
- protected boolean canReconfigureCodec(MediaCodec codec, boolean codecIsAdaptive,
- MediaFormat oldFormat, MediaFormat newFormat) {
- return newFormat.mimeType.equals(oldFormat.mimeType)
- && (codecIsAdaptive
- || (oldFormat.width == newFormat.width
- && oldFormat.height == newFormat.height));
- }
-
- @Override
- protected boolean processOutputBuffer(long positionUs, long elapsedRealtimeUs, MediaCodec codec,
- ByteBuffer buffer, MediaCodec.BufferInfo bufferInfo, int bufferIndex,
- boolean shouldSkip) {
- if (shouldSkip) {
- skipOutputBuffer(codec, bufferIndex);
- return true;
- }
-
- if (UsbTunerDebug.ENABLED) {
- UsbTunerDebug.setAudioPositionUs(positionUs);
- UsbTunerDebug.setVideoPtsUs(bufferInfo.presentationTimeUs);
- }
-
- // Compute how many microseconds it is until the buffer's presentation time.
- long elapsedSinceStartOfLoopUs = (SystemClock.elapsedRealtime() * 1000) - elapsedRealtimeUs;
- long earlyUs = bufferInfo.presentationTimeUs - positionUs - elapsedSinceStartOfLoopUs;
-
- // Compute the buffer's desired release time in nanoseconds.
- long systemTimeNs = System.nanoTime();
- long unadjustedFrameReleaseTimeNs = systemTimeNs + (earlyUs * 1000);
-
- // Apply a timestamp adjustment, if there is one.
- long adjustedReleaseTimeNs;
- if (frameReleaseTimeHelper != null) {
- adjustedReleaseTimeNs = frameReleaseTimeHelper.adjustReleaseTime(
- bufferInfo.presentationTimeUs, unadjustedFrameReleaseTimeNs);
- earlyUs = (adjustedReleaseTimeNs - systemTimeNs) / 1000;
- } else {
- adjustedReleaseTimeNs = unadjustedFrameReleaseTimeNs;
- }
-
- if (earlyUs < -30000) {
- // We're more than 30ms late rendering the frame.
- dropOutputBuffer(codec, bufferIndex);
- if (DEBUG) {
- Log.e(TAG, "video frame drop - earlyUs: " + earlyUs);
- }
- if (UsbTunerDebug.ENABLED) {
- UsbTunerDebug.notifyVideoFrameDrop(earlyUs);
- }
- return true;
- }
-
- if (!renderedFirstFrame) {
- renderOutputBufferImmediate(codec, bufferIndex);
- renderedFirstFrame = true;
- return true;
- }
-
- if (getState() != TrackRenderer.STATE_STARTED) {
- return false;
- }
-
- if (Util.SDK_INT >= 21) {
- // Let the underlying framework time the release.
- if (earlyUs < 50000) {
- renderOutputBufferTimedV21(codec, bufferIndex, adjustedReleaseTimeNs);
- return true;
- }
- } else {
- // We need to time the release ourselves.
- if (earlyUs < 30000) {
- if (earlyUs > 11000) {
- // We're a little too early to render the frame. Sleep until the frame can be
- // rendered.
- // Note: The 11ms threshold was chosen fairly arbitrarily.
- try {
- // Subtracting 10000 rather than 11000 ensures the sleep time will be at
- // least 1ms.
- Thread.sleep((earlyUs - 10000) / 1000);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- }
- renderOutputBufferImmediate(codec, bufferIndex);
- return true;
- }
- }
-
- // We're either not playing, or it's not time to render the frame yet.
- return false;
- }
-
- private void skipOutputBuffer(MediaCodec codec, int bufferIndex) {
- TraceUtil.beginSection("skipVideoBuffer");
- codec.releaseOutputBuffer(bufferIndex, false);
- TraceUtil.endSection();
- codecCounters.skippedOutputBufferCount++;
- }
-
- private void dropOutputBuffer(MediaCodec codec, int bufferIndex) {
- TraceUtil.beginSection("dropVideoBuffer");
- codec.releaseOutputBuffer(bufferIndex, false);
- TraceUtil.endSection();
- codecCounters.droppedOutputBufferCount++;
- droppedFrameCount++;
- if (droppedFrameCount == maxDroppedFrameCountToNotify) {
- maybeNotifyDroppedFrameCount();
- }
- }
-
- private void renderOutputBufferImmediate(MediaCodec codec, int bufferIndex) {
- TraceUtil.beginSection("renderVideoBufferImmediate");
- codec.releaseOutputBuffer(bufferIndex, true);
- TraceUtil.endSection();
- codecCounters.renderedOutputBufferCount++;
- maybeNotifyDrawnToSurface();
- }
-
- @TargetApi(21)
- private void renderOutputBufferTimedV21(MediaCodec codec, int bufferIndex, long releaseTimeNs) {
- TraceUtil.beginSection("releaseOutputBufferTimed");
- codec.releaseOutputBuffer(bufferIndex, releaseTimeNs);
- TraceUtil.endSection();
- codecCounters.renderedOutputBufferCount++;
- maybeNotifyDrawnToSurface();
- }
-
- private void maybeNotifyVideoSizeChanged() {
- if (eventHandler == null || eventListener == null
- || (lastReportedWidth == currentWidth && lastReportedHeight == currentHeight
- && lastReportedPixelWidthHeightRatio == currentPixelWidthHeightRatio)) {
- return;
- }
-
- // Make final copies to ensure the runnable reports the correct values.
- final int currentWidth = this.currentWidth;
- final int currentHeight = this.currentHeight;
- final float currentPixelWidthHeightRatio = this.currentPixelWidthHeightRatio;
- eventHandler.post(new Runnable() {
- @Override
- public void run() {
- eventListener.onVideoSizeChanged(currentWidth, currentHeight,
- currentPixelWidthHeightRatio);
- }
- });
-
- // Update the last reported values.
- lastReportedWidth = currentWidth;
- lastReportedHeight = currentHeight;
- lastReportedPixelWidthHeightRatio = currentPixelWidthHeightRatio;
- }
-
- private void maybeNotifyDrawnToSurface() {
- if (eventHandler == null || eventListener == null || reportedDrawnToSurface) {
- return;
- }
-
- // Make a final copy to ensure the runnable reports the correct {@link Surface}.
- final Surface surface = this.surface;
- eventHandler.post(new Runnable() {
- @Override
- public void run() {
- eventListener.onDrawnToSurface(surface);
- }
- });
-
- // Record that we have reported that the {@link Surface} has been drawn to.
- reportedDrawnToSurface = true;
- }
-
- private void maybeNotifyDroppedFrameCount() {
- if (eventHandler == null || eventListener == null || droppedFrameCount == 0) {
- return;
- }
- long now = SystemClock.elapsedRealtime();
-
- // Make final copies to ensure the runnable reports the correct values.
- final int countToNotify = droppedFrameCount;
- final long elapsedToNotify = now - droppedFrameAccumulationStartTimeMs;
- eventHandler.post(new Runnable() {
- @Override
- public void run() {
- eventListener.onDroppedFrames(countToNotify, elapsedToNotify);
- }
- });
-
- // Reset the dropped frame tracking.
- droppedFrameCount = 0;
- droppedFrameAccumulationStartTimeMs = now;
- }
-
-}
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/MpegTsPassthroughAc3RendererBuilder.java b/usbtuner/src/com/android/usbtuner/exoplayer/MpegTsPassthroughAc3RendererBuilder.java
index 971be491..70f266d6 100644
--- a/usbtuner/src/com/android/usbtuner/exoplayer/MpegTsPassthroughAc3RendererBuilder.java
+++ b/usbtuner/src/com/android/usbtuner/exoplayer/MpegTsPassthroughAc3RendererBuilder.java
@@ -16,7 +16,7 @@
package com.android.usbtuner.exoplayer;
-import android.media.MediaCodec;
+import android.content.Context;
import android.media.MediaDataSource;
import com.google.android.exoplayer.SampleSource;
@@ -24,21 +24,20 @@ import com.google.android.exoplayer.TrackRenderer;
import com.android.usbtuner.exoplayer.MpegTsPlayer.RendererBuilder;
import com.android.usbtuner.exoplayer.MpegTsPlayer.RendererBuilderCallback;
import com.android.usbtuner.exoplayer.ac3.Ac3TrackRenderer;
+import com.android.usbtuner.exoplayer.cache.CacheManager;
import com.android.usbtuner.tvinput.PlaybackCacheListener;
/**
* Builder class for AC3 Passthrough track renderer objects.
*/
public class MpegTsPassthroughAc3RendererBuilder implements RendererBuilder {
- private static final int NUM_TRACKS = 3;
- private static final int VIDEO_PLAYBACK_DEADLINE_IN_MS = 5000;
- private static final int DROPPED_FRAMES_NOTIFICATION_THRESHOLD = 50;
-
+ private final Context mContext;
private final CacheManager mCacheManager;
private final PlaybackCacheListener mCacheListener;
- public MpegTsPassthroughAc3RendererBuilder(CacheManager cacheManager,
+ public MpegTsPassthroughAc3RendererBuilder(Context context, CacheManager cacheManager,
PlaybackCacheListener cacheListener) {
+ mContext = context;
mCacheManager = cacheManager;
mCacheListener = cacheListener;
}
@@ -50,26 +49,17 @@ public class MpegTsPassthroughAc3RendererBuilder implements RendererBuilder {
SampleExtractor extractor = dataSource == null ?
new MpegTsSampleSourceExtractor(mCacheManager, mCacheListener) :
new MpegTsSampleSourceExtractor(dataSource, mCacheManager, mCacheListener);
- SampleSource sampleSource = new DefaultSampleSource(extractor,
- mpegTsPlayer.isAc3Playable() ? NUM_TRACKS : NUM_TRACKS - 1);
- MediaCodecVideoTrackRenderer videoRenderer =
- new MediaCodecVideoTrackRenderer(sampleSource, null, true,
- MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, VIDEO_PLAYBACK_DEADLINE_IN_MS,
- null, mpegTsPlayer.getMainHandler(), mpegTsPlayer,
- DROPPED_FRAMES_NOTIFICATION_THRESHOLD);
- Ac3TrackRenderer audioRenderer = null;
- if (mpegTsPlayer.isAc3Playable()) {
- audioRenderer = new Ac3TrackRenderer(sampleSource,
- mpegTsPlayer.getMainHandler(), mpegTsPlayer, false);
- } else {
- mpegTsPlayer.onAudioUnplayable();
- }
+ SampleSource sampleSource = new MpegTsSampleSource(extractor);
+ MpegTsVideoTrackRenderer videoRenderer = new MpegTsVideoTrackRenderer(mContext,
+ sampleSource, mpegTsPlayer.getMainHandler(), mpegTsPlayer);
+ Ac3TrackRenderer audioRenderer = new Ac3TrackRenderer(sampleSource,
+ mpegTsPlayer.getMainHandler(), mpegTsPlayer, false);
Cea708TextTrackRenderer textRenderer = new Cea708TextTrackRenderer(sampleSource);
TrackRenderer[] renderers = new TrackRenderer[MpegTsPlayer.RENDERER_COUNT];
renderers[MpegTsPlayer.TRACK_TYPE_VIDEO] = videoRenderer;
renderers[MpegTsPlayer.TRACK_TYPE_AUDIO] = audioRenderer;
renderers[MpegTsPlayer.TRACK_TYPE_TEXT] = textRenderer;
- callback.onRenderers(null, null, renderers);
+ callback.onRenderers(null, renderers);
}
}
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/MpegTsPlayer.java b/usbtuner/src/com/android/usbtuner/exoplayer/MpegTsPlayer.java
index db47fb90..34d83875 100644
--- a/usbtuner/src/com/android/usbtuner/exoplayer/MpegTsPlayer.java
+++ b/usbtuner/src/com/android/usbtuner/exoplayer/MpegTsPlayer.java
@@ -29,10 +29,10 @@ import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.ExoPlayer;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException;
+import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.audio.AudioCapabilities;
import com.google.android.exoplayer.audio.AudioTrack;
-import com.google.android.exoplayer.chunk.MultiTrackChunkSource;
import com.android.usbtuner.data.Cea708Data;
import com.android.usbtuner.data.Cea708Data.CaptionEvent;
import com.android.usbtuner.exoplayer.Cea708TextTrackRenderer.CcListener;
@@ -45,8 +45,7 @@ import java.lang.annotation.RetentionPolicy;
* MPEG-2 TS stream player implementation using ExoPlayer.
*/
public class MpegTsPlayer implements ExoPlayer.Listener,
- MediaCodecVideoTrackRenderer.EventListener, MediaCodecAudioTrackRenderer.EventListener,
- Ac3TrackRenderer.EventListener {
+ MediaCodecVideoTrackRenderer.EventListener, Ac3TrackRenderer.EventListener {
private int mCaptionServiceNumber = Cea708Data.EMPTY_SERVICE_NUMBER;
/**
@@ -61,8 +60,7 @@ public class MpegTsPlayer implements ExoPlayer.Listener,
* Interface definition for {@link RendererBuilder#buildRenderers} to notify the result.
*/
public interface RendererBuilderCallback {
- void onRenderers(String[][] trackNames, MultiTrackChunkSource[] multiTrackSources,
- TrackRenderer[] renderers);
+ void onRenderers(String[][] trackNames, TrackRenderer[] renderers);
void onRenderersError(Exception e);
}
@@ -138,7 +136,6 @@ public class MpegTsPlayer implements ExoPlayer.Listener,
private TrackRenderer mVideoRenderer;
private TrackRenderer mAudioRenderer;
- private MultiTrackChunkSource[] mMultiTrackSources;
private String[][] mTrackNames;
private int[] mSelectedTracks;
@@ -147,7 +144,7 @@ public class MpegTsPlayer implements ExoPlayer.Listener,
private VideoEventListener mVideoEventListener;
public MpegTsPlayer(int playerGeneration, RendererBuilder rendererBuilder, Handler handler,
- AudioCapabilities capabilities) {
+ AudioCapabilities capabilities, Listener listener) {
mRendererBuilder = rendererBuilder;
mPlayer = ExoPlayer.Factory.newInstance(RENDERER_COUNT, MIN_BUFFER_MS, MIN_REBUFFER_MS);
mPlayer.addListener(this);
@@ -158,16 +155,9 @@ public class MpegTsPlayer implements ExoPlayer.Listener,
mRendererBuildingState = RENDERER_BUILDING_STATE_IDLE;
mSelectedTracks = new int[RENDERER_COUNT];
mCcListener = new MpegTsCcListener();
- }
-
- public void addListener(Listener listener) {
mListener = listener;
}
- public void removeListener(Listener listener) {
- mListener = null;
- }
-
public void setVideoEventListener(VideoEventListener videoEventListener) {
mVideoEventListener = videoEventListener;
}
@@ -223,25 +213,17 @@ public class MpegTsPlayer implements ExoPlayer.Listener,
mRendererBuilder.buildRenderers(this, source, mBuilderCallback);
}
- /* package */ void onRenderers(String[][] trackNames,
- MultiTrackChunkSource[] multiTrackSources, TrackRenderer[] renderers) {
+ /* package */ void onRenderers(String[][] trackNames, TrackRenderer[] renderers) {
mBuilderCallback = null;
// Normalize the results.
if (trackNames == null) {
trackNames = new String[RENDERER_COUNT][];
}
- if (multiTrackSources == null) {
- multiTrackSources = new MultiTrackChunkSource[RENDERER_COUNT];
- }
for (int i = 0; i < RENDERER_COUNT; i++) {
if (renderers[i] == null) {
// Convert a null renderer to a dummy renderer.
renderers[i] = new DummyTrackRenderer();
- } else if (trackNames[i] == null) {
- int trackCount = multiTrackSources[i] == null
- ? 1 : multiTrackSources[i].getTrackCount();
- trackNames[i] = new String[trackCount];
}
}
mVideoRenderer = renderers[TRACK_TYPE_VIDEO];
@@ -251,13 +233,12 @@ public class MpegTsPlayer implements ExoPlayer.Listener,
mPlayer.sendMessage(
mTextRenderer, Cea708TextTrackRenderer.MSG_SERVICE_NUMBER, mCaptionServiceNumber);
mTrackNames = trackNames;
- mMultiTrackSources = multiTrackSources;
mRendererBuildingState = RENDERER_BUILDING_STATE_BUILT;
pushSurface(false);
+ mPlayer.prepare(renderers);
pushTrackSelection(TRACK_TYPE_VIDEO, true);
pushTrackSelection(TRACK_TYPE_AUDIO, true);
pushTrackSelection(TRACK_TYPE_TEXT, true);
- mPlayer.prepare(renderers);
}
/* package */ void onRenderersError(Exception e) {
@@ -284,6 +265,7 @@ public class MpegTsPlayer implements ExoPlayer.Listener,
}
mRendererBuildingState = RENDERER_BUILDING_STATE_IDLE;
mSurface = null;
+ mListener = null;
mPlayer.release();
}
@@ -361,7 +343,8 @@ public class MpegTsPlayer implements ExoPlayer.Listener,
}
@Override
- public void onVideoSizeChanged(int width, int height, float pixelWidthHeightRatio) {
+ public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
+ float pixelWidthHeightRatio) {
if (mListener != null) {
mListener.onVideoSizeChanged(mPlayerGeneration, width, height, pixelWidthHeightRatio);
}
@@ -446,19 +429,7 @@ public class MpegTsPlayer implements ExoPlayer.Listener,
if (mRendererBuildingState != RENDERER_BUILDING_STATE_BUILT) {
return;
}
-
- int trackIndex = mSelectedTracks[type];
- if (mMultiTrackSources[type] == null) {
- mPlayer.setRendererEnabled(type, allowRendererEnable);
- } else {
- boolean playWhenReady = mPlayer.getPlayWhenReady();
- mPlayer.setPlayWhenReady(false);
- mPlayer.setRendererEnabled(type, false);
- mPlayer.sendMessage(mMultiTrackSources[type], MultiTrackChunkSource.MSG_SELECT_TRACK,
- trackIndex);
- mPlayer.setRendererEnabled(type, allowRendererEnable);
- mPlayer.setPlayWhenReady(playWhenReady);
- }
+ mPlayer.setSelectedTrack(type, allowRendererEnable ? 0 : -1);
}
private class MpegTsCcListener implements CcListener {
@@ -486,10 +457,9 @@ public class MpegTsPlayer implements ExoPlayer.Listener,
}
@Override
- public void onRenderers(String[][] trackNames, MultiTrackChunkSource[] multiTrackSources,
- TrackRenderer[] renderers) {
+ public void onRenderers(String[][] trackNames, TrackRenderer[] renderers) {
if (!canceled) {
- MpegTsPlayer.this.onRenderers(trackNames, multiTrackSources, renderers);
+ MpegTsPlayer.this.onRenderers(trackNames, renderers);
}
}
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/MpegTsSampleSource.java b/usbtuner/src/com/android/usbtuner/exoplayer/MpegTsSampleSource.java
new file mode 100644
index 00000000..a799c9a4
--- /dev/null
+++ b/usbtuner/src/com/android/usbtuner/exoplayer/MpegTsSampleSource.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.usbtuner.exoplayer;
+
+import com.google.android.exoplayer.C;
+import com.google.android.exoplayer.MediaFormat;
+import com.google.android.exoplayer.MediaFormatHolder;
+import com.google.android.exoplayer.SampleHolder;
+import com.google.android.exoplayer.SampleSource;
+import com.google.android.exoplayer.SampleSource.SampleSourceReader;
+import com.google.android.exoplayer.util.Assertions;
+
+import java.io.IOException;
+
+/** {@link SampleSource} that extracts sample data using a {@link SampleExtractor}. */
+public final class MpegTsSampleSource implements SampleSource, SampleSourceReader {
+
+ private static final int TRACK_STATE_DISABLED = 0;
+ private static final int TRACK_STATE_ENABLED = 1;
+ private static final int TRACK_STATE_FORMAT_SENT = 2;
+
+ private final SampleExtractor mSampleExtractor;
+
+ private MediaFormat[] mTrackFormats;
+ private boolean mPrepared;
+ private IOException mPreparationError;
+ private int mRemainingReleaseCount;
+ private int[] mTrackStates;
+ private boolean[] mPendingDiscontinuities;
+
+ private long mLastSeekPositionUs;
+ private long mPendingSeekPositionUs;
+
+ /**
+ * Creates a new sample source that extracts samples using {@code mSampleExtractor}.
+ *
+ * @param sampleExtractor a sample extractor for accessing media samples
+ */
+ public MpegTsSampleSource(SampleExtractor sampleExtractor) {
+ mSampleExtractor = Assertions.checkNotNull(sampleExtractor);
+ }
+
+ @Override
+ public SampleSourceReader register() {
+ mRemainingReleaseCount++;
+ return this;
+ }
+
+ @Override
+ public boolean prepare(long positionUs) {
+ if (!mPrepared) {
+ if (mPreparationError != null) {
+ return false;
+ }
+ try {
+ if (mSampleExtractor.prepare()) {
+ mTrackFormats = mSampleExtractor.getTrackFormats();
+ mTrackStates = new int[mTrackFormats.length];
+ mPendingDiscontinuities = new boolean[mTrackStates.length];
+ mPrepared = true;
+ }
+ } catch (IOException e) {
+ mPreparationError = e;
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int getTrackCount() {
+ Assertions.checkState(mPrepared);
+ return mTrackFormats.length;
+ }
+
+ @Override
+ public MediaFormat getFormat(int track) {
+ Assertions.checkState(mPrepared);
+ return mTrackFormats[track];
+ }
+
+ @Override
+ public void enable(int track, long positionUs) {
+ Assertions.checkState(mPrepared);
+ Assertions.checkState(mTrackStates[track] == TRACK_STATE_DISABLED);
+ mTrackStates[track] = TRACK_STATE_ENABLED;
+ mSampleExtractor.selectTrack(track);
+ seekToUsInternal(positionUs, positionUs != 0);
+ }
+
+ @Override
+ public void disable(int track) {
+ Assertions.checkState(mPrepared);
+ Assertions.checkState(mTrackStates[track] != TRACK_STATE_DISABLED);
+ mSampleExtractor.deselectTrack(track);
+ mPendingDiscontinuities[track] = false;
+ mTrackStates[track] = TRACK_STATE_DISABLED;
+ }
+
+ @Override
+ public boolean continueBuffering(int track, long positionUs) {
+ return mSampleExtractor.continueBuffering(positionUs);
+ }
+
+ @Override
+ public long readDiscontinuity(int track) {
+ if (mPendingDiscontinuities[track]) {
+ mPendingDiscontinuities[track] = false;
+ return mLastSeekPositionUs;
+ }
+ return NO_DISCONTINUITY;
+ }
+
+ @Override
+ public int readData(int track, long positionUs, MediaFormatHolder formatHolder,
+ SampleHolder sampleHolder) {
+ Assertions.checkState(mPrepared);
+ Assertions.checkState(mTrackStates[track] != TRACK_STATE_DISABLED);
+ if (mPendingDiscontinuities[track]) {
+ return NOTHING_READ;
+ }
+ if (mTrackStates[track] != TRACK_STATE_FORMAT_SENT) {
+ mSampleExtractor.getTrackMediaFormat(track, formatHolder);
+ mTrackStates[track] = TRACK_STATE_FORMAT_SENT;
+ return FORMAT_READ;
+ }
+
+ mPendingSeekPositionUs = C.UNKNOWN_TIME_US;
+ return mSampleExtractor.readSample(track, sampleHolder);
+ }
+
+ @Override
+ public void maybeThrowError() throws IOException {
+ if (mPreparationError != null) {
+ throw mPreparationError;
+ }
+ }
+
+ @Override
+ public void seekToUs(long positionUs) {
+ Assertions.checkState(mPrepared);
+ seekToUsInternal(positionUs, false);
+ }
+
+ @Override
+ public long getBufferedPositionUs() {
+ Assertions.checkState(mPrepared);
+ return mSampleExtractor.getBufferedPositionUs();
+ }
+
+ @Override
+ public void release() {
+ Assertions.checkState(mRemainingReleaseCount > 0);
+ if (--mRemainingReleaseCount == 0) {
+ mSampleExtractor.release();
+ }
+ }
+
+ private void seekToUsInternal(long positionUs, boolean force) {
+ // Unless forced, avoid duplicate calls to the underlying extractor's seek method
+ // in the case that there have been no interleaving calls to readSample.
+ if (force || mPendingSeekPositionUs != positionUs) {
+ mLastSeekPositionUs = positionUs;
+ mPendingSeekPositionUs = positionUs;
+ mSampleExtractor.seekTo(positionUs);
+ for (int i = 0; i < mTrackStates.length; ++i) {
+ if (mTrackStates[i] != TRACK_STATE_DISABLED) {
+ mPendingDiscontinuities[i] = true;
+ }
+ }
+ }
+ }
+}
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/MpegTsSampleSourceExtractor.java b/usbtuner/src/com/android/usbtuner/exoplayer/MpegTsSampleSourceExtractor.java
index 7a6cbf10..7a7248be 100644
--- a/usbtuner/src/com/android/usbtuner/exoplayer/MpegTsSampleSourceExtractor.java
+++ b/usbtuner/src/com/android/usbtuner/exoplayer/MpegTsSampleSourceExtractor.java
@@ -18,12 +18,13 @@ package com.android.usbtuner.exoplayer;
import android.media.MediaDataSource;
-import com.google.android.exoplayer.C;
+import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.MediaFormatHolder;
+import com.google.android.exoplayer.MediaFormatUtil;
import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource;
-import com.google.android.exoplayer.TrackInfo;
import com.google.android.exoplayer.util.MimeTypes;
+import com.android.usbtuner.exoplayer.cache.CacheManager;
import com.android.usbtuner.tvinput.PlaybackCacheListener;
import java.io.IOException;
@@ -38,7 +39,7 @@ public final class MpegTsSampleSourceExtractor implements SampleExtractor {
private static final int CC_BUFFER_SIZE_IN_BYTES = 9600 / 8;
private final SampleExtractor mSampleExtractor;
- private TrackInfo[] mTrackInfos;
+ private MediaFormat[] mTrackFormats;
private boolean[] mGotEos;
private int mVideoTrackIndex;
private int mCea708TextTrackIndex;
@@ -56,16 +57,34 @@ public final class MpegTsSampleSourceExtractor implements SampleExtractor {
mCea708TextTrackSelected = false;
}
+ /**
+ * Creates MpegTsSampleSourceExtractor for {@link MediaDataSource}.
+ *
+ * @param source the {@link MediaDataSource} to extract from
+ * @param cacheManager the manager for reading & writing samples backed by physical storage
+ * @param cacheListener the {@link com.android.usbtuner.tvinput.PlaybackCacheListener}
+ * to notify cache storage status change
+ */
public MpegTsSampleSourceExtractor(MediaDataSource source,
CacheManager cacheManager, PlaybackCacheListener cacheListener) {
if (cacheManager == null || cacheManager.isDisabled()) {
- mSampleExtractor = new SimpleSampleSourceExtractor(source, cacheListener);
+ mSampleExtractor =
+ new PlaySampleExtractor(source, cacheManager, cacheListener, false);
+
} else {
- mSampleExtractor = new CachedSampleSourceExtractor(source, cacheManager, cacheListener);
+ mSampleExtractor =
+ new PlaySampleExtractor(source, cacheManager, cacheListener, true);
}
init();
}
+ /**
+ * Creates MpegTsSampleSourceExtractor for a recorded program.
+ *
+ * @param cacheManager the samples provider which is stored in physical storage
+ * @param cacheListener the {@link com.android.usbtuner.tvinput.PlaybackCacheListener}
+ * to notify cache storage status change
+ */
public MpegTsSampleSourceExtractor(CacheManager cacheManager,
PlaybackCacheListener cacheListener) {
mSampleExtractor = new ReplaySampleSourceExtractor(cacheManager, cacheListener);
@@ -74,13 +93,15 @@ public final class MpegTsSampleSourceExtractor implements SampleExtractor {
@Override
public boolean prepare() throws IOException {
- mSampleExtractor.prepare();
- TrackInfo trackInfos[] = mSampleExtractor.getTrackInfos();
- int trackCount = trackInfos.length;
+ if(!mSampleExtractor.prepare()) {
+ return false;
+ }
+ MediaFormat trackFormats[] = mSampleExtractor.getTrackFormats();
+ int trackCount = trackFormats.length;
mGotEos = new boolean[trackCount];
for (int i = 0; i < trackCount; ++i) {
- String mime = trackInfos[i].mimeType;
+ String mime = trackFormats[i].mimeType;
if (MimeTypes.isVideo(mime) && mVideoTrackIndex == -1) {
mVideoTrackIndex = i;
if (android.media.MediaFormat.MIMETYPE_VIDEO_MPEG2.equals(mime)) {
@@ -94,18 +115,18 @@ public final class MpegTsSampleSourceExtractor implements SampleExtractor {
if (mVideoTrackIndex != -1) {
mCea708TextTrackIndex = trackCount;
}
- mTrackInfos = new TrackInfo[mCea708TextTrackIndex < 0 ? trackCount : trackCount + 1];
- System.arraycopy(trackInfos, 0, mTrackInfos, 0, trackCount);
+ mTrackFormats = new MediaFormat[mCea708TextTrackIndex < 0 ? trackCount : trackCount + 1];
+ System.arraycopy(trackFormats, 0, mTrackFormats, 0, trackCount);
if (mCea708TextTrackIndex >= 0) {
- mTrackInfos[trackCount] = new TrackInfo(MIMETYPE_TEXT_CEA_708,
- trackCount > 0 ? mTrackInfos[0].durationUs : C.UNKNOWN_TIME_US);
+ mTrackFormats[trackCount] = MediaFormatUtil.createTextMediaFormat(MIMETYPE_TEXT_CEA_708,
+ mTrackFormats[0].durationUs);
}
return true;
}
@Override
- public TrackInfo[] getTrackInfos() {
- return mTrackInfos;
+ public MediaFormat[] getTrackFormats() {
+ return mTrackFormats;
}
@Override
@@ -137,9 +158,9 @@ public final class MpegTsSampleSourceExtractor implements SampleExtractor {
}
@Override
- public void getTrackMediaFormat(int track, MediaFormatHolder mediaFormatHolder) {
+ public void getTrackMediaFormat(int track, MediaFormatHolder outMediaFormatHolder) {
if (track != mCea708TextTrackIndex) {
- mSampleExtractor.getTrackMediaFormat(track, mediaFormatHolder);
+ mSampleExtractor.getTrackMediaFormat(track, outMediaFormatHolder);
}
}
@@ -163,12 +184,7 @@ public final class MpegTsSampleSourceExtractor implements SampleExtractor {
return mGotEos[track] ? SampleSource.END_OF_STREAM : SampleSource.NOTHING_READ;
}
- int result;
- try {
- result = mSampleExtractor.readSample(track, sampleHolder);
- } catch (IOException ex) {
- return SampleSource.NOTHING_READ;
- }
+ int result = mSampleExtractor.readSample(track, sampleHolder);
switch (result) {
case SampleSource.END_OF_STREAM: {
mGotEos[track] = true;
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/MpegTsVideoTrackRenderer.java b/usbtuner/src/com/android/usbtuner/exoplayer/MpegTsVideoTrackRenderer.java
new file mode 100644
index 00000000..d2caeaa4
--- /dev/null
+++ b/usbtuner/src/com/android/usbtuner/exoplayer/MpegTsVideoTrackRenderer.java
@@ -0,0 +1,60 @@
+package com.android.usbtuner.exoplayer;
+
+import android.content.Context;
+import android.media.MediaCodec;
+import android.os.Handler;
+
+import com.google.android.exoplayer.DecoderInfo;
+import com.google.android.exoplayer.ExoPlaybackException;
+import com.google.android.exoplayer.MediaCodecSelector;
+import com.google.android.exoplayer.MediaCodecUtil;
+import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
+import com.google.android.exoplayer.MediaFormatHolder;
+import com.google.android.exoplayer.MediaSoftwareCodecUtil;
+import com.google.android.exoplayer.SampleSource;
+import com.android.tv.common.feature.CommonFeatures;
+
+/**
+ * MPEG-2 TS video track renderer
+ */
+public class MpegTsVideoTrackRenderer extends MediaCodecVideoTrackRenderer {
+
+ private static final int VIDEO_PLAYBACK_DEADLINE_IN_MS = 5000;
+ private static final int DROPPED_FRAMES_NOTIFICATION_THRESHOLD = 50;
+ private static final int MIN_HD_HEIGHT = 720;
+ private static final String MIMETYPE_MPEG2 = "video/mpeg2";
+
+ private final boolean mIsSwCodecEnabled;
+ private boolean mCodecIsSwPreferred;
+
+ public MpegTsVideoTrackRenderer(Context context, SampleSource source, Handler handler,
+ MediaCodecVideoTrackRenderer.EventListener listener) {
+ super(context, source, MediaCodecSelector.DEFAULT,
+ MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, VIDEO_PLAYBACK_DEADLINE_IN_MS, handler,
+ listener, DROPPED_FRAMES_NOTIFICATION_THRESHOLD);
+ mIsSwCodecEnabled = CommonFeatures.USE_SW_CODEC_FOR_SD.isEnabled(context);
+ }
+
+ @Override
+ protected DecoderInfo getDecoderInfo(MediaCodecSelector codecSelector, String mimeType,
+ boolean requiresSecureDecoder) throws MediaCodecUtil.DecoderQueryException {
+ try {
+ if (mIsSwCodecEnabled && mCodecIsSwPreferred) {
+ DecoderInfo swCodec = MediaSoftwareCodecUtil.getSoftwareDecoderInfo(
+ mimeType, requiresSecureDecoder);
+ if (swCodec != null) {
+ return swCodec;
+ }
+ }
+ } catch (MediaSoftwareCodecUtil.DecoderQueryException e) {
+ }
+ return super.getDecoderInfo(codecSelector, mimeType,requiresSecureDecoder);
+ }
+
+ @Override
+ protected void onInputFormatChanged(MediaFormatHolder holder) throws ExoPlaybackException {
+ mCodecIsSwPreferred = MIMETYPE_MPEG2.equalsIgnoreCase(holder.format.mimeType)
+ && holder.format.height < MIN_HD_HEIGHT;
+ super.onInputFormatChanged(holder);
+ }
+}
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/BaseSampleSourceExtractor.java b/usbtuner/src/com/android/usbtuner/exoplayer/PlaySampleExtractor.java
index 69e44f21..e249e3cb 100644
--- a/usbtuner/src/com/android/usbtuner/exoplayer/BaseSampleSourceExtractor.java
+++ b/usbtuner/src/com/android/usbtuner/exoplayer/PlaySampleExtractor.java
@@ -19,37 +19,55 @@ package com.android.usbtuner.exoplayer;
import android.media.MediaDataSource;
import android.media.MediaExtractor;
import android.os.ConditionVariable;
+import android.os.SystemClock;
import android.util.Log;
-import com.google.android.exoplayer.C;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.MediaFormatHolder;
+import com.google.android.exoplayer.MediaFormatUtil;
import com.google.android.exoplayer.SampleHolder;
-import com.google.android.exoplayer.TrackInfo;
+import com.android.usbtuner.exoplayer.cache.CacheManager;
+import com.android.usbtuner.exoplayer.cache.RecordingSampleBuffer;
+import com.android.usbtuner.exoplayer.cache.SimpleSampleBuffer;
+import com.android.usbtuner.tvinput.PlaybackCacheListener;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.atomic.AtomicLong;
/**
- * Base class for feeding samples from a given media extractor using a extractor thread.
+ * A class that plays a live stream from a given media extractor using an extractor thread.
*/
-public abstract class BaseSampleSourceExtractor implements SampleExtractor {
- private static final String TAG = "BaseSampleSourceExt";
+public class PlaySampleExtractor implements SampleExtractor {
+ private static final String TAG = "PlaySampleExtractor";
// Maximum bandwidth of 1080p channel is about 2.2MB/s. 2MB for a sample will suffice.
private static final int SAMPLE_BUFFER_SIZE = 1024 * 1024 * 2;
+ private static final AtomicLong ID_COUNTER = new AtomicLong(0);
private final MediaDataSource mDataSource;
private final MediaExtractor mMediaExtractor;
private final ExtractorThread mExtractorThread;
- private TrackInfo[] mTrackInfos;
+ private final CacheManager.SampleBuffer mSampleBuffer;
+ private final long mId;
+ private MediaFormat[] mTrackFormats;
- private boolean mEos = false;
private boolean mReleased = false;
- public BaseSampleSourceExtractor(MediaDataSource source) {
+ public PlaySampleExtractor(MediaDataSource source, CacheManager cacheManager,
+ PlaybackCacheListener cacheListener, boolean useCache) {
+ mId = ID_COUNTER.incrementAndGet();
mDataSource = source;
mMediaExtractor = new MediaExtractor();
mExtractorThread = new ExtractorThread();
+ if (useCache) {
+ mSampleBuffer = new RecordingSampleBuffer(cacheManager, cacheListener, true,
+ RecordingSampleBuffer.CACHE_REASON_LIVE_PLAYBACK);
+ } else {
+ mSampleBuffer = new SimpleSampleBuffer(cacheListener);
+ }
}
private class ExtractorThread extends Thread {
@@ -62,7 +80,7 @@ public abstract class BaseSampleSourceExtractor implements SampleExtractor {
@Override
public void run() {
SampleHolder sample = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_NORMAL);
- sample.replaceBuffer(SAMPLE_BUFFER_SIZE);
+ sample.ensureSpaceForWrite(SAMPLE_BUFFER_SIZE);
ConditionVariable conditionVariable = new ConditionVariable();
while (!mQuitRequested) {
fetchSample(sample, conditionVariable);
@@ -75,7 +93,7 @@ public abstract class BaseSampleSourceExtractor implements SampleExtractor {
if (index < 0) {
Log.i(TAG, "EoS");
mQuitRequested = true;
- setEos();
+ mSampleBuffer.setEos();
return;
}
sample.data.clear();
@@ -95,7 +113,7 @@ public abstract class BaseSampleSourceExtractor implements SampleExtractor {
queueSample(index, sample, conditionVariable);
} catch (IOException e) {
mQuitRequested = true;
- setEos();
+ mSampleBuffer.setEos();
}
}
@@ -104,10 +122,18 @@ public abstract class BaseSampleSourceExtractor implements SampleExtractor {
}
}
- public abstract void queueSample(int index, SampleHolder sample, ConditionVariable
- conditionVariable) throws IOException;
+ public void queueSample(int index, SampleHolder sample, ConditionVariable conditionVariable)
+ throws IOException {
+ long writeStartTimeNs = SystemClock.elapsedRealtimeNanos();
+ mSampleBuffer.writeSample(index, sample, conditionVariable);
- public void initOnPrepareLocked(int trackCount) throws IOException {}
+ // Check if the storage has enough bandwidth for trickplay. Otherwise we disable it
+ // and notify the slowness through the playback cache listener.
+ if (mSampleBuffer.isWriteSpeedSlow(sample.size,
+ SystemClock.elapsedRealtimeNanos() - writeStartTimeNs)) {
+ mSampleBuffer.handleWriteSpeedSlow();
+ }
+ }
@Override
public boolean prepare() throws IOException {
@@ -115,41 +141,63 @@ public abstract class BaseSampleSourceExtractor implements SampleExtractor {
mMediaExtractor.setDataSource(mDataSource);
int trackCount = mMediaExtractor.getTrackCount();
- initOnPrepareLocked(trackCount);
- mTrackInfos = new TrackInfo[trackCount];
+ mTrackFormats = new MediaFormat[trackCount];
for (int i = 0; i < trackCount; i++) {
- android.media.MediaFormat format = mMediaExtractor.getTrackFormat(i);
- long durationUs = format.containsKey(android.media.MediaFormat.KEY_DURATION)
- ? format.getLong(android.media.MediaFormat.KEY_DURATION)
- : C.UNKNOWN_TIME_US;
- String mime = format.getString(android.media.MediaFormat.KEY_MIME);
+ mTrackFormats[i] =
+ MediaFormatUtil.createMediaFormat(mMediaExtractor.getTrackFormat(i));
mMediaExtractor.selectTrack(i);
- mTrackInfos[i] = new TrackInfo(mime, durationUs);
}
+ List<String> ids = new ArrayList<>();
+ for (int i = 0; i < trackCount; i++) {
+ ids.add(String.format(Locale.ENGLISH, "%s_%x", Long.toHexString(mId), i));
+
+ }
+ mSampleBuffer.init(ids, null);
+
}
mExtractorThread.start();
return true;
}
@Override
- public synchronized TrackInfo[] getTrackInfos() {
- return mTrackInfos;
+ public synchronized MediaFormat[] getTrackFormats() {
+ return mTrackFormats;
}
- private synchronized void setEos() {
- mEos = true;
+ @Override
+ public void getTrackMediaFormat(int track, MediaFormatHolder outMediaFormatHolder) {
+ outMediaFormatHolder.format = mTrackFormats[track];
+ outMediaFormatHolder.drmInitData = null;
}
- public synchronized boolean getEos() {
- return mEos;
+ @Override
+ public void selectTrack(int index) {
+ mSampleBuffer.selectTrack(index);
}
@Override
- public void getTrackMediaFormat(int track, MediaFormatHolder mediaFormatHolder) {
- mediaFormatHolder.format =
- MediaFormat.createFromFrameworkMediaFormatV16(mMediaExtractor
- .getTrackFormat(track));
- mediaFormatHolder.drmInitData = null;
+ public void deselectTrack(int index) {
+ mSampleBuffer.deselectTrack(index);
+ }
+
+ @Override
+ public long getBufferedPositionUs() {
+ return mSampleBuffer.getBufferedPositionUs();
+ }
+
+ @Override
+ public boolean continueBuffering(long positionUs) {
+ return mSampleBuffer.continueBuffering(positionUs);
+ }
+
+ @Override
+ public void seekTo(long positionUs) {
+ mSampleBuffer.seekTo(positionUs);
+ }
+
+ @Override
+ public int readSample(int track, SampleHolder sampleHolder) {
+ return mSampleBuffer.readSample(track, sampleHolder);
}
@Override
@@ -166,7 +214,9 @@ public abstract class BaseSampleSourceExtractor implements SampleExtractor {
}
}
- public void cleanUpImpl() {}
+ public void cleanUpImpl() {
+ mSampleBuffer.release();
+ }
public synchronized void cleanUp() {
if (!mReleased) {
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/RecordSampleSourceExtractor.java b/usbtuner/src/com/android/usbtuner/exoplayer/RecordSampleSourceExtractor.java
deleted file mode 100644
index fa416c25..00000000
--- a/usbtuner/src/com/android/usbtuner/exoplayer/RecordSampleSourceExtractor.java
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.usbtuner.exoplayer;
-
-import android.media.MediaCodec;
-import android.media.MediaDataSource;
-import android.media.MediaExtractor;
-import android.os.ConditionVariable;
-import android.os.SystemClock;
-import android.util.Log;
-import android.util.Pair;
-
-import com.google.android.exoplayer.C;
-import com.google.android.exoplayer.MediaFormat;
-import com.google.android.exoplayer.MediaFormatHolder;
-import com.google.android.exoplayer.SampleHolder;
-import com.google.android.exoplayer.SampleSource;
-import com.google.android.exoplayer.TrackInfo;
-import com.google.android.exoplayer.TrackRenderer;
-import com.google.android.exoplayer.util.MimeTypes;
-import com.android.usbtuner.tvinput.PlaybackCacheListener;
-
-import java.io.IOException;
-import java.util.Locale;
-import java.util.Random;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Records live streams on the disk for DVR.
- * <p>
- * For the convenience of testing, it implements {@link SampleExtractor}.
- */
-public class RecordSampleSourceExtractor implements SampleExtractor, CacheManager.EvictListener {
- // TODO: Decouple from {@link SampleExtractor}. Handle recording errors properly.
-
- private static final String TAG = "RecordSampleSourceExt";
-
- // Maximum bandwidth of 1080p channel is about 2.2MB/s. 2MB for a sample will suffice.
- private static final int SAMPLE_BUFFER_SIZE = 1024 * 1024 * 2;
-
- private final MediaDataSource mDataSource;
- private final MediaExtractor mMediaExtractor;
- private final ExtractorThread mExtractorThread;
- private int mTrackCount;
- private TrackInfo[] mTrackInfos;
- private android.media.MediaFormat[] mMediaFormat;
-
- private boolean mEos = false;
- private boolean mReleased = false;
-
- public static final long CHUNK_DURATION_US = TimeUnit.MILLISECONDS.toMicros(500);
-
- private static final long CACHE_WRITE_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10); // 10 seconds
-
- private final CacheManager mCacheManager;
- private final String mId;
-
- private final PlaybackCacheListener mCacheListener;
- private long[] mCacheEndPositionsUs;
- private volatile long mCacheDurationUs = 0;
- private SampleCache[] mSampleCaches;
- private final SamplePool mSamplePool;
-
- public RecordSampleSourceExtractor(MediaDataSource source, CacheManager cacheManager,
- PlaybackCacheListener cacheListener) {
- mDataSource = source;
- mMediaExtractor = new MediaExtractor();
- mExtractorThread = new ExtractorThread();
- mCacheManager = cacheManager;
- mCacheListener = cacheListener;
- mSamplePool = new SamplePool();
- // TODO: Use UUID afterwards.
- mId = Long.toHexString(new Random().nextLong());
- cacheListener.onCacheStateChanged(true); // Enable trickplay
- }
-
- private class ExtractorThread extends Thread {
- private volatile boolean mQuitRequested = false;
-
- public ExtractorThread() {
- super("ExtractorThread");
- }
-
- @Override
- public void run() {
- SampleHolder sample = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_NORMAL);
- sample.replaceBuffer(SAMPLE_BUFFER_SIZE);
- ConditionVariable conditionVariable = new ConditionVariable();
- while (!mQuitRequested) {
- fetchSample(sample, conditionVariable);
- }
- cleanUp();
- }
-
- private void fetchSample(SampleHolder sample, ConditionVariable conditionVariable) {
- int index = mMediaExtractor.getSampleTrackIndex();
- if (index < 0) {
- Log.i(TAG, "EoS");
- mQuitRequested = true;
- setEos();
- return;
- }
- sample.data.clear();
- sample.size = mMediaExtractor.readSampleData(sample.data, 0);
- if (sample.size < 0 || sample.size > SAMPLE_BUFFER_SIZE) {
- // Should not happen
- Log.e(TAG, "Invalid sample size: " + sample.size);
- mMediaExtractor.advance();
- return;
- }
- sample.data.position(sample.size);
- sample.timeUs = mMediaExtractor.getSampleTime();
- if (sample.timeUs > mCacheDurationUs) {
- mCacheDurationUs = sample.timeUs;
- }
- sample.flags = mMediaExtractor.getSampleFlags();
-
- mMediaExtractor.advance();
- try {
- queueSample(index, sample, conditionVariable);
- } catch (IOException e) {
- mQuitRequested = true;
- setEos();
- }
- }
-
- public void quit() {
- mQuitRequested = true;
- }
- }
-
- private void queueSample(int index, SampleHolder sample, ConditionVariable conditionVariable)
- throws IOException {
- long writeStartTimeNs = SystemClock.elapsedRealtimeNanos();
- synchronized (this) {
- SampleCache cache = mSampleCaches[index];
- if ((sample.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) {
- if (sample.timeUs >= mCacheEndPositionsUs[index]) {
- try {
- SampleCache nextCache = mCacheManager.createNewWriteFile(
- getTrackId(index), mCacheEndPositionsUs[index], mSamplePool);
- cache.finishWrite(nextCache);
- mSampleCaches[index] = cache = nextCache;
- mCacheEndPositionsUs[index] =
- ((sample.timeUs / CHUNK_DURATION_US) + 1) * CHUNK_DURATION_US;
- } catch (IOException e) {
- cache.finishWrite(null);
- throw e;
- }
- }
- }
- cache.writeSample(sample, conditionVariable);
- }
- if (!conditionVariable.block(CACHE_WRITE_TIMEOUT_MS)) {
- Log.e(TAG, "Error: Serious delay on writing cache");
- conditionVariable.block();
- }
-
- // Check if the storage has enough bandwidth for recording. Otherwise we disable it
- // and notify the slowness through the playback cache listener.
- mCacheManager.addWriteStat(sample.size,
- SystemClock.elapsedRealtimeNanos() - writeStartTimeNs);
- if (mCacheManager.isWriteSlow()) {
- Log.w(TAG, "Disk is too slow for trickplay. Disable trickplay.");
- mCacheManager.disable();
- mCacheListener.onDiskTooSlow();
- }
- }
-
- private String getTrackId(int index) {
- return String.format(Locale.ENGLISH, "%s_%x", mId, index);
- }
-
- private void initOnPrepareLocked(int trackCount) throws IOException {
- mSampleCaches = new SampleCache[trackCount];
- mCacheEndPositionsUs = new long[trackCount];
- for (int i = 0; i < trackCount; i++) {
- mSampleCaches[i] = mCacheManager.createNewWriteFile(getTrackId(i), 0, mSamplePool);
- mCacheEndPositionsUs[i] = CHUNK_DURATION_US;
- }
- }
- @Override
- public boolean prepare() throws IOException {
- synchronized (this) {
- mMediaExtractor.setDataSource(mDataSource);
-
- mTrackCount = mMediaExtractor.getTrackCount();
- initOnPrepareLocked(mTrackCount);
- mTrackInfos = new TrackInfo[mTrackCount];
- mMediaFormat = new android.media.MediaFormat[mTrackCount];
- for (int i = 0; i < mTrackCount; i++) {
- android.media.MediaFormat format = mMediaExtractor.getTrackFormat(i);
- long durationUs = format.containsKey(android.media.MediaFormat.KEY_DURATION)
- ? format.getLong(android.media.MediaFormat.KEY_DURATION)
- : C.UNKNOWN_TIME_US;
- String mime = format.getString(android.media.MediaFormat.KEY_MIME);
- mMediaExtractor.selectTrack(i);
- mTrackInfos[i] = new TrackInfo(mime, durationUs);
- mMediaFormat[i] = format;
- }
- }
- mExtractorThread.start();
- return true;
- }
-
- @Override
- public synchronized TrackInfo[] getTrackInfos() {
- return mTrackInfos;
- }
-
- private synchronized void setEos() {
- mEos = true;
- }
-
- /**
- * Notifies whether sample extraction met end of stream.
- */
- public synchronized boolean getEos() {
- return mEos;
- }
-
- @Override
- public void getTrackMediaFormat(int track, MediaFormatHolder mediaFormatHolder) {
- mediaFormatHolder.format =
- MediaFormat.createFromFrameworkMediaFormatV16(mMediaExtractor
- .getTrackFormat(track));
- mediaFormatHolder.drmInitData = null;
- }
-
- @Override
- public void selectTrack(int index) {
- }
-
- @Override
- public void deselectTrack(int index) {
- }
-
- @Override
- public void seekTo(long positionUs) {
- }
-
- @Override
- public int readSample(int track, SampleHolder sampleHolder) {
- return SampleSource.NOTHING_READ;
- }
-
- private void cleanUpInternal() {
- if (mSampleCaches == null) {
- return;
- }
- for (int i = 0; i < mSampleCaches.length; ++i) {
- mSampleCaches[i].finishWrite(null);
- mCacheManager.unregisterEvictListener(getTrackId(i));
- }
- if (mTrackCount > 0) {
- Pair<String, android.media.MediaFormat> audio = null, video = null;
- for (int i = 0; i < mTrackCount; ++i) {
- String mime = mTrackInfos[i].mimeType;
- mMediaFormat[i].setLong(android.media.MediaFormat.KEY_DURATION, mCacheDurationUs);
- if (MimeTypes.isAudio(mime)) {
- audio = new Pair<>(getTrackId(i), mMediaFormat[i]);
- }
- if (MimeTypes.isVideo(mime)) {
- video = new Pair<>(getTrackId(i), mMediaFormat[i]);
- }
- }
- mCacheManager.writeMetaFiles(audio, video);
- }
- for (int i = 0; i < mSampleCaches.length; ++i) {
- mCacheManager.clearTrack(getTrackId(i));
- }
- }
-
- @Override
- public void release() {
- synchronized (this) {
- mReleased = true;
- }
- if (mExtractorThread.isAlive()) {
- mExtractorThread.quit();
-
- // We don't join here to prevent hang --- MediaExtractor is released at the thread.
- } else {
- cleanUp();
- }
- }
-
- private synchronized void cleanUp() {
- if (!mReleased) {
- return;
- }
- cleanUpInternal();
- mMediaExtractor.release();
- }
-
- @Override
- public long getBufferedPositionUs() {
- // This will make player keep alive with no-op.
- return TrackRenderer.UNKNOWN_TIME_US;
- }
-
- @Override
- public boolean continueBuffering(long positionUs) {
- // This will make player keep alive with no-op.
- return true;
- }
-
- // CacheEvictListener
- @Override
- public void onCacheEvicted(String id, long createdTimeMs) {
- mCacheListener.onCacheStartTimeChanged(
- createdTimeMs + TimeUnit.MICROSECONDS.toMillis(CHUNK_DURATION_US));
- }
-}
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/Recorder.java b/usbtuner/src/com/android/usbtuner/exoplayer/Recorder.java
new file mode 100644
index 00000000..d7145f26
--- /dev/null
+++ b/usbtuner/src/com/android/usbtuner/exoplayer/Recorder.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.usbtuner.exoplayer;
+
+import android.media.MediaDataSource;
+import android.media.MediaExtractor;
+import android.os.ConditionVariable;
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.google.android.exoplayer.SampleHolder;
+import com.android.usbtuner.exoplayer.cache.CacheManager;
+import com.android.usbtuner.exoplayer.cache.RecordingSampleBuffer;
+import com.android.usbtuner.tvinput.PlaybackCacheListener;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Records live streams on the disk for DVR.
+ */
+public class Recorder {
+ private static final String TAG = "Recorder";
+
+ // Maximum bandwidth of 1080p channel is about 2.2MB/s. 2MB for a sample will suffice.
+ private static final int SAMPLE_BUFFER_SIZE = 1024 * 1024 * 2;
+ private static final AtomicLong ID_COUNTER = new AtomicLong(0);
+
+ private final MediaDataSource mDataSource;
+ private final MediaExtractor mMediaExtractor;
+ private final ExtractorThread mExtractorThread;
+ private int mTrackCount;
+ private List<android.media.MediaFormat> mMediaFormats;
+
+ private final CacheManager.SampleBuffer mSampleBuffer;
+
+ private boolean mReleased = false;
+ private boolean mResultNotified = false;
+ private final long mId;
+
+ private final RecordListener mRecordListener;
+
+ /**
+ * Listeners for events which happens during the recording.
+ */
+ public interface RecordListener {
+
+ /**
+ * Notifies recording completion.
+ *
+ * @param success {@code true} when the recording succeeded, {@code false} otherwise
+ */
+ void notifyRecordingFinished(boolean success);
+ }
+
+ /**
+ * Create a recorder for a {@link android.media.MediaDataSource}.
+ *
+ * @param source {@link android.media.MediaDataSource} to record from
+ * @param cacheManager the manager for recording samples to physical storage
+ * @param cacheListener the {@link com.android.usbtuner.tvinput.PlaybackCacheListener}
+ * to notify cache storage status change
+ * @param recordListener RecordListener to notify events during the recording
+ */
+ public Recorder(MediaDataSource source, CacheManager cacheManager,
+ PlaybackCacheListener cacheListener, RecordListener recordListener) {
+ mDataSource = source;
+ mMediaExtractor = new MediaExtractor();
+ mExtractorThread = new ExtractorThread();
+ mRecordListener = recordListener;
+
+ mSampleBuffer = new RecordingSampleBuffer(cacheManager, cacheListener, false,
+ RecordingSampleBuffer.CACHE_REASON_RECORDING);
+ mId = ID_COUNTER.incrementAndGet();
+ }
+
+ private class ExtractorThread extends Thread {
+ private volatile boolean mQuitRequested = false;
+
+ public ExtractorThread() {
+ super("ExtractorThread");
+ }
+
+ @Override
+ public void run() {
+ SampleHolder sample = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_NORMAL);
+ sample.ensureSpaceForWrite(SAMPLE_BUFFER_SIZE);
+ ConditionVariable conditionVariable = new ConditionVariable();
+ while (!mQuitRequested) {
+ fetchSample(sample, conditionVariable);
+ }
+ cleanUp();
+ }
+
+ private void fetchSample(SampleHolder sample, ConditionVariable conditionVariable) {
+ int index = mMediaExtractor.getSampleTrackIndex();
+ if (index < 0) {
+ Log.i(TAG, "EoS");
+ mQuitRequested = true;
+ mSampleBuffer.setEos();
+ return;
+ }
+ sample.data.clear();
+ sample.size = mMediaExtractor.readSampleData(sample.data, 0);
+ if (sample.size < 0 || sample.size > SAMPLE_BUFFER_SIZE) {
+ // Should not happen
+ Log.e(TAG, "Invalid sample size: " + sample.size);
+ mMediaExtractor.advance();
+ return;
+ }
+ sample.data.position(sample.size);
+ sample.timeUs = mMediaExtractor.getSampleTime();
+ sample.flags = mMediaExtractor.getSampleFlags();
+
+ mMediaExtractor.advance();
+ try {
+ queueSample(index, sample, conditionVariable);
+ } catch (IOException e) {
+ mQuitRequested = true;
+ mSampleBuffer.setEos();
+ }
+ }
+
+ public void quit() {
+ mQuitRequested = true;
+ }
+ }
+
+ private void queueSample(int index, SampleHolder sample, ConditionVariable conditionVariable)
+ throws IOException {
+ long writeStartTimeNs = SystemClock.elapsedRealtimeNanos();
+ mSampleBuffer.writeSample(index, sample, conditionVariable);
+
+ // Check if the storage has enough bandwidth for recording. Otherwise we disable it
+ // and notify the slowness.
+ if (mSampleBuffer.isWriteSpeedSlow(sample.size,
+ SystemClock.elapsedRealtimeNanos() - writeStartTimeNs)) {
+ Log.w(TAG, "Disk is too slow for trickplay. Disable trickplay.");
+ throw new IOException("Disk is too slow");
+ }
+ }
+
+ /**
+ * Prepares a recording.
+ *
+ * @return {@code true} when preparation finished successfully, {@code false} otherwise
+ * @throws IOException
+ */
+ public boolean prepare() throws IOException {
+ synchronized (this) {
+ mMediaExtractor.setDataSource(mDataSource);
+
+ mTrackCount = mMediaExtractor.getTrackCount();
+ List<String> ids = new ArrayList<>();
+ mMediaFormats = new ArrayList<>();
+ for (int i = 0; i < mTrackCount; i++) {
+ ids.add(String.format(Locale.ENGLISH, "%s_%x", Long.toHexString(mId), i));
+ android.media.MediaFormat format = mMediaExtractor.getTrackFormat(i);
+ mMediaExtractor.selectTrack(i);
+ mMediaFormats.add(format);
+ }
+ mSampleBuffer.init(ids, mMediaFormats);
+ }
+ mExtractorThread.start();
+ return true;
+ }
+
+ /**
+ * Releases all the resources which were used in the recording.
+ */
+ public void release() {
+ synchronized (this) {
+ mReleased = true;
+ }
+ if (mExtractorThread.isAlive()) {
+ mExtractorThread.quit();
+ // We don't join here to prevent hang --- MediaExtractor is released at the thread.
+ } else {
+ cleanUp();
+ }
+ }
+
+ private synchronized void cleanUp() {
+ if (!mReleased) {
+ if (!mResultNotified) {
+ mRecordListener.notifyRecordingFinished(false);
+ mResultNotified = true;
+ }
+ return;
+ }
+ mSampleBuffer.release();
+ if (!mResultNotified) {
+ mRecordListener.notifyRecordingFinished(true);
+ mResultNotified = true;
+ }
+ mMediaExtractor.release();
+ }
+
+}
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/ReplaySampleSourceExtractor.java b/usbtuner/src/com/android/usbtuner/exoplayer/ReplaySampleSourceExtractor.java
index 603f1e68..bbde8863 100644
--- a/usbtuner/src/com/android/usbtuner/exoplayer/ReplaySampleSourceExtractor.java
+++ b/usbtuner/src/com/android/usbtuner/exoplayer/ReplaySampleSourceExtractor.java
@@ -16,118 +16,44 @@
package com.android.usbtuner.exoplayer;
-import com.google.android.exoplayer.C;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.MediaFormatHolder;
+import com.google.android.exoplayer.MediaFormatUtil;
import com.google.android.exoplayer.SampleHolder;
-import com.google.android.exoplayer.SampleSource;
-import com.google.android.exoplayer.TrackInfo;
+import com.android.usbtuner.exoplayer.cache.CacheManager;
+import com.android.usbtuner.exoplayer.cache.RecordingSampleBuffer;
import com.android.usbtuner.tvinput.PlaybackCacheListener;
-import junit.framework.Assert;
-
-import android.util.Log;
import android.util.Pair;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.concurrent.TimeUnit;
+import java.util.List;
/**
* A class that plays a recorded stream without using {@link MediaExtractor},
* since all samples are extracted and stored to the permanent storage already.
*/
-public class ReplaySampleSourceExtractor implements SampleExtractor, CacheManager.EvictListener {
+public class ReplaySampleSourceExtractor implements SampleExtractor{
private static final String TAG = "ReplaySampleSourceExt";
private static final boolean DEBUG = false;
- public static final long CHUNK_DURATION_US = TimeUnit.MILLISECONDS.toMicros(500);
-
+ private int mTrackCount;
private android.media.MediaFormat[] mMediaFormats;
- private TrackInfo[] mTrackInfos;
- private String[] mIds;
+ private MediaFormat[] mTrackFormats;
- private boolean mEos;
private boolean mReleased;
private final CacheManager mCacheManager;
-
private final PlaybackCacheListener mCacheListener;
- private CachedSampleQueue[] mPlayingSampleQueues;
- private final SamplePool mSamplePool = new SamplePool();
- private long mLastBufferedPositionUs = C.UNKNOWN_TIME_US;
- private long mCurrentPlaybackPositionUs = 0;
-
- private class CachedSampleQueue extends SampleQueue {
- private SampleCache mCache = null;
-
- public CachedSampleQueue(SamplePool samplePool) {
- super(samplePool);
- }
-
- public void setSource(SampleCache newCache) {
- for (SampleCache cache = mCache; cache != null; cache = cache.getNext()) {
- cache.clear();
- cache.close();
- }
- mCache = newCache;
- for (SampleCache cache = mCache; cache != null; cache = cache.getNext()) {
- cache.resetRead();
- }
- }
-
- public boolean maybeReadSample() {
- if (isDurationGreaterThan(CHUNK_DURATION_US)) {
- return false;
- }
- SampleHolder sample = mCache.maybeReadSample();
- if (sample == null) {
- if (!mCache.canReadMore()) {
- if (mCache.getNext() == null) {
- // reached the end of the recording
- setEos();
- return false;
- } else {
- mCache.clear();
- mCache.close();
- mCache = mCache.getNext();
- mCache.resetRead();
- return maybeReadSample();
- }
- }
- return false;
- } else {
- queueSample(sample);
- return true;
- }
- }
-
- public int dequeueSample(SampleHolder sample) {
- maybeReadSample();
- return super.dequeueSample(sample);
- }
-
- @Override
- public void clear() {
- super.clear();
- for (SampleCache cache = mCache; cache != null; cache = cache.getNext()) {
- cache.clear();
- cache.close();
- }
- mCache = null;
- }
-
- public long getSourceStartPositionUs() {
- return mCache == null ? -1 : mCache.getStartPositionUs();
- }
- }
+ private CacheManager.SampleBuffer mSampleBuffer;
public ReplaySampleSourceExtractor(
CacheManager cacheManager, PlaybackCacheListener cacheListener) {
mCacheManager = cacheManager;
mCacheListener = cacheListener;
- cacheListener.onCacheStateChanged(true); // Enable trickplay
+ mTrackCount = -1;
}
@Override
@@ -137,175 +63,69 @@ public class ReplaySampleSourceExtractor implements SampleExtractor, CacheManage
if (trackInfos == null || trackInfos.size() <= 0) {
return false;
}
- int trackCount = trackInfos.size();
- mIds = new String[trackCount];
- mMediaFormats = new android.media.MediaFormat[trackCount];
- mTrackInfos = new TrackInfo[trackCount];
- for (int i = 0; i < trackCount; ++i) {
+ mTrackCount = trackInfos.size();
+ List<String> ids = new ArrayList<>();
+ mMediaFormats = new android.media.MediaFormat[mTrackCount];
+ mTrackFormats = new MediaFormat[mTrackCount];
+ for (int i = 0; i < mTrackCount; ++i) {
Pair<String, android.media.MediaFormat> pair = trackInfos.get(i);
- mIds[i] = pair.first;
+ ids.add(pair.first);
mMediaFormats[i] = pair.second;
-
- // TODO: save this according to recording length
- long durationUs = mMediaFormats[i].containsKey(android.media.MediaFormat.KEY_DURATION)
- ? mMediaFormats[i].getLong(android.media.MediaFormat.KEY_DURATION)
- : C.UNKNOWN_TIME_US;
- String mime = mMediaFormats[i].getString(android.media.MediaFormat.KEY_MIME);
- mTrackInfos[i] = new TrackInfo(mime, durationUs);
+ mTrackFormats[i] = MediaFormatUtil.createMediaFormat(mMediaFormats[i]);
}
- initOnLoad(trackCount);
+ mSampleBuffer = new RecordingSampleBuffer(mCacheManager, mCacheListener, true,
+ RecordingSampleBuffer.CACHE_REASON_RECORDED_PLAYBACK);
+ mSampleBuffer.init(ids, null);
return true;
}
@Override
- public TrackInfo[] getTrackInfos() {
- return mTrackInfos;
- }
-
- private void setEos() {
- mEos = true;
- }
-
- public boolean getEos() {
- return mEos;
+ public MediaFormat[] getTrackFormats() {
+ return mTrackFormats;
}
@Override
- public void getTrackMediaFormat(int track, MediaFormatHolder mediaFormatHolder) {
- mediaFormatHolder.format =
- MediaFormat.createFromFrameworkMediaFormatV16(mMediaFormats[track]);
- mediaFormatHolder.drmInitData = null;
+ public void getTrackMediaFormat(int track, MediaFormatHolder outMediaFormatHolder) {
+ outMediaFormatHolder.format = mTrackFormats[track];
+ outMediaFormatHolder.drmInitData = null;
}
@Override
public void release() {
if (!mReleased) {
- cleanUpImpl();
+ mSampleBuffer.release();
}
mReleased = true;
}
-
- private String getTrackId(int index) {
- return mIds[index];
- }
-
- public void initOnLoad(int trackCount) throws IOException {
- mPlayingSampleQueues = new CachedSampleQueue[trackCount];
- for (int i = 0; i < trackCount; i++) {
- mCacheManager.loadTrackFormStorage(mIds[i], mSamplePool);
- }
- }
-
@Override
public void selectTrack(int index) {
- if (mPlayingSampleQueues[index] == null) {
- String trackId = getTrackId(index);
- mPlayingSampleQueues[index] = new CachedSampleQueue(mSamplePool);
- mCacheManager.registerEvictListener(trackId, this);
- seekIndividualTrack(index, mCurrentPlaybackPositionUs);
- mPlayingSampleQueues[index].maybeReadSample();
- }
+ mSampleBuffer.selectTrack(index);
}
@Override
public void deselectTrack(int index) {
- if (mPlayingSampleQueues[index] != null) {
- mPlayingSampleQueues[index].clear();
- mPlayingSampleQueues[index] = null;
- mCacheManager.unregisterEvictListener(getTrackId(index));
- }
+ mSampleBuffer.deselectTrack(index);
}
@Override
public long getBufferedPositionUs() {
- Long result = null;
- for (CachedSampleQueue queue : mPlayingSampleQueues) {
- if (queue == null) {
- continue;
- }
- Long bufferedPositionUs = queue.getEndPositionUs();
- if (bufferedPositionUs == null) {
- continue;
- }
- if (result == null || result > bufferedPositionUs) {
- result = bufferedPositionUs;
- }
- }
- if (result == null) {
- return mLastBufferedPositionUs;
- } else {
- return (mLastBufferedPositionUs = result);
- }
+ return mSampleBuffer.getBufferedPositionUs();
}
@Override
public void seekTo(long positionUs) {
- // Seek video track first
- for (int i = 0; i < mPlayingSampleQueues.length; ++i) {
- CachedSampleQueue queue = mPlayingSampleQueues[i];
- if (queue == null) {
- continue;
- }
- seekIndividualTrack(i, positionUs);
- if (DEBUG) {
- Log.d(TAG, "start time = " + queue.getSourceStartPositionUs());
- }
- }
- mLastBufferedPositionUs = positionUs;
- }
-
- private void seekIndividualTrack(int index, long positionUs) {
- CachedSampleQueue queue = mPlayingSampleQueues[index];
- if (queue == null) {
- return;
- }
- queue.clear();
- queue.setSource(mCacheManager.getReadFile(getTrackId(index), positionUs));
- queue.maybeReadSample();
+ mSampleBuffer.seekTo(positionUs);
}
@Override
public int readSample(int track, SampleHolder sampleHolder) {
- CachedSampleQueue queue = mPlayingSampleQueues[track];
- Assert.assertNotNull(queue);
- queue.maybeReadSample();
- int result = queue.dequeueSample(sampleHolder);
- if (result != SampleSource.SAMPLE_READ && getEos()) {
- return SampleSource.END_OF_STREAM;
- }
- return result;
+ return mSampleBuffer.readSample(track, sampleHolder);
}
- public void cleanUpImpl() {
- mCacheManager.close();
- for (int i = 0; i < mIds.length; ++i) {
- mCacheManager.unregisterEvictListener(getTrackId(i));
- mCacheManager.clearTrack(getTrackId(i));
- }
- }
@Override
public boolean continueBuffering(long positionUs) {
- boolean hasSamples = true;
- mCurrentPlaybackPositionUs = positionUs;
- for (CachedSampleQueue queue : mPlayingSampleQueues) {
- if (queue == null) {
- continue;
- }
- queue.maybeReadSample();
- if (queue.isEmpty()) {
- hasSamples = false;
- }
- }
- return hasSamples;
- }
-
- // CacheEvictListener
- // TODO: Remove this. It will not be called.
- @Override
- public void onCacheEvicted(String id, long createdTimeMs) {
- mCacheListener.onCacheStartTimeChanged(
- createdTimeMs + TimeUnit.MICROSECONDS.toMillis(CHUNK_DURATION_US));
+ return mSampleBuffer.continueBuffering(positionUs);
}
}
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/SampleExtractor.java b/usbtuner/src/com/android/usbtuner/exoplayer/SampleExtractor.java
index b6559ad1..1a2e55d4 100644
--- a/usbtuner/src/com/android/usbtuner/exoplayer/SampleExtractor.java
+++ b/usbtuner/src/com/android/usbtuner/exoplayer/SampleExtractor.java
@@ -19,7 +19,6 @@ import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.MediaFormatHolder;
import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource;
-import com.google.android.exoplayer.TrackInfo;
import com.google.android.exoplayer.TrackRenderer;
import java.io.IOException;
@@ -28,7 +27,7 @@ import java.io.IOException;
* Extractor for reading track metadata and samples stored in tracks.
*
* <p>Call {@link #prepare} until it returns {@code true}, then access track metadata via
- * {@link #getTrackInfos} and {@link #getTrackMediaFormat}.
+ * {@link #getTrackFormats} and {@link #getTrackMediaFormat}.
*
* <p>Pass indices of tracks to read from to {@link #selectTrack}. A track can later be deselected
* by calling {@link #deselectTrack}. It is safe to select/deselect tracks after reading sample
@@ -48,7 +47,7 @@ public interface SampleExtractor {
boolean prepare() throws IOException;
/** Returns track information about all tracks that can be selected. */
- TrackInfo[] getTrackInfos();
+ MediaFormat[] getTrackFormats();
/** Selects the track at {@code index} for reading sample data. */
void selectTrack(int index);
@@ -77,7 +76,7 @@ public interface SampleExtractor {
void seekTo(long positionUs);
/** Stores the {@link MediaFormat} of {@code track}. */
- void getTrackMediaFormat(int track, MediaFormatHolder mediaFormatHolder);
+ void getTrackMediaFormat(int track, MediaFormatHolder outMediaFormatHolder);
/**
* Reads the next sample in the track at index {@code track} into {@code sampleHolder}, returning
@@ -92,9 +91,8 @@ public interface SampleExtractor {
* {@link SampleSource#END_OF_STREAM} if the last samples in all tracks have been read, or
* {@link SampleSource#NOTHING_READ} if the sample cannot be read immediately as it is not
* loaded.
- * @throws {@link IOException} thrown if the source can't be read
*/
- int readSample(int track, SampleHolder sampleHolder) throws IOException;
+ int readSample(int track, SampleHolder sampleHolder);
/** Releases resources associated with this extractor. */
void release();
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/ac3/Ac3TrackRenderer.java b/usbtuner/src/com/android/usbtuner/exoplayer/ac3/Ac3TrackRenderer.java
index 0e11b6f2..05327dba 100644
--- a/usbtuner/src/com/android/usbtuner/exoplayer/ac3/Ac3TrackRenderer.java
+++ b/usbtuner/src/com/android/usbtuner/exoplayer/ac3/Ac3TrackRenderer.java
@@ -22,9 +22,11 @@ import android.util.Log;
import com.google.android.exoplayer.CodecCounters;
import com.google.android.exoplayer.ExoPlaybackException;
+import com.google.android.exoplayer.MediaClock;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.MediaFormatHolder;
+import com.google.android.exoplayer.MediaFormatUtil;
import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.TrackRenderer;
@@ -39,7 +41,8 @@ import java.nio.ByteBuffer;
/**
* Decodes and renders AC3 audio.
*/
-public class Ac3TrackRenderer extends TrackRenderer implements Ac3Decoder.DecodeListener {
+public class Ac3TrackRenderer extends TrackRenderer implements Ac3Decoder.DecodeListener,
+ MediaClock {
public static final int MSG_SET_VOLUME = MediaCodecAudioTrackRenderer.MSG_SET_VOLUME;
public static final int MSG_SET_AUDIO_TRACK = MSG_SET_VOLUME + 1;
@@ -80,14 +83,14 @@ public class Ac3TrackRenderer extends TrackRenderer implements Ac3Decoder.Decode
private static final long ESTIMATED_TRACK_RENDERING_DELAY_US = 500000;
private final CodecCounters mCodecCounters;
- private final SampleSource mSource;
+ private final SampleSource.SampleSourceReader mSource;
private final SampleHolder mSampleHolder;
private final MediaFormatHolder mFormatHolder;
private final EventListener mEventListener;
private final Handler mEventHandler;
private final boolean mIsSoftware;
private final AudioTrackMonitor mMonitor;
- private final MediaClock mMediaClock;
+ private final AudioClock mAudioClock;
private MediaFormat mFormat;
private Ac3Decoder mDecoder;
@@ -106,52 +109,62 @@ public class Ac3TrackRenderer extends TrackRenderer implements Ac3Decoder.Decode
public Ac3TrackRenderer(SampleSource source, Handler eventHandler,
EventListener listener, boolean isSoftware) {
- mSource = source;
+ mSource = source.register();
mEventHandler = eventHandler;
mEventListener = listener;
mDecoder = Ac3Decoder.createAc3Decoder(isSoftware);
mIsSoftware = isSoftware;
+ mTrackIndex = -1;
mSampleHolder = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_DIRECT);
- mSampleHolder.replaceBuffer(DEFAULT_INPUT_BUFFER_SIZE);
+ mSampleHolder.ensureSpaceForWrite(DEFAULT_INPUT_BUFFER_SIZE);
mOutputBuffer = ByteBuffer.allocate(DEFAULT_OUTPUT_BUFFER_SIZE);
mFormatHolder = new MediaFormatHolder();
AUDIO_TRACK.restart();
mCodecCounters = new CodecCounters();
mMonitor = new AudioTrackMonitor();
- mMediaClock = new MediaClock();
+ mAudioClock = new AudioClock();
}
@Override
- protected boolean isTimeSource() {
- return true;
+ protected MediaClock getMediaClock() {
+ return this;
}
private static boolean handlesMimeType(String mimeType) {
- return mimeType.equals(MimeTypes.AUDIO_AC3) || mimeType.equals(MimeTypes.AUDIO_EC3);
+ return mimeType.equals(MimeTypes.AUDIO_AC3) || mimeType.equals(MimeTypes.AUDIO_E_AC3);
}
@Override
- protected int doPrepare(long positionUs) throws ExoPlaybackException {
- try {
- boolean sourcePrepared = mSource.prepare(positionUs);
- if (!sourcePrepared) {
- return TrackRenderer.STATE_UNPREPARED;
- }
- } catch (IOException e) {
- throw new ExoPlaybackException(e);
+ protected boolean doPrepare(long positionUs) throws ExoPlaybackException {
+ boolean sourcePrepared = mSource.prepare(positionUs);
+ if (!sourcePrepared) {
+ return false;
}
for (int i = 0; i < mSource.getTrackCount(); i++) {
- if (handlesMimeType(mSource.getTrackInfo(i).mimeType)) {
+ if (handlesMimeType(mSource.getFormat(i).mimeType)) {
mTrackIndex = i;
- return TrackRenderer.STATE_PREPARED;
+ return true;
}
}
- return TrackRenderer.STATE_IGNORE;
+ // TODO: Check this case. Source does not have the proper mime type.
+ return true;
+ }
+
+ @Override
+ protected int getTrackCount() {
+ return mTrackIndex < 0 ? 0 : 1;
+ }
+
+ @Override
+ protected MediaFormat getFormat(int track) {
+ Assertions.checkArgument(mTrackIndex != -1 && track == 0);
+ return mSource.getFormat(mTrackIndex);
}
@Override
- protected void onEnabled(long positionUs, boolean joining) {
+ protected void onEnabled(int track, long positionUs, boolean joining) {
+ Assertions.checkArgument(mTrackIndex != -1 && track == 0);
mSource.enable(mTrackIndex, positionUs);
mDecoder.startDecoder(this);
seekToInternal(positionUs);
@@ -171,7 +184,6 @@ public class Ac3TrackRenderer extends TrackRenderer implements Ac3Decoder.Decode
mSource.release();
}
-
@Override
protected boolean isEnded() {
return mOutputStreamEnded && AUDIO_TRACK.isEnded();
@@ -192,7 +204,7 @@ public class Ac3TrackRenderer extends TrackRenderer implements Ac3Decoder.Decode
mPreviousPositionUs = 0;
mCurrentPositionUs = Long.MIN_VALUE;
mInterpolatedTimeUs = Long.MIN_VALUE;
- mMediaClock.setPositionUs(positionUs);
+ mAudioClock.setPositionUs(positionUs);
}
@Override
@@ -209,13 +221,22 @@ public class Ac3TrackRenderer extends TrackRenderer implements Ac3Decoder.Decode
@Override
protected void onStarted() {
AUDIO_TRACK.play();
- mMediaClock.start();
+ mAudioClock.start();
}
@Override
protected void onStopped() {
AUDIO_TRACK.pause();
- mMediaClock.stop();
+ mAudioClock.stop();
+ }
+
+ @Override
+ protected void maybeThrowError() throws ExoPlaybackException {
+ try {
+ mSource.maybeThrowError();
+ } catch (IOException e) {
+ throw new ExoPlaybackException(e);
+ }
}
@Override
@@ -231,7 +252,7 @@ public class Ac3TrackRenderer extends TrackRenderer implements Ac3Decoder.Decode
throw new ExoPlaybackException("Much time has elapsed after EoS");
}
}
- boolean continueBuffering = mSource.continueBuffering(positionUs);
+ boolean continueBuffering = mSource.continueBuffering(mTrackIndex, positionUs);
if (mSourceStateReady != continueBuffering) {
mSourceStateReady = continueBuffering;
if (DEBUG) {
@@ -289,7 +310,7 @@ public class Ac3TrackRenderer extends TrackRenderer implements Ac3Decoder.Decode
private void readFormat() throws IOException, ExoPlaybackException {
int result = mSource.readData(mTrackIndex, mCurrentPositionUs,
- mFormatHolder, mSampleHolder, false);
+ mFormatHolder, mSampleHolder);
if (result == SampleSource.FORMAT_READ) {
onInputFormatChanged(mFormatHolder);
}
@@ -299,9 +320,8 @@ public class Ac3TrackRenderer extends TrackRenderer implements Ac3Decoder.Decode
throws ExoPlaybackException {
MediaFormat format = formatHolder.format;
if (mIsSoftware) {
- mFormat = MediaFormat.createAudioFormat(MimeTypes.AUDIO_RAW,
- MediaFormat.NO_VALUE, format.durationUs,
- format.channelCount, format.sampleRate, null);
+ mFormat = MediaFormatUtil.createAudioMediaFormat(MimeTypes.AUDIO_RAW, format.durationUs,
+ format.channelCount, format.sampleRate);
} else {
mFormat = format;
}
@@ -317,21 +337,25 @@ public class Ac3TrackRenderer extends TrackRenderer implements Ac3Decoder.Decode
return false;
}
+ long discontinuity = mSource.readDiscontinuity(mTrackIndex);
+ if (discontinuity != SampleSource.NO_DISCONTINUITY) {
+ // TODO: handle input discontinuity for trickplay.
+ Log.i(TAG, "Read discontinuity happened");
+ AUDIO_TRACK.handleDiscontinuity();
+ mPresentationTimeUs = discontinuity;
+ mPresentationCount = 0;
+ clearDecodeState();
+ return false;
+ }
+
mSampleHolder.data.clear();
mSampleHolder.size = 0;
int result = mSource.readData(mTrackIndex, mPresentationTimeUs, mFormatHolder,
- mSampleHolder, false);
+ mSampleHolder);
switch (result) {
case SampleSource.NOTHING_READ: {
return false;
}
- case SampleSource.DISCONTINUITY_READ: {
- // TODO: handle input discontinuity for trickplay.
- Log.i(TAG, "Read discontinuity happened");
- AUDIO_TRACK.handleDiscontinuity();
- clearDecodeState();
- return true;
- }
case SampleSource.FORMAT_READ: {
Log.i(TAG, "Format was read again");
onInputFormatChanged(mFormatHolder);
@@ -390,20 +414,20 @@ public class Ac3TrackRenderer extends TrackRenderer implements Ac3Decoder.Decode
@Override
protected long getDurationUs() {
- return mSource.getTrackInfo(mTrackIndex).durationUs;
+ return mSource.getFormat(mTrackIndex).durationUs;
}
@Override
protected long getBufferedPositionUs() {
long pos = mSource.getBufferedPositionUs();
return pos == UNKNOWN_TIME_US || pos == END_OF_TRACK_US
- ? pos : Math.max(pos, getCurrentPositionUs());
+ ? pos : Math.max(pos, getPositionUs());
}
@Override
- protected long getCurrentPositionUs() {
+ public long getPositionUs() {
if (!AUDIO_TRACK.isInitialized()) {
- return mMediaClock.getPositionUs();
+ return mAudioClock.getPositionUs();
} if (!AUDIO_TRACK.isEnabled()) {
if (mInterpolatedTimeUs > 0) {
return mInterpolatedTimeUs - ESTIMATED_TRACK_RENDERING_DELAY_US;
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/ac3/MediaClock.java b/usbtuner/src/com/android/usbtuner/exoplayer/ac3/AudioClock.java
index 5d28f4ef..ae6f2e91 100644
--- a/usbtuner/src/com/android/usbtuner/exoplayer/ac3/MediaClock.java
+++ b/usbtuner/src/com/android/usbtuner/exoplayer/ac3/AudioClock.java
@@ -25,7 +25,7 @@ import android.os.SystemClock;
* its time can be set and retrieved. When started, this clock is based on
* {@link SystemClock#elapsedRealtime()}.
*/
-/* package */ class MediaClock {
+/* package */ class AudioClock {
private boolean mStarted;
/**
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/ac3/AudioTrackWrapper.java b/usbtuner/src/com/android/usbtuner/exoplayer/ac3/AudioTrackWrapper.java
index e075dcc0..eb5efcb9 100644
--- a/usbtuner/src/com/android/usbtuner/exoplayer/ac3/AudioTrackWrapper.java
+++ b/usbtuner/src/com/android/usbtuner/exoplayer/ac3/AudioTrackWrapper.java
@@ -83,14 +83,12 @@ public class AudioTrackWrapper {
if (!mIsEnabled) {
return true;
}
- return !mAudioTrack.hasPendingData() || !mAudioTrack.hasEnoughDataToBeginPlayback();
+ return !mAudioTrack.hasPendingData();
}
public boolean isReady() {
- if (!mIsEnabled) {
- return false;
- }
- return mAudioTrack.hasPendingData();
+ // In the case of not playing actual audio data, Audio track is always ready.
+ return !mIsEnabled || mAudioTrack.hasPendingData();
}
public void play() {
@@ -118,7 +116,15 @@ public class AudioTrackWrapper {
if (!mIsEnabled) {
return;
}
- mAudioTrack.reconfigure(format);
+ // TODO: Handle non-AC3 or non-passthrough audio.
+ if (MediaFormat.MIMETYPE_AUDIO_AC3.equalsIgnoreCase(format.getString(MediaFormat.KEY_MIME))
+ && format.getInteger(MediaFormat.KEY_CHANNEL_COUNT) == 1) {
+ // Workarounds b/25955476 .
+ // Since all devices and platforms does not support AC3 mono passthrough,
+ // It is safe to fake AC3 mono as AC3 stereo which is default passthrough mode.
+ format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 2);
+ }
+ mAudioTrack.configure(format, true);
}
public void handleDiscontinuity() {
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/CacheManager.java b/usbtuner/src/com/android/usbtuner/exoplayer/cache/CacheManager.java
index c52a0a44..0441f288 100644
--- a/usbtuner/src/com/android/usbtuner/exoplayer/CacheManager.java
+++ b/usbtuner/src/com/android/usbtuner/exoplayer/cache/CacheManager.java
@@ -14,23 +14,28 @@
* limitations under the License.
*/
-package com.android.usbtuner.exoplayer;
+package com.android.usbtuner.exoplayer.cache;
import android.media.MediaFormat;
+import android.os.ConditionVariable;
import android.os.HandlerThread;
-import android.os.Looper;
import android.support.annotation.VisibleForTesting;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
+import com.google.android.exoplayer.SampleHolder;
+
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -51,6 +56,7 @@ public class CacheManager {
// Constants for the disk write speed checking
private static final long MINIMUM_WRITE_SIZE_FOR_SPEED_CHECK =
10L * 1024 * 1024; // Checks for every 10M disk write
+ private static final int MINIMUM_SAMPLE_SIZE_FOR_SPEED_CHECK = 15 * 1024;
private static final int MAXIMUM_SPEED_CHECK_COUNT = 5; // Checks only 5 times
private static final int MINIMUM_DISK_WRITE_SPEED_MBPS = 3; // 3 Megabytes per second
@@ -75,6 +81,7 @@ public class CacheManager {
};
private volatile boolean mClosed = false;
+ private int mMinSampleSizeForSpeedCheck = MINIMUM_SAMPLE_SIZE_FOR_SPEED_CHECK;
private long mTotalWriteSize;
private long mTotalWriteTimeNs;
private volatile int mSpeedCheckCount;
@@ -90,6 +97,88 @@ public class CacheManager {
}
/**
+ * Handles I/O
+ * between CacheManager and {@link com.android.usbtuner.exoplayer.SampleExtractor}.
+ */
+ public interface SampleBuffer {
+
+ /**
+ * Initializes SampleBuffer.
+ * @param Ids track identifiers for storage read/write.
+ * @param mediaFormats meta-data for each track, this will be saved to storage in recording.
+ * @throws IOException
+ */
+ void init(@NonNull List<String> Ids, @Nullable List<MediaFormat> mediaFormats)
+ throws IOException;
+
+ /**
+ * Selects the track {@code index} for reading sample data.
+ */
+ void selectTrack(int index);
+
+ /**
+ * Deselects the track at {@code index},
+ * so that no more samples will be read from the track.
+ */
+ void deselectTrack(int index);
+
+ /**
+ * Writes sample to storage.
+ *
+ * @param index track index
+ * @param sample sample to write at storage
+ * @param conditionVariable notifies the completion of writing sample.
+ * @throws IOException
+ */
+ void writeSample(int index, SampleHolder sample, ConditionVariable conditionVariable)
+ throws IOException;
+
+ /**
+ * Checks whether storage write speed is slow.
+ */
+ boolean isWriteSpeedSlow(int sampleSize, long writeDurationNs);
+
+ /**
+ * Handles when write speed is slow.
+ */
+ void handleWriteSpeedSlow();
+
+ /**
+ * Sets the flag when EoS was met.
+ */
+ void setEos();
+
+ /**
+ * Reads the next sample in the track at index {@code track} into {@code sampleHolder},
+ * returning {@link com.google.android.exoplayer.SampleSource#SAMPLE_READ}
+ * if it is available.
+ * If the next sample is not available,
+ * returns {@link com.google.android.exoplayer.SampleSource#NOTHING_READ}.
+ */
+ int readSample(int index, SampleHolder outSample);
+
+ /**
+ * Seeks to the specified time in microseconds.
+ */
+ void seekTo(long positionUs);
+
+ /**
+ * Returns an estimate of the position up to which data is buffered.
+ */
+ long getBufferedPositionUs();
+
+ /**
+ * Returns whether there is buffered data.
+ */
+ boolean continueBuffering(long positionUs);
+
+ /**
+ * Cleans up and releases everything.
+ */
+ void release();
+ }
+
+ /**
* Storage configuration and policy manager for {@link CacheManager}
*/
public interface StorageManager {
@@ -251,8 +340,7 @@ public class CacheManager {
}
/**
- * Loads a track using
- * {@link com.android.usbtuner.exoplayer.CacheManager.StorageManager}.
+ * Loads a track using {@link CacheManager.StorageManager}.
*
* @param trackId the name of the track.
* @param samplePool {@link SamplePool} for the fast creation of samples.
@@ -347,8 +435,7 @@ public class CacheManager {
/**
* Reads track information which includes {@link MediaFormat}.
*
- * @return returns all track information which is found by
- * {@link com.android.usbtuner.exoplayer.CacheManager.StorageManager}.
+ * @return returns all track information which is found by {@link CacheManager.StorageManager}.
* @throws {@link java.io.IOException}
*/
public ArrayList<Pair<String, MediaFormat>> readTrackInfoFiles() throws IOException {
@@ -438,8 +525,10 @@ public class CacheManager {
* Adds a disk write sample size to calculate the average disk write bandwidth.
*/
public void addWriteStat(long size, long timeNs) {
- mTotalWriteSize += size;
- mTotalWriteTimeNs += timeNs;
+ if (size >= mMinSampleSizeForSpeedCheck) {
+ mTotalWriteSize += size;
+ mTotalWriteTimeNs += timeNs;
+ }
}
/**
@@ -496,4 +585,13 @@ public class CacheManager {
public boolean hasSpeedCheckDone() {
return mSpeedCheckCount > 0;
}
+
+ /**
+ * Sets minimum sample size for write speed check.
+ * @param sampleSize minimum sample size for write speed check.
+ */
+ @VisibleForTesting
+ public void setMinimumSampleSizeForSpeedCheck(int sampleSize) {
+ mMinSampleSizeForSpeedCheck = sampleSize;
+ }
}
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/DvrStorageManager.java b/usbtuner/src/com/android/usbtuner/exoplayer/cache/DvrStorageManager.java
index 4ef86e29..b2b601ff 100644
--- a/usbtuner/src/com/android/usbtuner/exoplayer/DvrStorageManager.java
+++ b/usbtuner/src/com/android/usbtuner/exoplayer/cache/DvrStorageManager.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.usbtuner.exoplayer;
+package com.android.usbtuner.exoplayer.cache;
import android.media.MediaFormat;
import android.util.Pair;
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/cache/RecordingSampleBuffer.java b/usbtuner/src/com/android/usbtuner/exoplayer/cache/RecordingSampleBuffer.java
new file mode 100644
index 00000000..324fba82
--- /dev/null
+++ b/usbtuner/src/com/android/usbtuner/exoplayer/cache/RecordingSampleBuffer.java
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.usbtuner.exoplayer.cache;
+
+import android.media.MediaCodec;
+import android.media.MediaFormat;
+import android.os.ConditionVariable;
+import android.support.annotation.IntDef;
+import android.util.Log;
+import android.util.Pair;
+
+import com.google.android.exoplayer.C;
+import com.google.android.exoplayer.SampleHolder;
+import com.google.android.exoplayer.SampleSource;
+import com.google.android.exoplayer.util.MimeTypes;
+import com.android.usbtuner.tvinput.PlaybackCacheListener;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.Assert;
+
+/**
+ * Handles I/O between {@link com.android.usbtuner.exoplayer.SampleExtractor} and
+ * {@link CacheManager}.Reads & writes samples from/to {@link SampleCache} which is backed
+ * by physical storage.
+ */
+public class RecordingSampleBuffer implements CacheManager.SampleBuffer,
+ CacheManager.EvictListener {
+ private static final String TAG = "RecordingSampleBuffer";
+ private static final boolean DEBUG = false;
+
+ @IntDef({CACHE_REASON_LIVE_PLAYBACK, CACHE_REASON_RECORDED_PLAYBACK, CACHE_REASON_RECORDING})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CacheReason {}
+
+ /**
+ * A cache reason for live-stream playback.
+ */
+ public static final int CACHE_REASON_LIVE_PLAYBACK = 0;
+
+ /**
+ * A cache reason for playback of a recorded program.
+ */
+ public static final int CACHE_REASON_RECORDED_PLAYBACK = 1;
+
+ /**
+ * A cache reason for recording a program.
+ */
+ public static final int CACHE_REASON_RECORDING = 2;
+
+ private static final long CACHE_WRITE_TIMEOUT_MS = 10 * 1000; // 10 seconds
+ private static final long CHUNK_DURATION_US = TimeUnit.MILLISECONDS.toMicros(500);
+ private static final long LIVE_THRESHOLD_US = TimeUnit.SECONDS.toMicros(1);
+
+ private final CacheManager mCacheManager;
+ private final PlaybackCacheListener mCacheListener;
+ private final int mCacheReason;
+
+ private int mTrackCount;
+ private List<String> mIds;
+ private List<MediaFormat> mMediaFormats;
+ private volatile long mCacheDurationUs = 0;
+ private long[] mCacheEndPositionUs;
+ // SampleCache to append the latest live sample.
+ private SampleCache[] mSampleCaches;
+ private CachedSampleQueue[] mPlayingSampleQueues;
+ private final SamplePool mSamplePool = new SamplePool();
+ private long mLastBufferedPositionUs = C.UNKNOWN_TIME_US;
+ private long mCurrentPlaybackPositionUs = 0;
+ private boolean mEos = false;
+
+ private class CachedSampleQueue extends SampleQueue {
+ private SampleCache mCache = null;
+
+ public CachedSampleQueue(SamplePool samplePool) {
+ super(samplePool);
+ }
+
+ public void setSource(SampleCache newCache) {
+ for (SampleCache cache = mCache; cache != null; cache = cache.getNext()) {
+ cache.clear();
+ cache.close();
+ }
+ mCache = newCache;
+ for (SampleCache cache = mCache; cache != null; cache = cache.getNext()) {
+ cache.resetRead();
+ }
+ }
+
+ public boolean maybeReadSample() {
+ if (isDurationGreaterThan(CHUNK_DURATION_US)) {
+ return false;
+ }
+ SampleHolder sample = mCache.maybeReadSample();
+ if (sample == null) {
+ if (!mCache.canReadMore() && mCache.getNext() != null) {
+ mCache.clear();
+ mCache.close();
+ mCache = mCache.getNext();
+ mCache.resetRead();
+ return maybeReadSample();
+ } else {
+ if (mCacheReason == CACHE_REASON_RECORDED_PLAYBACK
+ && !mCache.canReadMore() && mCache.getNext() == null) {
+ // At the end of the recorded playback.
+ setEos();
+ }
+ return false;
+ }
+ } else {
+ queueSample(sample);
+ return true;
+ }
+ }
+
+ public int dequeueSample(SampleHolder sample) {
+ maybeReadSample();
+ return super.dequeueSample(sample);
+ }
+
+ @Override
+ public void clear() {
+ super.clear();
+ for (SampleCache cache = mCache; cache != null; cache = cache.getNext()) {
+ cache.clear();
+ cache.close();
+ }
+ mCache = null;
+ }
+
+ public long getSourceStartPositionUs() {
+ return mCache == null ? -1 : mCache.getStartPositionUs();
+ }
+ }
+
+ /**
+ * Creates {@link com.android.usbtuner.exoplayer.cache.CacheManager.SampleBuffer} with
+ * cached I/O backed by physical storage (e.g. trickplay,recording,recorded-playback).
+ *
+ * @param cacheManager
+ * @param cacheListener
+ * @param enableTrickplay {@code true} when trickplay should be enabled
+ * @param cacheReason the reason for caching samples {@link RecordingSampleBuffer.CacheReason}
+ */
+ public RecordingSampleBuffer(CacheManager cacheManager, PlaybackCacheListener cacheListener,
+ boolean enableTrickplay, @CacheReason int cacheReason) {
+ mCacheManager = cacheManager;
+ mCacheListener = cacheListener;
+ if (cacheListener != null) {
+ cacheListener.onCacheStateChanged(enableTrickplay);
+ }
+ mCacheReason = cacheReason;
+ }
+
+ private String getTrackId(int index) {
+ return mIds.get(index);
+ }
+
+ @Override
+ public synchronized void init(List<String> ids, List<MediaFormat> mediaFormats)
+ throws IOException {
+ mTrackCount = ids.size();
+ if (mTrackCount <= 0) {
+ throw new IOException("No tracks to initialize");
+ }
+ mIds = ids;
+ if (mCacheReason == CACHE_REASON_RECORDING && mediaFormats == null) {
+ throw new IOException("MediaFormat is not provided.");
+ }
+ mMediaFormats = mediaFormats;
+ mSampleCaches = new SampleCache[mTrackCount];
+ mPlayingSampleQueues = new CachedSampleQueue[mTrackCount];
+ mCacheEndPositionUs = new long[mTrackCount];
+ for (int i = 0; i < mTrackCount; i++) {
+ if (mCacheReason != CACHE_REASON_RECORDED_PLAYBACK) {
+ mSampleCaches[i] = mCacheManager.createNewWriteFile(getTrackId(i), 0, mSamplePool);
+ mPlayingSampleQueues[i] = null;
+ mCacheEndPositionUs[i] = CHUNK_DURATION_US;
+ } else {
+ mCacheManager.loadTrackFormStorage(mIds.get(i), mSamplePool);
+ }
+ }
+ }
+
+ private boolean isLiveLocked(long positionUs) {
+ Long livePositionUs = null;
+ for (SampleCache cache : mSampleCaches) {
+ if (livePositionUs == null || livePositionUs < cache.getEndPositionUs()) {
+ livePositionUs = cache.getEndPositionUs();
+ }
+ }
+ return (livePositionUs == null
+ || Math.abs(livePositionUs - positionUs) < LIVE_THRESHOLD_US);
+ }
+
+ private void seekIndividualTrackLocked(int index, long positionUs, boolean isLive) {
+ CachedSampleQueue queue = mPlayingSampleQueues[index];
+ if (queue == null) {
+ return;
+ }
+ queue.clear();
+ if (isLive) {
+ queue.setSource(mSampleCaches[index]);
+ } else {
+ queue.setSource(mCacheManager.getReadFile(getTrackId(index), positionUs));
+ }
+ queue.maybeReadSample();
+ }
+
+ @Override
+ public synchronized void selectTrack(int index) {
+ if (mPlayingSampleQueues[index] == null) {
+ String trackId = getTrackId(index);
+ mPlayingSampleQueues[index] = new CachedSampleQueue(mSamplePool);
+ mCacheManager.registerEvictListener(trackId, this);
+ seekIndividualTrackLocked(index, mCurrentPlaybackPositionUs,
+ mCacheReason != CACHE_REASON_RECORDED_PLAYBACK && isLiveLocked(
+ mCurrentPlaybackPositionUs));
+ mPlayingSampleQueues[index].maybeReadSample();
+ }
+ }
+
+ @Override
+ public synchronized void deselectTrack(int index) {
+ if (mPlayingSampleQueues[index] != null) {
+ mPlayingSampleQueues[index].clear();
+ mPlayingSampleQueues[index] = null;
+ mCacheManager.unregisterEvictListener(getTrackId(index));
+ }
+ }
+
+ @Override
+ public void writeSample(int index, SampleHolder sample,
+ ConditionVariable conditionVariable) throws IOException {
+ synchronized (this) {
+ SampleCache cache = mSampleCaches[index];
+ if ((sample.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) {
+ if (sample.timeUs > mCacheDurationUs) {
+ mCacheDurationUs = sample.timeUs;
+ }
+ if (sample.timeUs >= mCacheEndPositionUs[index]) {
+ try {
+ SampleCache nextCache = mCacheManager.createNewWriteFile(
+ getTrackId(index), mCacheEndPositionUs[index], mSamplePool);
+ cache.finishWrite(nextCache);
+ mSampleCaches[index] = cache = nextCache;
+ mCacheEndPositionUs[index] =
+ ((sample.timeUs / CHUNK_DURATION_US) + 1) * CHUNK_DURATION_US;
+ } catch (IOException e) {
+ cache.finishWrite(null);
+ throw e;
+ }
+ }
+ }
+ cache.writeSample(sample, conditionVariable);
+ }
+
+ if (!conditionVariable.block(CACHE_WRITE_TIMEOUT_MS)) {
+ Log.e(TAG, "Error: Serious delay on writing cache");
+ conditionVariable.block();
+ }
+ }
+
+ @Override
+ public boolean isWriteSpeedSlow(int sampleSize, long writeDurationNs) {
+ if (mCacheReason == CACHE_REASON_RECORDED_PLAYBACK) {
+ return false;
+ }
+ mCacheManager.addWriteStat(sampleSize, writeDurationNs);
+ return mCacheManager.isWriteSlow();
+ }
+
+ @Override
+ public void handleWriteSpeedSlow() {
+ Log.w(TAG, "Disk is too slow for trickplay. Disable trickplay.");
+ mCacheManager.disable();
+ mCacheListener.onDiskTooSlow();
+ }
+
+ @Override
+ public synchronized void setEos() {
+ mEos = true;
+ }
+
+ private synchronized boolean reachedEos() {
+ return mEos;
+ }
+
+ @Override
+ public synchronized int readSample(int track, SampleHolder sampleHolder) {
+ CachedSampleQueue queue = mPlayingSampleQueues[track];
+ Assert.assertNotNull(queue);
+ queue.maybeReadSample();
+ int result = queue.dequeueSample(sampleHolder);
+ if (result != SampleSource.SAMPLE_READ && reachedEos()) {
+ return SampleSource.END_OF_STREAM;
+ }
+ return result;
+ }
+
+ @Override
+ public synchronized void seekTo(long positionUs) {
+ boolean isLive = mCacheReason != CACHE_REASON_RECORDED_PLAYBACK && isLiveLocked(positionUs);
+
+ // Seek video track first
+ for (int i = 0; i < mPlayingSampleQueues.length; ++i) {
+ CachedSampleQueue queue = mPlayingSampleQueues[i];
+ if (queue == null) {
+ continue;
+ }
+ seekIndividualTrackLocked(i, positionUs, isLive);
+ if (DEBUG) {
+ Log.d(TAG, "start time = " + queue.getSourceStartPositionUs());
+ }
+ }
+ mLastBufferedPositionUs = positionUs;
+ }
+
+ @Override
+ public synchronized long getBufferedPositionUs() {
+ Long result = null;
+ for (CachedSampleQueue queue : mPlayingSampleQueues) {
+ if (queue == null) {
+ continue;
+ }
+ Long bufferedPositionUs = queue.getEndPositionUs();
+ if (bufferedPositionUs == null) {
+ continue;
+ }
+ if (result == null || result > bufferedPositionUs) {
+ result = bufferedPositionUs;
+ }
+ }
+ if (result == null) {
+ return mLastBufferedPositionUs;
+ } else {
+ return (mLastBufferedPositionUs = result);
+ }
+ }
+
+ @Override
+ public synchronized boolean continueBuffering(long positionUs) {
+ boolean hasSamples = true;
+ mCurrentPlaybackPositionUs = positionUs;
+ for (CachedSampleQueue queue : mPlayingSampleQueues) {
+ if (queue == null) {
+ continue;
+ }
+ queue.maybeReadSample();
+ if (queue.isEmpty()) {
+ hasSamples = false;
+ }
+ }
+ return hasSamples;
+ }
+
+ @Override
+ public synchronized void release() {
+ if (mSampleCaches == null) {
+ return;
+ }
+ if (mCacheReason == CACHE_REASON_RECORDED_PLAYBACK) {
+ mCacheManager.close();
+ }
+ for (int i = 0; i < mTrackCount; ++i) {
+ if (mCacheReason != CACHE_REASON_RECORDED_PLAYBACK) {
+ mSampleCaches[i].finishWrite(null);
+ }
+ mCacheManager.unregisterEvictListener(getTrackId(i));
+ }
+ if (mCacheReason == CACHE_REASON_RECORDING && mTrackCount > 0) {
+ // Saves meta information for recording.
+ Pair<String, android.media.MediaFormat> audio = null, video = null;
+ for (int i = 0; i < mTrackCount; ++i) {
+ MediaFormat mediaFormat = mMediaFormats.get(i);
+ String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
+ mediaFormat.setLong(android.media.MediaFormat.KEY_DURATION, mCacheDurationUs);
+ if (MimeTypes.isAudio(mime)) {
+ audio = new Pair<>(getTrackId(i), mediaFormat);
+ }
+ else if (MimeTypes.isVideo(mime)) {
+ video = new Pair<>(getTrackId(i), mediaFormat);
+ }
+ }
+ mCacheManager.writeMetaFiles(audio, video);
+ }
+
+ for (int i = 0; i < mTrackCount; ++i) {
+ mCacheManager.clearTrack(getTrackId(i));
+ }
+ }
+
+ // CacheEvictListener
+ @Override
+ public void onCacheEvicted(String id, long createdTimeMs) {
+ if (mCacheListener != null) {
+ mCacheListener.onCacheStartTimeChanged(
+ createdTimeMs + TimeUnit.MICROSECONDS.toMillis(CHUNK_DURATION_US));
+ }
+ }
+}
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/SampleCache.java b/usbtuner/src/com/android/usbtuner/exoplayer/cache/SampleCache.java
index f6c06c12..52b7daa9 100644
--- a/usbtuner/src/com/android/usbtuner/exoplayer/SampleCache.java
+++ b/usbtuner/src/com/android/usbtuner/exoplayer/cache/SampleCache.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.usbtuner.exoplayer;
+package com.android.usbtuner.exoplayer.cache;
import android.os.ConditionVariable;
import android.os.Handler;
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/SamplePool.java b/usbtuner/src/com/android/usbtuner/exoplayer/cache/SamplePool.java
index 20d12d7e..2c18283e 100644
--- a/usbtuner/src/com/android/usbtuner/exoplayer/SamplePool.java
+++ b/usbtuner/src/com/android/usbtuner/exoplayer/cache/SamplePool.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.usbtuner.exoplayer;
+package com.android.usbtuner.exoplayer.cache;
import com.google.android.exoplayer.SampleHolder;
@@ -33,7 +33,7 @@ public class SamplePool {
public synchronized SampleHolder acquireSample(int size) {
if (mSamplePool.isEmpty()) {
SampleHolder sample = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_NORMAL);
- sample.replaceBuffer(size);
+ sample.ensureSpaceForWrite(size);
return sample;
}
SampleHolder smallestSufficientSample = null;
@@ -55,7 +55,7 @@ public class SamplePool {
// If there's no sufficient sample, grab the maximum sample and resize it to size.
if (sampleFromPool == null) {
sampleFromPool = maxSample;
- sampleFromPool.replaceBuffer(size);
+ sampleFromPool.ensureSpaceForWrite(size);
}
mSamplePool.remove(sampleFromPool);
return sampleFromPool;
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/SampleQueue.java b/usbtuner/src/com/android/usbtuner/exoplayer/cache/SampleQueue.java
index 47f961bb..2bddd2c0 100644
--- a/usbtuner/src/com/android/usbtuner/exoplayer/SampleQueue.java
+++ b/usbtuner/src/com/android/usbtuner/exoplayer/cache/SampleQueue.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.usbtuner.exoplayer;
+package com.android.usbtuner.exoplayer.cache;
import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource;
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/SimpleSampleSourceExtractor.java b/usbtuner/src/com/android/usbtuner/exoplayer/cache/SimpleSampleBuffer.java
index 8e954036..29f06aa4 100644
--- a/usbtuner/src/com/android/usbtuner/exoplayer/SimpleSampleSourceExtractor.java
+++ b/usbtuner/src/com/android/usbtuner/exoplayer/cache/SimpleSampleBuffer.java
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.usbtuner.exoplayer;
+package com.android.usbtuner.exoplayer.cache;
-import android.media.MediaDataSource;
-import android.media.MediaExtractor;
+import android.media.MediaFormat;
import android.os.ConditionVariable;
import com.google.android.exoplayer.C;
@@ -25,41 +24,32 @@ import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource;
import com.android.usbtuner.tvinput.PlaybackCacheListener;
+import java.io.IOException;
+import java.util.List;
+
import junit.framework.Assert;
/**
- * Extracts from samples and keeps them in the memory.
+ * Handles I/O for {@link com.android.usbtuner.exoplayer.SampleExtractor} when
+ * physical storage based cache is not used. Trickplay is disabled.
*/
-public class SimpleSampleSourceExtractor extends BaseSampleSourceExtractor {
+public class SimpleSampleBuffer implements CacheManager.SampleBuffer {
private final SamplePool mSamplePool = new SamplePool();
private SampleQueue[] mPlayingSampleQueues;
private long mLastBufferedPositionUs = C.UNKNOWN_TIME_US;
- public SimpleSampleSourceExtractor(MediaDataSource source,
- PlaybackCacheListener cacheListener) {
- super(source);
- cacheListener.onCacheStateChanged(false); // Disable trickplay
- }
+ private volatile boolean mEos;
- public void queueSample(int index, SampleHolder sample, ConditionVariable conditionVariable) {
- sample.data.position(0).limit(sample.size);
- SampleHolder sampleToQueue = mSamplePool.acquireSample(sample.size);
- sampleToQueue.size = sample.size;
- sampleToQueue.clearData();
- sampleToQueue.data.put(sample.data);
- sampleToQueue.timeUs = sample.timeUs;
- sampleToQueue.flags = sample.flags;
-
- synchronized (this) {
- if (mPlayingSampleQueues[index] != null) {
- mPlayingSampleQueues[index].queueSample(sampleToQueue);
- }
+ public SimpleSampleBuffer(PlaybackCacheListener cacheListener) {
+ if (cacheListener != null) {
+ // Disables trickplay.
+ cacheListener.onCacheStateChanged(false);
}
- Thread.yield();
}
@Override
- public void initOnPrepareLocked(int trackCount) {
+ public synchronized void init(List<String> ids, List<MediaFormat> mediaFormats) {
+ int trackCount = ids.size();
mPlayingSampleQueues = new SampleQueue[trackCount];
for (int i = 0; i < trackCount; i++) {
mPlayingSampleQueues[i] = null;
@@ -67,6 +57,15 @@ public class SimpleSampleSourceExtractor extends BaseSampleSourceExtractor {
}
@Override
+ public void setEos() {
+ mEos = true;
+ }
+
+ private boolean reachedEos() {
+ return mEos;
+ }
+
+ @Override
public void selectTrack(int index) {
synchronized (this) {
if (mPlayingSampleQueues[index] == null) {
@@ -88,59 +87,88 @@ public class SimpleSampleSourceExtractor extends BaseSampleSourceExtractor {
}
@Override
- public long getBufferedPositionUs() {
- synchronized (this) {
- Long result = null;
- for (SampleQueue queue : mPlayingSampleQueues) {
- if (queue == null) {
- continue;
- }
- Long bufferedPositionUs = queue.getEndPositionUs();
- if (bufferedPositionUs == null) {
- continue;
- }
- if (result == null || result > bufferedPositionUs) {
- result = bufferedPositionUs;
- }
+ public synchronized long getBufferedPositionUs() {
+ Long result = null;
+ for (SampleQueue queue : mPlayingSampleQueues) {
+ if (queue == null) {
+ continue;
}
- if (result == null) {
- return mLastBufferedPositionUs;
- } else {
- return (mLastBufferedPositionUs = result);
+ Long bufferedPositionUs = queue.getEndPositionUs();
+ if (bufferedPositionUs == null) {
+ continue;
+ }
+ if (result == null || result > bufferedPositionUs) {
+ result = bufferedPositionUs;
}
}
+ if (result == null) {
+ return mLastBufferedPositionUs;
+ } else {
+ return (mLastBufferedPositionUs = result);
+ }
}
@Override
- public int readSample(int track, SampleHolder sampleHolder) {
+ public synchronized int readSample(int track, SampleHolder sampleHolder) {
+ SampleQueue queue = mPlayingSampleQueues[track];
+ Assert.assertNotNull(queue);
+ int result = queue.dequeueSample(sampleHolder);
+ if (result != SampleSource.SAMPLE_READ && reachedEos()) {
+ return SampleSource.END_OF_STREAM;
+ }
+ return result;
+ }
+
+ @Override
+ public void writeSample(int index, SampleHolder sample,
+ ConditionVariable conditionVariable) throws IOException {
+ sample.data.position(0).limit(sample.size);
+ SampleHolder sampleToQueue = mSamplePool.acquireSample(sample.size);
+ sampleToQueue.size = sample.size;
+ sampleToQueue.clearData();
+ sampleToQueue.data.put(sample.data);
+ sampleToQueue.timeUs = sample.timeUs;
+ sampleToQueue.flags = sample.flags;
+
synchronized (this) {
- SampleQueue queue = mPlayingSampleQueues[track];
- Assert.assertNotNull(queue);
- int result = queue.dequeueSample(sampleHolder);
- if (result != SampleSource.SAMPLE_READ && getEos()) {
- return SampleSource.END_OF_STREAM;
+ if (mPlayingSampleQueues[index] != null) {
+ mPlayingSampleQueues[index].queueSample(sampleToQueue);
}
- return result;
}
}
@Override
- public boolean continueBuffering(long positionUs) {
- synchronized (this) {
- for (SampleQueue queue : mPlayingSampleQueues) {
- if (queue == null) {
- continue;
- }
- if (queue.isEmpty()) {
- return false;
- }
+ public boolean isWriteSpeedSlow(int sampleSize, long durationNs) {
+ // Since SimpleSampleBuffer write samples only to memory (not to physical storage),
+ // write speed is always fine.
+ return false;
+ }
+
+ @Override
+ public void handleWriteSpeedSlow() {
+ // no-op
+ }
+
+ @Override
+ public synchronized boolean continueBuffering(long positionUs) {
+ for (SampleQueue queue : mPlayingSampleQueues) {
+ if (queue == null) {
+ continue;
+ }
+ if (queue.isEmpty()) {
+ return false;
}
- return true;
}
+ return true;
}
@Override
public void seekTo(long positionUs) {
// Not used.
}
+
+ @Override
+ public void release() {
+ // Not used.
+ }
}
diff --git a/usbtuner/src/com/android/usbtuner/exoplayer/TrickplayStorageManager.java b/usbtuner/src/com/android/usbtuner/exoplayer/cache/TrickplayStorageManager.java
index 57af8eba..801ae534 100644
--- a/usbtuner/src/com/android/usbtuner/exoplayer/TrickplayStorageManager.java
+++ b/usbtuner/src/com/android/usbtuner/exoplayer/cache/TrickplayStorageManager.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.usbtuner.exoplayer;
+package com.android.usbtuner.exoplayer.cache;
import android.content.Context;
import android.media.MediaFormat;
diff --git a/usbtuner/src/com/android/usbtuner/setup/ScanResultFragment.java b/usbtuner/src/com/android/usbtuner/setup/ScanResultFragment.java
index 1c32aeb8..bf2ca477 100644
--- a/usbtuner/src/com/android/usbtuner/setup/ScanResultFragment.java
+++ b/usbtuner/src/com/android/usbtuner/setup/ScanResultFragment.java
@@ -16,6 +16,7 @@
package com.android.usbtuner.setup;
+import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import android.support.annotation.NonNull;
@@ -64,11 +65,9 @@ public class ScanResultFragment extends SetupMultiPaneFragment {
private int mChannelCountOnPreference;
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- mChannelCountOnPreference = UsbTunerPreferences
- .getScannedChannelCount(getActivity().getApplicationContext());
- return super.onCreateView(inflater, container, savedInstanceState);
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ mChannelCountOnPreference = UsbTunerPreferences.getScannedChannelCount(context);
}
@NonNull
diff --git a/usbtuner/src/com/android/usbtuner/tvinput/BaseTunerTvInputService.java b/usbtuner/src/com/android/usbtuner/tvinput/BaseTunerTvInputService.java
index cb25cfec..a24b6146 100644
--- a/usbtuner/src/com/android/usbtuner/tvinput/BaseTunerTvInputService.java
+++ b/usbtuner/src/com/android/usbtuner/tvinput/BaseTunerTvInputService.java
@@ -16,44 +16,40 @@
package com.android.usbtuner.tvinput;
-import android.os.Handler;
+import android.media.tv.TvInputService;
import android.util.Log;
import com.google.android.exoplayer.audio.AudioCapabilities;
import com.google.android.exoplayer.audio.AudioCapabilitiesReceiver;
-import com.android.tv.common.recording.RecordingTvInputService;
-import com.android.tv.common.recording.TvRecording;
-import com.android.usbtuner.exoplayer.CacheManager;
+import com.android.usbtuner.exoplayer.cache.CacheManager;
import java.util.Collections;
import java.util.Set;
import java.util.WeakHashMap;
/**
- * {@link BaseTunerTvInputService} serves TV channels coming from a usb tuner device.
+ * {@link BaseTunerTvInputService} serves TV channels coming from a tuner device.
*/
-public abstract class BaseTunerTvInputService extends RecordingTvInputService
+public abstract class BaseTunerTvInputService extends TvInputService
implements AudioCapabilitiesReceiver.Listener {
private static final String TAG = "BaseTunerTvInputService";
private static final boolean DEBUG = false;
// WeakContainer for {@link TvInputSessionImpl}
- private final Set<TvInputSessionImpl> mTvInputSessions = Collections.newSetFromMap(
- new WeakHashMap<TvInputSessionImpl, Boolean>());
+ private final Set<TunerSession> mTunerSessions = Collections.newSetFromMap(
+ new WeakHashMap<TunerSession, Boolean>());
private ChannelDataManager mChannelDataManager;
private AudioCapabilitiesReceiver mAudioCapabilitiesReceiver;
private AudioCapabilities mAudioCapabilities;
- protected CacheManager mCacheManager;
+ private CacheManager mCacheManager;
@Override
public void onCreate() {
super.onCreate();
- if (DEBUG) {
- Log.d(TAG, "onCreate");
- }
+ if (DEBUG) Log.d(TAG, "onCreate");
mChannelDataManager = new ChannelDataManager(getApplicationContext());
mAudioCapabilitiesReceiver = new AudioCapabilitiesReceiver(getApplicationContext(), this);
mAudioCapabilitiesReceiver.register();
- maybeInitCacheManager();
+ mCacheManager = createCacheManager();
if (mCacheManager == null) {
Log.i(TAG, "Trickplay is disabled");
} else {
@@ -61,13 +57,14 @@ public abstract class BaseTunerTvInputService extends RecordingTvInputService
}
}
- protected abstract void maybeInitCacheManager();
+ /**
+ * Creates {@CacheManager}. It returns null, if storage in not enough.
+ */
+ protected abstract CacheManager createCacheManager();
@Override
public void onDestroy() {
- if (DEBUG) {
- Log.d(TAG, "onDestroy");
- }
+ if (DEBUG) Log.d(TAG, "onDestroy");
super.onDestroy();
mChannelDataManager.release();
mAudioCapabilitiesReceiver.unregister();
@@ -77,36 +74,27 @@ public abstract class BaseTunerTvInputService extends RecordingTvInputService
}
@Override
- public TvRecording.RecordingSession onCreateDvrSession(String inputId) {
- return new RecordingSessionImpl(this, inputId, mChannelDataManager);
+ public RecordingSession onCreateRecordingSession(String inputId) {
+ return new TunerRecordingSession(this, inputId, mChannelDataManager);
}
@Override
- public RecordingTvInputService.PlaybackSession onCreatePlaybackSession(String inputId) {
- if (DEBUG) {
- Log.d(TAG, "onCreateSession");
- }
- final TvInputSessionImpl session = new TvInputSessionImpl(
+ public Session onCreateSession(String inputId) {
+ if (DEBUG) Log.d(TAG, "onCreateSession");
+ final TunerSession session = new TunerSession(
this, mChannelDataManager, mCacheManager);
- mTvInputSessions.add(session);
- session.notifyAudioCapabilitiesChanged(mAudioCapabilities);
- new Handler().post(new Runnable() {
- @Override
- public void run() {
- // STOPSHIP(DVR): Session methods cannot be called inside onCreatePlaybackSession.
- // If DvrSession is added in API. we can call them inside onCreatePlaybackSession.
- session.setOverlayViewEnabled(true);
- }
- });
+ mTunerSessions.add(session);
+ session.setAudioCapabilities(mAudioCapabilities);
+ session.setOverlayViewEnabled(true);
return session;
}
@Override
public void onAudioCapabilitiesChanged(AudioCapabilities audioCapabilities) {
mAudioCapabilities = audioCapabilities;
- for (TvInputSessionImpl session : mTvInputSessions) {
+ for (TunerSession session : mTunerSessions) {
if (!session.isReleased()) {
- session.notifyAudioCapabilitiesChanged(audioCapabilities);
+ session.setAudioCapabilities(audioCapabilities);
}
}
}
diff --git a/usbtuner/src/com/android/usbtuner/tvinput/ChannelDataManager.java b/usbtuner/src/com/android/usbtuner/tvinput/ChannelDataManager.java
index 206d8ba4..57affe22 100644
--- a/usbtuner/src/com/android/usbtuner/tvinput/ChannelDataManager.java
+++ b/usbtuner/src/com/android/usbtuner/tvinput/ChannelDataManager.java
@@ -321,6 +321,13 @@ public class ChannelDataManager implements Handler.Callback {
channel.setChannelId(channelId);
long currentTime = System.currentTimeMillis();
List<EitItem> oldItems = getAllProgramsForChannel(channel);
+ // TODO: Find a right to check if the programs are added outside.
+ for (EitItem item : oldItems) {
+ if (item.getEventId() == 0) {
+ // The event has been added outside TV tuner. Do not update programs.
+ return;
+ }
+ }
List<EitItem> outdatedOldItems = new ArrayList<>();
List<EitItem> programsAddedToEPG = new ArrayList<>();
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
diff --git a/usbtuner/src/com/android/usbtuner/tvinput/DvrSessionImplInternal.java b/usbtuner/src/com/android/usbtuner/tvinput/DvrSessionImplInternal.java
deleted file mode 100644
index 80c5fca6..00000000
--- a/usbtuner/src/com/android/usbtuner/tvinput/DvrSessionImplInternal.java
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.usbtuner.tvinput;
-
-import android.content.ContentUris;
-import android.content.Context;
-import android.media.MediaDataSource;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Message;
-import android.support.annotation.IntDef;
-import android.support.annotation.Nullable;
-import android.util.Log;
-import android.util.Pair;
-import android.widget.Toast;
-
-import com.android.tv.common.recording.RecordingCapability;
-import com.android.usbtuner.DvbDeviceAccessor;
-import com.android.usbtuner.TunerHal;
-import com.android.usbtuner.UsbTunerDataSource;
-import com.android.usbtuner.data.PsipData;
-import com.android.usbtuner.data.TunerChannel;
-import com.android.usbtuner.exoplayer.CacheManager;
-import com.android.usbtuner.exoplayer.DvrStorageManager;
-import com.android.usbtuner.exoplayer.RecordSampleSourceExtractor;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-
-/**
- * Implements a DVR feature.
- */
-public class DvrSessionImplInternal implements PlaybackCacheListener, EventDetector.EventListener,
- Handler.Callback {
- private static String TAG = "DvrSessionImplInternal";
- private static final boolean DEBUG = true; // STOPSHIP(DVR)
-
-
- private static final int MSG_START_RECORDING = 1;
- private static final int MSG_STOP_RECORDING = 2;
- private static final int MSG_DELETE_RECORDING = 3;
- private static final int MSG_RELEASE = 4;
- private final String mInputId;
- private RecordingCapability mCapabilities;
-
- public RecordingCapability getCapabilities() {
- return mCapabilities;
- }
-
- @IntDef({STATE_IDLE, STATE_RECORDING})
- @Retention(RetentionPolicy.SOURCE)
- public @interface DvrSessionState {}
- private static final int STATE_IDLE = 1;
- private static final int STATE_RECORDING = 2;
-
- private static final long CHANNEL_ID_NONE = -1;
-
- private final Context mContext;
- private final ChannelDataManager mChannelDataManager;
- private final Handler mHandler;
- private final CountDownLatch mReleaseLatch = new CountDownLatch(1);
-
- private TunerHal mTunerHal;
- private UsbTunerDataSource mTunerSource;
- private CacheManager mCacheManager;
- private RecordSampleSourceExtractor mRecorder;
- private DvrEventListener mDvrEventListener;
- @DvrSessionState private int mSessionState = STATE_IDLE;
-
- // For event notification to LiveChannels
- public interface DvrEventListener {
- void onRecordStarted(Uri mediaUri);
- void onRecordUnexpectedlyStopped(Uri mediaUri, int reason);
- void onDeleted(Uri mediaUri);
- void onDeleteFailed(Uri mediaUri, int reason);
- }
-
- public DvrSessionImplInternal(Context context, String inputId, ChannelDataManager dataManager) {
- mContext = context;
- mInputId = inputId;
- HandlerThread handlerThread = new HandlerThread(TAG);
- handlerThread.start();
- mHandler = new Handler(handlerThread.getLooper(), this);
- mChannelDataManager = dataManager;
- mChannelDataManager.checkDataVersion(context);
- mCapabilities = new DvbDeviceAccessor(context).getRecordingCapability(mInputId);
- if (DEBUG) Log.d(TAG, mCapabilities.toString());
- }
-
- // PlaybackCacheListener
- @Override
- public void onCacheStartTimeChanged(long startTimeMs) {
- }
-
- @Override
- public void onCacheStateChanged(boolean available) {
- }
-
- @Override
- public void onDiskTooSlow() {
- }
-
- // EventDetector.EventListener
- @Override
- public void onChannelDetected(TunerChannel channel, boolean channelArrivedAtFirstTime) {
- mChannelDataManager.notifyChannelDetected(channel, channelArrivedAtFirstTime);
- }
-
- @Override
- public void onEventDetected(TunerChannel channel, List<PsipData.EitItem> items) {
- mChannelDataManager.notifyEventDetected(channel, items);
- }
-
- public void setDvrEventListener(DvrEventListener listener) {
- mDvrEventListener = listener;
- }
-
- public void startRecording(Uri channelUri, Uri mediaUri) {
- mHandler.obtainMessage(
- MSG_START_RECORDING, new Pair<>(channelUri, mediaUri)).sendToTarget();
- }
-
- public void stopRecording() {
- mHandler.sendEmptyMessage(MSG_STOP_RECORDING);
- }
-
- public void deleteRecording(Uri mediaUri) {
- mHandler.obtainMessage(MSG_DELETE_RECORDING, mediaUri).sendToTarget();
- }
-
- public void release() {
- mHandler.removeCallbacksAndMessages(null);
- mHandler.sendEmptyMessage(MSG_RELEASE);
- try {
- mReleaseLatch.await();
- } catch (InterruptedException e) {
- Log.e(TAG, "Couldn't wait for finish of MSG_RELEASE", e);
- } finally {
- mHandler.getLooper().quitSafely();
- }
- }
-
- @Override
- public boolean handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_START_RECORDING: {
- Pair<Uri, Uri> params = (Pair<Uri, Uri>) msg.obj;
- if(onStartRecording(params.first, params.second)) {
- Toast.makeText(mContext, "USB TV tuner: onStart is called",
- Toast.LENGTH_SHORT).show();
- if (mDvrEventListener != null) {
- mDvrEventListener.onRecordStarted(params.second);
- }
- }
- else {
- // TODO: apply reason
- if (mDvrEventListener != null) {
- mDvrEventListener.onRecordUnexpectedlyStopped(params.second, 0);
- }
- }
- return true;
- }
- case MSG_STOP_RECORDING: {
- onStopRecording();
- Toast.makeText(mContext, "USB TV tuner: onStopRecord is called",
- Toast.LENGTH_SHORT).show();
- return true;
- }
- case MSG_DELETE_RECORDING: {
- Uri toDelete = (Uri) msg.obj;
- onDeleteRecording(toDelete);
- return true;
- }
- case MSG_RELEASE: {
- onRelease();
- return true;
- }
- }
- return false;
- }
-
- @Nullable
- private TunerChannel getChannel(Uri channelUri) {
- long channelId;
- try {
- channelId = ContentUris.parseId(channelUri);
- } catch (UnsupportedOperationException | NumberFormatException e) {
- channelId = CHANNEL_ID_NONE;
- }
- return (channelId == CHANNEL_ID_NONE) ? null : mChannelDataManager.getChannel(channelId);
- }
-
- private File getMediaDir(Uri mediaUri) {
- String mediaPath = mediaUri.getPath();
- if (mediaPath == null || mediaPath.length() == 0) {
- return null;
- }
- return new File(mContext.getCacheDir().getAbsolutePath() + "/recording" +
- mediaUri.getPath());
- }
-
- private void resetRecorder() {
- if (mRecorder != null) {
- mRecorder.release();
- mRecorder = null;
- }
- if (mCacheManager != null) {
- mCacheManager.close();
- mCacheManager = null;
- }
- if (mTunerSource != null) {
- mTunerSource.stopStream();
- mTunerSource = null;
- }
- if (mTunerHal != null) {
- try {
- mTunerHal.close();
- } catch (Exception ex) {
- Log.e(TAG, "Error on closing tuner HAL.", ex);
- }
- mTunerHal = null;
- }
- }
-
- private boolean onStartRecording(Uri channelUri, Uri mediaUri) {
- if (mSessionState != STATE_IDLE) {
- return false;
- }
- TunerChannel channel = getChannel(channelUri);
- if (channel == null) {
- Log.w(TAG, "Failed to start recording. Couldn't find the channel for " + channelUri);
- return false;
- }
- mTunerHal = TunerHal.getInstance(mContext);
- if (mTunerHal == null) {
- Log.w(TAG, "Failed to start recording. Couldn't open a DVB device");
- resetRecorder();
- return false;
- }
- mTunerSource = new UsbTunerDataSource(mTunerHal, this);
- if (!mTunerSource.tuneToChannel(channel)) {
- Log.w(TAG, "Failed to start recording. Couldn't tune to the channel for " + channel);
- resetRecorder();
- return false;
- }
- File mediaDir = getMediaDir(mediaUri);
- if (mediaDir == null) {
- Log.w(TAG, "Failed to start recording. mediaUri is not provided properly " +
- mediaUri.toString());
- resetRecorder();
- return false;
- }
- mTunerSource.startStream();
- mCacheManager = new CacheManager(new DvrStorageManager(mediaDir, true));
- mRecorder = new RecordSampleSourceExtractor((MediaDataSource) mTunerSource,
- mCacheManager, this);
- try {
- mRecorder.prepare();
- } catch (IOException e) {
- Log.w(TAG, "Failed to start recording. Couldn't prepare a extractor");
- resetRecorder();
- return false;
- }
- mSessionState = STATE_RECORDING;
- return true;
- }
-
- private void onStopRecording() {
- // TODO: notify the recording result to LiveChannels
- if (mSessionState != STATE_RECORDING) {
- return;
- }
- resetRecorder();
- mSessionState = STATE_IDLE;
- }
-
- private void onDeleteRecording(Uri mediaUri) {
- // TODO: notify the deletion result to LiveChannels
- File mediaDir = getMediaDir(mediaUri);
- if (mediaDir == null) {
- return;
- }
- for(File file: mediaDir.listFiles()) {
- file.delete();
- }
- mediaDir.delete();
- }
-
- private void onRelease() {
- // Current recording will be canceled.
- onStopRecording();
- mReleaseLatch.countDown();
- }
-}
diff --git a/usbtuner/src/com/android/usbtuner/tvinput/InternalTunerTvInputService.java b/usbtuner/src/com/android/usbtuner/tvinput/InternalTunerTvInputService.java
index adc4f14a..b4e97833 100644
--- a/usbtuner/src/com/android/usbtuner/tvinput/InternalTunerTvInputService.java
+++ b/usbtuner/src/com/android/usbtuner/tvinput/InternalTunerTvInputService.java
@@ -24,8 +24,8 @@ import android.media.tv.TvInputInfo;
import android.os.Environment;
import android.util.Log;
-import com.android.usbtuner.exoplayer.CacheManager;
-import com.android.usbtuner.exoplayer.TrickplayStorageManager;
+import com.android.usbtuner.exoplayer.cache.CacheManager;
+import com.android.usbtuner.exoplayer.cache.TrickplayStorageManager;
import com.android.usbtuner.util.SystemPropertiesProxy;
import com.android.usbtuner.util.TisConfiguration;
import org.xmlpull.v1.XmlPullParserException;
@@ -57,7 +57,7 @@ public class InternalTunerTvInputService extends BaseTunerTvInputService {
}
@Override
- protected void maybeInitCacheManager() {
+ protected CacheManager createCacheManager() {
int maxCacheSizeMb = SystemPropertiesProxy.getInt(MAX_CACHE_SIZE_KEY, MAX_CACHE_SIZE_DEF);
if (maxCacheSizeMb >= MIN_CACHE_SIZE_DEF) {
boolean useExternalStorage = Environment.MEDIA_MOUNTED.equals(
@@ -67,21 +67,20 @@ public class InternalTunerTvInputService extends BaseTunerTvInputService {
boolean allowToUseInternalStorage = true;
if (useExternalStorage || allowToUseInternalStorage) {
File baseDir = useExternalStorage ? getExternalCacheDir() : getCacheDir();
- mCacheManager = new CacheManager(
+ return new CacheManager(
new TrickplayStorageManager(getApplicationContext(), baseDir,
1024L * 1024 * maxCacheSizeMb));
}
}
+ return null;
}
@Override
public TvInputInfo onHardwareAdded(TvInputHardwareInfo hardwareInfo) {
if (DEBUG) Log.d(TAG, "onHardwareAdded: " + hardwareInfo.toString());
-
if (mTvInputId != null) {
return null;
}
-
TvInputInfo info = null;
if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_TUNER &&
TisConfiguration.getTunerHwDeviceId(this) == hardwareInfo.getDeviceId()) {
@@ -101,7 +100,6 @@ public class InternalTunerTvInputService extends BaseTunerTvInputService {
@Override
public String onHardwareRemoved(TvInputHardwareInfo hardwareInfo) {
if (DEBUG) Log.d(TAG, "onHardwareRemoved: " + hardwareInfo.toString());
-
return null;
}
}
diff --git a/usbtuner/src/com/android/usbtuner/tvinput/RecordingSessionImpl.java b/usbtuner/src/com/android/usbtuner/tvinput/RecordingSessionImpl.java
deleted file mode 100644
index 0aac1211..00000000
--- a/usbtuner/src/com/android/usbtuner/tvinput/RecordingSessionImpl.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.usbtuner.tvinput;
-
-import android.content.Context;
-import android.net.Uri;
-
-import com.android.tv.common.recording.RecordingCapability;
-import com.android.tv.common.recording.TvRecording;
-
-/**
- * Processes DVR recordings, and deletes the previously recorded contents.
- */
-public class RecordingSessionImpl extends TvRecording.RecordingSession implements
- DvrSessionImplInternal.DvrEventListener {
- // TODO: recording request will be handled here
- private final DvrSessionImplInternal mSessionImplInternal;
- private final String mInputId;
-
- public RecordingSessionImpl(Context context, String inputId,
- ChannelDataManager channelDataManager) {
- super(context);
- mInputId = inputId;
- mSessionImplInternal = new DvrSessionImplInternal(context, inputId, channelDataManager);
- mSessionImplInternal.setDvrEventListener(this);
- }
-
- @Override
- public void onStopRecord() {
- mSessionImplInternal.stopRecording();
- }
-
- @Override
- public void onStartRecord(Uri channelUri, Uri mediaUri) {
- mSessionImplInternal.startRecording(channelUri, mediaUri);
- }
-
- @Override
- public void onDelete(Uri mediaUri) {
- mSessionImplInternal.deleteRecording(mediaUri);
- notifyDeleted(mediaUri);
- }
-
- @Override
- public RecordingCapability onGetCapability() {
- return mSessionImplInternal.getCapabilities();
- }
-
- @Override
- public void onRelease() {
- }
-
- // DvrSessionImplInternal.DvrEventListener
- @Override
- public void onRecordStarted(Uri mediaUri) {
- notifyRecordStarted(mediaUri);
- }
-
- @Override
- public void onRecordUnexpectedlyStopped(Uri mediaUri, int reason) {
- notifyRecordUnexpectedlyStopped(mediaUri, reason);
- }
-
- @Override
- public void onDeleted(Uri mediaUri) {
- notifyDeleted(mediaUri);
- }
-
- @Override
- public void onDeleteFailed(Uri mediaUri, int reason) {
- notifyDeleteFailed(mediaUri, reason);
- }
-}
diff --git a/usbtuner/src/com/android/usbtuner/tvinput/TunerRecordingSession.java b/usbtuner/src/com/android/usbtuner/tvinput/TunerRecordingSession.java
new file mode 100644
index 00000000..3d27a946
--- /dev/null
+++ b/usbtuner/src/com/android/usbtuner/tvinput/TunerRecordingSession.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.usbtuner.tvinput;
+
+import android.content.Context;
+import android.media.tv.TvInputManager;
+import android.media.tv.TvInputService;
+import android.net.Uri;
+import android.support.annotation.MainThread;
+import android.support.annotation.Nullable;
+import android.support.annotation.WorkerThread;
+import android.util.Log;
+
+/**
+ * Processes DVR recordings, and deletes the previously recorded contents.
+ */
+public class TunerRecordingSession extends TvInputService.RecordingSession {
+ private static String TAG = "TunerRecordingSession";
+ private static boolean DEBUG = false;
+
+ private final TunerRecordingSessionWorker mSessionWorker;
+ private final String mInputId;
+
+ public TunerRecordingSession(Context context, String inputId,
+ ChannelDataManager channelDataManager) {
+ super(context);
+ mInputId = inputId;
+ mSessionWorker = new TunerRecordingSessionWorker(context, inputId, channelDataManager,
+ this);
+ }
+
+ // RecordingSession
+ @MainThread
+ @Override
+ public void onTune(Uri channelUri) {
+ // TODO(dvr): support calling more than once, http://b/27171225
+ if (DEBUG) {
+ Log.d(TAG, "Requesting recording session tune: " + channelUri);
+ }
+ mSessionWorker.connect(channelUri);
+ }
+
+ @MainThread
+ @Override
+ public void onRelease() {
+ if (DEBUG) {
+ Log.d(TAG, "Requesting recording session release.");
+ }
+ mSessionWorker.release();
+ }
+
+ @MainThread
+ @Override
+ public void onStartRecording(@Nullable Uri programHint) {
+ if (DEBUG) {
+ Log.d(TAG, "Requesting start recording.");
+ }
+ mSessionWorker.startRecording();
+ }
+
+ @MainThread
+ @Override
+ public void onStopRecording() {
+ if (DEBUG) {
+ Log.d(TAG, "Requesting stop recording.");
+ }
+ mSessionWorker.stopRecording();
+ }
+
+ // Called from TunerRecordingSessionImpl in a worker thread.
+ @WorkerThread
+ public void onTuned(Uri channelUri) {
+ if (DEBUG) {
+ Log.d(TAG, "Notifying recording session tuned.");
+ }
+ notifyTuned(channelUri);
+ }
+
+ @WorkerThread
+ public void onConnectFailed() {
+ if (DEBUG) {
+ Log.d(TAG, "Notifying recording session connection failed.");
+ }
+ notifyError(TvInputManager.RECORDING_ERROR_UNKNOWN);
+ }
+
+ @WorkerThread
+ public void onRecordFinished(final Uri recordedProgramUri) {
+ if (DEBUG) {
+ Log.d(TAG, "Notifying record successfully finished.");
+ }
+ notifyRecordingStopped(recordedProgramUri);
+ }
+
+ @WorkerThread
+ public void onRecordUnexpectedlyStopped(int reason) {
+ Log.w(TAG, "Notifying record failed: " + reason);
+ notifyError(reason);
+ }
+}
diff --git a/usbtuner/src/com/android/usbtuner/tvinput/TunerRecordingSessionWorker.java b/usbtuner/src/com/android/usbtuner/tvinput/TunerRecordingSessionWorker.java
new file mode 100644
index 00000000..3d121a85
--- /dev/null
+++ b/usbtuner/src/com/android/usbtuner/tvinput/TunerRecordingSessionWorker.java
@@ -0,0 +1,549 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.usbtuner.tvinput;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.media.MediaDataSource;
+import android.media.tv.TvContract;
+import android.media.tv.TvInputManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.support.annotation.IntDef;
+import android.support.annotation.Nullable;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.google.android.exoplayer.util.Assertions;
+import com.android.tv.common.recording.RecordedProgram;
+import com.android.tv.common.recording.RecordingCapability;
+import com.android.usbtuner.DvbDeviceAccessor;
+import com.android.usbtuner.TunerHal;
+import com.android.usbtuner.UsbTunerDataSource;
+import com.android.usbtuner.data.PsipData;
+import com.android.usbtuner.data.TunerChannel;
+import com.android.usbtuner.exoplayer.Recorder;
+import com.android.usbtuner.exoplayer.cache.CacheManager;
+import com.android.usbtuner.exoplayer.cache.DvrStorageManager;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.Locale;
+import java.util.Random;
+
+/**
+ * Implements a DVR feature.
+ */
+public class TunerRecordingSessionWorker implements PlaybackCacheListener,
+ EventDetector.EventListener, Recorder.RecordListener,
+ Handler.Callback {
+ private static String TAG = "TunerRecordingSessionWorker";
+ private static final boolean DEBUG = false;
+
+ private static final String SORT_BY_TIME = TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS
+ + ", " + TvContract.Programs.COLUMN_CHANNEL_ID + ", "
+ + TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS;
+ private static final int MSG_CONNECT = 1;
+ private static final int MSG_DISCONNECT = 2;
+ private static final int MSG_START_RECORDING = 3;
+ private static final int MSG_STOP_RECORDING = 4;
+ private static final int MSG_RECORDING_RESULT = 5;
+ private static final int MSG_DELETE_RECORDING = 6;
+ private static final int MSG_RELEASE = 7;
+ private RecordingCapability mCapabilities;
+
+ public RecordingCapability getCapabilities() {
+ return mCapabilities;
+ }
+
+ @IntDef({STATE_IDLE, STATE_CONNECTED, STATE_RECORDING})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DvrSessionState {}
+ private static final int STATE_IDLE = 1;
+ private static final int STATE_CONNECTED = 2;
+ private static final int STATE_RECORDING = 3;
+
+ private static final long CHANNEL_ID_NONE = -1;
+
+ private final Context mContext;
+ private final ChannelDataManager mChannelDataManager;
+ private final Handler mHandler;
+ private final Random mRandom = new Random();
+
+ private TunerHal mTunerHal;
+ private UsbTunerDataSource mTunerSource;
+ private TunerChannel mChannel;
+ private File mStorageDir;
+ private long mRecordStartTime;
+ private long mRecordEndTime;
+ private CacheManager mCacheManager;
+ private Recorder mRecorder;
+ private final TunerRecordingSession mSession;
+ @DvrSessionState private int mSessionState = STATE_IDLE;
+ private final String mInputId;
+
+ public TunerRecordingSessionWorker(Context context, String inputId,
+ ChannelDataManager dataManager, TunerRecordingSession session) {
+ mRandom.setSeed(System.nanoTime());
+ mContext = context;
+ HandlerThread handlerThread = new HandlerThread(TAG);
+ handlerThread.start();
+ mHandler = new Handler(handlerThread.getLooper(), this);
+ mChannelDataManager = dataManager;
+ mChannelDataManager.checkDataVersion(context);
+ mCapabilities = new DvbDeviceAccessor(context).getRecordingCapability(inputId);
+ mInputId = inputId;
+ if (DEBUG) Log.d(TAG, mCapabilities.toString());
+ mSession = session;
+ }
+
+ // PlaybackCacheListener
+ @Override
+ public void onCacheStartTimeChanged(long startTimeMs) {
+ }
+
+ @Override
+ public void onCacheStateChanged(boolean available) {
+ }
+
+ @Override
+ public void onDiskTooSlow() {
+ }
+
+ // EventDetector.EventListener
+ @Override
+ public void onChannelDetected(TunerChannel channel, boolean channelArrivedAtFirstTime) {
+ if (mChannel == null || mChannel.compareTo(channel) != 0) {
+ return;
+ }
+ mChannelDataManager.notifyChannelDetected(channel, channelArrivedAtFirstTime);
+ }
+
+ @Override
+ public void onEventDetected(TunerChannel channel, List<PsipData.EitItem> items) {
+ if (mChannel == null || mChannel.compareTo(channel) != 0) {
+ return;
+ }
+ mChannelDataManager.notifyEventDetected(channel, items);
+ }
+
+ public void connect(Uri channelUri) {
+ mHandler.removeCallbacksAndMessages(null);
+ mHandler.obtainMessage(MSG_CONNECT, channelUri).sendToTarget();
+ }
+
+ public void disconnect() {
+ mHandler.sendEmptyMessage(MSG_DISCONNECT);
+ }
+
+ public void startRecording() {
+ mHandler.sendEmptyMessage(MSG_START_RECORDING);
+ }
+
+ public void stopRecording() {
+ mHandler.sendEmptyMessage(MSG_STOP_RECORDING);
+ }
+
+ public void notifyRecordingFinished(boolean success) {
+ mHandler.obtainMessage(MSG_RECORDING_RESULT, success).sendToTarget();
+ }
+
+ public void deleteRecording(Uri mediaUri) {
+ mHandler.obtainMessage(MSG_DELETE_RECORDING, mediaUri).sendToTarget();
+ }
+
+ public void release() {
+ mHandler.removeCallbacksAndMessages(null);
+ mHandler.sendEmptyMessage(MSG_RELEASE);
+ }
+
+ @Override
+ public boolean handleMessage(Message msg) {
+ // TODO: Add RecordStopped status
+ switch (msg.what) {
+ case MSG_CONNECT: {
+ Uri channelUri = (Uri) msg.obj;
+ if (onConnect(channelUri)) {
+ mSession.onTuned(channelUri);
+ } else {
+ Log.w(TAG, "Recording session connect failed");
+ mSession.onConnectFailed();
+ }
+ return true;
+ }
+ case MSG_START_RECORDING: {
+ if(onStartRecording()) {
+ Toast.makeText(mContext, "USB TV tuner: Recording started",
+ Toast.LENGTH_SHORT).show();
+ }
+ else {
+ mSession.onRecordUnexpectedlyStopped(TvInputManager.RECORDING_ERROR_UNKNOWN);
+ }
+ return true;
+ }
+ case MSG_DISCONNECT: {
+ return true;
+ }
+ case MSG_STOP_RECORDING: {
+ onStopRecording();
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(mContext, "USB TV tuner: Recording stopped",
+ Toast.LENGTH_SHORT).show();
+ }
+ });
+ return true;
+ }
+ case MSG_RECORDING_RESULT: {
+ onRecordingResult((Boolean) msg.obj);
+ return true;
+ }
+ case MSG_DELETE_RECORDING: {
+ Uri toDelete = (Uri) msg.obj;
+ onDeleteRecording(toDelete);
+ return true;
+ }
+ case MSG_RELEASE: {
+ onRelease();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Nullable
+ private TunerChannel getChannel(Uri channelUri) {
+ if (channelUri == null) {
+ return null;
+ }
+ long channelId;
+ try {
+ channelId = ContentUris.parseId(channelUri);
+ } catch (UnsupportedOperationException | NumberFormatException e) {
+ channelId = CHANNEL_ID_NONE;
+ }
+ return (channelId == CHANNEL_ID_NONE) ? null : mChannelDataManager.getChannel(channelId);
+ }
+
+ private String getStorageKey() {
+ long prefix = System.currentTimeMillis();
+ int suffix = mRandom.nextInt();
+ return String.format(Locale.ENGLISH, "%016x_%016x", prefix, suffix);
+ }
+
+ private File getMediaDir(String storageKey) {
+ return new File(mContext.getCacheDir().getAbsolutePath() + "/recording/" + storageKey);
+ }
+
+ private File getMediaDir(Uri mediaUri) {
+ String mediaPath = mediaUri.getPath();
+ if (mediaPath == null || mediaPath.length() == 0) {
+ return null;
+ }
+ return new File(mContext.getCacheDir().getAbsolutePath() + "/recording" +
+ mediaUri.getPath());
+ }
+
+ private void reset() {
+ if (mRecorder != null) {
+ mRecorder.release();
+ mRecorder = null;
+ }
+ if (mCacheManager != null) {
+ mCacheManager.close();
+ mCacheManager = null;
+ }
+ if (mTunerSource != null) {
+ mTunerSource.stopStream();
+ mTunerSource = null;
+ }
+ if (mTunerHal != null) {
+ try {
+ mTunerHal.close();
+ } catch (Exception ex) {
+ Log.e(TAG, "Error on closing tuner HAL.", ex);
+ }
+ mTunerHal = null;
+ }
+ mSessionState = STATE_IDLE;
+ }
+
+ private void resetRecorder() {
+ Assertions.checkArgument(mSessionState != STATE_IDLE);
+ if (mRecorder != null) {
+ mRecorder.release();
+ mRecorder = null;
+ }
+ if (mCacheManager != null) {
+ mCacheManager.close();
+ mCacheManager = null;
+ }
+ if (mTunerSource != null) {
+ mTunerSource.stopStream();
+ mTunerSource = null;
+ }
+ mSessionState = STATE_CONNECTED;
+ }
+
+ private boolean onConnect(Uri channelUri) {
+ if (mSessionState == STATE_RECORDING) {
+ return false;
+ }
+ mChannel = getChannel(channelUri);
+ if (mChannel == null) {
+ Log.w(TAG, "Failed to start recording. Couldn't find the channel for " + mChannel);
+ return false;
+ }
+ if (mSessionState == STATE_CONNECTED) {
+ return true;
+ }
+ mTunerHal = TunerHal.createInstance(mContext);
+ if (mTunerHal == null) {
+ Log.w(TAG, "Failed to start recording. Couldn't open a DVB device");
+ reset();
+ return false;
+ }
+ mSessionState = STATE_CONNECTED;
+ return true;
+ }
+
+ private boolean onStartRecording() {
+ if (mSessionState != STATE_CONNECTED) {
+ return false;
+ }
+ mStorageDir = getMediaDir(getStorageKey());
+ mTunerSource = new UsbTunerDataSource(mTunerHal, this);
+ if (!mTunerSource.tuneToChannel(mChannel)) {
+ Log.w(TAG, "Failed to start recording. Couldn't tune to the channel for " +
+ mChannel.toString());
+ resetRecorder();
+ return false;
+ }
+ mCacheManager = new CacheManager(new DvrStorageManager(mStorageDir, true));
+ mTunerSource.startStream();
+ mRecordStartTime = System.currentTimeMillis();
+ mRecorder = new Recorder((MediaDataSource) mTunerSource,
+ mCacheManager, this, this);
+ try {
+ mRecorder.prepare();
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to start recording. Couldn't prepare a extractor");
+ resetRecorder();
+ return false;
+ }
+ mSessionState = STATE_RECORDING;
+ return true;
+ }
+
+ private void onStopRecording() {
+ if (mSessionState != STATE_RECORDING) {
+ return;
+ }
+ // Do not change session status.
+ if (mRecorder != null) {
+ mRecorder.release();
+ mRecordEndTime = System.currentTimeMillis();
+ mRecorder = null;
+ }
+ }
+
+ private static class Program {
+ private long mChannelId;
+ private String mTitle;
+ private String mEpisodeTitle;
+ private int mSeasonNumber;
+ private int mEpisodeNumber;
+ private String mDescription;
+ private String mPosterArtUri;
+ private String mThumbnailUri;
+ private String mCanonicalGenres;
+ private String mContentRatings;
+ private long mStartTimeUtcMillis;
+ private long mEndTimeUtcMillis;
+ private long mVideoWidth;
+ private long mVideoHeight;
+
+ private static final String[] PROJECTION = {
+ TvContract.Programs.COLUMN_CHANNEL_ID,
+ TvContract.Programs.COLUMN_TITLE,
+ TvContract.Programs.COLUMN_EPISODE_TITLE,
+ TvContract.Programs.COLUMN_SEASON_NUMBER,
+ TvContract.Programs.COLUMN_EPISODE_NUMBER,
+ TvContract.Programs.COLUMN_SHORT_DESCRIPTION,
+ TvContract.Programs.COLUMN_POSTER_ART_URI,
+ TvContract.Programs.COLUMN_THUMBNAIL_URI,
+ TvContract.Programs.COLUMN_CANONICAL_GENRE,
+ TvContract.Programs.COLUMN_CONTENT_RATING,
+ TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS,
+ TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS,
+ TvContract.Programs.COLUMN_VIDEO_WIDTH,
+ TvContract.Programs.COLUMN_VIDEO_HEIGHT
+ };
+
+ public Program(Cursor cursor) {
+ int index = 0;
+ mChannelId = cursor.getLong(index++);
+ mTitle = cursor.getString(index++);
+ mEpisodeTitle = cursor.getString(index++);
+ mSeasonNumber = cursor.getInt(index++);
+ mEpisodeNumber = cursor.getInt(index++);
+ mDescription = cursor.getString(index++);
+ mPosterArtUri = cursor.getString(index++);
+ mThumbnailUri = cursor.getString(index++);
+ mCanonicalGenres = cursor.getString(index++);
+ mContentRatings = cursor.getString(index++);
+ mStartTimeUtcMillis = cursor.getLong(index++);
+ mEndTimeUtcMillis = cursor.getLong(index++);
+ mVideoWidth = cursor.getLong(index++);
+ mVideoHeight = cursor.getLong(index++);
+ }
+
+ public Program(long channelId) {
+ mChannelId = channelId;
+ mTitle = "Unknown";
+ mEpisodeTitle = "";
+ mSeasonNumber = 0;
+ mEpisodeNumber = 0;
+ mDescription = "Unknown";
+ mPosterArtUri = null;
+ mThumbnailUri = null;
+ mCanonicalGenres = null;
+ mContentRatings = null;
+ mStartTimeUtcMillis = 0;
+ mEndTimeUtcMillis = 0;
+ mVideoWidth = 0;
+ mVideoHeight = 0;
+ }
+
+ public static Program onQuery(Cursor c) {
+ Program program = null;
+ if (c != null && c.moveToNext()) {
+ program = new Program(c);
+ }
+ return program;
+ }
+
+ public ContentValues buildValues() {
+ ContentValues values = new ContentValues();
+ values.put(PROJECTION[0], mChannelId);
+ values.put(PROJECTION[1], mTitle);
+ values.put(PROJECTION[2], mEpisodeTitle);
+ values.put(PROJECTION[3], mSeasonNumber);
+ values.put(PROJECTION[4], mEpisodeNumber);
+ values.put(PROJECTION[5], mDescription);
+ values.put(PROJECTION[6], mPosterArtUri);
+ values.put(PROJECTION[7], mThumbnailUri);
+ values.put(PROJECTION[8], mCanonicalGenres);
+ values.put(PROJECTION[9], mContentRatings);
+ values.put(PROJECTION[10], mStartTimeUtcMillis);
+ values.put(PROJECTION[11], mEndTimeUtcMillis);
+ values.put(PROJECTION[12], mVideoWidth);
+ values.put(PROJECTION[13], mVideoHeight);
+ return values;
+ }
+ }
+
+ private Program getRecordedProgram() {
+ ContentResolver resolver = mContext.getContentResolver();
+ long avg = mRecordStartTime / 2 + mRecordEndTime / 2;
+ Uri programUri = TvContract.buildProgramsUriForChannel(mChannel.getChannelId(), avg, avg);
+ try (Cursor c = resolver.query(programUri, Program.PROJECTION, null, null, SORT_BY_TIME)) {
+ if (c != null) {
+ Program result = Program.onQuery(c);
+ if (DEBUG) {
+ Log.v(TAG, "Finished query for " + this);
+ }
+ return result;
+ } else {
+ if (c == null) {
+ Log.e(TAG, "Unknown query error for " + this);
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "Canceled query for " + this);
+ }
+ }
+ return null;
+ }
+ }
+ }
+
+ private Uri insertRecordedProgram(Program program, long channelId, String storageUri,
+ long totalBytes, long startTime, long endTime) {
+ RecordedProgram recordedProgram = RecordedProgram.builder()
+ .setInputId(mInputId)
+ .setChannelId(channelId)
+ .setDataUri(storageUri)
+ .setDurationMillis(endTime - startTime)
+ .setDataBytes(totalBytes)
+ .build();
+ Uri uri = mContext.getContentResolver().insert(TvContract.RecordedPrograms.CONTENT_URI,
+ RecordedProgram.toValues(recordedProgram));
+ return uri;
+ }
+
+ private boolean onRecordingResult(boolean success) {
+ if (mSessionState == STATE_RECORDING && success) {
+ Uri uri = insertRecordedProgram(getRecordedProgram(), mChannel.getChannelId(),
+ mStorageDir.toURI().toString(), 1024 * 1024,
+ mRecordStartTime, mRecordEndTime);
+ if (uri != null) {
+ mSession.onRecordFinished(uri);
+ }
+ resetRecorder();
+ return true;
+ }
+
+ if (mSessionState == STATE_RECORDING) {
+ mSession.onRecordUnexpectedlyStopped(TvInputManager.RECORDING_ERROR_UNKNOWN);
+ Log.w(TAG, "Recording failed: " + mChannel == null ? "" : mChannel.toString());
+ resetRecorder();
+ } else {
+ Log.e(TAG, "Recording session status abnormal");
+ reset();
+ }
+ return false;
+ }
+
+ private void onDeleteRecording(Uri mediaUri) {
+ // TODO: notify the deletion result to LiveChannels
+ File mediaDir = getMediaDir(mediaUri);
+ if (mediaDir == null) {
+ return;
+ }
+ for(File file: mediaDir.listFiles()) {
+ file.delete();
+ }
+ mediaDir.delete();
+ }
+
+ private void onRelease() {
+ // Current recording will be canceled.
+ reset();
+ mHandler.getLooper().quitSafely();
+ // TODO: Remove failed recording files.
+ }
+}
diff --git a/usbtuner/src/com/android/usbtuner/tvinput/TvInputSessionImpl.java b/usbtuner/src/com/android/usbtuner/tvinput/TunerSession.java
index 94a8607a..da3f17f3 100644
--- a/usbtuner/src/com/android/usbtuner/tvinput/TvInputSessionImpl.java
+++ b/usbtuner/src/com/android/usbtuner/tvinput/TunerSession.java
@@ -16,12 +16,14 @@
package com.android.usbtuner.tvinput;
+import android.annotation.TargetApi;
import android.content.Context;
import android.media.PlaybackParams;
import android.media.tv.TvContentRating;
import android.media.tv.TvInputManager;
-import android.media.tv.TvTrackInfo;
+import android.media.tv.TvInputService;
import android.net.Uri;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
@@ -35,25 +37,22 @@ import android.widget.TextView;
import android.widget.Toast;
import com.google.android.exoplayer.audio.AudioCapabilities;
-import com.android.tv.common.recording.RecordingTvInputService;
import com.android.usbtuner.R;
import com.android.usbtuner.cc.CaptionLayout;
import com.android.usbtuner.cc.CaptionTrackRenderer;
import com.android.usbtuner.data.Cea708Data.CaptionEvent;
import com.android.usbtuner.data.Track.AtscCaptionTrack;
import com.android.usbtuner.data.TunerChannel;
-import com.android.usbtuner.exoplayer.CacheManager;
+import com.android.usbtuner.exoplayer.cache.CacheManager;
import com.android.usbtuner.util.StatusTextUtils;
import com.android.usbtuner.util.SystemPropertiesProxy;
-import java.util.ArrayList;
-
/**
- * Provides a USB tuner TV input session.
+ * Provides a USB tuner TV input session. It handles Overlay UI works. Main tuner input functions
+ * are implemented in {@link TunerSessionWorker}.
*/
-public class TvInputSessionImpl extends RecordingTvInputService.PlaybackSession
- implements Handler.Callback, TvInputSessionImplInternal.InternalListener {
- private static final String TAG = "TvInputSessionImpl";
+public class TunerSession extends TvInputService.Session implements Handler.Callback {
+ private static final String TAG = "TunerSession";
private static final boolean DEBUG = false;
private static final String USBTUNER_SHOW_DEBUG = "persist.usbtuner.show_debug";
@@ -76,13 +75,13 @@ public class TvInputSessionImpl extends RecordingTvInputService.PlaybackSession
private final TextView mAudioStatusView;
private final ViewGroup mMessageLayout;
private final CaptionTrackRenderer mCaptionTrackRenderer;
- private final TvInputSessionImplInternal mSessionImplInternal;
+ private final TunerSessionWorker mSessionWorker;
private boolean mReleased = false;
private boolean mVideoAvailable = false;
private boolean mPlayPaused;
private long mTuneStartTimestamp;
- public TvInputSessionImpl(Context context, ChannelDataManager channelDataManager,
+ public TunerSession(Context context, ChannelDataManager channelDataManager,
CacheManager cacheManager) {
super(context);
mContext = context;
@@ -102,9 +101,8 @@ public class TvInputSessionImpl extends RecordingTvInputService.PlaybackSession
context.getString(R.string.ut_ac3_passthrough_unavailable))));
CaptionLayout captionLayout = (CaptionLayout) mOverlayView.findViewById(R.id.caption);
mCaptionTrackRenderer = new CaptionTrackRenderer(captionLayout);
- mSessionImplInternal = new TvInputSessionImplInternal(context, channelDataManager,
- cacheManager);
- mSessionImplInternal.setInternalListener(this);
+ mSessionWorker = new TunerSessionWorker(context, channelDataManager,
+ cacheManager, this);
}
public boolean isReleased() {
@@ -118,67 +116,61 @@ public class TvInputSessionImpl extends RecordingTvInputService.PlaybackSession
@Override
public boolean onSelectTrack(int type, String trackId) {
- mSessionImplInternal.sendMessage(
- TvInputSessionImplInternal.MSG_SELECT_TRACK, type, 0, trackId);
+ mSessionWorker.sendMessage(
+ TunerSessionWorker.MSG_SELECT_TRACK, type, 0, trackId);
return false;
}
@Override
public void onSetCaptionEnabled(boolean enabled) {
- mSessionImplInternal.sendMessage(
- TvInputSessionImplInternal.MSG_SET_CAPTION_ENABLED, enabled);
+ mSessionWorker.sendMessage(
+ TunerSessionWorker.MSG_SET_CAPTION_ENABLED, enabled);
}
@Override
public void onSetStreamVolume(float volume) {
- mSessionImplInternal.sendMessage(TvInputSessionImplInternal.MSG_SET_STREAM_VOLUME, volume);
+ mSessionWorker.sendMessage(TunerSessionWorker.MSG_SET_STREAM_VOLUME, volume);
}
@Override
public boolean onSetSurface(Surface surface) {
- mSessionImplInternal.sendMessage(TvInputSessionImplInternal.MSG_SET_SURFACE, surface);
+ mSessionWorker.sendMessage(TunerSessionWorker.MSG_SET_SURFACE, surface);
return true;
}
@Override
public void onTimeShiftPause() {
- mSessionImplInternal.sendMessage(TvInputSessionImplInternal.MSG_TIMESHIFT_PAUSE);
+ mSessionWorker.sendMessage(TunerSessionWorker.MSG_TIMESHIFT_PAUSE);
mPlayPaused = true;
}
@Override
public void onTimeShiftResume() {
- mSessionImplInternal.sendMessage(TvInputSessionImplInternal.MSG_TIMESHIFT_RESUME);
+ mSessionWorker.sendMessage(TunerSessionWorker.MSG_TIMESHIFT_RESUME);
mPlayPaused = false;
}
@Override
public void onTimeShiftSeekTo(long timeMs) {
- if (DEBUG) {
- Log.d(TAG, "Timeshift seekTo requested position: " + timeMs / 1000);
- }
- mSessionImplInternal.sendMessage(TvInputSessionImplInternal.MSG_TIMESHIFT_SEEK_TO,
+ if (DEBUG) Log.d(TAG, "Timeshift seekTo requested position: " + timeMs / 1000);
+ mSessionWorker.sendMessage(TunerSessionWorker.MSG_TIMESHIFT_SEEK_TO,
mPlayPaused ? 1 : 0, 0, timeMs);
}
@Override
public void onTimeShiftSetPlaybackParams(PlaybackParams params) {
- mSessionImplInternal.sendMessage(
- TvInputSessionImplInternal.MSG_TIMESHIFT_SET_PLAYBACKPARAMS, params);
+ mSessionWorker.sendMessage(
+ TunerSessionWorker.MSG_TIMESHIFT_SET_PLAYBACKPARAMS, params);
}
@Override
public long onTimeShiftGetStartPosition() {
- Long duration = mSessionImplInternal.getDurationForRecording();
- if (duration != null) {
- notifyTimeShiftEndPosition(mSessionImplInternal.getStartPosition() + duration);
- }
- return mSessionImplInternal.getStartPosition();
+ return mSessionWorker.getStartPosition();
}
@Override
public long onTimeShiftGetCurrentPosition() {
- return mSessionImplInternal.getCurrentPosition();
+ return mSessionWorker.getCurrentPosition();
}
@Override
@@ -188,30 +180,31 @@ public class TvInputSessionImpl extends RecordingTvInputService.PlaybackSession
}
if (channelUri == null) {
Log.w(TAG, "onTune() is failed due to null channelUri.");
- mSessionImplInternal.stopTune();
+ mSessionWorker.stopTune();
return false;
}
mTuneStartTimestamp = SystemClock.elapsedRealtime();
- mSessionImplInternal.tune(channelUri);
+ mSessionWorker.tune(channelUri);
mPlayPaused = false;
return true;
}
+ @TargetApi(Build.VERSION_CODES.N)
@Override
- public void onPlayMedia(Uri recordUri) {
+ public void onTimeShiftPlay(Uri recordUri) {
if (recordUri == null) {
- Log.w(TAG, "onPlayMedia() is failed due to null channelUri.");
- mSessionImplInternal.stopTune();
+ Log.w(TAG, "onTimeShiftPlay() is failed due to null channelUri.");
+ mSessionWorker.stopTune();
return;
}
mTuneStartTimestamp = SystemClock.elapsedRealtime();
- mSessionImplInternal.tune(recordUri);
+ mSessionWorker.tune(recordUri);
mPlayPaused = false;
}
@Override
public void onUnblockContent(TvContentRating unblockedRating) {
- mSessionImplInternal.sendMessage(TvInputSessionImplInternal.MSG_UNBLOCKED_RATING,
+ mSessionWorker.sendMessage(TunerSessionWorker.MSG_UNBLOCKED_RATING,
unblockedRating);
}
@@ -221,36 +214,19 @@ public class TvInputSessionImpl extends RecordingTvInputService.PlaybackSession
Log.d(TAG, "onRelease");
}
mReleased = true;
- mSessionImplInternal.release();
+ mSessionWorker.release();
mUiHandler.removeCallbacksAndMessages(null);
}
- public void notifyAudioCapabilitiesChanged(AudioCapabilities audioCapabilities) {
- mSessionImplInternal.sendMessage(TvInputSessionImplInternal.MSG_AUDIO_CAPABILITIES_CHANGED,
+ /**
+ * Sets {@link AudioCapabilities}.
+ */
+ public void setAudioCapabilities(AudioCapabilities audioCapabilities) {
+ mSessionWorker.sendMessage(TunerSessionWorker.MSG_AUDIO_CAPABILITIES_CHANGED,
audioCapabilities);
}
@Override
- public void notifyContentAllowed() {
- super.notifyContentAllowed();
- }
-
- @Override
- public void notifyContentBlocked(TvContentRating rating) {
- super.notifyContentBlocked(rating);
- }
-
- @Override
- public void notifyTimeShiftStatusChanged(int status) {
- super.notifyTimeShiftStatusChanged(status);
- }
-
- @Override
- public void notifyTracksChanged(ArrayList<TvTrackInfo> tvTracks) {
- super.notifyTracksChanged(tvTracks);
- }
-
- @Override
public void notifyVideoAvailable() {
super.notifyVideoAvailable();
if (mTuneStartTimestamp != 0) {
@@ -267,22 +243,18 @@ public class TvInputSessionImpl extends RecordingTvInputService.PlaybackSession
case TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING:
case TvInputManager.VIDEO_UNAVAILABLE_REASON_BUFFERING:
case TvInputManager.VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY:
- super.notifyVideoUnavailable(reason);
- break;
case TvInputManager.VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL:
- super.notifyVideoAvailable();
- sendUiMessage(TvInputSessionImpl.MSG_UI_SHOW_MESSAGE,
- mContext.getString(R.string.ut_no_signal));
+ super.notifyVideoUnavailable(reason);
break;
case TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN:
default:
super.notifyVideoAvailable();
- TunerChannel channel = mSessionImplInternal.getCurrentChannel();
+ TunerChannel channel = mSessionWorker.getCurrentChannel();
if (channel != null) {
- sendUiMessage(TvInputSessionImpl.MSG_UI_SHOW_MESSAGE,
+ sendUiMessage(TunerSession.MSG_UI_SHOW_MESSAGE,
mContext.getString(R.string.ut_fail_to_tune, channel.getName()));
} else {
- sendUiMessage(TvInputSessionImpl.MSG_UI_SHOW_MESSAGE,
+ sendUiMessage(TunerSession.MSG_UI_SHOW_MESSAGE,
mContext.getString(R.string.ut_fail_to_tune_to_unknown_channel));
}
break;
@@ -293,17 +265,14 @@ public class TvInputSessionImpl extends RecordingTvInputService.PlaybackSession
}
}
- @Override
public void sendUiMessage(int message) {
mUiHandler.sendEmptyMessage(message);
}
- @Override
public void sendUiMessage(int message, Object object) {
mUiHandler.obtainMessage(message, object).sendToTarget();
}
- @Override
public void sendUiMessage(int message, int arg1, int arg2, Object object) {
mUiHandler.obtainMessage(message, arg1, arg2, object).sendToTarget();
}
@@ -312,11 +281,6 @@ public class TvInputSessionImpl extends RecordingTvInputService.PlaybackSession
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_UI_SHOW_MESSAGE: {
- if (!mVideoAvailable) {
- // A workaround to show error message before notifyVideoAvailable().
- mVideoAvailable = true;
- super.notifyVideoAvailable();
- }
mMessageView.setText((String) msg.obj);
mMessageLayout.setVisibility(View.VISIBLE);
return true;
diff --git a/usbtuner/src/com/android/usbtuner/tvinput/TvInputSessionImplInternal.java b/usbtuner/src/com/android/usbtuner/tvinput/TunerSessionWorker.java
index c49d0833..6b71fcf6 100644
--- a/usbtuner/src/com/android/usbtuner/tvinput/TvInputSessionImplInternal.java
+++ b/usbtuner/src/com/android/usbtuner/tvinput/TunerSessionWorker.java
@@ -16,12 +16,15 @@
package com.android.usbtuner.tvinput;
+import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
+import android.database.Cursor;
import android.media.MediaDataSource;
import android.media.MediaFormat;
import android.media.PlaybackParams;
import android.media.tv.TvContentRating;
+import android.media.tv.TvContract;
import android.media.tv.TvInputManager;
import android.media.tv.TvTrackInfo;
import android.net.Uri;
@@ -50,10 +53,10 @@ import com.android.usbtuner.data.PsipData.TvTracksInterface;
import com.android.usbtuner.data.Track.AtscAudioTrack;
import com.android.usbtuner.data.Track.AtscCaptionTrack;
import com.android.usbtuner.data.TunerChannel;
-import com.android.usbtuner.exoplayer.CacheManager;
-import com.android.usbtuner.exoplayer.DvrStorageManager;
import com.android.usbtuner.exoplayer.MpegTsPassthroughAc3RendererBuilder;
import com.android.usbtuner.exoplayer.MpegTsPlayer;
+import com.android.usbtuner.exoplayer.cache.CacheManager;
+import com.android.usbtuner.exoplayer.cache.DvrStorageManager;
import com.android.usbtuner.util.IsoUtils;
import com.android.usbtuner.util.StatusTextUtils;
@@ -69,17 +72,16 @@ import java.util.Objects;
import java.util.concurrent.CountDownLatch;
/**
- * {@link TvInputSessionImplInternal} implements a handler thread which processes TV input jobs
+ * {@link TunerSessionWorker} implements a handler thread which processes TV input jobs
* such as handling {@link ExoPlayer}, managing a tuner device, trickplay, and so on.
*/
-public class TvInputSessionImplInternal implements PlaybackCacheListener,
+public class TunerSessionWorker implements PlaybackCacheListener,
MpegTsPlayer.VideoEventListener, MpegTsPlayer.Listener, EventDetector.EventListener,
ChannelDataManager.ProgramInfoListener, Handler.Callback {
- private static final String TAG = "TvInputSessionInternal";
+ private static final String TAG = "TunerSessionWorker";
private static final boolean DEBUG = false;
private static final boolean ENABLE_PROFILER = true;
private static final String PLAY_FROM_CHANNEL = "channel";
- private static final String PLAY_FROM_RECORDING = "record";
// Public messages
public static final int MSG_SELECT_TRACK = 1;
@@ -158,7 +160,6 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
private String mRecordingId;
private volatile Long mRecordingDuration;
private final Handler mHandler;
- private final HandlerThread mHandlerThread;
private int mRetryCount;
private float mVolume;
private final ArrayList<TvTrackInfo> mTvTracks;
@@ -184,21 +185,24 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
private long mLastPositionInBytes = 0L;
private final CacheManager mCacheManager;
private final TvContentRatingCache mTvContentRatingCache = TvContentRatingCache.getInstance();
+ private final TunerSession mSession;
- public TvInputSessionImplInternal(Context context, ChannelDataManager channelDataManager,
- CacheManager cacheManager) {
+ public TunerSessionWorker(Context context, ChannelDataManager channelDataManager,
+ CacheManager cacheManager, TunerSession tunerSession) {
mContext = context;
- mTunerHal = TunerHal.getInstance(context);
+ mTunerHal = TunerHal.createInstance(context);
if (mTunerHal == null) {
throw new RuntimeException("Failed to open a DVB device");
}
// HandlerThread should be set up before it is registered as a listener in the all other
// components.
- mHandlerThread = new HandlerThread(TAG);
- mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper(), this);
+ HandlerThread handlerThread = new HandlerThread(TAG);
+ handlerThread.start();
+ mHandler = new Handler(handlerThread.getLooper(), this);
+ mSession = tunerSession;
mChannelDataManager = channelDataManager;
+ // TODO: need to refactor it for multi-tuner support.
mChannelDataManager.setListener(this);
mChannelDataManager.checkDataVersion(mContext);
mTvInputManager = (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE);
@@ -238,7 +242,7 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
private String getRecordingPath() {
- return mContext.getCacheDir().getAbsolutePath() + "/recording" + mRecordingId;
+ return Uri.parse(mRecordingId).getPath();
}
public Long getDurationForRecording() {
@@ -300,7 +304,7 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
} catch (InterruptedException e) {
Log.e(TAG, "Couldn't wait for finish of MSG_RELEASE", e);
} finally {
- mHandlerThread.quitSafely();
+ mHandler.getLooper().quitSafely();
}
}
@@ -333,7 +337,7 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
// MpegTsPlayer.VideoEventListener
@Override
public void onEmitCaptionEvent(Cea708Data.CaptionEvent event) {
- mInternalListener.sendUiMessage(TvInputSessionImpl.MSG_UI_PROCESS_CAPTION_TRACK, event);
+ mSession.sendUiMessage(TunerSession.MSG_UI_PROCESS_CAPTION_TRACK, event);
}
@Override
@@ -354,7 +358,7 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
@Override
public void onRescanNeeded() {
- mInternalListener.sendUiMessage(TvInputSessionImpl.MSG_UI_TOAST_RESCAN_NEEDED);
+ mSession.sendUiMessage(TunerSession.MSG_UI_TOAST_RESCAN_NEEDED);
}
@Override
@@ -389,90 +393,76 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
mChannelDataManager.notifyEventDetected(channel, items);
}
- // InternalListener
- public interface InternalListener {
- void sendUiMessage(int message);
- void sendUiMessage(int message, Object object);
- void sendUiMessage(int message, int arg1, int arg2, Object object);
- void notifyVideoAvailable();
- void notifyVideoUnavailable(int reason);
- void notifyTimeShiftStatusChanged(int status);
- void notifyContentBlocked(TvContentRating tvContentRating);
- void notifyContentAllowed();
- void notifyTracksChanged(ArrayList<TvTrackInfo> tvTracks);
- void notifyTrackSelected(int type, String trackId);
- }
-
- public void setInternalListener(TvInputSessionImpl internalListener) {
- mInternalListener = internalListener;
- }
-
- private InternalListener mInternalListener = new InternalListener() {
- @Override
- public void sendUiMessage(int message) {
- // do nothing.
- }
-
- @Override
- public void sendUiMessage(int message, Object object) {
- // do nothing.
- }
-
- @Override
- public void sendUiMessage(int message, int arg1, int arg2, Object object) {
- // do nothing.
- }
-
- @Override
- public void notifyVideoAvailable() {
- // do nothing.
+ private long parseChannel(Uri uri) {
+ try {
+ List<String> paths = uri.getPathSegments();
+ if (paths.size() > 1 && paths.get(0).equals(PLAY_FROM_CHANNEL)) {
+ return ContentUris.parseId(uri);
+ }
+ } catch (UnsupportedOperationException | NumberFormatException e) {
}
+ return -1;
+ }
- @Override
- public void notifyVideoUnavailable(int reason) {
- // do nothing.
- }
+ private static class RecordedProgram {
+ private long mChannelId;
+ private String mDataUri;
- @Override
- public void notifyTimeShiftStatusChanged(int status) {
- // do nothing.
- }
+ private static final String[] PROJECTION = {
+ TvContract.Programs.COLUMN_CHANNEL_ID,
+ TvContract.RecordedPrograms.COLUMN_RECORDING_DATA_URI,
+ };
- @Override
- public void notifyContentBlocked(TvContentRating tvContentRating) {
- // do nothing.
+ public RecordedProgram(Cursor cursor) {
+ int index = 0;
+ mChannelId = cursor.getLong(index++);
+ mDataUri = cursor.getString(index++);
}
- @Override
- public void notifyContentAllowed() {
- // do nothing.
+ public RecordedProgram(long channelId, String dataUri) {
+ mChannelId = channelId;
+ mDataUri = dataUri;
}
- @Override
- public void notifyTracksChanged(ArrayList<TvTrackInfo> tvTracks) {
- // do nothing.
+ public static RecordedProgram onQuery(Cursor c) {
+ RecordedProgram recording = null;
+ if (c != null && c.moveToNext()) {
+ recording = new RecordedProgram(c);
+ }
+ return recording;
}
- @Override
- public void notifyTrackSelected(int type, String trackId) {
- // do nothing.
+ public String getDataUri() {
+ return mDataUri;
}
- };
+ }
- private long parseChannel(Uri uri) {
- try {
- List<String> paths = uri.getPathSegments();
- if (paths.size() > 1 && paths.get(0).equals(PLAY_FROM_CHANNEL)) {
- return ContentUris.parseId(uri);
+ private RecordedProgram getRecordedProgram(Uri recordedUri) {
+ ContentResolver resolver = mContext.getContentResolver();
+ try(Cursor c = resolver.query(recordedUri, RecordedProgram.PROJECTION, null, null, null)) {
+ if (c != null) {
+ RecordedProgram result = RecordedProgram.onQuery(c);
+ if (DEBUG) {
+ Log.d(TAG, "Finished query for " + this);
+ }
+ return result;
+ } else {
+ if (c == null) {
+ Log.e(TAG, "Unknown query error for " + this);
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "Canceled query for " + this);
+ }
+ }
+ return null;
}
- } catch (UnsupportedOperationException | NumberFormatException e) {
}
- return -1;
}
private String parseRecording(Uri uri) {
- if (uri.getScheme().equals(PLAY_FROM_RECORDING)) {
- return uri.getPath();
+ RecordedProgram recording = getRecordedProgram(uri);
+ if (recording != null) {
+ return recording.getDataUri();
}
return null;
}
@@ -499,7 +489,7 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
if (channel == null && recording == null) {
Log.w(TAG, "onTune() is failed. Can't find channel for " + channelUri);
stopTune();
- mInternalListener.notifyVideoUnavailable(
+ mSession.notifyVideoUnavailable(
TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN);
return true;
}
@@ -508,7 +498,9 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
mChannelDataManager.requestProgramsData(channel);
}
prepareTune(channel, recording);
- mInternalListener.notifyContentAllowed();
+ // TODO: Need to refactor. notifyContentAllowed() should not be called if parental
+ // control is turned on.
+ mSession.notifyContentAllowed();
resetPlayback();
resetTvTracks();
mHandler.sendEmptyMessageDelayed(MSG_RESCHEDULE_PROGRAMS,
@@ -527,7 +519,7 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
resetTvTracks();
mTunerHal.stopTune();
mSource = null;
- mInternalListener.notifyVideoUnavailable(
+ mSession.notifyVideoUnavailable(
TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN);
return true;
}
@@ -564,7 +556,7 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
stopCaptionTrack();
mTunerHal.stopTune();
- mInternalListener.notifyVideoUnavailable(
+ mSession.notifyVideoUnavailable(
TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN);
// After MAX_RETRY_COUNT, give some delay of an empirically chosen value
@@ -642,6 +634,9 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
if (mChannel != null && mChannel.hasVideo()) {
updateVideoTrack(size.getWidth(), size.getHeight());
}
+ if (mRecordingId != null) {
+ updateVideoTrack(size.getWidth(), size.getHeight());
+ }
return true;
}
case MSG_AUDIO_UNPLAYABLE: {
@@ -650,8 +645,8 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
return true;
}
Log.i(TAG, "AC3 audio cannot be played due to device limitation");
- mInternalListener.sendUiMessage(
- TvInputSessionImpl.MSG_UI_SHOW_AUDIO_UNPLAYABLE);
+ mSession.sendUiMessage(
+ TunerSession.MSG_UI_SHOW_AUDIO_UNPLAYABLE);
return true;
}
case MSG_UPDATE_PROGRAM: {
@@ -728,7 +723,7 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
}
mCacheStartTimeMs = mRecordStartTimeMs =
(mRecordingId != null) ? 0 : System.currentTimeMillis();
- mInternalListener.notifyVideoAvailable();
+ mSession.notifyVideoAvailable();
mReportedDrawnToSurface = true;
// If surface is drawn successfully, it means that the playback was brought back
@@ -774,9 +769,11 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
return true;
}
case MSG_SELECT_TRACK: {
- // TODO : mChannel == null && mRecordingId != null
if (mChannel != null) {
doSelectTrack(msg.arg1, (String) msg.obj);
+ } else if (mRecordingId != null) {
+ // TODO : mChannel == null && mRecordingId != null
+ Log.d(TAG, "track selected for recording");
}
return true;
}
@@ -855,7 +852,7 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
}
case MSG_CACHE_STATE_CHANGED: {
boolean available = (boolean) msg.obj;
- mInternalListener.notifyTimeShiftStatusChanged(available
+ mSession.notifyTimeShiftStatusChanged(available
? TvInputManager.TIME_SHIFT_STATUS_AVAILABLE
: TvInputManager.TIME_SHIFT_STATUS_UNAVAILABLE);
return true;
@@ -868,7 +865,7 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
long positionInBytes = mSource != null ? mSource.getPosition() : 0L;
if (UsbTunerDebug.ENABLED) {
UsbTunerDebug.calculateDiff();
- mInternalListener.sendUiMessage(TvInputSessionImpl.MSG_UI_SET_STATUS_TEXT,
+ mSession.sendUiMessage(TunerSession.MSG_UI_SET_STATUS_TEXT,
Html.fromHtml(
StatusTextUtils.getStatusWarningInHTML(
(limitInBytes - mLastLimitInBytes)
@@ -887,17 +884,17 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
Log.d(TAG, String.format("MSG_CHECK_SIGNAL position: %d, limit: %d",
positionInBytes, limitInBytes));
}
- mInternalListener.sendUiMessage(TvInputSessionImpl.MSG_UI_HIDE_MESSAGE);
+ mSession.sendUiMessage(TunerSession.MSG_UI_HIDE_MESSAGE);
if (mSource != null && mChannel.getType() == Channel.TYPE_TUNER
&& positionInBytes == mLastPositionInBytes
&& limitInBytes == mLastLimitInBytes) {
- mInternalListener.notifyVideoUnavailable(
+ mSession.notifyVideoUnavailable(
TvInputManager.VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL);
mReportedSignalAvailable = false;
} else {
if (mReportedDrawnToSurface && !mReportedSignalAvailable) {
- mInternalListener.notifyVideoAvailable();
+ mSession.notifyVideoAvailable();
mReportedSignalAvailable = true;
}
}
@@ -933,10 +930,10 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
// TODO: Implement a switching between tracks more smoothly.
resetPlayback();
}
- mInternalListener.notifyTrackSelected(type, trackId);
+ mSession.notifyTrackSelected(type, trackId);
} else if (type == TvTrackInfo.TYPE_SUBTITLE) {
if (trackId == null) {
- mInternalListener.notifyTrackSelected(type, null);
+ mSession.notifyTrackSelected(type, null);
mCaptionTrack = null;
stopCaptionTrack();
return;
@@ -945,7 +942,7 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
if (track.getId().equals(trackId)) {
// The service number of the caption service is used for track id of a
// subtitle track. Passes the following track id on to TsParser.
- mInternalListener.notifyTrackSelected(type, trackId);
+ mSession.notifyTrackSelected(type, trackId);
mCaptionTrack = mCaptionTrackMap.get(numTrackId);
startCaptionTrack();
return;
@@ -961,19 +958,17 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
++mPlayerGeneration;
MpegTsPlayer player = new MpegTsPlayer(mPlayerGeneration,
- new MpegTsPassthroughAc3RendererBuilder(cacheManager, this),
- mHandler, capabilities);
+ new MpegTsPassthroughAc3RendererBuilder(mContext, cacheManager, this),
+ mHandler, capabilities, this);
Log.i(TAG, "Passthrough AC3 renderer");
- if (DEBUG) {
- Log.d(TAG, "ExoPlayer created: " + mPlayerGeneration);
- }
+ if (DEBUG) Log.d(TAG, "ExoPlayer created: " + mPlayerGeneration);
return player;
}
private void startCaptionTrack() {
if (mCaptionEnabled && mCaptionTrack != null) {
- mInternalListener.sendUiMessage(
- TvInputSessionImpl.MSG_UI_START_CAPTION_TRACK, mCaptionTrack);
+ mSession.sendUiMessage(
+ TunerSession.MSG_UI_START_CAPTION_TRACK, mCaptionTrack);
if (mPlayer != null) {
mPlayer.setCaptionServiceNumber(mCaptionTrack.serviceNumber);
}
@@ -984,15 +979,15 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
if (mPlayer != null) {
mPlayer.setCaptionServiceNumber(Cea708Data.EMPTY_SERVICE_NUMBER);
}
- mInternalListener.sendUiMessage(TvInputSessionImpl.MSG_UI_STOP_CAPTION_TRACK);
+ mSession.sendUiMessage(TunerSession.MSG_UI_STOP_CAPTION_TRACK);
}
private void resetTvTracks() {
mTvTracks.clear();
mAudioTrackMap.clear();
mCaptionTrackMap.clear();
- mInternalListener.sendUiMessage(TvInputSessionImpl.MSG_UI_RESET_CAPTION_TRACK);
- mInternalListener.notifyTracksChanged(mTvTracks);
+ mSession.sendUiMessage(TunerSession.MSG_UI_RESET_CAPTION_TRACK);
+ mSession.notifyTracksChanged(mTvTracks);
}
private void updateTvTracks(TvTracksInterface tvTracksInterface) {
@@ -1027,8 +1022,8 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
removeTvTracks(TvTrackInfo.TYPE_VIDEO);
mTvTracks.add(new TvTrackInfo.Builder(TvTrackInfo.TYPE_VIDEO, VIDEO_TRACK_ID)
.setVideoWidth(width).setVideoHeight(height).build());
- mInternalListener.notifyTracksChanged(mTvTracks);
- mInternalListener.notifyTrackSelected(TvTrackInfo.TYPE_VIDEO, VIDEO_TRACK_ID);
+ mSession.notifyTracksChanged(mTvTracks);
+ mSession.notifyTrackSelected(TvTrackInfo.TYPE_VIDEO, VIDEO_TRACK_ID);
}
private void updateAudioTracks(List<AtscAudioTrack> audioTracks) {
@@ -1068,7 +1063,7 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
++index;
}
}
- mInternalListener.notifyTracksChanged(mTvTracks);
+ mSession.notifyTracksChanged(mTvTracks);
}
private void updateCaptionTracks(List<AtscCaptionTrack> captionTracks) {
@@ -1096,7 +1091,7 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
mCaptionTrackMap.put(captionTrack.serviceNumber, captionTrack);
}
}
- mInternalListener.notifyTracksChanged(mTvTracks);
+ mSession.notifyTracksChanged(mTvTracks);
}
private void updateChannelInfo(TunerChannel channel) {
@@ -1126,7 +1121,7 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
}
}
mChannel.selectAudioTrack(index);
- mInternalListener.notifyTrackSelected(TvTrackInfo.TYPE_AUDIO,
+ mSession.notifyTrackSelected(TvTrackInfo.TYPE_AUDIO,
index == -1 ? null : AUDIO_TRACK_PREFIX + index);
// Reset playback if there is a change in the listening streaming PIDs.
@@ -1154,7 +1149,7 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
mPlayerStarted = false;
mReportedDrawnToSurface = false;
mReportedSignalAvailable = false;
- mInternalListener.sendUiMessage(TvInputSessionImpl.MSG_UI_HIDE_AUDIO_UNPLAYABLE);
+ mSession.sendUiMessage(TunerSession.MSG_UI_HIDE_AUDIO_UNPLAYABLE);
}
}
@@ -1165,7 +1160,7 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
}
if (mChannel != null && !mChannel.hasAudio()) {
// A channel needs to have a audio stream at least to play in exoPlayer.
- mInternalListener.notifyVideoUnavailable(
+ mSession.notifyVideoUnavailable(
TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN);
return;
}
@@ -1174,20 +1169,25 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
mPlayer.setPlayWhenReady(true);
mPlayer.setVolume(mVolume);
if (mChannel != null && !mChannel.hasVideo() && mChannel.hasAudio()) {
- mInternalListener.notifyVideoUnavailable(
+ mSession.notifyVideoUnavailable(
TvInputManager.VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY);
} else {
- mInternalListener.notifyVideoUnavailable(
+ mSession.notifyVideoUnavailable(
TvInputManager.VIDEO_UNAVAILABLE_REASON_BUFFERING);
}
- mInternalListener.sendUiMessage(TvInputSessionImpl.MSG_UI_HIDE_MESSAGE);
+ mSession.sendUiMessage(TunerSession.MSG_UI_HIDE_MESSAGE);
mPlayerStarted = true;
}
}
private void playFromChannel(long timestamp) {
long oldTimestamp;
- mSource = getDataSource(mChannel.getType());
+ mSource = null;
+ if (mChannel.getType() == Channel.TYPE_TUNER) {
+ mSource = mTunerSource;
+ } else if (mChannel.getType() == Channel.TYPE_FILE) {
+ mSource = mFileSource;
+ }
Assert.assertNotNull(mSource);
if (mSource.tuneToChannel(mChannel)) {
if (ENABLE_PROFILER) {
@@ -1199,7 +1199,6 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
mSource.startStream();
mPlayer = createPlayer(mAudioCapabilities, mCacheManager);
mPlayer.setCaptionServiceNumber(Cea708Data.EMPTY_SERVICE_NUMBER);
- mPlayer.addListener(this);
mPlayer.setVideoEventListener(this);
mPlayer.setCaptionServiceNumber(mCaptionTrack != null ?
mCaptionTrack.serviceNumber : Cea708Data.EMPTY_SERVICE_NUMBER);
@@ -1209,7 +1208,7 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
} else {
// Close TunerHal when tune fails.
mTunerHal.stopTune();
- mInternalListener.notifyVideoUnavailable(
+ mSession.notifyVideoUnavailable(
TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN);
}
}
@@ -1221,7 +1220,6 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
mSource = null;
mPlayer = createPlayer(mAudioCapabilities, cacheManager);
mPlayer.setCaptionServiceNumber(Cea708Data.EMPTY_SERVICE_NUMBER);
- mPlayer.addListener(this);
mPlayer.setVideoEventListener(this);
mPlayer.setCaptionServiceNumber(mCaptionTrack != null ?
mCaptionTrack.serviceNumber : Cea708Data.EMPTY_SERVICE_NUMBER);
@@ -1241,7 +1239,7 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
Log.i(TAG, "[Profiler] stopPlayback() takes " + (timestamp - oldTimestamp) + " ms");
}
if (!mChannelBlocked && mSurface != null) {
- mInternalListener.notifyVideoUnavailable(
+ mSession.notifyVideoUnavailable(
TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING);
if (mChannel != null) {
playFromChannel(timestamp);
@@ -1251,17 +1249,6 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
}
}
- private InputStreamSource getDataSource(int type) {
- switch (type) {
- case Channel.TYPE_TUNER:
- return mTunerSource;
- case Channel.TYPE_FILE:
- return mFileSource;
- default:
- return null;
- }
- }
-
private void prepareTune(TunerChannel channel, String recording) {
mChannelBlocked = false;
mUnblockedContentRating = null;
@@ -1413,7 +1400,7 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
mCaptionTrackMap.put(serviceNumber, captionTrack);
mTvTracks.add(new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE,
SUBTITLE_TRACK_PREFIX + serviceNumber).build());
- mInternalListener.notifyTracksChanged(mTvTracks);
+ mSession.notifyTracksChanged(mTvTracks);
}
}
@@ -1448,13 +1435,13 @@ public class TvInputSessionImplInternal implements PlaybackCacheListener,
stopPlayback();
resetTvTracks();
if (contentRating != null) {
- mInternalListener.notifyContentBlocked(contentRating);
+ mSession.notifyContentBlocked(contentRating);
}
mHandler.sendEmptyMessageDelayed(MSG_PARENTAL_CONTROLS, PARENTAL_CONTROLS_INTERVAL_MS);
} else {
mHandler.removeCallbacksAndMessages(null);
resetPlayback();
- mInternalListener.notifyContentAllowed();
+ mSession.notifyContentAllowed();
mHandler.sendEmptyMessageDelayed(MSG_RESCHEDULE_PROGRAMS,
RESCHEDULE_PROGRAMS_INITIAL_DELAY_MS);
mHandler.sendEmptyMessageDelayed(MSG_CHECK_SIGNAL, CHECK_NO_SIGNAL_INITIAL_DELAY_MS);
diff --git a/usbtuner/src/com/android/usbtuner/tvinput/UsbTunerDebug.java b/usbtuner/src/com/android/usbtuner/tvinput/UsbTunerDebug.java
index 5c24bed7..4732eea1 100644
--- a/usbtuner/src/com/android/usbtuner/tvinput/UsbTunerDebug.java
+++ b/usbtuner/src/com/android/usbtuner/tvinput/UsbTunerDebug.java
@@ -42,7 +42,6 @@ public class UsbTunerDebug {
private long mAudioPtsUsRate;
private long mVideoPtsUsRate;
-
private UsbTunerDebug() {
mVideoFrameDrop = 0;
mLastCheckTimestampMs = SystemClock.elapsedRealtime();
diff --git a/usbtuner/src/com/android/usbtuner/tvinput/UsbTunerTvInputService.java b/usbtuner/src/com/android/usbtuner/tvinput/UsbTunerTvInputService.java
index 2397e5f2..e91d8769 100644
--- a/usbtuner/src/com/android/usbtuner/tvinput/UsbTunerTvInputService.java
+++ b/usbtuner/src/com/android/usbtuner/tvinput/UsbTunerTvInputService.java
@@ -16,12 +16,14 @@
package com.android.usbtuner.tvinput;
+import android.content.ComponentName;
+import android.content.Context;
+import android.media.tv.TvContract;
import android.os.Environment;
import android.util.Log;
-import com.android.tv.common.recording.TvRecording;
-import com.android.usbtuner.exoplayer.CacheManager;
-import com.android.usbtuner.exoplayer.TrickplayStorageManager;
+import com.android.usbtuner.exoplayer.cache.CacheManager;
+import com.android.usbtuner.exoplayer.cache.TrickplayStorageManager;
import com.android.usbtuner.util.SystemPropertiesProxy;
import java.io.File;
@@ -39,7 +41,7 @@ public class UsbTunerTvInputService extends BaseTunerTvInputService {
private static final int MIN_CACHE_SIZE_DEF = 256; // 256MB
@Override
- protected void maybeInitCacheManager() {
+ protected CacheManager createCacheManager() {
int maxCacheSizeMb = SystemPropertiesProxy.getInt(MAX_CACHE_SIZE_KEY, MAX_CACHE_SIZE_DEF);
if (maxCacheSizeMb >= MIN_CACHE_SIZE_DEF) {
boolean useExternalStorage = Environment.MEDIA_MOUNTED.equals(
@@ -49,11 +51,15 @@ public class UsbTunerTvInputService extends BaseTunerTvInputService {
boolean allowToUseInternalStorage = true;
if (useExternalStorage || allowToUseInternalStorage) {
File baseDir = useExternalStorage ? getExternalCacheDir() : getCacheDir();
- mCacheManager = new CacheManager(
+ return new CacheManager(
new TrickplayStorageManager(getApplicationContext(), baseDir,
1024L * 1024 * maxCacheSizeMb));
}
}
+ return null;
}
+ public static String getInputId(Context context) {
+ return TvContract.buildInputId(new ComponentName(context, UsbTunerTvInputService.class));
+ }
}
diff --git a/usbtuner/src/com/google/android/exoplayer/MediaFormatUtil.java b/usbtuner/src/com/google/android/exoplayer/MediaFormatUtil.java
new file mode 100644
index 00000000..5a5713e3
--- /dev/null
+++ b/usbtuner/src/com/google/android/exoplayer/MediaFormatUtil.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.exoplayer;
+
+import android.support.annotation.Nullable;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/** {@link MediaFormat} creation helper util */
+public class MediaFormatUtil {
+
+ /**
+ * Creates {@link MediaFormat} from {@link android.media.MediaFormat}.
+ * Since {@link com.google.android.exoplayer.TrackRenderer} uses {@link MediaFormat},
+ * {@link android.media.MediaFormat} should be converted to be used with ExoPlayer.
+ */
+ public static MediaFormat createMediaFormat(android.media.MediaFormat format) {
+ // TODO: Add test for this method.
+ String mimeType = format.getString(android.media.MediaFormat.KEY_MIME);
+ String language = getOptionalStringV16(format, android.media.MediaFormat.KEY_LANGUAGE);
+ int maxInputSize =
+ getOptionalIntegerV16(format, android.media.MediaFormat.KEY_MAX_INPUT_SIZE);
+ int width = getOptionalIntegerV16(format, android.media.MediaFormat.KEY_WIDTH);
+ int height = getOptionalIntegerV16(format, android.media.MediaFormat.KEY_HEIGHT);
+ int rotationDegrees = getOptionalIntegerV16(format, "rotation-degrees");
+ int channelCount =
+ getOptionalIntegerV16(format, android.media.MediaFormat.KEY_CHANNEL_COUNT);
+ int sampleRate = getOptionalIntegerV16(format, android.media.MediaFormat.KEY_SAMPLE_RATE);
+ int encoderDelay = getOptionalIntegerV16(format, "encoder-delay");
+ int encoderPadding = getOptionalIntegerV16(format, "encoder-padding");
+ ArrayList<byte[]> initializationData = new ArrayList<>();
+ for (int i = 0; format.containsKey("csd-" + i); i++) {
+ ByteBuffer buffer = format.getByteBuffer("csd-" + i);
+ byte[] data = new byte[buffer.limit()];
+ buffer.get(data);
+ initializationData.add(data);
+ buffer.flip();
+ }
+ long durationUs = format.containsKey(android.media.MediaFormat.KEY_DURATION)
+ ? format.getLong(android.media.MediaFormat.KEY_DURATION) : C.UNKNOWN_TIME_US;
+ MediaFormat mediaFormat = new MediaFormat(null, mimeType, MediaFormat.NO_VALUE,
+ maxInputSize, durationUs, width, height, rotationDegrees, MediaFormat.NO_VALUE,
+ channelCount, sampleRate, language, MediaFormat.OFFSET_SAMPLE_RELATIVE,
+ initializationData, false, MediaFormat.NO_VALUE, MediaFormat.NO_VALUE, encoderDelay,
+ encoderPadding);
+ mediaFormat.setFrameworkFormatV16(format);
+ return mediaFormat;
+ }
+
+ /**
+ * Creates {@link MediaFormat} for audio track.
+ */
+ public static MediaFormat createAudioMediaFormat(String mimeType, long durationUs,
+ int channelCount, int sampleRate) {
+ return MediaFormat.createAudioFormat(null, mimeType, MediaFormat.NO_VALUE,
+ MediaFormat.NO_VALUE, durationUs, channelCount, sampleRate, null, "");
+ }
+
+ /**
+ * Creates {@link MediaFormat} for closed caption track.
+ */
+ public static MediaFormat createTextMediaFormat(String mimeType, long durationUs) {
+ return new MediaFormat(null, mimeType, 0, MediaFormat.NO_VALUE, durationUs,
+ MediaFormat.NO_VALUE, MediaFormat.NO_VALUE, MediaFormat.NO_VALUE,
+ MediaFormat.NO_VALUE, MediaFormat.NO_VALUE, MediaFormat.NO_VALUE, "",
+ MediaFormat.OFFSET_SAMPLE_RELATIVE, null, false, MediaFormat.NO_VALUE,
+ MediaFormat.NO_VALUE, MediaFormat.NO_VALUE, MediaFormat.NO_VALUE);
+ }
+
+ @Nullable
+ private static final String getOptionalStringV16(android.media.MediaFormat format, String key) {
+ return format.containsKey(key) ? format.getString(key) : null;
+ }
+
+ private static final int getOptionalIntegerV16(android.media.MediaFormat format, String key) {
+ return format.containsKey(key) ? format.getInteger(key) : MediaFormat.NO_VALUE;
+ }
+
+}
diff --git a/usbtuner/src/com/google/android/exoplayer/MediaSoftwareCodecUtil.java b/usbtuner/src/com/google/android/exoplayer/MediaSoftwareCodecUtil.java
new file mode 100644
index 00000000..4b605fbb
--- /dev/null
+++ b/usbtuner/src/com/google/android/exoplayer/MediaSoftwareCodecUtil.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.exoplayer;
+
+import com.google.android.exoplayer.util.MimeTypes;
+
+import android.annotation.TargetApi;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import java.util.HashMap;
+
+/**
+ * Mostly copied from {@link com.google.android.exoplayer.MediaCodecUtil} in order to choose
+ * software codec over hardware codec.
+ */
+public class MediaSoftwareCodecUtil {
+ private static final String TAG = "MediaSoftwareCodecUtil";
+
+ /**
+ * Thrown when an error occurs querying the device for its underlying media capabilities.
+ * <p>
+ * Such failures are not expected in normal operation and are normally temporary (e.g. if the
+ * mediaserver process has crashed and is yet to restart).
+ */
+ public static class DecoderQueryException extends Exception {
+
+ private DecoderQueryException(Throwable cause) {
+ super("Failed to query underlying media codecs", cause);
+ }
+
+ }
+
+ private static final HashMap<CodecKey, Pair<String, MediaCodecInfo.CodecCapabilities>>
+ sSwCodecs = new HashMap<>();
+
+ /**
+ * Gets information about the software decoder that will be used for a given mime type.
+ */
+ public static DecoderInfo getSoftwareDecoderInfo(String mimeType, boolean secure)
+ throws DecoderQueryException {
+ // TODO: Add a test for this method.
+ Pair<String, MediaCodecInfo.CodecCapabilities> info =
+ getMediaSoftwareCodecInfo(mimeType, secure);
+ if (info == null) {
+ return null;
+ }
+ return new DecoderInfo(info.first, info.second.isFeatureSupported(
+ MediaCodecInfo.CodecCapabilities.FEATURE_AdaptivePlayback));
+ }
+
+ /**
+ * Returns the name of the software decoder and its capabilities for the given mimeType.
+ */
+ private static synchronized Pair<String, MediaCodecInfo.CodecCapabilities>
+ getMediaSoftwareCodecInfo(String mimeType, boolean secure) throws DecoderQueryException {
+ CodecKey key = new CodecKey(mimeType, secure);
+ if (sSwCodecs.containsKey(key)) {
+ return sSwCodecs.get(key);
+ }
+ MediaCodecListCompat mediaCodecList = new MediaCodecListCompatV21(secure);
+ Pair<String, MediaCodecInfo.CodecCapabilities> codecInfo =
+ getMediaSoftwareCodecInfo(key, mediaCodecList);
+ if (secure && codecInfo == null) {
+ // Some devices don't list secure decoders on API level 21. Try the legacy path.
+ mediaCodecList = new MediaCodecListCompatV16();
+ codecInfo = getMediaSoftwareCodecInfo(key, mediaCodecList);
+ if (codecInfo != null) {
+ Log.w(TAG, "MediaCodecList API didn't list secure decoder for: " + mimeType
+ + ". Assuming: " + codecInfo.first);
+ }
+ }
+ return codecInfo;
+ }
+
+ private static Pair<String, MediaCodecInfo.CodecCapabilities> getMediaSoftwareCodecInfo(
+ CodecKey key, MediaCodecListCompat mediaCodecList) throws DecoderQueryException {
+ try {
+ return getMediaSoftwareCodecInfoInternal(key, mediaCodecList);
+ } catch (Exception e) {
+ // If the underlying mediaserver is in a bad state, we may catch an
+ // IllegalStateException or an IllegalArgumentException here.
+ throw new DecoderQueryException(e);
+ }
+ }
+
+ private static Pair<String, MediaCodecInfo.CodecCapabilities> getMediaSoftwareCodecInfoInternal(
+ CodecKey key, MediaCodecListCompat mediaCodecList) {
+ String mimeType = key.mimeType;
+ int numberOfCodecs = mediaCodecList.getCodecCount();
+ boolean secureDecodersExplicit = mediaCodecList.secureDecodersExplicit();
+ // Note: MediaCodecList is sorted by the framework such that the best decoders come first.
+ for (int i = 0; i < numberOfCodecs; i++) {
+ MediaCodecInfo info = mediaCodecList.getCodecInfoAt(i);
+ String codecName = info.getName();
+ if (!info.isEncoder() && codecName.startsWith("OMX.google.")
+ && (secureDecodersExplicit || !codecName.endsWith(".secure"))) {
+ String[] supportedTypes = info.getSupportedTypes();
+ for (int j = 0; j < supportedTypes.length; j++) {
+ String supportedType = supportedTypes[j];
+ if (supportedType.equalsIgnoreCase(mimeType)) {
+ MediaCodecInfo.CodecCapabilities capabilities =
+ info.getCapabilitiesForType(supportedType);
+ boolean secure = mediaCodecList.isSecurePlaybackSupported(
+ key.mimeType, capabilities);
+ if (!secureDecodersExplicit) {
+ // Cache variants for both insecure and (if we think it's supported)
+ // secure playback.
+ sSwCodecs.put(key.secure ? new CodecKey(mimeType, false) : key,
+ Pair.create(codecName, capabilities));
+ if (secure) {
+ sSwCodecs.put(key.secure ? key : new CodecKey(mimeType, true),
+ Pair.create(codecName + ".secure", capabilities));
+ }
+ } else {
+ // Only cache this variant. If both insecure and secure decoders are
+ // available, they should both be listed separately.
+ sSwCodecs.put(
+ key.secure == secure ? key : new CodecKey(mimeType, secure),
+ Pair.create(codecName, capabilities));
+ }
+ if (sSwCodecs.containsKey(key)) {
+ return sSwCodecs.get(key);
+ }
+ }
+ }
+ }
+ }
+ sSwCodecs.put(key, null);
+ return null;
+ }
+
+ private interface MediaCodecListCompat {
+
+ /**
+ * Returns the number of codecs in the list.
+ */
+ public int getCodecCount();
+
+ /**
+ * Returns the info at the specified index in the list.
+ *
+ * @param index The index.
+ */
+ public MediaCodecInfo getCodecInfoAt(int index);
+
+ /**
+ * Returns whether secure decoders are explicitly listed, if present.
+ */
+ public boolean secureDecodersExplicit();
+
+ /**
+ * Returns true if secure playback is supported for the given
+ * {@link android.media.MediaCodecInfo.CodecCapabilities}, which should
+ * have been obtained from a {@link MediaCodecInfo} obtained from this list.
+ */
+ public boolean isSecurePlaybackSupported(String mimeType,
+ MediaCodecInfo.CodecCapabilities capabilities);
+
+ }
+
+ @TargetApi(21)
+ private static final class MediaCodecListCompatV21 implements MediaCodecListCompat {
+
+ private final int codecKind;
+
+ private MediaCodecInfo[] mediaCodecInfos;
+
+ public MediaCodecListCompatV21(boolean includeSecure) {
+ codecKind = includeSecure ? MediaCodecList.ALL_CODECS : MediaCodecList.REGULAR_CODECS;
+ }
+
+ @Override
+ public int getCodecCount() {
+ ensureMediaCodecInfosInitialized();
+ return mediaCodecInfos.length;
+ }
+
+ @Override
+ public MediaCodecInfo getCodecInfoAt(int index) {
+ ensureMediaCodecInfosInitialized();
+ return mediaCodecInfos[index];
+ }
+
+ @Override
+ public boolean secureDecodersExplicit() {
+ return true;
+ }
+
+ @Override
+ public boolean isSecurePlaybackSupported(String mimeType,
+ MediaCodecInfo.CodecCapabilities capabilities) {
+ return capabilities.isFeatureSupported(
+ MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback);
+ }
+
+ private void ensureMediaCodecInfosInitialized() {
+ if (mediaCodecInfos == null) {
+ mediaCodecInfos = new MediaCodecList(codecKind).getCodecInfos();
+ }
+ }
+
+ }
+
+ @SuppressWarnings("deprecation")
+ private static final class MediaCodecListCompatV16 implements MediaCodecListCompat {
+
+ @Override
+ public int getCodecCount() {
+ return MediaCodecList.getCodecCount();
+ }
+
+ @Override
+ public MediaCodecInfo getCodecInfoAt(int index) {
+ return MediaCodecList.getCodecInfoAt(index);
+ }
+
+ @Override
+ public boolean secureDecodersExplicit() {
+ return false;
+ }
+
+ @Override
+ public boolean isSecurePlaybackSupported(String mimeType,
+ MediaCodecInfo.CodecCapabilities capabilities) {
+ // Secure decoders weren't explicitly listed prior to API level 21. We assume that
+ // a secure H264 decoder exists.
+ return MimeTypes.VIDEO_H264.equals(mimeType);
+ }
+
+ }
+
+ private static final class CodecKey {
+
+ public final String mimeType;
+ public final boolean secure;
+
+ public CodecKey(String mimeType, boolean secure) {
+ this.mimeType = mimeType;
+ this.secure = secure;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((mimeType == null) ? 0 : mimeType.hashCode());
+ result = 2 * result + (secure ? 0 : 1);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof CodecKey)) {
+ return false;
+ }
+ CodecKey other = (CodecKey) obj;
+ return TextUtils.equals(mimeType, other.mimeType) && secure == other.secure;
+ }
+
+ }
+
+}
diff --git a/usbtuner/src/com/google/android/exoplayer/text/SubtitleView.java b/usbtuner/src/com/google/android/exoplayer/text/SubtitleView.java
new file mode 100644
index 00000000..b21d6be3
--- /dev/null
+++ b/usbtuner/src/com/google/android/exoplayer/text/SubtitleView.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.exoplayer.text;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Join;
+import android.graphics.Paint.Style;
+import android.graphics.RectF;
+import android.graphics.Typeface;
+import android.text.Layout.Alignment;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.View;
+
+import com.google.android.exoplayer.util.Util;
+
+/**
+ * Since this class does not exist in recent version of ExoPlayer and used by
+ * {@link com.android.usbtuner.cc.CaptionWindowLayout}, this class is copied from
+ * older version of ExoPlayer.
+ * A view for rendering a single caption.
+ */
+@Deprecated
+public class SubtitleView extends View {
+ // TODO: Change usage of this class to up-to-date class of ExoPlayer.
+
+ /**
+ * Ratio of inner padding to font size.
+ */
+ private static final float INNER_PADDING_RATIO = 0.125f;
+
+ /**
+ * Temporary rectangle used for computing line bounds.
+ */
+ private final RectF mLineBounds = new RectF();
+
+ // Styled dimensions.
+ private final float mCornerRadius;
+ private final float mOutlineWidth;
+ private final float mShadowRadius;
+ private final float mShadowOffset;
+
+ private TextPaint mTextPaint;
+ private Paint mPaint;
+
+ private CharSequence mText;
+
+ private int mForegroundColor;
+ private int mBackgroundColor;
+ private int mEdgeColor;
+ private int mEdgeType;
+
+ private boolean mHasMeasurements;
+ private int mLastMeasuredWidth;
+ private StaticLayout mLayout;
+
+ private Alignment mAlignment;
+ private float mSpacingMult;
+ private float mSpacingAdd;
+ private int mInnerPaddingX;
+
+ public SubtitleView(Context context) {
+ this(context, null);
+ }
+
+ public SubtitleView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SubtitleView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ int[] viewAttr = {android.R.attr.text, android.R.attr.textSize,
+ android.R.attr.lineSpacingExtra, android.R.attr.lineSpacingMultiplier};
+ TypedArray a = context.obtainStyledAttributes(attrs, viewAttr, defStyleAttr, 0);
+ CharSequence text = a.getText(0);
+ int textSize = a.getDimensionPixelSize(1, 15);
+ mSpacingAdd = a.getDimensionPixelSize(2, 0);
+ mSpacingMult = a.getFloat(3, 1);
+ a.recycle();
+
+ Resources resources = getContext().getResources();
+ DisplayMetrics displayMetrics = resources.getDisplayMetrics();
+ int twoDpInPx =
+ Math.round((2f * displayMetrics.densityDpi) / DisplayMetrics.DENSITY_DEFAULT);
+ mCornerRadius = twoDpInPx;
+ mOutlineWidth = twoDpInPx;
+ mShadowRadius = twoDpInPx;
+ mShadowOffset = twoDpInPx;
+
+ mTextPaint = new TextPaint();
+ mTextPaint.setAntiAlias(true);
+ mTextPaint.setSubpixelText(true);
+
+ mAlignment = Alignment.ALIGN_CENTER;
+
+ mPaint = new Paint();
+ mPaint.setAntiAlias(true);
+
+ mInnerPaddingX = 0;
+ setText(text);
+ setTextSize(textSize);
+ setStyle(CaptionStyleCompat.DEFAULT);
+ }
+
+ @Override
+ public void setBackgroundColor(int color) {
+ mBackgroundColor = color;
+ forceUpdate(false);
+ }
+
+ /**
+ * Sets the text to be displayed by the view.
+ *
+ * @param text The text to display.
+ */
+ public void setText(CharSequence text) {
+ this.mText = text;
+ forceUpdate(true);
+ }
+
+ /**
+ * Sets the text size in pixels.
+ *
+ * @param size The text size in pixels.
+ */
+ public void setTextSize(float size) {
+ if (mTextPaint.getTextSize() != size) {
+ mTextPaint.setTextSize(size);
+ mInnerPaddingX = (int) (size * INNER_PADDING_RATIO + 0.5f);
+ forceUpdate(true);
+ }
+ }
+
+ /**
+ * Sets the text alignment.
+ *
+ * @param textAlignment The text alignment.
+ */
+ public void setTextAlignment(Alignment textAlignment) {
+ mAlignment = textAlignment;
+ }
+
+ /**
+ * Configures the view according to the given style.
+ *
+ * @param style A style for the view.
+ */
+ public void setStyle(CaptionStyleCompat style) {
+ mForegroundColor = style.foregroundColor;
+ mBackgroundColor = style.backgroundColor;
+ mEdgeType = style.edgeType;
+ mEdgeColor = style.edgeColor;
+ setTypeface(style.typeface);
+ super.setBackgroundColor(style.windowColor);
+ forceUpdate(true);
+ }
+
+ private void setTypeface(Typeface typeface) {
+ if (mTextPaint.getTypeface() != typeface) {
+ mTextPaint.setTypeface(typeface);
+ forceUpdate(true);
+ }
+ }
+
+ private void forceUpdate(boolean needsLayout) {
+ if (needsLayout) {
+ mHasMeasurements = false;
+ requestLayout();
+ }
+ invalidate();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int widthSpec = MeasureSpec.getSize(widthMeasureSpec);
+
+ if (computeMeasurements(widthSpec)) {
+ final StaticLayout layout = this.mLayout;
+ final int paddingX = getPaddingLeft() + getPaddingRight() + mInnerPaddingX * 2;
+ final int height = layout.getHeight() + getPaddingTop() + getPaddingBottom();
+ int width = 0;
+ int lineCount = layout.getLineCount();
+ for (int i = 0; i < lineCount; i++) {
+ width = Math.max((int) Math.ceil(layout.getLineWidth(i)), width);
+ }
+ width += paddingX;
+ setMeasuredDimension(width, height);
+ } else if (Util.SDK_INT >= 11) {
+ setTooSmallMeasureDimensionV11();
+ } else {
+ setMeasuredDimension(0, 0);
+ }
+ }
+
+ @TargetApi(11)
+ private void setTooSmallMeasureDimensionV11() {
+ setMeasuredDimension(MEASURED_STATE_TOO_SMALL, MEASURED_STATE_TOO_SMALL);
+ }
+
+ @Override
+ public void onLayout(boolean changed, int l, int t, int r, int b) {
+ final int width = r - l;
+ computeMeasurements(width);
+ }
+
+ private boolean computeMeasurements(int maxWidth) {
+ if (mHasMeasurements && maxWidth == mLastMeasuredWidth) {
+ return true;
+ }
+
+ // Account for padding.
+ final int paddingX = getPaddingLeft() + getPaddingRight() + mInnerPaddingX * 2;
+ maxWidth -= paddingX;
+ if (maxWidth <= 0) {
+ return false;
+ }
+
+ mHasMeasurements = true;
+ mLastMeasuredWidth = maxWidth;
+ mLayout = new StaticLayout(mText, mTextPaint, maxWidth, mAlignment,
+ mSpacingMult, mSpacingAdd, true);
+ return true;
+ }
+
+ @Override
+ protected void onDraw(Canvas c) {
+ final StaticLayout layout = this.mLayout;
+ if (layout == null) {
+ return;
+ }
+
+ final int saveCount = c.save();
+ final int innerPaddingX = this.mInnerPaddingX;
+ c.translate(getPaddingLeft() + innerPaddingX, getPaddingTop());
+
+ final int lineCount = layout.getLineCount();
+ final Paint textPaint = this.mTextPaint;
+ final Paint paint = this.mPaint;
+ final RectF bounds = mLineBounds;
+
+ if (Color.alpha(mBackgroundColor) > 0) {
+ final float cornerRadius = this.mCornerRadius;
+ float previousBottom = layout.getLineTop(0);
+
+ paint.setColor(mBackgroundColor);
+ paint.setStyle(Style.FILL);
+
+ for (int i = 0; i < lineCount; i++) {
+ bounds.left = layout.getLineLeft(i) - innerPaddingX;
+ bounds.right = layout.getLineRight(i) + innerPaddingX;
+ bounds.top = previousBottom;
+ bounds.bottom = layout.getLineBottom(i);
+ previousBottom = bounds.bottom;
+
+ c.drawRoundRect(bounds, cornerRadius, cornerRadius, paint);
+ }
+ }
+
+ if (mEdgeType == CaptionStyleCompat.EDGE_TYPE_OUTLINE) {
+ textPaint.setStrokeJoin(Join.ROUND);
+ textPaint.setStrokeWidth(mOutlineWidth);
+ textPaint.setColor(mEdgeColor);
+ textPaint.setStyle(Style.FILL_AND_STROKE);
+ layout.draw(c);
+ } else if (mEdgeType == CaptionStyleCompat.EDGE_TYPE_DROP_SHADOW) {
+ textPaint.setShadowLayer(mShadowRadius, mShadowOffset, mShadowOffset, mEdgeColor);
+ } else if (mEdgeType == CaptionStyleCompat.EDGE_TYPE_RAISED
+ || mEdgeType == CaptionStyleCompat.EDGE_TYPE_DEPRESSED) {
+ boolean raised = mEdgeType == CaptionStyleCompat.EDGE_TYPE_RAISED;
+ int colorUp = raised ? Color.WHITE : mEdgeColor;
+ int colorDown = raised ? mEdgeColor : Color.WHITE;
+ float offset = mShadowRadius / 2f;
+ textPaint.setColor(mForegroundColor);
+ textPaint.setStyle(Style.FILL);
+ textPaint.setShadowLayer(mShadowRadius, -offset, -offset, colorUp);
+ layout.draw(c);
+ textPaint.setShadowLayer(mShadowRadius, offset, offset, colorDown);
+ }
+
+ textPaint.setColor(mForegroundColor);
+ textPaint.setStyle(Style.FILL);
+ layout.draw(c);
+ textPaint.setShadowLayer(0, 0, 0, 0);
+ c.restoreToCount(saveCount);
+ }
+
+}
diff --git a/version.mk b/version.mk
index 70f4ec4c..e0d8cd02 100644
--- a/version.mk
+++ b/version.mk
@@ -48,17 +48,13 @@
base_version_major := 1
# Change this for each branch
-base_version_minor := 09
-# The date of the first commit checked in to the current branch
-base_version_since := 2015-11-21
+base_version_minor := 10
# code_version_major will overflow at 22
code_version_major := $(shell echo $$(($(base_version_major)+3)))
-git_commit_count := $(shell git --git-dir $(LOCAL_PATH)/.git rev-list --since=$(base_version_since) --all --count HEAD)
# x86 and arm sometimes don't match.
-#code_version_build := $(shell printf "%03d" $(git_commit_count))
-code_version_build := 420
+code_version_build := 596
#####################################################
#####################################################
# Collect automatic version code parameters