aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2018-06-08 11:08:06 -0700
committerXin Li <delphij@google.com>2018-06-08 11:08:06 -0700
commit3d7a02a34d2e0061d55d4a56721c261d94284eb6 (patch)
treec720748dfccbb7c22ffecdf5767b2af564ab703e
parentd195718c2e752876c207fe6d3d7a9a7e5f3692d4 (diff)
parent35b6f17937f6923110447cc8db0721825fd6060d (diff)
downloadTV-3d7a02a34d2e0061d55d4a56721c261d94284eb6.tar.gz
Merge pi-dev-plus-aosp-without-vendor into stage-aosp-master
Bug: 79597307 Change-Id: I2aed66909831766ab308bfe4dd32f310d533c05a
-rw-r--r--Android.mk85
-rw-r--r--AndroidManifest.xml320
-rw-r--r--LiveChannelsAndroidStyle.xml280
-rw-r--r--ResourceManifest.xml22
-rw-r--r--common/Android.mk8
-rw-r--r--common/AndroidManifest.xml2
-rw-r--r--common/BuildConfig.java.in1
-rw-r--r--common/res/values/strings.xml9
-rw-r--r--common/src/com/android/tv/common/BaseApplication.java107
-rw-r--r--common/src/com/android/tv/common/BaseSingletons.java35
-rw-r--r--common/src/com/android/tv/common/BooleanSystemProperty.java16
-rw-r--r--common/src/com/android/tv/common/CommonConstants.java28
-rw-r--r--common/src/com/android/tv/common/CommonPreferenceProvider.java (renamed from src/com/android/tv/tuner/TunerPreferenceProvider.java)68
-rw-r--r--common/src/com/android/tv/common/CommonPreferences.java332
-rw-r--r--common/src/com/android/tv/common/SoftPreconditions.java151
-rw-r--r--common/src/com/android/tv/common/TvCommonConstants.java68
-rw-r--r--common/src/com/android/tv/common/TvCommonUtils.java74
-rw-r--r--common/src/com/android/tv/common/TvContentRatingCache.java34
-rw-r--r--common/src/com/android/tv/common/WeakHandler.java9
-rw-r--r--common/src/com/android/tv/common/actions/InputSetupActionUtils.java127
-rw-r--r--common/src/com/android/tv/common/annotation/UsedByNative.java43
-rw-r--r--common/src/com/android/tv/common/concurrent/NamedThreadFactory.java (renamed from src/com/android/tv/util/NamedThreadFactory.java)11
-rw-r--r--common/src/com/android/tv/common/config/DefaultConfigManager.java (renamed from src/com/android/tv/config/DefaultConfigManager.java)21
-rw-r--r--common/src/com/android/tv/common/config/RemoteConfigFeature.java (renamed from src/com/android/tv/config/RemoteConfigFeature.java)9
-rw-r--r--common/src/com/android/tv/common/config/api/RemoteConfig.java (renamed from src/com/android/tv/config/RemoteConfig.java)33
-rw-r--r--common/src/com/android/tv/common/config/api/RemoteConfigValue.java48
-rw-r--r--common/src/com/android/tv/common/customization/CustomAction.java (renamed from src/com/android/tv/customization/CustomAction.java)25
-rw-r--r--common/src/com/android/tv/common/customization/CustomizationManager.java (renamed from src/com/android/tv/customization/TvCustomizationManager.java)98
-rw-r--r--common/src/com/android/tv/common/experiments/ExperimentFlag.java (renamed from src/com/android/tv/experiments/ExperimentFlag.java)11
-rw-r--r--common/src/com/android/tv/common/experiments/ExperimentLoader.java28
-rw-r--r--common/src/com/android/tv/common/experiments/Experiments.java (renamed from src/com/android/tv/experiments/Experiments.java)30
-rw-r--r--common/src/com/android/tv/common/feature/CommonFeatures.java71
-rw-r--r--common/src/com/android/tv/common/feature/EngOnlyFeature.java7
-rw-r--r--common/src/com/android/tv/common/feature/ExperimentFeature.java44
-rw-r--r--common/src/com/android/tv/common/feature/Feature.java13
-rw-r--r--common/src/com/android/tv/common/feature/FeatureUtils.java74
-rw-r--r--common/src/com/android/tv/common/feature/GServiceFeature.java5
-rw-r--r--common/src/com/android/tv/common/feature/Model.java48
-rw-r--r--common/src/com/android/tv/common/feature/PackageVersionFeature.java7
-rw-r--r--common/src/com/android/tv/common/feature/PropertyFeature.java17
-rw-r--r--common/src/com/android/tv/common/feature/Sdk.java14
-rw-r--r--common/src/com/android/tv/common/feature/SharedPreferencesFeature.java17
-rw-r--r--common/src/com/android/tv/common/feature/SystemAppFeature.java6
-rw-r--r--common/src/com/android/tv/common/feature/TestableFeature.java38
-rw-r--r--common/src/com/android/tv/common/memory/MemoryManageable.java (renamed from common/src/com/android/tv/common/MemoryManageable.java)11
-rw-r--r--common/src/com/android/tv/common/recording/RecordingCapability.java88
-rw-r--r--common/src/com/android/tv/common/recording/RecordingStorageStatusManager.java256
-rw-r--r--common/src/com/android/tv/common/ui/setup/OnActionClickListener.java8
-rw-r--r--common/src/com/android/tv/common/ui/setup/SetupActionHelper.java51
-rw-r--r--common/src/com/android/tv/common/ui/setup/SetupActivity.java49
-rw-r--r--common/src/com/android/tv/common/ui/setup/SetupFragment.java128
-rw-r--r--common/src/com/android/tv/common/ui/setup/SetupGuidedStepFragment.java133
-rw-r--r--common/src/com/android/tv/common/ui/setup/SetupMultiPaneFragment.java60
-rw-r--r--common/src/com/android/tv/common/ui/setup/animation/FadeAndShortSlide.java127
-rw-r--r--common/src/com/android/tv/common/ui/setup/animation/SetupAnimationHelper.java96
-rw-r--r--common/src/com/android/tv/common/ui/setup/animation/TranslationAnimationCreator.java42
-rw-r--r--common/src/com/android/tv/common/util/AutoCloseableUtils.java (renamed from common/src/com/android/tv/common/AutoCloseableUtils.java)8
-rw-r--r--common/src/com/android/tv/common/util/Clock.java (renamed from src/com/android/tv/util/Clock.java)55
-rw-r--r--common/src/com/android/tv/common/util/CollectionUtils.java (renamed from common/src/com/android/tv/common/CollectionUtils.java)36
-rw-r--r--common/src/com/android/tv/common/util/CommonUtils.java154
-rw-r--r--common/src/com/android/tv/common/util/ContentUriUtils.java45
-rw-r--r--common/src/com/android/tv/common/util/Debug.java (renamed from src/com/android/tv/util/Debug.java)28
-rw-r--r--common/src/com/android/tv/common/util/DurationTimer.java (renamed from src/com/android/tv/util/DurationTimer.java)27
-rw-r--r--common/src/com/android/tv/common/util/LocationUtils.java (renamed from src/com/android/tv/util/LocationUtils.java)54
-rw-r--r--common/src/com/android/tv/common/util/NetworkTrafficTags.java (renamed from src/com/android/tv/util/NetworkTrafficTags.java)3
-rw-r--r--common/src/com/android/tv/common/util/PermissionUtils.java68
-rw-r--r--common/src/com/android/tv/common/util/PostalCodeUtils.java (renamed from src/com/android/tv/tuner/util/PostalCodeUtils.java)27
-rw-r--r--common/src/com/android/tv/common/util/SharedPreferencesUtils.java (renamed from common/src/com/android/tv/common/SharedPreferencesUtils.java)36
-rw-r--r--common/src/com/android/tv/common/util/StringUtils.java (renamed from src/com/android/tv/util/StringUtils.java)12
-rw-r--r--common/src/com/android/tv/common/util/SystemProperties.java53
-rw-r--r--common/src/com/android/tv/common/util/SystemPropertiesProxy.java (renamed from src/com/android/tv/tuner/util/SystemPropertiesProxy.java)28
-rw-r--r--icu/icu4j/main/classes/core/src/com/ibm/icu/text/SCSU.java186
-rw-r--r--icu/icu4j/main/classes/core/src/com/ibm/icu/text/UnicodeDecompressor.java559
-rw-r--r--jni/DvbManager.cpp23
-rw-r--r--jni/DvbManager.h24
-rwxr-xr-xjni/gen_jni.sh15
-rw-r--r--jni/tunertvinput_jni.cpp171
-rw-r--r--jni/tunertvinput_jni.h35
-rw-r--r--libs/exoplayer-core-2-SNAPHOT-20180114.aarbin0 -> 1101772 bytes
-rw-r--r--libs/exoplayer-r1.5.16.aarbin0 -> 737991 bytes
-rw-r--r--libs/exoplayer.jarbin792705 -> 0 bytes
-rw-r--r--libs/exoplayer_v2.jarbin1077775 -> 0 bytes
-rw-r--r--libs/exoplayer_v2_ext_ffmpeg.jarbin7271 -> 0 bytes
-rw-r--r--partner_support/Android.mk22
-rw-r--r--partner_support/AndroidManifest.xml22
-rw-r--r--partner_support/BuildConfig.java.in8
-rw-r--r--partner_support/README.md3
-rw-r--r--partner_support/buildconfig.mk39
-rw-r--r--partner_support/g3doc/CloudEpgForPartners.md112
-rw-r--r--partner_support/res/values/strings.xml19
-rw-r--r--partner_support/sample_customization/Android.mk18
-rw-r--r--partner_support/sample_customization/AndroidManifest.xml35
-rwxr-xr-xpartner_support/sample_customization/res/mipmap-hdpi/ic_launcher.pngbin0 -> 3925 bytes
-rwxr-xr-xpartner_support/sample_customization/res/mipmap-mdpi/ic_launcher.pngbin0 -> 2363 bytes
-rwxr-xr-xpartner_support/sample_customization/res/mipmap-xhdpi/ic_launcher.pngbin0 -> 5185 bytes
-rwxr-xr-xpartner_support/sample_customization/res/mipmap-xxhdpi/ic_launcher.pngbin0 -> 8427 bytes
-rwxr-xr-xpartner_support/sample_customization/res/mipmap-xxxhdpi/ic_launcher.pngbin0 -> 11531 bytes
-rw-r--r--partner_support/sample_customization/res/values/bools.xml20
-rw-r--r--partner_support/samples/Android.mk33
-rw-r--r--partner_support/samples/AndroidManifest.xml55
-rwxr-xr-xpartner_support/samples/res/mipmap-hdpi/ic_launcher.pngbin0 -> 3931 bytes
-rwxr-xr-xpartner_support/samples/res/mipmap-mdpi/ic_launcher.pngbin0 -> 2382 bytes
-rwxr-xr-xpartner_support/samples/res/mipmap-xhdpi/ic_launcher.pngbin0 -> 5121 bytes
-rwxr-xr-xpartner_support/samples/res/mipmap-xxhdpi/ic_launcher.pngbin0 -> 8222 bytes
-rwxr-xr-xpartner_support/samples/res/mipmap-xxxhdpi/ic_launcher.pngbin0 -> 11381 bytes
-rw-r--r--partner_support/samples/res/values/strings.xml20
-rw-r--r--partner_support/samples/res/xml/sampletvinputservice.xml19
-rw-r--r--partner_support/samples/src/com/example/partnersupportsampletvinput/ChannelScanFragment.java265
-rw-r--r--partner_support/samples/src/com/example/partnersupportsampletvinput/LineupSelectionFragment.java278
-rw-r--r--partner_support/samples/src/com/example/partnersupportsampletvinput/LocationFragment.java91
-rw-r--r--partner_support/samples/src/com/example/partnersupportsampletvinput/ResultFragment.java95
-rw-r--r--partner_support/samples/src/com/example/partnersupportsampletvinput/SampleTvInputService.java53
-rw-r--r--partner_support/samples/src/com/example/partnersupportsampletvinput/SampleTvInputSetupActivity.java36
-rw-r--r--partner_support/samples/src/com/example/partnersupportsampletvinput/WelcomeFragment.java67
-rw-r--r--partner_support/src/com/google/android/tv/partner/support/AutoValue_EpgInput.java97
-rw-r--r--partner_support/src/com/google/android/tv/partner/support/AutoValue_Lineup.java114
-rw-r--r--partner_support/src/com/google/android/tv/partner/support/BaseCustomization.java93
-rw-r--r--partner_support/src/com/google/android/tv/partner/support/EpgContract.java201
-rw-r--r--partner_support/src/com/google/android/tv/partner/support/EpgInput.java64
-rw-r--r--partner_support/src/com/google/android/tv/partner/support/EpgInputs.java84
-rw-r--r--partner_support/src/com/google/android/tv/partner/support/Lineup.java81
-rw-r--r--partner_support/src/com/google/android/tv/partner/support/Lineups.java45
-rw-r--r--partner_support/src/com/google/android/tv/partner/support/PartnerCustomizations.java43
-rw-r--r--partner_support/src/com/google/android/tv/partner/support/TunerSetupUtils.java124
-rw-r--r--partner_support/tests/robotests/javatests/com/google/android/tv/partner/support/BaseCustomizationTest.java88
-rw-r--r--partner_support/tests/robotests/javatests/com/google/android/tv/partner/support/TunerSetupUtilsTest.java118
-rw-r--r--proguard.flags33
-rw-r--r--res/drawable-xhdpi/ic_live_channels.pngbin0 -> 33740 bytes
-rw-r--r--res/drawable-xhdpi/ic_live_channels_96x96.png (renamed from res/drawable-xhdpi/ic_launcher.png)bin3325 -> 3325 bytes
-rw-r--r--res/layout/activity_dvr_history.xml37
-rw-r--r--res/layout/details_overview.xml (renamed from res/layout/lb_details_overview.xml)0
-rw-r--r--res/layout/dvr_recording_card_view.xml2
-rw-r--r--res/layout/tunable_tv_view.xml21
-rw-r--r--res/values/arrays.xml2
-rw-r--r--res/values/strings.xml37
-rw-r--r--res/xml/remote_config_defaults.xml37
-rw-r--r--src/com/android/exoplayer2/ext/ffmpeg/FfmpegAudioDecoder.java127
-rw-r--r--src/com/android/exoplayer2/ext/ffmpeg/FfmpegLibrary.java84
-rw-r--r--src/com/android/tv/AudioManagerHelper.java91
-rw-r--r--src/com/android/tv/ChannelTuner.java106
-rw-r--r--src/com/android/tv/Features.java204
-rw-r--r--src/com/android/tv/InputSessionManager.java284
-rw-r--r--src/com/android/tv/LauncherActivity.java38
-rw-r--r--src/com/android/tv/MainActivity.java1281
-rw-r--r--src/com/android/tv/MainActivityWrapper.java56
-rw-r--r--src/com/android/tv/MediaSessionWrapper.java116
-rw-r--r--src/com/android/tv/SelectInputActivity.java50
-rw-r--r--src/com/android/tv/SetupPassthroughActivity.java140
-rw-r--r--src/com/android/tv/Starter.java42
-rw-r--r--src/com/android/tv/TimeShiftManager.java591
-rw-r--r--src/com/android/tv/TvActivity.java1
-rw-r--r--src/com/android/tv/TvApplication.java453
-rw-r--r--src/com/android/tv/TvFeatures.java104
-rw-r--r--src/com/android/tv/TvOptionsManager.java52
-rw-r--r--src/com/android/tv/TvSingletons.java (renamed from src/com/android/tv/ApplicationSingletons.java)47
-rw-r--r--src/com/android/tv/analytics/Analytics.java14
-rw-r--r--src/com/android/tv/analytics/ConfigurationInfo.java4
-rw-r--r--src/com/android/tv/analytics/HasTrackerLabel.java4
-rw-r--r--src/com/android/tv/analytics/SendChannelStatusRunnable.java76
-rw-r--r--src/com/android/tv/analytics/SendConfigInfoRunnable.java10
-rw-r--r--src/com/android/tv/analytics/StubAnalytics.java7
-rw-r--r--src/com/android/tv/analytics/StubTracker.java73
-rw-r--r--src/com/android/tv/analytics/Tracker.java62
-rw-r--r--src/com/android/tv/app/LiveTvApplication.java139
-rw-r--r--src/com/android/tv/config/RemoteConfigUtils.java42
-rw-r--r--src/com/android/tv/data/BaseProgram.java183
-rw-r--r--src/com/android/tv/data/ChannelDataManager.java320
-rw-r--r--src/com/android/tv/data/ChannelImpl.java (renamed from src/com/android/tv/data/Channel.java)397
-rw-r--r--src/com/android/tv/data/ChannelLogoFetcher.java59
-rw-r--r--src/com/android/tv/data/ChannelNumber.java54
-rw-r--r--src/com/android/tv/data/DisplayMode.java7
-rw-r--r--src/com/android/tv/data/GenreItems.java23
-rw-r--r--src/com/android/tv/data/InternalDataUtils.java34
-rw-r--r--src/com/android/tv/data/Lineup.java104
-rw-r--r--src/com/android/tv/data/OnCurrentProgramUpdatedListener.java4
-rw-r--r--src/com/android/tv/data/ParcelableList.java40
-rw-r--r--src/com/android/tv/data/PreviewDataManager.java204
-rw-r--r--src/com/android/tv/data/PreviewProgramContent.java132
-rw-r--r--src/com/android/tv/data/Program.java325
-rw-r--r--src/com/android/tv/data/ProgramDataManager.java358
-rw-r--r--src/com/android/tv/data/StreamInfo.java14
-rw-r--r--src/com/android/tv/data/TvInputNewComparator.java6
-rw-r--r--src/com/android/tv/data/WatchedHistoryManager.java110
-rw-r--r--src/com/android/tv/data/api/Channel.java130
-rw-r--r--src/com/android/tv/data/epg/AutoValue_EpgReader_EpgChannel.java86
-rw-r--r--src/com/android/tv/data/epg/EpgFetchHelper.java109
-rw-r--r--src/com/android/tv/data/epg/EpgFetchService.java70
-rw-r--r--src/com/android/tv/data/epg/EpgFetcher.java710
-rw-r--r--src/com/android/tv/data/epg/EpgFetcherImpl.java811
-rw-r--r--src/com/android/tv/data/epg/EpgInputWhiteList.java103
-rw-r--r--src/com/android/tv/data/epg/EpgReader.java46
-rw-r--r--src/com/android/tv/data/epg/StubEpgReader.java26
-rw-r--r--src/com/android/tv/dialog/DvrHistoryDialogFragment.java126
-rw-r--r--src/com/android/tv/dialog/FullscreenDialogFragment.java25
-rw-r--r--src/com/android/tv/dialog/HalfSizedDialogFragment.java48
-rw-r--r--src/com/android/tv/dialog/PinDialogFragment.java329
-rw-r--r--src/com/android/tv/dialog/RecentlyWatchedDialogFragment.java117
-rw-r--r--src/com/android/tv/dialog/SafeDismissDialogFragment.java16
-rw-r--r--src/com/android/tv/dialog/WebDialogFragment.java20
-rw-r--r--src/com/android/tv/dvr/BaseDvrDataManager.java106
-rw-r--r--src/com/android/tv/dvr/DvrDataManager.java152
-rw-r--r--src/com/android/tv/dvr/DvrDataManagerImpl.java439
-rw-r--r--src/com/android/tv/dvr/DvrManager.java391
-rw-r--r--src/com/android/tv/dvr/DvrScheduleManager.java596
-rw-r--r--src/com/android/tv/dvr/DvrStorageStatusManager.java334
-rw-r--r--src/com/android/tv/dvr/DvrWatchedPositionManager.java61
-rw-r--r--src/com/android/tv/dvr/WritableDvrDataManager.java37
-rw-r--r--src/com/android/tv/dvr/data/IdGenerator.java20
-rw-r--r--src/com/android/tv/dvr/data/RecordedProgram.java473
-rw-r--r--src/com/android/tv/dvr/data/ScheduledRecording.java659
-rw-r--r--src/com/android/tv/dvr/data/SeasonEpisodeNumber.java27
-rw-r--r--src/com/android/tv/dvr/data/SeriesInfo.java28
-rw-r--r--src/com/android/tv/dvr/data/SeriesRecording.java399
-rw-r--r--src/com/android/tv/dvr/provider/AsyncDvrDbTask.java60
-rw-r--r--src/com/android/tv/dvr/provider/DvrContract.java120
-rw-r--r--src/com/android/tv/dvr/provider/DvrDatabaseHelper.java285
-rw-r--r--src/com/android/tv/dvr/provider/DvrDbSync.java202
-rw-r--r--src/com/android/tv/dvr/provider/EpisodicProgramLoadTask.java157
-rw-r--r--src/com/android/tv/dvr/recorder/ConflictChecker.java113
-rw-r--r--src/com/android/tv/dvr/recorder/DvrRecordingService.java58
-rw-r--r--src/com/android/tv/dvr/recorder/DvrStartRecordingReceiver.java12
-rw-r--r--src/com/android/tv/dvr/recorder/InputTaskScheduler.java172
-rw-r--r--src/com/android/tv/dvr/recorder/RecordingScheduler.java118
-rw-r--r--src/com/android/tv/dvr/recorder/RecordingTask.java362
-rw-r--r--src/com/android/tv/dvr/recorder/ScheduledProgramReaper.java16
-rw-r--r--src/com/android/tv/dvr/recorder/SeriesRecordingScheduler.java283
-rw-r--r--src/com/android/tv/dvr/ui/BigArguments.java20
-rw-r--r--src/com/android/tv/dvr/ui/ChangeImageTransformWithScaledParent.java24
-rw-r--r--src/com/android/tv/dvr/ui/DvrAlreadyRecordedFragment.java47
-rw-r--r--src/com/android/tv/dvr/ui/DvrAlreadyScheduledFragment.java57
-rw-r--r--src/com/android/tv/dvr/ui/DvrChannelRecordDurationOptionFragment.java57
-rw-r--r--src/com/android/tv/dvr/ui/DvrConflictFragment.java159
-rw-r--r--src/com/android/tv/dvr/ui/DvrFutureProgramInfoFragment.java87
-rw-r--r--src/com/android/tv/dvr/ui/DvrGuidedActionsStylist.java15
-rw-r--r--src/com/android/tv/dvr/ui/DvrGuidedStepFragment.java92
-rw-r--r--src/com/android/tv/dvr/ui/DvrHalfSizedDialogFragment.java62
-rw-r--r--src/com/android/tv/dvr/ui/DvrInsufficientSpaceErrorFragment.java83
-rw-r--r--src/com/android/tv/dvr/ui/DvrMissingStorageErrorFragment.java24
-rw-r--r--src/com/android/tv/dvr/ui/DvrPrioritySettingsFragment.java86
-rw-r--r--src/com/android/tv/dvr/ui/DvrScheduleFragment.java114
-rw-r--r--src/com/android/tv/dvr/ui/DvrSeriesDeletionActivity.java13
-rw-r--r--src/com/android/tv/dvr/ui/DvrSeriesDeletionFragment.java126
-rw-r--r--src/com/android/tv/dvr/ui/DvrSeriesScheduledDialogActivity.java13
-rw-r--r--src/com/android/tv/dvr/ui/DvrSeriesScheduledFragment.java119
-rw-r--r--src/com/android/tv/dvr/ui/DvrSeriesSettingsActivity.java32
-rw-r--r--src/com/android/tv/dvr/ui/DvrSeriesSettingsFragment.java151
-rw-r--r--src/com/android/tv/dvr/ui/DvrStopRecordingFragment.java57
-rw-r--r--src/com/android/tv/dvr/ui/DvrStopSeriesRecordingDialogFragment.java9
-rw-r--r--src/com/android/tv/dvr/ui/DvrStopSeriesRecordingFragment.java40
-rw-r--r--src/com/android/tv/dvr/ui/DvrUiHelper.java547
-rw-r--r--src/com/android/tv/dvr/ui/FadeBackground.java17
-rw-r--r--src/com/android/tv/dvr/ui/SortedArrayAdapter.java34
-rw-r--r--src/com/android/tv/dvr/ui/TrackedGuidedStepFragment.java5
-rw-r--r--src/com/android/tv/dvr/ui/browse/ActionPresenterSelector.java34
-rw-r--r--src/com/android/tv/dvr/ui/browse/CurrentRecordingDetailsFragment.java28
-rw-r--r--src/com/android/tv/dvr/ui/browse/DetailsContent.java172
-rw-r--r--src/com/android/tv/dvr/ui/browse/DetailsContentPresenter.java188
-rw-r--r--src/com/android/tv/dvr/ui/browse/DetailsViewBackgroundHelper.java17
-rw-r--r--src/com/android/tv/dvr/ui/browse/DvrBrowseActivity.java11
-rw-r--r--src/com/android/tv/dvr/ui/browse/DvrBrowseFragment.java339
-rw-r--r--src/com/android/tv/dvr/ui/browse/DvrDetailsActivity.java48
-rw-r--r--src/com/android/tv/dvr/ui/browse/DvrDetailsFragment.java151
-rw-r--r--src/com/android/tv/dvr/ui/browse/DvrHistoryCardHolder.java (renamed from common/src/com/android/tv/common/annotation/UsedByReflection.java)18
-rw-r--r--src/com/android/tv/dvr/ui/browse/DvrHistoryCardPresenter.java64
-rw-r--r--src/com/android/tv/dvr/ui/browse/DvrItemPresenter.java41
-rw-r--r--src/com/android/tv/dvr/ui/browse/DvrListRowPresenter.java1
-rw-r--r--src/com/android/tv/dvr/ui/browse/FullScheduleCardHolder.java10
-rw-r--r--src/com/android/tv/dvr/ui/browse/FullSchedulesCardPresenter.java35
-rw-r--r--src/com/android/tv/dvr/ui/browse/RecordedProgramDetailsFragment.java71
-rw-r--r--src/com/android/tv/dvr/ui/browse/RecordedProgramPresenter.java55
-rw-r--r--src/com/android/tv/dvr/ui/browse/RecordingCardView.java92
-rw-r--r--src/com/android/tv/dvr/ui/browse/RecordingDetailsFragment.java27
-rw-r--r--src/com/android/tv/dvr/ui/browse/ScheduledRecordingDetailsFragment.java45
-rw-r--r--src/com/android/tv/dvr/ui/browse/ScheduledRecordingPresenter.java102
-rw-r--r--src/com/android/tv/dvr/ui/browse/SeriesRecordingDetailsFragment.java104
-rw-r--r--src/com/android/tv/dvr/ui/browse/SeriesRecordingPresenter.java44
-rw-r--r--src/com/android/tv/dvr/ui/list/BaseDvrSchedulesFragment.java87
-rw-r--r--src/com/android/tv/dvr/ui/list/DvrHistoryActivity.java39
-rw-r--r--src/com/android/tv/dvr/ui/list/DvrHistoryFragment.java166
-rw-r--r--src/com/android/tv/dvr/ui/list/DvrHistoryRowAdapter.java353
-rw-r--r--src/com/android/tv/dvr/ui/list/DvrSchedulesActivity.java47
-rw-r--r--src/com/android/tv/dvr/ui/list/DvrSchedulesFocusView.java11
-rw-r--r--src/com/android/tv/dvr/ui/list/DvrSchedulesFragment.java15
-rw-r--r--src/com/android/tv/dvr/ui/list/DvrSeriesSchedulesFragment.java117
-rw-r--r--src/com/android/tv/dvr/ui/list/EpisodicProgramRow.java19
-rw-r--r--src/com/android/tv/dvr/ui/list/ScheduleRow.java111
-rw-r--r--src/com/android/tv/dvr/ui/list/ScheduleRowAdapter.java174
-rw-r--r--src/com/android/tv/dvr/ui/list/ScheduleRowPresenter.java611
-rw-r--r--src/com/android/tv/dvr/ui/list/SchedulesHeaderRow.java63
-rw-r--r--src/com/android/tv/dvr/ui/list/SchedulesHeaderRowPresenter.java198
-rw-r--r--src/com/android/tv/dvr/ui/list/SeriesScheduleRowAdapter.java66
-rw-r--r--src/com/android/tv/dvr/ui/list/SeriesScheduleRowPresenter.java71
-rw-r--r--src/com/android/tv/dvr/ui/playback/DvrPlaybackActivity.java19
-rw-r--r--src/com/android/tv/dvr/ui/playback/DvrPlaybackCardPresenter.java14
-rw-r--r--src/com/android/tv/dvr/ui/playback/DvrPlaybackControlHelper.java132
-rw-r--r--src/com/android/tv/dvr/ui/playback/DvrPlaybackMediaSessionHelper.java171
-rw-r--r--src/com/android/tv/dvr/ui/playback/DvrPlaybackOverlayFragment.java188
-rw-r--r--src/com/android/tv/dvr/ui/playback/DvrPlaybackSideFragment.java59
-rw-r--r--src/com/android/tv/dvr/ui/playback/DvrPlayer.java361
-rw-r--r--src/com/android/tv/guide/GenreListAdapter.java59
-rw-r--r--src/com/android/tv/guide/GuideUtils.java42
-rw-r--r--src/com/android/tv/guide/ProgramGrid.java40
-rw-r--r--src/com/android/tv/guide/ProgramGuide.java684
-rw-r--r--src/com/android/tv/guide/ProgramItemView.java522
-rw-r--r--src/com/android/tv/guide/ProgramListAdapter.java14
-rw-r--r--src/com/android/tv/guide/ProgramManager.java379
-rw-r--r--src/com/android/tv/guide/ProgramRow.java129
-rw-r--r--src/com/android/tv/guide/ProgramTableAdapter.java303
-rw-r--r--src/com/android/tv/guide/TimeListAdapter.java13
-rw-r--r--src/com/android/tv/guide/TimelineGridView.java17
-rw-r--r--src/com/android/tv/guide/TimelineRow.java13
-rw-r--r--src/com/android/tv/license/LicenseDialogFragment.java1
-rw-r--r--src/com/android/tv/license/LicenseSideFragment.java2
-rw-r--r--src/com/android/tv/license/LicenseUtils.java17
-rw-r--r--src/com/android/tv/license/Licenses.java3
-rw-r--r--src/com/android/tv/livetv/receiver/GlobalKeyReceiver.java27
-rw-r--r--src/com/android/tv/menu/ActionCardView.java7
-rw-r--r--src/com/android/tv/menu/AppLinkCardView.java124
-rw-r--r--src/com/android/tv/menu/BaseCardView.java132
-rw-r--r--src/com/android/tv/menu/ChannelCardView.java29
-rw-r--r--src/com/android/tv/menu/ChannelsPosterPrefetcher.java55
-rw-r--r--src/com/android/tv/menu/ChannelsRow.java53
-rw-r--r--src/com/android/tv/menu/ChannelsRowAdapter.java138
-rw-r--r--src/com/android/tv/menu/ChannelsRowItem.java34
-rw-r--r--src/com/android/tv/menu/CustomizableOptionsRowAdapter.java13
-rw-r--r--src/com/android/tv/menu/IMenuView.java23
-rw-r--r--src/com/android/tv/menu/ItemListRow.java22
-rw-r--r--src/com/android/tv/menu/ItemListRowView.java47
-rw-r--r--src/com/android/tv/menu/Menu.java171
-rw-r--r--src/com/android/tv/menu/MenuAction.java51
-rw-r--r--src/com/android/tv/menu/MenuLayoutManager.java540
-rw-r--r--src/com/android/tv/menu/MenuRow.java72
-rw-r--r--src/com/android/tv/menu/MenuRowFactory.java68
-rw-r--r--src/com/android/tv/menu/MenuRowView.java63
-rw-r--r--src/com/android/tv/menu/MenuUpdater.java78
-rw-r--r--src/com/android/tv/menu/MenuView.java64
-rw-r--r--src/com/android/tv/menu/OptionsRowAdapter.java51
-rw-r--r--src/com/android/tv/menu/PartnerOptionsRowAdapter.java7
-rw-r--r--src/com/android/tv/menu/PlayControlsButton.java64
-rw-r--r--src/com/android/tv/menu/PlayControlsRow.java13
-rw-r--r--src/com/android/tv/menu/PlayControlsRowView.java459
-rw-r--r--src/com/android/tv/menu/PlaybackProgressBar.java22
-rw-r--r--src/com/android/tv/menu/SimpleCardView.java4
-rw-r--r--src/com/android/tv/menu/TvOptionsRowAdapter.java42
-rw-r--r--src/com/android/tv/onboarding/NewSourcesFragment.java36
-rw-r--r--src/com/android/tv/onboarding/OnboardingActivity.java160
-rw-r--r--src/com/android/tv/onboarding/SetupSourcesFragment.java235
-rw-r--r--src/com/android/tv/onboarding/WelcomeFragment.java1227
-rw-r--r--src/com/android/tv/parental/ContentRatingLevelPolicy.java22
-rw-r--r--src/com/android/tv/parental/ContentRatingSystem.java87
-rw-r--r--src/com/android/tv/parental/ContentRatingsManager.java28
-rw-r--r--src/com/android/tv/parental/ContentRatingsParser.java169
-rw-r--r--src/com/android/tv/parental/ParentalControlSettings.java92
-rw-r--r--src/com/android/tv/perf/EventNames.java9
-rw-r--r--src/com/android/tv/perf/PerformanceMonitor.java5
-rw-r--r--src/com/android/tv/receiver/AbstractGlobalKeyReceiver.java (renamed from src/com/android/tv/receiver/GlobalKeyReceiver.java)25
-rw-r--r--src/com/android/tv/receiver/AudioCapabilitiesReceiver.java35
-rw-r--r--src/com/android/tv/receiver/BootCompletedReceiver.java27
-rw-r--r--src/com/android/tv/receiver/PackageIntentsReceiver.java16
-rw-r--r--src/com/android/tv/recommendation/ChannelPreviewUpdater.java151
-rw-r--r--src/com/android/tv/recommendation/ChannelRecord.java19
-rw-r--r--src/com/android/tv/recommendation/FavoriteChannelEvaluator.java5
-rw-r--r--src/com/android/tv/recommendation/NotificationService.java269
-rw-r--r--src/com/android/tv/recommendation/RecentChannelEvaluator.java11
-rw-r--r--src/com/android/tv/recommendation/RecommendationDataManager.java292
-rw-r--r--src/com/android/tv/recommendation/Recommender.java81
-rw-r--r--src/com/android/tv/recommendation/RecordedProgramPreviewUpdater.java113
-rw-r--r--src/com/android/tv/recommendation/RoutineWatchEvaluator.java88
-rw-r--r--src/com/android/tv/search/AutoValue_LocalSearchProvider_SearchResult.java363
-rw-r--r--src/com/android/tv/search/DataManagerSearch.java185
-rw-r--r--src/com/android/tv/search/LocalSearchProvider.java224
-rw-r--r--src/com/android/tv/search/ProgramGuideSearchFragment.java177
-rw-r--r--src/com/android/tv/search/SearchInterface.java11
-rw-r--r--src/com/android/tv/search/TvProviderSearch.java386
-rw-r--r--src/com/android/tv/setup/SystemSetupActivity.java101
-rw-r--r--src/com/android/tv/tuner/TunerInputController.java480
-rw-r--r--src/com/android/tv/tuner/TunerPreferences.java428
-rw-r--r--src/com/android/tv/tuner/exoplayer/ffmpeg/FfmpegDecoderClient.java249
-rw-r--r--src/com/android/tv/tuner/exoplayer/ffmpeg/FfmpegDecoderService.java205
-rw-r--r--src/com/android/tv/tuner/tvinput/EventDetector.java334
-rw-r--r--src/com/android/tv/tuner/tvinput/FileSourceEventDetector.java249
-rw-r--r--src/com/android/tv/tuner/util/Ints.java28
-rw-r--r--src/com/android/tv/tuner/util/TisConfiguration.java22
-rw-r--r--src/com/android/tv/ui/AppLayerTvView.java21
-rw-r--r--src/com/android/tv/ui/BlockScreenView.java97
-rw-r--r--src/com/android/tv/ui/ChannelBannerView.java364
-rw-r--r--src/com/android/tv/ui/DialogUtils.java34
-rw-r--r--src/com/android/tv/ui/FullscreenDialogView.java99
-rw-r--r--src/com/android/tv/ui/GuidedActionsStylistWithDivider.java14
-rw-r--r--src/com/android/tv/ui/InputBannerView.java36
-rw-r--r--src/com/android/tv/ui/IntroView.java18
-rw-r--r--src/com/android/tv/ui/KeypadChannelSwitchView.java192
-rw-r--r--src/com/android/tv/ui/OnRepeatedKeyInterceptListener.java26
-rw-r--r--src/com/android/tv/ui/SelectInputView.java207
-rw-r--r--src/com/android/tv/ui/TunableTvView.java922
-rw-r--r--src/com/android/tv/ui/TunableTvViewPlayingApi.java56
-rw-r--r--src/com/android/tv/ui/TvOverlayManager.java720
-rw-r--r--src/com/android/tv/ui/TvTransitionManager.java152
-rw-r--r--src/com/android/tv/ui/TvViewUiManager.java318
-rw-r--r--src/com/android/tv/ui/ViewUtils.java53
-rw-r--r--src/com/android/tv/ui/hideable/AutoHideScheduler.java98
-rw-r--r--src/com/android/tv/ui/sidepanel/ActionItem.java3
-rw-r--r--src/com/android/tv/ui/sidepanel/ChannelCheckItem.java51
-rw-r--r--src/com/android/tv/ui/sidepanel/CheckBoxItem.java16
-rw-r--r--src/com/android/tv/ui/sidepanel/ClosedCaptionFragment.java54
-rw-r--r--src/com/android/tv/ui/sidepanel/CompoundButtonItem.java11
-rw-r--r--src/com/android/tv/ui/sidepanel/CustomizeChannelListFragment.java177
-rw-r--r--src/com/android/tv/ui/sidepanel/DeveloperOptionFragment.java83
-rw-r--r--src/com/android/tv/ui/sidepanel/DisplayModeFragment.java2
-rw-r--r--src/com/android/tv/ui/sidepanel/DividerItem.java11
-rw-r--r--src/com/android/tv/ui/sidepanel/Item.java23
-rw-r--r--src/com/android/tv/ui/sidepanel/MultiAudioFragment.java10
-rw-r--r--src/com/android/tv/ui/sidepanel/RadioButtonItem.java2
-rw-r--r--src/com/android/tv/ui/sidepanel/SettingsFragment.java182
-rw-r--r--src/com/android/tv/ui/sidepanel/SideFragment.java49
-rw-r--r--src/com/android/tv/ui/sidepanel/SideFragmentManager.java122
-rw-r--r--src/com/android/tv/ui/sidepanel/SimpleActionItem.java7
-rw-r--r--src/com/android/tv/ui/sidepanel/SubMenuItem.java1
-rw-r--r--src/com/android/tv/ui/sidepanel/SwitchItem.java6
-rw-r--r--src/com/android/tv/ui/sidepanel/parentalcontrols/ChannelsBlockedFragment.java108
-rw-r--r--src/com/android/tv/ui/sidepanel/parentalcontrols/ParentalControlsFragment.java181
-rw-r--r--src/com/android/tv/ui/sidepanel/parentalcontrols/ProgramRestrictionsFragment.java64
-rw-r--r--src/com/android/tv/ui/sidepanel/parentalcontrols/RatingSystemsFragment.java39
-rw-r--r--src/com/android/tv/ui/sidepanel/parentalcontrols/RatingsFragment.java92
-rw-r--r--src/com/android/tv/ui/sidepanel/parentalcontrols/SubRatingsFragment.java25
-rw-r--r--src/com/android/tv/util/AsyncDbTask.java240
-rw-r--r--src/com/android/tv/util/CaptionSettings.java13
-rw-r--r--src/com/android/tv/util/CompositeComparator.java4
-rw-r--r--src/com/android/tv/util/Filter.java8
-rw-r--r--src/com/android/tv/util/MainThreadExecutor.java19
-rw-r--r--src/com/android/tv/util/MultiLongSparseArray.java21
-rw-r--r--src/com/android/tv/util/NetworkUtils.java11
-rw-r--r--src/com/android/tv/util/OnboardingUtils.java43
-rw-r--r--src/com/android/tv/util/Partner.java3
-rw-r--r--src/com/android/tv/util/PermissionUtils.java55
-rw-r--r--src/com/android/tv/util/RecurringRunner.java39
-rw-r--r--src/com/android/tv/util/SetupUtils.java245
-rw-r--r--src/com/android/tv/util/SqlParams.java74
-rw-r--r--src/com/android/tv/util/SystemProperties.java68
-rw-r--r--src/com/android/tv/util/TimeShiftUtils.java18
-rw-r--r--src/com/android/tv/util/ToastUtils.java9
-rw-r--r--src/com/android/tv/util/TvInputManagerHelper.java440
-rw-r--r--src/com/android/tv/util/TvSettings.java126
-rw-r--r--src/com/android/tv/util/TvTrackInfoUtils.java50
-rw-r--r--src/com/android/tv/util/TvUriMatcher.java31
-rw-r--r--src/com/android/tv/util/Utils.java484
-rw-r--r--src/com/android/tv/util/ViewCache.java52
-rw-r--r--src/com/android/tv/util/account/AccountHelper.java38
-rw-r--r--src/com/android/tv/util/account/AccountHelperImpl.java (renamed from src/com/android/tv/util/AccountHelper.java)52
-rw-r--r--src/com/android/tv/util/images/BitmapUtils.java (renamed from src/com/android/tv/util/BitmapUtils.java)103
-rw-r--r--src/com/android/tv/util/images/ImageCache.java (renamed from src/com/android/tv/util/ImageCache.java)74
-rw-r--r--src/com/android/tv/util/images/ImageLoader.java (renamed from src/com/android/tv/util/ImageLoader.java)205
-rw-r--r--tests/common/Android.mk8
-rw-r--r--tests/common/AndroidManifest.xml2
-rw-r--r--tests/common/src/com/android/tv/input/TunerHelper.java23
-rw-r--r--tests/common/src/com/android/tv/testing/ChannelNumberSubject.java66
-rw-r--r--tests/common/src/com/android/tv/testing/ComparableTester.java48
-rw-r--r--tests/common/src/com/android/tv/testing/ComparatorTester.java55
-rw-r--r--tests/common/src/com/android/tv/testing/Constants.java42
-rw-r--r--tests/common/src/com/android/tv/testing/DbTestingUtils.java40
-rw-r--r--tests/common/src/com/android/tv/testing/EpgTestData.java198
-rw-r--r--tests/common/src/com/android/tv/testing/FakeClock.java25
-rw-r--r--tests/common/src/com/android/tv/testing/FakeEpgFetcher.java57
-rw-r--r--tests/common/src/com/android/tv/testing/FakeEpgReader.java164
-rw-r--r--tests/common/src/com/android/tv/testing/FakeRemoteConfig.java55
-rw-r--r--tests/common/src/com/android/tv/testing/FakeTvInputManager.java117
-rw-r--r--tests/common/src/com/android/tv/testing/FakeTvInputManagerHelper.java32
-rw-r--r--tests/common/src/com/android/tv/testing/FakeTvProvider.java2605
-rw-r--r--tests/common/src/com/android/tv/testing/SingletonProvider.java37
-rw-r--r--tests/common/src/com/android/tv/testing/TestSingletonApp.java247
-rw-r--r--tests/common/src/com/android/tv/testing/activities/BaseMainActivityTestCase.java (renamed from tests/unit/src/com/android/tv/BaseMainActivityTestCase.java)52
-rw-r--r--tests/common/src/com/android/tv/testing/constants/ConfigConstants.java (renamed from src/com/android/tv/tuner/exoplayer/ffmpeg/IFfmpegDecoder.aidl)25
-rw-r--r--tests/common/src/com/android/tv/testing/constants/Constants.java47
-rw-r--r--tests/common/src/com/android/tv/testing/constants/TvContentRatingConstants.java (renamed from tests/common/src/com/android/tv/testing/TvContentRatingConstants.java)18
-rw-r--r--tests/common/src/com/android/tv/testing/data/ChannelInfo.java (renamed from tests/common/src/com/android/tv/testing/ChannelInfo.java)159
-rw-r--r--tests/common/src/com/android/tv/testing/data/ChannelUtils.java (renamed from tests/common/src/com/android/tv/testing/ChannelUtils.java)32
-rw-r--r--tests/common/src/com/android/tv/testing/data/ProgramInfo.java (renamed from tests/common/src/com/android/tv/testing/ProgramInfo.java)167
-rw-r--r--tests/common/src/com/android/tv/testing/data/ProgramUtils.java (renamed from tests/common/src/com/android/tv/testing/ProgramUtils.java)68
-rw-r--r--tests/common/src/com/android/tv/testing/dvr/DvrDataManagerInMemoryImpl.java (renamed from tests/unit/src/com/android/tv/dvr/DvrDataManagerInMemoryImpl.java)80
-rw-r--r--tests/common/src/com/android/tv/testing/dvr/RecordingTestUtils.java36
-rw-r--r--tests/common/src/com/android/tv/testing/robo/ContentProviders.java40
-rw-r--r--tests/common/src/com/android/tv/testing/robo/RobotTestAppHelper.java36
-rw-r--r--tests/common/src/com/android/tv/testing/shadows/ShadowMediaSession.java89
-rw-r--r--tests/common/src/com/android/tv/testing/testdata/TestData.java86
-rw-r--r--tests/common/src/com/android/tv/testing/testinput/ChannelState.java35
-rw-r--r--tests/common/src/com/android/tv/testing/testinput/ChannelStateData.java31
-rw-r--r--tests/common/src/com/android/tv/testing/testinput/TestInputControlConnection.java13
-rw-r--r--tests/common/src/com/android/tv/testing/testinput/TestInputControlUtils.java14
-rw-r--r--tests/common/src/com/android/tv/testing/testinput/TvTestInputConstants.java13
-rw-r--r--tests/common/src/com/android/tv/testing/uihelper/BaseUiDeviceHelper.java4
-rw-r--r--tests/common/src/com/android/tv/testing/uihelper/ByResource.java7
-rw-r--r--tests/common/src/com/android/tv/testing/uihelper/Constants.java6
-rw-r--r--tests/common/src/com/android/tv/testing/uihelper/DialogHelper.java9
-rw-r--r--tests/common/src/com/android/tv/testing/uihelper/LiveChannelsUiDeviceHelper.java44
-rw-r--r--tests/common/src/com/android/tv/testing/uihelper/MenuHelper.java89
-rw-r--r--tests/common/src/com/android/tv/testing/uihelper/SidePanelHelper.java11
-rw-r--r--tests/common/src/com/android/tv/testing/uihelper/UiDeviceAsserts.java73
-rw-r--r--tests/common/src/com/android/tv/testing/uihelper/UiDeviceUtils.java73
-rw-r--r--tests/common/src/com/android/tv/testing/uihelper/UiObject2Asserts.java29
-rw-r--r--tests/common/src/com/android/tv/testing/uihelper/UiObject2Utils.java7
-rw-r--r--tests/common/src/com/android/tv/testing/utils/TestUtils.java193
-rw-r--r--tests/common/src/com/android/tv/testing/utils/Utils.java (renamed from tests/common/src/com/android/tv/testing/Utils.java)39
-rw-r--r--tests/func/Android.mk6
-rw-r--r--tests/func/AndroidManifest.xml2
-rw-r--r--tests/func/src/com/android/tv/tests/ui/ChannelBannerViewTest.java49
-rw-r--r--tests/func/src/com/android/tv/tests/ui/ChannelSourcesTest.java72
-rw-r--r--tests/func/src/com/android/tv/tests/ui/LiveChannelsAppTest.java139
-rw-r--r--tests/func/src/com/android/tv/tests/ui/LiveChannelsTestCase.java103
-rw-r--r--tests/func/src/com/android/tv/tests/ui/LiveChannelsTestController.java231
-rw-r--r--tests/func/src/com/android/tv/tests/ui/ParentalControlsTest.java172
-rw-r--r--tests/func/src/com/android/tv/tests/ui/PlayControlsRowViewTest.java127
-rw-r--r--tests/func/src/com/android/tv/tests/ui/ProgramGuideTest.java34
-rw-r--r--tests/func/src/com/android/tv/tests/ui/TimeoutTest.java52
-rw-r--r--tests/func/src/com/android/tv/tests/ui/dvr/DvrLibraryTest.java377
-rw-r--r--tests/func/src/com/android/tv/tests/ui/sidepanel/CustomizeChannelListFragmentTest.java112
-rw-r--r--tests/input/Android.mk2
-rw-r--r--tests/input/AndroidManifest.xml2
-rw-r--r--tests/input/func.sh24
-rw-r--r--tests/input/jank.sh24
-rw-r--r--tests/input/src/com/android/tv/testinput/TestInputControl.java26
-rw-r--r--tests/input/src/com/android/tv/testinput/TestInputControlService.java4
-rw-r--r--tests/input/src/com/android/tv/testinput/TestTvInputService.java156
-rw-r--r--tests/input/src/com/android/tv/testinput/TestTvInputSetupActivity.java69
-rw-r--r--tests/input/src/com/android/tv/testinput/instrument/TestSetupInstrumentation.java41
-rwxr-xr-xtests/input/tools/get_test_logos.sh57
-rw-r--r--tests/input/unit.sh24
-rw-r--r--tests/jank/Android.mk6
-rw-r--r--tests/jank/AndroidManifest.xml2
-rw-r--r--tests/jank/src/com/android/tv/tests/jank/ChannelZappingJankTest.java21
-rw-r--r--tests/jank/src/com/android/tv/tests/jank/LiveChannelsTestCase.java10
-rw-r--r--tests/jank/src/com/android/tv/tests/jank/MenuJankTest.java16
-rw-r--r--tests/jank/src/com/android/tv/tests/jank/ProgramGuideJankTest.java39
-rw-r--r--tests/jank/src/com/android/tv/tests/jank/Utils.java9
-rw-r--r--tests/tunerscripts/measure-tuning-time.awk32
-rwxr-xr-xtests/tunerscripts/usbtuner-test.sh159
-rw-r--r--tests/unit/Android.mk7
-rw-r--r--tests/unit/AndroidManifest.xml2
-rw-r--r--tests/unit/src/com/android/tv/CurrentPositionMediatorTest.java40
-rw-r--r--tests/unit/src/com/android/tv/FeaturesTest.java12
-rw-r--r--tests/unit/src/com/android/tv/MainActivityTest.java50
-rw-r--r--tests/unit/src/com/android/tv/TimeShiftManagerTest.java46
-rw-r--r--tests/unit/src/com/android/tv/data/ChannelDataManagerTest.java325
-rw-r--r--tests/unit/src/com/android/tv/data/ChannelImplTest.java381
-rw-r--r--tests/unit/src/com/android/tv/data/ChannelNumberTest.java97
-rw-r--r--tests/unit/src/com/android/tv/data/ChannelTest.java312
-rw-r--r--tests/unit/src/com/android/tv/data/GenreItemTest.java94
-rw-r--r--tests/unit/src/com/android/tv/data/ProgramDataManagerTest.java542
-rw-r--r--tests/unit/src/com/android/tv/data/ProgramTest.java182
-rw-r--r--tests/unit/src/com/android/tv/data/TvInputNewComparatorTest.java83
-rw-r--r--tests/unit/src/com/android/tv/data/WatchedHistoryManagerTest.java59
-rw-r--r--tests/unit/src/com/android/tv/dvr/BaseDvrDataManagerTest.java90
-rw-r--r--tests/unit/src/com/android/tv/dvr/DvrDataManagerImplTest.java76
-rw-r--r--tests/unit/src/com/android/tv/dvr/DvrScheduleManagerTest.java693
-rw-r--r--tests/unit/src/com/android/tv/dvr/ScheduledRecordingTest.java117
-rw-r--r--tests/unit/src/com/android/tv/dvr/data/SeriesRecordingTest.java133
-rw-r--r--tests/unit/src/com/android/tv/dvr/provider/DvrDbSyncTest.java143
-rw-r--r--tests/unit/src/com/android/tv/dvr/provider/EpisodicProgramLoadTaskTest.java83
-rw-r--r--tests/unit/src/com/android/tv/dvr/recorder/DvrRecordingServiceTest.java93
-rw-r--r--tests/unit/src/com/android/tv/dvr/recorder/InputTaskSchedulerTest.java231
-rw-r--r--tests/unit/src/com/android/tv/dvr/recorder/RecordingTaskTest.java149
-rw-r--r--tests/unit/src/com/android/tv/dvr/recorder/ScheduledProgramReaperTest.java137
-rw-r--r--tests/unit/src/com/android/tv/dvr/recorder/SchedulerTest.java125
-rw-r--r--tests/unit/src/com/android/tv/dvr/recorder/SeriesRecordingSchedulerTest.java129
-rw-r--r--tests/unit/src/com/android/tv/dvr/ui/SortedArrayAdapterTest.java250
-rw-r--r--tests/unit/src/com/android/tv/experiments/ExperimentsTest.java53
-rw-r--r--tests/unit/src/com/android/tv/menu/MenuTest.java61
-rw-r--r--tests/unit/src/com/android/tv/menu/TvOptionsRowAdapterTest.java73
-rw-r--r--tests/unit/src/com/android/tv/recommendation/ChannelRecordTest.java42
-rw-r--r--tests/unit/src/com/android/tv/recommendation/EvaluatorTestCase.java74
-rw-r--r--tests/unit/src/com/android/tv/recommendation/FavoriteChannelEvaluatorTest.java73
-rw-r--r--tests/unit/src/com/android/tv/recommendation/RecentChannelEvaluatorTest.java47
-rw-r--r--tests/unit/src/com/android/tv/recommendation/RecommendationUtils.java95
-rw-r--r--tests/unit/src/com/android/tv/recommendation/RecommenderTest.java287
-rw-r--r--tests/unit/src/com/android/tv/recommendation/RoutineWatchEvaluatorTest.java219
-rw-r--r--tests/unit/src/com/android/tv/search/LocalSearchProviderTest.java132
-rw-r--r--tests/unit/src/com/android/tv/tests/TvActivityTest.java43
-rw-r--r--tests/unit/src/com/android/tv/util/MockTvSingletons.java (renamed from tests/unit/src/com/android/tv/util/MockApplicationSingletons.java)81
-rw-r--r--tests/unit/src/com/android/tv/util/MultiLongSparseArrayTest.java108
-rw-r--r--tests/unit/src/com/android/tv/util/ScaledBitmapInfoTest.java59
-rw-r--r--tests/unit/src/com/android/tv/util/TestUtils.java115
-rw-r--r--tests/unit/src/com/android/tv/util/TvInputManagerHelperTest.java258
-rw-r--r--tests/unit/src/com/android/tv/util/TvTrackInfoUtilsTest.java52
-rw-r--r--tests/unit/src/com/android/tv/util/UtilsTest_GetDurationString.java273
-rw-r--r--tests/unit/src/com/android/tv/util/UtilsTest_GetMultiAudioString.java94
-rw-r--r--tests/unit/src/com/android/tv/util/UtilsTest_IsInGivenDay.java68
-rw-r--r--tests/unit/src/com/android/tv/util/images/ImageCacheTest.java (renamed from tests/unit/src/com/android/tv/util/ImageCacheTest.java)43
-rw-r--r--tests/unit/src/com/android/tv/util/images/ScaledBitmapInfoTest.java79
-rw-r--r--tuner/Android.mk42
-rw-r--r--tuner/AndroidManifest.xml22
-rw-r--r--tuner/BuildConfig.java.in8
-rwxr-xr-xtuner/SampleDvbTuner/AndroidManifest.xml92
-rw-r--r--tuner/SampleDvbTuner/ResourceManifest.xml24
-rw-r--r--tuner/SampleDvbTuner/res/mipmap-hdpi/ic_launcher.pngbin0 -> 3750 bytes
-rw-r--r--tuner/SampleDvbTuner/res/mipmap-mdpi/ic_launcher.pngbin0 -> 2218 bytes
-rw-r--r--tuner/SampleDvbTuner/res/mipmap-xhdpi/ic_launcher.pngbin0 -> 4847 bytes
-rw-r--r--tuner/SampleDvbTuner/res/mipmap-xxhdpi/ic_launcher.pngbin0 -> 7784 bytes
-rw-r--r--tuner/SampleDvbTuner/res/mipmap-xxxhdpi/ic_launcher.pngbin0 -> 11119 bytes
-rw-r--r--tuner/SampleDvbTuner/res/values/strings.xml5
-rw-r--r--tuner/SampleDvbTuner/res/xml/sample_dvb_tvinputservice.xml39
-rw-r--r--tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/AndroidManifest.xml41
-rw-r--r--tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/README.md37
-rw-r--r--tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/app/SampleDvbTuner.java64
-rw-r--r--tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/setup/SampleDvbTunerSetupActivity.java462
-rw-r--r--tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/tvinput/SampleDvbTunerTvInputService.java21
-rw-r--r--tuner/buildconfig.mk39
-rw-r--r--tuner/proto/channel.proto (renamed from proto/channel.proto)7
-rw-r--r--tuner/proto/track.proto (renamed from proto/track.proto)0
-rw-r--r--tuner/res/drawable-xhdpi/recommendation_antenna.png (renamed from usbtuner-res/drawable-xhdpi/recommendation_antenna.png)bin1662 -> 1662 bytes
-rw-r--r--tuner/res/drawable-xhdpi/usb_antenna.png (renamed from usbtuner-res/drawable-xhdpi/usb_antenna.png)bin1639 -> 1639 bytes
-rw-r--r--tuner/res/drawable/ut_scan_progress.xml (renamed from usbtuner-res/drawable/ut_scan_progress.xml)0
-rw-r--r--tuner/res/layout/guided_action_editable.xml (renamed from res/layout/guided_action_editable.xml)0
-rw-r--r--tuner/res/layout/ut_channel_list.xml (renamed from usbtuner-res/layout/ut_channel_list.xml)0
-rw-r--r--tuner/res/layout/ut_channel_scan.xml (renamed from usbtuner-res/layout/ut_channel_scan.xml)0
-rw-r--r--tuner/res/layout/ut_overlay_view.xml (renamed from usbtuner-res/layout/ut_overlay_view.xml)0
-rw-r--r--tuner/res/raw/ut_euro_dvbt_all (renamed from usbtuner-res/raw/ut_euro_dvbt_all)0
-rw-r--r--tuner/res/raw/ut_kr_all (renamed from usbtuner-res/raw/ut_kr_all)0
-rw-r--r--tuner/res/raw/ut_kr_atsc_center_frequencies_8vsb (renamed from usbtuner-res/raw/ut_kr_atsc_center_frequencies_8vsb)0
-rw-r--r--tuner/res/raw/ut_kr_cable_standard_center_frequencies_qam256 (renamed from usbtuner-res/raw/ut_kr_cable_standard_center_frequencies_qam256)0
-rw-r--r--tuner/res/raw/ut_kr_dev_cj_cable_center_frequencies_qam256 (renamed from usbtuner-res/raw/ut_kr_dev_cj_cable_center_frequencies_qam256)0
-rw-r--r--tuner/res/raw/ut_us_all (renamed from usbtuner-res/raw/ut_us_all)0
-rw-r--r--tuner/res/raw/ut_us_atsc_center_frequencies_8vsb (renamed from usbtuner-res/raw/ut_us_atsc_center_frequencies_8vsb)0
-rw-r--r--tuner/res/raw/ut_us_cable_standard_center_frequencies_qam256 (renamed from usbtuner-res/raw/ut_us_cable_standard_center_frequencies_qam256)0
-rw-r--r--tuner/res/values/attrs.xml (renamed from usbtuner-res/values/attrs.xml)0
-rw-r--r--tuner/res/values/colors.xml (renamed from usbtuner-res/values/colors.xml)0
-rw-r--r--tuner/res/values/dimens.xml (renamed from usbtuner-res/values/dimens.xml)0
-rw-r--r--tuner/res/values/integers.xml (renamed from usbtuner-res/values/integers.xml)0
-rw-r--r--tuner/res/values/strings.xml (renamed from usbtuner-res/values/strings.xml)46
-rw-r--r--tuner/res/xml/ut_tvinputservice.xml (renamed from usbtuner-res/xml/ut_tvinputservice.xml)2
-rw-r--r--tuner/src/com/android/tv/tuner/ChannelScanFileParser.java (renamed from src/com/android/tv/tuner/ChannelScanFileParser.java)41
-rw-r--r--tuner/src/com/android/tv/tuner/DvbDeviceAccessor.java (renamed from src/com/android/tv/tuner/DvbDeviceAccessor.java)31
-rw-r--r--tuner/src/com/android/tv/tuner/DvbTunerHal.java (renamed from src/com/android/tv/tuner/DvbTunerHal.java)26
-rw-r--r--tuner/src/com/android/tv/tuner/TunerFeatures.java103
-rw-r--r--tuner/src/com/android/tv/tuner/TunerHal.java (renamed from src/com/android/tv/tuner/TunerHal.java)93
-rw-r--r--tuner/src/com/android/tv/tuner/TunerPreferences.java100
-rw-r--r--tuner/src/com/android/tv/tuner/cc/CaptionLayout.java (renamed from src/com/android/tv/tuner/cc/CaptionLayout.java)17
-rw-r--r--tuner/src/com/android/tv/tuner/cc/CaptionTrackRenderer.java (renamed from src/com/android/tv/tuner/cc/CaptionTrackRenderer.java)17
-rw-r--r--tuner/src/com/android/tv/tuner/cc/CaptionWindowLayout.java (renamed from src/com/android/tv/tuner/cc/CaptionWindowLayout.java)122
-rw-r--r--tuner/src/com/android/tv/tuner/cc/Cea708Parser.java (renamed from src/com/android/tv/tuner/cc/Cea708Parser.java)638
-rw-r--r--tuner/src/com/android/tv/tuner/data/Cea708Data.java (renamed from src/com/android/tv/tuner/data/Cea708Data.java)83
-rw-r--r--tuner/src/com/android/tv/tuner/data/PsiData.java (renamed from src/com/android/tv/tuner/data/PsiData.java)19
-rw-r--r--tuner/src/com/android/tv/tuner/data/PsipData.java (renamed from src/com/android/tv/tuner/data/PsipData.java)163
-rw-r--r--tuner/src/com/android/tv/tuner/data/TunerChannel.java (renamed from src/com/android/tv/tuner/data/TunerChannel.java)235
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/Cea708TextTrackRenderer.java (renamed from src/com/android/tv/tuner/exoplayer/Cea708TextTrackRenderer.java)69
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/ExoPlayerExtractorsFactory.java (renamed from src/com/android/tv/tuner/exoplayer/ExoPlayerExtractorsFactory.java)14
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/ExoPlayerSampleExtractor.java (renamed from src/com/android/tv/tuner/exoplayer/ExoPlayerSampleExtractor.java)298
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/FileSampleExtractor.java (renamed from src/com/android/tv/tuner/exoplayer/FileSampleExtractor.java)29
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/MpegTsPlayer.java (renamed from src/com/android/tv/tuner/exoplayer/MpegTsPlayer.java)232
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/MpegTsRendererBuilder.java (renamed from src/com/android/tv/tuner/exoplayer/MpegTsRendererBuilder.java)40
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/MpegTsSampleExtractor.java (renamed from src/com/android/tv/tuner/exoplayer/MpegTsSampleExtractor.java)92
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/MpegTsSampleSource.java (renamed from src/com/android/tv/tuner/exoplayer/MpegTsSampleSource.java)5
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/MpegTsVideoTrackRenderer.java (renamed from src/com/android/tv/tuner/exoplayer/MpegTsVideoTrackRenderer.java)69
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/SampleExtractor.java (renamed from src/com/android/tv/tuner/exoplayer/SampleExtractor.java)83
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/audio/AudioClock.java (renamed from src/com/android/tv/tuner/exoplayer/audio/AudioClock.java)43
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/audio/AudioDecoder.java (renamed from src/com/android/tv/tuner/exoplayer/audio/AudioDecoder.java)3
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/audio/AudioTrackMonitor.java (renamed from src/com/android/tv/tuner/exoplayer/audio/AudioTrackMonitor.java)37
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/audio/AudioTrackWrapper.java (renamed from src/com/android/tv/tuner/exoplayer/audio/AudioTrackWrapper.java)10
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/audio/MediaCodecAudioDecoder.java (renamed from src/com/android/tv/tuner/exoplayer/audio/MediaCodecAudioDecoder.java)2
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/audio/MpegTsDefaultAudioTrackRenderer.java (renamed from src/com/android/tv/tuner/exoplayer/audio/MpegTsDefaultAudioTrackRenderer.java)194
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/audio/MpegTsMediaCodecAudioTrackRenderer.java (renamed from src/com/android/tv/tuner/exoplayer/audio/MpegTsMediaCodecAudioTrackRenderer.java)22
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/buffer/BufferManager.java (renamed from src/com/android/tv/tuner/exoplayer/buffer/BufferManager.java)217
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/buffer/DvrStorageManager.java (renamed from src/com/android/tv/tuner/exoplayer/buffer/DvrStorageManager.java)43
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/buffer/RecordingSampleBuffer.java (renamed from src/com/android/tv/tuner/exoplayer/buffer/RecordingSampleBuffer.java)86
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/buffer/SampleChunk.java (renamed from src/com/android/tv/tuner/exoplayer/buffer/SampleChunk.java)142
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/buffer/SampleChunkIoHelper.java (renamed from src/com/android/tv/tuner/exoplayer/buffer/SampleChunkIoHelper.java)103
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/buffer/SamplePool.java (renamed from src/com/android/tv/tuner/exoplayer/buffer/SamplePool.java)18
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/buffer/SampleQueue.java (renamed from src/com/android/tv/tuner/exoplayer/buffer/SampleQueue.java)5
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/buffer/SimpleSampleBuffer.java (renamed from src/com/android/tv/tuner/exoplayer/buffer/SimpleSampleBuffer.java)21
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/buffer/TrickplayStorageManager.java (renamed from src/com/android/tv/tuner/exoplayer/buffer/TrickplayStorageManager.java)82
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/text/SubtitleView.java (renamed from src/com/android/exoplayer/text/SubtitleView.java)37
-rw-r--r--tuner/src/com/android/tv/tuner/layout/ScaledLayout.java (renamed from src/com/android/tv/tuner/layout/ScaledLayout.java)124
-rw-r--r--tuner/src/com/android/tv/tuner/livetuner/LiveTvTunerTvInputService.java (renamed from src/com/android/tv/config/ConfigKeys.java)15
-rw-r--r--tuner/src/com/android/tv/tuner/setup/BaseTunerSetupActivity.java (renamed from src/com/android/tv/tuner/setup/TunerSetupActivity.java)427
-rw-r--r--tuner/src/com/android/tv/tuner/setup/ConnectionTypeFragment.java (renamed from src/com/android/tv/tuner/setup/ConnectionTypeFragment.java)32
-rw-r--r--tuner/src/com/android/tv/tuner/setup/LineupFragment.java235
-rw-r--r--tuner/src/com/android/tv/tuner/setup/LiveTvTunerSetupActivity.java75
-rw-r--r--tuner/src/com/android/tv/tuner/setup/PostalCodeFragment.java (renamed from src/com/android/tv/tuner/setup/PostalCodeFragment.java)54
-rw-r--r--tuner/src/com/android/tv/tuner/setup/ScanFragment.java (renamed from src/com/android/tv/tuner/setup/ScanFragment.java)196
-rw-r--r--tuner/src/com/android/tv/tuner/setup/ScanResultFragment.java (renamed from src/com/android/tv/tuner/setup/ScanResultFragment.java)47
-rw-r--r--tuner/src/com/android/tv/tuner/setup/WelcomeFragment.java (renamed from src/com/android/tv/tuner/setup/WelcomeFragment.java)38
-rw-r--r--tuner/src/com/android/tv/tuner/source/FileTsStreamer.java (renamed from src/com/android/tv/tuner/source/FileTsStreamer.java)71
-rw-r--r--tuner/src/com/android/tv/tuner/source/TsDataSource.java (renamed from src/com/android/tv/tuner/source/TsDataSource.java)13
-rw-r--r--tuner/src/com/android/tv/tuner/source/TsDataSourceManager.java (renamed from src/com/android/tv/tuner/source/TsDataSourceManager.java)49
-rw-r--r--tuner/src/com/android/tv/tuner/source/TsStreamWriter.java (renamed from src/com/android/tv/tuner/source/TsStreamWriter.java)33
-rw-r--r--tuner/src/com/android/tv/tuner/source/TsStreamer.java (renamed from src/com/android/tv/tuner/source/TsStreamer.java)13
-rw-r--r--tuner/src/com/android/tv/tuner/source/TunerTsStreamer.java (renamed from src/com/android/tv/tuner/source/TunerTsStreamer.java)72
-rw-r--r--tuner/src/com/android/tv/tuner/source/TunerTsStreamerManager.java (renamed from src/com/android/tv/tuner/source/TunerTsStreamerManager.java)69
-rw-r--r--tuner/src/com/android/tv/tuner/ts/SectionParser.java (renamed from src/com/android/tv/tuner/ts/SectionParser.java)701
-rw-r--r--tuner/src/com/android/tv/tuner/ts/TsParser.java (renamed from src/com/android/tv/tuner/ts/TsParser.java)337
-rw-r--r--tuner/src/com/android/tv/tuner/tvinput/BaseTunerTvInputService.java (renamed from src/com/android/tv/tuner/tvinput/TunerTvInputService.java)43
-rw-r--r--tuner/src/com/android/tv/tuner/tvinput/ChannelDataManager.java (renamed from src/com/android/tv/tuner/tvinput/ChannelDataManager.java)375
-rw-r--r--tuner/src/com/android/tv/tuner/tvinput/EventDetector.java349
-rw-r--r--tuner/src/com/android/tv/tuner/tvinput/FileSourceEventDetector.java259
-rw-r--r--tuner/src/com/android/tv/tuner/tvinput/PlaybackBufferListener.java (renamed from src/com/android/tv/tuner/tvinput/PlaybackBufferListener.java)8
-rw-r--r--tuner/src/com/android/tv/tuner/tvinput/TunerDebug.java (renamed from src/com/android/tv/tuner/tvinput/TunerDebug.java)15
-rw-r--r--tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSession.java (renamed from src/com/android/tv/tuner/tvinput/TunerRecordingSession.java)13
-rw-r--r--tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionWorker.java (renamed from src/com/android/tv/tuner/tvinput/TunerRecordingSessionWorker.java)447
-rw-r--r--tuner/src/com/android/tv/tuner/tvinput/TunerSession.java (renamed from src/com/android/tv/tuner/tvinput/TunerSession.java)177
-rw-r--r--tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorker.java (renamed from src/com/android/tv/tuner/tvinput/TunerSessionWorker.java)1079
-rw-r--r--tuner/src/com/android/tv/tuner/tvinput/TunerStorageCleanUpService.java (renamed from src/com/android/tv/tuner/tvinput/TunerStorageCleanUpService.java)52
-rw-r--r--tuner/src/com/android/tv/tuner/util/ByteArrayBuffer.java (renamed from src/com/android/tv/tuner/util/ByteArrayBuffer.java)22
-rw-r--r--tuner/src/com/android/tv/tuner/util/ConvertUtils.java (renamed from src/com/android/tv/tuner/util/ConvertUtils.java)6
-rw-r--r--tuner/src/com/android/tv/tuner/util/GlobalSettingsUtils.java (renamed from src/com/android/tv/tuner/util/GlobalSettingsUtils.java)6
-rw-r--r--tuner/src/com/android/tv/tuner/util/Ints.java41
-rw-r--r--tuner/src/com/android/tv/tuner/util/StatusTextUtils.java (renamed from src/com/android/tv/tuner/util/StatusTextUtils.java)64
-rw-r--r--tuner/src/com/android/tv/tuner/util/TunerInputInfoUtils.java (renamed from src/com/android/tv/tuner/util/TunerInputInfoUtils.java)23
-rw-r--r--tuner/src/com/google/android/exoplayer/MediaFormatUtil.java (renamed from src/com/android/exoplayer/MediaFormatUtil.java)50
-rw-r--r--tuner/src/com/google/android/exoplayer/MediaSoftwareCodecUtil.java (renamed from src/com/android/exoplayer/MediaSoftwareCodecUtil.java)73
-rw-r--r--tuner/src/com/mediatek/tunerservice/IMtkTuner.aidl27
-rw-r--r--tuner/tests/TestManifest.xml23
-rw-r--r--tuner/tests/assets/capture_kqed.tsbin0 -> 31457280 bytes
-rw-r--r--tuner/tests/assets/capture_stream.tsbin0 -> 73699196 bytes
-rw-r--r--tuner/tests/testing/Android.mk29
-rw-r--r--tuner/tests/testing/AndroidManifest.xml23
-rw-r--r--tuner/tests/testing/src/com/android/tv/tuner/testing/buffer/VerySlowSampleChunk.java55
-rw-r--r--tuner/tests/unittests/javatests/AndroidManifest.xml34
-rw-r--r--tuner/tests/unittests/javatests/com/android/tv/tuner/AndroidManifest.xml31
-rw-r--r--tuner/tests/unittests/javatests/com/android/tv/tuner/FakeTunerHal.java79
-rw-r--r--tuner/tests/unittests/javatests/com/android/tv/tuner/FileTunerHal.java274
-rw-r--r--tuner/tests/unittests/javatests/com/android/tv/tuner/ZappingTimeTest.java413
-rw-r--r--tuner/tests/unittests/javatests/com/android/tv/tuner/layout/tests/AndroidManifest.xml25
-rw-r--r--tuner/tests/unittests/javatests/com/android/tv/tuner/layout/tests/ScaledLayoutActivity.java83
-rw-r--r--tuner/tests/unittests/javatests/com/android/tv/tuner/layout/tests/ScaledLayoutTest.java147
-rw-r--r--tuner/tests/unittests/javatests/com/android/tv/tuner/setup/TunerHalFactoryTest.java89
-rw-r--r--tuner/tests/unittests/javatests/res/layout/activity_scaled_layout_test.xml53
-rw-r--r--tuner/tests/unittests/javatests/res/values/attrs.xml25
-rw-r--r--usbtuner-res/values-af/strings.xml90
-rw-r--r--usbtuner-res/values-am/strings.xml90
-rw-r--r--usbtuner-res/values-ar/strings.xml102
-rw-r--r--usbtuner-res/values-az-rAZ/strings.xml90
-rw-r--r--usbtuner-res/values-bg/strings.xml90
-rw-r--r--usbtuner-res/values-bn-rBD/strings.xml90
-rw-r--r--usbtuner-res/values-ca/strings.xml90
-rw-r--r--usbtuner-res/values-cs/strings.xml96
-rw-r--r--usbtuner-res/values-da/strings.xml90
-rw-r--r--usbtuner-res/values-de/strings.xml90
-rw-r--r--usbtuner-res/values-el/strings.xml90
-rw-r--r--usbtuner-res/values-en-rAU/strings.xml90
-rw-r--r--usbtuner-res/values-en-rGB/strings.xml90
-rw-r--r--usbtuner-res/values-en-rIN/strings.xml90
-rw-r--r--usbtuner-res/values-es-rUS/strings.xml90
-rw-r--r--usbtuner-res/values-es/strings.xml90
-rw-r--r--usbtuner-res/values-et-rEE/strings.xml90
-rw-r--r--usbtuner-res/values-eu-rES/strings.xml90
-rw-r--r--usbtuner-res/values-fa/strings.xml90
-rw-r--r--usbtuner-res/values-fi/strings.xml90
-rw-r--r--usbtuner-res/values-fr-rCA/strings.xml90
-rw-r--r--usbtuner-res/values-fr/strings.xml90
-rw-r--r--usbtuner-res/values-gl-rES/strings.xml90
-rw-r--r--usbtuner-res/values-hi/strings.xml90
-rw-r--r--usbtuner-res/values-hr/strings.xml93
-rw-r--r--usbtuner-res/values-hu/strings.xml90
-rw-r--r--usbtuner-res/values-hy-rAM/strings.xml90
-rw-r--r--usbtuner-res/values-in/strings.xml90
-rw-r--r--usbtuner-res/values-is-rIS/strings.xml90
-rw-r--r--usbtuner-res/values-it/strings.xml90
-rw-r--r--usbtuner-res/values-iw/strings.xml96
-rw-r--r--usbtuner-res/values-ja/strings.xml90
-rw-r--r--usbtuner-res/values-ka-rGE/strings.xml90
-rw-r--r--usbtuner-res/values-kk-rKZ/strings.xml90
-rw-r--r--usbtuner-res/values-km-rKH/strings.xml90
-rw-r--r--usbtuner-res/values-kn-rIN/strings.xml90
-rw-r--r--usbtuner-res/values-ko/strings.xml90
-rw-r--r--usbtuner-res/values-ky-rKG/strings.xml90
-rw-r--r--usbtuner-res/values-lo-rLA/strings.xml90
-rw-r--r--usbtuner-res/values-lt/strings.xml96
-rw-r--r--usbtuner-res/values-lv/strings.xml93
-rw-r--r--usbtuner-res/values-mk-rMK/strings.xml90
-rw-r--r--usbtuner-res/values-ml-rIN/strings.xml90
-rw-r--r--usbtuner-res/values-mn-rMN/strings.xml90
-rw-r--r--usbtuner-res/values-mr-rIN/strings.xml90
-rw-r--r--usbtuner-res/values-ms-rMY/strings.xml90
-rw-r--r--usbtuner-res/values-my-rMM/strings.xml90
-rw-r--r--usbtuner-res/values-nb/strings.xml90
-rw-r--r--usbtuner-res/values-ne-rNP/strings.xml90
-rw-r--r--usbtuner-res/values-nl/strings.xml90
-rw-r--r--usbtuner-res/values-pl/strings.xml96
-rw-r--r--usbtuner-res/values-pt-rPT/strings.xml90
-rw-r--r--usbtuner-res/values-pt/strings.xml90
-rw-r--r--usbtuner-res/values-ro/strings.xml93
-rw-r--r--usbtuner-res/values-ru/strings.xml92
-rw-r--r--usbtuner-res/values-si-rLK/strings.xml90
-rw-r--r--usbtuner-res/values-sk/strings.xml96
-rw-r--r--usbtuner-res/values-sl/strings.xml96
-rw-r--r--usbtuner-res/values-sr/strings.xml93
-rw-r--r--usbtuner-res/values-sv/strings.xml90
-rw-r--r--usbtuner-res/values-sw/strings.xml90
-rw-r--r--usbtuner-res/values-ta-rIN/strings.xml90
-rw-r--r--usbtuner-res/values-te-rIN/strings.xml90
-rw-r--r--usbtuner-res/values-th/strings.xml90
-rw-r--r--usbtuner-res/values-tl/strings.xml90
-rw-r--r--usbtuner-res/values-tr/strings.xml90
-rw-r--r--usbtuner-res/values-uk/strings.xml96
-rw-r--r--usbtuner-res/values-ur-rPK/strings.xml90
-rw-r--r--usbtuner-res/values-uz-rUZ/strings.xml90
-rw-r--r--usbtuner-res/values-vi/strings.xml90
-rw-r--r--usbtuner-res/values-zh-rCN/strings.xml90
-rw-r--r--usbtuner-res/values-zh-rHK/strings.xml90
-rw-r--r--usbtuner-res/values-zh-rTW/strings.xml90
-rw-r--r--usbtuner-res/values-zu/strings.xml90
-rw-r--r--version.mk12
800 files changed, 42923 insertions, 38770 deletions
diff --git a/Android.mk b/Android.mk
index 76e429b9..351d8b57 100644
--- a/Android.mk
+++ b/Android.mk
@@ -23,14 +23,10 @@ LOCAL_MODULE_TAGS := optional
include $(LOCAL_PATH)/version.mk
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
- $(call all-proto-files-under, proto)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := LiveTv
-
-
# It is required for com.android.providers.tv.permission.ALL_EPG_DATA
LOCAL_PRIVILEGED_MODULE := true
@@ -41,26 +37,29 @@ LOCAL_USE_AAPT2 := true
LOCAL_RESOURCE_DIR := \
$(LOCAL_PATH)/res \
- $(LOCAL_PATH)/usbtuner-res
-
-LOCAL_SRC_FILES += \
- src/com/android/tv/tuner/exoplayer/ffmpeg/IFfmpegDecoder.aidl
LOCAL_STATIC_JAVA_LIBRARIES := \
- icu4j-usbtuner \
+ android-support-annotations \
lib-exoplayer \
- lib-exoplayer-v2 \
- lib-exoplayer-v2-ext-ffmpeg
+ lib-exoplayer-v2-core \
+ jsr330 \
LOCAL_STATIC_ANDROID_LIBRARIES := \
- android-support-annotations \
android-support-compat \
android-support-core-ui \
android-support-tv-provider \
+ android-support-v4 \
+ android-support-v7-appcompat \
android-support-v7-palette \
+ android-support-v7-preference \
android-support-v7-recyclerview \
+ android-support-v14-preference \
android-support-v17-leanback \
- tv-common
+ android-support-v17-preference-leanback \
+ live-channels-partner-support \
+ live-tv-tuner \
+ tv-common \
+
LOCAL_JAVACFLAGS := -Xlint:deprecation -Xlint:unchecked
@@ -68,48 +67,36 @@ LOCAL_AAPT_FLAGS += \
--version-name "$(version_name_package)" \
--version-code $(version_code_package) \
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-
LOCAL_JNI_SHARED_LIBRARIES := libtunertvinput_jni
LOCAL_AAPT_FLAGS += --extra-packages com.android.tv.tuner
-LOCAL_PROTOC_OPTIMIZE_TYPE := nano
-LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/proto/
-
include $(BUILD_PACKAGE)
-# --------------------------------------------------------------
-# Build a tiny icu4j library out of the classes necessary for the project.
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := icu4j-usbtuner
-LOCAL_MODULE_TAGS := optional
-icu4j_path := icu/icu4j
-LOCAL_SRC_FILES := \
- $(icu4j_path)/main/classes/core/src/com/ibm/icu/text/SCSU.java \
- $(icu4j_path)/main/classes/core/src/com/ibm/icu/text/UnicodeDecompressor.java
-LOCAL_SDK_VERSION := system_current
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-
#############################################################
# Pre-built dependency jars
#############################################################
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \
- lib-exoplayer:libs/exoplayer.jar \
- lib-exoplayer-v2:libs/exoplayer_v2.jar \
- lib-exoplayer-v2-ext-ffmpeg:libs/exoplayer_v2_ext_ffmpeg.jar \
-
-
-include $(BUILD_MULTI_PREBUILT)
-
+prebuilts := \
+ lib-exoplayer:libs/exoplayer-r1.5.16.aar \
+ lib-exoplayer-v2-core:libs/exoplayer-core-2-SNAPHOT-20180114.aar \
+ auto-value-jar:../../../prebuilts/tools/common/m2/repository/com/google/auto/value/auto-value/1.5.2/auto-value-1.5.2.jar \
+ javax-annotations-jar:../../../prebuilts/tools/common/m2/repository/javax/annotation/javax.annotation-api/1.2/javax.annotation-api-1.2.jar \
+ truth-0-36-prebuilt-jar:../../../prebuilts/tools/common/m2/repository/com/google/truth/truth/0.36/truth-0.36.jar \
+
+define define-prebuilt
+ $(eval tw := $(subst :, ,$(strip $(1)))) \
+ $(eval include $(CLEAR_VARS)) \
+ $(eval LOCAL_MODULE := $(word 1,$(tw))) \
+ $(eval LOCAL_MODULE_TAGS := optional) \
+ $(eval LOCAL_MODULE_CLASS := JAVA_LIBRARIES) \
+ $(eval LOCAL_SRC_FILES := $(word 2,$(tw))) \
+ $(eval LOCAL_UNINSTALLABLE_MODULE := true) \
+ $(eval LOCAL_SDK_VERSION := current) \
+ $(eval include $(BUILD_PREBUILT))
+endef
+
+$(foreach p,$(prebuilts),\
+ $(call define-prebuilt,$(p)))
+
+prebuilts :=
include $(call all-makefiles-under,$(LOCAL_PATH))
-
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 8770301e..3456f16b 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -13,15 +13,19 @@
~ 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.
- -->
-
+-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.tv" xmlns:tools="http://schemas.android.com/tools">
+ package="com.android.tv" >
+
+ <uses-sdk
+ android:minSdkVersion="23"
+ android:targetSdkVersion="26" />
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_HDMI_CEC_ACTIVE_SOURCE" />
- <uses-permission android:name="android.permission.GLOBAL_SEARCH" tools:ignore="ProtectedPermissions"/>
+ <uses-permission android:name="android.permission.GLOBAL_SEARCH" />
+ <uses-permission android:name="android.permission.HDMI_CEC" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MODIFY_PARENTAL_CONTROLS" />
<uses-permission android:name="android.permission.READ_CONTENT_RATING_SYSTEMS" />
@@ -32,57 +36,94 @@
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
<uses-permission android:name="com.android.providers.tv.permission.ACCESS_ALL_EPG_DATA" />
<uses-permission android:name="com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS" />
-
<!-- Permissions/feature for USB tuner -->
<uses-permission android:name="android.permission.DVB_DEVICE" />
- <uses-feature android:name="android.hardware.usb.host" android:required="false" />
- <!-- Limit only for Android TV -->
- <uses-feature android:name="android.software.leanback" android:required="true" />
- <uses-feature android:name="android.software.live_tv" android:required="true" />
- <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
+ <uses-feature
+ android:name="android.hardware.usb.host"
+ android:required="false" />
+ <!-- Limit only for Android TV -->
+ <uses-feature
+ android:name="android.software.leanback"
+ android:required="true" />
+ <uses-feature
+ android:name="android.software.live_tv"
+ android:required="true" />
+ <uses-feature
+ android:name="android.hardware.touchscreen"
+ android:required="false" />
<!-- Receives input events from the TV app. -->
- <permission android:name="com.android.tv.permission.RECEIVE_INPUT_EVENT"
- android:protectionLevel="signatureOrSystem"
- android:label="@string/permlab_receiveInputEvent"
+ <permission
+ android:name="com.android.tv.permission.RECEIVE_INPUT_EVENT"
android:description="@string/permdesc_receiveInputEvent"
- tools:ignore="SignatureOrSystemPermissions"/>
+ android:label="@string/permlab_receiveInputEvent"
+ android:protectionLevel="signatureOrSystem" />
<!-- Customizes Live TV with customization packages. -->
- <permission android:name="com.android.tv.permission.CUSTOMIZE_TV_APP"
- android:protectionLevel="signatureOrSystem"
- android:label="@string/permlab_customizeTvApp"
+ <permission
+ android:name="com.android.tv.permission.CUSTOMIZE_TV_APP"
android:description="@string/permdesc_customizeTvApp"
- tools:ignore="SignatureOrSystemPermissions"/>
-
- <uses-sdk android:targetSdkVersion="26" android:minSdkVersion="23"/>
+ android:label="@string/permlab_customizeTvApp"
+ android:protectionLevel="signatureOrSystem" />
- <application android:label="@string/app_name"
- android:name=".TvApplication"
+ <application
+ android:name="com.android.tv.app.LiveTvApplication"
android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
android:banner="@drawable/banner"
+ android:icon="@drawable/ic_live_channels"
+ android:label="@string/app_name"
android:supportsRtl="true"
- android:theme="@style/Theme.TV">
- <activity android:name="com.android.tv.TvActivity">
+ android:theme="@style/Theme.TV" >
+ <activity
+ android:name="com.android.tv.tuner.setup.LiveTvTunerSetupActivity"
+ android:configChanges="keyboard|keyboardHidden"
+ android:label="@string/bt_app_name"
+ android:launchMode="singleInstance"
+ android:process="com.android.tv.tuner"
+ android:theme="@style/Theme.Setup.GuidedStep" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
+
+ <!-- providers are listed here to keep them separate from the internal versions -->
+ <provider
+ android:name="com.android.tv.search.LocalSearchProvider"
+ android:authorities="com.android.tv.search"
+ android:enabled="true"
+ android:exported="true" >
+ <meta-data
+ android:name="SupportedSwitchActionType"
+ android:value="CHANNEL|TVINPUT" />
+ </provider>
+ <provider
+ android:name="com.android.tv.common.CommonPreferenceProvider"
+ android:authorities="com.android.tv.common.preferences"
+ android:exported="false"
+ android:process="com.android.tv.common" />
+
+ <activity android:name="com.android.tv.TvActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
+
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity>
-
- <activity android:name="com.android.tv.MainActivity"
+ <activity
+ android:name="com.android.tv.MainActivity"
android:configChanges="keyboard|keyboardHidden|screenSize|smallestScreenSize|screenLayout|orientation"
- android:screenOrientation="landscape"
android:launchMode="singleTask"
android:resizeableActivity="true"
+ android:screenOrientation="landscape"
android:supportsPictureInPicture="true"
- android:theme="@style/Theme.TV.MainActivity">
+ android:theme="@style/Theme.TV.MainActivity" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
+
<category android:name="android.intent.category.DEFAULT" />
+
<data android:mimeType="vnd.android.cursor.item/channel" />
<data android:mimeType="vnd.android.cursor.dir/channel" />
<data android:mimeType="vnd.android.cursor.item/program" />
@@ -90,175 +131,146 @@
</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" />
- <meta-data android:name="android.app.searchable"
+
+ <meta-data
+ android:name="supports_leanback"
+ android:value="true" />
+ <meta-data
+ android:name="android.app.searchable"
android:resource="@xml/searchable" />
</activity>
-
- <activity android:name=".LauncherActivity"
+ <activity
+ android:name="com.android.tv.LauncherActivity"
android:configChanges="keyboard|keyboardHidden"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
-
- <activity android:name=".SetupPassthroughActivity"
+ <activity
+ android:name="com.android.tv.SetupPassthroughActivity"
android:configChanges="keyboard|keyboardHidden"
- android:theme="@android:style/Theme.Translucent.NoTitleBar">
+ android:exported="true"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar" >
<intent-filter>
<action android:name="com.android.tv.action.LAUNCH_INPUT_SETUP" />
+
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
-
- <activity android:name=".SelectInputActivity"
+ <activity
+ android:name="com.android.tv.SelectInputActivity"
android:configChanges="keyboard|keyboardHidden"
android:launchMode="singleTask"
android:theme="@style/Theme.SelectInputActivity" />
-
- <activity android:name=".onboarding.OnboardingActivity"
+ <activity
+ android:name="com.android.tv.onboarding.OnboardingActivity"
android:configChanges="keyboard|keyboardHidden"
android:launchMode="singleTop"
android:theme="@style/Theme.Setup.GuidedStep" />
-
- <activity android:name=".dvr.ui.browse.DvrBrowseActivity"
+ <activity
+ android:name="com.android.tv.dvr.ui.browse.DvrBrowseActivity"
android:configChanges="keyboard|keyboardHidden"
android:launchMode="singleTask"
- android:theme="@style/Theme.Leanback.Browse">
+ android:theme="@style/Theme.Leanback.Browse" >
<intent-filter>
<action android:name="android.media.tv.action.VIEW_RECORDING_SCHEDULES" />
+
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
+
<category android:name="android.intent.category.DEFAULT" />
+
<data android:mimeType="vnd.android.cursor.dir/recorded_program" />
</intent-filter>
</activity>
-
- <activity android:name=".dvr.ui.playback.DvrPlaybackActivity"
+ <activity
+ android:name="com.android.tv.dvr.ui.playback.DvrPlaybackActivity"
android:configChanges="keyboard|keyboardHidden|screenSize|smallestScreenSize|screenLayout|orientation"
android:launchMode="singleTask"
- android:theme="@style/Theme.Leanback">
+ android:theme="@style/Theme.Leanback" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
+
<category android:name="android.intent.category.DEFAULT" />
+
<data android:mimeType="vnd.android.cursor.item/recorded_program" />
</intent-filter>
</activity>
-
- <activity android:name=".dvr.ui.browse.DvrDetailsActivity"
+ <activity
+ android:name="com.android.tv.dvr.ui.browse.DvrDetailsActivity"
android:configChanges="keyboard|keyboardHidden"
android:theme="@style/Theme.TV.Dvr.Browse.Details" />
-
- <activity android:name=".dvr.ui.DvrSeriesSettingsActivity"
+ <activity
+ android:name="com.android.tv.dvr.ui.DvrSeriesSettingsActivity"
android:configChanges="keyboard|keyboardHidden"
android:theme="@style/Theme.TV.Dvr.Series.Settings.GuidedStep" />
-
- <activity android:name=".dvr.ui.DvrSeriesDeletionActivity"
+ <activity
+ android:name="com.android.tv.dvr.ui.DvrSeriesDeletionActivity"
android:configChanges="keyboard|keyboardHidden"
android:theme="@style/Theme.TV.Dvr.Series.Deletion.GuidedStep" />
-
- <activity android:name=".dvr.ui.DvrSeriesScheduledDialogActivity"
- android:theme="@style/Theme.TV.dialog.HalfSizedDialog"/>
-
- <activity android:name=".dvr.ui.list.DvrSchedulesActivity"
+ <activity
+ android:name="com.android.tv.dvr.ui.DvrSeriesScheduledDialogActivity"
+ android:theme="@style/Theme.TV.dialog.HalfSizedDialog" />
+ <activity
+ android:name="com.android.tv.dvr.ui.list.DvrSchedulesActivity"
android:configChanges="keyboard|keyboardHidden"
android:theme="@style/Theme.Leanback.Details" />
+ <activity
+ android:name="com.android.tv.dvr.ui.list.DvrHistoryActivity"
+ android:configChanges="keyboard|keyboardHidden"
+ android:exported="false"
+ android:theme="@style/Theme.Leanback.Details" />
- <provider android:name="com.android.tv.search.LocalSearchProvider"
- android:authorities="com.android.tv.search"
- android:exported="true"
- android:enabled="true" tools:ignore="ExportedContentProvider">
- <meta-data android:name="SupportedSwitchActionType" android:value="CHANNEL|TVINPUT" />
- </provider>
-
- <service android:name="com.android.tv.recommendation.NotificationService"
- android:exported="false" />
- <service android:name=".recommendation.ChannelPreviewUpdater$ChannelPreviewUpdateService"
- android:permission="android.permission.BIND_JOB_SERVICE" />
+ <service
+ android:name="com.android.tv.recommendation.NotificationService"
+ android:exported="false" />
+ <service
+ android:name="com.android.tv.recommendation.ChannelPreviewUpdater$ChannelPreviewUpdateService"
+ android:permission="android.permission.BIND_JOB_SERVICE" />
- <receiver android:name="com.android.tv.receiver.BootCompletedReceiver">
+ <receiver android:name="com.android.tv.receiver.BootCompletedReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
- <receiver android:name="com.android.tv.receiver.PackageIntentsReceiver">
+ <receiver android:name="com.android.tv.receiver.PackageIntentsReceiver" >
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<!-- PACKAGE_CHANGED for package enabled/disabled notification -->
<action android:name="android.intent.action.PACKAGE_CHANGED" />
<action android:name="android.intent.action.PACKAGE_REMOVED" />
- <data android:scheme="package"/>
+
+ <data android:scheme="package" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
- </receiver>
- <receiver android:name="com.android.tv.receiver.GlobalKeyReceiver">
- <intent-filter>
- <action android:name="android.intent.action.GLOBAL_BUTTON" />
- </intent-filter>
-
- <!-- Not directly related to GlobalKeyReceiver but needed to be able to provide our
- content rating definitions to the system service. -->
- <intent-filter>
- <action android:name="android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS" />
- </intent-filter>
- <meta-data android:name="android.media.tv.metadata.CONTENT_RATING_SYSTEMS"
- android:resource="@xml/tv_content_rating_systems" />
- </receiver>
-
- <!-- USB tuner components definition -->
- <activity android:name="com.android.tv.tuner.setup.TunerSetupActivity"
+ </receiver> <!-- System initial setup component definition -->
+ <activity
+ android:name="com.android.tv.setup.SystemSetupActivity"
android:configChanges="keyboard|keyboardHidden"
+ android:exported="true"
android:label="@string/bt_app_name"
android:launchMode="singleInstance"
- android:process="com.android.tv.tuner"
android:theme="@style/Theme.Setup.GuidedStep" >
<intent-filter>
- <action android:name="android.intent.action.MAIN" />
- </intent-filter>
- </activity>
-
- <service android:name=".tuner.tvinput.TunerTvInputService"
- android:enabled="false"
- android:process="com.android.tv.tuner"
- android:label="@string/bt_app_name"
- android:permission="android.permission.BIND_TV_INPUT" >
- <intent-filter>
- <action android:name="android.media.tv.TvInputService" />
- </intent-filter>
- <meta-data android:name="android.media.tv.input"
- android:resource="@xml/ut_tvinputservice" />
- </service>
- <service android:name=".tuner.exoplayer.ffmpeg.FfmpegDecoderService"
- android:isolatedProcess="true"
- android:process="com.android.tv.ffmpeg" >
- <intent-filter>
- <action android:name=".tuner.exoplayer.ffmpeg.IFfmpegDecoder" />
- </intent-filter>
- </service>
- <provider android:name=".tuner.TunerPreferenceProvider"
- android:authorities="com.android.tv.tuner.preferences"
- android:process="com.android.tv.tuner"
- android:exported="false" />
- <!-- System initial setup component definition -->
- <activity android:name=".setup.SystemSetupActivity"
- android:configChanges="keyboard|keyboardHidden"
- android:label="@string/bt_app_name"
- android:launchMode="singleInstance"
- android:theme="@style/Theme.Setup.GuidedStep" >
- <intent-filter>
<action android:name="com.android.tv.action.LAUNCH_SYSTEM_SETUP" />
+
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
- <!-- TunerInputController should be the same process with MainActivity to check status of MainActivity -->
- <receiver android:name=".tuner.TunerInputController$IntentReceiver"
- android:exported="false">
+ <!--
+ TunerInputController should be the same process with MainActivity to check status
+ of MainActivity
+ -->
+ <receiver
+ android:name="com.android.tv.tuner.TunerInputController$IntentReceiver"
+ android:exported="false" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
@@ -267,20 +279,56 @@
<action android:name="com.android.tv.action.NETWORK_TUNER_ATTACHED" />
<action android:name="com.android.tv.action.NETWORK_TUNER_DETACHED" />
</intent-filter>
- </receiver>
+ </receiver> <!-- DVR -->
+ <service
+ android:name="com.android.tv.dvr.recorder.DvrRecordingService"
+ android:label="@string/dvr_service_name" />
- <!-- DVR -->
- <service android:name=".dvr.recorder.DvrRecordingService" android:label="@string/dvr_service_name" />
- <receiver android:name=".dvr.recorder.DvrStartRecordingReceiver" />
+ <receiver android:name="com.android.tv.dvr.recorder.DvrStartRecordingReceiver" />
- <service android:name=".tuner.tvinput.TunerStorageCleanUpService"
+ <service
+ android:name="com.android.tv.tuner.tvinput.TunerStorageCleanUpService"
+ android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE"
- android:process="com.android.tv.tuner"
- android:exported="true" />
-
- <service android:name=".data.epg.EpgFetcher$EpgFetchService"
+ android:process="com.android.tv.tuner" />
+ <service
+ android:name="com.android.tv.data.epg.EpgFetchService"
android:permission="android.permission.BIND_JOB_SERVICE" />
+ <receiver
+ android:name="com.android.tv.livetv.receiver.GlobalKeyReceiver"
+ android:exported="true" >
+ <intent-filter>
+ <action android:name="android.intent.action.GLOBAL_BUTTON" />
+ </intent-filter>
+ <!--
+ Not directly related to GlobalKeyReceiver but needed to be able to provide our
+ content rating definitions to the system service.
+ -->
+ <intent-filter>
+ <action android:name="android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS" />
+ </intent-filter>
+
+ <meta-data
+ android:name="android.media.tv.metadata.CONTENT_RATING_SYSTEMS"
+ android:resource="@xml/tv_content_rating_systems" />
+ </receiver>
+
+ <service
+ android:name="com.android.tv.tuner.livetuner.LiveTvTunerTvInputService"
+ android:enabled="false"
+ android:label="@string/bt_app_name"
+ android:permission="android.permission.BIND_TV_INPUT"
+ android:process="com.android.tv.tuner" >
+ <intent-filter>
+ <action android:name="android.media.tv.TvInputService" />
+ </intent-filter>
+
+ <meta-data
+ android:name="android.media.tv.input"
+ android:resource="@xml/ut_tvinputservice" />
+ </service>
</application>
-</manifest>
+
+</manifest> \ No newline at end of file
diff --git a/LiveChannelsAndroidStyle.xml b/LiveChannelsAndroidStyle.xml
deleted file mode 100644
index a2627ce2..00000000
--- a/LiveChannelsAndroidStyle.xml
+++ /dev/null
@@ -1,280 +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.
- -->
-
-<code_scheme name="LiveChannelsAndroidStyle">
- <option name="JAVA_INDENT_OPTIONS">
- <value>
- <option name="INDENT_SIZE" value="4" />
- <option name="CONTINUATION_INDENT_SIZE" value="8" />
- <option name="TAB_SIZE" value="8" />
- <option name="USE_TAB_CHARACTER" value="false" />
- <option name="SMART_TABS" value="false" />
- <option name="LABEL_INDENT_SIZE" value="0" />
- <option name="LABEL_INDENT_ABSOLUTE" value="false" />
- <option name="USE_RELATIVE_INDENTS" value="false" />
- </value>
- </option>
- <option name="FIELD_NAME_PREFIX" value="m" />
- <option name="STATIC_FIELD_NAME_PREFIX" value="s" />
- <option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="9999" />
- <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" />
- <emptyLine />
- <package name="com" withSubpackages="true" static="false" />
- <emptyLine />
- <package name="junit" withSubpackages="true" static="false" />
- <emptyLine />
- <package name="net" withSubpackages="true" static="false" />
- <emptyLine />
- <package name="org" withSubpackages="true" static="false" />
- <emptyLine />
- <package name="java" withSubpackages="true" static="false" />
- <emptyLine />
- <package name="javax" withSubpackages="true" static="false" />
- <emptyLine />
- <package name="" withSubpackages="true" static="false" />
- </value>
- </option>
- <option name="RIGHT_MARGIN" value="100" />
- <option name="JD_P_AT_EMPTY_LINES" value="false" />
- <option name="JD_DO_NOT_WRAP_ONE_LINE_COMMENTS" value="true" />
- <option name="JD_KEEP_EMPTY_PARAMETER" value="false" />
- <option name="JD_KEEP_EMPTY_EXCEPTION" value="false" />
- <option name="JD_KEEP_EMPTY_RETURN" value="false" />
- <option name="JD_PRESERVE_LINE_FEEDS" value="true" />
- <option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" />
- <option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
- <option name="BLANK_LINES_AROUND_FIELD" value="1" />
- <option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
- <option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
- <option name="ALIGN_MULTILINE_FOR" value="false" />
- <option name="CALL_PARAMETERS_WRAP" value="1" />
- <option name="METHOD_PARAMETERS_WRAP" value="1" />
- <option name="EXTENDS_LIST_WRAP" value="1" />
- <option name="THROWS_LIST_WRAP" value="1" />
- <option name="EXTENDS_KEYWORD_WRAP" value="1" />
- <option name="THROWS_KEYWORD_WRAP" value="1" />
- <option name="METHOD_CALL_CHAIN_WRAP" value="1" />
- <option name="BINARY_OPERATION_WRAP" value="1" />
- <option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
- <option name="TERNARY_OPERATION_WRAP" value="1" />
- <option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
- <option name="FOR_STATEMENT_WRAP" value="1" />
- <option name="ARRAY_INITIALIZER_WRAP" value="1" />
- <option name="ASSIGNMENT_WRAP" value="1" />
- <option name="PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE" value="true" />
- <option name="WRAP_COMMENTS" value="true" />
- <option name="IF_BRACE_FORCE" value="3" />
- <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">
- <option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
- <option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
- <option name="ALIGN_MULTILINE_FOR" value="false" />
- <option name="CALL_PARAMETERS_WRAP" value="1" />
- <option name="METHOD_PARAMETERS_WRAP" value="1" />
- <option name="BINARY_OPERATION_WRAP" value="1" />
- <option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
- <option name="TERNARY_OPERATION_WRAP" value="1" />
- <option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
- <option name="FOR_STATEMENT_WRAP" value="1" />
- <option name="ASSIGNMENT_WRAP" value="1" />
- <option name="PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE" value="true" />
- <option name="PARENT_SETTINGS_INSTALLED" value="true" />
- </codeStyleSettings>
- <codeStyleSettings language="CoffeeScript">
- <option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
- <option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
- <option name="METHOD_PARAMETERS_WRAP" value="1" />
- <option name="PARENT_SETTINGS_INSTALLED" value="true" />
- </codeStyleSettings>
- <codeStyleSettings language="ECMA Script Level 4">
- <option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
- <option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
- <option name="ALIGN_MULTILINE_FOR" value="false" />
- <option name="CALL_PARAMETERS_WRAP" value="1" />
- <option name="METHOD_PARAMETERS_WRAP" value="1" />
- <option name="EXTENDS_LIST_WRAP" value="1" />
- <option name="EXTENDS_KEYWORD_WRAP" value="1" />
- <option name="BINARY_OPERATION_WRAP" value="1" />
- <option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
- <option name="TERNARY_OPERATION_WRAP" value="1" />
- <option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
- <option name="FOR_STATEMENT_WRAP" value="1" />
- <option name="ARRAY_INITIALIZER_WRAP" value="1" />
- <option name="ASSIGNMENT_WRAP" value="1" />
- <option name="PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE" value="true" />
- <option name="WRAP_COMMENTS" value="true" />
- <option name="IF_BRACE_FORCE" value="3" />
- <option name="DOWHILE_BRACE_FORCE" value="3" />
- <option name="WHILE_BRACE_FORCE" value="3" />
- <option name="FOR_BRACE_FORCE" value="3" />
- <option name="PARENT_SETTINGS_INSTALLED" value="true" />
- </codeStyleSettings>
- <codeStyleSettings language="Groovy">
- <option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" />
- <option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
- <option name="BLANK_LINES_AROUND_FIELD" value="1" />
- <option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
- <option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
- <option name="ALIGN_MULTILINE_FOR" value="false" />
- <option name="CALL_PARAMETERS_WRAP" value="1" />
- <option name="METHOD_PARAMETERS_WRAP" value="1" />
- <option name="EXTENDS_LIST_WRAP" value="1" />
- <option name="THROWS_LIST_WRAP" value="1" />
- <option name="EXTENDS_KEYWORD_WRAP" value="1" />
- <option name="THROWS_KEYWORD_WRAP" value="1" />
- <option name="METHOD_CALL_CHAIN_WRAP" value="1" />
- <option name="BINARY_OPERATION_WRAP" value="1" />
- <option name="TERNARY_OPERATION_WRAP" value="1" />
- <option name="FOR_STATEMENT_WRAP" value="1" />
- <option name="ASSIGNMENT_WRAP" value="1" />
- <option name="IF_BRACE_FORCE" value="3" />
- <option name="WHILE_BRACE_FORCE" value="3" />
- <option name="FOR_BRACE_FORCE" value="3" />
- <option name="PARENT_SETTINGS_INSTALLED" value="true" />
- </codeStyleSettings>
- <codeStyleSettings language="JAVA">
- <option name="KEEP_LINE_BREAKS" value="false" />
- <option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" />
- <option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
- <option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
- <option name="ALIGN_MULTILINE_FOR" value="false" />
- <option name="CALL_PARAMETERS_WRAP" value="1" />
- <option name="METHOD_PARAMETERS_WRAP" value="1" />
- <option name="EXTENDS_LIST_WRAP" value="1" />
- <option name="THROWS_LIST_WRAP" value="1" />
- <option name="EXTENDS_KEYWORD_WRAP" value="1" />
- <option name="THROWS_KEYWORD_WRAP" value="1" />
- <option name="METHOD_CALL_CHAIN_WRAP" value="1" />
- <option name="BINARY_OPERATION_WRAP" value="1" />
- <option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
- <option name="TERNARY_OPERATION_WRAP" value="1" />
- <option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
- <option name="FOR_STATEMENT_WRAP" value="1" />
- <option name="ARRAY_INITIALIZER_WRAP" value="1" />
- <option name="ASSIGNMENT_WRAP" value="1" />
- <option name="PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE" value="true" />
- <option name="IF_BRACE_FORCE" value="3" />
- <option name="DOWHILE_BRACE_FORCE" value="3" />
- <option name="WHILE_BRACE_FORCE" value="3" />
- <option name="FOR_BRACE_FORCE" value="3" />
- <option name="PARENT_SETTINGS_INSTALLED" value="true" />
- <indentOptions>
- <option name="TAB_SIZE" value="8" />
- </indentOptions>
- </codeStyleSettings>
- <codeStyleSettings language="JSON">
- <option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
- <option name="PARENT_SETTINGS_INSTALLED" value="true" />
- </codeStyleSettings>
- <codeStyleSettings language="JavaScript">
- <option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
- <option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
- <option name="ALIGN_MULTILINE_FOR" value="false" />
- <option name="CALL_PARAMETERS_WRAP" value="1" />
- <option name="METHOD_PARAMETERS_WRAP" value="1" />
- <option name="BINARY_OPERATION_WRAP" value="1" />
- <option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
- <option name="TERNARY_OPERATION_WRAP" value="1" />
- <option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
- <option name="FOR_STATEMENT_WRAP" value="1" />
- <option name="ARRAY_INITIALIZER_WRAP" value="1" />
- <option name="ASSIGNMENT_WRAP" value="1" />
- <option name="PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE" value="true" />
- <option name="WRAP_COMMENTS" value="true" />
- <option name="IF_BRACE_FORCE" value="3" />
- <option name="DOWHILE_BRACE_FORCE" value="3" />
- <option name="WHILE_BRACE_FORCE" value="3" />
- <option name="FOR_BRACE_FORCE" value="3" />
- <option name="PARENT_SETTINGS_INSTALLED" value="true" />
- </codeStyleSettings>
- <codeStyleSettings language="SQL">
- <option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
- <option name="PARENT_SETTINGS_INSTALLED" value="true" />
- <indentOptions>
- <option name="CONTINUATION_INDENT_SIZE" value="8" />
- <option name="TAB_SIZE" value="4" />
- </indentOptions>
- </codeStyleSettings>
- <codeStyleSettings language="TypeScript">
- <option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
- <option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
- <option name="ALIGN_MULTILINE_FOR" value="false" />
- <option name="CALL_PARAMETERS_WRAP" value="1" />
- <option name="METHOD_PARAMETERS_WRAP" value="1" />
- <option name="EXTENDS_LIST_WRAP" value="1" />
- <option name="EXTENDS_KEYWORD_WRAP" value="1" />
- <option name="BINARY_OPERATION_WRAP" value="1" />
- <option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
- <option name="TERNARY_OPERATION_WRAP" value="1" />
- <option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
- <option name="FOR_STATEMENT_WRAP" value="1" />
- <option name="ARRAY_INITIALIZER_WRAP" value="1" />
- <option name="ASSIGNMENT_WRAP" value="1" />
- <option name="PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE" value="true" />
- <option name="WRAP_COMMENTS" value="true" />
- <option name="IF_BRACE_FORCE" value="3" />
- <option name="DOWHILE_BRACE_FORCE" value="3" />
- <option name="WHILE_BRACE_FORCE" value="3" />
- <option name="FOR_BRACE_FORCE" value="3" />
- <option name="PARENT_SETTINGS_INSTALLED" value="true" />
- </codeStyleSettings>
-</code_scheme> \ No newline at end of file
diff --git a/ResourceManifest.xml b/ResourceManifest.xml
new file mode 100644
index 00000000..a859327f
--- /dev/null
+++ b/ResourceManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tv" xmlns:tools="http://schemas.android.com/tools">
+ <uses-sdk android:targetSdkVersion="26" android:minSdkVersion="23"/>
+ <application />
+</manifest>
diff --git a/common/Android.mk b/common/Android.mk
index ff19392e..a0333fc9 100644
--- a/common/Android.mk
+++ b/common/Android.mk
@@ -13,12 +13,16 @@ LOCAL_USE_AAPT2 := true
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations
+
+LOCAL_DISABLE_RESOLVE_SUPPORT_LIBRARIES := true
+
LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-annotations \
android-support-compat \
android-support-core-ui \
android-support-v7-recyclerview \
- android-support-v17-leanback \
+ android-support-v17-leanback
include $(LOCAL_PATH)/buildconfig.mk
diff --git a/common/AndroidManifest.xml b/common/AndroidManifest.xml
index 19fb2a00..c1c698c5 100644
--- a/common/AndroidManifest.xml
+++ b/common/AndroidManifest.xml
@@ -17,6 +17,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.tv.common"
android:versionCode="1">
- <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="21"/>
+ <uses-sdk android:targetSdkVersion="26" android:minSdkVersion="21"/>
<application />
</manifest>
diff --git a/common/BuildConfig.java.in b/common/BuildConfig.java.in
index f4631340..10816db1 100644
--- a/common/BuildConfig.java.in
+++ b/common/BuildConfig.java.in
@@ -4,5 +4,6 @@ package com.android.tv.common;
public final class BuildConfig {
public static final boolean DEBUG = %DEBUG%;
public static final boolean ENG = %ENG%;
+ public static final boolean NO_JNI_TEST = false;
private BuildConfig() {}
} \ No newline at end of file
diff --git a/common/res/values/strings.xml b/common/res/values/strings.xml
index ec029136..1c8a990c 100644
--- a/common/res/values/strings.xml
+++ b/common/res/values/strings.xml
@@ -18,6 +18,7 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_text_done">Done</string>
<string name="action_text_skip">Skip</string>
+ <string name="action_text_retry">Retry</string>
<!-- The episode display format with season and episode number used in series details view.
"S" is an abbreviation for Season and "Ep." is an abbreviation for episode. ":" is used as a separator.
For example, "S1: Ep. 1"-->
@@ -34,6 +35,14 @@
"Ep." is an abbreviation for episode.
For example, "Ep. 1807 Headline News". -->
<string name="display_episode_title_format_no_season_number">Ep. <xliff:g id="episode_number" example="1807">%1$s</xliff:g> <xliff:g id="episode_title" example="Headline News">%2$s</xliff:g></string>
+
+ <!-- The content description for an episode. It will be spoken by accessibility service when needed.
+ For example, "Season 1 Episode 1 Winter is coming". -->
+ <string name="content_description_episode_format">Season <xliff:g id="season_number" example="1">%1$s</xliff:g> Episode <xliff:g id="episode_number" example="1">%2$s</xliff:g> <xliff:g id="episode_title" example="Winter is Coming">%3$s</xliff:g></string>
+ <!-- The content description for an episode. It will be spoken by accessibility service when needed.
+ For example, "Episode 1807 Headline News". -->
+ <string name="content_description_episode_format_no_season_number">Episode <xliff:g id="episode_number" example="1807">%1$s</xliff:g> <xliff:g id="episode_title" example="Headline News">%2$s</xliff:g></string>
+
<!-- Program title with season and episode number used in DVR card views.
HTML tag <i> is a placeholder for styling episode number part.
"S" is an abbreviation for Season and "Ep." is an abbreviation for episode. ":" is used as a separator.
diff --git a/common/src/com/android/tv/common/BaseApplication.java b/common/src/com/android/tv/common/BaseApplication.java
new file mode 100644
index 00000000..71c9b4d7
--- /dev/null
+++ b/common/src/com/android/tv/common/BaseApplication.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.annotation.TargetApi;
+import android.app.Application;
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.StrictMode;
+import android.support.annotation.VisibleForTesting;
+import com.android.tv.common.feature.CommonFeatures;
+import com.android.tv.common.recording.RecordingStorageStatusManager;
+import com.android.tv.common.util.Clock;
+import com.android.tv.common.util.CommonUtils;
+import com.android.tv.common.util.Debug;
+import com.android.tv.common.util.SystemProperties;
+
+/** The base application class for Live TV applications. */
+public abstract class BaseApplication extends Application implements BaseSingletons {
+ private RecordingStorageStatusManager mRecordingStorageStatusManager;
+
+ /**
+ * An instance of {@link BaseSingletons}. Note that this can be set directly only for the test
+ * purpose.
+ */
+ @VisibleForTesting public static BaseSingletons sSingletons;
+
+ /** Returns the {@link BaseSingletons} using the application context. */
+ public static BaseSingletons getSingletons(Context context) {
+ // STOP-SHIP: changing the method to protected once the Tuner application is created.
+ // No need to be "synchronized" because this doesn't create any instance.
+ if (sSingletons == null) {
+ sSingletons = (BaseSingletons) context.getApplicationContext();
+ }
+ return sSingletons;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Debug.getTimer(Debug.TAG_START_UP_TIMER).start();
+ Debug.getTimer(Debug.TAG_START_UP_TIMER)
+ .log("Start " + this.getClass().getSimpleName() + ".onCreate");
+ CommonPreferences.initialize(this);
+
+ // Only set StrictMode for ENG builds because the build server only produces userdebug
+ // builds.
+ if (BuildConfig.ENG && SystemProperties.ALLOW_STRICT_MODE.getValue()) {
+ StrictMode.ThreadPolicy.Builder threadPolicyBuilder =
+ new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog();
+ // TODO(b/69565157): Turn penaltyDeath on for VMPolicy when tests are fixed.
+ StrictMode.VmPolicy.Builder vmPolicyBuilder =
+ new StrictMode.VmPolicy.Builder().detectAll().penaltyLog();
+
+ if (!CommonUtils.isRunningInTest()) {
+ threadPolicyBuilder.penaltyDialog();
+ }
+ StrictMode.setThreadPolicy(threadPolicyBuilder.build());
+ StrictMode.setVmPolicy(vmPolicyBuilder.build());
+ }
+ if (CommonFeatures.DVR.isEnabled(this)) {
+ getRecordingStorageStatusManager();
+ }
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ // Fetch remote config
+ getRemoteConfig().fetch(null);
+ return null;
+ }
+ }.execute();
+ }
+
+ @Override
+ public Clock getClock() {
+ return Clock.SYSTEM;
+ }
+
+ /** Returns {@link RecordingStorageStatusManager}. */
+ @Override
+ @TargetApi(Build.VERSION_CODES.N)
+ public RecordingStorageStatusManager getRecordingStorageStatusManager() {
+ if (mRecordingStorageStatusManager == null) {
+ mRecordingStorageStatusManager = new RecordingStorageStatusManager(this);
+ }
+ return mRecordingStorageStatusManager;
+ }
+
+ @Override
+ public abstract Intent getTunerSetupIntent(Context context);
+}
diff --git a/common/src/com/android/tv/common/BaseSingletons.java b/common/src/com/android/tv/common/BaseSingletons.java
new file mode 100644
index 00000000..e735cdb4
--- /dev/null
+++ b/common/src/com/android/tv/common/BaseSingletons.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.content.Context;
+import android.content.Intent;
+import com.android.tv.common.config.api.RemoteConfig.HasRemoteConfig;
+import com.android.tv.common.recording.RecordingStorageStatusManager;
+import com.android.tv.common.util.Clock;
+
+/** Injection point for the base app */
+public interface BaseSingletons extends HasRemoteConfig {
+
+ Clock getClock();
+
+ RecordingStorageStatusManager getRecordingStorageStatusManager();
+
+ Intent getTunerSetupIntent(Context context);
+
+ String getEmbeddedTunerInputId();
+}
diff --git a/common/src/com/android/tv/common/BooleanSystemProperty.java b/common/src/com/android/tv/common/BooleanSystemProperty.java
index 21e575a1..5436524f 100644
--- a/common/src/com/android/tv/common/BooleanSystemProperty.java
+++ b/common/src/com/android/tv/common/BooleanSystemProperty.java
@@ -17,7 +17,6 @@
package com.android.tv.common;
import android.util.Log;
-
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@@ -25,16 +24,15 @@ import java.util.List;
/**
* Lazy loaded boolean system property.
*
- * <p>Set with <code>adb shell setprop <em>key</em> <em>value</em></code> where:
- * Values 'n', 'no', '0', 'false' or 'off' are considered false.
- * Values 'y', 'yes', '1', 'true' or 'on' are considered true.
- * (case sensitive). See <a href=
+ * <p>Set with <code>adb shell setprop <em>key</em> <em>value</em></code> where: Values 'n', 'no',
+ * '0', 'false' or 'off' are considered false. Values 'y', 'yes', '1', 'true' or 'on' are considered
+ * true. (case sensitive). See <a href=
* "https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/SystemProperties.java"
* >android.os.SystemProperties.getBoolean</a>.
*/
public class BooleanSystemProperty {
- private final static String TAG = "BooleanSystemProperty";
- private final static boolean DEBUG = false;
+ private static final String TAG = "BooleanSystemProperty";
+ private static final boolean DEBUG = false;
private static final List<BooleanSystemProperty> ALL_PROPERTIES = new ArrayList<>();
private final boolean mDefaultValue;
private final String mKey;
@@ -78,7 +76,7 @@ public class BooleanSystemProperty {
}
/**
- * Clears the cached value. The next call to getValue will check {@code
+ * Clears the cached value. The next call to getValue will check {@code
* android.os.SystemProperties}.
*/
public void reset() {
@@ -88,7 +86,7 @@ public class BooleanSystemProperty {
/**
* Returns the value of the system property.
*
- * <p>If the value is cached get the value from {@code android.os.SystemProperties} with the
+ * <p>If the value is cached get the value from {@code android.os.SystemProperties} with the
* default set in the constructor.
*/
public boolean getValue() {
diff --git a/common/src/com/android/tv/common/CommonConstants.java b/common/src/com/android/tv/common/CommonConstants.java
new file mode 100644
index 00000000..ac379d18
--- /dev/null
+++ b/common/src/com/android/tv/common/CommonConstants.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 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;
+
+/** Constants for common use in apps and tests. */
+public final class CommonConstants {
+
+ public static final String BASE_PACKAGE =
+ "com.android.tv";
+ /** A constant for the key of the extra data for the app linking intent. */
+ public static final String EXTRA_APP_LINK_CHANNEL_URI = "app_link_channel_uri";
+
+ private CommonConstants() {}
+}
diff --git a/src/com/android/tv/tuner/TunerPreferenceProvider.java b/common/src/com/android/tv/common/CommonPreferenceProvider.java
index 3a3561b6..b0be3038 100644
--- a/src/com/android/tv/tuner/TunerPreferenceProvider.java
+++ b/common/src/com/android/tv/common/CommonPreferenceProvider.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.tv.tuner;
+package com.android.tv.common;
import android.content.ContentProvider;
import android.content.ContentValues;
@@ -27,11 +27,11 @@ import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
/**
- * A content provider for storing the preferences. It's used across TV app and USB tuner TV input.
+ * A content provider for storing common preferences. It's used across TV app and tuner TV inputs.
*/
-public class TunerPreferenceProvider extends ContentProvider {
+public class CommonPreferenceProvider extends ContentProvider {
/** The authority of the provider */
- public static final String AUTHORITY = "com.android.tv.tuner.preferences";
+ public static final String AUTHORITY = CommonConstants.BASE_PACKAGE + ".common.preferences";
private static final String PATH_PREFERENCES = "preferences";
@@ -54,31 +54,23 @@ public class TunerPreferenceProvider extends ContentProvider {
/**
* Builds a Uri that points to a specific preference.
-
+ *
* @param key a key of the preference to point to
*/
public static Uri buildPreferenceUri(String key) {
return Preferences.CONTENT_URI.buildUpon().appendPath(key).build();
}
- /**
- * Columns definitions for the preferences table.
- */
+ /** Columns definitions for the preferences table. */
public interface Preferences {
- /**
- * The content:// style for the preferences table.
- */
+ /** The content:// style for the preferences table. */
Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + PATH_PREFERENCES);
- /**
- * The MIME type of a directory of preferences.
- */
+ /** The MIME type of a directory of preferences. */
String CONTENT_TYPE = "vnd.android.cursor.dir/preferences";
- /**
- * The MIME type of a single preference.
- */
+ /** The MIME type of a single preference. */
String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/preferences";
/**
@@ -114,10 +106,16 @@ public class TunerPreferenceProvider extends ContentProvider {
@Override
public void onCreate(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE " + PREFERENCES_TABLE + " ("
- + Preferences._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
- + Preferences.COLUMN_KEY + " TEXT NOT NULL,"
- + Preferences.COLUMN_VALUE + " TEXT);");
+ db.execSQL(
+ "CREATE TABLE "
+ + PREFERENCES_TABLE
+ + " ("
+ + Preferences._ID
+ + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + Preferences.COLUMN_KEY
+ + " TEXT NOT NULL,"
+ + Preferences.COLUMN_VALUE
+ + " TEXT);");
}
@Override
@@ -133,15 +131,26 @@ public class TunerPreferenceProvider extends ContentProvider {
}
@Override
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ public Cursor query(
+ Uri uri,
+ String[] projection,
+ String selection,
+ String[] selectionArgs,
String sortOrder) {
int match = sUriMatcher.match(uri);
if (match != MATCH_PREFERENCE && match != MATCH_PREFERENCE_KEY) {
throw new UnsupportedOperationException();
}
SQLiteDatabase db = mDatabaseOpenHelper.getReadableDatabase();
- Cursor cursor = db.query(PREFERENCES_TABLE, projection, selection, selectionArgs,
- null, null, sortOrder);
+ Cursor cursor =
+ db.query(
+ PREFERENCES_TABLE,
+ projection,
+ selection,
+ selectionArgs,
+ null,
+ null,
+ sortOrder);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
@@ -153,14 +162,15 @@ public class TunerPreferenceProvider extends ContentProvider {
return Preferences.CONTENT_TYPE;
case MATCH_PREFERENCE_KEY:
return Preferences.CONTENT_ITEM_TYPE;
+ default:
+ throw new IllegalArgumentException("Unknown URI " + uri);
}
- throw new IllegalArgumentException("Unknown URI " + uri);
}
/**
* Inserts a preference row into the preference table.
*
- * If a key is already exists in the table, it removes the old row and inserts a new row.
+ * <p>If a key is already exists in the table, it removes the old row and inserts a new row.
*
* @param uri the URL of the table to insert into
* @param values the initial values for the newly inserted row
@@ -178,8 +188,10 @@ public class TunerPreferenceProvider extends ContentProvider {
SQLiteDatabase db = mDatabaseOpenHelper.getWritableDatabase();
// Remove the old row.
- db.delete(PREFERENCES_TABLE, Preferences.COLUMN_KEY + " like ?",
- new String[]{values.getAsString(Preferences.COLUMN_KEY)});
+ db.delete(
+ PREFERENCES_TABLE,
+ Preferences.COLUMN_KEY + " like ?",
+ new String[] {values.getAsString(Preferences.COLUMN_KEY)});
long rowId = db.insert(PREFERENCES_TABLE, null, values);
if (rowId > 0) {
diff --git a/common/src/com/android/tv/common/CommonPreferences.java b/common/src/com/android/tv/common/CommonPreferences.java
new file mode 100644
index 00000000..5a94eecb
--- /dev/null
+++ b/common/src/com/android/tv/common/CommonPreferences.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.annotation.GuardedBy;
+import android.support.annotation.IntDef;
+import android.support.annotation.MainThread;
+import android.util.Log;
+import com.android.tv.common.CommonPreferenceProvider.Preferences;
+import com.android.tv.common.util.CommonUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+
+/** A helper class for setting/getting common preferences across applications. */
+public class CommonPreferences {
+ private static final String TAG = "CommonPreferences";
+
+ private static final String PREFS_KEY_LAUNCH_SETUP = "launch_setup";
+ private static final String PREFS_KEY_STORE_TS_STREAM = "store_ts_stream";
+ private static final String PREFS_KEY_TRICKPLAY_SETTING = "trickplay_setting";
+ private static final String PREFS_KEY_LAST_POSTAL_CODE = "last_postal_code";
+
+ private static final Map<String, Class> sPref2TypeMapping = new HashMap<>();
+
+ static {
+ sPref2TypeMapping.put(PREFS_KEY_TRICKPLAY_SETTING, int.class);
+ sPref2TypeMapping.put(PREFS_KEY_STORE_TS_STREAM, boolean.class);
+ sPref2TypeMapping.put(PREFS_KEY_LAUNCH_SETUP, boolean.class);
+ sPref2TypeMapping.put(PREFS_KEY_LAST_POSTAL_CODE, String.class);
+ }
+
+ private static final String SHARED_PREFS_NAME =
+ CommonConstants.BASE_PACKAGE + ".common.preferences";
+
+ @IntDef({TRICKPLAY_SETTING_NOT_SET, TRICKPLAY_SETTING_DISABLED, TRICKPLAY_SETTING_ENABLED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TrickplaySetting {}
+
+ /** Trickplay setting is not changed by a user. Trickplay will be enabled in this case. */
+ public static final int TRICKPLAY_SETTING_NOT_SET = -1;
+
+ /** Trickplay setting is disabled. */
+ public static final int TRICKPLAY_SETTING_DISABLED = 0;
+
+ /** Trickplay setting is enabled. */
+ public static final int TRICKPLAY_SETTING_ENABLED = 1;
+
+ @GuardedBy("CommonPreferences.class")
+ private static final Bundle sPreferenceValues = new Bundle();
+
+ private static LoadPreferencesTask sLoadPreferencesTask;
+ private static ContentObserver sContentObserver;
+ private static CommonPreferencesChangedListener sPreferencesChangedListener = null;
+
+ protected static boolean sInitialized;
+
+ /** Listeners for CommonPreferences change. */
+ public interface CommonPreferencesChangedListener {
+ void onCommonPreferencesChanged();
+ }
+
+ /** Initializes the common preferences. */
+ @MainThread
+ public static void initialize(final Context context) {
+ if (sInitialized) {
+ return;
+ }
+ sInitialized = true;
+ if (useContentProvider(context)) {
+ loadPreferences(context);
+ sContentObserver =
+ new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ loadPreferences(context);
+ }
+ };
+ context.getContentResolver()
+ .registerContentObserver(Preferences.CONTENT_URI, true, sContentObserver);
+ } else {
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ getSharedPreferences(context);
+ return null;
+ }
+ }.execute();
+ }
+ }
+
+ /** Releases the resources. */
+ public static synchronized void release(Context context) {
+ if (useContentProvider(context) && sContentObserver != null) {
+ context.getContentResolver().unregisterContentObserver(sContentObserver);
+ }
+ setCommonPreferencesChangedListener(null);
+ }
+
+ /** Sets the listener for CommonPreferences change. */
+ public static void setCommonPreferencesChangedListener(
+ CommonPreferencesChangedListener listener) {
+ sPreferencesChangedListener = listener;
+ }
+
+ /**
+ * Loads the preferences from database.
+ *
+ * <p>This preferences is used across processes, so the preferences should be loaded again when
+ * the databases changes.
+ */
+ @MainThread
+ public static void loadPreferences(Context context) {
+ if (sLoadPreferencesTask != null
+ && sLoadPreferencesTask.getStatus() != AsyncTask.Status.FINISHED) {
+ sLoadPreferencesTask.cancel(true);
+ }
+ sLoadPreferencesTask = new LoadPreferencesTask(context);
+ sLoadPreferencesTask.execute();
+ }
+
+ private static boolean useContentProvider(Context context) {
+ // If TIS is a part of LC, it should use ContentProvider to resolve multiple process access.
+ return CommonUtils.isPackagedWithLiveChannels(context);
+ }
+
+ public static synchronized boolean shouldShowSetupActivity(Context context) {
+ SoftPreconditions.checkState(sInitialized);
+ if (useContentProvider(context)) {
+ return sPreferenceValues.getBoolean(PREFS_KEY_LAUNCH_SETUP);
+ } else {
+ return getSharedPreferences(context).getBoolean(PREFS_KEY_LAUNCH_SETUP, false);
+ }
+ }
+
+ public static synchronized void setShouldShowSetupActivity(Context context, boolean need) {
+ if (useContentProvider(context)) {
+ setPreference(context, PREFS_KEY_LAUNCH_SETUP, need);
+ } else {
+ getSharedPreferences(context).edit().putBoolean(PREFS_KEY_LAUNCH_SETUP, need).apply();
+ }
+ }
+
+ public static synchronized @TrickplaySetting int getTrickplaySetting(Context context) {
+ SoftPreconditions.checkState(sInitialized);
+ if (useContentProvider(context)) {
+ return sPreferenceValues.getInt(PREFS_KEY_TRICKPLAY_SETTING, TRICKPLAY_SETTING_NOT_SET);
+ } else {
+ return getSharedPreferences(context)
+ .getInt(PREFS_KEY_TRICKPLAY_SETTING, TRICKPLAY_SETTING_NOT_SET);
+ }
+ }
+
+ public static synchronized void setTrickplaySetting(
+ Context context, @TrickplaySetting int trickplaySetting) {
+ SoftPreconditions.checkState(sInitialized);
+ SoftPreconditions.checkArgument(trickplaySetting != TRICKPLAY_SETTING_NOT_SET);
+ if (useContentProvider(context)) {
+ setPreference(context, PREFS_KEY_TRICKPLAY_SETTING, trickplaySetting);
+ } else {
+ getSharedPreferences(context)
+ .edit()
+ .putInt(PREFS_KEY_TRICKPLAY_SETTING, trickplaySetting)
+ .apply();
+ }
+ }
+
+ public static synchronized boolean getStoreTsStream(Context context) {
+ SoftPreconditions.checkState(sInitialized);
+ if (useContentProvider(context)) {
+ return sPreferenceValues.getBoolean(PREFS_KEY_STORE_TS_STREAM, false);
+ } else {
+ return getSharedPreferences(context).getBoolean(PREFS_KEY_STORE_TS_STREAM, false);
+ }
+ }
+
+ public static synchronized void setStoreTsStream(Context context, boolean shouldStore) {
+ if (useContentProvider(context)) {
+ setPreference(context, PREFS_KEY_STORE_TS_STREAM, shouldStore);
+ } else {
+ getSharedPreferences(context)
+ .edit()
+ .putBoolean(PREFS_KEY_STORE_TS_STREAM, shouldStore)
+ .apply();
+ }
+ }
+
+ public static synchronized String getLastPostalCode(Context context) {
+ SoftPreconditions.checkState(sInitialized);
+ if (useContentProvider(context)) {
+ return sPreferenceValues.getString(PREFS_KEY_LAST_POSTAL_CODE);
+ } else {
+ return getSharedPreferences(context).getString(PREFS_KEY_LAST_POSTAL_CODE, null);
+ }
+ }
+
+ public static synchronized void setLastPostalCode(Context context, String postalCode) {
+ if (useContentProvider(context)) {
+ setPreference(context, PREFS_KEY_LAST_POSTAL_CODE, postalCode);
+ } else {
+ getSharedPreferences(context)
+ .edit()
+ .putString(PREFS_KEY_LAST_POSTAL_CODE, postalCode)
+ .apply();
+ }
+ }
+
+ protected static SharedPreferences getSharedPreferences(Context context) {
+ return context.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE);
+ }
+
+ private static synchronized void setPreference(Context context, String key, String value) {
+ sPreferenceValues.putString(key, value);
+ savePreference(context, key, value);
+ }
+
+ private static synchronized void setPreference(Context context, String key, int value) {
+ sPreferenceValues.putInt(key, value);
+ savePreference(context, key, Integer.toString(value));
+ }
+
+ private static synchronized void setPreference(Context context, String key, long value) {
+ sPreferenceValues.putLong(key, value);
+ savePreference(context, key, Long.toString(value));
+ }
+
+ private static synchronized void setPreference(Context context, String key, boolean value) {
+ sPreferenceValues.putBoolean(key, value);
+ savePreference(context, key, Boolean.toString(value));
+ }
+
+ private static void savePreference(
+ 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", e, "Error writing preference values");
+ }
+ return null;
+ }
+ }.execute();
+ }
+
+ private static class LoadPreferencesTask extends AsyncTask<Void, Void, Bundle> {
+ private final Context mContext;
+
+ private LoadPreferencesTask(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ protected Bundle doInBackground(Void... params) {
+ Bundle bundle = new Bundle();
+ ContentResolver resolver = mContext.getContentResolver();
+ String[] projection = new String[] {Preferences.COLUMN_KEY, Preferences.COLUMN_VALUE};
+ try (Cursor cursor =
+ resolver.query(Preferences.CONTENT_URI, projection, null, null, null)) {
+ if (cursor != null) {
+ while (!isCancelled() && cursor.moveToNext()) {
+ String key = cursor.getString(0);
+ String value = cursor.getString(1);
+ Class prefClass = sPref2TypeMapping.get(key);
+ if (prefClass == int.class) {
+ try {
+ bundle.putInt(key, Integer.parseInt(value));
+ } catch (NumberFormatException e) {
+ Log.w(TAG, "Invalid format, key=" + key + ", value=" + value);
+ }
+ } else if (prefClass == long.class) {
+ try {
+ bundle.putLong(key, Long.parseLong(value));
+ } catch (NumberFormatException e) {
+ Log.w(TAG, "Invalid format, key=" + key + ", value=" + value);
+ }
+ } else if (prefClass == boolean.class) {
+ bundle.putBoolean(key, Boolean.parseBoolean(value));
+ } else {
+ bundle.putString(key, value);
+ }
+ }
+ }
+ } catch (Exception e) {
+ SoftPreconditions.warn(TAG, "getPreference", e, "Error querying preference values");
+ return null;
+ }
+ return bundle;
+ }
+
+ @Override
+ protected void onPostExecute(Bundle bundle) {
+ synchronized (CommonPreferences.class) {
+ if (bundle != null) {
+ sPreferenceValues.putAll(bundle);
+ }
+ }
+ if (sPreferencesChangedListener != null) {
+ sPreferencesChangedListener.onCommonPreferencesChanged();
+ }
+ }
+ }
+}
diff --git a/common/src/com/android/tv/common/SoftPreconditions.java b/common/src/com/android/tv/common/SoftPreconditions.java
index 823c42ff..3b0510d8 100644
--- a/common/src/com/android/tv/common/SoftPreconditions.java
+++ b/common/src/com/android/tv/common/SoftPreconditions.java
@@ -17,17 +17,18 @@
package com.android.tv.common;
import android.content.Context;
+import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
-
import com.android.tv.common.feature.Feature;
+import com.android.tv.common.util.CommonUtils;
/**
- * Simple static methods to be called at the start of your own methods to verify
- * correct arguments and state.
+ * Simple static methods to be called at the start of your own methods to verify correct arguments
+ * and state.
*
- * <p>{@code checkXXX} methods throw exceptions when {@link BuildConfig#ENG} is true, and
- * logs a warning when it is false.
+ * <p>{@code checkXXX} methods throw exceptions when {@link BuildConfig#ENG} is true, and logs a
+ * warning when it is false.
*
* <p>This is based on com.android.internal.util.Preconditions.
*/
@@ -35,26 +36,35 @@ public final class SoftPreconditions {
private static final String TAG = "SoftPreconditions";
/**
- * Throws or logs if an expression involving the parameter of the calling
- * method is not true.
+ * Throws or logs if an expression involving the parameter of the calling method is not true.
*
* @param expression a boolean expression
- * @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 tag Used to identify the source of a log message. It usually identifies the class or
+ * activity where the log call occurs.
+ * @param errorMessageTemplate a template for the exception message should the check fail. The
+ * message is formed by replacing each {@code %s} placeholder in the template with an
+ * argument. These are matched by position - the first {@code %s} gets {@code
+ * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message
+ * in square braces. Unmatched placeholders will be left as-is.
+ * @param errorMessageArgs the arguments to be substituted into the message template. Arguments
+ * are converted to strings using {@link String#valueOf(Object)}.
* @return the evaluation result of the boolean expression
* @throws IllegalArgumentException if {@code expression} is true
*/
- public static boolean checkArgument(final boolean expression, String tag, String msg) {
+ public static boolean checkArgument(
+ final boolean expression,
+ String tag,
+ @Nullable String errorMessageTemplate,
+ @Nullable Object... errorMessageArgs) {
if (!expression) {
- warn(tag, "Illegal argument", msg, new IllegalArgumentException(msg));
+ String msg = format(errorMessageTemplate, errorMessageArgs);
+ warn(tag, "Illegal argument", new IllegalArgumentException(msg), msg);
}
return expression;
}
/**
- * Throws or logs if an expression involving the parameter of the calling
- * method is not true.
+ * Throws or logs if an expression involving the parameter of the calling method is not true.
*
* @param expression a boolean expression
* @return the evaluation result of the boolean expression
@@ -69,15 +79,26 @@ public final class SoftPreconditions {
* Throws or logs if an and object is null.
*
* @param reference an object reference
- * @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 tag Used to identify the source of a log message. It usually identifies the class or
+ * activity where the log call occurs.
+ * @param errorMessageTemplate a template for the exception message should the check fail. The
+ * message is formed by replacing each {@code %s} placeholder in the template with an
+ * argument. These are matched by position - the first {@code %s} gets {@code
+ * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message
+ * in square braces. Unmatched placeholders will be left as-is.
+ * @param errorMessageArgs the arguments to be substituted into the message template. Arguments
+ * are converted to strings using {@link String#valueOf(Object)}.
* @return true if the object is null
* @throws NullPointerException if {@code reference} is null
*/
- public static <T> T checkNotNull(final T reference, String tag, String msg) {
+ public static <T> T checkNotNull(
+ final T reference,
+ String tag,
+ @Nullable String errorMessageTemplate,
+ @Nullable Object... errorMessageArgs) {
if (reference == null) {
- warn(tag, "Null Pointer", msg, new NullPointerException(msg));
+ String msg = format(errorMessageTemplate, errorMessageArgs);
+ warn(tag, "Null Pointer", new NullPointerException(msg), msg);
}
return reference;
}
@@ -94,26 +115,37 @@ public final class SoftPreconditions {
}
/**
- * Throws or logs if an expression involving the state of the calling
- * instance, but not involving any parameters to the calling method is not true.
+ * Throws or logs if an expression involving the state of the calling instance, but not
+ * involving any parameters to the calling method is not true.
*
* @param expression a boolean expression
- * @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 tag Used to identify the source of a log message. It usually identifies the class or
+ * activity where the log call occurs.
+ * @param errorMessageTemplate a template for the exception message should the check fail. The
+ * message is formed by replacing each {@code %s} placeholder in the template with an
+ * argument. These are matched by position - the first {@code %s} gets {@code
+ * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message
+ * in square braces. Unmatched placeholders will be left as-is.
+ * @param errorMessageArgs the arguments to be substituted into the message template. Arguments
+ * are converted to strings using {@link String#valueOf(Object)}.
* @return the evaluation result of the boolean expression
* @throws IllegalStateException if {@code expression} is true
*/
- public static boolean checkState(final boolean expression, String tag, String msg) {
+ public static boolean checkState(
+ final boolean expression,
+ String tag,
+ @Nullable String errorMessageTemplate,
+ @Nullable Object... errorMessageArgs) {
if (!expression) {
- warn(tag, "Illegal State", msg, new IllegalStateException(msg));
+ String msg = format(errorMessageTemplate, errorMessageArgs);
+ warn(tag, "Illegal State", new IllegalStateException(msg), msg);
}
return expression;
}
/**
- * Throws or logs if an expression involving the state of the calling
- * instance, but not involving any parameters to the calling method is not true.
+ * Throws or logs if an expression involving the state of the calling instance, but not
+ * involving any parameters to the calling method is not true.
*
* @param expression a boolean expression
* @return the evaluation result of the boolean expression
@@ -129,8 +161,8 @@ public final class SoftPreconditions {
*
* @param context an android context
* @param feature the required feature
- * @param tag used to identify the source of a log message. It usually
- * identifies the class or activity where the log call occurs
+ * @param tag used to identify the source of a log message. It usually identifies the class or
+ * activity where the log call occurs
* @throws IllegalStateException if {@code feature} is not enabled
*/
public static void checkFeatureEnabled(Context context, Feature feature, String tag) {
@@ -138,14 +170,15 @@ public final class SoftPreconditions {
}
/**
- * Throws a {@link RuntimeException} if {@link BuildConfig#ENG} is true, else log a warning.
+ * Throws a {@link RuntimeException} if {@link BuildConfig#ENG} is true and not running in a
+ * test, else log a warning.
*
- * @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 tag Used to identify the source of a log message. It usually identifies the class or
+ * activity where the log call occurs.
* @param e The exception to wrap with a RuntimeException when thrown.
+ * @param msg The message to be logged
*/
- public static void warn(String tag, String prefix, String msg, Exception e)
+ public static void warn(String tag, String prefix, Exception e, String msg)
throws RuntimeException {
if (TextUtils.isEmpty(tag)) {
tag = TAG;
@@ -159,13 +192,57 @@ public final class SoftPreconditions {
logMessage = prefix + ": " + msg;
}
- if (BuildConfig.ENG) {
+ if (BuildConfig.ENG && !CommonUtils.isRunningInTest()) {
throw new RuntimeException(msg, e);
} else {
Log.w(tag, logMessage, e);
}
}
- private SoftPreconditions() {
+ /**
+ * Substitutes each {@code %s} in {@code template} with an argument. These are matched by
+ * position: the first {@code %s} gets {@code args[0]}, etc. If there are more arguments than
+ * placeholders, the unmatched arguments will be appended to the end of the formatted message in
+ * square braces.
+ *
+ * @param template a string containing 0 or more {@code %s} placeholders. null is treated as
+ * "null".
+ * @param args the arguments to be substituted into the message template. Arguments are
+ * converted to strings using {@link String#valueOf(Object)}. Arguments can be null.
+ */
+ static String format(@Nullable String template, @Nullable Object... args) {
+ template = String.valueOf(template); // null -> "null"
+
+ args = args == null ? new Object[] {"(Object[])null"} : args;
+
+ // start substituting the arguments into the '%s' placeholders
+ StringBuilder builder = new StringBuilder(template.length() + 16 * args.length);
+ int templateStart = 0;
+ int i = 0;
+ while (i < args.length) {
+ int placeholderStart = template.indexOf("%s", templateStart);
+ if (placeholderStart == -1) {
+ break;
+ }
+ builder.append(template, templateStart, placeholderStart);
+ builder.append(args[i++]);
+ templateStart = placeholderStart + 2;
+ }
+ builder.append(template, templateStart, template.length());
+
+ // if we run out of placeholders, append the extra args in square braces
+ if (i < args.length) {
+ builder.append(" [");
+ builder.append(args[i++]);
+ while (i < args.length) {
+ builder.append(", ");
+ builder.append(args[i++]);
+ }
+ builder.append(']');
+ }
+
+ return builder.toString();
}
+
+ private SoftPreconditions() {}
}
diff --git a/common/src/com/android/tv/common/TvCommonConstants.java b/common/src/com/android/tv/common/TvCommonConstants.java
deleted file mode 100644
index 9824497e..00000000
--- a/common/src/com/android/tv/common/TvCommonConstants.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 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;
-
-import android.media.tv.TvInputInfo;
-import android.os.Build;
-
-/**
- * Constants for common use in TV app and tests.
- */
-public final class TvCommonConstants {
- /**
- * A constant for the key of the extra data for the app linking intent.
- */
- public static final String EXTRA_APP_LINK_CHANNEL_URI = "app_link_channel_uri";
-
- /**
- * An intent action to launch setup activity of a TV input. The intent should include
- * TV input ID in the value of {@link EXTRA_INPUT_ID}. Optionally, given the value of
- * {@link EXTRA_ACTIVITY_AFTER_COMPLETION}, the activity will be launched after the setup
- * activity successfully finishes.
- */
- public static final String INTENT_ACTION_INPUT_SETUP =
- "com.android.tv.action.LAUNCH_INPUT_SETUP";
-
- /**
- * A constant of the key to indicate a TV input ID for the intent action
- * {@link INTENT_ACTION_INPUT_SETUP}.
- *
- * <p>Value type: String
- */
- public static final String EXTRA_INPUT_ID = TvInputInfo.EXTRA_INPUT_ID;
-
- /**
- * A constant of the key for intent to launch actual TV input setup activity used with
- * {@link INTENT_ACTION_INPUT_SETUP}.
- *
- * <p>Value type: Intent (Parcelable)
- */
- public static final String EXTRA_SETUP_INTENT =
- "com.android.tv.extra.SETUP_INTENT";
-
- /**
- * A constant of the key to indicate an Activity launch intent for the intent action
- * {@link INTENT_ACTION_INPUT_SETUP}.
- *
- * <p>Value type: Intent (Parcelable)
- */
- public static final String EXTRA_ACTIVITY_AFTER_COMPLETION =
- "com.android.tv.intent.extra.ACTIVITY_AFTER_COMPLETION";
-
- private TvCommonConstants() {
- }
-}
diff --git a/common/src/com/android/tv/common/TvCommonUtils.java b/common/src/com/android/tv/common/TvCommonUtils.java
deleted file mode 100644
index c391ad24..00000000
--- a/common/src/com/android/tv/common/TvCommonUtils.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 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;
-
-import android.content.Intent;
-import android.media.tv.TvInputInfo;
-
-/**
- * Util class for common use in TV app and inputs.
- */
-public final class TvCommonUtils {
- private static Boolean sRunningInTest;
-
- private TvCommonUtils() { }
-
- /**
- * Returns an intent to start the setup activity for the TV input using {@link
- * TvCommonConstants#INTENT_ACTION_INPUT_SETUP}.
- */
- public static Intent createSetupIntent(Intent originalSetupIntent, String inputId) {
- if (originalSetupIntent == null) {
- return null;
- }
- Intent setupIntent = new Intent(originalSetupIntent);
- if (!TvCommonConstants.INTENT_ACTION_INPUT_SETUP.equals(originalSetupIntent.getAction())) {
- Intent intentContainer = new Intent(TvCommonConstants.INTENT_ACTION_INPUT_SETUP);
- intentContainer.putExtra(TvCommonConstants.EXTRA_SETUP_INTENT, originalSetupIntent);
- intentContainer.putExtra(TvCommonConstants.EXTRA_INPUT_ID, inputId);
- setupIntent = intentContainer;
- }
- return setupIntent;
- }
-
- /**
- * Returns an intent to start the setup activity for this TV input using {@link
- * TvCommonConstants#INTENT_ACTION_INPUT_SETUP}.
- */
- public static Intent createSetupIntent(TvInputInfo input) {
- return createSetupIntent(input.createSetupIntent(), input.getId());
- }
-
- /**
- * Checks if this application is running in tests.
- *
- * <p>{@link android.app.ActivityManager#isRunningInTestHarness} doesn't return {@code true} for
- * the usual devices even the application is running in tests. We need to figure it out by
- * checking whether the class in tv-tests-common module can be loaded or not.
- */
- public static synchronized boolean isRunningInTest() {
- if (sRunningInTest == null) {
- try {
- Class.forName("com.android.tv.testing.Utils");
- sRunningInTest = true;
- } catch (ClassNotFoundException e) {
- sRunningInTest = false;
- }
- }
- return sRunningInTest;
- }
-}
diff --git a/common/src/com/android/tv/common/TvContentRatingCache.java b/common/src/com/android/tv/common/TvContentRatingCache.java
index 8b3c06f1..cfdb8e4d 100644
--- a/common/src/com/android/tv/common/TvContentRatingCache.java
+++ b/common/src/com/android/tv/common/TvContentRatingCache.java
@@ -22,7 +22,7 @@ import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
-
+import com.android.tv.common.memory.MemoryManageable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -31,13 +31,11 @@ import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
-/**
- * TvContentRating cache.
- */
+/** TvContentRating cache. */
public final class TvContentRatingCache implements MemoryManageable {
- private final static String TAG = "TvContentRatings";
+ private static final String TAG = "TvContentRatings";
- private final static TvContentRatingCache INSTANCE = new TvContentRatingCache();
+ private static final TvContentRatingCache INSTANCE = new TvContentRatingCache();
public static TvContentRatingCache getInstance() {
return INSTANCE;
@@ -48,8 +46,8 @@ public final class TvContentRatingCache implements MemoryManageable {
/**
* Returns an array TvContentRatings from a string of comma separated set of rating strings
- * creating each from {@link TvContentRating#unflattenFromString(String)} if needed.
- * Returns {@code null} if the string is empty or contains no valid ratings.
+ * creating each from {@link TvContentRating#unflattenFromString(String)} if needed. Returns
+ * {@code null} if the string is empty or contains no valid ratings.
*/
@Nullable
public synchronized TvContentRating[] getRatings(String commaSeparatedRatings) {
@@ -60,8 +58,8 @@ public final class TvContentRatingCache implements MemoryManageable {
if (mRatingsMultiMap.containsKey(commaSeparatedRatings)) {
tvContentRatings = mRatingsMultiMap.get(commaSeparatedRatings);
} else {
- String normalizedRatings = TextUtils
- .join(",", getSortedSetFromCsv(commaSeparatedRatings));
+ String normalizedRatings =
+ TextUtils.join(",", getSortedSetFromCsv(commaSeparatedRatings));
if (mRatingsMultiMap.containsKey(normalizedRatings)) {
tvContentRatings = mRatingsMultiMap.get(normalizedRatings);
} else {
@@ -76,9 +74,7 @@ public final class TvContentRatingCache implements MemoryManageable {
return tvContentRatings;
}
- /**
- * Returns a sorted array of TvContentRatings from a comma separated string of ratings.
- */
+ /** Returns a sorted array of TvContentRatings from a comma separated string of ratings. */
@VisibleForTesting
static TvContentRating[] stringToContentRatings(String commaSeparatedRatings) {
if (TextUtils.isEmpty(commaSeparatedRatings)) {
@@ -93,8 +89,9 @@ public final class TvContentRatingCache implements MemoryManageable {
Log.e(TAG, "Can't parse the content rating: '" + rating + "'", e);
}
}
- return contentRatings.size() == 0 ?
- null : contentRatings.toArray(new TvContentRating[contentRatings.size()]);
+ return contentRatings.size() == 0
+ ? null
+ : contentRatings.toArray(new TvContentRating[contentRatings.size()]);
}
private static Set<String> getSortedSetFromCsv(String commaSeparatedRatings) {
@@ -118,8 +115,8 @@ public final class TvContentRatingCache implements MemoryManageable {
}
/**
- * Returns a string of each flattened content rating, sorted and concatenated together
- * with a comma.
+ * Returns a string of each flattened content rating, sorted and concatenated together with a
+ * comma.
*/
public static String contentRatingsToString(TvContentRating[] contentRatings) {
if (contentRatings == null || contentRatings.length == 0) {
@@ -141,6 +138,5 @@ public final class TvContentRatingCache implements MemoryManageable {
mRatingsMultiMap.clear();
}
- private TvContentRatingCache() {
- }
+ private TvContentRatingCache() {}
}
diff --git a/common/src/com/android/tv/common/WeakHandler.java b/common/src/com/android/tv/common/WeakHandler.java
index 188f20ec..dbdd4769 100644
--- a/common/src/com/android/tv/common/WeakHandler.java
+++ b/common/src/com/android/tv/common/WeakHandler.java
@@ -20,7 +20,6 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.NonNull;
-
import java.lang.ref.WeakReference;
/**
@@ -37,7 +36,7 @@ public abstract class WeakHandler<T> extends Handler {
private final WeakReference<T> mRef;
/**
- * Constructs a new handler with a weak reference to the given referent using the provided
+ * Constructs a new handler with a weak reference to the given referent using the provided
* Looper instead of the default one.
*
* @param looper The looper, must not be null.
@@ -57,9 +56,7 @@ public abstract class WeakHandler<T> extends Handler {
mRef = new WeakReference<>(ref);
}
- /**
- * Calls {@link #handleMessage(Message, Object)} if the WeakReference is not cleared.
- */
+ /** Calls {@link #handleMessage(Message, Object)} if the WeakReference is not cleared. */
@Override
public final void handleMessage(Message msg) {
T referent = mRef.get();
@@ -74,7 +71,7 @@ public abstract class WeakHandler<T> extends Handler {
*
* <p>If the WeakReference is cleared this method will no longer be called.
*
- * @param msg the message to handle
+ * @param msg the message to handle
* @param referent the referent. Guaranteed to be non null.
*/
protected abstract void handleMessage(Message msg, @NonNull T referent);
diff --git a/common/src/com/android/tv/common/actions/InputSetupActionUtils.java b/common/src/com/android/tv/common/actions/InputSetupActionUtils.java
new file mode 100644
index 00000000..7ba799ed
--- /dev/null
+++ b/common/src/com/android/tv/common/actions/InputSetupActionUtils.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2018 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.actions;
+
+import android.content.Intent;
+import android.media.tv.TvInputInfo;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+
+/** Constants and static utilities for the Input Setup Action. */
+public class InputSetupActionUtils {
+
+ /**
+ * An intent action to launch setup activity of a TV input.
+ *
+ * <p>The intent should include TV input ID in the value of {@link #EXTRA_INPUT_ID}. Optionally,
+ * given the value of {@link #EXTRA_ACTIVITY_AFTER_COMPLETION}, the activity will be launched
+ * after the setup activity successfully finishes.
+ */
+ public static final String INTENT_ACTION_INPUT_SETUP =
+ "com.android.tv.action.LAUNCH_INPUT_SETUP";
+ /**
+ * A constant of the key to indicate a TV input ID for the intent action {@link
+ * #INTENT_ACTION_INPUT_SETUP}.
+ *
+ * <p>Value type: String
+ */
+ public static final String EXTRA_INPUT_ID = TvInputInfo.EXTRA_INPUT_ID;
+ /**
+ * A constant of the key for intent to launch actual TV input setup activity used with {@link
+ * #INTENT_ACTION_INPUT_SETUP}.
+ *
+ * <p>Value type: Intent (Parcelable)
+ */
+ public static final String EXTRA_SETUP_INTENT = "com.android.tv.extra.SETUP_INTENT";
+ /**
+ * A constant of the key to indicate an Activity launch intent for the intent action {@link
+ * #INTENT_ACTION_INPUT_SETUP}.
+ *
+ * <p>Value type: Intent (Parcelable)
+ */
+ public static final String EXTRA_ACTIVITY_AFTER_COMPLETION =
+ "com.android.tv.intent.extra.ACTIVITY_AFTER_COMPLETION";
+ /**
+ * An intent action to launch setup activity of a TV input.
+ *
+ * <p>The intent should include TV input ID in the value of {@link #EXTRA_INPUT_ID}. Optionally,
+ * given the value of {@link #EXTRA_GOOGLE_ACTIVITY_AFTER_COMPLETION}, the activity will be
+ * launched after the setup activity successfully finishes.
+ *
+ * <p>Value type: Intent (Parcelable)
+ *
+ * @deprecated Use {@link #INTENT_ACTION_INPUT_SETUP} instead
+ */
+ @Deprecated
+ private static final String INTENT_GOOGLE_ACTION_INPUT_SETUP =
+ "com.google.android.tv.action.LAUNCH_INPUT_SETUP";
+ /**
+ * A Google specific constant of the key for intent to launch actual TV input setup activity
+ * used with {@link #INTENT_ACTION_INPUT_SETUP}.
+ *
+ * <p>Value type: Intent (Parcelable)
+ *
+ * @deprecated Use {@link #EXTRA_SETUP_INTENT} instead
+ */
+ @Deprecated
+ private static final String EXTRA_GOOGLE_SETUP_INTENT =
+ "com.google.android.tv.extra.SETUP_INTENT";
+ /**
+ * A Google specific constant of the key to indicate an Activity launch intent for the intent
+ * action {@link #INTENT_ACTION_INPUT_SETUP}.
+ *
+ * <p>Value type: Intent (Parcelable)
+ *
+ * @deprecated Use {@link #EXTRA_ACTIVITY_AFTER_COMPLETION} instead
+ */
+ @Deprecated
+ private static final String EXTRA_GOOGLE_ACTIVITY_AFTER_COMPLETION =
+ "com.google.android.tv.intent.extra.ACTIVITY_AFTER_COMPLETION";
+
+ public static void removeSetupIntent(Bundle extras) {
+ extras.remove(EXTRA_SETUP_INTENT);
+ extras.remove(EXTRA_GOOGLE_SETUP_INTENT);
+ }
+
+ @Nullable
+ public static Intent getExtraSetupIntent(Intent intent) {
+ Bundle extras = intent.getExtras();
+ if (extras == null) {
+ return null;
+ }
+ Intent setupIntent = extras.getParcelable(EXTRA_SETUP_INTENT);
+ return setupIntent != null ? setupIntent : extras.getParcelable(EXTRA_GOOGLE_SETUP_INTENT);
+ }
+
+ @Nullable
+ public static Intent getExtraActivityAfter(Intent intent) {
+ Bundle extras = intent.getExtras();
+ if (extras == null) {
+ return null;
+ }
+ Intent setupIntent = extras.getParcelable(EXTRA_ACTIVITY_AFTER_COMPLETION);
+ return setupIntent != null
+ ? setupIntent
+ : extras.getParcelable(EXTRA_GOOGLE_ACTIVITY_AFTER_COMPLETION);
+ }
+
+ public static boolean hasInputSetupAction(Intent intent) {
+ String action = intent.getAction();
+ return INTENT_ACTION_INPUT_SETUP.equals(action)
+ || INTENT_GOOGLE_ACTION_INPUT_SETUP.equals(action);
+ }
+}
diff --git a/common/src/com/android/tv/common/annotation/UsedByNative.java b/common/src/com/android/tv/common/annotation/UsedByNative.java
new file mode 100644
index 00000000..dc829e08
--- /dev/null
+++ b/common/src/com/android/tv/common/annotation/UsedByNative.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 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.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation used for marking methods and fields that are called from native
+ * code. Useful for keeping components that would otherwise be removed by
+ * Proguard. Use the value parameter to mention a file that calls this method.
+ *
+ * Note that adding this annotation to a method is not enough to guarantee that
+ * it is kept - either its class must be referenced elsewhere in the program, or
+ * the class must be annotated with this as well.
+ *
+ * Usage example:<br />
+ * <pre>{@code
+ * @UsedByNative("NativeCrashHandler.cpp")
+ * public static void reportCrash(int signal, int code, int address) {
+ * ...
+ * }
+ * </pre>
+ *
+ */
+@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.CONSTRUCTOR})
+public @interface UsedByNative {
+ String value();
+}
diff --git a/src/com/android/tv/util/NamedThreadFactory.java b/common/src/com/android/tv/common/concurrent/NamedThreadFactory.java
index fcdde952..4c8371f3 100644
--- a/src/com/android/tv/util/NamedThreadFactory.java
+++ b/common/src/com/android/tv/common/concurrent/NamedThreadFactory.java
@@ -14,25 +14,22 @@
* limitations under the License
*/
-package com.android.tv.util;
+package com.android.tv.common.concurrent;
import android.support.annotation.NonNull;
-
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
-/**
- * A thread factory that creates threads with a suffix.
- */
+/** A thread factory that creates threads with named <code>prefix-##</code>. */
public class NamedThreadFactory implements ThreadFactory {
private final AtomicInteger mCount = new AtomicInteger(0);
private final ThreadFactory mDefaultThreadFactory;
private final String mPrefix;
- public NamedThreadFactory(final String baseName) {
+ public NamedThreadFactory(final String prefix) {
mDefaultThreadFactory = Executors.defaultThreadFactory();
- mPrefix = baseName + "-";
+ mPrefix = prefix + "-";
}
@Override
diff --git a/src/com/android/tv/config/DefaultConfigManager.java b/common/src/com/android/tv/common/config/DefaultConfigManager.java
index bbabc6d4..ae240855 100644
--- a/src/com/android/tv/config/DefaultConfigManager.java
+++ b/common/src/com/android/tv/common/config/DefaultConfigManager.java
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package com.android.tv.config;
+package com.android.tv.common.config;
import android.content.Context;
+import com.android.tv.common.config.api.RemoteConfig;
-/**
- * Stub Remote Config.
- */
+/** Stub Remote Config. */
public class DefaultConfigManager {
public static final long DEFAULT_LONG_VALUE = 0;
+
public static DefaultConfigManager createInstance(Context context) {
return new DefaultConfigManager();
}
@@ -35,9 +35,7 @@ public class DefaultConfigManager {
private static class StubRemoteConfig implements RemoteConfig {
@Override
- public void fetch(OnRemoteConfigUpdatedListener listener) {
-
- }
+ public void fetch(OnRemoteConfigUpdatedListener listener) {}
@Override
public String getString(String key) {
@@ -53,9 +51,10 @@ public class DefaultConfigManager {
public long getLong(String key) {
return DEFAULT_LONG_VALUE;
}
+
+ @Override
+ public long getLong(String key, long defaultValue) {
+ return defaultValue;
+ }
}
}
-
-
-
-
diff --git a/src/com/android/tv/config/RemoteConfigFeature.java b/common/src/com/android/tv/common/config/RemoteConfigFeature.java
index 502e6a9c..2ea381f0 100644
--- a/src/com/android/tv/config/RemoteConfigFeature.java
+++ b/common/src/com/android/tv/common/config/RemoteConfigFeature.java
@@ -14,15 +14,14 @@
* limitations under the License
*/
-package com.android.tv.config;
+package com.android.tv.common.config;
import android.content.Context;
-
-import com.android.tv.TvApplication;
+import com.android.tv.common.BaseApplication;
import com.android.tv.common.feature.Feature;
/**
- * A {@link Feature} controlled by a {@link RemoteConfig} boolean.
+ * A {@link Feature} controlled by a {@link com.android.tv.common.config.api.RemoteConfig} boolean.
*/
public class RemoteConfigFeature implements Feature {
private final String mKey;
@@ -38,6 +37,6 @@ public class RemoteConfigFeature implements Feature {
@Override
public boolean isEnabled(Context context) {
- return TvApplication.getSingletons(context).getRemoteConfig().getBoolean(mKey);
+ return BaseApplication.getSingletons(context).getRemoteConfig().getBoolean(mKey);
}
}
diff --git a/src/com/android/tv/config/RemoteConfig.java b/common/src/com/android/tv/common/config/api/RemoteConfig.java
index f7ae87e7..74597f9d 100644
--- a/src/com/android/tv/config/RemoteConfig.java
+++ b/common/src/com/android/tv/common/config/api/RemoteConfig.java
@@ -14,38 +14,41 @@
* limitations under the License.
*/
-package com.android.tv.config;
+package com.android.tv.common.config.api;
/**
* Manages Live TV Configuration, allowing remote updates.
*
- * <p>This is a thin wrapper around
- * <a href="https://firebase.google.com/docs/remote-config/"></a>Firebase Remote Config</a>
+ * <p>This is a thin wrapper around <a
+ * href="https://firebase.google.com/docs/remote-config/"></a>Firebase Remote Config</a>
*/
public interface RemoteConfig {
- /**
- * Notified on successful completion of a {@link #fetch)}
- */
+ /** Used to inject a remote config */
+ interface HasRemoteConfig {
+ RemoteConfig getRemoteConfig();
+ }
+
+ /** Notified on successful completion of a {@link #fetch)} */
interface OnRemoteConfigUpdatedListener {
void onRemoteConfigUpdated();
}
- /**
- * Starts a fetch and notifies {@code listener} on successful completion.
- */
+ /** Starts a fetch and notifies {@code listener} on successful completion. */
void fetch(OnRemoteConfigUpdatedListener listener);
- /**
- * Gets value as a string corresponding to the specified key.
- */
+ /** Gets value as a string corresponding to the specified key. */
String getString(String key);
- /**
- * Gets value as a boolean corresponding to the specified key.
- */
+ /** Gets value as a boolean corresponding to the specified key. */
boolean getBoolean(String key);
/** Gets value as a long corresponding to the specified key. */
long getLong(String key);
+
+ /**
+ * Gets value as a long corresponding to the specified key. Returns the defaultValue if no value
+ * is found.
+ */
+ long getLong(String key, long defaultValue);
}
diff --git a/common/src/com/android/tv/common/config/api/RemoteConfigValue.java b/common/src/com/android/tv/common/config/api/RemoteConfigValue.java
new file mode 100644
index 00000000..6da89fb9
--- /dev/null
+++ b/common/src/com/android/tv/common/config/api/RemoteConfigValue.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 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.config.api;
+
+/** Wrapper for a RemoteConfig key and default value. */
+public abstract class RemoteConfigValue<T> {
+ private final T defaultValue;
+ private final String key;
+
+ private RemoteConfigValue(String key, T defaultValue) {
+ this.defaultValue = defaultValue;
+ this.key = key;
+ }
+
+ /** Create with the given key and default value */
+ public static RemoteConfigValue<Long> create(String key, long defaultValue) {
+ return new RemoteConfigValue<Long>(key, defaultValue) {
+ @Override
+ public Long get(RemoteConfig remoteConfig) {
+ return remoteConfig.getLong(key, defaultValue);
+ }
+ };
+ }
+
+ public abstract T get(RemoteConfig remoteConfig);
+
+ public final T getDefaultValue() {
+ return defaultValue;
+ }
+
+ @Override
+ public final String toString() {
+ return "RemoteConfigValue(key=" + key + ", defalutValue=" + defaultValue + "]";
+ }
+}
diff --git a/src/com/android/tv/customization/CustomAction.java b/common/src/com/android/tv/common/customization/CustomAction.java
index b8f4695b..ee18cb3c 100644
--- a/src/com/android/tv/customization/CustomAction.java
+++ b/common/src/com/android/tv/common/customization/CustomAction.java
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-package com.android.tv.customization;
+package com.android.tv.common.customization;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
-/**
- * Describes a custom option defined in customization package.
- * This will be added to main menu.
- */
+/** Describes a custom option defined in customization package. This will be added to main menu. */
public class CustomAction implements Comparable<CustomAction> {
private static final int POSITION_THRESHOLD = 100;
@@ -40,9 +37,9 @@ public class CustomAction implements Comparable<CustomAction> {
}
/**
- * Returns if this option comes before the existing items.
- * Note that custom options can only be placed at the front or back.
- * (i.e. cannot be added in the middle of existing options.)
+ * Returns if this option comes before the existing items. Note that custom options can only be
+ * placed at the front or back. (i.e. cannot be added in the middle of existing options.)
+ *
* @return {@code true} if it goes to the beginning. {@code false} if it goes to the end.
*/
public boolean isFront() {
@@ -54,23 +51,17 @@ public class CustomAction implements Comparable<CustomAction> {
return mPositionPriority - another.mPositionPriority;
}
- /**
- * Returns title.
- */
+ /** Returns title. */
public String getTitle() {
return mTitle;
}
- /**
- * Returns icon drawable.
- */
+ /** Returns icon drawable. */
public Drawable getIconDrawable() {
return mIconDrawable;
}
- /**
- * Returns intent to launch when this option is clicked.
- */
+ /** Returns intent to launch when this option is clicked. */
public Intent getIntent() {
return mIntent;
}
diff --git a/src/com/android/tv/customization/TvCustomizationManager.java b/common/src/com/android/tv/common/customization/CustomizationManager.java
index ed6b98ca..09ecaef8 100644
--- a/src/com/android/tv/customization/TvCustomizationManager.java
+++ b/common/src/com/android/tv/common/customization/CustomizationManager.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.tv.customization;
+package com.android.tv.common.customization;
import android.content.Context;
import android.content.Intent;
@@ -27,7 +27,7 @@ import android.graphics.drawable.Drawable;
import android.support.annotation.IntDef;
import android.text.TextUtils;
import android.util.Log;
-
+import com.android.tv.common.CommonConstants;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -36,38 +36,36 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
-public class TvCustomizationManager {
- private static final String TAG = "TvCustomizationManager";
+public class CustomizationManager {
+ private static final String TAG = "CustomizationManager";
private static final boolean DEBUG = false;
private static final String[] CUSTOMIZE_PERMISSIONS = {
- "com.android.tv.permission.CUSTOMIZE_TV_APP"
+ CommonConstants.BASE_PACKAGE + ".permission.CUSTOMIZE_TV_APP"
};
private static final String CATEGORY_TV_CUSTOMIZATION =
- "com.android.tv.category";
+ CommonConstants.BASE_PACKAGE + ".category";
- /**
- * Row IDs to share customized actions.
- * Only rows listed below can have customized action.
- */
+ /** Row IDs to share customized actions. Only rows listed below can have customized action. */
public static final String ID_OPTIONS_ROW = "options_row";
+
public static final String ID_PARTNER_ROW = "partner_row";
@IntDef({TRICKPLAY_MODE_ENABLED, TRICKPLAY_MODE_DISABLED, TRICKPLAY_MODE_USE_EXTERNAL_STORAGE})
@Retention(RetentionPolicy.SOURCE)
public @interface TRICKPLAY_MODE {}
+
public static final int TRICKPLAY_MODE_ENABLED = 0;
public static final int TRICKPLAY_MODE_DISABLED = 1;
public static final int TRICKPLAY_MODE_USE_EXTERNAL_STORAGE = 2;
private static final String[] TRICKPLAY_MODE_STRINGS = {
- "enabled",
- "disabled",
- "use_external_storage_only"
+ "enabled", "disabled", "use_external_storage_only"
};
private static final HashMap<String, String> INTENT_CATEGORY_TO_ROW_ID;
+
static {
INTENT_CATEGORY_TO_ROW_ID = new HashMap<>();
INTENT_CATEGORY_TO_ROW_ID.put(CATEGORY_TV_CUSTOMIZATION + ".OPTIONS_ROW", ID_OPTIONS_ROW);
@@ -92,7 +90,7 @@ public class TvCustomizationManager {
private String mPartnerRowTitle;
private final Map<String, List<CustomAction>> mRowIdToCustomActionsMap = new HashMap<>();
- public TvCustomizationManager(Context context) {
+ public CustomizationManager(Context context) {
mContext = context;
mInitialized = false;
}
@@ -108,10 +106,14 @@ public class TvCustomizationManager {
sHasLinuxDvbBuiltInTuner = false;
} else {
try {
- Resources res = context.getPackageManager()
- .getResourcesForApplication(sCustomizationPackage);
- int resId = res.getIdentifier(RES_ID_HAS_LINUX_DVB_BUILT_IN_TUNER,
- RES_TYPE_BOOLEAN, sCustomizationPackage);
+ Resources res =
+ context.getPackageManager()
+ .getResourcesForApplication(sCustomizationPackage);
+ int resId =
+ res.getIdentifier(
+ RES_ID_HAS_LINUX_DVB_BUILT_IN_TUNER,
+ RES_TYPE_BOOLEAN,
+ sCustomizationPackage);
sHasLinuxDvbBuiltInTuner = resId != 0 && res.getBoolean(resId);
} catch (NameNotFoundException e) {
sHasLinuxDvbBuiltInTuner = false;
@@ -128,10 +130,12 @@ public class TvCustomizationManager {
} else {
try {
String customization = null;
- Resources res = context.getPackageManager()
- .getResourcesForApplication(sCustomizationPackage);
- int resId = res.getIdentifier(RES_ID_TRICKPLAY_MODE,
- RES_TYPE_STRING, sCustomizationPackage);
+ Resources res =
+ context.getPackageManager()
+ .getResourcesForApplication(sCustomizationPackage);
+ int resId =
+ res.getIdentifier(
+ RES_ID_TRICKPLAY_MODE, RES_TYPE_STRING, sCustomizationPackage);
customization = resId == 0 ? null : res.getString(resId);
sTrickplayMode = TRICKPLAY_MODE_ENABLED;
if (customization != null) {
@@ -152,17 +156,15 @@ public class TvCustomizationManager {
private static String getCustomizationPackageName(Context context) {
if (sCustomizationPackage == null) {
- List<PackageInfo> packageInfos = context.getPackageManager()
- .getPackagesHoldingPermissions(CUSTOMIZE_PERMISSIONS, 0);
+ List<PackageInfo> packageInfos =
+ context.getPackageManager()
+ .getPackagesHoldingPermissions(CUSTOMIZE_PERMISSIONS, 0);
sCustomizationPackage = packageInfos.size() == 0 ? "" : packageInfos.get(0).packageName;
}
return sCustomizationPackage;
}
- /**
- * Initialize TV customization options.
- * Run this API only on the main thread.
- */
+ /** Initialize TV customization options. Run this API only on the main thread. */
public void initialize() {
if (mInitialized) {
return;
@@ -181,14 +183,21 @@ public class TvCustomizationManager {
Intent customOptionIntent = new Intent(Intent.ACTION_MAIN);
customOptionIntent.addCategory(intentCategory);
- List<ResolveInfo> activities = pm.queryIntentActivities(customOptionIntent,
- PackageManager.GET_RECEIVERS | PackageManager.GET_RESOLVED_FILTER
- | PackageManager.GET_META_DATA);
+ List<ResolveInfo> activities =
+ pm.queryIntentActivities(
+ customOptionIntent,
+ PackageManager.GET_RECEIVERS
+ | PackageManager.GET_RESOLVED_FILTER
+ | PackageManager.GET_META_DATA);
for (ResolveInfo info : activities) {
String packageName = info.activityInfo.packageName;
if (!TextUtils.equals(packageName, sCustomizationPackage)) {
- Log.w(TAG, "A customization package " + sCustomizationPackage
- + " already exist. Ignoring " + packageName);
+ Log.w(
+ TAG,
+ "A customization package "
+ + sCustomizationPackage
+ + " already exist. Ignoring "
+ + packageName);
continue;
}
@@ -217,8 +226,14 @@ public class TvCustomizationManager {
Log.d(TAG, "Dumping custom actions");
for (String id : mRowIdToCustomActionsMap.keySet()) {
for (CustomAction action : mRowIdToCustomActionsMap.get(id)) {
- Log.d(TAG, "Custom row rowId=" + id + " title=" + action.getTitle()
- + " class=" + action.getIntent());
+ Log.d(
+ TAG,
+ "Custom row rowId="
+ + id
+ + " title="
+ + action.getTitle()
+ + " class="
+ + action.getIntent());
}
}
Log.d(TAG, "Dumping custom actions - end of dump");
@@ -228,7 +243,7 @@ public class TvCustomizationManager {
/**
* Returns custom actions for given row id.
*
- * Row ID is one of ID_OPTIONS_ROW or ID_PARTNER_ROW.
+ * <p>Row ID is one of ID_OPTIONS_ROW or ID_PARTNER_ROW.
*/
public List<CustomAction> getCustomActions(String rowId) {
return mRowIdToCustomActionsMap.get(rowId);
@@ -238,23 +253,20 @@ public class TvCustomizationManager {
mPartnerRowTitle = null;
Resources res;
try {
- res = mContext.getPackageManager()
- .getResourcesForApplication(sCustomizationPackage);
+ res = mContext.getPackageManager().getResourcesForApplication(sCustomizationPackage);
} catch (NameNotFoundException e) {
Log.w(TAG, "Could not get resources for package " + sCustomizationPackage);
return;
}
- int resId = res.getIdentifier(
- RES_ID_PARTNER_ROW_TITLE, RES_TYPE_STRING, sCustomizationPackage);
+ int resId =
+ res.getIdentifier(RES_ID_PARTNER_ROW_TITLE, RES_TYPE_STRING, sCustomizationPackage);
if (resId != 0) {
mPartnerRowTitle = res.getString(resId);
}
if (DEBUG) Log.d(TAG, "Partner row title [" + mPartnerRowTitle + "]");
}
- /**
- * Returns partner row title.
- */
+ /** Returns partner row title. */
public String getPartnerRowTitle() {
return mPartnerRowTitle;
}
diff --git a/src/com/android/tv/experiments/ExperimentFlag.java b/common/src/com/android/tv/common/experiments/ExperimentFlag.java
index c0cbd643..c9bacac4 100644
--- a/src/com/android/tv/experiments/ExperimentFlag.java
+++ b/common/src/com/android/tv/common/experiments/ExperimentFlag.java
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package com.android.tv.experiments;
+package com.android.tv.common.experiments;
import android.support.annotation.VisibleForTesting;
+import com.android.tv.common.BuildConfig;
-/**
- * Experiments return values based on user, device and other criteria.
- */
+
+/** Experiments return values based on user, device and other criteria. */
public final class ExperimentFlag<T> {
private static boolean sAllowOverrides = false;
@@ -64,7 +64,4 @@ public final class ExperimentFlag<T> {
public void resetOverride() {
mOverridden = false;
}
-
-
-
}
diff --git a/common/src/com/android/tv/common/experiments/ExperimentLoader.java b/common/src/com/android/tv/common/experiments/ExperimentLoader.java
new file mode 100644
index 00000000..5f012e11
--- /dev/null
+++ b/common/src/com/android/tv/common/experiments/ExperimentLoader.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 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.experiments;
+
+import android.content.Context;
+
+/** Used to sync {@link ExperimentFlag}s. */
+public class ExperimentLoader {
+
+ /** Starts a background task to update {@link ExperimentFlag}s */
+ public void asyncRefreshExperiments(Context context) {
+ // Override for your experiment system
+ }
+}
diff --git a/src/com/android/tv/experiments/Experiments.java b/common/src/com/android/tv/common/experiments/Experiments.java
index 53cce979..96b15e53 100644
--- a/src/com/android/tv/experiments/Experiments.java
+++ b/common/src/com/android/tv/common/experiments/Experiments.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.tv.experiments;
+package com.android.tv.common.experiments;
-import static com.android.tv.experiments.ExperimentFlag.createFlag;
+import static com.android.tv.common.experiments.ExperimentFlag.createFlag;
import com.android.tv.common.BuildConfig;
@@ -26,10 +26,16 @@ import com.android.tv.common.BuildConfig;
* <p>This file is maintained by hand.
*/
public final class Experiments {
- public static final ExperimentFlag<Boolean> CLOUD_EPG = createFlag(
- true);
+ public static final ExperimentFlag<Boolean> CLOUD_EPG =
+ ExperimentFlag.createFlag(
+ true);
public static final ExperimentFlag<Boolean> ENABLE_UNRATED_CONTENT_SETTINGS =
+ ExperimentFlag.createFlag(
+ false);
+
+ /** Turn analytics on or off based on the System Checkbox for logging. */
+ public static final ExperimentFlag<Boolean> ENABLE_ANALYTICS_VIA_CHECKBOX =
createFlag(
false);
@@ -38,8 +44,20 @@ public final class Experiments {
*
* <p>These features are available to select users(aka fishfooders) on production builds.
*/
- public static final ExperimentFlag<Boolean> ENABLE_DEVELOPER_FEATURES = createFlag(
- BuildConfig.ENG);
+ public static final ExperimentFlag<Boolean> ENABLE_DEVELOPER_FEATURES =
+ ExperimentFlag.createFlag(
+ BuildConfig.ENG);
+
+ /**
+ * Allow QA features.
+ *
+ * <p>These features must be carefully limited, keeping QA differences to a minimum.
+ *
+ * <p>These features are available to select users(aka QA) on production builds.
+ */
+ public static final ExperimentFlag<Boolean> ENABLE_QA_FEATURES =
+ ExperimentFlag.createFlag(
+ false);
private Experiments() {}
}
diff --git a/common/src/com/android/tv/common/feature/CommonFeatures.java b/common/src/com/android/tv/common/feature/CommonFeatures.java
index 62c88ead..1fceabb3 100644
--- a/common/src/com/android/tv/common/feature/CommonFeatures.java
+++ b/common/src/com/android/tv/common/feature/CommonFeatures.java
@@ -19,36 +19,81 @@ package com.android.tv.common.feature;
import static com.android.tv.common.feature.FeatureUtils.AND;
import static com.android.tv.common.feature.TestableFeature.createTestableFeature;
+import android.content.Context;
+import android.os.Build;
+import android.text.TextUtils;
+import android.util.Log;
+import com.android.tv.common.config.api.RemoteConfig.HasRemoteConfig;
+import com.android.tv.common.experiments.Experiments;
+
+import com.android.tv.common.util.CommonUtils;
+import com.android.tv.common.util.LocationUtils;
+
/**
* List of {@link Feature} that affect more than just the Live TV app.
*
* <p>Remove the {@code Feature} once it is launched.
*/
public class CommonFeatures {
+ private static final String TAG = "CommonFeatures";
+ private static final boolean DEBUG = false;
+
/**
* DVR
*
* <p>See <a href="https://goto.google.com/atv-dvr-onepager">go/atv-dvr-onepager</a>
*
- * DVR API is introduced in N, it only works when app runs as a system app.
+ * <p>DVR API is introduced in N, it only works when app runs as a system app.
*/
- public static final TestableFeature DVR = createTestableFeature(
- AND(Sdk.AT_LEAST_N, SystemAppFeature.SYSTEM_APP_FEATURE));
+ public static final TestableFeature DVR =
+ createTestableFeature(AND(Sdk.AT_LEAST_N, SystemAppFeature.SYSTEM_APP_FEATURE));
/**
* ENABLE_RECORDING_REGARDLESS_OF_STORAGE_STATUS
*
- * Enables dvr recording regardless of storage status.
+ * <p>Enables dvr recording regardless of storage status.
*/
public static final Feature FORCE_RECORDING_UNTIL_NO_SPACE =
- new PropertyFeature("force_recording_until_no_space", false);
+ PropertyFeature.create("force_recording_until_no_space", false);
- /**
- * USE_SW_CODEC_FOR_SD
- *
- * Prefer software based codec for SD channels.
- */
- public static final Feature USE_SW_CODEC_FOR_SD =
- new PropertyFeature("use_sw_codec_for_sd", false
- );
+ public static final Feature TUNER =
+ new Feature() {
+ @Override
+ public boolean isEnabled(Context context) {
+
+ if (CommonUtils.isDeveloper()) {
+ // we enable tuner for developers to test tuner in any platform.
+ return true;
+ }
+
+ // 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
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
+ }
+ };
+
+ /** Show postal code fragment before channel scan. */
+ public static final Feature ENABLE_CLOUD_EPG_REGION =
+ new Feature() {
+ private final String[] supportedRegions = {
+ };
+
+
+ @Override
+ public boolean isEnabled(Context context) {
+ if (!Experiments.CLOUD_EPG.get()) {
+ if (DEBUG) Log.d(TAG, "Experiments.CLOUD_EPG is false");
+ return false;
+ }
+ String country = LocationUtils.getCurrentCountry(context);
+ for (int i = 0; i < supportedRegions.length; i++) {
+ if (supportedRegions[i].equalsIgnoreCase(country)) {
+ return true;
+ }
+ }
+ if (DEBUG) Log.d(TAG, "EPG flag false after country check");
+ return false;
+ }
+ };
}
diff --git a/common/src/com/android/tv/common/feature/EngOnlyFeature.java b/common/src/com/android/tv/common/feature/EngOnlyFeature.java
index 9fc39d9f..5feb5481 100644
--- a/common/src/com/android/tv/common/feature/EngOnlyFeature.java
+++ b/common/src/com/android/tv/common/feature/EngOnlyFeature.java
@@ -17,16 +17,13 @@
package com.android.tv.common.feature;
import android.content.Context;
-
import com.android.tv.common.BuildConfig;
-/**
- * A feature that is only available on {@link BuildConfig#ENG} builds.
- */
+/** A feature that is only available on {@link BuildConfig#ENG} builds. */
public final class EngOnlyFeature implements Feature {
public static final Feature ENG_ONLY_FEATURE = new EngOnlyFeature();
- private EngOnlyFeature() { }
+ private EngOnlyFeature() {}
@Override
public boolean isEnabled(Context context) {
diff --git a/common/src/com/android/tv/common/feature/ExperimentFeature.java b/common/src/com/android/tv/common/feature/ExperimentFeature.java
new file mode 100644
index 00000000..820eda49
--- /dev/null
+++ b/common/src/com/android/tv/common/feature/ExperimentFeature.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 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.feature;
+
+import android.content.Context;
+import com.android.tv.common.experiments.ExperimentFlag;
+
+/** A {@link Feature} base on an {@link ExperimentFlag}. */
+public final class ExperimentFeature implements Feature {
+
+ public static Feature from(ExperimentFlag<Boolean> flag) {
+ return new ExperimentFeature(flag);
+ }
+
+ private final ExperimentFlag<Boolean> mFlag;
+
+ private ExperimentFeature(ExperimentFlag<Boolean> flag) {
+ mFlag = flag;
+ }
+
+ @Override
+ public boolean isEnabled(Context context) {
+ return mFlag.get();
+ }
+
+ @Override
+ public String toString() {
+ return "ExperimentFeature for " + mFlag;
+ }
+}
diff --git a/common/src/com/android/tv/common/feature/Feature.java b/common/src/com/android/tv/common/feature/Feature.java
index b3a8336c..fd5625e5 100644
--- a/common/src/com/android/tv/common/feature/Feature.java
+++ b/common/src/com/android/tv/common/feature/Feature.java
@@ -23,16 +23,15 @@ import android.content.Context;
* launched.
*
* <p>Expected usage is:
+ *
* <pre>{@code
- * if (MY_FEATURE.isEnabled(context) {
- * showNewCoolUi();
- * } else{
- * showOldBoringUi();
- * }
+ * if (MY_FEATURE.isEnabled(context) {
+ * showNewCoolUi();
+ * } else{
+ * showOldBoringUi();
+ * }
* }</pre>
*/
public interface Feature {
boolean isEnabled(Context context);
-
-
}
diff --git a/common/src/com/android/tv/common/feature/FeatureUtils.java b/common/src/com/android/tv/common/feature/FeatureUtils.java
index f60b2048..8650d151 100644
--- a/common/src/com/android/tv/common/feature/FeatureUtils.java
+++ b/common/src/com/android/tv/common/feature/FeatureUtils.java
@@ -17,12 +17,10 @@
package com.android.tv.common.feature;
import android.content.Context;
-
+import com.android.tv.common.util.CommonUtils;
import java.util.Arrays;
-/**
- * Static utilities for features.
- */
+/** Static utilities for features. */
public class FeatureUtils {
/**
@@ -47,7 +45,6 @@ public class FeatureUtils {
return "or(" + Arrays.asList(features) + ")";
}
};
-
}
/**
@@ -74,36 +71,47 @@ public class FeatureUtils {
};
}
- /**
- * A feature that is always enabled.
- */
- public static final Feature ON = new Feature() {
- @Override
- public boolean isEnabled(Context context) {
- return true;
- }
+ /** A feature that is always enabled. */
+ public static final Feature ON =
+ new Feature() {
+ @Override
+ public boolean isEnabled(Context context) {
+ return true;
+ }
- @Override
- public String toString() {
- return "on";
- }
- };
+ @Override
+ public String toString() {
+ return "on";
+ }
+ };
- /**
- * A feature that is always disabled.
- */
- public static final Feature OFF = new Feature() {
- @Override
- public boolean isEnabled(Context context) {
- return false;
- }
+ /** A feature that is always disabled. */
+ public static final Feature OFF =
+ new Feature() {
+ @Override
+ public boolean isEnabled(Context context) {
+ return false;
+ }
- @Override
- public String toString() {
- return "off";
- }
- };
+ @Override
+ public String toString() {
+ return "off";
+ }
+ };
- private FeatureUtils() {
- }
+ /** True if running in robolectric. */
+ public static final Feature ROBOLECTRIC =
+ new Feature() {
+ @Override
+ public boolean isEnabled(Context context) {
+ return CommonUtils.isRoboTest();
+ }
+
+ @Override
+ public String toString() {
+ return "isRobolecteric";
+ }
+ };
+
+ private FeatureUtils() {}
}
diff --git a/common/src/com/android/tv/common/feature/GServiceFeature.java b/common/src/com/android/tv/common/feature/GServiceFeature.java
index 9e6e11a6..1d7d1156 100644
--- a/common/src/com/android/tv/common/feature/GServiceFeature.java
+++ b/common/src/com/android/tv/common/feature/GServiceFeature.java
@@ -18,10 +18,7 @@ package com.android.tv.common.feature;
import android.content.Context;
-
-/**
- * A feature controlled by a GServices flag.
- */
+/** A feature controlled by a GServices flag. */
public class GServiceFeature implements Feature {
private static final String LIVECHANNELS_PREFIX = "livechannels:";
private final String mKey;
diff --git a/common/src/com/android/tv/common/feature/Model.java b/common/src/com/android/tv/common/feature/Model.java
new file mode 100644
index 00000000..7aa5148e
--- /dev/null
+++ b/common/src/com/android/tv/common/feature/Model.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 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.feature;
+
+import android.content.Context;
+
+/** Holder for {@link android.os.Build#MODEL} features. */
+public interface Model {
+
+ ModelFeature NEXUS_PLAYER = new ModelFeature("Nexus Player");
+
+ /** True when the {@link android.os.Build#MODEL} equals the {@code model} given. */
+ public static final class ModelFeature implements Feature {
+ private final String mModel;
+
+ private ModelFeature(String model) {
+ mModel = model;
+ }
+
+ @Override
+ public boolean isEnabled(Context context) {
+ return isEnabled();
+ }
+
+ public boolean isEnabled() {
+ return android.os.Build.MODEL.equals(mModel);
+ }
+
+ @Override
+ public String toString() {
+ return "ModelFeature(" + mModel + ")=" + isEnabled();
+ }
+ }
+}
diff --git a/common/src/com/android/tv/common/feature/PackageVersionFeature.java b/common/src/com/android/tv/common/feature/PackageVersionFeature.java
index 7f615d31..f41a14a0 100644
--- a/common/src/com/android/tv/common/feature/PackageVersionFeature.java
+++ b/common/src/com/android/tv/common/feature/PackageVersionFeature.java
@@ -49,7 +49,10 @@ public class PackageVersionFeature implements Feature {
@Override
public String toString() {
- return "PackageVersionFeature[packageName=" + mPackageName + ",requiredVersion="
- + mRequiredVersionCode + "]";
+ return "PackageVersionFeature[packageName="
+ + mPackageName
+ + ",requiredVersion="
+ + mRequiredVersionCode
+ + "]";
}
}
diff --git a/common/src/com/android/tv/common/feature/PropertyFeature.java b/common/src/com/android/tv/common/feature/PropertyFeature.java
index fdcffa04..0cf36317 100644
--- a/common/src/com/android/tv/common/feature/PropertyFeature.java
+++ b/common/src/com/android/tv/common/feature/PropertyFeature.java
@@ -17,7 +17,6 @@
package com.android.tv.common.feature;
import android.content.Context;
-
import com.android.tv.common.BooleanSystemProperty;
/**
@@ -26,21 +25,29 @@ import com.android.tv.common.BooleanSystemProperty;
* <p>See {@link BooleanSystemProperty} for instructions on how to set using adb.
*/
public final class PropertyFeature implements Feature {
+
+ public static PropertyFeature create(String key, boolean defaultValue) {
+ return new PropertyFeature(key, defaultValue);
+ }
+
private final BooleanSystemProperty mProperty;
/**
* Create System Property backed feature.
*
- * @param key the system property key. Length must be <= 31 characters.
+ * @param key the system property key. Length must be <= 31 characters.
* @param defaultValue the value to return if the property is undefined or empty.
*/
- public PropertyFeature(String key, boolean defaultValue) {
+ private PropertyFeature(String key, boolean defaultValue) {
if (key.length() > 31) {
// Since Features are initialized at startup and the keys are static go ahead and kill
// the application.
throw new IllegalArgumentException(
- "Property keys have a max length of 31 characters but '" + key + "' is " + key
- .length() + " characters.");
+ "Property keys have a max length of 31 characters but '"
+ + key
+ + "' is "
+ + key.length()
+ + " characters.");
}
mProperty = new BooleanSystemProperty(key, defaultValue);
}
diff --git a/common/src/com/android/tv/common/feature/Sdk.java b/common/src/com/android/tv/common/feature/Sdk.java
index 9f99a64f..155b391d 100644
--- a/common/src/com/android/tv/common/feature/Sdk.java
+++ b/common/src/com/android/tv/common/feature/Sdk.java
@@ -19,10 +19,8 @@ package com.android.tv.common.feature;
import android.content.Context;
import android.os.Build;
-/**
- * Holder for SDK version features
- */
-public class Sdk {
+/** Holder for SDK version features */
+public final class Sdk {
public static final Feature AT_LEAST_N =
new Feature() {
@Override
@@ -31,5 +29,13 @@ public class Sdk {
}
};
+ public static final Feature AT_LEAST_O =
+ new Feature() {
+ @Override
+ public boolean isEnabled(Context context) {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
+ }
+ };
+
private Sdk() {}
}
diff --git a/common/src/com/android/tv/common/feature/SharedPreferencesFeature.java b/common/src/com/android/tv/common/feature/SharedPreferencesFeature.java
index 881f53d6..ef2260d6 100644
--- a/common/src/com/android/tv/common/feature/SharedPreferencesFeature.java
+++ b/common/src/com/android/tv/common/feature/SharedPreferencesFeature.java
@@ -19,11 +19,9 @@ package com.android.tv.common.feature;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
-import com.android.tv.common.SharedPreferencesUtils;
+import com.android.tv.common.util.SharedPreferencesUtils;
-/**
- * Feature controlled by shared preferences.
- */
+/** Feature controlled by shared preferences. */
public final class SharedPreferencesFeature implements Feature {
private static final String TAG = "SharedPrefFeature";
private static final boolean DEBUG = false;
@@ -53,8 +51,9 @@ public final class SharedPreferencesFeature implements Feature {
return false;
}
if (mSharedPreferences == null) {
- mSharedPreferences = context.getSharedPreferences(
- SharedPreferencesUtils.SHARED_PREF_FEATURES, Context.MODE_PRIVATE);
+ mSharedPreferences =
+ context.getSharedPreferences(
+ SharedPreferencesUtils.SHARED_PREF_FEATURES, Context.MODE_PRIVATE);
mEnabled = mSharedPreferences.getBoolean(mKey, mDefaultValue);
}
if (DEBUG) Log.d(TAG, mKey + " is " + mEnabled);
@@ -69,14 +68,14 @@ public final class SharedPreferencesFeature implements Feature {
public void setEnabled(Context context, boolean enable) {
if (DEBUG) Log.d(TAG, mKey + " is set to " + enable);
if (mSharedPreferences == null) {
- mSharedPreferences = context.getSharedPreferences(
- SharedPreferencesUtils.SHARED_PREF_FEATURES, Context.MODE_PRIVATE);
+ mSharedPreferences =
+ context.getSharedPreferences(
+ SharedPreferencesUtils.SHARED_PREF_FEATURES, Context.MODE_PRIVATE);
mEnabled = enable;
mSharedPreferences.edit().putBoolean(mKey, enable).apply();
} else if (mEnabled != enable) {
mEnabled = enable;
mSharedPreferences.edit().putBoolean(mKey, enable).apply();
}
-
}
}
diff --git a/common/src/com/android/tv/common/feature/SystemAppFeature.java b/common/src/com/android/tv/common/feature/SystemAppFeature.java
index 79fd32f3..1dd3b8dd 100644
--- a/common/src/com/android/tv/common/feature/SystemAppFeature.java
+++ b/common/src/com/android/tv/common/feature/SystemAppFeature.java
@@ -19,13 +19,11 @@ package com.android.tv.common.feature;
import android.content.Context;
import android.content.pm.ApplicationInfo;
-/**
- * A feature that is for system App.
- */
+/** A feature that is for system App. */
public final class SystemAppFeature implements Feature {
public static final Feature SYSTEM_APP_FEATURE = new SystemAppFeature();
- private SystemAppFeature() { }
+ private SystemAppFeature() {}
@Override
public boolean isEnabled(Context context) {
diff --git a/common/src/com/android/tv/common/feature/TestableFeature.java b/common/src/com/android/tv/common/feature/TestableFeature.java
index d7e707a1..1f18639d 100644
--- a/common/src/com/android/tv/common/feature/TestableFeature.java
+++ b/common/src/com/android/tv/common/feature/TestableFeature.java
@@ -19,33 +19,28 @@ package com.android.tv.common.feature;
import android.content.Context;
import android.support.annotation.VisibleForTesting;
import android.util.Log;
-
-import com.android.tv.common.TvCommonUtils;
+import com.android.tv.common.util.CommonUtils;
/**
* When run in a test harness this feature can be turned on or off, overriding the normal value.
*
- * <p><b>Warning</b> making a feature testable will cause the code to stay in the APK and
- * could leak unreleased features.
+ * <p><b>Warning</b> making a feature testable will cause the code to stay in the APK and could leak
+ * unreleased features.
*/
public class TestableFeature implements Feature {
- private final static String TAG = "TestableFeature";
- private final static String DETAIL_MESSAGE
- = "TestableFeatures should only be changed in tests.";
+ private static final String TAG = "TestableFeature";
+ private static final String DETAIL_MESSAGE =
+ "TestableFeatures should only be changed in tests.";
private final Feature mDelegate;
private Boolean mTestValue = null;
- /**
- * Creates testable feature.
- */
+ /** Creates testable feature. */
public static TestableFeature createTestableFeature(Feature delegate) {
return new TestableFeature(delegate);
}
- /**
- * Creates testable feature with initial value.
- */
+ /** Creates testable feature with initial value. */
public static TestableFeature createTestableFeature(Feature delegate, Boolean initialValue) {
return new TestableFeature(delegate, initialValue);
}
@@ -61,9 +56,8 @@ public class TestableFeature implements Feature {
@VisibleForTesting
public void enableForTest() {
- if (!TvCommonUtils.isRunningInTest()) {
- Log.e(TAG, "Not enabling for test:" + this,
- new IllegalStateException(DETAIL_MESSAGE));
+ if (!CommonUtils.isRunningInTest()) {
+ Log.e(TAG, "Not enabling for test:" + this, new IllegalStateException(DETAIL_MESSAGE));
} else {
mTestValue = true;
}
@@ -71,8 +65,10 @@ public class TestableFeature implements Feature {
@VisibleForTesting
public void disableForTests() {
- if (!TvCommonUtils.isRunningInTest()) {
- Log.e(TAG, "Not disabling for test: " + this,
+ if (!CommonUtils.isRunningInTest()) {
+ Log.e(
+ TAG,
+ "Not disabling for test: " + this,
new IllegalStateException(DETAIL_MESSAGE));
} else {
mTestValue = false;
@@ -81,7 +77,7 @@ public class TestableFeature implements Feature {
@VisibleForTesting
public void resetForTests() {
- if (!TvCommonUtils.isRunningInTest()) {
+ if (!CommonUtils.isRunningInTest()) {
Log.e(TAG, "Not resetting feature: " + this, new IllegalStateException(DETAIL_MESSAGE));
} else {
mTestValue = null;
@@ -90,7 +86,7 @@ public class TestableFeature implements Feature {
@Override
public boolean isEnabled(Context context) {
- if (TvCommonUtils.isRunningInTest() && mTestValue != null) {
+ if (CommonUtils.isRunningInTest() && mTestValue != null) {
return mTestValue;
}
return mDelegate.isEnabled(context);
@@ -99,7 +95,7 @@ public class TestableFeature implements Feature {
@Override
public String toString() {
String msg = mDelegate.toString();
- if (TvCommonUtils.isRunningInTest()) {
+ if (CommonUtils.isRunningInTest()) {
if (mTestValue == null) {
msg = "Testable Feature is unchanged: " + msg;
} else {
diff --git a/common/src/com/android/tv/common/MemoryManageable.java b/common/src/com/android/tv/common/memory/MemoryManageable.java
index 0cb36103..3e81fb5e 100644
--- a/common/src/com/android/tv/common/MemoryManageable.java
+++ b/common/src/com/android/tv/common/memory/MemoryManageable.java
@@ -14,16 +14,13 @@
* limitations under the License
*/
-package com.android.tv.common;
+package com.android.tv.common.memory;
/**
- * Interface for the fine-grained memory management.
- * The class which wants to release memory based on the system constraints should inherit
- * this interface and implement {@link #performTrimMemory}.
+ * Interface for the fine-grained memory management. The class which wants to release memory based
+ * on the system constraints should inherit this interface and implement {@link #performTrimMemory}.
*/
public interface MemoryManageable {
- /**
- * For more information, see {@link android.content.ComponentCallbacks2#onTrimMemory}.
- */
+ /** For more information, see {@link android.content.ComponentCallbacks2#onTrimMemory}. */
void performTrimMemory(int level);
}
diff --git a/common/src/com/android/tv/common/recording/RecordingCapability.java b/common/src/com/android/tv/common/recording/RecordingCapability.java
index 266fd271..9fef527c 100644
--- a/common/src/com/android/tv/common/recording/RecordingCapability.java
+++ b/common/src/com/android/tv/common/recording/RecordingCapability.java
@@ -18,16 +18,11 @@ package com.android.tv.common.recording;
import android.os.Parcel;
import android.os.Parcelable;
-
import java.util.Objects;
-/**
- * Static representation of the recording capability of a TvInputService.
- */
-public final class RecordingCapability implements Parcelable{
- /**
- * The inputId this capability represents.
- */
+/** Static representation of the recording capability of a TvInputService. */
+public final class RecordingCapability implements Parcelable {
+ /** The inputId this capability represents. */
public final String inputId;
/**
@@ -40,8 +35,8 @@ public final class RecordingCapability implements Parcelable{
/**
* The max number concurrent session that play a stream.
*
- *<p>This is often limited by the number of decoders available.
- * The count includes both playing live TV and playing a recorded stream.
+ * <p>This is often limited by the number of decoders available. The count includes both playing
+ * live TV and playing a recorded stream.
*/
public final int maxConcurrentPlayingSessions;
@@ -52,13 +47,14 @@ public final class RecordingCapability implements Parcelable{
*/
public final int maxConcurrentSessionsOfAllTypes;
- /**
- * True if a tuned session can support recording and playback from the same resource.
- */
+ /** True if a tuned session can support recording and playback from the same resource. */
public final boolean playbackWhileRecording;
- private RecordingCapability(String inputId, int maxConcurrentTunedSessions,
- int maxConcurrentPlayingSessions, int maxConcurrentSessionsOfAllTypes,
+ private RecordingCapability(
+ String inputId,
+ int maxConcurrentTunedSessions,
+ int maxConcurrentPlayingSessions,
+ int maxConcurrentSessionsOfAllTypes,
boolean playbackWhileRecording) {
this.inputId = inputId;
this.maxConcurrentTunedSessions = maxConcurrentTunedSessions;
@@ -93,12 +89,12 @@ public final class RecordingCapability implements Parcelable{
return false;
}
RecordingCapability that = (RecordingCapability) o;
- return Objects.equals(maxConcurrentTunedSessions, that.maxConcurrentTunedSessions) &&
- Objects.equals(maxConcurrentPlayingSessions, that.maxConcurrentPlayingSessions) &&
- Objects.equals(maxConcurrentSessionsOfAllTypes,
- that.maxConcurrentSessionsOfAllTypes) &&
- Objects.equals(playbackWhileRecording, that.playbackWhileRecording) &&
- Objects.equals(inputId, that.inputId);
+ return Objects.equals(maxConcurrentTunedSessions, that.maxConcurrentTunedSessions)
+ && Objects.equals(maxConcurrentPlayingSessions, that.maxConcurrentPlayingSessions)
+ && Objects.equals(
+ maxConcurrentSessionsOfAllTypes, that.maxConcurrentSessionsOfAllTypes)
+ && Objects.equals(playbackWhileRecording, that.playbackWhileRecording)
+ && Objects.equals(inputId, that.inputId);
}
@Override
@@ -108,13 +104,19 @@ public final class RecordingCapability implements Parcelable{
@Override
public String toString() {
- return "RecordingCapability{" +
- "inputId='" + inputId + '\'' +
- ", maxConcurrentTunedSessions=" + maxConcurrentTunedSessions +
- ", maxConcurrentPlayingSessions=" + maxConcurrentPlayingSessions +
- ", maxConcurrentSessionsOfAllTypes=" + maxConcurrentSessionsOfAllTypes +
- ", playbackWhileRecording=" + playbackWhileRecording +
- '}';
+ return "RecordingCapability{"
+ + "inputId='"
+ + inputId
+ + '\''
+ + ", maxConcurrentTunedSessions="
+ + maxConcurrentTunedSessions
+ + ", maxConcurrentPlayingSessions="
+ + maxConcurrentPlayingSessions
+ + ", maxConcurrentSessionsOfAllTypes="
+ + maxConcurrentSessionsOfAllTypes
+ + ", playbackWhileRecording="
+ + playbackWhileRecording
+ + '}';
}
@Override
@@ -122,17 +124,18 @@ public final class RecordingCapability implements Parcelable{
return 0;
}
- public static final Creator<RecordingCapability> CREATOR = new Creator<RecordingCapability>() {
- @Override
- public RecordingCapability createFromParcel(Parcel in) {
- return new RecordingCapability(in);
- }
+ public static final Creator<RecordingCapability> CREATOR =
+ new Creator<RecordingCapability>() {
+ @Override
+ public RecordingCapability createFromParcel(Parcel in) {
+ return new RecordingCapability(in);
+ }
- @Override
- public RecordingCapability[] newArray(int size) {
- return new RecordingCapability[size];
- }
- };
+ @Override
+ public RecordingCapability[] newArray(int size) {
+ return new RecordingCapability[size];
+ }
+ };
public static Builder builder() {
return new Builder();
@@ -171,11 +174,12 @@ public final class RecordingCapability implements Parcelable{
}
public RecordingCapability build() {
- return new RecordingCapability(mInputId, mMaxConcurrentTunedSessions,
- mMaxConcurrentPlayingSessions, mMaxConcurrentSessionsOfAllTypes,
+ return new RecordingCapability(
+ mInputId,
+ mMaxConcurrentTunedSessions,
+ mMaxConcurrentPlayingSessions,
+ mMaxConcurrentSessionsOfAllTypes,
mPlaybackWhileRecording);
}
}
}
-
-
diff --git a/common/src/com/android/tv/common/recording/RecordingStorageStatusManager.java b/common/src/com/android/tv/common/recording/RecordingStorageStatusManager.java
new file mode 100644
index 00000000..8b45a730
--- /dev/null
+++ b/common/src/com/android/tv/common/recording/RecordingStorageStatusManager.java
@@ -0,0 +1,256 @@
+/*
+ * 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 android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Environment;
+import android.os.Looper;
+import android.os.StatFs;
+import android.support.annotation.AnyThread;
+import android.support.annotation.IntDef;
+import android.support.annotation.WorkerThread;
+import android.util.Log;
+import com.android.tv.common.SoftPreconditions;
+import com.android.tv.common.feature.CommonFeatures;
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+/** Signals DVR storage status change such as plugging/unplugging. */
+public class RecordingStorageStatusManager {
+ private static final String TAG = "RecordingStorageStatusManager";
+ private static final boolean DEBUG = false;
+
+ /** Minimum storage size to support DVR */
+ public static final long MIN_STORAGE_SIZE_FOR_DVR_IN_BYTES = 50 * 1024 * 1024 * 1024L; // 50GB
+
+ private static final long MIN_FREE_STORAGE_SIZE_FOR_DVR_IN_BYTES =
+ 10 * 1024 * 1024 * 1024L; // 10GB
+ private static final String RECORDING_DATA_SUB_PATH = "/recording";
+
+ /** Storage status constants. */
+ @IntDef({
+ STORAGE_STATUS_OK,
+ STORAGE_STATUS_TOTAL_CAPACITY_TOO_SMALL,
+ STORAGE_STATUS_FREE_SPACE_INSUFFICIENT,
+ STORAGE_STATUS_MISSING
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StorageStatus {}
+
+ /** Current storage is OK to record a program. */
+ public static final int STORAGE_STATUS_OK = 0;
+
+ /** Current storage's total capacity is smaller than DVR requirement. */
+ public static final int STORAGE_STATUS_TOTAL_CAPACITY_TOO_SMALL = 1;
+
+ /** Current storage's free space is insufficient to record programs. */
+ public static final int STORAGE_STATUS_FREE_SPACE_INSUFFICIENT = 2;
+
+ /** Current storage is missing. */
+ public static final int STORAGE_STATUS_MISSING = 3;
+
+ private final Context mContext;
+ private final Set<OnStorageMountChangedListener> mOnStorageMountChangedListeners =
+ new CopyOnWriteArraySet<>();
+ private MountedStorageStatus mMountedStorageStatus;
+ private boolean mStorageValid;
+
+ private class MountedStorageStatus {
+ private final boolean mStorageMounted;
+ private final File mStorageMountedDir;
+ private final long mStorageMountedCapacity;
+
+ private MountedStorageStatus(boolean mounted, File mountedDir, long capacity) {
+ mStorageMounted = mounted;
+ mStorageMountedDir = mountedDir;
+ mStorageMountedCapacity = capacity;
+ }
+
+ private boolean isValidForDvr() {
+ return mStorageMounted && mStorageMountedCapacity >= MIN_STORAGE_SIZE_FOR_DVR_IN_BYTES;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof MountedStorageStatus)) {
+ return false;
+ }
+ MountedStorageStatus status = (MountedStorageStatus) other;
+ return mStorageMounted == status.mStorageMounted
+ && Objects.equals(mStorageMountedDir, status.mStorageMountedDir)
+ && mStorageMountedCapacity == status.mStorageMountedCapacity;
+ }
+ }
+
+ public interface OnStorageMountChangedListener {
+
+ /**
+ * Listener for DVR storage status change.
+ *
+ * @param storageMounted {@code true} when DVR possible storage is mounted, {@code false}
+ * otherwise.
+ */
+ void onStorageMountChanged(boolean storageMounted);
+ }
+
+ private final class StorageStatusBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ MountedStorageStatus result = getStorageStatusInternal();
+ if (mMountedStorageStatus.equals(result)) {
+ return;
+ }
+ mMountedStorageStatus = result;
+ if (result.mStorageMounted) {
+ cleanUpDbIfNeeded();
+ }
+ boolean valid = result.isValidForDvr();
+ if (valid == mStorageValid) {
+ return;
+ }
+ mStorageValid = valid;
+ for (OnStorageMountChangedListener l : mOnStorageMountChangedListeners) {
+ l.onStorageMountChanged(valid);
+ }
+ }
+ }
+
+ /**
+ * Creates RecordingStorageStatusManager.
+ *
+ * @param context {@link Context}
+ */
+ public RecordingStorageStatusManager(final Context context) {
+ mContext = context;
+ mMountedStorageStatus = getStorageStatusInternal();
+ mStorageValid = mMountedStorageStatus.isValidForDvr();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_MEDIA_MOUNTED);
+ filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
+ filter.addAction(Intent.ACTION_MEDIA_EJECT);
+ filter.addAction(Intent.ACTION_MEDIA_REMOVED);
+ filter.addAction(Intent.ACTION_MEDIA_BAD_REMOVAL);
+ filter.addDataScheme(ContentResolver.SCHEME_FILE);
+ mContext.registerReceiver(new StorageStatusBroadcastReceiver(), filter);
+ }
+
+ /**
+ * Adds the listener for receiving storage status change.
+ *
+ * @param listener
+ */
+ public void addListener(OnStorageMountChangedListener listener) {
+ mOnStorageMountChangedListeners.add(listener);
+ }
+
+ /** Removes the current listener. */
+ public void removeListener(OnStorageMountChangedListener listener) {
+ mOnStorageMountChangedListeners.remove(listener);
+ }
+
+ /** Returns true if a storage is mounted. */
+ public boolean isStorageMounted() {
+ return mMountedStorageStatus.mStorageMounted;
+ }
+
+ /** Returns the path to DVR recording data directory. This can take for a while sometimes. */
+ @WorkerThread
+ public File getRecordingRootDataDirectory() {
+ SoftPreconditions.checkState(Looper.myLooper() != Looper.getMainLooper());
+ if (mMountedStorageStatus.mStorageMountedDir == null) {
+ return null;
+ }
+ File root = mContext.getExternalFilesDir(null);
+ String rootPath;
+ try {
+ rootPath = root != null ? root.getCanonicalPath() : null;
+ } catch (IOException | SecurityException e) {
+ return null;
+ }
+ return rootPath == null ? null : new File(rootPath + RECORDING_DATA_SUB_PATH);
+ }
+
+ /**
+ * Returns the current storage status for DVR recordings.
+ *
+ * @return {@link StorageStatus}
+ */
+ @AnyThread
+ public @StorageStatus int getDvrStorageStatus() {
+ MountedStorageStatus status = mMountedStorageStatus;
+ if (status.mStorageMountedDir == null) {
+ return STORAGE_STATUS_MISSING;
+ }
+ if (CommonFeatures.FORCE_RECORDING_UNTIL_NO_SPACE.isEnabled(mContext)) {
+ return STORAGE_STATUS_OK;
+ }
+ if (status.mStorageMountedCapacity < MIN_STORAGE_SIZE_FOR_DVR_IN_BYTES) {
+ return STORAGE_STATUS_TOTAL_CAPACITY_TOO_SMALL;
+ }
+ try {
+ StatFs statFs = new StatFs(status.mStorageMountedDir.toString());
+ if (statFs.getAvailableBytes() < MIN_FREE_STORAGE_SIZE_FOR_DVR_IN_BYTES) {
+ return STORAGE_STATUS_FREE_SPACE_INSUFFICIENT;
+ }
+ } catch (IllegalArgumentException e) {
+ // In rare cases, storage status change was not notified yet.
+ SoftPreconditions.checkState(false);
+ return STORAGE_STATUS_FREE_SPACE_INSUFFICIENT;
+ }
+ return STORAGE_STATUS_OK;
+ }
+
+ /**
+ * Returns whether the storage has sufficient storage.
+ *
+ * @return {@code true} when there is sufficient storage, {@code false} otherwise
+ */
+ public boolean isStorageSufficient() {
+ return getDvrStorageStatus() == STORAGE_STATUS_OK;
+ }
+
+ /** APPs that want to clean up DB for recordings should override this method to do the job. */
+ protected void cleanUpDbIfNeeded() {}
+
+ private MountedStorageStatus getStorageStatusInternal() {
+ boolean storageMounted =
+ Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
+ File storageMountedDir = storageMounted ? Environment.getExternalStorageDirectory() : null;
+ storageMounted = storageMounted && storageMountedDir != null;
+ long storageMountedCapacity = 0L;
+ if (storageMounted) {
+ try {
+ StatFs statFs = new StatFs(storageMountedDir.toString());
+ storageMountedCapacity = statFs.getTotalBytes();
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Storage mount status was changed.");
+ storageMounted = false;
+ storageMountedDir = null;
+ }
+ }
+ return new MountedStorageStatus(storageMounted, storageMountedDir, storageMountedCapacity);
+ }
+}
diff --git a/common/src/com/android/tv/common/ui/setup/OnActionClickListener.java b/common/src/com/android/tv/common/ui/setup/OnActionClickListener.java
index 392d489f..6f088c0b 100644
--- a/common/src/com/android/tv/common/ui/setup/OnActionClickListener.java
+++ b/common/src/com/android/tv/common/ui/setup/OnActionClickListener.java
@@ -18,14 +18,12 @@ package com.android.tv.common.ui.setup;
import android.os.Bundle;
-/**
- * A listener for the action click.
- */
+/** A listener for the action click. */
public interface OnActionClickListener {
/**
* Called when the action is clicked.
- * <p>
- * The method should return {@code true} if the action is handled, otherwise {@code false}.
+ *
+ * <p>The method should return {@code true} if the action is handled, otherwise {@code false}.
*
* @param category The action category.
* @param id The action id.
diff --git a/common/src/com/android/tv/common/ui/setup/SetupActionHelper.java b/common/src/com/android/tv/common/ui/setup/SetupActionHelper.java
index 7ee06faf..8a7dbd70 100644
--- a/common/src/com/android/tv/common/ui/setup/SetupActionHelper.java
+++ b/common/src/com/android/tv/common/ui/setup/SetupActionHelper.java
@@ -22,46 +22,45 @@ import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
-/**
- * Helper class for the execution in the fragment.
- */
+/** Helper class for the execution in the fragment. */
public class SetupActionHelper {
private static final String TAG = "SetupActionHelper";
- /**
- * Executes the action.
- */
+ /** Executes the action. */
public static boolean onActionClick(Fragment fragment, String category, int actionId) {
return onActionClick(fragment, category, actionId, null);
}
- /**
- * Executes the action.
- */
- public static boolean onActionClick(Fragment fragment, String category, int actionId,
- Bundle params) {
+ /** Executes the action. */
+ public static boolean onActionClick(
+ Fragment fragment, String category, int actionId, Bundle params) {
if (fragment.getActivity() instanceof OnActionClickListener) {
- return ((OnActionClickListener) fragment.getActivity()).onActionClick(category,
- actionId, params);
+ return ((OnActionClickListener) fragment.getActivity())
+ .onActionClick(category, actionId, params);
}
- Log.e(TAG, "Activity can't handle the action: {category=" + category + ", actionId="
- + actionId + ", params=" + params + "}");
+ Log.e(
+ TAG,
+ "Activity can't handle the action: {category="
+ + category
+ + ", actionId="
+ + actionId
+ + ", params="
+ + params
+ + "}");
return false;
}
- /**
- * Creates an {@link OnClickListener} to handle the action.
- */
- public static OnClickListener createOnClickListenerForAction(Fragment fragment, String category,
- int actionId, Bundle params) {
+ /** Creates an {@link OnClickListener} to handle the action. */
+ public static OnClickListener createOnClickListenerForAction(
+ Fragment fragment, String category, int actionId, Bundle params) {
return new OnActionClickListenerForAction(fragment, category, actionId, params);
}
/**
* The {@link OnClickListener} for the view.
- * <p>
- * Note that this class should be used only for the views in the {@code mFragment} to avoid the
- * leak of mFragment.
+ *
+ * <p>Note that this class should be used only for the views in the {@code mFragment} to avoid
+ * the leak of mFragment.
*/
private static class OnActionClickListenerForAction implements OnClickListener {
private final Fragment mFragment;
@@ -69,8 +68,8 @@ public class SetupActionHelper {
private final int mActionId;
private final Bundle mParams;
- OnActionClickListenerForAction(Fragment fragment, String category, int actionId,
- Bundle params) {
+ OnActionClickListenerForAction(
+ Fragment fragment, String category, int actionId, Bundle params) {
mFragment = fragment;
mCategory = category;
mActionId = actionId;
@@ -83,5 +82,5 @@ public class SetupActionHelper {
}
}
- private SetupActionHelper() { }
+ private SetupActionHelper() {}
}
diff --git a/common/src/com/android/tv/common/ui/setup/SetupActivity.java b/common/src/com/android/tv/common/ui/setup/SetupActivity.java
index 2b381a6e..67418ce0 100644
--- a/common/src/com/android/tv/common/ui/setup/SetupActivity.java
+++ b/common/src/com/android/tv/common/ui/setup/SetupActivity.java
@@ -28,7 +28,6 @@ import android.transition.Transition;
import android.transition.TransitionInflater;
import android.view.View;
import android.view.ViewTreeObserver.OnPreDrawListener;
-
import com.android.tv.common.R;
import com.android.tv.common.WeakHandler;
import com.android.tv.common.ui.setup.animation.SetupAnimationHelper;
@@ -51,23 +50,28 @@ public abstract class SetupActivity extends Activity implements OnActionClickLis
super.onCreate(savedInstanceState);
SetupAnimationHelper.initialize(this);
setContentView(R.layout.activity_setup);
- mFragmentTransitionDuration = getResources().getInteger(
- R.integer.setup_fragment_transition_duration);
+ mFragmentTransitionDuration =
+ getResources().getInteger(R.integer.setup_fragment_transition_duration);
// Show initial fragment only when the saved state is not restored, because the last
// fragment is restored if savesInstanceState is not null.
if (savedInstanceState == null) {
// This is the workaround to show the first fragment with delay to show the fragment
// enter transition. See http://b/26255145
- getWindow().getDecorView().getViewTreeObserver().addOnPreDrawListener(
- new OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- getWindow().getDecorView().getViewTreeObserver()
- .removeOnPreDrawListener(this);
- showInitialFragment();
- return true;
- }
- });
+ getWindow()
+ .getDecorView()
+ .getViewTreeObserver()
+ .addOnPreDrawListener(
+ new OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ getWindow()
+ .getDecorView()
+ .getViewTreeObserver()
+ .removeOnPreDrawListener(this);
+ showInitialFragment();
+ return true;
+ }
+ });
} else {
mShowInitialFragment = false;
}
@@ -76,8 +80,8 @@ public abstract class SetupActivity extends Activity implements OnActionClickLis
/**
* The inherited class should provide the initial fragment to show.
*
- * <p>If this method returns {@code null} during {@link #onCreate}, then call
- * {@link #showInitialFragment} explicitly later with non null initial fragment.
+ * <p>If this method returns {@code null} during {@link #onCreate}, then call {@link
+ * #showInitialFragment} explicitly later with non null initial fragment.
*/
protected abstract Fragment onCreateInitialFragment();
@@ -98,16 +102,15 @@ public abstract class SetupActivity extends Activity implements OnActionClickLis
}
}
- /**
- * Shows the given fragment.
- */
+ /** Shows the given fragment. */
protected FragmentTransaction showFragment(Fragment fragment, boolean addToBackStack) {
FragmentTransaction ft = getFragmentManager().beginTransaction();
if (fragment instanceof SetupFragment) {
int[] sharedElements = ((SetupFragment) fragment).getSharedElementIds();
if (sharedElements != null && sharedElements.length > 0) {
- Transition sharedTransition = TransitionInflater.from(this)
- .inflateTransition(R.transition.transition_action_background);
+ Transition sharedTransition =
+ TransitionInflater.from(this)
+ .inflateTransition(R.transition.transition_action_background);
sharedTransition.setDuration(getSharedElementTransitionDuration());
SetupAnimationHelper.applyAnimationTimeScale(sharedTransition);
fragment.setSharedElementEnterTransition(sharedTransition);
@@ -143,9 +146,9 @@ public abstract class SetupActivity extends Activity implements OnActionClickLis
/**
* Override this method if the inherited class wants to handle the action.
- * <p>
- * The override method should return {@code true} if the action is handled, otherwise
- * {@code false}.
+ *
+ * <p>The override method should return {@code true} if the action is handled, otherwise {@code
+ * false}.
*/
protected boolean executeAction(String category, int actionId, Bundle params) {
return false;
diff --git a/common/src/com/android/tv/common/ui/setup/SetupFragment.java b/common/src/com/android/tv/common/ui/setup/SetupFragment.java
index d2b9d7c8..7d47548d 100644
--- a/common/src/com/android/tv/common/ui/setup/SetupFragment.java
+++ b/common/src/com/android/tv/common/ui/setup/SetupFragment.java
@@ -26,22 +26,25 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-
import com.android.tv.common.ui.setup.animation.FadeAndShortSlide;
import com.android.tv.common.ui.setup.animation.SetupAnimationHelper;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-/**
- * A fragment which slides when it is entering/exiting.
- */
+/** A fragment which slides when it is entering/exiting. */
public abstract class SetupFragment extends Fragment {
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true,
- value = {FRAGMENT_ENTER_TRANSITION, FRAGMENT_EXIT_TRANSITION,
- FRAGMENT_REENTER_TRANSITION, FRAGMENT_RETURN_TRANSITION})
+ @IntDef(
+ flag = true,
+ value = {
+ FRAGMENT_ENTER_TRANSITION,
+ FRAGMENT_EXIT_TRANSITION,
+ FRAGMENT_REENTER_TRANSITION,
+ FRAGMENT_RETURN_TRANSITION
+ }
+ )
public @interface FragmentTransitionType {}
+
public static final int FRAGMENT_ENTER_TRANSITION = 0x01;
public static final int FRAGMENT_EXIT_TRANSITION = FRAGMENT_ENTER_TRANSITION << 1;
public static final int FRAGMENT_REENTER_TRANSITION = FRAGMENT_ENTER_TRANSITION << 2;
@@ -49,50 +52,50 @@ public abstract class SetupFragment extends Fragment {
private boolean mEnterTransitionRunning;
- private final TransitionListener mTransitionListener = new TransitionListener() {
- @Override
- public void onTransitionStart(Transition transition) {
- mEnterTransitionRunning = true;
- }
+ private final TransitionListener mTransitionListener =
+ new TransitionListener() {
+ @Override
+ public void onTransitionStart(Transition transition) {
+ mEnterTransitionRunning = true;
+ }
- @Override
- public void onTransitionEnd(Transition transition) {
- mEnterTransitionRunning = false;
- onEnterTransitionEnd();
- }
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ mEnterTransitionRunning = false;
+ onEnterTransitionEnd();
+ }
- @Override
- public void onTransitionCancel(Transition transition) { }
+ @Override
+ public void onTransitionCancel(Transition transition) {}
- @Override
- public void onTransitionPause(Transition transition) { }
+ @Override
+ public void onTransitionPause(Transition transition) {}
- @Override
- public void onTransitionResume(Transition transition) { }
- };
+ @Override
+ public void onTransitionResume(Transition transition) {}
+ };
- /**
- * Returns {@code true} if the enter/reenter transition is running.
- */
+ /** Returns {@code true} if the enter/reenter transition is running. */
protected boolean isEnterTransitionRunning() {
return mEnterTransitionRunning;
}
- /**
- * Called when the enter/reenter transition ends.
- */
- protected void onEnterTransitionEnd() { }
+ /** Called when the enter/reenter transition ends. */
+ protected void onEnterTransitionEnd() {}
public SetupFragment() {
setAllowEnterTransitionOverlap(false);
setAllowReturnTransitionOverlap(false);
- enableFragmentTransition(FRAGMENT_ENTER_TRANSITION | FRAGMENT_EXIT_TRANSITION
- | FRAGMENT_REENTER_TRANSITION | FRAGMENT_RETURN_TRANSITION);
+ enableFragmentTransition(
+ FRAGMENT_ENTER_TRANSITION
+ | FRAGMENT_EXIT_TRANSITION
+ | FRAGMENT_REENTER_TRANSITION
+ | FRAGMENT_RETURN_TRANSITION);
}
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(getLayoutResourceId(), container, false);
// After the transition animation, we need to request the focus. If not, this fragment
// doesn't have the focus.
@@ -100,18 +103,17 @@ public abstract class SetupFragment extends Fragment {
return view;
}
- /**
- * Returns the layout resource ID for this fragment.
- */
- abstract protected int getLayoutResourceId();
+ /** Returns the layout resource ID for this fragment. */
+ protected abstract int getLayoutResourceId();
protected void setOnClickAction(View view, final String category, final int actionId) {
- view.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View view) {
- onActionClick(category, actionId);
- }
- });
+ view.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ onActionClick(category, actionId);
+ }
+ });
}
protected boolean onActionClick(String category, int actionId) {
@@ -141,24 +143,22 @@ public abstract class SetupFragment extends Fragment {
/**
* Enables fragment transition according to the given {@code mask}.
*
- * @param mask This value is the combination of {@link #FRAGMENT_ENTER_TRANSITION},
- * {@link #FRAGMENT_EXIT_TRANSITION}, {@link #FRAGMENT_REENTER_TRANSITION}, and
- * {@link #FRAGMENT_RETURN_TRANSITION}.
+ * @param mask This value is the combination of {@link #FRAGMENT_ENTER_TRANSITION}, {@link
+ * #FRAGMENT_EXIT_TRANSITION}, {@link #FRAGMENT_REENTER_TRANSITION}, and {@link
+ * #FRAGMENT_RETURN_TRANSITION}.
*/
public void enableFragmentTransition(@FragmentTransitionType int mask) {
- setEnterTransition((mask & FRAGMENT_ENTER_TRANSITION) == 0 ? null
- : createTransition(Gravity.END));
- setExitTransition((mask & FRAGMENT_EXIT_TRANSITION) == 0 ? null
- : createTransition(Gravity.START));
- setReenterTransition((mask & FRAGMENT_REENTER_TRANSITION) == 0 ? null
- : createTransition(Gravity.START));
- setReturnTransition((mask & FRAGMENT_RETURN_TRANSITION) == 0 ? null
- : createTransition(Gravity.END));
+ setEnterTransition(
+ (mask & FRAGMENT_ENTER_TRANSITION) == 0 ? null : createTransition(Gravity.END));
+ setExitTransition(
+ (mask & FRAGMENT_EXIT_TRANSITION) == 0 ? null : createTransition(Gravity.START));
+ setReenterTransition(
+ (mask & FRAGMENT_REENTER_TRANSITION) == 0 ? null : createTransition(Gravity.START));
+ setReturnTransition(
+ (mask & FRAGMENT_RETURN_TRANSITION) == 0 ? null : createTransition(Gravity.END));
}
- /**
- * Sets the transition with the given {@code slidEdge}.
- */
+ /** Sets the transition with the given {@code slidEdge}. */
public void setFragmentTransition(@FragmentTransitionType int transitionType, int slideEdge) {
switch (transitionType) {
case FRAGMENT_ENTER_TRANSITION:
@@ -184,9 +184,7 @@ public abstract class SetupFragment extends Fragment {
.build();
}
- /**
- * Changes the move distance of the transitions to short distance.
- */
+ /** Changes the move distance of the transitions to short distance. */
public void setShortDistance(@FragmentTransitionType int mask) {
if ((mask & FRAGMENT_ENTER_TRANSITION) != 0) {
Transition transition = getEnterTransition();
@@ -218,7 +216,7 @@ public abstract class SetupFragment extends Fragment {
* Returns the ID's of the view's whose descendants will perform delayed move.
*
* @see com.android.tv.common.ui.setup.animation.SetupAnimationHelper.TransitionBuilder
- * #setParentIdsForDelay
+ * #setParentIdsForDelay
*/
protected int[] getParentIdsForDelay() {
return null;
@@ -228,7 +226,7 @@ public abstract class SetupFragment extends Fragment {
* Sets the ID's of the views which will not be included in the transition.
*
* @see com.android.tv.common.ui.setup.animation.SetupAnimationHelper.TransitionBuilder
- * #setExcludeIds
+ * #setExcludeIds
*/
protected int[] getExcludedTargetIds() {
return null;
diff --git a/common/src/com/android/tv/common/ui/setup/SetupGuidedStepFragment.java b/common/src/com/android/tv/common/ui/setup/SetupGuidedStepFragment.java
index 7bce9c27..3c76c269 100644
--- a/common/src/com/android/tv/common/ui/setup/SetupGuidedStepFragment.java
+++ b/common/src/com/android/tv/common/ui/setup/SetupGuidedStepFragment.java
@@ -16,22 +16,26 @@
package com.android.tv.common.ui.setup;
+import static android.content.Context.ACCESSIBILITY_SERVICE;
+
import android.os.Bundle;
import android.support.v17.leanback.app.GuidedStepFragment;
import android.support.v17.leanback.widget.GuidanceStylist;
import android.support.v17.leanback.widget.GuidedAction;
+import android.support.v17.leanback.widget.GuidedActionsStylist;
import android.support.v17.leanback.widget.VerticalGridView;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.View.AccessibilityDelegate;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.LinearLayout;
-
import com.android.tv.common.R;
-/**
- * A fragment for channel source info/setup.
- */
+/** A fragment for channel source info/setup. */
public abstract class SetupGuidedStepFragment extends GuidedStepFragment {
/**
* Key of the argument which indicate whether the parent of this fragment has three panes.
@@ -40,23 +44,30 @@ public abstract class SetupGuidedStepFragment extends GuidedStepFragment {
*/
public static final String KEY_THREE_PANE = "key_three_pane";
+ private View mContentFragment;
+ private boolean mFromContentFragment;
+ private boolean mAccessibilityMode;
+
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
Bundle arguments = getArguments();
view.findViewById(android.support.v17.leanback.R.id.action_fragment_root)
.setPadding(0, 0, 0, 0);
- LinearLayout.LayoutParams guidanceLayoutParams = (LinearLayout.LayoutParams)
- view.findViewById(android.support.v17.leanback.R.id.content_fragment)
- .getLayoutParams();
+ mContentFragment = view.findViewById(android.support.v17.leanback.R.id.content_fragment);
+ LinearLayout.LayoutParams guidanceLayoutParams =
+ (LinearLayout.LayoutParams) mContentFragment.getLayoutParams();
guidanceLayoutParams.weight = 0;
if (arguments != null && arguments.getBoolean(KEY_THREE_PANE, false)) {
// Content fragment.
- guidanceLayoutParams.width = getResources().getDimensionPixelOffset(
- R.dimen.setup_guidedstep_guidance_section_width_3pane);
- int doneButtonWidth = getResources().getDimensionPixelOffset(
- R.dimen.setup_done_button_container_width);
+ guidanceLayoutParams.width =
+ getResources()
+ .getDimensionPixelOffset(
+ R.dimen.setup_guidedstep_guidance_section_width_3pane);
+ int doneButtonWidth =
+ getResources()
+ .getDimensionPixelOffset(R.dimen.setup_done_button_container_width);
// Guided actions list
View list = view.findViewById(android.support.v17.leanback.R.id.guidedactions_list);
MarginLayoutParams marginLayoutParams = (MarginLayoutParams) list.getLayoutParams();
@@ -69,13 +80,16 @@ public abstract class SetupGuidedStepFragment extends GuidedStepFragment {
}
} else {
// Content fragment.
- guidanceLayoutParams.width = getResources().getDimensionPixelOffset(
- R.dimen.setup_guidedstep_guidance_section_width_2pane);
+ guidanceLayoutParams.width =
+ getResources()
+ .getDimensionPixelOffset(
+ R.dimen.setup_guidedstep_guidance_section_width_2pane);
}
// gridView Alignment
VerticalGridView gridView = getGuidedActionsStylist().getActionsGridView();
- int offset = getResources().getDimensionPixelOffset(
- R.dimen.setup_guidedactions_selector_margin_top);
+ int offset =
+ getResources()
+ .getDimensionPixelOffset(R.dimen.setup_guidedactions_selector_margin_top);
gridView.setWindowAlignmentOffset(offset);
gridView.setWindowAlignmentOffsetPercent(0);
gridView.setItemAlignmentOffsetPercent(0);
@@ -83,19 +97,51 @@ public abstract class SetupGuidedStepFragment extends GuidedStepFragment {
.setTransitionGroup(false);
// Needed for the shared element transition.
// content_frame is defined in leanback.
- ViewGroup group = (ViewGroup) view.findViewById(
- android.support.v17.leanback.R.id.content_frame);
+ ViewGroup group =
+ (ViewGroup) view.findViewById(android.support.v17.leanback.R.id.content_frame);
group.setClipChildren(false);
group.setClipToPadding(false);
return view;
}
@Override
+ public GuidedActionsStylist onCreateActionsStylist() {
+ return new SetupGuidedStepFragmentGuidedActionsStylist();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ AccessibilityManager am =
+ (AccessibilityManager) getActivity().getSystemService(ACCESSIBILITY_SERVICE);
+ mAccessibilityMode = am != null && am.isEnabled() && am.isTouchExplorationEnabled();
+ mContentFragment.setFocusable(mAccessibilityMode);
+ if (mAccessibilityMode) {
+ mContentFragment.setAccessibilityDelegate(
+ new AccessibilityDelegate() {
+ @Override
+ public boolean performAccessibilityAction(View host, int action, Bundle args) {
+ if (action == AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS
+ && !getActions().isEmpty()) {
+ // scroll to the top. This makes the first action view on the screen.
+ // Otherwise, the view can be recycled, so accessibility events cannot
+ // be sent later.
+ getGuidedActionsStylist().getActionsGridView().scrollToPosition(0);
+ mFromContentFragment = true;
+ }
+ return super.performAccessibilityAction(host, action, args);
+ }
+ });
+ mContentFragment.requestFocus();
+ }
+ }
+
+ @Override
public GuidanceStylist onCreateGuidanceStylist() {
return new GuidanceStylist() {
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Guidance guidance) {
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Guidance guidance) {
View view = super.onCreateView(inflater, container, guidance);
if (guidance.getIconDrawable() == null) {
// Icon view should not take up space when we don't use image.
@@ -106,10 +152,19 @@ public abstract class SetupGuidedStepFragment extends GuidedStepFragment {
};
}
- abstract protected String getActionCategory();
+ protected abstract String getActionCategory();
+
+ protected View getDoneButton() {
+ return getActivity().findViewById(R.id.button_done);
+ }
@Override
public void onGuidedActionClicked(GuidedAction action) {
+ if (!action.isFocusable()) {
+ // an unfocusable action may be clicked in accessibility mode when it's accessibility
+ // focused
+ return;
+ }
SetupActionHelper.onActionClick(this, getActionCategory(), (int) action.getId());
}
@@ -122,4 +177,38 @@ public abstract class SetupGuidedStepFragment extends GuidedStepFragment {
public boolean isFocusOutEndAllowed() {
return true;
}
+
+ protected void setAccessibilityDelegate(GuidedActionsStylist.ViewHolder vh,
+ GuidedAction action) {
+ if (!mAccessibilityMode || findActionPositionById(action.getId()) == 0) {
+ return;
+ }
+ vh.itemView.setAccessibilityDelegate(
+ new AccessibilityDelegate() {
+ @Override
+ public boolean performAccessibilityAction(View host, int action, Bundle args) {
+ if ((action == AccessibilityNodeInfo.ACTION_FOCUS
+ || action == AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS)
+ && mFromContentFragment) {
+ // block the action and make the first action view accessibility focused
+ View view = getActionItemView(0);
+ if (view != null) {
+ view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ mFromContentFragment = false;
+ return true;
+ }
+ }
+ return super.performAccessibilityAction(host, action, args);
+ }
+ });
+ }
+
+ private class SetupGuidedStepFragmentGuidedActionsStylist extends GuidedActionsStylist {
+
+ @Override
+ public void onBindViewHolder(GuidedActionsStylist.ViewHolder vh, GuidedAction action) {
+ super.onBindViewHolder(vh, action);
+ setAccessibilityDelegate(vh, action);
+ }
+ }
}
diff --git a/common/src/com/android/tv/common/ui/setup/SetupMultiPaneFragment.java b/common/src/com/android/tv/common/ui/setup/SetupMultiPaneFragment.java
index f5c2bf26..c02d3f56 100644
--- a/common/src/com/android/tv/common/ui/setup/SetupMultiPaneFragment.java
+++ b/common/src/com/android/tv/common/ui/setup/SetupMultiPaneFragment.java
@@ -23,34 +23,43 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
-
import com.android.tv.common.R;
-/**
- * A fragment for channel source info/setup.
- */
+/** A fragment for channel source info/setup. */
public abstract class SetupMultiPaneFragment extends SetupFragment {
private static final String TAG = "SetupMultiPaneFragment";
private static final boolean DEBUG = false;
public static final int ACTION_DONE = Integer.MAX_VALUE;
public static final int ACTION_SKIP = ACTION_DONE - 1;
+ public static final int MAX_SUBCLASSES_ID = ACTION_SKIP - 1;
- private static final String CONTENT_FRAGMENT_TAG = "content_fragment";
+ public static final String CONTENT_FRAGMENT_TAG = "content_fragment";
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (DEBUG) {
- Log.d(TAG, "onCreateView(" + inflater + ", " + container + ", " + savedInstanceState
- + ")");
+ Log.d(
+ TAG,
+ "onCreateView("
+ + inflater
+ + ", "
+ + container
+ + ", "
+ + savedInstanceState
+ + ")");
}
View view = super.onCreateView(inflater, container, savedInstanceState);
if (savedInstanceState == null) {
SetupGuidedStepFragment contentFragment = onCreateContentFragment();
- getChildFragmentManager().beginTransaction()
- .replace(R.id.guided_step_fragment_container, contentFragment,
- CONTENT_FRAGMENT_TAG).commit();
+ getChildFragmentManager()
+ .beginTransaction()
+ .replace(
+ R.id.guided_step_fragment_container,
+ contentFragment,
+ CONTENT_FRAGMENT_TAG)
+ .commit();
}
if (needsDoneButton()) {
setOnClickAction(view.findViewById(R.id.button_done), getActionCategory(), ACTION_DONE);
@@ -65,12 +74,12 @@ public abstract class SetupMultiPaneFragment extends SetupFragment {
if (getResources().getConfiguration().getLayoutDirection()
== View.LAYOUT_DIRECTION_LTR) {
((MarginLayoutParams) doneButtonContainer.getLayoutParams()).rightMargin =
- -getResources().getDimensionPixelOffset(
- R.dimen.setup_done_button_container_width);
+ -getResources()
+ .getDimensionPixelOffset(R.dimen.setup_done_button_container_width);
} else {
((MarginLayoutParams) doneButtonContainer.getLayoutParams()).leftMargin =
- -getResources().getDimensionPixelOffset(
- R.dimen.setup_done_button_container_width);
+ -getResources()
+ .getDimensionPixelOffset(R.dimen.setup_done_button_container_width);
}
view.findViewById(R.id.button_done).setFocusable(false);
}
@@ -82,15 +91,15 @@ public abstract class SetupMultiPaneFragment extends SetupFragment {
return R.layout.fragment_setup_multi_pane;
}
- abstract protected SetupGuidedStepFragment onCreateContentFragment();
+ protected abstract SetupGuidedStepFragment onCreateContentFragment();
@Nullable
protected SetupGuidedStepFragment getContentFragment() {
- return (SetupGuidedStepFragment) getChildFragmentManager()
- .findFragmentByTag(CONTENT_FRAGMENT_TAG);
+ return (SetupGuidedStepFragment)
+ getChildFragmentManager().findFragmentByTag(CONTENT_FRAGMENT_TAG);
}
- abstract protected String getActionCategory();
+ protected abstract String getActionCategory();
protected boolean needsDoneButton() {
return true;
@@ -102,13 +111,16 @@ public abstract class SetupMultiPaneFragment extends SetupFragment {
@Override
protected int[] getParentIdsForDelay() {
- return new int[] {android.support.v17.leanback.R.id.content_fragment,
- android.support.v17.leanback.R.id.guidedactions_list};
+ return new int[] {
+ android.support.v17.leanback.R.id.content_fragment,
+ android.support.v17.leanback.R.id.guidedactions_list
+ };
}
@Override
public int[] getSharedElementIds() {
- return new int[] {android.support.v17.leanback.R.id.action_fragment_background,
- R.id.done_button_container};
+ return new int[] {
+ android.support.v17.leanback.R.id.action_fragment_background, R.id.done_button_container
+ };
}
}
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 e1a8e60c..60ffb70f 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
@@ -29,15 +29,12 @@ import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
-/**
- * Execute horizontal slide of 1/4 width and fade (to workaround bug 23718734)
- */
+/** Execute horizontal slide of 1/4 width and fade (to workaround bug 23718734) */
public class FadeAndShortSlide extends Visibility {
private static final TimeInterpolator APPEAR_INTERPOLATOR = new DecelerateInterpolator();
private static final TimeInterpolator DISAPPEAR_INTERPOLATOR = new AccelerateInterpolator();
@@ -48,39 +45,45 @@ public class FadeAndShortSlide extends Visibility {
private static final int DEFAULT_DISTANCE = 200;
- private static abstract class CalculateSlide {
+ private abstract static class CalculateSlide {
/** Returns the translation value for view when it goes out of the scene */
- public abstract float getGoneX(ViewGroup sceneRoot, View view, int[] position,
- int distance);
+ public abstract float getGoneX(
+ ViewGroup sceneRoot, View view, int[] position, int distance);
}
- private static final CalculateSlide sCalculateStart = new CalculateSlide() {
- @Override
- public float getGoneX(ViewGroup sceneRoot, View view, int[] position, int distance) {
- final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
- final float x;
- if (isRtl) {
- x = view.getTranslationX() + distance;
- } else {
- x = view.getTranslationX() - distance;
- }
- return x;
- }
- };
-
- private static final CalculateSlide sCalculateEnd = new CalculateSlide() {
- @Override
- public float getGoneX(ViewGroup sceneRoot, View view, int[] position, int distance) {
- final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
- final float x;
- if (isRtl) {
- x = view.getTranslationX() - distance;
- } else {
- x = view.getTranslationX() + distance;
- }
- return x;
- }
- };
+ private static final CalculateSlide sCalculateStart =
+ new CalculateSlide() {
+ @Override
+ public float getGoneX(
+ ViewGroup sceneRoot, View view, int[] position, int distance) {
+ final boolean isRtl =
+ sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+ final float x;
+ if (isRtl) {
+ x = view.getTranslationX() + distance;
+ } else {
+ x = view.getTranslationX() - distance;
+ }
+ return x;
+ }
+ };
+
+ private static final CalculateSlide sCalculateEnd =
+ new CalculateSlide() {
+ @Override
+ public float getGoneX(
+ ViewGroup sceneRoot, View view, int[] position, int distance) {
+ final boolean isRtl =
+ sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+ final float x;
+ if (isRtl) {
+ x = view.getTranslationX() - distance;
+ } else {
+ x = view.getTranslationX() + distance;
+ }
+ return x;
+ }
+ };
private static final ViewPositionComparator sViewPositionComparator =
new ViewPositionComparator();
@@ -131,9 +134,10 @@ public class FadeAndShortSlide extends Visibility {
getTransitionTargets((ViewGroup) parentForDelay, transitionTargets);
sViewPositionComparator.mParentForDelay = parentForDelay;
sViewPositionComparator.mIsLtr = view.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
- sViewPositionComparator.mToLeft = sViewPositionComparator.mIsLtr
- ? mSlideEdge == (appear ? Gravity.END : Gravity.START)
- : mSlideEdge == (appear ? Gravity.START : Gravity.END);
+ sViewPositionComparator.mToLeft =
+ sViewPositionComparator.mIsLtr
+ ? mSlideEdge == (appear ? Gravity.END : Gravity.START)
+ : mSlideEdge == (appear ? Gravity.START : Gravity.END);
Collections.sort(transitionTargets, sViewPositionComparator);
return transitionTargets.indexOf(view);
}
@@ -180,8 +184,8 @@ public class FadeAndShortSlide extends Visibility {
captureValues(transitionValues);
int delayIndex = getDelayOrder(transitionValues.view, false);
if (delayIndex > 0) {
- transitionValues.values.put(PROPNAME_DELAY,
- delayIndex * SetupAnimationHelper.DELAY_BETWEEN_SIBLINGS_MS);
+ transitionValues.values.put(
+ PROPNAME_DELAY, delayIndex * SetupAnimationHelper.DELAY_BETWEEN_SIBLINGS_MS);
}
}
@@ -192,8 +196,8 @@ public class FadeAndShortSlide extends Visibility {
captureValues(transitionValues);
int delayIndex = getDelayOrder(transitionValues.view, true);
if (delayIndex > 0) {
- transitionValues.values.put(PROPNAME_DELAY,
- delayIndex * SetupAnimationHelper.DELAY_BETWEEN_SIBLINGS_MS);
+ transitionValues.values.put(
+ PROPNAME_DELAY, delayIndex * SetupAnimationHelper.DELAY_BETWEEN_SIBLINGS_MS);
}
}
@@ -212,7 +216,10 @@ public class FadeAndShortSlide extends Visibility {
}
@Override
- public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
+ public Animator onAppear(
+ ViewGroup sceneRoot,
+ View view,
+ TransitionValues startValues,
TransitionValues endValues) {
if (endValues == null) {
return null;
@@ -221,15 +228,16 @@ public class FadeAndShortSlide extends Visibility {
int left = position[0];
float endX = view.getTranslationX();
float startX = mSlideCalculator.getGoneX(sceneRoot, view, position, mDistance);
- final Animator slideAnimator = TranslationAnimationCreator.createAnimation(view, endValues,
- left, startX, endX, APPEAR_INTERPOLATOR, this);
+ 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));
- Long delay = (Long ) endValues.values.get(PROPNAME_DELAY);
+ Long delay = (Long) endValues.values.get(PROPNAME_DELAY);
if (delay != null) {
set.setStartDelay(delay);
}
@@ -237,7 +245,10 @@ public class FadeAndShortSlide extends Visibility {
}
@Override
- public Animator onDisappear(ViewGroup sceneRoot, final View view, TransitionValues startValues,
+ public Animator onDisappear(
+ ViewGroup sceneRoot,
+ final View view,
+ TransitionValues startValues,
TransitionValues endValues) {
if (startValues == null) {
return null;
@@ -246,8 +257,9 @@ public class FadeAndShortSlide extends Visibility {
int left = position[0];
float startX = view.getTranslationX();
float endX = mSlideCalculator.getGoneX(sceneRoot, view, position, mDistance);
- final Animator slideAnimator = TranslationAnimationCreator.createAnimation(view,
- startValues, left, startX, endX, DISAPPEAR_INTERPOLATOR, this);
+ final Animator slideAnimator =
+ TranslationAnimationCreator.createAnimation(
+ view, startValues, left, startX, endX, DISAPPEAR_INTERPOLATOR, this);
if (slideAnimator == null) { // slideAnimator is null if startX == endX
return null;
}
@@ -257,13 +269,14 @@ public class FadeAndShortSlide extends Visibility {
if (fadeAnimator == null) {
return null;
}
- fadeAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animator) {
- fadeAnimator.removeListener(this);
- view.setAlpha(0.0f);
- }
- });
+ fadeAnimator.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ fadeAnimator.removeListener(this);
+ view.setAlpha(0.0f);
+ }
+ });
final AnimatorSet set = new AnimatorSet();
set.play(slideAnimator).with(fadeAnimator);
@@ -300,9 +313,7 @@ public class FadeAndShortSlide extends Visibility {
return super.setDuration(scaledDuration);
}
- /**
- * Sets the moving distance in pixel.
- */
+ /** Sets the moving distance in pixel. */
public void setDistance(int distance) {
mDistance = distance;
}
diff --git a/common/src/com/android/tv/common/ui/setup/animation/SetupAnimationHelper.java b/common/src/com/android/tv/common/ui/setup/animation/SetupAnimationHelper.java
index d98138a2..2a913c6e 100644
--- a/common/src/com/android/tv/common/ui/setup/animation/SetupAnimationHelper.java
+++ b/common/src/com/android/tv/common/ui/setup/animation/SetupAnimationHelper.java
@@ -27,12 +27,9 @@ import android.transition.TransitionSet;
import android.view.Gravity;
import android.view.View;
import android.widget.ImageView;
-
import com.android.tv.common.R;
-/**
- * A helper class for setup animation.
- */
+/** A helper class for setup animation. */
public final class SetupAnimationHelper {
public static final long DELAY_BETWEEN_SIBLINGS_MS = applyAnimationTimeScale(33);
@@ -43,21 +40,21 @@ public final class SetupAnimationHelper {
private static int sFragmentTransitionLongDistance;
private static int sFragmentTransitionShortDistance;
- private SetupAnimationHelper() { }
+ private SetupAnimationHelper() {}
- /**
- * Load initial parameters. This method should be called before using this class.
- */
+ /** Load initial parameters. This method should be called before using this class. */
public static void initialize(Context context) {
if (sInitialized) {
return;
}
- sFragmentTransitionDuration = context.getResources()
- .getInteger(R.integer.setup_fragment_transition_duration);
- sFragmentTransitionLongDistance = context.getResources()
- .getDimensionPixelOffset(R.dimen.setup_fragment_transition_long_distance);
- sFragmentTransitionShortDistance = context.getResources()
- .getDimensionPixelOffset(R.dimen.setup_fragment_transition_short_distance);
+ sFragmentTransitionDuration =
+ context.getResources().getInteger(R.integer.setup_fragment_transition_duration);
+ sFragmentTransitionLongDistance =
+ context.getResources()
+ .getDimensionPixelOffset(R.dimen.setup_fragment_transition_long_distance);
+ sFragmentTransitionShortDistance =
+ context.getResources()
+ .getDimensionPixelOffset(R.dimen.setup_fragment_transition_short_distance);
sInitialized = true;
}
@@ -88,9 +85,7 @@ public final class SetupAnimationHelper {
return this;
}
- /**
- * Sets the duration of the transition.
- */
+ /** Sets the duration of the transition. */
public TransitionBuilder setDuration(long duration) {
mDuration = duration;
return this;
@@ -106,17 +101,13 @@ public final class SetupAnimationHelper {
return this;
}
- /**
- * Sets the ID's of the views which will not be included in the transition.
- */
+ /** Sets the ID's of the views which will not be included in the transition. */
public TransitionBuilder setExcludeIds(int[] excludeIds) {
mExcludeIds = excludeIds;
return this;
}
- /**
- * Builds and returns the {@link android.transition.Transition}.
- */
+ /** Builds and returns the {@link android.transition.Transition}. */
public Transition build() {
FadeAndShortSlide transition = new FadeAndShortSlide(mSlideEdge, mParentIdForDelay);
transition.setDistance(mDistance);
@@ -130,25 +121,19 @@ public final class SetupAnimationHelper {
}
}
- /**
- * Changes the move distance of the {@code transition} to long distance.
- */
+ /** Changes the move distance of the {@code transition} to long distance. */
public static void setLongDistance(FadeAndShortSlide transition) {
checkInitialized();
transition.setDistance(sFragmentTransitionLongDistance);
}
- /**
- * Changes the move distance of the {@code transition} to short distance.
- */
+ /** Changes the move distance of the {@code transition} to short distance. */
public static void setShortDistance(FadeAndShortSlide transition) {
checkInitialized();
transition.setDistance(sFragmentTransitionShortDistance);
}
- /**
- * Applies the animation scale to the given {@code animator}.
- */
+ /** Applies the animation scale to the given {@code animator}. */
public static Animator applyAnimationTimeScale(Animator animator) {
if (animator instanceof AnimatorSet) {
for (Animator child : ((AnimatorSet) animator).getChildAnimations()) {
@@ -162,9 +147,7 @@ public final class SetupAnimationHelper {
return animator;
}
- /**
- * Applies the animation scale to the given {@code transition}.
- */
+ /** Applies the animation scale to the given {@code transition}. */
public static Transition applyAnimationTimeScale(Transition transition) {
if (transition instanceof TransitionSet) {
TransitionSet set = (TransitionSet) transition;
@@ -180,9 +163,7 @@ public final class SetupAnimationHelper {
return transition;
}
- /**
- * Applies the animation scale to the given {@code time}.
- */
+ /** Applies the animation scale to the given {@code time}. */
public static long applyAnimationTimeScale(long time) {
return (long) (time * ANIMATION_TIME_SCALE);
}
@@ -197,23 +178,25 @@ public final class SetupAnimationHelper {
}
/**
- * Returns an animator which animates the source image of the {@link ImageView} with start delay.
+ * Returns an animator which animates the source image of the {@link ImageView} with start
+ * delay.
*
* <p>The frame rate is 60 fps.
*/
- public static ObjectAnimator createFrameAnimatorWithDelay(ImageView imageView, int[] frames,
- long startDelay) {
+ public static ObjectAnimator createFrameAnimatorWithDelay(
+ ImageView imageView, int[] frames, long startDelay) {
ObjectAnimator animator = ObjectAnimator.ofInt(imageView, "imageResource", frames);
// Make it 60 fps.
animator.setDuration(frames.length * 1000 / 60);
animator.setInterpolator(null);
animator.setStartDelay(startDelay);
- animator.setEvaluator(new TypeEvaluator<Integer>() {
- @Override
- public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
- return startValue;
- }
- });
+ animator.setEvaluator(
+ new TypeEvaluator<Integer>() {
+ @Override
+ public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
+ return startValue;
+ }
+ });
return animator;
}
@@ -223,19 +206,20 @@ public final class SetupAnimationHelper {
* @param view The view which will be animated.
* @param duration The duration of the animation.
* @param makeVisibleAfterAnimation If {@code true}, the view will become visible after the
- * animation ends.
+ * animation ends.
*/
- public static Animator createFadeOutAnimator(final View view, long duration,
- boolean makeVisibleAfterAnimation) {
+ public static Animator createFadeOutAnimator(
+ final View view, long duration, boolean makeVisibleAfterAnimation) {
ObjectAnimator animator =
ObjectAnimator.ofFloat(view, View.ALPHA, 1.0f, 0.0f).setDuration(duration);
if (makeVisibleAfterAnimation) {
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- view.setAlpha(1.0f);
- }
- });
+ animator.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setAlpha(1.0f);
+ }
+ });
}
return animator;
}
diff --git a/common/src/com/android/tv/common/ui/setup/animation/TranslationAnimationCreator.java b/common/src/com/android/tv/common/ui/setup/animation/TranslationAnimationCreator.java
index 7705f7a7..13b89ea1 100644
--- a/common/src/com/android/tv/common/ui/setup/animation/TranslationAnimationCreator.java
+++ b/common/src/com/android/tv/common/ui/setup/animation/TranslationAnimationCreator.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2017 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.animation;
import android.animation.Animator;
@@ -5,18 +20,16 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.graphics.Path;
+import android.support.v17.leanback.R;
import android.transition.Transition;
import android.transition.TransitionValues;
import android.view.View;
-import android.support.v17.leanback.R;
-
/**
* This class is used by Slide and Explode to create an animator that goes from the start position
* to the end position. It takes into account the canceled position so that it will not blink out or
- * shift suddenly when the transition is interrupted.
- * The original class is android.support.v17.leanback.transition.TranslationAnimationCreator which
- * is hidden.
+ * shift suddenly when the transition is interrupted. The original class is
+ * android.support.v17.leanback.transition.TranslationAnimationCreator which is hidden.
*/
// Copied from android.support.v17.leanback.transition.TransltaionAnimationCreator
class TranslationAnimationCreator {
@@ -31,11 +44,16 @@ class TranslationAnimationCreator {
* @param endX The end translation x of view
* @param interpolator The interpolator to use with this animator.
* @return An animator that moves from (startX, startY) to (endX, endY) unless there was a
- * previous interruption, in which case it moves from the current position to (endX,
- * endY).
+ * previous interruption, in which case it moves from the current position to (endX, endY).
*/
- static Animator createAnimation(View view, TransitionValues values, int viewPosX, float startX,
- float endX, TimeInterpolator interpolator, Transition transition) {
+ static Animator createAnimation(
+ View view,
+ TransitionValues values,
+ int viewPosX,
+ float startX,
+ float endX,
+ TimeInterpolator interpolator,
+ Transition transition) {
float terminalX = view.getTranslationX();
Integer startPosition = (Integer) values.view.getTag(R.id.transitionPosition);
if (startPosition != null) {
@@ -74,8 +92,8 @@ class TranslationAnimationCreator {
private float mPausedX;
private final float mTerminalX;
- private TransitionPositionListener(View movingView, View viewInHierarchy, int startX,
- float terminalX) {
+ private TransitionPositionListener(
+ View movingView, View viewInHierarchy, int startX, float terminalX) {
mMovingView = movingView;
mViewInHierarchy = viewInHierarchy;
mStartX = startX - Math.round(mMovingView.getTranslationX());
@@ -123,6 +141,4 @@ class TranslationAnimationCreator {
@Override
public void onTransitionResume(Transition transition) {}
}
-
}
-
diff --git a/common/src/com/android/tv/common/AutoCloseableUtils.java b/common/src/com/android/tv/common/util/AutoCloseableUtils.java
index ad364cc4..605715ef 100644
--- a/common/src/com/android/tv/common/AutoCloseableUtils.java
+++ b/common/src/com/android/tv/common/util/AutoCloseableUtils.java
@@ -14,17 +14,15 @@
* limitations under the License
*/
-package com.android.tv.common;
+package com.android.tv.common.util;
import android.util.Log;
-/**
- * Static utilities for AutoCloseable.
- */
+/** Static utilities for AutoCloseable. */
public class AutoCloseableUtils {
private static final String TAG = "AutoCloseableUtils";
- private AutoCloseableUtils() { }
+ private AutoCloseableUtils() {}
public static void closeQuietly(AutoCloseable closeable) {
try {
diff --git a/src/com/android/tv/util/Clock.java b/common/src/com/android/tv/common/util/Clock.java
index c5e96431..cd6ede86 100644
--- a/src/com/android/tv/util/Clock.java
+++ b/common/src/com/android/tv/common/util/Clock.java
@@ -13,18 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.tv.util;
+package com.android.tv.common.util;
import android.os.SystemClock;
/**
- * An interface through which system clocks can be read. The {@link #SYSTEM} implementation
- * must be used for all non-test cases.
+ * An interface through which system clocks can be read. The {@link #SYSTEM} implementation must be
+ * used for all non-test cases.
*/
public interface Clock {
/**
* Returns the current time in milliseconds since January 1, 1970 00:00:00.0 UTC.
- * See {@link System#currentTimeMillis()}.
+ *
+ * @see System#currentTimeMillis().
*/
long currentTimeMillis();
@@ -36,6 +37,14 @@ public interface Clock {
long elapsedRealtime();
/**
+ * Returns milliseconds since boot, not counting time spent in deep sleep.
+ *
+ * @return milliseconds of non-sleep uptime since boot.
+ * @see SystemClock#uptimeMillis()
+ */
+ long uptimeMillis();
+
+ /**
* Waits a given number of milliseconds (of uptimeMillis) before returning.
*
* @param ms to sleep before returning, in milliseconds of uptime.
@@ -43,23 +52,27 @@ public interface Clock {
*/
void sleep(long ms);
- /**
- * The default implementation of Clock.
- */
- Clock SYSTEM = new Clock() {
- @Override
- public long currentTimeMillis() {
- return System.currentTimeMillis();
- }
+ /** The default implementation of Clock. */
+ Clock SYSTEM =
+ new Clock() {
+ @Override
+ public long currentTimeMillis() {
+ return System.currentTimeMillis();
+ }
+
+ @Override
+ public long elapsedRealtime() {
+ return SystemClock.elapsedRealtime();
+ }
- @Override
- public long elapsedRealtime() {
- return SystemClock.elapsedRealtime();
- }
+ @Override
+ public void sleep(long ms) {
+ SystemClock.sleep(ms);
+ }
- @Override
- public void sleep(long ms) {
- SystemClock.sleep(ms);
- }
- };
+ @Override
+ public long uptimeMillis() {
+ return SystemClock.uptimeMillis();
+ }
+ };
}
diff --git a/common/src/com/android/tv/common/CollectionUtils.java b/common/src/com/android/tv/common/util/CollectionUtils.java
index 300ad8f2..8ca7e3cc 100644
--- a/common/src/com/android/tv/common/CollectionUtils.java
+++ b/common/src/com/android/tv/common/util/CollectionUtils.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.tv.common;
+package com.android.tv.common.util;
import java.util.ArrayList;
import java.util.Arrays;
@@ -23,16 +23,14 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.List;
-/**
- * Static utilities for collections
- */
+/** Static utilities for collections */
public class CollectionUtils {
/**
* Returns an array with the arrays concatenated together.
*
- * @see <a href="http://stackoverflow.com/a/784842/1122089">Stackoverflow answer</a> by
- * <a href="http://stackoverflow.com/users/40342/joachim-sauer">Joachim Sauer</a>
+ * @see <a href="http://stackoverflow.com/a/784842/1122089">Stackoverflow answer</a> by <a
+ * href="http://stackoverflow.com/users/40342/joachim-sauer">Joachim Sauer</a>
*/
public static <T> T[] concatAll(T[] first, T[]... rest) {
int totalLength = first.length;
@@ -50,12 +48,12 @@ public class CollectionUtils {
/**
* Unions the two collections and returns the unified list.
- * <p>
- * The elements is not compared with hashcode() or equals(). Comparator is used for the equality
- * check.
+ *
+ * <p>The elements is not compared with hashcode() or equals(). Comparator is used for the
+ * equality check.
*/
- public static <T> List<T> union(Collection<T> originals, Collection<T> toAdds,
- Comparator<T> comparator) {
+ public static <T> List<T> union(
+ Collection<T> originals, Collection<T> toAdds, Comparator<T> comparator) {
List<T> result = new ArrayList<>(originals);
Collections.sort(result, comparator);
List<T> resultToAdd = new ArrayList<>();
@@ -68,11 +66,9 @@ public class CollectionUtils {
return result;
}
- /**
- * Subtracts the elements from the original collection.
- */
- public static <T> List<T> subtract(Collection<T> originals, T[] toSubtracts,
- Comparator<T> comparator) {
+ /** Subtracts the elements from the original collection. */
+ public static <T> List<T> subtract(
+ Collection<T> originals, T[] toSubtracts, Comparator<T> comparator) {
List<T> result = new ArrayList<>(originals);
Collections.sort(result, comparator);
for (T toSubtract : toSubtracts) {
@@ -84,11 +80,9 @@ public class CollectionUtils {
return result;
}
- /**
- * Returns {@code true} if the two specified collections have common elements.
- */
- public static <T> boolean containsAny(Collection<T> c1, Collection<T> c2,
- Comparator<T> comparator) {
+ /** Returns {@code true} if the two specified collections have common elements. */
+ public static <T> boolean containsAny(
+ Collection<T> c1, Collection<T> c2, Comparator<T> comparator) {
List<T> contains = new ArrayList<>(c1);
Collections.sort(contains, comparator);
for (T iterate : c2) {
diff --git a/common/src/com/android/tv/common/util/CommonUtils.java b/common/src/com/android/tv/common/util/CommonUtils.java
new file mode 100644
index 00000000..305431d3
--- /dev/null
+++ b/common/src/com/android/tv/common/util/CommonUtils.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 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.util;
+
+import android.content.Context;
+import android.content.Intent;
+import android.media.tv.TvInputInfo;
+import android.os.Build;
+import android.util.ArraySet;
+import android.util.Log;
+import com.android.tv.common.BuildConfig;
+import com.android.tv.common.CommonConstants;
+import com.android.tv.common.actions.InputSetupActionUtils;
+import com.android.tv.common.experiments.Experiments;
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Set;
+
+/** Util class for common use in TV app and inputs. */
+@SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated
+public final class CommonUtils {
+ private static final String TAG = "CommonUtils";
+ private static final ThreadLocal<SimpleDateFormat> ISO_8601 =
+ new ThreadLocal() {
+ private final SimpleDateFormat value =
+ new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US);
+
+ @Override
+ protected SimpleDateFormat initialValue() {
+ return value;
+ }
+ };
+ // Hardcoded list for known bundled inputs not written by OEM/SOCs.
+ // Bundled (system) inputs not in the list will get the high priority
+ // so they and their channels come first in the UI.
+ private static final Set<String> BUNDLED_PACKAGE_SET = new ArraySet<>();
+
+ static {
+ BUNDLED_PACKAGE_SET.add("com.android.tv");
+ }
+
+ private static Boolean sRunningInTest;
+
+ private CommonUtils() {}
+
+ /**
+ * Returns an intent to start the setup activity for the TV input using {@link
+ * InputSetupActionUtils#INTENT_ACTION_INPUT_SETUP}.
+ */
+ public static Intent createSetupIntent(Intent originalSetupIntent, String inputId) {
+ if (originalSetupIntent == null) {
+ return null;
+ }
+ Intent setupIntent = new Intent(originalSetupIntent);
+ if (!InputSetupActionUtils.hasInputSetupAction(originalSetupIntent)) {
+ Intent intentContainer = new Intent(InputSetupActionUtils.INTENT_ACTION_INPUT_SETUP);
+ intentContainer.putExtra(InputSetupActionUtils.EXTRA_SETUP_INTENT, originalSetupIntent);
+ intentContainer.putExtra(InputSetupActionUtils.EXTRA_INPUT_ID, inputId);
+ setupIntent = intentContainer;
+ }
+ return setupIntent;
+ }
+
+ /**
+ * Returns an intent to start the setup activity for this TV input using {@link
+ * InputSetupActionUtils#INTENT_ACTION_INPUT_SETUP}.
+ */
+ public static Intent createSetupIntent(TvInputInfo input) {
+ return createSetupIntent(input.createSetupIntent(), input.getId());
+ }
+
+ /**
+ * Checks if this application is running in tests.
+ *
+ * <p>{@link android.app.ActivityManager#isRunningInTestHarness} doesn't return {@code true} for
+ * the usual devices even the application is running in tests. We need to figure it out by
+ * checking whether the class in tv-tests-common module can be loaded or not.
+ */
+ public static synchronized boolean isRunningInTest() {
+ if (sRunningInTest == null) {
+ try {
+ Class.forName("com.android.tv.testing.utils.Utils");
+ Log.i(
+ TAG,
+ "Assumed to be running in a test because"
+ + " com.android.tv.testing.utils.Utils is found");
+ sRunningInTest = true;
+ } catch (ClassNotFoundException e) {
+ sRunningInTest = false;
+ }
+ }
+ return sRunningInTest;
+ }
+
+ /** Checks whether a given package is in our bundled package set. */
+ public static boolean isInBundledPackageSet(String packageName) {
+ return BUNDLED_PACKAGE_SET.contains(packageName);
+ }
+
+ /** Checks whether a given input is a bundled input. */
+ public static boolean isBundledInput(String inputId) {
+ for (String prefix : BUNDLED_PACKAGE_SET) {
+ if (inputId.startsWith(prefix + "/")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Returns true if the application is packaged with Live TV. */
+ public static boolean isPackagedWithLiveChannels(Context context) {
+ return (CommonConstants.BASE_PACKAGE.equals(context.getPackageName()));
+ }
+
+ /** Returns true if the current user is a developer. */
+ public static boolean isDeveloper() {
+ return BuildConfig.ENG || Experiments.ENABLE_DEVELOPER_FEATURES.get();
+ }
+
+ /** Converts time in milliseconds to a ISO 8061 string. */
+ public static String toIsoDateTimeString(long timeMillis) {
+ return ISO_8601.get().format(new Date(timeMillis));
+ }
+
+ /** Deletes a file or a directory. */
+ public static void deleteDirOrFile(File fileOrDirectory) {
+ if (fileOrDirectory.isDirectory()) {
+ for (File child : fileOrDirectory.listFiles()) {
+ deleteDirOrFile(child);
+ }
+ }
+ fileOrDirectory.delete();
+ }
+
+ public static boolean isRoboTest() {
+ return "robolectric".equals(Build.FINGERPRINT);
+ }
+}
diff --git a/common/src/com/android/tv/common/util/ContentUriUtils.java b/common/src/com/android/tv/common/util/ContentUriUtils.java
new file mode 100644
index 00000000..6cbe5e12
--- /dev/null
+++ b/common/src/com/android/tv/common/util/ContentUriUtils.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2018 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.util;
+
+import android.content.ContentUris;
+import android.net.Uri;
+import android.util.Log;
+
+/** Static utils for{@link android.content.ContentUris}. */
+public class ContentUriUtils {
+ private static final String TAG = "ContentUriUtils";
+
+ /**
+ * Converts the last path segment to a long.
+ *
+ * <p>This supports a common convention for content URIs where an ID is stored in the last
+ * segment.
+ *
+ * @return the long conversion of the last segment or -1 if the path is empty or there is any
+ * error
+ * @see ContentUris#parseId(Uri)
+ */
+ public static long safeParseId(Uri uri) {
+ try {
+ return ContentUris.parseId(uri);
+ } catch (Exception e) {
+ Log.d(TAG, "Error parsing " + uri, e);
+ return -1;
+ }
+ }
+}
diff --git a/src/com/android/tv/util/Debug.java b/common/src/com/android/tv/common/util/Debug.java
index 67a2683d..ab908741 100644
--- a/src/com/android/tv/util/Debug.java
+++ b/common/src/com/android/tv/common/util/Debug.java
@@ -14,34 +14,26 @@
* limitations under the License.
*/
-package com.android.tv.util;
+package com.android.tv.common.util;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
-/**
- * A class only for help developers.
- */
+/** A class only for help developers. */
public class Debug {
/**
- * A threshold of start up time, when the start up time of Live TV is more than it,
- * a warning will show to the developer.
+ * A threshold of start up time, when the start up time of Live TV is more than it, a
+ * warning will show to the developer.
*/
public static final long TIME_START_UP_DURATION_THRESHOLD = TimeUnit.SECONDS.toMillis(6);
- /**
- * Tag for measuring start up time of Live TV.
- */
+ /** Tag for measuring start up time of Live TV. */
public static final String TAG_START_UP_TIMER = "start_up_timer";
- /**
- * A global map for duration timers.
- */
- private final static Map<String, DurationTimer> sTimerMap = new HashMap<>();
+ /** A global map for duration timers. */
+ private static final Map<String, DurationTimer> sTimerMap = new HashMap<>();
- /**
- * Returns the global duration timer by tag.
- */
+ /** Returns the global duration timer by tag. */
public static DurationTimer getTimer(String tag) {
if (sTimerMap.get(tag) != null) {
return sTimerMap.get(tag);
@@ -51,9 +43,7 @@ public class Debug {
return timer;
}
- /**
- * Removes the global duration timer by tag.
- */
+ /** Removes the global duration timer by tag. */
public static DurationTimer removeTimer(String tag) {
return sTimerMap.remove(tag);
}
diff --git a/src/com/android/tv/util/DurationTimer.java b/common/src/com/android/tv/common/util/DurationTimer.java
index 1f057bf6..91581ad5 100644
--- a/src/com/android/tv/util/DurationTimer.java
+++ b/common/src/com/android/tv/common/util/DurationTimer.java
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-package com.android.tv.util;
+package com.android.tv.common.util;
import android.os.SystemClock;
import android.util.Log;
-
import com.android.tv.common.BuildConfig;
-/**
- * Times a duration.
- */
+/** Times a duration. */
public final class DurationTimer {
private static final String TAG = "DurationTimer";
public static final long TIME_NOT_SET = -1;
@@ -32,30 +29,24 @@ public final class DurationTimer {
private String mTag = TAG;
private boolean mLogEngOnly;
- public DurationTimer() { }
+ public DurationTimer() {}
public DurationTimer(String tag, boolean logEngOnly) {
mTag = tag;
mLogEngOnly = logEngOnly;
}
- /**
- * Returns true if the timer is running.
- */
+ /** Returns true if the timer is running. */
public boolean isRunning() {
return mStartTimeMs != TIME_NOT_SET;
}
- /**
- * Start the timer.
- */
+ /** Start the timer. */
public void start() {
mStartTimeMs = SystemClock.elapsedRealtime();
}
- /**
- * Returns true if timer is started.
- */
+ /** Returns true if timer is started. */
public boolean isStarted() {
return mStartTimeMs != TIME_NOT_SET;
}
@@ -72,7 +63,7 @@ public final class DurationTimer {
* Stops the timer and resets its value to {@link #TIME_NOT_SET}.
*
* @return the current duration in milliseconds or {@link #TIME_NOT_SET} if the timer is not
- * running.
+ * running.
*/
public long reset() {
long duration = getDuration();
@@ -80,9 +71,7 @@ public final class DurationTimer {
return duration;
}
- /**
- * Adds information and duration time to the log.
- */
+ /** Adds information and duration time to the log. */
public void log(String message) {
if (isRunning() && (!mLogEngOnly || BuildConfig.ENG)) {
Log.i(mTag, message + " : " + getDuration() + "ms");
diff --git a/src/com/android/tv/util/LocationUtils.java b/common/src/com/android/tv/common/util/LocationUtils.java
index d5d7bee3..53155298 100644
--- a/src/com/android/tv/util/LocationUtils.java
+++ b/common/src/com/android/tv/common/util/LocationUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.tv.util;
+package com.android.tv.common.util;
import android.Manifest;
import android.content.Context;
@@ -28,16 +28,16 @@ import android.os.Bundle;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
+import com.android.tv.common.BuildConfig;
+
+
-import com.android.tv.tuner.util.PostalCodeUtils;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
-/**
- * A utility class to get the current location.
- */
+/** A utility class to get the current location. */
public class LocationUtils {
private static final String TAG = "LocationUtils";
private static final boolean DEBUG = false;
@@ -47,11 +47,9 @@ public class LocationUtils {
private static String sCountry;
private static IOException sError;
- /**
- * Checks the current location.
- */
- public static synchronized Address getCurrentAddress(Context context) throws IOException,
- SecurityException {
+ /** Checks the current location. */
+ public static synchronized Address getCurrentAddress(Context context)
+ throws IOException, SecurityException {
if (sAddress != null) {
return sAddress;
}
@@ -84,8 +82,8 @@ public class LocationUtils {
}
Geocoder geocoder = new Geocoder(sApplicationContext, Locale.getDefault());
try {
- List<Address> addresses = geocoder.getFromLocation(
- location.getLatitude(), location.getLongitude(), 1);
+ List<Address> addresses =
+ geocoder.getFromLocation(location.getLatitude(), location.getLongitude(), 1);
if (addresses != null && !addresses.isEmpty()) {
sAddress = addresses.get(0);
if (DEBUG) Log.d(TAG, "Got " + sAddress);
@@ -104,31 +102,33 @@ public class LocationUtils {
}
}
- private LocationUtils() { }
+ private LocationUtils() {}
private static class LocationUtilsHelper {
- private static final LocationListener LOCATION_LISTENER = new LocationListener() {
- @Override
- public void onLocationChanged(Location location) {
- updateAddress(location);
- }
+ private static final LocationListener LOCATION_LISTENER =
+ new LocationListener() {
+ @Override
+ public void onLocationChanged(Location location) {
+ updateAddress(location);
+ }
- @Override
- public void onStatusChanged(String provider, int status, Bundle extras) { }
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) {}
- @Override
- public void onProviderEnabled(String provider) { }
+ @Override
+ public void onProviderEnabled(String provider) {}
- @Override
- public void onProviderDisabled(String provider) { }
- };
+ @Override
+ public void onProviderDisabled(String provider) {}
+ };
private static LocationManager sLocationManager;
public static void startLocationUpdates() {
if (sLocationManager == null) {
- sLocationManager = (LocationManager) sApplicationContext.getSystemService(
- Context.LOCATION_SERVICE);
+ sLocationManager =
+ (LocationManager)
+ sApplicationContext.getSystemService(Context.LOCATION_SERVICE);
try {
sLocationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 1000, 10, LOCATION_LISTENER, null);
diff --git a/src/com/android/tv/util/NetworkTrafficTags.java b/common/src/com/android/tv/common/util/NetworkTrafficTags.java
index 2dca613c..91f2bcd1 100644
--- a/src/com/android/tv/util/NetworkTrafficTags.java
+++ b/common/src/com/android/tv/common/util/NetworkTrafficTags.java
@@ -14,11 +14,10 @@
* limitations under the License
*/
-package com.android.tv.util;
+package com.android.tv.common.util;
import android.net.TrafficStats;
import android.support.annotation.NonNull;
-
import java.util.concurrent.Executor;
/** Constants for tagging network traffic in the Live channels app. */
diff --git a/common/src/com/android/tv/common/util/PermissionUtils.java b/common/src/com/android/tv/common/util/PermissionUtils.java
new file mode 100644
index 00000000..8d409e50
--- /dev/null
+++ b/common/src/com/android/tv/common/util/PermissionUtils.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 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.util;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+/** Util class to handle permissions. */
+public class PermissionUtils {
+ /** Permission to read the TV listings. */
+ public static final String PERMISSION_READ_TV_LISTINGS = "android.permission.READ_TV_LISTINGS";
+
+ private static Boolean sHasAccessAllEpgPermission;
+ private static Boolean sHasAccessWatchedHistoryPermission;
+ private static Boolean sHasModifyParentalControlsPermission;
+
+ public static boolean hasAccessAllEpg(Context context) {
+ if (sHasAccessAllEpgPermission == null) {
+ sHasAccessAllEpgPermission =
+ context.checkSelfPermission(
+ "com.android.providers.tv.permission.ACCESS_ALL_EPG_DATA")
+ == PackageManager.PERMISSION_GRANTED;
+ }
+ return sHasAccessAllEpgPermission;
+ }
+
+ public static boolean hasAccessWatchedHistory(Context context) {
+ if (sHasAccessWatchedHistoryPermission == null) {
+ sHasAccessWatchedHistoryPermission =
+ context.checkSelfPermission(
+ "com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS")
+ == PackageManager.PERMISSION_GRANTED;
+ }
+ return sHasAccessWatchedHistoryPermission;
+ }
+
+ public static boolean hasModifyParentalControls(Context context) {
+ if (sHasModifyParentalControlsPermission == null) {
+ sHasModifyParentalControlsPermission =
+ context.checkSelfPermission("android.permission.MODIFY_PARENTAL_CONTROLS")
+ == PackageManager.PERMISSION_GRANTED;
+ }
+ return sHasModifyParentalControlsPermission;
+ }
+
+ public static boolean hasReadTvListings(Context context) {
+ return context.checkSelfPermission(PERMISSION_READ_TV_LISTINGS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ public static boolean hasInternet(Context context) {
+ return context.checkSelfPermission("android.permission.INTERNET")
+ == PackageManager.PERMISSION_GRANTED;
+ }
+}
diff --git a/src/com/android/tv/tuner/util/PostalCodeUtils.java b/common/src/com/android/tv/common/util/PostalCodeUtils.java
index 9eb689a7..c0917af2 100644
--- a/src/com/android/tv/tuner/util/PostalCodeUtils.java
+++ b/common/src/com/android/tv/common/util/PostalCodeUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.tv.tuner.util;
+package com.android.tv.common.util;
import android.content.Context;
import android.location.Address;
@@ -22,18 +22,14 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
-import com.android.tv.tuner.TunerPreferences;
-import com.android.tv.util.LocationUtils;
-
+import com.android.tv.common.CommonPreferences;
import java.io.IOException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;
-/**
- * A utility class to update, get, and set the last known postal or zip code.
- */
+/** A utility class to update, get, and set the last known postal or zip code. */
public class PostalCodeUtils {
private static final String TAG = "PostalCodeUtils";
@@ -84,7 +80,7 @@ public class PostalCodeUtils {
* input by users.
*/
public static String getLastPostalCode(Context context) {
- return TunerPreferences.getLastPostalCode(context);
+ return CommonPreferences.getLastPostalCode(context);
}
/**
@@ -93,15 +89,19 @@ public class PostalCodeUtils {
*/
public static void setLastPostalCode(Context context, String postalCode) {
Log.i(TAG, "Set Postal Code:" + postalCode);
- TunerPreferences.setLastPostalCode(context, postalCode);
+ CommonPreferences.setLastPostalCode(context, postalCode);
}
@Nullable
private static String getPostalCode(Context context) throws IOException, SecurityException {
Address address = LocationUtils.getCurrentAddress(context);
if (address != null) {
- Log.i(TAG, "Current country and postal code is " + address.getCountryName() + ", "
- + address.getPostalCode());
+ Log.i(
+ TAG,
+ "Current country and postal code is "
+ + address.getCountryName()
+ + ", "
+ + address.getPostalCode());
return address.getPostalCode();
}
return null;
@@ -109,8 +109,7 @@ public class PostalCodeUtils {
/** An {@link java.lang.Exception} class to notify no valid postal or zip code is available. */
public static class NoPostalCodeException extends Exception {
- public NoPostalCodeException() {
- }
+ public NoPostalCodeException() {}
}
/**
@@ -135,4 +134,4 @@ public class PostalCodeUtils {
REGION_MAX_LENGTH.get(LocationUtils.getCurrentCountry(context).toUpperCase());
return maxLength == null ? DEFAULT_MAX_LENGTH : maxLength;
}
-} \ No newline at end of file
+}
diff --git a/common/src/com/android/tv/common/SharedPreferencesUtils.java b/common/src/com/android/tv/common/util/SharedPreferencesUtils.java
index 140c4e6f..e8bfe61b 100644
--- a/common/src/com/android/tv/common/SharedPreferencesUtils.java
+++ b/common/src/com/android/tv/common/util/SharedPreferencesUtils.java
@@ -14,15 +14,14 @@
* limitations under the License
*/
-package com.android.tv.common;
+package com.android.tv.common.util;
import android.content.Context;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
+import com.android.tv.common.CommonConstants;
-/**
- * Static utilities for {@link android.content.SharedPreferences}
- */
+/** Static utilities for {@link android.content.SharedPreferences} */
public final class SharedPreferencesUtils {
// Note that changing the preference name will reset the preference values.
public static final String SHARED_PREF_FEATURES = "sharePreferencesFeatures";
@@ -31,7 +30,7 @@ public final class SharedPreferencesUtils {
public static final String SHARED_PREF_DVR_WATCHED_POSITION =
"dvr_watched_position_shared_preference";
public static final String SHARED_PREF_AUDIO_CAPABILITIES =
- "com.android.tv.audio_capabilities";
+ CommonConstants.BASE_PACKAGE + ".audio_capabilities";
public static final String SHARED_PREF_RECURRING_RUNNER = "sharedPreferencesRecurringRunner";
public static final String SHARED_PREF_EPG = "epg_preferences";
public static final String SHARED_PREF_SERIES_RECORDINGS = "seriesRecordings";
@@ -39,15 +38,16 @@ public final class SharedPreferencesUtils {
public static final String SHARED_PREF_CHANNEL_LOGO_URIS = "channelLogoUris";
/** Stores the UI related settings */
public static final String SHARED_PREF_UI_SETTINGS = "ui_settings";
+
public static final String SHARED_PREF_PREVIEW_PROGRAMS = "previewPrograms";
private static boolean sInitializeCalled;
/**
- * {@link android.content.SharedPreferences} loads the preference file when
- * {@link Context#getSharedPreferences(String, int)} is called for the first time.
- * Call {@link Context#getSharedPreferences(String, int)} as early as possible to avoid the ANR
- * due to the file loading.
+ * {@link android.content.SharedPreferences} loads the preference file when {@link
+ * Context#getSharedPreferences(String, int)} is called for the first time. Call {@link
+ * Context#getSharedPreferences(String, int)} as early as possible to avoid the ANR due to the
+ * file loading.
*/
public static synchronized void initialize(final Context context, final Runnable postTask) {
if (!sInitializeCalled) {
@@ -59,15 +59,15 @@ public final class SharedPreferencesUtils {
context.getSharedPreferences(SHARED_PREF_FEATURES, Context.MODE_PRIVATE);
context.getSharedPreferences(SHARED_PREF_BROWSABLE, Context.MODE_PRIVATE);
context.getSharedPreferences(SHARED_PREF_WATCHED_HISTORY, Context.MODE_PRIVATE);
- context.getSharedPreferences(SHARED_PREF_DVR_WATCHED_POSITION,
- Context.MODE_PRIVATE);
- context.getSharedPreferences(SHARED_PREF_AUDIO_CAPABILITIES,
- Context.MODE_PRIVATE);
- context.getSharedPreferences(SHARED_PREF_RECURRING_RUNNER,
- Context.MODE_PRIVATE);
+ context.getSharedPreferences(
+ SHARED_PREF_DVR_WATCHED_POSITION, Context.MODE_PRIVATE);
+ context.getSharedPreferences(
+ SHARED_PREF_AUDIO_CAPABILITIES, Context.MODE_PRIVATE);
+ context.getSharedPreferences(
+ SHARED_PREF_RECURRING_RUNNER, Context.MODE_PRIVATE);
context.getSharedPreferences(SHARED_PREF_EPG, Context.MODE_PRIVATE);
- context.getSharedPreferences(SHARED_PREF_SERIES_RECORDINGS,
- Context.MODE_PRIVATE);
+ context.getSharedPreferences(
+ SHARED_PREF_SERIES_RECORDINGS, Context.MODE_PRIVATE);
context.getSharedPreferences(SHARED_PREF_UI_SETTINGS, Context.MODE_PRIVATE);
return null;
}
@@ -80,5 +80,5 @@ public final class SharedPreferencesUtils {
}
}
- private SharedPreferencesUtils() { }
+ private SharedPreferencesUtils() {}
}
diff --git a/src/com/android/tv/util/StringUtils.java b/common/src/com/android/tv/common/util/StringUtils.java
index 659807e2..b9461426 100644
--- a/src/com/android/tv/util/StringUtils.java
+++ b/common/src/com/android/tv/common/util/StringUtils.java
@@ -14,18 +14,14 @@
* limitations under the License.
*/
-package com.android.tv.util;
+package com.android.tv.common.util;
-/**
- * Utility class for handling {@link String}.
- */
+/** Utility class for handling {@link String}. */
public final class StringUtils {
- private StringUtils() { }
+ private StringUtils() {}
- /**
- * Returns compares two strings lexicographically and handles null values quietly.
- */
+ /** Returns compares two strings lexicographically and handles null values quietly. */
public static int compare(String a, String b) {
if (a == null) {
return b == null ? 0 : -1;
diff --git a/common/src/com/android/tv/common/util/SystemProperties.java b/common/src/com/android/tv/common/util/SystemProperties.java
new file mode 100644
index 00000000..a9f18d4b
--- /dev/null
+++ b/common/src/com/android/tv/common/util/SystemProperties.java
@@ -0,0 +1,53 @@
+/*
+ * 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.util;
+
+import com.android.tv.common.BooleanSystemProperty;
+
+/** A convenience class for getting TV related system properties. */
+public final class SystemProperties {
+
+ /** Allow Google Analytics for eng builds. */
+ public static final BooleanSystemProperty ALLOW_ANALYTICS_IN_ENG =
+ new BooleanSystemProperty("tv_allow_analytics_in_eng", false);
+
+ /** Allow Strict mode for debug builds. */
+ public static final BooleanSystemProperty ALLOW_STRICT_MODE =
+ new BooleanSystemProperty("tv_allow_strict_mode", true);
+
+ /** When true {@link android.view.KeyEvent}s are logged. Defaults to false. */
+ public static final BooleanSystemProperty LOG_KEYEVENT =
+ new BooleanSystemProperty("tv_log_keyevent", false);
+ /** When true debug keys are used. Defaults to false. */
+ public static final BooleanSystemProperty USE_DEBUG_KEYS =
+ new BooleanSystemProperty("tv_use_debug_keys", false);
+
+ /** Send {@link com.android.tv.analytics.Tracker} information. Defaults to {@code true}. */
+ public static final BooleanSystemProperty USE_TRACKER =
+ new BooleanSystemProperty("tv_use_tracker", true);
+
+ static {
+ updateSystemProperties();
+ }
+
+ private SystemProperties() {}
+
+ /** Update the TV related system properties. */
+ public static void updateSystemProperties() {
+ BooleanSystemProperty.resetAll();
+ }
+}
diff --git a/src/com/android/tv/tuner/util/SystemPropertiesProxy.java b/common/src/com/android/tv/common/util/SystemPropertiesProxy.java
index 2817ccbf..a3ffd0fa 100644
--- a/src/com/android/tv/tuner/util/SystemPropertiesProxy.java
+++ b/common/src/com/android/tv/common/util/SystemPropertiesProxy.java
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.tv.tuner.util;
+package com.android.tv.common.util;
import android.util.Log;
-
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -27,32 +26,35 @@ import java.lang.reflect.Method;
public class SystemPropertiesProxy {
private static final String TAG = "SystemPropertiesProxy";
- private SystemPropertiesProxy() { }
+ private SystemPropertiesProxy() {}
- public static boolean getBoolean(String key, boolean def)
- throws IllegalArgumentException {
+ public static boolean getBoolean(String key, boolean def) throws IllegalArgumentException {
try {
Class SystemPropertiesClass = Class.forName("android.os.SystemProperties");
- Method getBooleanMethod = SystemPropertiesClass.getDeclaredMethod("getBoolean",
- String.class, boolean.class);
+ Method getBooleanMethod =
+ SystemPropertiesClass.getDeclaredMethod(
+ "getBoolean", String.class, boolean.class);
getBooleanMethod.setAccessible(true);
return (boolean) getBooleanMethod.invoke(SystemPropertiesClass, key, def);
- } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException
+ } catch (InvocationTargetException
+ | IllegalAccessException
+ | NoSuchMethodException
| ClassNotFoundException e) {
Log.e(TAG, "Failed to invoke SystemProperties.getBoolean()", e);
}
return def;
}
- public static int getInt(String key, int def)
- throws IllegalArgumentException {
+ public static int getInt(String key, int def) throws IllegalArgumentException {
try {
Class SystemPropertiesClass = Class.forName("android.os.SystemProperties");
- Method getIntMethod = SystemPropertiesClass.getDeclaredMethod("getInt",
- String.class, int.class);
+ Method getIntMethod =
+ SystemPropertiesClass.getDeclaredMethod("getInt", String.class, int.class);
getIntMethod.setAccessible(true);
return (int) getIntMethod.invoke(SystemPropertiesClass, key, def);
- } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException
+ } catch (InvocationTargetException
+ | IllegalAccessException
+ | NoSuchMethodException
| ClassNotFoundException e) {
Log.e(TAG, "Failed to invoke SystemProperties.getInt()", e);
}
diff --git a/icu/icu4j/main/classes/core/src/com/ibm/icu/text/SCSU.java b/icu/icu4j/main/classes/core/src/com/ibm/icu/text/SCSU.java
deleted file mode 100644
index 39196fe3..00000000
--- a/icu/icu4j/main/classes/core/src/com/ibm/icu/text/SCSU.java
+++ /dev/null
@@ -1,186 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html#License
-/*
- *******************************************************************************
- * Copyright (C) 1996-2007, International Business Machines Corporation and *
- * others. All Rights Reserved. *
- *******************************************************************************
- */
-
-package com.ibm.icu.text;
-
-/**
- * An interface defining constants for the Standard Compression Scheme
- * for Unicode (SCSU) as outlined in <A
- * HREF="http://www.unicode.org/unicode/reports/tr6">Unicode Technical
- * Report #6</A>.
- *
- * @author Stephen F. Booth
- * @version 1.1 05 Aug 99
- * @version 1.0 26 Jul 99
- */
-interface SCSU
-{
- //==========================
- // Generic window shift
- //==========================
- final static int COMPRESSIONOFFSET = 0x80;
-
- //==========================
- // Number of windows
- //==========================
- final static int NUMWINDOWS = 8;
- final static int NUMSTATICWINDOWS = 8;
-
- //==========================
- // Indicates a window index is invalid
- //==========================
- final static int INVALIDWINDOW = -1;
-
- //==========================
- // Indicates a character doesn't exist in input (past end of buffer)
- //==========================
- final static int INVALIDCHAR = -1;
-
- //==========================
- // Compression modes
- //==========================
- final static int SINGLEBYTEMODE = 0;
- final static int UNICODEMODE = 1;
-
- //==========================
- // Maximum value for a window's index
- //==========================
- final static int MAXINDEX = 0xFF;
-
- //==========================
- // Reserved index value (characters belongs to first block)
- //==========================
- final static int RESERVEDINDEX = 0x00;
-
- //==========================
- // Indices for scripts which cross a half-block boundary
- //==========================
- final static int LATININDEX = 0xF9;
- final static int IPAEXTENSIONINDEX = 0xFA;
- final static int GREEKINDEX = 0xFB;
- final static int ARMENIANINDEX = 0xFC;
- final static int HIRAGANAINDEX = 0xFD;
- final static int KATAKANAINDEX = 0xFE;
- final static int HALFWIDTHKATAKANAINDEX = 0xFF;
-
- //==========================
- // Single-byte mode tags
- //==========================
- final static int SDEFINEX = 0x0B;
- final static int SRESERVED = 0x0C; // reserved value
- final static int SQUOTEU = 0x0E;
- final static int SCHANGEU = 0x0F;
-
- final static int SQUOTE0 = 0x01;
- final static int SQUOTE1 = 0x02;
- final static int SQUOTE2 = 0x03;
- final static int SQUOTE3 = 0x04;
- final static int SQUOTE4 = 0x05;
- final static int SQUOTE5 = 0x06;
- final static int SQUOTE6 = 0x07;
- final static int SQUOTE7 = 0x08;
-
- final static int SCHANGE0 = 0x10;
- final static int SCHANGE1 = 0x11;
- final static int SCHANGE2 = 0x12;
- final static int SCHANGE3 = 0x13;
- final static int SCHANGE4 = 0x14;
- final static int SCHANGE5 = 0x15;
- final static int SCHANGE6 = 0x16;
- final static int SCHANGE7 = 0x17;
-
- final static int SDEFINE0 = 0x18;
- final static int SDEFINE1 = 0x19;
- final static int SDEFINE2 = 0x1A;
- final static int SDEFINE3 = 0x1B;
- final static int SDEFINE4 = 0x1C;
- final static int SDEFINE5 = 0x1D;
- final static int SDEFINE6 = 0x1E;
- final static int SDEFINE7 = 0x1F;
-
- //==========================
- // Unicode mode tags
- //==========================
- final static int UCHANGE0 = 0xE0;
- final static int UCHANGE1 = 0xE1;
- final static int UCHANGE2 = 0xE2;
- final static int UCHANGE3 = 0xE3;
- final static int UCHANGE4 = 0xE4;
- final static int UCHANGE5 = 0xE5;
- final static int UCHANGE6 = 0xE6;
- final static int UCHANGE7 = 0xE7;
-
- final static int UDEFINE0 = 0xE8;
- final static int UDEFINE1 = 0xE9;
- final static int UDEFINE2 = 0xEA;
- final static int UDEFINE3 = 0xEB;
- final static int UDEFINE4 = 0xEC;
- final static int UDEFINE5 = 0xED;
- final static int UDEFINE6 = 0xEE;
- final static int UDEFINE7 = 0xEF;
-
- final static int UQUOTEU = 0xF0;
- final static int UDEFINEX = 0xF1;
- final static int URESERVED = 0xF2; // reserved value
-
-
- //==========================
- // Class variables
- //==========================
-
- /** For window offset mapping */
- final static int [] sOffsetTable = {
- // table generated by CompressionTableGenerator
- 0x0, 0x80, 0x100, 0x180, 0x200, 0x280, 0x300, 0x380, 0x400,
- 0x480, 0x500, 0x580, 0x600, 0x680, 0x700, 0x780, 0x800, 0x880,
- 0x900, 0x980, 0xa00, 0xa80, 0xb00, 0xb80, 0xc00, 0xc80, 0xd00,
- 0xd80, 0xe00, 0xe80, 0xf00, 0xf80, 0x1000, 0x1080, 0x1100,
- 0x1180, 0x1200, 0x1280, 0x1300, 0x1380, 0x1400, 0x1480,
- 0x1500, 0x1580, 0x1600, 0x1680, 0x1700, 0x1780, 0x1800,
- 0x1880, 0x1900, 0x1980, 0x1a00, 0x1a80, 0x1b00, 0x1b80,
- 0x1c00, 0x1c80, 0x1d00, 0x1d80, 0x1e00, 0x1e80, 0x1f00,
- 0x1f80, 0x2000, 0x2080, 0x2100, 0x2180, 0x2200, 0x2280,
- 0x2300, 0x2380, 0x2400, 0x2480, 0x2500, 0x2580, 0x2600,
- 0x2680, 0x2700, 0x2780, 0x2800, 0x2880, 0x2900, 0x2980,
- 0x2a00, 0x2a80, 0x2b00, 0x2b80, 0x2c00, 0x2c80, 0x2d00,
- 0x2d80, 0x2e00, 0x2e80, 0x2f00, 0x2f80, 0x3000, 0x3080,
- 0x3100, 0x3180, 0x3200, 0x3280, 0x3300, 0x3380, 0xe000,
- 0xe080, 0xe100, 0xe180, 0xe200, 0xe280, 0xe300, 0xe380,
- 0xe400, 0xe480, 0xe500, 0xe580, 0xe600, 0xe680, 0xe700,
- 0xe780, 0xe800, 0xe880, 0xe900, 0xe980, 0xea00, 0xea80,
- 0xeb00, 0xeb80, 0xec00, 0xec80, 0xed00, 0xed80, 0xee00,
- 0xee80, 0xef00, 0xef80, 0xf000, 0xf080, 0xf100, 0xf180,
- 0xf200, 0xf280, 0xf300, 0xf380, 0xf400, 0xf480, 0xf500,
- 0xf580, 0xf600, 0xf680, 0xf700, 0xf780, 0xf800, 0xf880,
- 0xf900, 0xf980, 0xfa00, 0xfa80, 0xfb00, 0xfb80, 0xfc00,
- 0xfc80, 0xfd00, 0xfd80, 0xfe00, 0xfe80, 0xff00, 0xff80, 0x0,
- 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
- 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
- 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
- 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
- 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
- 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
- 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0, 0x250, 0x370,
- 0x530, 0x3040, 0x30a0, 0xff60
- };
-
- /** Static compression window offsets */
- final static int [] sOffsets = {
- 0x0000, // for quoting single-byte mode tags
- 0x0080, // Latin-1 Supplement
- 0x0100, // Latin Extended-A
- 0x0300, // Combining Diacritical Marks
- 0x2000, // General Punctuation
- 0x2080, // Curency Symbols
- 0x2100, // Letterlike Symbols and Number Forms
- 0x3000 // CJK Symbols and Punctuation
- };
-
-}
-
diff --git a/icu/icu4j/main/classes/core/src/com/ibm/icu/text/UnicodeDecompressor.java b/icu/icu4j/main/classes/core/src/com/ibm/icu/text/UnicodeDecompressor.java
deleted file mode 100644
index 6789469f..00000000
--- a/icu/icu4j/main/classes/core/src/com/ibm/icu/text/UnicodeDecompressor.java
+++ /dev/null
@@ -1,559 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html#License
-/*
- *******************************************************************************
- * Copyright (C) 1996-2016, International Business Machines Corporation and *
- * others. All Rights Reserved. *
- *******************************************************************************
- */
-
-package com.ibm.icu.text;
-
-/**
-* A decompression engine implementing the Standard Compression Scheme
-* for Unicode (SCSU) as outlined in <A
-* HREF="http://www.unicode.org/unicode/reports/tr6">Unicode Technical
-* Report #6</A>.
-*
-* <P><STRONG>USAGE</STRONG></P>
-*
-* <P>The static methods on <TT>UnicodeDecompressor</TT> may be used in a
-* straightforward manner to decompress simple strings:</P>
-*
-* <PRE>
-* byte [] compressed = ... ; // get compressed bytes from somewhere
-* String result = UnicodeDecompressor.decompress(compressed);
-* </PRE>
-*
-* <P>The static methods have a fairly large memory footprint.
-* For finer-grained control over memory usage,
-* <TT>UnicodeDecompressor</TT> offers more powerful APIs allowing
-* iterative decompression:</P>
-*
-* <PRE>
-* // Decompress an array "bytes" of length "len" using a buffer of 512 chars
-* // to the Writer "out"
-*
-* UnicodeDecompressor myDecompressor = new UnicodeDecompressor();
-* final static int BUFSIZE = 512;
-* char [] charBuffer = new char [ BUFSIZE ];
-* int charsWritten = 0;
-* int [] bytesRead = new int [1];
-* int totalBytesDecompressed = 0;
-* int totalCharsWritten = 0;
-*
-* do {
-* // do the decompression
-* charsWritten = myDecompressor.decompress(bytes, totalBytesDecompressed,
-* len, bytesRead,
-* charBuffer, 0, BUFSIZE);
-*
-* // do something with the current set of chars
-* out.write(charBuffer, 0, charsWritten);
-*
-* // update the no. of bytes decompressed
-* totalBytesDecompressed += bytesRead[0];
-*
-* // update the no. of chars written
-* totalCharsWritten += charsWritten;
-*
-* } while(totalBytesDecompressed &lt; len);
-*
-* myDecompressor.reset(); // reuse decompressor
-* </PRE>
-*
-* <P>Decompression is performed according to the standard set forth in
-* <A HREF="http://www.unicode.org/unicode/reports/tr6">Unicode Technical
-* Report #6</A></P>
-*
-* @see UnicodeCompressor
-*
-* @author Stephen F. Booth
-* @stable ICU 2.4
-*/
-public final class UnicodeDecompressor implements SCSU
-{
- //==========================
- // Instance variables
- //==========================
-
- /** Alias to current dynamic window */
- private int fCurrentWindow = 0;
-
- /** Dynamic compression window offsets */
- private int [] fOffsets = new int [ NUMWINDOWS ];
-
- /** Current compression mode */
- private int fMode = SINGLEBYTEMODE;
-
- /** Size of our internal buffer */
- private final static int BUFSIZE = 3;
-
- /** Internal buffer for saving state */
- private byte [] fBuffer = new byte [BUFSIZE];
-
- /** Number of characters in our internal buffer */
- private int fBufferLength = 0;
-
-
- /**
- * Create a UnicodeDecompressor.
- * Sets all windows to their default values.
- * @see #reset
- * @stable ICU 2.4
- */
- public UnicodeDecompressor(){
- reset(); // initialize to defaults
- }
-
- /**
- * Decompress a byte array into a String.
- * @param buffer The byte array to decompress.
- * @return A String containing the decompressed characters.
- * @see #decompress(byte [], int, int)
- * @stable ICU 2.4
- */
- public static String decompress(byte [] buffer){
- char [] buf = decompress(buffer, 0, buffer.length);
- return new String(buf);
- }
-
- /**
- * Decompress a byte array into a Unicode character array.
- * @param buffer The byte array to decompress.
- * @param start The start of the byte run to decompress.
- * @param limit The limit of the byte run to decompress.
- * @return A character array containing the decompressed bytes.
- * @see #decompress(byte [])
- * @stable ICU 2.4
- */
- public static char [] decompress(byte [] buffer, int start, int limit) {
- UnicodeDecompressor comp = new UnicodeDecompressor();
-
- // use a buffer we know will never overflow
- // in the worst case, each byte will decompress
- // to a surrogate pair (buffer must be at least 2 chars)
- int len = Math.max(2, 2 * (limit - start));
- char [] temp = new char [len];
-
- int charCount = comp.decompress(buffer, start, limit, null,
- temp, 0, len);
-
- char [] result = new char [charCount];
- System.arraycopy(temp, 0, result, 0, charCount);
- return result;
- }
-
- /**
- * Decompress a byte array into a Unicode character array.
- *
- * This function will either completely fill the output buffer,
- * or consume the entire input.
- *
- * @param byteBuffer The byte buffer to decompress.
- * @param byteBufferStart The start of the byte run to decompress.
- * @param byteBufferLimit The limit of the byte run to decompress.
- * @param bytesRead A one-element array. If not null, on return
- * the number of bytes read from byteBuffer.
- * @param charBuffer A buffer to receive the decompressed data.
- * This buffer must be at minimum two characters in size.
- * @param charBufferStart The starting offset to which to write
- * decompressed data.
- * @param charBufferLimit The limiting offset for writing
- * decompressed data.
- * @return The number of Unicode characters written to charBuffer.
- * @stable ICU 2.4
- */
- public int decompress(byte [] byteBuffer,
- int byteBufferStart,
- int byteBufferLimit,
- int [] bytesRead,
- char [] charBuffer,
- int charBufferStart,
- int charBufferLimit)
- {
- // the current position in the source byte buffer
- int bytePos = byteBufferStart;
-
- // the current position in the target char buffer
- int ucPos = charBufferStart;
-
- // the current byte from the source buffer
- int aByte = 0x00;
-
-
- // charBuffer must be at least 2 chars in size
- if(charBuffer.length < 2 || (charBufferLimit - charBufferStart) < 2)
- throw new IllegalArgumentException("charBuffer.length < 2");
-
- // if our internal buffer isn't empty, flush its contents
- // to the output buffer before doing any more decompression
- if(fBufferLength > 0) {
-
- int newBytes = 0;
-
- // fill the buffer completely, to guarantee one full character
- if(fBufferLength != BUFSIZE) {
- newBytes = fBuffer.length - fBufferLength;
-
- // verify there are newBytes bytes in byteBuffer
- if(byteBufferLimit - byteBufferStart < newBytes)
- newBytes = byteBufferLimit - byteBufferStart;
-
- System.arraycopy(byteBuffer, byteBufferStart,
- fBuffer, fBufferLength, newBytes);
- }
-
- // reset buffer length to 0 before recursive call
- fBufferLength = 0;
-
- // call self recursively to decompress the buffer
- int count = decompress(fBuffer, 0, fBuffer.length, null,
- charBuffer, charBufferStart,
- charBufferLimit);
-
- // update the positions into the arrays
- ucPos += count;
- bytePos += newBytes;
- }
-
- // the main decompression loop
- mainLoop:
- while(bytePos < byteBufferLimit && ucPos < charBufferLimit) {
- switch(fMode) {
- case SINGLEBYTEMODE:
- // single-byte mode decompression loop
- singleByteModeLoop:
- while(bytePos < byteBufferLimit && ucPos < charBufferLimit) {
- aByte = byteBuffer[bytePos++] & 0xFF;
- switch(aByte) {
- // All bytes from 0x80 through 0xFF are remapped
- // to chars or surrogate pairs according to the
- // currently active window
- case 0x80: case 0x81: case 0x82: case 0x83: case 0x84:
- case 0x85: case 0x86: case 0x87: case 0x88: case 0x89:
- case 0x8A: case 0x8B: case 0x8C: case 0x8D: case 0x8E:
- case 0x8F: case 0x90: case 0x91: case 0x92: case 0x93:
- case 0x94: case 0x95: case 0x96: case 0x97: case 0x98:
- case 0x99: case 0x9A: case 0x9B: case 0x9C: case 0x9D:
- case 0x9E: case 0x9F: case 0xA0: case 0xA1: case 0xA2:
- case 0xA3: case 0xA4: case 0xA5: case 0xA6: case 0xA7:
- case 0xA8: case 0xA9: case 0xAA: case 0xAB: case 0xAC:
- case 0xAD: case 0xAE: case 0xAF: case 0xB0: case 0xB1:
- case 0xB2: case 0xB3: case 0xB4: case 0xB5: case 0xB6:
- case 0xB7: case 0xB8: case 0xB9: case 0xBA: case 0xBB:
- case 0xBC: case 0xBD: case 0xBE: case 0xBF: case 0xC0:
- case 0xC1: case 0xC2: case 0xC3: case 0xC4: case 0xC5:
- case 0xC6: case 0xC7: case 0xC8: case 0xC9: case 0xCA:
- case 0xCB: case 0xCC: case 0xCD: case 0xCE: case 0xCF:
- case 0xD0: case 0xD1: case 0xD2: case 0xD3: case 0xD4:
- case 0xD5: case 0xD6: case 0xD7: case 0xD8: case 0xD9:
- case 0xDA: case 0xDB: case 0xDC: case 0xDD: case 0xDE:
- case 0xDF: case 0xE0: case 0xE1: case 0xE2: case 0xE3:
- case 0xE4: case 0xE5: case 0xE6: case 0xE7: case 0xE8:
- case 0xE9: case 0xEA: case 0xEB: case 0xEC: case 0xED:
- case 0xEE: case 0xEF: case 0xF0: case 0xF1: case 0xF2:
- case 0xF3: case 0xF4: case 0xF5: case 0xF6: case 0xF7:
- case 0xF8: case 0xF9: case 0xFA: case 0xFB: case 0xFC:
- case 0xFD: case 0xFE: case 0xFF:
- // For offsets <= 0xFFFF, convert to a single char
- // by adding the window's offset and subtracting
- // the generic compression offset
- if(fOffsets[ fCurrentWindow ] <= 0xFFFF) {
- charBuffer[ucPos++] = (char)
- (aByte + fOffsets[ fCurrentWindow ]
- - COMPRESSIONOFFSET);
- }
- // For offsets > 0x10000, convert to a surrogate pair by
- // normBase = window's offset - 0x10000
- // high surr. = 0xD800 + (normBase >> 10)
- // low surr. = 0xDC00 + (normBase & 0x3FF) + (byte & 0x7F)
- else {
- // make sure there is enough room to write
- // both characters
- // if not, save state and break out
- if((ucPos + 1) >= charBufferLimit) {
- --bytePos;
- System.arraycopy(byteBuffer, bytePos,
- fBuffer, 0,
- byteBufferLimit - bytePos);
- fBufferLength = byteBufferLimit - bytePos;
- bytePos += fBufferLength;
- break mainLoop;
- }
-
- int normalizedBase = fOffsets[ fCurrentWindow ]
- - 0x10000;
- charBuffer[ucPos++] = (char)
- (0xD800 + (normalizedBase >> 10));
- charBuffer[ucPos++] = (char)
- (0xDC00 + (normalizedBase & 0x3FF)+(aByte & 0x7F));
- }
- break;
-
- // bytes from 0x20 through 0x7F are treated as ASCII and
- // are remapped to chars by padding the high byte
- // (this is the same as quoting from static window 0)
- // NUL (0x00), HT (0x09), CR (0x0A), LF (0x0D)
- // are treated as ASCII as well
- case 0x00: case 0x09: case 0x0A: case 0x0D:
- case 0x20: case 0x21: case 0x22: case 0x23: case 0x24:
- case 0x25: case 0x26: case 0x27: case 0x28: case 0x29:
- case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2E:
- case 0x2F: case 0x30: case 0x31: case 0x32: case 0x33:
- case 0x34: case 0x35: case 0x36: case 0x37: case 0x38:
- case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D:
- case 0x3E: case 0x3F: case 0x40: case 0x41: case 0x42:
- case 0x43: case 0x44: case 0x45: case 0x46: case 0x47:
- case 0x48: case 0x49: case 0x4A: case 0x4B: case 0x4C:
- case 0x4D: case 0x4E: case 0x4F: case 0x50: case 0x51:
- case 0x52: case 0x53: case 0x54: case 0x55: case 0x56:
- case 0x57: case 0x58: case 0x59: case 0x5A: case 0x5B:
- case 0x5C: case 0x5D: case 0x5E: case 0x5F: case 0x60:
- case 0x61: case 0x62: case 0x63: case 0x64: case 0x65:
- case 0x66: case 0x67: case 0x68: case 0x69: case 0x6A:
- case 0x6B: case 0x6C: case 0x6D: case 0x6E: case 0x6F:
- case 0x70: case 0x71: case 0x72: case 0x73: case 0x74:
- case 0x75: case 0x76: case 0x77: case 0x78: case 0x79:
- case 0x7A: case 0x7B: case 0x7C: case 0x7D: case 0x7E:
- case 0x7F:
- charBuffer[ucPos++] = (char) aByte;
- break;
-
- // quote unicode
- case SQUOTEU:
- // verify we have two bytes following tag
- // if not, save state and break out
- if( (bytePos + 1) >= byteBufferLimit ) {
- --bytePos;
- System.arraycopy(byteBuffer, bytePos,
- fBuffer, 0,
- byteBufferLimit - bytePos);
- fBufferLength = byteBufferLimit - bytePos;
- bytePos += fBufferLength;
- break mainLoop;
- }
-
- aByte = byteBuffer[bytePos++];
- charBuffer[ucPos++] = (char)
- (aByte << 8 | (byteBuffer[bytePos++] & 0xFF));
- break;
-
- // switch to Unicode mode
- case SCHANGEU:
- fMode = UNICODEMODE;
- break singleByteModeLoop;
- //break;
-
- // handle all quote tags
- case SQUOTE0: case SQUOTE1: case SQUOTE2: case SQUOTE3:
- case SQUOTE4: case SQUOTE5: case SQUOTE6: case SQUOTE7:
- // verify there is a byte following the tag
- // if not, save state and break out
- if(bytePos >= byteBufferLimit) {
- --bytePos;
- System.arraycopy(byteBuffer, bytePos,
- fBuffer, 0,
- byteBufferLimit - bytePos);
- fBufferLength = byteBufferLimit - bytePos;
- bytePos += fBufferLength;
- break mainLoop;
- }
-
- // if the byte is in the range 0x00 - 0x7F, use
- // static window n otherwise, use dynamic window n
- int dByte = byteBuffer[bytePos++] & 0xFF;
- charBuffer[ucPos++] = (char)
- (dByte+ (dByte >= 0x00 && dByte < 0x80
- ? sOffsets[aByte - SQUOTE0]
- : (fOffsets[aByte - SQUOTE0]
- - COMPRESSIONOFFSET)));
- break;
-
- // handle all change tags
- case SCHANGE0: case SCHANGE1: case SCHANGE2: case SCHANGE3:
- case SCHANGE4: case SCHANGE5: case SCHANGE6: case SCHANGE7:
- fCurrentWindow = aByte - SCHANGE0;
- break;
-
- // handle all define tags
- case SDEFINE0: case SDEFINE1: case SDEFINE2: case SDEFINE3:
- case SDEFINE4: case SDEFINE5: case SDEFINE6: case SDEFINE7:
- // verify there is a byte following the tag
- // if not, save state and break out
- if(bytePos >= byteBufferLimit) {
- --bytePos;
- System.arraycopy(byteBuffer, bytePos,
- fBuffer, 0,
- byteBufferLimit - bytePos);
- fBufferLength = byteBufferLimit - bytePos;
- bytePos += fBufferLength;
- break mainLoop;
- }
-
- fCurrentWindow = aByte - SDEFINE0;
- fOffsets[fCurrentWindow] =
- sOffsetTable[byteBuffer[bytePos++] & 0xFF];
- break;
-
- // handle define extended tag
- case SDEFINEX:
- // verify we have two bytes following tag
- // if not, save state and break out
- if((bytePos + 1) >= byteBufferLimit ) {
- --bytePos;
- System.arraycopy(byteBuffer, bytePos,
- fBuffer, 0,
- byteBufferLimit - bytePos);
- fBufferLength = byteBufferLimit - bytePos;
- bytePos += fBufferLength;
- break mainLoop;
- }
-
- aByte = byteBuffer[bytePos++] & 0xFF;
- fCurrentWindow = (aByte & 0xE0) >> 5;
- fOffsets[fCurrentWindow] = 0x10000 +
- (0x80 * (((aByte & 0x1F) << 8)
- | (byteBuffer[bytePos++] & 0xFF)));
- break;
-
- // reserved, shouldn't happen
- case SRESERVED:
- break;
-
- } // end switch
- } // end while
- break;
-
- case UNICODEMODE:
- // unicode mode decompression loop
- unicodeModeLoop:
- while(bytePos < byteBufferLimit && ucPos < charBufferLimit) {
- aByte = byteBuffer[bytePos++] & 0xFF;
- switch(aByte) {
- // handle all define tags
- case UDEFINE0: case UDEFINE1: case UDEFINE2: case UDEFINE3:
- case UDEFINE4: case UDEFINE5: case UDEFINE6: case UDEFINE7:
- // verify there is a byte following tag
- // if not, save state and break out
- if(bytePos >= byteBufferLimit ) {
- --bytePos;
- System.arraycopy(byteBuffer, bytePos,
- fBuffer, 0,
- byteBufferLimit - bytePos);
- fBufferLength = byteBufferLimit - bytePos;
- bytePos += fBufferLength;
- break mainLoop;
- }
-
- fCurrentWindow = aByte - UDEFINE0;
- fOffsets[fCurrentWindow] =
- sOffsetTable[byteBuffer[bytePos++] & 0xFF];
- fMode = SINGLEBYTEMODE;
- break unicodeModeLoop;
- //break;
-
- // handle define extended tag
- case UDEFINEX:
- // verify we have two bytes following tag
- // if not, save state and break out
- if((bytePos + 1) >= byteBufferLimit ) {
- --bytePos;
- System.arraycopy(byteBuffer, bytePos,
- fBuffer, 0,
- byteBufferLimit - bytePos);
- fBufferLength = byteBufferLimit - bytePos;
- bytePos += fBufferLength;
- break mainLoop;
- }
-
- aByte = byteBuffer[bytePos++] & 0xFF;
- fCurrentWindow = (aByte & 0xE0) >> 5;
- fOffsets[fCurrentWindow] = 0x10000 +
- (0x80 * (((aByte & 0x1F) << 8)
- | (byteBuffer[bytePos++] & 0xFF)));
- fMode = SINGLEBYTEMODE;
- break unicodeModeLoop;
- //break;
-
- // handle all change tags
- case UCHANGE0: case UCHANGE1: case UCHANGE2: case UCHANGE3:
- case UCHANGE4: case UCHANGE5: case UCHANGE6: case UCHANGE7:
- fCurrentWindow = aByte - UCHANGE0;
- fMode = SINGLEBYTEMODE;
- break unicodeModeLoop;
- //break;
-
- // quote unicode
- case UQUOTEU:
- // verify we have two bytes following tag
- // if not, save state and break out
- if(bytePos >= byteBufferLimit - 1) {
- --bytePos;
- System.arraycopy(byteBuffer, bytePos,
- fBuffer, 0,
- byteBufferLimit - bytePos);
- fBufferLength = byteBufferLimit - bytePos;
- bytePos += fBufferLength;
- break mainLoop;
- }
-
- aByte = byteBuffer[bytePos++];
- charBuffer[ucPos++] = (char)
- (aByte << 8 | (byteBuffer[bytePos++] & 0xFF));
- break;
-
- default:
- // verify there is a byte following tag
- // if not, save state and break out
- if(bytePos >= byteBufferLimit ) {
- --bytePos;
- System.arraycopy(byteBuffer, bytePos,
- fBuffer, 0,
- byteBufferLimit - bytePos);
- fBufferLength = byteBufferLimit - bytePos;
- bytePos += fBufferLength;
- break mainLoop;
- }
-
- charBuffer[ucPos++] = (char)
- (aByte << 8 | (byteBuffer[bytePos++] & 0xFF));
- break;
-
- } // end switch
- } // end while
- break;
-
- } // end switch( fMode )
- } // end while
-
- // fill in output parameter
- if(bytesRead != null)
- bytesRead [0] = (bytePos - byteBufferStart);
-
- // return # of chars written
- return (ucPos - charBufferStart);
- }
-
- /**
- * Reset the decompressor to its initial state.
- * @stable ICU 2.4
- */
- public void reset()
- {
- // reset dynamic windows
- fOffsets[0] = 0x0080; // Latin-1
- fOffsets[1] = 0x00C0; // Latin-1 Supplement + Latin Extended-A
- fOffsets[2] = 0x0400; // Cyrillic
- fOffsets[3] = 0x0600; // Arabic
- fOffsets[4] = 0x0900; // Devanagari
- fOffsets[5] = 0x3040; // Hiragana
- fOffsets[6] = 0x30A0; // Katakana
- fOffsets[7] = 0xFF00; // Fullwidth ASCII
-
-
- fCurrentWindow = 0; // Make current window Latin-1
- fMode = SINGLEBYTEMODE; // Always start in single-byte mode
- fBufferLength = 0; // Empty buffer
- }
-}
diff --git a/jni/DvbManager.cpp b/jni/DvbManager.cpp
index b344f803..8e51999c 100644
--- a/jni/DvbManager.cpp
+++ b/jni/DvbManager.cpp
@@ -39,22 +39,17 @@ static double currentTimeMillis() {
DvbManager::DvbManager(JNIEnv *env, jobject)
: mFeFd(-1),
- mDemuxFd(-1),
mDvrFd(-1),
mPatFilterFd(-1),
mDvbApiVersion(DVB_API_VERSION_UNDEFINED),
mDeliverySystemType(-1),
mFeHasLock(false),
mHasPendingTune(false) {
- (void) mDemuxFd; // suppress unused warning
- jclass clazz = env->FindClass(
- "com/android/tv/tuner/TunerHal");
- mOpenDvbFrontEndMethodID = env->GetMethodID(
- clazz, "openDvbFrontEndFd", "()I");
- mOpenDvbDemuxMethodID = env->GetMethodID(
- clazz, "openDvbDemuxFd", "()I");
- mOpenDvbDvrMethodID = env->GetMethodID(
- clazz, "openDvbDvrFd", "()I");
+ jclass clazz = env->FindClass("com/android/tv/tuner/TunerHal");
+ mOpenDvbFrontEndMethodID =
+ env->GetMethodID(clazz, "openDvbFrontEndFd", "()I");
+ mOpenDvbDemuxMethodID = env->GetMethodID(clazz, "openDvbDemuxFd", "()I");
+ mOpenDvbDvrMethodID = env->GetMethodID(clazz, "openDvbDvrFd", "()I");
}
DvbManager::~DvbManager() {
@@ -118,11 +113,13 @@ int DvbManager::tune(JNIEnv *env, jobject thiz,
if (mDvbApiVersion == DVB_API_VERSION5) {
struct dtv_property deliverySystemProperty = {
- .cmd = DTV_DELIVERY_SYSTEM, .u.data = SYS_ATSC
+ .cmd = DTV_DELIVERY_SYSTEM
};
+ deliverySystemProperty.u.data = SYS_ATSC;
struct dtv_property frequencyProperty = {
- .cmd = DTV_FREQUENCY, .u.data = static_cast<__u32>(frequency)
+ .cmd = DTV_FREQUENCY
};
+ frequencyProperty.u.data = static_cast<__u32>(frequency);
struct dtv_property modulationProperty = { .cmd = DTV_MODULATION };
if (strncmp(modulationStr, "QAM", 3) == 0) {
modulationProperty.u.data = QAM_AUTO;
@@ -503,4 +500,4 @@ int DvbManager::getDeliverySystemType(JNIEnv *env, jobject thiz) {
}
}
return mDeliverySystemType;
-}
+} \ No newline at end of file
diff --git a/jni/DvbManager.h b/jni/DvbManager.h
index 2252332c..124fa943 100644
--- a/jni/DvbManager.h
+++ b/jni/DvbManager.h
@@ -36,32 +36,30 @@ class DvbManager {
static const int DVB_API_VERSION5 = 5;
static const int FILTER_TYPE_OTHER =
- com_android_tv_tuner_TunerHal_FILTER_TYPE_OTHER;
+ com_android_tv_tuner_TunerHal_FILTER_TYPE_OTHER;
static const int FILTER_TYPE_AUDIO =
- com_android_tv_tuner_TunerHal_FILTER_TYPE_AUDIO;
+ com_android_tv_tuner_TunerHal_FILTER_TYPE_AUDIO;
static const int FILTER_TYPE_VIDEO =
- com_android_tv_tuner_TunerHal_FILTER_TYPE_VIDEO;
+ com_android_tv_tuner_TunerHal_FILTER_TYPE_VIDEO;
static const int FILTER_TYPE_PCR =
- com_android_tv_tuner_TunerHal_FILTER_TYPE_PCR;
+ com_android_tv_tuner_TunerHal_FILTER_TYPE_PCR;
static const int DELIVERY_SYSTEM_UNDEFINED =
- com_android_tv_tuner_TunerHal_DELIVERY_SYSTEM_UNDEFINED;
+ com_android_tv_tuner_TunerHal_DELIVERY_SYSTEM_UNDEFINED;
static const int DELIVERY_SYSTEM_ATSC =
- com_android_tv_tuner_TunerHal_DELIVERY_SYSTEM_ATSC;
+ com_android_tv_tuner_TunerHal_DELIVERY_SYSTEM_ATSC;
static const int DELIVERY_SYSTEM_DVBC =
- com_android_tv_tuner_TunerHal_DDELIVERY_SYSTEM_DVBC;
+ com_android_tv_tuner_TunerHal_DDELIVERY_SYSTEM_DVBC;
static const int DELIVERY_SYSTEM_DVBS =
- com_android_tv_tuner_TunerHal_DELIVERY_SYSTEM_DVBS;
+ com_android_tv_tuner_TunerHal_DELIVERY_SYSTEM_DVBS;
static const int DELIVERY_SYSTEM_DVBS2 =
- com_android_tv_tuner_TunerHal_DELIVERY_SYSTEM_DVBS2;
+ com_android_tv_tuner_TunerHal_DELIVERY_SYSTEM_DVBS2;
static const int DELIVERY_SYSTEM_DVBT =
- com_android_tv_tuner_TunerHal_DELIVERY_SYSTEM_DVBT;
+ com_android_tv_tuner_TunerHal_DELIVERY_SYSTEM_DVBT;
static const int DELIVERY_SYSTEM_DVBT2 =
- com_android_tv_tuner_TunerHal_DELIVERY_SYSTEM_DVBT2;
-
+ com_android_tv_tuner_TunerHal_DELIVERY_SYSTEM_DVBT2;
int mFeFd;
- int mDemuxFd;
int mDvrFd;
int mPatFilterFd;
int mDvbApiVersion;
diff --git a/jni/gen_jni.sh b/jni/gen_jni.sh
index 6acadee3..c06b7b91 100755
--- a/jni/gen_jni.sh
+++ b/jni/gen_jni.sh
@@ -1,3 +1,18 @@
#!/bin/bash
+#
+# Copyright (C) 2017 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.
+
javah -jni -classpath ../../bin/classes:../../../../../../prebuilts/sdk/current/public/android.jar -o tunertvinput_jni.h com.android.tv.tuner.TunerHal
diff --git a/jni/tunertvinput_jni.cpp b/jni/tunertvinput_jni.cpp
index 9ad15141..368e2d5b 100644
--- a/jni/tunertvinput_jni.cpp
+++ b/jni/tunertvinput_jni.cpp
@@ -37,14 +37,13 @@ static std::map<jlong, DvbManager *> sDvbManagers;
* Method: nativeFinalize
* Signature: (J)V
*/
-JNIEXPORT void JNICALL
-Java_com_android_tv_tuner_TunerHal_nativeFinalize
-(JNIEnv *, jobject, jlong deviceId) {
- std::map<jlong, DvbManager *>::iterator it = sDvbManagers.find(deviceId);
- if (it != sDvbManagers.end()) {
- delete it->second;
- sDvbManagers.erase(it);
- }
+JNIEXPORT void JNICALL Java_com_android_tv_tuner_TunerHal_nativeFinalize(
+ JNIEnv *, jobject, jlong deviceId) {
+ std::map<jlong, DvbManager *>::iterator it = sDvbManagers.find(deviceId);
+ if (it != sDvbManagers.end()) {
+ delete it->second;
+ sDvbManagers.erase(it);
+ }
}
/*
@@ -52,20 +51,20 @@ Java_com_android_tv_tuner_TunerHal_nativeFinalize
* Method: nativeTune
* Signature: (JILjava/lang/String;)Z
*/
-JNIEXPORT jboolean JNICALL
-Java_com_android_tv_tuner_TunerHal_nativeTune
-(JNIEnv *env, jobject thiz, jlong deviceId, jint frequency, jstring modulation, jint timeout_ms) {
- std::map<jlong, DvbManager *>::iterator it = sDvbManagers.find(deviceId);
- DvbManager *dvbManager;
- if (it == sDvbManagers.end()) {
- dvbManager = new DvbManager(env, thiz);
- sDvbManagers.insert(std::pair<jlong, DvbManager *>(deviceId, dvbManager));
- } else {
- dvbManager = it->second;
- }
- int res = dvbManager->tune(env, thiz,
- frequency, env->GetStringUTFChars(modulation, 0), timeout_ms);
- return (res == 0);
+JNIEXPORT jboolean JNICALL Java_com_android_tv_tuner_TunerHal_nativeTune(
+ JNIEnv *env, jobject thiz, jlong deviceId, jint frequency,
+ jstring modulation, jint timeout_ms) {
+ std::map<jlong, DvbManager *>::iterator it = sDvbManagers.find(deviceId);
+ DvbManager *dvbManager;
+ if (it == sDvbManagers.end()) {
+ dvbManager = new DvbManager(env, thiz);
+ sDvbManagers.insert(std::pair<jlong, DvbManager *>(deviceId, dvbManager));
+ } else {
+ dvbManager = it->second;
+ }
+ int res = dvbManager->tune(env, thiz, frequency,
+ env->GetStringUTFChars(modulation, 0), timeout_ms);
+ return (res == 0);
}
/*
@@ -73,12 +72,13 @@ Java_com_android_tv_tuner_TunerHal_nativeTune
* Method: nativeCloseAllPidFilters
* Signature: (J)V
*/
-JNIEXPORT void JNICALL Java_com_android_tv_tuner_TunerHal_nativeCloseAllPidFilters
- (JNIEnv *, jobject, jlong deviceId) {
- std::map<jlong, DvbManager *>::iterator it = sDvbManagers.find(deviceId);
- if (it != sDvbManagers.end()) {
- it->second->closeAllDvbPidFilter();
- }
+JNIEXPORT void JNICALL
+Java_com_android_tv_tuner_TunerHal_nativeCloseAllPidFilters(JNIEnv *, jobject,
+ jlong deviceId) {
+ std::map<jlong, DvbManager *>::iterator it = sDvbManagers.find(deviceId);
+ if (it != sDvbManagers.end()) {
+ it->second->closeAllDvbPidFilter();
+ }
}
/*
@@ -86,13 +86,12 @@ JNIEXPORT void JNICALL Java_com_android_tv_tuner_TunerHal_nativeCloseAllPidFilte
* Method: nativeStopTune
* Signature: (J)V
*/
-JNIEXPORT void JNICALL
-Java_com_android_tv_tuner_TunerHal_nativeStopTune
-(JNIEnv *, jobject, jlong deviceId) {
- std::map<jlong, DvbManager *>::iterator it = sDvbManagers.find(deviceId);
- if (it != sDvbManagers.end()) {
- it->second->stopTune();
- }
+JNIEXPORT void JNICALL Java_com_android_tv_tuner_TunerHal_nativeStopTune(
+ JNIEnv *, jobject, jlong deviceId) {
+ std::map<jlong, DvbManager *>::iterator it = sDvbManagers.find(deviceId);
+ if (it != sDvbManagers.end()) {
+ it->second->stopTune();
+ }
}
/*
@@ -100,13 +99,12 @@ Java_com_android_tv_tuner_TunerHal_nativeStopTune
* Method: nativeAddPidFilter
* Signature: (JII)V
*/
-JNIEXPORT void JNICALL
-Java_com_android_tv_tuner_TunerHal_nativeAddPidFilter
-(JNIEnv *env, jobject thiz, jlong deviceId, jint pid, jint filterType) {
- std::map<jlong, DvbManager *>::iterator it = sDvbManagers.find(deviceId);
- if (it != sDvbManagers.end()) {
- it->second->startTsPidFilter(env, thiz, pid, filterType);
- }
+JNIEXPORT void JNICALL Java_com_android_tv_tuner_TunerHal_nativeAddPidFilter(
+ JNIEnv *env, jobject thiz, jlong deviceId, jint pid, jint filterType) {
+ std::map<jlong, DvbManager *>::iterator it = sDvbManagers.find(deviceId);
+ if (it != sDvbManagers.end()) {
+ it->second->startTsPidFilter(env, thiz, pid, filterType);
+ }
}
/*
@@ -114,32 +112,34 @@ Java_com_android_tv_tuner_TunerHal_nativeAddPidFilter
* Method: nativeWriteInBuffer
* Signature: (J[BI)I
*/
-JNIEXPORT jint JNICALL
-Java_com_android_tv_tuner_TunerHal_nativeWriteInBuffer
-(JNIEnv *env, jobject thiz, jlong deviceId, jbyteArray javaBuffer, jint javaBufferSize) {
- uint8_t tsBuffer[TS_PAYLOAD_SIZE];
- std::map<jlong, DvbManager *>::iterator it = sDvbManagers.find(deviceId);
- if (it == sDvbManagers.end()) {
- return -1;
- }
- DvbManager *dvbManager = it->second;
-
- // Always read multiple of TS_PACKET_SIZE
- javaBufferSize = (javaBufferSize / TS_PACKET_SIZE) * TS_PACKET_SIZE;
- int readBufferSize = (javaBufferSize < TS_PAYLOAD_SIZE) ? javaBufferSize : TS_PAYLOAD_SIZE;
-
- int dataSize = dvbManager->readTsStream(env, thiz, tsBuffer, readBufferSize, READ_TIMEOUT_MS);
- if (dataSize == 0) {
- ALOGD("No data to read DVR");
- return 0;
- } else if (dataSize < 0) {
- return -1;
- }
-
- sTotalBytesFetched += dataSize;
-
- env->SetByteArrayRegion(javaBuffer, 0, dataSize, (jbyte *) tsBuffer);
- return dataSize;
+JNIEXPORT jint JNICALL Java_com_android_tv_tuner_TunerHal_nativeWriteInBuffer(
+ JNIEnv *env, jobject thiz, jlong deviceId, jbyteArray javaBuffer,
+ jint javaBufferSize) {
+ uint8_t tsBuffer[TS_PAYLOAD_SIZE];
+ std::map<jlong, DvbManager *>::iterator it = sDvbManagers.find(deviceId);
+ if (it == sDvbManagers.end()) {
+ return -1;
+ }
+ DvbManager *dvbManager = it->second;
+
+ // Always read multiple of TS_PACKET_SIZE
+ javaBufferSize = (javaBufferSize / TS_PACKET_SIZE) * TS_PACKET_SIZE;
+ int readBufferSize =
+ (javaBufferSize < TS_PAYLOAD_SIZE) ? javaBufferSize : TS_PAYLOAD_SIZE;
+
+ int dataSize = dvbManager->readTsStream(env, thiz, tsBuffer, readBufferSize,
+ READ_TIMEOUT_MS);
+ if (dataSize == 0) {
+ ALOGD("No data to read DVR");
+ return 0;
+ } else if (dataSize < 0) {
+ return -1;
+ }
+
+ sTotalBytesFetched += dataSize;
+
+ env->SetByteArrayRegion(javaBuffer, 0, dataSize, (jbyte *)tsBuffer);
+ return dataSize;
}
/*
@@ -148,12 +148,12 @@ Java_com_android_tv_tuner_TunerHal_nativeWriteInBuffer
* Signature: (JZ)V
*/
JNIEXPORT void JNICALL
-Java_com_android_tv_tuner_TunerHal_nativeSetHasPendingTune
-(JNIEnv *, jobject, jlong deviceId, jboolean hasPendingTune) {
- std::map<jlong, DvbManager *>::iterator it = sDvbManagers.find(deviceId);
- if (it != sDvbManagers.end()) {
- it->second->setHasPendingTune(hasPendingTune);
- }
+Java_com_android_tv_tuner_TunerHal_nativeSetHasPendingTune(
+ JNIEnv *, jobject, jlong deviceId, jboolean hasPendingTune) {
+ std::map<jlong, DvbManager *>::iterator it = sDvbManagers.find(deviceId);
+ if (it != sDvbManagers.end()) {
+ it->second->setHasPendingTune(hasPendingTune);
+ }
}
/*
@@ -162,14 +162,15 @@ Java_com_android_tv_tuner_TunerHal_nativeSetHasPendingTune
* Signature: (J)I
*/
JNIEXPORT int JNICALL
-Java_com_android_tv_tuner_TunerHal_nativeGetDeliverySystemType
-(JNIEnv *env, jobject thiz, jlong deviceId) {
- std::map<jlong, DvbManager *>::iterator it = sDvbManagers.find(deviceId);
- if (it != sDvbManagers.end()) {
- return it->second->getDeliverySystemType(env, thiz);
- } else {
- DvbManager *dvbManager = new DvbManager(env, thiz);
- sDvbManagers.insert(std::pair<jlong, DvbManager *>(deviceId, dvbManager));
- return dvbManager->getDeliverySystemType(env, thiz);
- }
-}
+Java_com_android_tv_tuner_TunerHal_nativeGetDeliverySystemType(JNIEnv *env,
+ jobject thiz,
+ jlong deviceId) {
+ std::map<jlong, DvbManager *>::iterator it = sDvbManagers.find(deviceId);
+ if (it != sDvbManagers.end()) {
+ return it->second->getDeliverySystemType(env, thiz);
+ } else {
+ DvbManager *dvbManager = new DvbManager(env, thiz);
+ sDvbManagers.insert(std::pair<jlong, DvbManager *>(deviceId, dvbManager));
+ return dvbManager->getDeliverySystemType(env, thiz);
+ }
+} \ No newline at end of file
diff --git a/jni/tunertvinput_jni.h b/jni/tunertvinput_jni.h
index fcd64d50..d299c304 100644
--- a/jni/tunertvinput_jni.h
+++ b/jni/tunertvinput_jni.h
@@ -44,64 +44,67 @@ extern "C" {
* Method: nativeFinalize
* Signature: (J)V
*/
-JNIEXPORT void JNICALL Java_com_android_tv_tuner_TunerHal_nativeFinalize
- (JNIEnv *, jobject, jlong);
+JNIEXPORT void JNICALL
+Java_com_android_tv_tuner_TunerHal_nativeFinalize(JNIEnv *, jobject, jlong);
/*
* Class: com_android_tv_tuner_TunerHal
* Method: nativeTune
* Signature: (JILjava/lang/String;I)Z
*/
-JNIEXPORT jboolean JNICALL Java_com_android_tv_tuner_TunerHal_nativeTune
- (JNIEnv *, jobject, jlong, jint, jstring, jint);
+JNIEXPORT jboolean JNICALL Java_com_android_tv_tuner_TunerHal_nativeTune(
+ JNIEnv *, jobject, jlong, jint, jstring, jint);
/*
* Class: com_android_tv_tuner_TunerHal
* Method: nativeAddPidFilter
* Signature: (JII)V
*/
-JNIEXPORT void JNICALL Java_com_android_tv_tuner_TunerHal_nativeAddPidFilter
- (JNIEnv *, jobject, jlong, jint, jint);
+JNIEXPORT void JNICALL Java_com_android_tv_tuner_TunerHal_nativeAddPidFilter(
+ JNIEnv *, jobject, jlong, jint, jint);
/*
* Class: com_android_tv_tuner_TunerHal
* Method: nativeCloseAllPidFilters
* Signature: (J)V
*/
-JNIEXPORT void JNICALL Java_com_android_tv_tuner_TunerHal_nativeCloseAllPidFilters
- (JNIEnv *, jobject, jlong);
+JNIEXPORT void JNICALL
+Java_com_android_tv_tuner_TunerHal_nativeCloseAllPidFilters(JNIEnv *, jobject,
+ jlong);
/*
* Class: com_android_tv_tuner_TunerHal
* Method: nativeStopTune
* Signature: (J)V
*/
-JNIEXPORT void JNICALL Java_com_android_tv_tuner_TunerHal_nativeStopTune
- (JNIEnv *, jobject, jlong);
+JNIEXPORT void JNICALL
+Java_com_android_tv_tuner_TunerHal_nativeStopTune(JNIEnv *, jobject, jlong);
/*
* Class: com_android_tv_tuner_TunerHal
* Method: nativeWriteInBuffer
* Signature: (J[BI)I
*/
-JNIEXPORT jint JNICALL Java_com_android_tv_tuner_TunerHal_nativeWriteInBuffer
- (JNIEnv *, jobject, jlong, jbyteArray, jint);
+JNIEXPORT jint JNICALL Java_com_android_tv_tuner_TunerHal_nativeWriteInBuffer(
+ JNIEnv *, jobject, jlong, jbyteArray, jint);
/*
* Class: com_android_tv_tuner_TunerHal
* Method: nativeSetHasPendingTune
* Signature: (JZ)V
*/
-JNIEXPORT void JNICALL Java_com_android_tv_tuner_TunerHal_nativeSetHasPendingTune
- (JNIEnv *, jobject, jlong, jboolean);
+JNIEXPORT void JNICALL
+Java_com_android_tv_tuner_TunerHal_nativeSetHasPendingTune(JNIEnv *, jobject,
+ jlong, jboolean);
/*
* Class: com_android_tv_tuner_TunerHal
* Method: nativeGetDeliverySystemType
* Signature: (J)I
*/
-JNIEXPORT int JNICALL Java_com_android_tv_tuner_TunerHal_nativeGetDeliverySystemType
- (JNIEnv *, jobject, jlong);
+JNIEXPORT int JNICALL
+Java_com_android_tv_tuner_TunerHal_nativeGetDeliverySystemType(JNIEnv *,
+ jobject, jlong);
#ifdef __cplusplus
}
diff --git a/libs/exoplayer-core-2-SNAPHOT-20180114.aar b/libs/exoplayer-core-2-SNAPHOT-20180114.aar
new file mode 100644
index 00000000..90af2e63
--- /dev/null
+++ b/libs/exoplayer-core-2-SNAPHOT-20180114.aar
Binary files differ
diff --git a/libs/exoplayer-r1.5.16.aar b/libs/exoplayer-r1.5.16.aar
new file mode 100644
index 00000000..364104fc
--- /dev/null
+++ b/libs/exoplayer-r1.5.16.aar
Binary files differ
diff --git a/libs/exoplayer.jar b/libs/exoplayer.jar
deleted file mode 100644
index 43aea971..00000000
--- a/libs/exoplayer.jar
+++ /dev/null
Binary files differ
diff --git a/libs/exoplayer_v2.jar b/libs/exoplayer_v2.jar
deleted file mode 100644
index 6cfcde9c..00000000
--- a/libs/exoplayer_v2.jar
+++ /dev/null
Binary files differ
diff --git a/libs/exoplayer_v2_ext_ffmpeg.jar b/libs/exoplayer_v2_ext_ffmpeg.jar
deleted file mode 100644
index 65117267..00000000
--- a/libs/exoplayer_v2_ext_ffmpeg.jar
+++ /dev/null
Binary files differ
diff --git a/partner_support/Android.mk b/partner_support/Android.mk
new file mode 100644
index 00000000..9a45e705
--- /dev/null
+++ b/partner_support/Android.mk
@@ -0,0 +1,22 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# Include all java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE := live-channels-partner-support
+LOCAL_MODULE_CLASS := STATIC_JAVA_LIBRARIES
+LOCAL_MODULE_TAGS := optional
+LOCAL_SDK_VERSION := system_current
+
+LOCAL_USE_AAPT2 := true
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-annotations
+
+include $(LOCAL_PATH)/buildconfig.mk
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/partner_support/AndroidManifest.xml b/partner_support/AndroidManifest.xml
new file mode 100644
index 00000000..5a45f0db
--- /dev/null
+++ b/partner_support/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 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
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.tv.partner.support"
+ android:versionCode="1">
+ <uses-sdk android:targetSdkVersion="26" android:minSdkVersion="21"/>
+ <application />
+</manifest>
diff --git a/partner_support/BuildConfig.java.in b/partner_support/BuildConfig.java.in
new file mode 100644
index 00000000..21d59011
--- /dev/null
+++ b/partner_support/BuildConfig.java.in
@@ -0,0 +1,8 @@
+/* This file is auto generated. Do not modify. */
+package com.google.android.tv.partner.support;
+
+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/partner_support/README.md b/partner_support/README.md
new file mode 100644
index 00000000..13c9a763
--- /dev/null
+++ b/partner_support/README.md
@@ -0,0 +1,3 @@
+# Code and samples for Partner integration with Live Channels
+
+This code will only work for partners that are working with the Live Channels team on integration. \ No newline at end of file
diff --git a/partner_support/buildconfig.mk b/partner_support/buildconfig.mk
new file mode 100644
index 00000000..cece7f2b
--- /dev/null
+++ b/partner_support/buildconfig.mk
@@ -0,0 +1,39 @@
+#
+# 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.
+#
+
+# Emulate gradles BuildConfig.java
+
+ifeq "$(TARGET_BUILD_VARIANT)" "eng"
+ BC_DEBUG_STATUS := true
+else ifeq "$(TARGET_BUILD_VARIANT)" "userdebug"
+ BC_DEBUG_STATUS := true
+else
+ BC_DEBUG_STATUS := false
+endif
+
+ifeq "$(TARGET_BUILD_VARIANT)" "eng"
+ BC_ENG_STATUS := true
+else
+ BC_ENG_STATUS := false
+endif
+
+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/partner_support/g3doc/CloudEpgForPartners.md b/partner_support/g3doc/CloudEpgForPartners.md
new file mode 100644
index 00000000..bec6b504
--- /dev/null
+++ b/partner_support/g3doc/CloudEpgForPartners.md
@@ -0,0 +1,112 @@
+# 3rd party instructions for using Cloud EPG feature of Live Channels
+
+Partners can ask Live Channels to retrieve EPG data for their TV Input Service
+using live channels
+
+## Prerequisites
+
+* Updated agreement with Google
+* Oreo or patched Nougat
+
+## Nougat
+
+To use cloud epg with Nougat you will need the following changes.
+
+### Patch TVProvider
+
+To run in Nougat you must cherry pick [change
+455319](https://android-review.googlesource.com/c/platform/packages/providers/TvProvider/+/455319)
+to TV Provider.
+
+### Customisation
+
+Indicate TvProvider is patched by including the following in their TV
+customization resource
+
+```
+<bool name="tvprovider_allows_system_inserts_to_program_table">true</bool>
+```
+
+See https://source.android.com/devices/tv/customize-tv-app
+
+## **Input Setup**
+
+During the input setup activity, the TIS will query the content provider for
+lineups in a given postal code. The TIS then inserts a row to the inputs table
+with input_id and lineup_id
+
+On completion of the activity the TIS sets the extra data in the result to
+
+* `com.android.tv.extra.USE_CLOUD_EPG = true`
+* `TvInputInfo.EXTRA_INPUT_ID` with their input_id
+
+This is used to tell Live Channels to immediately start the EPG fetch for that
+input.
+
+### Sample Input Setup code.
+
+A complete sample is at
+../third_party/samples/src/com/example/partnersupportsampletvinput
+
+#### query lineup
+
+```java
+ private AsyncTask<Void, Void, List<Lineup>> createFetchLineupsTask() {
+ return new AsyncTask<Void, Void, List<Lineup>>() {
+ @Override
+ protected List<Lineup> doInBackground(Void... params) {
+ ContentResolver cr = getActivity().getContentResolver();
+
+ List<Lineup> results = new ArrayList<>();
+ Cursor cursor =
+ cr.query(
+ Uri.parse(
+ "content://com.android.tv.data.epg/lineups/postal_code/"
+ + ZIP),
+ null,
+ null,
+ null,
+ null);
+
+ while (cursor.moveToNext()) {
+ String id = cursor.getString(0);
+ String name = cursor.getString(1);
+ String channels = cursor.getString(2);
+ results.add(new Lineup(id, name, channels));
+ }
+
+ return results;
+ }
+
+ @Override
+ protected void onPostExecute(List<Lineup> lineups) {
+ showLineups(lineups);
+ }
+ };
+ }
+```
+
+#### Insert cloud_epg_input
+
+```java
+ContentValues values = new ContentValues();
+values.put(EpgContract.EpgInputs.COLUMN_INPUT_ID, SampleTvInputService.INPUT_ID);
+values.put(EpgContract.EpgInputs.COLUMN_LINEUP_ID, lineup.getId());
+ContentResolver contentResolver = getActivity().getContentResolver();
+EpgInput epgInput = EpgInputs.queryEpgInput(contentResolver, SampleTvInputService.INPUT_ID);
+if (epgInput == null) {
+ contentResolver.insert(EpgContract.EpgInputs.CONTENT_URI, values);
+} else {
+ values.put(EpgContract.EpgInputs.COLUMN_ID, epgInput.getId());
+ EpgInputs.update(contentResolver, EpgInput.createEpgChannel(values));
+}
+```
+
+#### Return use_cloud_epg
+
+```java
+Intent data = new Intent();
+data.putExtra(TvInputInfo.EXTRA_INPUT_ID, inputId);
+data.putExtra(com.android.tv.extra.USE_CLOUD_EPG, true);
+setResult(Activity.RESULT_OK, data);
+```
diff --git a/partner_support/res/values/strings.xml b/partner_support/res/values/strings.xml
new file mode 100644
index 00000000..aad2d446
--- /dev/null
+++ b/partner_support/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2017 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>
+</resources> \ No newline at end of file
diff --git a/partner_support/sample_customization/Android.mk b/partner_support/sample_customization/Android.mk
new file mode 100644
index 00000000..11ed13b0
--- /dev/null
+++ b/partner_support/sample_customization/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := PartnerSupportSampleCustomization
+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
+
+# Required for customization
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_RESOURCE_DIR := \
+ $(LOCAL_PATH)/res
+
+include $(BUILD_PACKAGE)
diff --git a/partner_support/sample_customization/AndroidManifest.xml b/partner_support/sample_customization/AndroidManifest.xml
new file mode 100644
index 00000000..f1edad3f
--- /dev/null
+++ b/partner_support/sample_customization/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 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
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.tvcustomization">
+ <!-- Customization package must have this permission to customize TV apps. -->
+ <uses-permission android:name="com.android.tv.permission.CUSTOMIZE_TV_APP"/>
+
+ <!-- Enable leanback library support. -->
+ <uses-feature android:name="android.software.leanback" android:required="true" />
+ <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
+
+ <uses-sdk android:targetSdkVersion="26" android:minSdkVersion="23"/>
+
+ <application android:label="Partner Customization"
+ android:theme="@android:style/Theme.Holo.Light.NoActionBar"
+ android:icon="@mipmap/ic_launcher"
+ >
+
+ </application>
+</manifest>
diff --git a/partner_support/sample_customization/res/mipmap-hdpi/ic_launcher.png b/partner_support/sample_customization/res/mipmap-hdpi/ic_launcher.png
new file mode 100755
index 00000000..26add7f6
--- /dev/null
+++ b/partner_support/sample_customization/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/partner_support/sample_customization/res/mipmap-mdpi/ic_launcher.png b/partner_support/sample_customization/res/mipmap-mdpi/ic_launcher.png
new file mode 100755
index 00000000..1ac20dbf
--- /dev/null
+++ b/partner_support/sample_customization/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/partner_support/sample_customization/res/mipmap-xhdpi/ic_launcher.png b/partner_support/sample_customization/res/mipmap-xhdpi/ic_launcher.png
new file mode 100755
index 00000000..f6cf6450
--- /dev/null
+++ b/partner_support/sample_customization/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/partner_support/sample_customization/res/mipmap-xxhdpi/ic_launcher.png b/partner_support/sample_customization/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100755
index 00000000..72a250db
--- /dev/null
+++ b/partner_support/sample_customization/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/partner_support/sample_customization/res/mipmap-xxxhdpi/ic_launcher.png b/partner_support/sample_customization/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100755
index 00000000..648001f7
--- /dev/null
+++ b/partner_support/sample_customization/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/partner_support/sample_customization/res/values/bools.xml b/partner_support/sample_customization/res/values/bools.xml
new file mode 100644
index 00000000..54fbe071
--- /dev/null
+++ b/partner_support/sample_customization/res/values/bools.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 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>
+ <bool name="tvprovider_allows_system_inserts_to_program_table">true</bool>
+</resources> \ No newline at end of file
diff --git a/partner_support/samples/Android.mk b/partner_support/samples/Android.mk
new file mode 100644
index 00000000..922a5b5d
--- /dev/null
+++ b/partner_support/samples/Android.mk
@@ -0,0 +1,33 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# Include all java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := PartnerSupportSampleTvInput
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-annotations \
+ live-channels-partner-support
+
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+ android-support-compat \
+ android-support-core-ui \
+ android-support-v7-recyclerview \
+ android-support-v17-leanback \
+ android-support-tv-provider
+
+LOCAL_USE_AAPT2 := true
+
+LOCAL_PROGUARD_ENABLED := disabled
+# Overlay view related functionality requires system APIs.
+LOCAL_SDK_VERSION := system_current
+LOCAL_MIN_SDK_VERSION := 23 # M
+
+# Required for com.android.tv.permission.RECEIVE_INPUT_EVENT
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+include $(BUILD_PACKAGE)
diff --git a/partner_support/samples/AndroidManifest.xml b/partner_support/samples/AndroidManifest.xml
new file mode 100644
index 00000000..b9e086cb
--- /dev/null
+++ b/partner_support/samples/AndroidManifest.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.example.partnersupportsampletvinput">
+
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+ <!-- TODO: READ_EPG_DATA and WRITE_EPG_DATA need to be removed, once we fully
+ migrate all test environment from LMP to MNC, because the permissions
+ are not required from MNC. -->
+ <uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" />
+ <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
+ <uses-permission android:name="com.android.tv.permission.RECEIVE_INPUT_EVENT" />
+ <uses-sdk android:targetSdkVersion="26" android:minSdkVersion="23"/>
+ <!--TODO(b/68949299): remove tool hint when we have smaller dependency targets-->
+ <application android:label="@string/partner_support_sample_tv_input"
+ tools:replace="android:label,icon,theme"
+ android:icon="@mipmap/ic_launcher"
+ android:theme="@android:style/Theme.Holo.Light.NoActionBar" >
+ <activity android:name=".SampleTvInputSetupActivity"
+ android:theme="@style/Theme.Leanback.GuidedStep">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
+ <service android:name=".SampleTvInputService"
+ android:permission="android.permission.BIND_TV_INPUT"
+ android:label="@string/partner_support_sample_tv_input"
+ android:process="com.example.partnersupportsampletvinput">
+ <intent-filter>
+ <action android:name="android.media.tv.TvInputService" />
+ </intent-filter>
+ <meta-data android:name="android.media.tv.input"
+ android:resource="@xml/sampletvinputservice" />
+ </service>
+ </application>
+</manifest>
diff --git a/partner_support/samples/res/mipmap-hdpi/ic_launcher.png b/partner_support/samples/res/mipmap-hdpi/ic_launcher.png
new file mode 100755
index 00000000..a827add4
--- /dev/null
+++ b/partner_support/samples/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/partner_support/samples/res/mipmap-mdpi/ic_launcher.png b/partner_support/samples/res/mipmap-mdpi/ic_launcher.png
new file mode 100755
index 00000000..d7d36f26
--- /dev/null
+++ b/partner_support/samples/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/partner_support/samples/res/mipmap-xhdpi/ic_launcher.png b/partner_support/samples/res/mipmap-xhdpi/ic_launcher.png
new file mode 100755
index 00000000..210bfcf1
--- /dev/null
+++ b/partner_support/samples/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/partner_support/samples/res/mipmap-xxhdpi/ic_launcher.png b/partner_support/samples/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100755
index 00000000..59a090c7
--- /dev/null
+++ b/partner_support/samples/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/partner_support/samples/res/mipmap-xxxhdpi/ic_launcher.png b/partner_support/samples/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100755
index 00000000..388b6ebe
--- /dev/null
+++ b/partner_support/samples/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/partner_support/samples/res/values/strings.xml b/partner_support/samples/res/values/strings.xml
new file mode 100644
index 00000000..c6f0ac94
--- /dev/null
+++ b/partner_support/samples/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 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>
+ <string name="partner_support_sample_tv_input" translatable="false">Partner Support Sample Tv Input</string>
+</resources> \ No newline at end of file
diff --git a/partner_support/samples/res/xml/sampletvinputservice.xml b/partner_support/samples/res/xml/sampletvinputservice.xml
new file mode 100644
index 00000000..e5211dba
--- /dev/null
+++ b/partner_support/samples/res/xml/sampletvinputservice.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2017 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.
+ -->
+
+<tv-input xmlns:android="http://schemas.android.com/apk/res/android"
+ android:setupActivity="com.example.partnersupportsampletvinput.SampleTvInputSetupActivity" /> \ No newline at end of file
diff --git a/partner_support/samples/src/com/example/partnersupportsampletvinput/ChannelScanFragment.java b/partner_support/samples/src/com/example/partnersupportsampletvinput/ChannelScanFragment.java
new file mode 100644
index 00000000..35f4b694
--- /dev/null
+++ b/partner_support/samples/src/com/example/partnersupportsampletvinput/ChannelScanFragment.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2017 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.example.partnersupportsampletvinput;
+
+import android.app.FragmentManager;
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.support.annotation.NonNull;
+import android.support.media.tv.Channel;
+import android.support.media.tv.TvContractCompat;
+import android.support.v17.leanback.app.GuidedStepFragment;
+import android.support.v17.leanback.widget.GuidanceStylist;
+import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
+import android.support.v17.leanback.widget.GuidedAction;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+/** Channel Scan Fragment shows detected channels */
+public class ChannelScanFragment extends GuidedStepFragment {
+ public static final long ACTION_ID_SCAN_RESULTS = 1;
+ public static final String KEY_POSTCODE = "postcode";
+ public static final String KEY_CHANNEL_NUMBERS = "channel numbers";
+ private GuidedAction mNextAction;
+ private List<Channel> mChannels = new ArrayList<>();
+ private Bundle mSavedInstanceState;
+
+ private AsyncTask<Void, Void, List<Channel>> mChannelScanTask;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mSavedInstanceState = savedInstanceState;
+ }
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View v = super.onCreateView(inflater, container, savedInstanceState);
+ mChannelScanTask =
+ new AsyncTask<Void, Void, List<Channel>>() {
+ @Override
+ protected List<Channel> doInBackground(Void... voids) {
+ return scanChannel();
+ }
+
+ @Override
+ protected void onPostExecute(List<Channel> result) {
+ mChannels = result;
+ mNextAction.setFocusable(true);
+ mNextAction.setEnabled(true);
+ int position = findButtonActionPositionById(GuidedAction.ACTION_ID_NEXT);
+ if (position >= 0) {
+ notifyButtonActionChanged(position);
+ View buttonView = getButtonActionItemView(position);
+ buttonView.setFocusable(true);
+ buttonView.requestFocus();
+ }
+ }
+ };
+ mChannelScanTask.execute();
+ return v;
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ mNextAction = null;
+ if (mChannelScanTask != null) {
+ mChannelScanTask.cancel(true);
+ mChannelScanTask = null;
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ // reset actions
+ List<GuidedAction> actions = new ArrayList<>();
+ onCreateActions(actions, mSavedInstanceState);
+ setActions(actions);
+ actions = new ArrayList<>();
+ onCreateButtonActions(actions, mSavedInstanceState);
+ setButtonActions(actions);
+ }
+
+ @NonNull
+ @Override
+ public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
+ return new Guidance("Channel Scan", "Fake scanning for channels", null, null);
+ }
+
+ @Override
+ public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(ACTION_ID_SCAN_RESULTS)
+ .title("Channels Found:")
+ .description("")
+ .multilineDescription(true)
+ .focusable(false)
+ .infoOnly(true)
+ .build());
+ }
+
+ @Override
+ public void onCreateButtonActions(
+ @NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
+ mNextAction =
+ new GuidedAction.Builder(getActivity())
+ .id(GuidedAction.ACTION_ID_NEXT)
+ .title("NEXT")
+ .focusable(false)
+ .enabled(false)
+ .build();
+ actions.add(mNextAction);
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(GuidedAction.ACTION_ID_CANCEL)
+ .title("CANCEL")
+ .build());
+ }
+
+ @Override
+ public void onGuidedActionClicked(GuidedAction action) {
+ if (action == null) {
+ return;
+ }
+ FragmentManager fm = getFragmentManager();
+ switch ((int) action.getId()) {
+ case (int) GuidedAction.ACTION_ID_NEXT:
+ LineupSelectionFragment fragment = new LineupSelectionFragment();
+ Bundle args = new Bundle();
+ args.putString(KEY_POSTCODE, getArguments().getString(KEY_POSTCODE));
+ ArrayList<String> channelNumbers = new ArrayList<>();
+ for (Channel channel : mChannels) {
+ if (channel != null) {
+ channelNumbers.add(channel.getDisplayNumber());
+ }
+ }
+ args.putStringArrayList(KEY_CHANNEL_NUMBERS, channelNumbers);
+ fragment.setArguments(args);
+ GuidedStepFragment.add(getFragmentManager(), fragment);
+ break;
+ case (int) GuidedAction.ACTION_ID_CANCEL:
+ fm.popBackStack();
+ break;
+ default:
+ }
+ }
+
+ private List<Channel> scanChannel() {
+ List<Channel> channels = new ArrayList<>();
+ channels.add(createFakeChannel("KQED", "9.1", 1));
+ channels.add(createFakeChannel("BBC", "2-2", 2));
+ channels.add(createFakeChannel("What\'s on", "1.1", 3));
+ channels.add(createFakeChannel("FOX", "7.1", 4));
+ channels.add(createFakeChannel("Play Movie", "1.1", 5));
+ channels.add(createFakeChannel("PBS", "9.2", 6));
+ channels.add(createFakeChannel("ApiTestChannel", "A.1", 7));
+ updateChannels(channels);
+
+ return channels;
+ }
+
+ private void updateChannels(List<Channel> channels) {
+ HashMap<Integer, Channel> networkId2newChannel = new HashMap<>();
+ for (Channel channel : channels) {
+ networkId2newChannel.put(channel.getOriginalNetworkId(), channel);
+ }
+
+ ContentResolver cr = getActivity().getContentResolver();
+ Cursor cursor =
+ cr.query(
+ TvContractCompat.buildChannelsUriForInput(SampleTvInputService.INPUT_ID),
+ Channel.PROJECTION,
+ null,
+ null,
+ null,
+ null);
+ ArrayList<Channel> existingChannels = new ArrayList<>();
+ while (cursor.moveToNext()) {
+ existingChannels.add(Channel.fromCursor(cursor));
+ }
+ Iterator<Channel> i = existingChannels.iterator();
+ while (i.hasNext()) {
+ Channel existingChannel = i.next();
+ if (networkId2newChannel.containsKey(existingChannel.getOriginalNetworkId())) {
+ i.remove();
+ Channel newChannel =
+ networkId2newChannel.remove(existingChannel.getOriginalNetworkId());
+ cr.update(
+ TvContractCompat.buildChannelUri(existingChannel.getId()),
+ newChannel.toContentValues(),
+ null,
+ null);
+ }
+ }
+ for (Channel newChannel : networkId2newChannel.values()) {
+ cr.insert(TvContractCompat.Channels.CONTENT_URI, newChannel.toContentValues());
+ }
+ for (Channel existingChannel : existingChannels) {
+ cr.delete(TvContractCompat.buildChannelUri(existingChannel.getId()), null, null);
+ }
+ }
+
+ private Channel createFakeChannel(String name, String number, int networkId) {
+ Channel channel =
+ new Channel.Builder()
+ .setDisplayName(name)
+ .setDisplayNumber(number)
+ .setInputId(SampleTvInputService.INPUT_ID)
+ .setOriginalNetworkId(networkId)
+ .build();
+ onChannelDetected(channel);
+ SystemClock.sleep(1000);
+
+ return channel;
+ }
+
+ private void onChannelDetected(Channel channel) {
+ getActivity()
+ .runOnUiThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ List<GuidedAction> actions = getActions();
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(ACTION_ID_SCAN_RESULTS)
+ .title(
+ channel.getDisplayNumber()
+ + " "
+ + channel.getDisplayName())
+ .focusable(false)
+ .build());
+ if (actions.size() >= 7) {
+ actions.remove(1);
+ }
+ setActions(actions);
+ }
+ });
+ }
+}
diff --git a/partner_support/samples/src/com/example/partnersupportsampletvinput/LineupSelectionFragment.java b/partner_support/samples/src/com/example/partnersupportsampletvinput/LineupSelectionFragment.java
new file mode 100644
index 00000000..8c3ca77a
--- /dev/null
+++ b/partner_support/samples/src/com/example/partnersupportsampletvinput/LineupSelectionFragment.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2017 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.example.partnersupportsampletvinput;
+
+import android.app.Activity;
+import android.app.FragmentManager;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v17.leanback.app.GuidedStepFragment;
+import android.support.v17.leanback.widget.GuidanceStylist;
+import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
+import android.support.v17.leanback.widget.GuidedAction;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import com.google.android.tv.partner.support.EpgContract;
+import com.google.android.tv.partner.support.EpgInput;
+import com.google.android.tv.partner.support.EpgInputs;
+import com.google.android.tv.partner.support.Lineup;
+import com.google.android.tv.partner.support.Lineups;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/** Lineup Selection Fragment for users to select a lineup */
+public class LineupSelectionFragment extends GuidedStepFragment {
+ public static final boolean DEBUG = false;
+ public static final String TAG = "LineupSelectionFragment";
+
+ public static final String KEY_TITLE = "title";
+ public static final String KEY_DESCRIPTION = "description";
+ private static final long ACTION_ID_STATUS = 1;
+ private static final long ACTION_ID_LINEUPS = 2;
+ private static final Pattern CHANNEL_NUMBER_DELIMITER = Pattern.compile("([ .-])");
+ private Activity mActivity;
+ private GuidedAction mStatusAction;
+ private String mPostcode;
+ private List<String> mChannelNumbers;
+ private Map<GuidedAction, Lineup> mActionLineupMap = new HashMap<>();
+
+ private AsyncTask<Void, Void, List<Pair<Lineup, Integer>>> mFetchLineupTask;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ mActivity = getActivity();
+ mStatusAction =
+ new GuidedAction.Builder(mActivity)
+ .id(ACTION_ID_STATUS)
+ .title("Loading lineups")
+ .description("please wait")
+ .focusable(false)
+ .build();
+
+ mFetchLineupTask =
+ new AsyncTask<Void, Void, List<Pair<Lineup, Integer>>>() {
+ @Override
+ protected List<Pair<Lineup, Integer>> doInBackground(Void... voids) {
+ return lineupChannelMatchCount(getLineups(), getChannelNumbers());
+ }
+
+ @Override
+ protected void onPostExecute(List<Pair<Lineup, Integer>> result) {
+ List<GuidedAction> actions = new ArrayList<>();
+ if (result.isEmpty()) {
+ mStatusAction.setTitle("No lineup found for postcode: " + mPostcode);
+ mStatusAction.setDescription("");
+ mStatusAction.setFocusable(true);
+ notifyActionChanged(findActionPositionById(ACTION_ID_STATUS));
+ return;
+ }
+ mActionLineupMap = new HashMap<>();
+ for (Pair<Lineup, Integer> pair : result) {
+ Lineup lineup = pair.first;
+ String title =
+ TextUtils.isEmpty(lineup.getName())
+ ? lineup.getId()
+ : lineup.getName();
+ GuidedAction action =
+ new GuidedAction.Builder(getActivity())
+ .id(ACTION_ID_LINEUPS)
+ .title(title)
+ .description(pair.second + " channels match")
+ .build();
+ mActionLineupMap.put(action, lineup);
+ actions.add(action);
+ }
+ setActions(actions);
+ }
+ };
+
+ super.onCreate(savedInstanceState);
+ }
+
+ @NonNull
+ @Override
+ public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
+ return new Guidance(
+ "LineupSelectionFragment", "LineupSelectionFragment Description", null, null);
+ }
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View v = super.onCreateView(inflater, container, savedInstanceState);
+ if (!mFetchLineupTask.isCancelled()) {
+ mPostcode = getArguments().getString(ChannelScanFragment.KEY_POSTCODE);
+ mChannelNumbers =
+ getArguments().getStringArrayList(ChannelScanFragment.KEY_CHANNEL_NUMBERS);
+ mFetchLineupTask.execute();
+ }
+ return v;
+ }
+
+ @Override
+ public void onDestroyView() {
+ mFetchLineupTask.cancel(true);
+ super.onDestroyView();
+ }
+
+ @Override
+ public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
+ actions.add(mStatusAction);
+ }
+
+ @Override
+ public void onGuidedActionClicked(GuidedAction action) {
+ if (action == null) {
+ return;
+ }
+ switch ((int) action.getId()) {
+ case (int) ACTION_ID_STATUS:
+ finishGuidedStepFragments();
+ break;
+ case (int) ACTION_ID_LINEUPS:
+ FragmentManager fm = getFragmentManager();
+ super.onGuidedActionClicked(action);
+ onLineupSelected(mActionLineupMap.get(action));
+ ResultFragment fragment = new ResultFragment();
+ Bundle args = new Bundle();
+ args.putString(KEY_TITLE, action.getTitle().toString());
+ args.putString(KEY_DESCRIPTION, action.getDescription().toString());
+ fragment.setArguments(args);
+ GuidedStepFragment.add(fm, fragment);
+ break;
+ default:
+ }
+ }
+
+ private List<Pair<Lineup, Integer>> lineupChannelMatchCount(
+ List<Lineup> lineups, List<String> localChannels) {
+ List<Pair<Lineup, Integer>> result = new ArrayList<>();
+ for (Lineup lineup : lineups) {
+ result.add(new Pair<>(lineup, getMatchCount(lineup.getChannels(), localChannels)));
+ }
+ // sort in decreasing order
+ Collections.sort(
+ result,
+ new Comparator<Pair<Lineup, Integer>>() {
+ @Override
+ public int compare(Pair<Lineup, Integer> pair, Pair<Lineup, Integer> other) {
+ return Integer.compare(other.second, pair.second);
+ }
+ });
+ return result;
+ }
+
+ private static int getMatchCount(List<String> lineupChannels, List<String> localChannels) {
+ int count = 0;
+ for (String lineupChannel : lineupChannels) {
+ if (TextUtils.isEmpty(lineupChannel)) {
+ continue;
+ }
+ List<String> parsedNumbers = parseChannelNumber(lineupChannel);
+ for (String channel : localChannels) {
+ if (TextUtils.isEmpty(channel)) {
+ continue;
+ }
+ if (matchChannelNumber(parsedNumbers, parseChannelNumber(channel))) {
+ if (DEBUG) {
+ Log.d(TAG, lineupChannel + " matches " + channel);
+ }
+ count++;
+ break;
+ }
+ }
+ }
+ return count;
+ }
+
+ private List<Lineup> getLineups() {
+ return new ArrayList<>(Lineups.query(mActivity.getContentResolver(), mPostcode));
+ }
+
+ private List<String> getChannelNumbers() {
+ return mChannelNumbers;
+ }
+
+ private void onLineupSelected(@Nullable Lineup lineup) {
+ if (lineup == null) {
+ return;
+ }
+ ContentValues values = new ContentValues();
+ values.put(EpgContract.EpgInputs.COLUMN_INPUT_ID, SampleTvInputService.INPUT_ID);
+ values.put(EpgContract.EpgInputs.COLUMN_LINEUP_ID, lineup.getId());
+
+ ContentResolver contentResolver = getActivity().getContentResolver();
+ EpgInput epgInput = EpgInputs.queryEpgInput(contentResolver, SampleTvInputService.INPUT_ID);
+ if (epgInput == null) {
+ contentResolver.insert(EpgContract.EpgInputs.CONTENT_URI, values);
+ } else {
+ values.put(EpgContract.EpgInputs.COLUMN_ID, epgInput.getId());
+ EpgInputs.update(contentResolver, EpgInput.createEpgChannel(values));
+ }
+ }
+
+ /**
+ * Parses the channel number string to a list of numbers (major number, minor number, etc.).
+ *
+ * @param channelNumber the display number of the channel
+ * @return a list of numbers
+ */
+ private static List<String> parseChannelNumber(String channelNumber) {
+ // TODO: add tests for this method
+ List<String> numbers =
+ new ArrayList<>(
+ Arrays.asList(TextUtils.split(channelNumber, CHANNEL_NUMBER_DELIMITER)));
+ numbers.removeAll(Collections.singleton(""));
+ if (numbers.size() < 1 || numbers.size() > 2) {
+ Log.w(TAG, "unsupported channel number format: " + channelNumber);
+ return new ArrayList<>();
+ }
+ return numbers;
+ }
+
+ /**
+ * Checks whether two lists of channel numbers match or not. If the sizes are different,
+ * additional elements are ignore.
+ */
+ private static boolean matchChannelNumber(List<String> numbers, List<String> other) {
+ if (numbers.isEmpty() || other.isEmpty()) {
+ return false;
+ }
+ int i = 0;
+ int j = 0;
+ while (i < numbers.size() && j < other.size()) {
+ if (!numbers.get(i++).equals(other.get(j++))) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/partner_support/samples/src/com/example/partnersupportsampletvinput/LocationFragment.java b/partner_support/samples/src/com/example/partnersupportsampletvinput/LocationFragment.java
new file mode 100644
index 00000000..9492e7e5
--- /dev/null
+++ b/partner_support/samples/src/com/example/partnersupportsampletvinput/LocationFragment.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 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.example.partnersupportsampletvinput;
+
+import android.app.FragmentManager;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v17.leanback.app.GuidedStepFragment;
+import android.support.v17.leanback.widget.GuidanceStylist;
+import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
+import android.support.v17.leanback.widget.GuidedAction;
+import java.util.List;
+
+/** Location Fragment for users to enter postal code */
+public class LocationFragment extends GuidedStepFragment {
+ public static final long ACTION_ID_ZIPCODE = 1;
+ private final GuidedAction mZipCodeAction =
+ new GuidedAction.Builder(getActivity())
+ .id(ACTION_ID_ZIPCODE)
+ .title("zip code")
+ .description("please enter zip code")
+ .editDescription("")
+ .descriptionEditable(true)
+ .build();
+
+ @NonNull
+ @Override
+ public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
+ return new Guidance("LocationFragment", "LocationFragment Description", null, null);
+ }
+
+ @Override
+ public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
+ actions.add(mZipCodeAction);
+ }
+
+ @Override
+ public void onCreateButtonActions(
+ @NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(GuidedAction.ACTION_ID_NEXT)
+ .title("NEXT")
+ .build());
+ }
+
+ @Override
+ public void onGuidedActionClicked(GuidedAction action) {
+ if (action == null) {
+ return;
+ }
+ FragmentManager fm = getFragmentManager();
+ switch ((int) action.getId()) {
+ case (int) ACTION_ID_ZIPCODE:
+ super.onGuidedActionClicked(action);
+ break;
+ case (int) GuidedAction.ACTION_ID_NEXT:
+ ChannelScanFragment fragment = new ChannelScanFragment();
+ Bundle args = new Bundle();
+ // TODO: validate the input postcode
+ args.putString(
+ ChannelScanFragment.KEY_POSTCODE,
+ mZipCodeAction.getDescription().toString());
+ fragment.setArguments(args);
+ GuidedStepFragment.add(fm, fragment);
+ break;
+ default:
+ }
+ }
+
+ @Override
+ public long onGuidedActionEditedAndProceed(GuidedAction action) {
+ long result = super.onGuidedActionEditedAndProceed(action);
+ action.setDescription(action.getEditDescription());
+ return result;
+ }
+}
diff --git a/partner_support/samples/src/com/example/partnersupportsampletvinput/ResultFragment.java b/partner_support/samples/src/com/example/partnersupportsampletvinput/ResultFragment.java
new file mode 100644
index 00000000..a1c17acd
--- /dev/null
+++ b/partner_support/samples/src/com/example/partnersupportsampletvinput/ResultFragment.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 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.example.partnersupportsampletvinput;
+
+import android.app.Activity;
+import android.app.FragmentManager;
+import android.content.Intent;
+import android.media.tv.TvInputInfo;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v17.leanback.app.GuidedStepFragment;
+import android.support.v17.leanback.widget.GuidanceStylist;
+import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
+import android.support.v17.leanback.widget.GuidedAction;
+import com.google.android.tv.partner.support.EpgContract;
+import java.util.List;
+
+/** Result Fragment shows the setup result */
+public class ResultFragment extends GuidedStepFragment {
+ private static final long ACTION_ID_LINEUP = 1;
+
+ @NonNull
+ @Override
+ public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
+ return new Guidance("ResultFragment", "ResultFragment Description", null, null);
+ }
+
+ @Override
+ public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
+ String title = getArguments().getString(LineupSelectionFragment.KEY_TITLE);
+ String description = getArguments().getString(LineupSelectionFragment.KEY_DESCRIPTION);
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(ACTION_ID_LINEUP)
+ .title("Selected Lineup")
+ .description(title + " (" + description + ")")
+ .focusable(false)
+ .build());
+ }
+
+ @Override
+ public void onCreateButtonActions(
+ @NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(GuidedAction.ACTION_ID_FINISH)
+ .title("FINISH")
+ .build());
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(GuidedAction.ACTION_ID_CANCEL)
+ .title("BACK")
+ .build());
+ }
+
+ @Override
+ public void onGuidedActionClicked(GuidedAction action) {
+ if (action == null) {
+ return;
+ }
+ FragmentManager fm = getFragmentManager();
+ switch ((int) action.getId()) {
+ case (int) GuidedAction.ACTION_ID_CANCEL:
+ super.onGuidedActionClicked(action);
+ fm.popBackStack();
+ break;
+ case (int) GuidedAction.ACTION_ID_FINISH:
+ setResultAndFinishActivity(SampleTvInputService.INPUT_ID);
+ finishGuidedStepFragments();
+ break;
+ default:
+ }
+ }
+
+ private void setResultAndFinishActivity(String inputId) {
+ Intent data = new Intent();
+ data.putExtra(TvInputInfo.EXTRA_INPUT_ID, inputId);
+ data.putExtra(EpgContract.EXTRA_USE_CLOUD_EPG, true);
+ getActivity().setResult(Activity.RESULT_OK, data);
+ getActivity().finishActivity(SampleTvInputSetupActivity.REQUEST_CODE_START_SETUP_ACTIVITY);
+ }
+}
diff --git a/partner_support/samples/src/com/example/partnersupportsampletvinput/SampleTvInputService.java b/partner_support/samples/src/com/example/partnersupportsampletvinput/SampleTvInputService.java
new file mode 100644
index 00000000..3edf2281
--- /dev/null
+++ b/partner_support/samples/src/com/example/partnersupportsampletvinput/SampleTvInputService.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 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.example.partnersupportsampletvinput;
+
+import android.content.Context;
+import android.media.tv.TvInputService;
+import android.net.Uri;
+import android.view.Surface;
+
+/** SampleTvInputService */
+public class SampleTvInputService extends TvInputService {
+ public static final String INPUT_ID =
+ "com.example.partnersupportsampletvinput/.SampleTvInputService";
+
+ public BaseTvInputSessionImpl onCreateSession(String s) {
+ return null;
+ }
+
+ class BaseTvInputSessionImpl extends Session {
+
+ public BaseTvInputSessionImpl(Context context) {
+ super(context);
+ }
+
+ public void onRelease() {}
+
+ public boolean onSetSurface(Surface surface) {
+ return false;
+ }
+
+ public void onSetStreamVolume(float v) {}
+
+ public boolean onTune(Uri uri) {
+ return false;
+ }
+
+ public void onSetCaptionEnabled(boolean b) {}
+ }
+}
diff --git a/partner_support/samples/src/com/example/partnersupportsampletvinput/SampleTvInputSetupActivity.java b/partner_support/samples/src/com/example/partnersupportsampletvinput/SampleTvInputSetupActivity.java
new file mode 100644
index 00000000..a0a75881
--- /dev/null
+++ b/partner_support/samples/src/com/example/partnersupportsampletvinput/SampleTvInputSetupActivity.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 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.example.partnersupportsampletvinput;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v17.leanback.app.GuidedStepFragment;
+
+/** The setup activity for partner support sample TV input. */
+public class SampleTvInputSetupActivity extends Activity {
+ public static final int REQUEST_CODE_START_SETUP_ACTIVITY = 1;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (savedInstanceState == null) {
+ WelcomeFragment wf = new WelcomeFragment();
+ wf.setArguments(getIntent().getExtras());
+ GuidedStepFragment.addAsRoot(this, wf, android.R.id.content);
+ }
+ }
+}
diff --git a/partner_support/samples/src/com/example/partnersupportsampletvinput/WelcomeFragment.java b/partner_support/samples/src/com/example/partnersupportsampletvinput/WelcomeFragment.java
new file mode 100644
index 00000000..286f34f2
--- /dev/null
+++ b/partner_support/samples/src/com/example/partnersupportsampletvinput/WelcomeFragment.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 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.example.partnersupportsampletvinput;
+
+import android.app.FragmentManager;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v17.leanback.app.GuidedStepFragment;
+import android.support.v17.leanback.widget.GuidanceStylist;
+import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
+import android.support.v17.leanback.widget.GuidedAction;
+import java.util.List;
+
+/** Welcome Fragment shows welcome information for users */
+public class WelcomeFragment extends GuidedStepFragment {
+
+ @NonNull
+ @Override
+ public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
+ return new Guidance("WelcomeFragment Title", "WelcomeFragment Description", null, null);
+ }
+
+ @Override
+ public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(GuidedAction.ACTION_ID_NEXT)
+ .title("NEXT")
+ .build());
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(GuidedAction.ACTION_ID_CANCEL)
+ .title("CANCEL")
+ .build());
+ }
+
+ @Override
+ public void onGuidedActionClicked(GuidedAction action) {
+ if (action == null) {
+ return;
+ }
+ FragmentManager fm = getFragmentManager();
+ switch ((int) action.getId()) {
+ case (int) GuidedAction.ACTION_ID_NEXT:
+ GuidedStepFragment.add(fm, new LocationFragment());
+ break;
+ case (int) GuidedAction.ACTION_ID_CANCEL:
+ finishGuidedStepFragments();
+ break;
+ default:
+ }
+ }
+}
diff --git a/partner_support/src/com/google/android/tv/partner/support/AutoValue_EpgInput.java b/partner_support/src/com/google/android/tv/partner/support/AutoValue_EpgInput.java
new file mode 100644
index 00000000..aad51c76
--- /dev/null
+++ b/partner_support/src/com/google/android/tv/partner/support/AutoValue_EpgInput.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 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.tv.partner.support;
+
+
+
+/**
+ * Hand copy of generated Autovalue class.
+ *
+ * TODO get autovalue working
+ */
+final class AutoValue_EpgInput extends EpgInput {
+
+ private final long id;
+ private final String inputId;
+ private final String lineupId;
+
+ AutoValue_EpgInput(
+ long id,
+ String inputId,
+ String lineupId) {
+ this.id = id;
+ if (inputId == null) {
+ throw new NullPointerException("Null inputId");
+ }
+ this.inputId = inputId;
+ if (lineupId == null) {
+ throw new NullPointerException("Null lineupId");
+ }
+ this.lineupId = lineupId;
+ }
+
+ @Override
+ public long getId() {
+ return id;
+ }
+
+ @Override
+ public String getInputId() {
+ return inputId;
+ }
+
+ @Override
+ public String getLineupId() {
+ return lineupId;
+ }
+
+ @Override
+ public String toString() {
+ return "EpgInput{"
+ + "id=" + id + ", "
+ + "inputId=" + inputId + ", "
+ + "lineupId=" + lineupId
+ + "}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof EpgInput) {
+ EpgInput that = (EpgInput) o;
+ return (this.id == that.getId())
+ && (this.inputId.equals(that.getInputId()))
+ && (this.lineupId.equals(that.getLineupId()));
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int h$ = 1;
+ h$ *= 1000003;
+ h$ ^= (int) ((id >>> 32) ^ id);
+ h$ *= 1000003;
+ h$ ^= inputId.hashCode();
+ h$ *= 1000003;
+ h$ ^= lineupId.hashCode();
+ return h$;
+ }
+
+}
diff --git a/partner_support/src/com/google/android/tv/partner/support/AutoValue_Lineup.java b/partner_support/src/com/google/android/tv/partner/support/AutoValue_Lineup.java
new file mode 100644
index 00000000..076f8a2a
--- /dev/null
+++ b/partner_support/src/com/google/android/tv/partner/support/AutoValue_Lineup.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2018 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.tv.partner.support;
+
+import android.support.annotation.Nullable;
+import java.util.List;
+
+/**
+ * Hand copy of generated Autovalue class.
+ *
+ * TODO get autovalue working
+ */
+
+final class AutoValue_Lineup extends Lineup {
+
+ private final String id;
+ private final int type;
+ private final String name;
+ private final List<String> channels;
+
+ AutoValue_Lineup(
+ String id,
+ int type,
+ @Nullable String name,
+ List<String> channels) {
+ if (id == null) {
+ throw new NullPointerException("Null id");
+ }
+ this.id = id;
+ this.type = type;
+ this.name = name;
+ if (channels == null) {
+ throw new NullPointerException("Null channels");
+ }
+ this.channels = channels;
+ }
+
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ @Override
+ public int getType() {
+ return type;
+ }
+
+ @Nullable
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public List<String> getChannels() {
+ return channels;
+ }
+
+ @Override
+ public String toString() {
+ return "Lineup{"
+ + "id=" + id + ", "
+ + "type=" + type + ", "
+ + "name=" + name + ", "
+ + "channels=" + channels
+ + "}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof Lineup) {
+ Lineup that = (Lineup) o;
+ return (this.id.equals(that.getId()))
+ && (this.type == that.getType())
+ && ((this.name == null) ? (that.getName() == null) : this.name.equals(that.getName()))
+ && (this.channels.equals(that.getChannels()));
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int h$ = 1;
+ h$ *= 1000003;
+ h$ ^= id.hashCode();
+ h$ *= 1000003;
+ h$ ^= type;
+ h$ *= 1000003;
+ h$ ^= (name == null) ? 0 : name.hashCode();
+ h$ *= 1000003;
+ h$ ^= channels.hashCode();
+ return h$;
+ }
+
+}
diff --git a/partner_support/src/com/google/android/tv/partner/support/BaseCustomization.java b/partner_support/src/com/google/android/tv/partner/support/BaseCustomization.java
new file mode 100644
index 00000000..e40d90d7
--- /dev/null
+++ b/partner_support/src/com/google/android/tv/partner/support/BaseCustomization.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2017 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.tv.partner.support;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.Build;
+import android.util.Log;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Abstract class for TV Customization support.
+ *
+ * <p>Implement customization resources as needed in a child class.
+ */
+@TargetApi(Build.VERSION_CODES.N)
+@SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated
+public class BaseCustomization {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "BaseCustomization";
+
+ // TODO move common aosp library
+
+ // TODO cache resource if performance suffers.
+
+ private static final String RES_TYPE_BOOLEAN = "bool";
+
+ private final String packageName;
+
+ protected BaseCustomization(Context context, String[] permissions) {
+ packageName = getFirstPackageNameWithPermissions(context, permissions);
+ }
+
+ public final String getPackageName() {
+ return packageName;
+ }
+
+ private static String getFirstPackageNameWithPermissions(
+ Context context, String[] permissions) {
+
+ List<PackageInfo> packageInfos =
+ context.getPackageManager().getPackagesHoldingPermissions(permissions, 0);
+ if (DEBUG) {
+ Log.d(TAG, "These packages have " + Arrays.toString(permissions) + ": " + packageInfos);
+ }
+ return packageInfos == null || packageInfos.size() == 0
+ ? ""
+ : packageInfos.get(0).packageName;
+ }
+
+ private static Resources getResourceForPackage(Context context, String packageName)
+ throws PackageManager.NameNotFoundException {
+ return context.getPackageManager().getResourcesForApplication(packageName);
+ }
+
+ public final Optional<Boolean> getBooleanResource(Context context, String resourceName) {
+ if (resourceName.isEmpty()) {
+ return Optional.empty();
+ }
+ try {
+ Resources res = getResourceForPackage(context, packageName);
+ int resId =
+ res == null
+ ? 0
+ : res.getIdentifier(resourceName, RES_TYPE_BOOLEAN, packageName);
+ if (DEBUG) {
+ Log.d(TAG, "Boolean resource " + resourceName + " has " + resId);
+ }
+ return resId == 0 ? Optional.empty() : Optional.of(res.getBoolean(resId));
+ } catch (PackageManager.NameNotFoundException e) {
+ return Optional.empty();
+ }
+ }
+}
diff --git a/partner_support/src/com/google/android/tv/partner/support/EpgContract.java b/partner_support/src/com/google/android/tv/partner/support/EpgContract.java
new file mode 100644
index 00000000..1f47b15b
--- /dev/null
+++ b/partner_support/src/com/google/android/tv/partner/support/EpgContract.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2017 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.tv.partner.support;
+
+import android.content.ContentUris;
+import android.net.Uri;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The contract between the EPG provider and applications. Contains definitions for the supported
+ * URIs and columns.
+ *
+ * <h3>Overview</h3>
+ *
+ * <p>
+ *
+ * <p>EpgContract defines a basic database of EPG data for third party inputs. The information is
+ * stored in {@link Lineups} and {@link EpgInputs} tables.
+ *
+ * <p>
+ *
+ * <ul>
+ * <li>A row in the {@link Lineups} table represents information TV Lineups for postal codes.
+ * <li>A row in the {@link EpgInputs} table represents partner or 3rd party inputs with the lineup
+ * that input uses.
+ * </ul>
+ */
+public final class EpgContract {
+
+ /** The authority for the EPG provider. */
+ public static final String AUTHORITY = "com.google.android.tv.data.epg";
+
+ /** The delimiter between channel numbers. */
+ public static final String CHANNEL_NUMBER_DELIMITER = ", ";
+
+ /**
+ * A constant of the key for intent to indicate whether cloud EPG is used.
+ *
+ * <p>Value type: Boolean
+ */
+ public static final String EXTRA_USE_CLOUD_EPG = "com.google.android.tv.extra.USE_CLOUD_EPG";
+
+ /**
+ * Returns the list of channels as a CSV string.
+ *
+ * <p>Any commas in the original channels are converted to periods.
+ */
+ public static String toChannelString(List<String> channels) {
+ // TODO use a StringJoiner if we set the min SDK to N
+ StringBuilder result = new StringBuilder();
+ for (String channel : channels) {
+ channel = channel.replace(",", ".");
+ if (result.length() > 0) {
+ result.append(CHANNEL_NUMBER_DELIMITER);
+ }
+ result.append(channel);
+ }
+ return result.toString();
+ }
+
+ /** Returns a list of channels. */
+ public static List<String> toChannelList(String channelString) {
+ return channelString == null
+ ? Collections.EMPTY_LIST
+ : Arrays.asList(channelString.split(CHANNEL_NUMBER_DELIMITER));
+ }
+
+ /** Column definitions for the EPG lineups table. */
+ public static final class Lineups {
+
+ /** Base content:// style URI for all lines in a postal code. */
+ private static final Uri LINEUPS_IN_POSTAL_CODE_URI =
+ Uri.parse("content://" + AUTHORITY + "/lineups/postal_code");
+
+ /** The MIME type of a directory of TV channels. */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/lineup";
+
+ /** The MIME type of a single TV channel. */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/lineup";
+
+ /** The content:// style URI all lineups in a postal code. */
+ public static Uri uriForPostalCode(String postalCode) {
+ return LINEUPS_IN_POSTAL_CODE_URI.buildUpon().appendPath(postalCode).build();
+ }
+
+ /**
+ * The line up id.
+ *
+ * <p>This is a opaque string that should not be parsed.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_ID = "_ID";
+
+ /**
+ * The line up name that is displayed to the user.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_NAME = "NAME";
+
+ /**
+ * The channel numbers that are supported by this lineup that is displayed to the user.
+ *
+ * <p>Comma separated value of channel numbers
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_CHANNELS = "CHANNELS";
+
+ /**
+ * The line up type.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_TYPE = "TYPE";
+
+ private Lineups() {}
+ }
+
+ /** Column definitions for the EPG inputs table. */
+ public static final class EpgInputs {
+
+ /** The content:// style URI for this table. */
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/epg_input");
+
+ /** The MIME type of a directory of TV channels. */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/epg_input";
+
+ /** The MIME type of a single TV channel. */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/epg_input";
+
+ /**
+ * Builds a URI that points to a specific input.
+ *
+ * @param rowId The ID of the input to point to.
+ */
+ public static final Uri buildUri(long rowId) {
+ return ContentUris.withAppendedId(CONTENT_URI, rowId);
+ }
+
+ /**
+ * The unique ID for a row.
+ *
+ * <p>Type: INTEGER (long)
+ */
+ public static final String COLUMN_ID = "_ID";
+
+ /**
+ * The name of the package that owns the current row.
+ *
+ * <p>The EPG provider fills in this column with the name of the package that provides the
+ * initial data of the row. If the package is later uninstalled, the rows it owns are
+ * automatically removed from the tables.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_PACKAGE_NAME = "PACKAGE_NAME";
+
+ /**
+ * The ID of the TV input service that provides this TV channel.
+ *
+ * <p>Use {@link android.media.tv.TvContract#buildInputId} to build the ID.
+ *
+ * <p>This is a required field.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_INPUT_ID = "INPUT_ID";
+ /**
+ * The line up id.
+ *
+ * <p>This is a opaque string that should not be parsed.
+ *
+ * <p>This is a required field.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_LINEUP_ID = "LINEUP_ID";
+
+ private EpgInputs() {}
+ }
+
+ private EpgContract() {}
+}
diff --git a/partner_support/src/com/google/android/tv/partner/support/EpgInput.java b/partner_support/src/com/google/android/tv/partner/support/EpgInput.java
new file mode 100644
index 00000000..82cc463a
--- /dev/null
+++ b/partner_support/src/com/google/android/tv/partner/support/EpgInput.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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.tv.partner.support;
+
+import android.content.ContentValues;
+
+/**
+ * Value class representing a TV Input that uses Live TV EPG.
+ *
+ * @see {@link EpgContract.EpgInputs}
+ */
+// TODO(b/72052568): Get autovalue to work in aosp master
+public abstract class EpgInput {
+
+ public static EpgInput createEpgChannel(long id, String inputId, String lineupId) {
+ return new AutoValue_EpgInput(id, inputId, lineupId);
+ }
+
+ public static EpgInput createEpgChannel(ContentValues contentValues) {
+ long id = contentValues.getAsLong(EpgContract.EpgInputs.COLUMN_ID);
+ String inputId = contentValues.getAsString(EpgContract.EpgInputs.COLUMN_INPUT_ID);
+ String lineupId = contentValues.getAsString(EpgContract.EpgInputs.COLUMN_LINEUP_ID);
+ return createEpgChannel(id, inputId, lineupId);
+ }
+
+ /** The unique ID for a row. */
+ public abstract long getId();
+
+ /**
+ * The ID of the TV input service that provides this TV channel.
+ *
+ * <p>Use {@link android.media.tv.TvContract#buildInputId} to build the ID.
+ */
+ public abstract String getInputId();
+
+ /**
+ * The line up id.
+ *
+ * <p>This is a opaque string that should not be parsed.
+ */
+ public abstract String getLineupId();
+
+ public final ContentValues toContentValues() {
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(EpgContract.EpgInputs.COLUMN_ID, getId());
+ contentValues.put(EpgContract.EpgInputs.COLUMN_INPUT_ID, getInputId());
+ contentValues.put(EpgContract.EpgInputs.COLUMN_LINEUP_ID, getLineupId());
+ return contentValues;
+ }
+}
diff --git a/partner_support/src/com/google/android/tv/partner/support/EpgInputs.java b/partner_support/src/com/google/android/tv/partner/support/EpgInputs.java
new file mode 100644
index 00000000..53485ec8
--- /dev/null
+++ b/partner_support/src/com/google/android/tv/partner/support/EpgInputs.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 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.tv.partner.support;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.net.Uri;
+import android.support.annotation.Nullable;
+import android.support.annotation.WorkerThread;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/** Static utilities for {@link EpgInput}. */
+public final class EpgInputs {
+ // TODO create a fake EpgContentProvider for testing.
+
+ /** Returns {@link EpgInput} for {@code inputId} or null if not found. */
+ @WorkerThread
+ @Nullable
+ public static EpgInput queryEpgInput(ContentResolver contentResolver, String inputId) {
+
+ for (EpgInput epgInput : queryEpgInputs(contentResolver)) {
+ if (inputId.equals(epgInput.getInputId())) {
+ return epgInput;
+ }
+ }
+ return null;
+ }
+
+ /** Returns all {@link EpgInput}. */
+ @WorkerThread
+ public static Set<EpgInput> queryEpgInputs(ContentResolver contentResolver) {
+ try (Cursor cursor =
+ contentResolver.query(EpgContract.EpgInputs.CONTENT_URI, null, null, null, null)) {
+ if (cursor == null) {
+ return Collections.emptySet();
+ }
+ HashSet<EpgInput> result = new HashSet<>(cursor.getCount());
+ while (cursor.moveToNext()) {
+ ContentValues contentValues = new ContentValues();
+ DatabaseUtils.cursorRowToContentValues(cursor, contentValues);
+ result.add(EpgInput.createEpgChannel(contentValues));
+ }
+ return result;
+ }
+ }
+
+ /** Insert an {@link EpgInput}. */
+ @WorkerThread
+ @Nullable
+ public static Uri insert(ContentResolver contentResolver, EpgInput epgInput) {
+ return contentResolver.insert(
+ EpgContract.EpgInputs.CONTENT_URI, epgInput.toContentValues());
+ }
+
+ /** Update an {@link EpgInput}. */
+ @WorkerThread
+ public static int update(ContentResolver contentResolver, EpgInput epgInput) {
+ return contentResolver.update(
+ EpgContract.EpgInputs.buildUri(epgInput.getId()),
+ epgInput.toContentValues(),
+ null,
+ null);
+ }
+
+ private EpgInputs() {}
+}
diff --git a/partner_support/src/com/google/android/tv/partner/support/Lineup.java b/partner_support/src/com/google/android/tv/partner/support/Lineup.java
new file mode 100644
index 00000000..6123eebd
--- /dev/null
+++ b/partner_support/src/com/google/android/tv/partner/support/Lineup.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 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.tv.partner.support;
+
+import android.content.ContentValues;
+import android.support.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** Value class for {@link com.google.android.tv.partner.support.EpgContract.Lineups} */
+// TODO(b/72052568): Get autovalue to work in aosp master
+public abstract class Lineup {
+ /** Lineup type for cable. */
+ public static final int LINEUP_CABLE = 0;
+
+ /** Lineup type for satelite. */
+ public static final int LINEUP_SATELLITE = 1;
+
+ /** Lineup type for broadcast digital. */
+ public static final int LINEUP_BROADCAST_DIGITAL = 2;
+
+ /** Lineup type for broadcast analog. */
+ public static final int LINEUP_BROADCAST_ANALOG = 3;
+
+ /** Lineup type for IPTV. */
+ public static final int LINEUP_IPTV = 4;
+
+ /**
+ * Indicates the lineup is either satellite, cable or IPTV but we are not sure which specific
+ * type.
+ */
+ public static final int LINEUP_MVPD = 5;
+
+ /** Lineup type for Internet. */
+ public static final int LINEUP_INTERNET = 6;
+
+ /** Lineup type for other. */
+ public static final int LINEUP_OTHER = 7;
+
+ public static Lineup createLineup(String id, int type, String name, List<String> channels) {
+ return new AutoValue_Lineup(
+ id, type, name, Collections.unmodifiableList(new ArrayList<>(channels)));
+ }
+
+ public static Lineup createLineup(ContentValues contentValues) {
+ String id = contentValues.getAsString(EpgContract.Lineups.COLUMN_ID);
+ int type = contentValues.getAsInteger(EpgContract.Lineups.COLUMN_TYPE);
+ String name = contentValues.getAsString(EpgContract.Lineups.COLUMN_NAME);
+ String channels = contentValues.getAsString(EpgContract.Lineups.COLUMN_CHANNELS);
+ List<String> channelList = EpgContract.toChannelList(channels);
+ return new AutoValue_Lineup(id, type, name, Collections.unmodifiableList(channelList));
+ }
+
+ /** The ID of this lineup. */
+ public abstract String getId();
+
+ /** The type associated with this lineup. */
+ public abstract int getType();
+
+ /** The human readable name associated with this lineup. */
+ @Nullable
+ public abstract String getName();
+
+ /** An unmodifiable list of channel numbers that this lineup has. */
+ public abstract List<String> getChannels();
+}
diff --git a/partner_support/src/com/google/android/tv/partner/support/Lineups.java b/partner_support/src/com/google/android/tv/partner/support/Lineups.java
new file mode 100644
index 00000000..84f02bfb
--- /dev/null
+++ b/partner_support/src/com/google/android/tv/partner/support/Lineups.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 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.tv.partner.support;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import java.util.HashSet;
+import java.util.Set;
+
+/** Static utilities for {@link Lineup}. */
+public final class Lineups {
+
+ public static Set<Lineup> query(ContentResolver contentResolver, String postalCode) {
+ Set<Lineup> result = new HashSet<>();
+ try (Cursor cursor =
+ contentResolver.query(
+ EpgContract.Lineups.uriForPostalCode(postalCode), null, null, null, null)) {
+ ContentValues contentValues = new ContentValues();
+ while (cursor.moveToNext()) {
+ contentValues.clear();
+ DatabaseUtils.cursorRowToContentValues(cursor, contentValues);
+ result.add(Lineup.createLineup(contentValues));
+ }
+ return result;
+ }
+ }
+
+ private Lineups() {}
+}
diff --git a/partner_support/src/com/google/android/tv/partner/support/PartnerCustomizations.java b/partner_support/src/com/google/android/tv/partner/support/PartnerCustomizations.java
new file mode 100644
index 00000000..7ff168e1
--- /dev/null
+++ b/partner_support/src/com/google/android/tv/partner/support/PartnerCustomizations.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 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.tv.partner.support;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build;
+
+/** Customizations available to partners */
+@TargetApi(Build.VERSION_CODES.N)
+@SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated
+public final class PartnerCustomizations extends BaseCustomization {
+
+ private static final String[] CUSTOMIZE_PERMISSIONS = {
+ "com.google.android.tv.permission.CUSTOMIZE_TV_APP"
+ };
+
+ public static final String TVPROVIDER_ALLOWS_SYSTEM_INSERTS_TO_PROGRAM_TABLE =
+ "tvprovider_allows_system_inserts_to_program_table";
+
+ public PartnerCustomizations(Context context) {
+ super(context, CUSTOMIZE_PERMISSIONS);
+ }
+
+ public boolean doesTvProviderAllowSystemInsertsToProgramTable(Context context) {
+ return getBooleanResource(context, TVPROVIDER_ALLOWS_SYSTEM_INSERTS_TO_PROGRAM_TABLE)
+ .orElse(false);
+ }
+}
diff --git a/partner_support/src/com/google/android/tv/partner/support/TunerSetupUtils.java b/partner_support/src/com/google/android/tv/partner/support/TunerSetupUtils.java
new file mode 100644
index 00000000..e25d5836
--- /dev/null
+++ b/partner_support/src/com/google/android/tv/partner/support/TunerSetupUtils.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2017 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.tv.partner.support;
+
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/** Utility class for providing tuner setup. */
+public class TunerSetupUtils {
+ private static final String TAG = "TunerSetupUtils";
+
+ private static final Pattern CHANNEL_NUMBER_DELIMITER = Pattern.compile("([ .-])");
+
+ public static List<Pair<Lineup, Integer>> lineupChannelMatchCount(
+ List<Lineup> lineups, List<String> localChannels) {
+ List<Pair<Lineup, Integer>> result = new ArrayList<>();
+ List<List<String>> parsedLocalChannels = parseChannelNumbers(localChannels);
+ for (Lineup lineup : lineups) {
+ result.add(
+ new Pair<>(lineup, getMatchCount(lineup.getChannels(), parsedLocalChannels)));
+ }
+ // sort in decreasing order
+ Collections.sort(
+ result,
+ new Comparator<Pair<Lineup, Integer>>() {
+ @Override
+ public int compare(Pair<Lineup, Integer> pair, Pair<Lineup, Integer> other) {
+ return Integer.compare(other.second, pair.second);
+ }
+ });
+ return result;
+ }
+
+ @VisibleForTesting
+ static int getMatchCount(List<String> lineupChannels, List<List<String>> parsedLocalChannels) {
+ int count = 0;
+ List<List<String>> parsedLineupChannels = parseChannelNumbers(lineupChannels);
+ for (List<String> parsedLineupChannel : parsedLineupChannels) {
+ for (List<String> parsedLocalChannel : parsedLocalChannels) {
+ if (matchChannelNumber(parsedLineupChannel, parsedLocalChannel)) {
+ count++;
+ break;
+ }
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Parses the channel number string to a list of numbers (major number, minor number, etc.).
+ *
+ * @param channelNumber the display number of the channel
+ * @return a list of numbers
+ */
+ @VisibleForTesting
+ static List<String> parseChannelNumber(String channelNumber) {
+ List<String> numbers =
+ new ArrayList<>(
+ Arrays.asList(TextUtils.split(channelNumber, CHANNEL_NUMBER_DELIMITER)));
+ numbers.removeAll(Collections.singleton(""));
+ if (numbers.size() < 1 || numbers.size() > 2) {
+ Log.w(TAG, "unsupported channel number format: " + channelNumber);
+ return new ArrayList<>();
+ }
+ return numbers;
+ }
+
+ /**
+ * Parses a list of channel numbers. See {@link #parseChannelNumber(String)}.
+ *
+ * @param channelNumbers a list of channel display numbers
+ */
+ @VisibleForTesting
+ static List<List<String>> parseChannelNumbers(List<String> channelNumbers) {
+ List<List<String>> numbers = new ArrayList<>(channelNumbers.size());
+ for (String channelNumber : channelNumbers) {
+ if (!TextUtils.isEmpty(channelNumber)) {
+ numbers.add(parseChannelNumber(channelNumber));
+ }
+ }
+ return numbers;
+ }
+
+ /**
+ * Checks whether two lists of channel numbers match or not. If the sizes are different,
+ * additional elements are ignore.
+ */
+ @VisibleForTesting
+ static boolean matchChannelNumber(List<String> numbers, List<String> other) {
+ if (numbers.isEmpty() || other.isEmpty()) {
+ return false;
+ }
+ int i = 0;
+ int j = 0;
+ while (i < numbers.size() && j < other.size()) {
+ if (!numbers.get(i++).equals(other.get(j++))) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/partner_support/tests/robotests/javatests/com/google/android/tv/partner/support/BaseCustomizationTest.java b/partner_support/tests/robotests/javatests/com/google/android/tv/partner/support/BaseCustomizationTest.java
new file mode 100644
index 00000000..a6292e3e
--- /dev/null
+++ b/partner_support/tests/robotests/javatests/com/google/android/tv/partner/support/BaseCustomizationTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 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.tv.partner.support;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import com.android.tv.testing.TestSingletonApp;
+import com.android.tv.testing.constants.ConfigConstants;
+import com.google.thirdparty.robolectric.GoogleRobolectricTestRunner;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+/** Tests for {@link BaseCustomization}. */
+
+// TODO: move to partner-support
+
+@RunWith(GoogleRobolectricTestRunner.class)
+@Config(
+ manifest =
+ "//third_party/java_src/android_app/live_channels/common/src"
+ + "/com/android/tv/common"
+ + ":common/AndroidManifest.xml",
+ sdk = ConfigConstants.SDK,
+ application = TestSingletonApp.class
+)
+public class BaseCustomizationTest {
+
+ private static final String[] PERMISSIONS = {"com.example.permission"};
+
+ private Context context;
+ private PackageManager packageManager;
+
+ @Before
+ public void setUp() {
+ context = mock(Context.class);
+ // TODO(b/68809029): Don't mock PackageManager
+ packageManager = mock(PackageManager.class);
+ when(context.getPackageManager()).thenReturn(packageManager);
+ List<PackageInfo> packageInfos = new ArrayList<>();
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = "com.example.custom";
+ packageInfos.add(packageInfo);
+ when(packageManager.getPackagesHoldingPermissions(PERMISSIONS, 0)).thenReturn(packageInfos);
+ }
+
+ @Test
+ public void getPackageName_example() {
+ TestCustomizations testCustomizations = new TestCustomizations(context);
+ assertThat(testCustomizations.getPackageName()).isEqualTo("com.example.custom");
+ }
+
+ @Test
+ public void getPackageName_none() {
+ TestCustomizations testCustomizations =
+ new TestCustomizations(RuntimeEnvironment.application);
+ assertThat(testCustomizations.getPackageName()).isEmpty();
+ }
+
+ static class TestCustomizations extends BaseCustomization {
+ protected TestCustomizations(Context context) {
+ super(context, PERMISSIONS);
+ }
+ }
+}
diff --git a/partner_support/tests/robotests/javatests/com/google/android/tv/partner/support/TunerSetupUtilsTest.java b/partner_support/tests/robotests/javatests/com/google/android/tv/partner/support/TunerSetupUtilsTest.java
new file mode 100644
index 00000000..d9b48cfd
--- /dev/null
+++ b/partner_support/tests/robotests/javatests/com/google/android/tv/partner/support/TunerSetupUtilsTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2017 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.tv.partner.support;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.tv.testing.constants.ConfigConstants;
+import com.google.thirdparty.robolectric.GoogleRobolectricTestRunner;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+/** Tests for {@link TunerSetupUtils} */
+@RunWith(GoogleRobolectricTestRunner.class)
+@Config(sdk = ConfigConstants.SDK)
+public class TunerSetupUtilsTest {
+
+ @Test
+ public void getMatchCount_allMatch() {
+ List<String> channelNumbers1 = Arrays.asList("1.1", "1.2", "2.3");
+ List<List<String>> parsedChannelNumbers2 =
+ TunerSetupUtils.parseChannelNumbers(Arrays.asList("1.1", "1.2", "2.3"));
+ assertThat(TunerSetupUtils.getMatchCount(channelNumbers1, parsedChannelNumbers2))
+ .isEqualTo(3);
+ }
+
+ @Test
+ public void getMatchCount_someMatch() {
+ List<String> channelNumbers1 = Arrays.asList("1.1", "1.2", "2.3");
+ List<List<String>> parsedChannelNumbers2 =
+ TunerSetupUtils.parseChannelNumbers(Arrays.asList("1.0", "1.1", "2"));
+ assertThat(TunerSetupUtils.getMatchCount(channelNumbers1, parsedChannelNumbers2))
+ .isEqualTo(2);
+ }
+
+ @Test
+ public void getMatchCount_noMatch() {
+ List<String> channelNumbers1 = Arrays.asList("1.1", "1.2", "2.3");
+ List<List<String>> parsedChannelNumbers2 =
+ TunerSetupUtils.parseChannelNumbers(Arrays.asList("1.0", "1.3", "3"));
+ assertThat(TunerSetupUtils.getMatchCount(channelNumbers1, parsedChannelNumbers2))
+ .isEqualTo(0);
+ }
+
+ @Test
+ public void parseChannelNumber_majorNumberOnly() {
+ assertThat(TunerSetupUtils.parseChannelNumber("11")).containsExactly("11");
+ }
+
+ @Test
+ public void parseChannelNumber_majorAndMinorNumbers() {
+ assertThat(TunerSetupUtils.parseChannelNumber("2-11")).containsExactly("2", "11");
+ }
+
+ @Test
+ public void parseChannelNumber_containsExtraSpaces() {
+ assertThat(TunerSetupUtils.parseChannelNumber(" 2 - 11 ")).containsExactly("2", "11");
+ }
+
+ @Test
+ public void parseChannelNumber_tooLong() {
+ assertThat(TunerSetupUtils.parseChannelNumber("2-11-1")).isEmpty();
+ }
+
+ @Test
+ public void parseChannelNumber_empty() {
+ assertThat(TunerSetupUtils.parseChannelNumber(" ")).isEmpty();
+ }
+
+ @Test
+ public void matchChannelNumber_match_majorAndMinorNumbers() {
+ assertThat(
+ TunerSetupUtils.matchChannelNumber(
+ Arrays.asList("22", "1"), Arrays.asList("22", "1")))
+ .isTrue();
+ }
+
+ @Test
+ public void matchChannelNumber_match_majorNumber() {
+ assertThat(
+ TunerSetupUtils.matchChannelNumber(
+ Collections.singletonList("22"), Collections.singletonList("22")))
+ .isTrue();
+ }
+
+ @Test
+ public void matchChannelNumber_match_differentSize() {
+ assertThat(
+ TunerSetupUtils.matchChannelNumber(
+ Arrays.asList("22", "1"), Collections.singletonList("22")))
+ .isTrue();
+ }
+
+ @Test
+ public void matchChannelNumber_notMatch() {
+ assertThat(
+ TunerSetupUtils.matchChannelNumber(
+ Arrays.asList("22", "1"), Collections.singletonList("11")))
+ .isFalse();
+ }
+}
diff --git a/proguard.flags b/proguard.flags
index 0edd14f3..69b17861 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -27,12 +27,6 @@
-dontwarn com.google.android.volley.**
-dontwarn com.google.android.common.**
-# Keep the methods called from native code.
--keepclasseswithmembers class com.android.tv.tuner.TunerHal {
- int openDvbFrontEndFd();
- int openDvbDemuxFd();
- int openDvbDvrFd();
-}
-keepclasseswithmembers class com.android.tv.tuner.*DataSource {
int readAt(long, byte[], int, int);
long getSize();
@@ -42,10 +36,28 @@
native <methods>;
}
-# Keep method which is used for reflection.
--keep @com.android.tv.common.annotation.UsedByReflection class * {*;}
--keepclasseswithmembers class * {
- @com.android.tv.common.annotation.UsedByReflection <methods>;
+# Configuration of proguard via annotations. Apply them to
+# the elements of your program not only to ensure correct proguard
+# functionality, but to document non-obvious entry points to your code to make
+# it survive refactorings.
+
+# Annotations are implemented as attributes, so we have to explicitly keep them.
+# Catch all which encompasses attributes like RuntimeVisibleParameterAnnotations
+# and RuntimeVisibleTypeAnnotations
+-keepattributes RuntimeVisible*Annotation*
+
+# JNI is an entry point that's hard to keep track of, so there's
+# an annotation to mark fields and methods used by native code.
+
+# Keep the annotations that proguard needs to process.
+-keep class com.android.tv.common.annotation.UsedBy*
+
+# Just because native code accesses members of a class, does not mean that the
+# class itself needs to be annotated - only annotate classes that are
+# referenced themselves in native code.
+-keep @com.android.tv.common.annotation.UsedBy* class *
+-keepclassmembers class * {
+ @com.android.tv.common.annotation.UsedBy* *;
}
# For tests
@@ -56,4 +68,3 @@
# Grpc used by epg via reflection
-keep class io.grpc.internal.DnsNameResolverProvider
-
diff --git a/res/drawable-xhdpi/ic_live_channels.png b/res/drawable-xhdpi/ic_live_channels.png
new file mode 100644
index 00000000..bb1c2d9d
--- /dev/null
+++ b/res/drawable-xhdpi/ic_live_channels.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_launcher.png b/res/drawable-xhdpi/ic_live_channels_96x96.png
index 7f4eb10d..7f4eb10d 100644
--- a/res/drawable-xhdpi/ic_launcher.png
+++ b/res/drawable-xhdpi/ic_live_channels_96x96.png
Binary files differ
diff --git a/res/layout/activity_dvr_history.xml b/res/layout/activity_dvr_history.xml
new file mode 100644
index 00000000..c44bc8a3
--- /dev/null
+++ b/res/layout/activity_dvr_history.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 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"
+ android:id="@+id/dvr_history"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/empty_info_screen"
+ android:layout_width="600dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:fontFamily="@string/font"
+ android:textSize="@dimen/tvview_block_text_size"
+ android:lineSpacingExtra="@dimen/tvview_block_line_spacing_extra"
+ android:textColor="@color/tvview_block_text_color" />
+
+ <FrameLayout android:id="@+id/fragment_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+</FrameLayout> \ No newline at end of file
diff --git a/res/layout/lb_details_overview.xml b/res/layout/details_overview.xml
index dbcf2055..dbcf2055 100644
--- a/res/layout/lb_details_overview.xml
+++ b/res/layout/details_overview.xml
diff --git a/res/layout/dvr_recording_card_view.xml b/res/layout/dvr_recording_card_view.xml
index 53a7cf3d..3e953510 100644
--- a/res/layout/dvr_recording_card_view.xml
+++ b/res/layout/dvr_recording_card_view.xml
@@ -15,7 +15,7 @@
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tv="http://schemas.android.com/apk/res/com.android.tv"
+ xmlns:tv="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
diff --git a/res/layout/tunable_tv_view.xml b/res/layout/tunable_tv_view.xml
index 00c9908c..549d0535 100644
--- a/res/layout/tunable_tv_view.xml
+++ b/res/layout/tunable_tv_view.xml
@@ -17,6 +17,27 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
+ <View android:id="@+id/channel_up"
+ android:layout_width="wrap_content"
+ android:focusable="false"
+ android:focusableInTouchMode="true"
+ android:layout_height="1dp"
+ android:layout_gravity="top" />
+ <View android:id="@+id/placeholder"
+ android:layout_width="1dp"
+ android:layout_height="1dp"
+ android:focusable="false"
+ android:focusableInTouchMode="true"
+ android:focusedByDefault="true"
+ android:layout_gravity="center" />
+
+ <View android:id="@+id/channel_down"
+ android:layout_width="wrap_content"
+ android:focusable="false"
+ android:focusableInTouchMode="true"
+ android:layout_height="1dp"
+ android:layout_gravity="bottom" />
+
<com.android.tv.ui.AppLayerTvView android:id="@+id/tv_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index a6c8735b..e57b0546 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -65,7 +65,7 @@
<item>Browse content from your apps with a familiar guide and friendly interface,
\njust like channels on TV.</item>
<item>Add more channels by installing apps that offer live channels.
-\nFind compatible apps in Google Play Store by using the link within the TV menu.</item>
+\nFind compatible apps in the online store by using the link within the TV menu.</item>
<!-- Refer to @string/settings_channel_source_item_setup for "Channel sources" menu
and @string/options_item_settings for "Settings" menu. -->
<item>Set up your newly installed channel sources to customize your channel list.
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 57049aba..cea4ee6c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -129,7 +129,7 @@
<string name="multi_audio_unknown_language">Unknown language</string>
<!-- Default string for unknown language used in "Closed Caption" options. [CHAR LIMIT=30] -->
- <string name="closed_caption_unknown_language">Closed captions %1$d</string>
+ <string name="closed_caption_unknown_language">Closed captions <xliff:g id="unknown_language_index" example="1">%1$d</xliff:g></string>
<!-- Inside "Closed captions" option side panel -->
<eat-comment />
@@ -162,7 +162,7 @@
<!-- Label of audio track with eight audio channels [CHAR LIMIT=30] -->
<string name="multi_audio_channel_surround_8">7.1 surround</string>
<!-- Label of audio track with other audio channels than 1, 2, 6, and 8 [CHAR LIMIT=30] -->
- <string name="multi_audio_channel_suffix">%d channels</string>
+ <string name="multi_audio_channel_suffix"><xliff:g id="audio_channel_count" example="5">%d</xliff:g> channels</string>
<!-- Inside "Edit Channels for a specific input" option side panel -->
<eat-comment />
@@ -330,15 +330,6 @@
<!-- Toast message when an user couldn't pass the PIN confirmation. [CHAR LIMIT=NONE] -->
<string name="pin_toast_not_match">Try again, PIN doesn\'t match</string>
- <!-- Title of postal/zip code input guided step fragment [CHAR LIMIT=30] -->
- <string name="postal_code_guidance_title">Enter your ZIP Code.</string>
- <!-- Description of postal/zip code input guided step fragment [CHAR LIMIT=NONE] -->
- <string name="postal_code_guidance_description">Live TV app will use the ZIP Code to provide a complete program guide for the TV channels.</string>
- <!-- Description of postal/zip code input edit text view to prompt users entering ZIP Code [CHAR LIMIT=30] -->
- <string name="postal_code_action_description">Enter your ZIP Code</string>
- <!-- Warning message shown in description field of postal/zip code input edit text view when user enters an invalid ZIP Code and presses Done [CHAR LIMIT=30] -->
- <string name="postal_code_invalid_warning">Invalid ZIP Code</string>
-
<!-- menu for "Settings" option -->
<eat-comment />
<!-- Title of "Settings" option. [CHAR LIMIT=30] -->
@@ -522,6 +513,13 @@
<!-- Content text of DVR recording service's notification during updating schedules. -->
<string name="dvr_notification_content_text_loading" translatable="false">Live TV are updating recording schedules.</string>
+ <!-- Default content title of tuner installing notifications. -->
+ <string name="tuner_install_notification_content_title" translatable="false">Install <xliff:g id="tuner_package" example="Tuner package">%s</xliff:g></string>
+ <!-- Default application name for tuner installing notifications. -->
+ <string name="tuner_install_default_application_name" translatable="false">Tuner Package</string>
+ <!-- Default content text of tuner installing notifications. -->
+ <string name="tuner_install_notification_content_text" translatable="false">Install tuner package to watch tuner channels.</string>
+
<!-- EPG Search strings. -->
<!-- Remove translatable="false" once UI is finalized -->
<string name="search_result_no_result" translatable="false">No result</string>
@@ -533,7 +531,7 @@
<string name="setup_sources_text">Set up your sources</string>
<!-- Description for channel sources screen in onboarding. -->
<string name="setup_sources_description">Live channels combines the experience of traditional TV channels with streaming channels provided by apps.
-\n\nGet started by setting up the channel sources already installed. Or browse Google Play Store for more apps that offer live channels.</string>
+\n\nGet started by setting up the channel sources already installed. Or browse the online store for more apps that offer live channels.</string>
<!-- Menu item label to start DVR manager UI. -->
<string name="channels_item_dvr">Recordings &amp; schedules</string>
@@ -574,8 +572,12 @@
<!-- Toast message that a new recording schedule of the current program has been created
from the user action. -->
<string name="dvr_msg_current_program_scheduled">Recording <xliff:g id="programName" example="Big bang theory">%1$s</xliff:g> from now to <xliff:g id="endTime" example="12:30 PM">%2$s</xliff:g></string>
+ <!-- Description of a card view to show Recording (DVR) history.[CHAR LIMIT=25]-->
+ <string name="dvr_history_card_view_title">Recording History</string>
<!-- Description of a card view to show full list of scheduled recordings. [CHAR LIMIT=25] -->
<string name="dvr_full_schedule_card_view_title">Full schedule</string>
+ <!-- Description of failed recordings. [CHAR LIMIT=25] -->
+ <string name="dvr_recording_failed">Recording Failed</string>
<!-- Description of how many following days the schedule list will show. [CHAR LIMIT=25] -->
<plurals name="dvr_full_schedule_card_view_content">
<item quantity="one">Next %1$d day</item>
@@ -752,9 +754,9 @@
sufficient space.-->
<string name="dvr_error_insufficient_space_description_three_or_more_recordings">The recordings of <xliff:g id="programName_1" example="Friends">%1$s</xliff:g>, <xliff:g id="programName_2" example="Friends">%2$s</xliff:g> and <xliff:g id="programName_3" example="Friends">%3$s</xliff:g> didn\'t complete due to insufficient storage.</string>
<!-- Dialog title which will be shown when the current storage is too small for DVR. -->
- <string name="dvr_error_small_sized_storage_title">DVR needs more storage</string>
+ <string name="dvr_error_small_sized_storage_title">More stroage needed</string>
<!-- Dialog description which will be shown when the current storage is too small for DVR. -->
- <string name="dvr_error_small_sized_storage_description">You will be able to record programs with DVR. However there is not enough storage on your device now for DVR to work. Please connect an external drive that is <xliff:g id="storage size" example="10GB">%1$d</xliff:g>GB or larger and follow the steps to format it as device storage.</string>
+ <string name="dvr_error_small_sized_storage_description">You will be able to record programs. However there is not enough storage on your device to start recording. Please connect an external drive that is <xliff:g id="storage_size" example="10GB">%1$d</xliff:g>GB or larger and follow the steps to format it as device storage.</string>
<!-- Dialog title which will be shown when there is no free space on the current storage for DVR. -->
<string name="dvr_error_no_free_space_title">Not enough storage</string>
<!-- Dialog description which will be shown when there is no free space on the current storage for DVR. -->
@@ -762,7 +764,7 @@
<!-- Dialog title which will be shown when the current DVR storage is not accessible. -->
<string name="dvr_error_missing_storage_title">Missing storage</string>
<!-- Dialog description which will be shown when the current DVR storage is not accessible. -->
- <string name="dvr_error_missing_storage_description" translatable="false">Some of the storage used by DVR is missing. Please connect the external drive you used before to re-enable DVR. Alternately, you can forget the storage in the storage settings, if it\'s no longer available.</string>
+ <string name="dvr_error_missing_storage_description" translatable="false">Some of the storage used for recording is missing. Please connect the external drive you used before to re-enable recording. Alternately, you can forget the storage in the storage settings, if it\'s no longer available.</string>
<!-- The recording being requested to play is not existent in storage. It may be deleted. -->
<string name="dvr_toast_recording_deleted" translatable="false">The recording seems to be deleted.</string>
@@ -892,7 +894,10 @@
<string name="dvr_schedules_tuner_conflict_will_not_be_recorded_info">Won\'t be recorded due to tuner conflicts.</string>
<!-- Description of no schedule recording for now, and ask user to schedule recordings from
the program guide. -->
- <string name="dvr_schedules_empty_state">There are no recordings on schedule yet.\nYou can schedule recording from the program guide.</string>
+ <string name="dvr_schedules_empty_state">There are no recordings scheduled yet.\nYou can schedule recordings from the program guide.</string>
+ <!-- Description of no recording history for now, and ask user to schedule recordings from
+ the program guide. -->
+ <string name="dvr_history_empty_state">There is no recording history.\nYou can schedule recordings from the program guide.</string>
<!-- Description of schedule list header about how many recordings conflict. -->
<plurals name="dvr_series_schedules_header_description">
<item quantity="one">%1$d recording conflict</item>
diff --git a/res/xml/remote_config_defaults.xml b/res/xml/remote_config_defaults.xml
deleted file mode 100644
index 6ecad191..00000000
--- a/res/xml/remote_config_defaults.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?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.
- -->
-<defaultsMap>
- <entry>
- <key>live_channels_ac3_software_decoder</key>
- <value>false</value>
- </entry>
-
- <entry>
- <key>live_channels_epg_host_and_port</key>
- <value>datamixer-pa.googleapis.com:443</value>
- </entry>
-
- <entry>
- <key>live_channels_epg_fetcher_interval_hour</key>
- <value>4</value>
- </entry>
-
- <entry>
- <key>live_channels_epg_reader_max_channels_per_program_fetch</key>
- <value>20</value>
- </entry>
-</defaultsMap> \ No newline at end of file
diff --git a/src/com/android/exoplayer2/ext/ffmpeg/FfmpegAudioDecoder.java b/src/com/android/exoplayer2/ext/ffmpeg/FfmpegAudioDecoder.java
deleted file mode 100644
index 2b7817dc..00000000
--- a/src/com/android/exoplayer2/ext/ffmpeg/FfmpegAudioDecoder.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2017 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.exoplayer2.ext.ffmpeg;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
-import com.google.android.exoplayer2.decoder.SimpleOutputBuffer;
-import com.google.android.exoplayer2.util.MimeTypes;
-import com.android.tv.common.SoftPreconditions;
-
-import java.nio.ByteBuffer;
-
-/**
- * Audio decoder which uses ffmpeg extension of ExoPlayer2. Since {@link FfmpegDecoder} is package
- * private, expose the decoder via this class. Supported formats are AC3 and MP2.
- */
-public class FfmpegAudioDecoder {
- private static final int NUM_DECODER_BUFFERS = 1;
-
- // The largest AC3 sample size. This is bigger than the largest MP2 sample size (1729).
- private static final int INITIAL_INPUT_BUFFER_SIZE = 2560;
- private static boolean AVAILABLE;
-
- static {
- AVAILABLE =
- FfmpegLibrary.supportsFormat(MimeTypes.AUDIO_AC3)
- && FfmpegLibrary.supportsFormat(MimeTypes.AUDIO_MPEG_L2);
- }
-
- private FfmpegDecoder mDecoder;
- private DecoderInputBuffer mInputBuffer;
- private SimpleOutputBuffer mOutputBuffer;
- private boolean mStarted;
-
- /** Return whether Ffmpeg based software audio decoder is available. */
- public static boolean isAvailable() {
- return AVAILABLE;
- }
-
- /** Creates an Ffmpeg based software audio decoder. */
- public FfmpegAudioDecoder(Context context) {
- if (context.checkSelfPermission("android.permission.INTERNET")
- == PackageManager.PERMISSION_GRANTED) {
- throw new IllegalStateException("This code should run in an isolated process");
- }
- }
-
- /**
- * Decodes an audio sample.
- *
- * @param timeUs presentation timestamp of the sample
- * @param sample data
- */
- public void decode(long timeUs, byte[] sample) {
- SoftPreconditions.checkState(AVAILABLE);
- mInputBuffer.data.clear();
- mInputBuffer.data.put(sample);
- mInputBuffer.data.flip();
- mInputBuffer.timeUs = timeUs;
- mDecoder.decode(mInputBuffer, mOutputBuffer, !mStarted);
- if (!mStarted) {
- mStarted = true;
- }
- }
-
- /** Returns a decoded sample from decoder. */
- public ByteBuffer getDecodedSample() {
- return mOutputBuffer.data;
- }
-
- /** Returns the presentation time for the decoded sample. */
- public long getDecodedTimeUs() {
- return mOutputBuffer.timeUs;
- }
-
- /**
- * Clear previous decode state if any. Prepares to decode samples of the specified encoding.
- * This method should be called before using decode.
- *
- * @param mime audio encoding
- */
- public void resetDecoderState(String mime) {
- SoftPreconditions.checkState(AVAILABLE);
- release();
- try {
- mDecoder =
- new FfmpegDecoder(
- NUM_DECODER_BUFFERS,
- NUM_DECODER_BUFFERS,
- INITIAL_INPUT_BUFFER_SIZE,
- mime,
- null);
- mStarted = false;
- mInputBuffer = mDecoder.createInputBuffer();
- // Since native JNI requires direct buffer, we should allocate it by #allocateDirect.
- mInputBuffer.data = ByteBuffer.allocateDirect(INITIAL_INPUT_BUFFER_SIZE);
- mOutputBuffer = mDecoder.createOutputBuffer();
- } catch (FfmpegDecoderException e) {
- // if AVAILABLE is {@code true}, this will not happen.
- }
- }
-
- /** Releases all the resource. */
- public void release() {
- SoftPreconditions.checkState(AVAILABLE);
- if (mDecoder != null) {
- mDecoder.release();
- mInputBuffer = null;
- mOutputBuffer = null;
- mDecoder = null;
- }
- }
-}
diff --git a/src/com/android/exoplayer2/ext/ffmpeg/FfmpegLibrary.java b/src/com/android/exoplayer2/ext/ffmpeg/FfmpegLibrary.java
deleted file mode 100644
index daa77340..00000000
--- a/src/com/android/exoplayer2/ext/ffmpeg/FfmpegLibrary.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2017 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.exoplayer2.ext.ffmpeg;
-
-import com.google.android.exoplayer2.util.LibraryLoader;
-import com.google.android.exoplayer2.util.MimeTypes;
-
-/**
- * This class is based on com.google.android.exoplayer2.ext.ffmpeg.FfmpegLibrary from ExoPlayer2
- * in order to support mp2 decoder.
- * Configures and queries the underlying native library.
- */
-public final class FfmpegLibrary {
-
- private static final LibraryLoader LOADER =
- new LibraryLoader("avutil", "avresample", "avcodec", "ffmpeg");
-
- private FfmpegLibrary() {}
-
- /**
- * Overrides the names of the FFmpeg native libraries. If an application wishes to call this
- * method, it must do so before calling any other method defined by this class, and before
- * instantiating a {@link FfmpegAudioRenderer} instance.
- */
- public static void setLibraries(String... libraries) {
- LOADER.setLibraries(libraries);
- }
-
- /**
- * Returns whether the underlying library is available, loading it if necessary.
- */
- public static boolean isAvailable() {
- return LOADER.isAvailable();
- }
-
- /**
- * Returns the version of the underlying library if available, or null otherwise.
- */
- public static String getVersion() {
- return isAvailable() ? ffmpegGetVersion() : null;
- }
-
- /**
- * Returns whether the underlying library supports the specified MIME type.
- */
- public static boolean supportsFormat(String mimeType) {
- if (!isAvailable()) {
- return false;
- }
- String codecName = getCodecName(mimeType);
- return codecName != null && ffmpegHasDecoder(codecName);
- }
-
- /**
- * Returns the name of the FFmpeg decoder that could be used to decode {@code mimeType}.
- */
- /* package */ static String getCodecName(String mimeType) {
- switch (mimeType) {
- case MimeTypes.AUDIO_MPEG_L2:
- return "mp2";
- case MimeTypes.AUDIO_AC3:
- return "ac3";
- default:
- return null;
- }
- }
-
- private static native String ffmpegGetVersion();
- private static native boolean ffmpegHasDecoder(String codecName);
-
-}
diff --git a/src/com/android/tv/AudioManagerHelper.java b/src/com/android/tv/AudioManagerHelper.java
index 4fca06ac..942d431d 100644
--- a/src/com/android/tv/AudioManagerHelper.java
+++ b/src/com/android/tv/AudioManagerHelper.java
@@ -1,66 +1,94 @@
+/*
+ * Copyright (C) 2017 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;
import android.app.Activity;
import android.content.Context;
import android.media.AudioManager;
import android.os.Build;
-
import com.android.tv.receiver.AudioCapabilitiesReceiver;
import com.android.tv.ui.TunableTvView;
+import com.android.tv.ui.TunableTvViewPlayingApi;
-/**
- * A helper class to help {@link MainActivity} to handle audio-related stuffs.
- */
+/** A helper class to help {@link MainActivity} to handle audio-related stuffs. */
class AudioManagerHelper implements AudioManager.OnAudioFocusChangeListener {
private static final float AUDIO_MAX_VOLUME = 1.0f;
private static final float AUDIO_MIN_VOLUME = 0.0f;
private static final float AUDIO_DUCKING_VOLUME = 0.3f;
private final Activity mActivity;
- private final TunableTvView mTvView;
+ private final TunableTvViewPlayingApi mTvView;
private final AudioManager mAudioManager;
private final AudioCapabilitiesReceiver mAudioCapabilitiesReceiver;
private boolean mAc3PassthroughSupported;
private int mAudioFocusStatus = AudioManager.AUDIOFOCUS_LOSS;
- AudioManagerHelper(Activity activity, TunableTvView tvView) {
+ AudioManagerHelper(Activity activity, TunableTvViewPlayingApi tvView) {
mActivity = activity;
mTvView = tvView;
mAudioManager = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);
- mAudioCapabilitiesReceiver = new AudioCapabilitiesReceiver(activity,
- new AudioCapabilitiesReceiver.OnAc3PassthroughCapabilityChangeListener() {
- @Override
- public void onAc3PassthroughCapabilityChange(boolean capability) {
- mAc3PassthroughSupported = capability;
- }
- });
+ mAudioCapabilitiesReceiver =
+ new AudioCapabilitiesReceiver(
+ activity,
+ new AudioCapabilitiesReceiver.OnAc3PassthroughCapabilityChangeListener() {
+ @Override
+ public void onAc3PassthroughCapabilityChange(boolean capability) {
+ mAc3PassthroughSupported = capability;
+ }
+ });
mAudioCapabilitiesReceiver.register();
}
/**
- * Sets suitable volume to {@link TunableTvView} according to the current audio focus.
- * If the focus status is {@link AudioManager#AUDIOFOCUS_LOSS} and the activity is under PIP
- * mode, this method will finish the activity.
+ * Sets suitable volume to {@link TunableTvView} according to the current audio focus. If the
+ * focus status is {@link AudioManager#AUDIOFOCUS_LOSS} and the activity is under PIP mode, this
+ * method will finish the activity.
*/
void setVolumeByAudioFocusStatus() {
if (mTvView.isPlaying()) {
switch (mAudioFocusStatus) {
case AudioManager.AUDIOFOCUS_GAIN:
- mTvView.setStreamVolume(AUDIO_MAX_VOLUME);
+ if (mTvView.isTimeShiftAvailable()) {
+ mTvView.timeshiftPlay();
+ } else {
+ mTvView.setStreamVolume(AUDIO_MAX_VOLUME);
+ }
break;
case AudioManager.AUDIOFOCUS_LOSS:
- if (Features.PICTURE_IN_PICTURE.isEnabled(mActivity)
+ if (TvFeatures.PICTURE_IN_PICTURE.isEnabled(mActivity)
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
&& mActivity.isInPictureInPictureMode()) {
mActivity.finish();
break;
}
+ // fall through
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
- mTvView.setStreamVolume(AUDIO_MIN_VOLUME);
+ if (mTvView.isTimeShiftAvailable()) {
+ mTvView.timeshiftPause();
+ } else {
+ mTvView.setStreamVolume(AUDIO_MIN_VOLUME);
+ }
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
- mTvView.setStreamVolume(AUDIO_DUCKING_VOLUME);
+ if (mTvView.isTimeShiftAvailable()) {
+ mTvView.timeshiftPause();
+ } else {
+ mTvView.setStreamVolume(AUDIO_DUCKING_VOLUME);
+ }
break;
}
}
@@ -71,31 +99,28 @@ class AudioManagerHelper implements AudioManager.OnAudioFocusChangeListener {
* returned result.
*/
void requestAudioFocus() {
- int result = mAudioManager.requestAudioFocus(this,
- AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
- mAudioFocusStatus = (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) ?
- AudioManager.AUDIOFOCUS_GAIN : AudioManager.AUDIOFOCUS_LOSS;
+ int result =
+ mAudioManager.requestAudioFocus(
+ this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
+ mAudioFocusStatus =
+ (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED)
+ ? AudioManager.AUDIOFOCUS_GAIN
+ : AudioManager.AUDIOFOCUS_LOSS;
setVolumeByAudioFocusStatus();
}
- /**
- * Abandons audio focus.
- */
+ /** Abandons audio focus. */
void abandonAudioFocus() {
mAudioFocusStatus = AudioManager.AUDIOFOCUS_LOSS;
mAudioManager.abandonAudioFocus(this);
}
- /**
- * Returns {@code true} if the device supports AC3 pass-through.
- */
+ /** Returns {@code true} if the device supports AC3 pass-through. */
boolean isAc3PassthroughSupported() {
return mAc3PassthroughSupported;
}
- /**
- * Release the resources the helper class may occupied.
- */
+ /** Release the resources the helper class may occupied. */
void release() {
mAudioCapabilitiesReceiver.unregister();
}
diff --git a/src/com/android/tv/ChannelTuner.java b/src/com/android/tv/ChannelTuner.java
index faa27bbd..8ab145a4 100644
--- a/src/com/android/tv/ChannelTuner.java
+++ b/src/com/android/tv/ChannelTuner.java
@@ -24,12 +24,10 @@ import android.support.annotation.MainThread;
import android.support.annotation.Nullable;
import android.util.ArraySet;
import android.util.Log;
-
import com.android.tv.common.SoftPreconditions;
-import com.android.tv.data.Channel;
import com.android.tv.data.ChannelDataManager;
+import com.android.tv.data.api.Channel;
import com.android.tv.util.TvInputManagerHelper;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -57,11 +55,9 @@ public class ChannelTuner {
private final Handler mHandler = new Handler();
private final ChannelDataManager mChannelDataManager;
private final Set<Listener> mListeners = new ArraySet<>();
- @Nullable
- private Channel mCurrentChannel;
+ @Nullable private Channel mCurrentChannel;
private final TvInputManagerHelper mInputManager;
- @Nullable
- private TvInputInfo mCurrentChannelInputInfo;
+ @Nullable private TvInputInfo mCurrentChannelInputInfo;
private final ChannelDataManager.Listener mChannelDataManagerListener =
new ChannelDataManager.Listener() {
@@ -86,16 +82,14 @@ public class ChannelTuner {
l.onBrowsableChannelListChanged();
}
}
- };
+ };
public ChannelTuner(ChannelDataManager channelDataManager, TvInputManagerHelper inputManager) {
mChannelDataManager = channelDataManager;
mInputManager = inputManager;
}
- /**
- * Starts ChannelTuner. It cannot be called twice before calling {@link #stop}.
- */
+ /** Starts ChannelTuner. It cannot be called twice before calling {@link #stop}. */
public void start() {
if (mStarted) {
throw new IllegalStateException("start is called twice");
@@ -103,18 +97,17 @@ public class ChannelTuner {
mStarted = true;
mChannelDataManager.addListener(mChannelDataManagerListener);
if (mChannelDataManager.isDbLoadFinished()) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mChannelDataManagerListener.onLoadFinished();
- }
- });
+ mHandler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ mChannelDataManagerListener.onLoadFinished();
+ }
+ });
}
}
- /**
- * Stops ChannelTuner.
- */
+ /** Stops ChannelTuner. */
public void stop() {
if (!mStarted) {
return;
@@ -130,30 +123,22 @@ public class ChannelTuner {
mChannelDataManagerLoaded = false;
}
- /**
- * Returns true, if all the channels are loaded.
- */
+ /** Returns true, if all the channels are loaded. */
public boolean areAllChannelsLoaded() {
return mChannelDataManagerLoaded;
}
- /**
- * Returns browsable channel lists.
- */
+ /** Returns browsable channel lists. */
public List<Channel> getBrowsableChannelList() {
return Collections.unmodifiableList(mBrowsableChannels);
}
- /**
- * Returns the number of browsable channels.
- */
+ /** Returns the number of browsable channels. */
public int getBrowsableChannelCount() {
return mBrowsableChannels.size();
}
- /**
- * Returns the current channel.
- */
+ /** Returns the current channel. */
@Nullable
public Channel getCurrentChannel() {
return mCurrentChannel;
@@ -169,16 +154,12 @@ public class ChannelTuner {
mCurrentChannel = currentChannel;
}
- /**
- * Returns the current channel's ID.
- */
+ /** Returns the current channel's ID. */
public long getCurrentChannelId() {
return mCurrentChannel != null ? mCurrentChannel.getId() : Channel.INVALID_ID;
}
- /**
- * Returns the current channel's URI
- */
+ /** Returns the current channel's URI */
public Uri getCurrentChannelUri() {
if (mCurrentChannel == null) {
return null;
@@ -190,17 +171,13 @@ public class ChannelTuner {
}
}
- /**
- * Returns the current {@link TvInputInfo}.
- */
+ /** Returns the current {@link TvInputInfo}. */
@Nullable
public TvInputInfo getCurrentInputInfo() {
return mCurrentChannelInputInfo;
}
- /**
- * Returns true, if the current channel is for a passthrough TV input.
- */
+ /** Returns true, if the current channel is for a passthrough TV input. */
public boolean isCurrentChannelPassthrough() {
return mCurrentChannel != null && mCurrentChannel.isPassthrough();
}
@@ -208,8 +185,8 @@ public class ChannelTuner {
/**
* Moves the current channel to the next (or previous) browsable channel.
*
- * @return true, if the channel is changed to the adjacent channel. If there is no
- * browsable channel, it returns false.
+ * @return true, if the channel is changed to the adjacent channel. If there is no browsable
+ * channel, it returns false.
*/
public boolean moveToAdjacentBrowsableChannel(boolean up) {
Channel channel = getAdjacentBrowsableChannel(up);
@@ -221,8 +198,8 @@ public class ChannelTuner {
}
/**
- * Returns a next browsable channel. It doesn't change the current channel unlike
- * {@link #moveToAdjacentBrowsableChannel}.
+ * Returns a next browsable channel. It doesn't change the current channel unlike {@link
+ * #moveToAdjacentBrowsableChannel}.
*/
public Channel getAdjacentBrowsableChannel(boolean up) {
if (isCurrentChannelPassthrough() || getBrowsableChannelCount() == 0) {
@@ -240,8 +217,7 @@ public class ChannelTuner {
}
int size = mChannels.size();
for (int i = 0; i < size; ++i) {
- int nextChannelIndex = up ? channelIndex + 1 + i
- : channelIndex - 1 - i + size;
+ int nextChannelIndex = up ? channelIndex + 1 + i : channelIndex - 1 - i + size;
if (nextChannelIndex >= size) {
nextChannelIndex -= size;
}
@@ -289,7 +265,7 @@ public class ChannelTuner {
* as a browsable channel.
*
* @return true, the channel change is success. But, if the channel doesn't exist, the channel
- * change will be failed and it will return false.
+ * change will be failed and it will return false.
*/
public boolean moveToChannel(Channel channel) {
if (channel == null) {
@@ -308,43 +284,29 @@ public class ChannelTuner {
return false;
}
- /**
- * Resets the current channel to {@code null}.
- */
+ /** Resets the current channel to {@code null}. */
public void resetCurrentChannel() {
setCurrentChannelAndNotify(null);
}
- /**
- * Adds {@link Listener}.
- */
+ /** Adds {@link Listener}. */
public void addListener(Listener listener) {
mListeners.add(listener);
}
- /**
- * Removes {@link Listener}.
- */
+ /** Removes {@link Listener}. */
public void removeListener(Listener listener) {
mListeners.remove(listener);
}
public interface Listener {
- /**
- * Called when all the channels are loaded.
- */
+ /** Called when all the channels are loaded. */
void onLoadFinished();
- /**
- * Called when the browsable channel list is changed.
- */
+ /** Called when the browsable channel list is changed. */
void onBrowsableChannelListChanged();
- /**
- * Called when the current channel is removed.
- */
+ /** Called when the current channel is removed. */
void onCurrentChannelUnavailable(Channel channel);
- /**
- * Called when the current channel is changed.
- */
+ /** Called when the current channel is changed. */
void onChannelChanged(Channel previousChannel, Channel currentChannel);
}
diff --git a/src/com/android/tv/Features.java b/src/com/android/tv/Features.java
deleted file mode 100644
index 2052f2e7..00000000
--- a/src/com/android/tv/Features.java
+++ /dev/null
@@ -1,204 +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;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.Build;
-import android.support.annotation.VisibleForTesting;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.tv.common.feature.Feature;
-import com.android.tv.common.feature.GServiceFeature;
-import com.android.tv.common.feature.PropertyFeature;
-import com.android.tv.config.RemoteConfig;
-import com.android.tv.experiments.Experiments;
-import com.android.tv.util.LocationUtils;
-import com.android.tv.util.PermissionUtils;
-import com.android.tv.util.Utils;
-
-import java.util.Locale;
-
-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.OFF;
-import static com.android.tv.common.feature.FeatureUtils.ON;
-import static com.android.tv.common.feature.FeatureUtils.OR;
-
-/**
- * List of {@link Feature} for the Live TV App.
- *
- * <p>Remove the {@code Feature} once it is launched.
- */
-public final class Features {
- private static final String TAG = "Features";
- private static final boolean DEBUG = false;
-
- /**
- * UI for opting in to analytics.
- *
- * <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 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 final Feature ANALYTICS_V2 = AND(ON, ANALYTICS_OPT_IN);
-
- public static final Feature EPG_SEARCH =
- new PropertyFeature("feature_tv_use_epg_search", false);
-
- public static final Feature TUNER =
- new Feature() {
- @Override
- public boolean isEnabled(Context context) {
-
- if (Utils.isDeveloper()) {
- // we enable tuner for developers to test tuner in any platform.
- return true;
- }
-
- // 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
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
- }
- };
-
- /**
- * Use network tuner if it is available and there is no other tuner types.
- */
- public static final Feature NETWORK_TUNER =
- new Feature() {
- @Override
- public boolean isEnabled(Context context) {
- if (!TUNER.isEnabled(context)) {
- return false;
- }
- if (Utils.isDeveloper()) {
- // Network tuner will be enabled for developers.
- return true;
- }
- return Locale.US.getCountry().equalsIgnoreCase(
- LocationUtils.getCurrentCountry(context));
- }
- };
-
- 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 final Feature UNHIDE =
- 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);
- }
- });
-
- public static final Feature PICTURE_IN_PICTURE =
- new Feature() {
- private Boolean mEnabled;
-
- @Override
- public boolean isEnabled(Context context) {
- if (mEnabled == null) {
- mEnabled =
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
- && context.getPackageManager()
- .hasSystemFeature(
- PackageManager.FEATURE_PICTURE_IN_PICTURE);
- }
- return mEnabled;
- }
- };
-
- /** Use AC3 software decode. */
- public static final Feature AC3_SOFTWARE_DECODE =
- new Feature() {
- private final String[] SUPPORTED_REGIONS = {};
-
- private Boolean mEnabled;
-
- @Override
- public boolean isEnabled(Context context) {
- if (mEnabled == null) {
- if (mEnabled == null) {
- // We will not cache the result of fallback solution.
- String country = LocationUtils.getCurrentCountry(context);
- for (int i = 0; i < SUPPORTED_REGIONS.length; ++i) {
- if (SUPPORTED_REGIONS[i].equalsIgnoreCase(country)) {
- return true;
- }
- }
- if (DEBUG) Log.d(TAG, "AC3 flag false after country check");
- return false;
- }
- }
- if (DEBUG) Log.d(TAG, "AC3 flag " + mEnabled);
- return mEnabled;
- }
- };
-
- /** Show postal code fragment before channel scan. */
- public static final Feature ENABLE_CLOUD_EPG_REGION =
- new Feature() {
- private final String[] SUPPORTED_REGIONS = {
- };
-
-
- @Override
- public boolean isEnabled(Context context) {
- if (!Experiments.CLOUD_EPG.get()) {
- if (DEBUG) Log.d(TAG, "Experiments.CLOUD_EPG is false");
- return false;
- }
- String country = LocationUtils.getCurrentCountry(context);
- for (int i = 0; i < SUPPORTED_REGIONS.length; i++) {
- if (SUPPORTED_REGIONS[i].equalsIgnoreCase(country)) {
- return true;
- }
- }
- if (DEBUG) Log.d(TAG, "EPG flag false after country check");
- return false;
- }
- };
-
- /** Enable a conflict dialog between currently watched channel and upcoming recording. */
- public static final Feature SHOW_UPCOMING_CONFLICT_DIALOG = OFF;
-
- /**
- * Use input blacklist to disable partner's tuner input.
- */
- public static final Feature USE_PARTNER_INPUT_BLACKLIST = ON;
-
- /**
- * Enable Dvb parsers and listeners.
- */
- public static final Feature ENABLE_FILE_DVB = OFF;
-
- @VisibleForTesting
- public static final Feature TEST_FEATURE = new PropertyFeature("test_feature", false);
-
- private Features() {
- }
-}
diff --git a/src/com/android/tv/InputSessionManager.java b/src/com/android/tv/InputSessionManager.java
index 2978f409..4f298ed6 100644
--- a/src/com/android/tv/InputSessionManager.java
+++ b/src/com/android/tv/InputSessionManager.java
@@ -36,28 +36,27 @@ import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
-
-import com.android.tv.data.Channel;
+import com.android.tv.data.api.Channel;
import com.android.tv.ui.TunableTvView;
import com.android.tv.ui.TunableTvView.OnTuneListener;
import com.android.tv.util.TvInputManagerHelper;
-
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
- * Manages input sessions.
- * Responsible for:
+ * Manages input sessions. Responsible for:
+ *
* <ul>
- * <li>Manage {@link TvView} sessions and recording sessions</li>
- * <li>Manage capabilities (conflict)</li>
+ * <li>Manage {@link TvView} sessions and recording sessions
+ * <li>Manage capabilities (conflict)
* </ul>
- * <p>
- * As TvView's methods should be called on the main thread and the {@link RecordingSession} should
- * look at the state of the {@link TvViewSession} when it calls the framework methods, the framework
- * calls in RecordingSession are made on the main thread not to introduce the multi-thread problems.
+ *
+ * <p>As TvView's methods should be called on the main thread and the {@link RecordingSession}
+ * should look at the state of the {@link TvViewSession} when it calls the framework methods, the
+ * framework calls in RecordingSession are made on the main thread not to introduce the multi-thread
+ * problems.
*/
@TargetApi(Build.VERSION_CODES.N)
public class InputSessionManager {
@@ -77,27 +76,25 @@ public class InputSessionManager {
public InputSessionManager(Context context) {
mContext = context.getApplicationContext();
- mInputManager = TvApplication.getSingletons(context).getTvInputManagerHelper();
+ mInputManager = TvSingletons.getSingletons(context).getTvInputManagerHelper();
}
/**
* Creates the session for {@link TvView}.
- * <p>
- * Do not call {@link TvView#setCallback} after the session is created.
+ *
+ * <p>Do not call {@link TvView#setCallback} after the session is created.
*/
@MainThread
@NonNull
- public TvViewSession createTvViewSession(TvView tvView, TunableTvView tunableTvView,
- TvInputCallback callback) {
+ public TvViewSession createTvViewSession(
+ TvView tvView, TunableTvView tunableTvView, TvInputCallback callback) {
TvViewSession session = new TvViewSession(tvView, tunableTvView, callback);
mTvViewSessions.add(session);
if (DEBUG) Log.d(TAG, "TvView session created: " + session);
return session;
}
- /**
- * Releases the {@link TvView} session.
- */
+ /** Releases the {@link TvView} session. */
@MainThread
public void releaseTvViewSession(TvViewSession session) {
mTvViewSessions.remove(session);
@@ -105,12 +102,14 @@ public class InputSessionManager {
if (DEBUG) Log.d(TAG, "TvView session released: " + session);
}
- /**
- * Creates the session for recording.
- */
+ /** Creates the session for recording. */
@NonNull
- public RecordingSession createRecordingSession(String inputId, String tag,
- RecordingCallback callback, Handler handler, long endTimeMs) {
+ public RecordingSession createRecordingSession(
+ String inputId,
+ String tag,
+ RecordingCallback callback,
+ Handler handler,
+ long endTimeMs) {
RecordingSession session = new RecordingSession(inputId, tag, callback, handler, endTimeMs);
mRecordingSessions.add(session);
if (DEBUG) Log.d(TAG, "Recording session created: " + session);
@@ -120,9 +119,7 @@ public class InputSessionManager {
return session;
}
- /**
- * Releases the recording session.
- */
+ /** Releases the recording session. */
public void releaseRecordingSession(RecordingSession session) {
mRecordingSessions.remove(session);
session.release();
@@ -132,17 +129,13 @@ public class InputSessionManager {
}
}
- /**
- * Adds the {@link OnTvViewChannelChangeListener}.
- */
+ /** Adds the {@link OnTvViewChannelChangeListener}. */
@MainThread
public void addOnTvViewChannelChangeListener(OnTvViewChannelChangeListener listener) {
mOnTvViewChannelChangeListeners.add(listener);
}
- /**
- * Removes the {@link OnTvViewChannelChangeListener}.
- */
+ /** Removes the {@link OnTvViewChannelChangeListener}. */
@MainThread
public void removeOnTvViewChannelChangeListener(OnTvViewChannelChangeListener listener) {
mOnTvViewChannelChangeListeners.remove(listener);
@@ -176,9 +169,7 @@ public class InputSessionManager {
return null;
}
- /**
- * Retruns the earliest end time of recording sessions in progress of the certain TV input.
- */
+ /** Retruns the earliest end time of recording sessions in progress of the certain TV input. */
@MainThread
public Long getEarliestRecordingSessionEndTimeMs(String inputId) {
long timeMs = Long.MAX_VALUE;
@@ -240,8 +231,8 @@ public class InputSessionManager {
/**
* The session for {@link TvView}.
- * <p>
- * The methods which create or release session for the TV input should be called through this
+ *
+ * <p>The methods which create or release session for the TV input should be called through this
* session.
*/
@MainThread
@@ -261,31 +252,32 @@ public class InputSessionManager {
mTvView = tvView;
mTunableTvView = tunableTvView;
mCallback = callback;
- mTvView.setCallback(new DelegateTvInputCallback(mCallback) {
- @Override
- public void onConnectionFailed(String inputId) {
- if (DEBUG) Log.d(TAG, "TvViewSession: connection failed");
- mTuned = false;
- mNeedToBeRetuned = false;
- super.onConnectionFailed(inputId);
- notifyTvViewChannelChange(null);
- }
+ mTvView.setCallback(
+ new DelegateTvInputCallback(mCallback) {
+ @Override
+ public void onConnectionFailed(String inputId) {
+ if (DEBUG) Log.d(TAG, "TvViewSession: connection failed");
+ mTuned = false;
+ mNeedToBeRetuned = false;
+ super.onConnectionFailed(inputId);
+ notifyTvViewChannelChange(null);
+ }
- @Override
- public void onDisconnected(String inputId) {
- if (DEBUG) Log.d(TAG, "TvViewSession: disconnected");
- mTuned = false;
- mNeedToBeRetuned = false;
- super.onDisconnected(inputId);
- notifyTvViewChannelChange(null);
- }
- });
+ @Override
+ public void onDisconnected(String inputId) {
+ if (DEBUG) Log.d(TAG, "TvViewSession: disconnected");
+ mTuned = false;
+ mNeedToBeRetuned = false;
+ super.onDisconnected(inputId);
+ notifyTvViewChannelChange(null);
+ }
+ });
}
/**
* Tunes to the channel.
- * <p>
- * As this is called only for the warming up, there's no need to be retuned.
+ *
+ * <p>As this is called only for the warming up, there's no need to be retuned.
*/
public void tune(String inputId, Uri channelUri) {
if (DEBUG) {
@@ -299,13 +291,22 @@ public class InputSessionManager {
notifyTvViewChannelChange(channelUri);
}
- /**
- * Tunes to the channel.
- */
+ /** Tunes to the channel. */
public void tune(Channel channel, Bundle params, OnTuneListener listener) {
if (DEBUG) {
- Log.d(TAG, "tune: {session=" + this + ", channel=" + channel + ", params=" + params
- + ", listener=" + listener + ", mTuned=" + mTuned + "}");
+ Log.d(
+ TAG,
+ "tune: {session="
+ + this
+ + ", channel="
+ + channel
+ + ", params="
+ + params
+ + ", listener="
+ + listener
+ + ", mTuned="
+ + mTuned
+ + "}");
}
mChannel = channel;
mInputId = channel.getInputId();
@@ -313,8 +314,10 @@ public class InputSessionManager {
mParams = params;
mOnTuneListener = listener;
TvInputInfo input = mInputManager.getTvInputInfo(mInputId);
- if (input == null || (input.canRecord() && !isTunedForRecording(mChannelUri)
- && getTunedRecordingSessionCount(mInputId) >= input.getTunerCount())) {
+ if (input == null
+ || (input.canRecord()
+ && !isTunedForRecording(mChannelUri)
+ && getTunedRecordingSessionCount(mInputId) >= input.getTunerCount())) {
if (DEBUG) {
if (input == null) {
Log.d(TAG, "Can't find input for input ID: " + mInputId);
@@ -354,9 +357,7 @@ public class InputSessionManager {
notifyTvViewChannelChange(null);
}
- /**
- * Resets this TvView.
- */
+ /** Resets this TvView. */
public void reset() {
if (DEBUG) Log.d(TAG, "Reset TvView session");
mTuned = false;
@@ -366,8 +367,8 @@ public class InputSessionManager {
}
void resetByRecording() {
- mCallback.onVideoUnavailable(mInputId,
- TunableTvView.VIDEO_UNAVAILABLE_REASON_NO_RESOURCE);
+ mCallback.onVideoUnavailable(
+ mInputId, TunableTvView.VIDEO_UNAVAILABLE_REASON_NO_RESOURCE);
if (mTuned) {
if (DEBUG) Log.d(TAG, "Reset TvView session by recording");
mTunableTvView.resetByRecording();
@@ -379,8 +380,8 @@ public class InputSessionManager {
/**
* The session for recording.
- * <p>
- * The caller is responsible for releasing the session when the error occurs.
+ *
+ * <p>The caller is responsible for releasing the session when the error occurs.
*/
public class RecordingSession {
private final String mInputId;
@@ -391,8 +392,12 @@ public class InputSessionManager {
private TvRecordingClient mClient;
private boolean mTuned;
- RecordingSession(String inputId, String tag, RecordingCallback callback,
- Handler handler, long endTimeMs) {
+ RecordingSession(
+ String inputId,
+ String tag,
+ RecordingCallback callback,
+ Handler handler,
+ long endTimeMs) {
mInputId = inputId;
mCallback = callback;
mHandler = handler;
@@ -402,83 +407,90 @@ public class InputSessionManager {
void release() {
if (DEBUG) Log.d(TAG, "Release of recording session requested.");
- runOnHandler(mMainThreadHandler, new Runnable() {
- @Override
- public void run() {
- if (DEBUG) Log.d(TAG, "Releasing of recording session.");
- mTuned = false;
- mClient.release();
- mClient = null;
- for (TvViewSession session : mTvViewSessions) {
- if (DEBUG) {
- Log.d(TAG, "Finding TvView sessions for retune: {tuned="
- + session.mTuned + ", inputId=" + session.mInputId
- + ", session=" + session + "}");
- }
- if (!session.mTuned && Objects.equals(session.mInputId, mInputId)) {
- session.retune();
- break;
+ runOnHandler(
+ mMainThreadHandler,
+ new Runnable() {
+ @Override
+ public void run() {
+ if (DEBUG) Log.d(TAG, "Releasing of recording session.");
+ mTuned = false;
+ mClient.release();
+ mClient = null;
+ for (TvViewSession session : mTvViewSessions) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "Finding TvView sessions for retune: {tuned="
+ + session.mTuned
+ + ", inputId="
+ + session.mInputId
+ + ", session="
+ + session
+ + "}");
+ }
+ if (!session.mTuned && Objects.equals(session.mInputId, mInputId)) {
+ session.retune();
+ break;
+ }
+ }
}
- }
- }
- });
+ });
}
- /**
- * Tunes to the channel for recording.
- */
+ /** Tunes to the channel for recording. */
public void tune(String inputId, Uri channelUri) {
- runOnHandler(mMainThreadHandler, new Runnable() {
- @Override
- public void run() {
- int tunedRecordingSessionCount = getTunedRecordingSessionCount(inputId);
- TvInputInfo input = mInputManager.getTvInputInfo(inputId);
- if (input == null || !input.canRecord()
- || input.getTunerCount() <= tunedRecordingSessionCount) {
- runOnHandler(mHandler, new Runnable() {
- @Override
- public void run() {
- mCallback.onConnectionFailed(inputId);
+ runOnHandler(
+ mMainThreadHandler,
+ new Runnable() {
+ @Override
+ public void run() {
+ int tunedRecordingSessionCount = getTunedRecordingSessionCount(inputId);
+ TvInputInfo input = mInputManager.getTvInputInfo(inputId);
+ if (input == null
+ || !input.canRecord()
+ || input.getTunerCount() <= tunedRecordingSessionCount) {
+ runOnHandler(
+ mHandler,
+ new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onConnectionFailed(inputId);
+ }
+ });
+ return;
}
- });
- return;
- }
- mTuned = true;
- int tunedTuneSessionCount = getTunedTvViewSessionCount(inputId);
- if (!isTunedForTvView(channelUri) && tunedTuneSessionCount > 0
- && tunedRecordingSessionCount + tunedTuneSessionCount
- >= input.getTunerCount()) {
- for (TvViewSession session : mTvViewSessions) {
- if (session.mTuned && Objects.equals(session.mInputId, inputId)
- && !isTunedForRecording(session.mChannelUri)) {
- session.resetByRecording();
- break;
+ mTuned = true;
+ int tunedTuneSessionCount = getTunedTvViewSessionCount(inputId);
+ if (!isTunedForTvView(channelUri)
+ && tunedTuneSessionCount > 0
+ && tunedRecordingSessionCount + tunedTuneSessionCount
+ >= input.getTunerCount()) {
+ for (TvViewSession session : mTvViewSessions) {
+ if (session.mTuned
+ && Objects.equals(session.mInputId, inputId)
+ && !isTunedForRecording(session.mChannelUri)) {
+ session.resetByRecording();
+ break;
+ }
+ }
}
+ mChannelUri = channelUri;
+ mClient.tune(inputId, channelUri);
}
- }
- mChannelUri = channelUri;
- mClient.tune(inputId, channelUri);
- }
- });
+ });
}
- /**
- * Starts recording.
- */
+ /** Starts recording. */
public void startRecording(Uri programHintUri) {
mClient.startRecording(programHintUri);
}
- /**
- * Stops recording.
- */
+ /** Stops recording. */
public void stopRecording() {
mClient.stopRecording();
}
- /**
- * Sets recording session's ending time.
- */
+ /** Sets recording session's ending time. */
public void setEndTimeMs(long endTimeMs) {
mEndTimeMs = endTimeMs;
}
@@ -555,9 +567,7 @@ public class InputSessionManager {
}
}
- /**
- * Called when the {@link TvView} channel is changed.
- */
+ /** Called when the {@link TvView} channel is changed. */
public interface OnTvViewChannelChangeListener {
void onTvViewChannelChange(@Nullable Uri channelUri);
}
diff --git a/src/com/android/tv/LauncherActivity.java b/src/com/android/tv/LauncherActivity.java
index e03952da..679d612d 100644
--- a/src/com/android/tv/LauncherActivity.java
+++ b/src/com/android/tv/LauncherActivity.java
@@ -26,8 +26,8 @@ import android.util.Log;
/**
* An activity to launch a new activity.
*
- * <p>In the case when {@link MainActivity} starts a new activity using
- * {@link Activity#startActivity} or {@link Activity#startActivityForResult}, Live TV app is
+ * <p>In the case when {@link MainActivity} starts a new activity using {@link
+ * Activity#startActivity} or {@link Activity#startActivityForResult}, Live TV app is
* terminated if the new activity crashes. That's because the {@link android.app.ActivityManager}
* terminates the activity which is just below the crashed activity in the activity stack. To avoid
* this, we need to locate an additional activity between these activities in the activity stack.
@@ -35,8 +35,7 @@ import android.util.Log;
public class LauncherActivity extends Activity {
private static final String TAG = "LauncherActivity";
- public static final String ERROR_MESSAGE
- = "com.android.tv.LauncherActivity.ErrorMessage";
+ public static final String ERROR_MESSAGE = "com.android.tv.LauncherActivity.ErrorMessage";
private static final int REQUEST_CODE_DEFAULT = 0;
private static final int REQUEST_START_ACTIVITY = 100;
@@ -45,34 +44,16 @@ public class LauncherActivity extends Activity {
private static final String EXTRA_REQUEST_RESULT =
"com.android.tv.LauncherActivity.REQUEST_RESULT";
- /**
- * Starts an activity by calling {@link Activity#startActivity}.
- */
+ /** Starts an activity by calling {@link Activity#startActivity}. */
public static void startActivitySafe(Activity baseActivity, Intent intentToLaunch) {
// To avoid the app termination when the new activity crashes, LauncherActivity should be
// started by calling startActivityForResult().
- baseActivity.startActivityForResult(createIntent(baseActivity, intentToLaunch, false),
- REQUEST_CODE_DEFAULT);
+ baseActivity.startActivityForResult(
+ createIntent(baseActivity, intentToLaunch, false), REQUEST_CODE_DEFAULT);
}
- /**
- * Starts an activity by calling {@link Activity#startActivityForResult}.
- *
- * <p>Note: {@code requestCode} should not be 0. The value is reserved for internal use.
- */
- public static void startActivityForResultSafe(Activity baseActivity, Intent intentToLaunch,
- int requestCode) {
- if (requestCode == REQUEST_CODE_DEFAULT) {
- throw new IllegalArgumentException("requestCode should not be 0.");
- }
- // To avoid the app termination when the new activity crashes, LauncherActivity should be
- // started by calling startActivityForResult().
- baseActivity.startActivityForResult(createIntent(baseActivity, intentToLaunch, true),
- requestCode);
- }
-
- private static Intent createIntent(Context context, Intent intentToLaunch,
- boolean requestResult) {
+ private static Intent createIntent(
+ Context context, Intent intentToLaunch, boolean requestResult) {
Intent intent = new Intent(context, LauncherActivity.class);
intent.putExtra(EXTRA_INTENT, intentToLaunch);
if (requestResult) {
@@ -98,8 +79,7 @@ public class LauncherActivity extends Activity {
}
} catch (ActivityNotFoundException e) {
Log.w(TAG, "Activity not found for " + intent);
- intent.putExtra(ERROR_MESSAGE,
- getResources().getString(R.string.msg_missing_app));
+ intent.putExtra(ERROR_MESSAGE, getResources().getString(R.string.msg_missing_app));
setResult(Activity.RESULT_CANCELED, intent);
finish();
}
diff --git a/src/com/android/tv/MainActivity.java b/src/com/android/tv/MainActivity.java
index ed5f79a1..94a86cce 100644
--- a/src/com/android/tv/MainActivity.java
+++ b/src/com/android/tv/MainActivity.java
@@ -17,9 +17,12 @@
package com.android.tv;
import android.app.Activity;
+import android.app.PendingIntent;
+import android.app.SearchManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
@@ -62,26 +65,31 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
import android.widget.Toast;
-
import com.android.tv.analytics.SendChannelStatusRunnable;
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.CommonPreferences;
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.memory.MemoryManageable;
import com.android.tv.common.ui.setup.OnActionClickListener;
-import com.android.tv.data.Channel;
+import com.android.tv.common.util.CommonUtils;
+import com.android.tv.common.util.ContentUriUtils;
+import com.android.tv.common.util.Debug;
+import com.android.tv.common.util.DurationTimer;
+import com.android.tv.common.util.PermissionUtils;
+import com.android.tv.common.util.SystemProperties;
import com.android.tv.data.ChannelDataManager;
+import com.android.tv.data.ChannelImpl;
import com.android.tv.data.OnCurrentProgramUpdatedListener;
import com.android.tv.data.Program;
import com.android.tv.data.ProgramDataManager;
import com.android.tv.data.StreamInfo;
import com.android.tv.data.WatchedHistoryManager;
-import com.android.tv.data.epg.EpgFetcher;
+import com.android.tv.data.api.Channel;
import com.android.tv.dialog.HalfSizedDialogFragment;
import com.android.tv.dialog.PinDialogFragment;
import com.android.tv.dialog.PinDialogFragment.OnPinCheckedListener;
@@ -97,15 +105,10 @@ import com.android.tv.parental.ContentRatingsManager;
import com.android.tv.parental.ParentalControlSettings;
import com.android.tv.perf.EventNames;
import com.android.tv.perf.PerformanceMonitor;
-import com.android.tv.perf.StubPerformanceMonitor;
import com.android.tv.perf.TimerEvent;
import com.android.tv.recommendation.ChannelPreviewUpdater;
import com.android.tv.recommendation.NotificationService;
import com.android.tv.search.ProgramGuideSearchFragment;
-import com.android.tv.tuner.TunerInputController;
-import com.android.tv.tuner.TunerPreferences;
-import com.android.tv.tuner.setup.TunerSetupActivity;
-import com.android.tv.tuner.tvinput.TunerTvInputService;
import com.android.tv.ui.ChannelBannerView;
import com.android.tv.ui.InputBannerView;
import com.android.tv.ui.KeypadChannelSwitchView;
@@ -124,21 +127,18 @@ import com.android.tv.ui.sidepanel.MultiAudioFragment;
import com.android.tv.ui.sidepanel.SettingsFragment;
import com.android.tv.ui.sidepanel.SideFragment;
import com.android.tv.ui.sidepanel.parentalcontrols.ParentalControlsFragment;
-import com.android.tv.util.AccountHelper;
+import com.android.tv.util.AsyncDbTask;
import com.android.tv.util.CaptionSettings;
-import com.android.tv.util.Debug;
-import com.android.tv.util.DurationTimer;
-import com.android.tv.util.ImageCache;
import com.android.tv.util.OnboardingUtils;
-import com.android.tv.util.PermissionUtils;
import com.android.tv.util.RecurringRunner;
import com.android.tv.util.SetupUtils;
-import com.android.tv.util.SystemProperties;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.TvSettings;
import com.android.tv.util.TvTrackInfoUtils;
import com.android.tv.util.Utils;
import com.android.tv.util.ViewCache;
+import com.android.tv.util.account.AccountHelper;
+import com.android.tv.util.images.ImageCache;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -148,19 +148,23 @@ import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
-/**
- * The main activity for the Live TV app.
- */
+/** The main activity for the Live TV app. */
public class MainActivity extends Activity implements OnActionClickListener, OnPinCheckedListener {
private static final String TAG = "MainActivity";
private static final boolean DEBUG = false;
@Retention(RetentionPolicy.SOURCE)
- @IntDef({KEY_EVENT_HANDLER_RESULT_PASSTHROUGH, KEY_EVENT_HANDLER_RESULT_NOT_HANDLED,
- KEY_EVENT_HANDLER_RESULT_HANDLED, KEY_EVENT_HANDLER_RESULT_DISPATCH_TO_OVERLAY})
+ @IntDef({
+ KEY_EVENT_HANDLER_RESULT_PASSTHROUGH,
+ KEY_EVENT_HANDLER_RESULT_NOT_HANDLED,
+ KEY_EVENT_HANDLER_RESULT_HANDLED,
+ KEY_EVENT_HANDLER_RESULT_DISPATCH_TO_OVERLAY
+ })
public @interface KeyHandlerResultType {}
+
public static final int KEY_EVENT_HANDLER_RESULT_PASSTHROUGH = 0;
public static final int KEY_EVENT_HANDLER_RESULT_NOT_HANDLED = 1;
public static final int KEY_EVENT_HANDLER_RESULT_HANDLED = 2;
@@ -171,12 +175,12 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
private static final float FRAME_RATE_FOR_FILM = 23.976f;
private static final float FRAME_RATE_EPSILON = 0.1f;
-
private static final int PERMISSIONS_REQUEST_READ_TV_LISTINGS = 1;
private static final String PERMISSION_READ_TV_LISTINGS = "android.permission.READ_TV_LISTINGS";
// Tracker screen names.
public static final String SCREEN_NAME = "Main";
+ private static final String SCREEN_PIP = "PIP";
private static final String SCREEN_BEHIND_NAME = "Behind";
private static final float REFRESH_RATE_EPSILON = 0.01f;
@@ -196,8 +200,8 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
BLACKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_WINDOW);
}
-
private static final IntentFilter SYSTEM_INTENT_FILTER = new IntentFilter();
+
static {
SYSTEM_INTENT_FILTER.addAction(TvInputManager.ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED);
SYSTEM_INTENT_FILTER.addAction(Intent.ACTION_SCREEN_OFF);
@@ -206,6 +210,7 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
}
private static final int REQUEST_CODE_START_SETUP_ACTIVITY = 1;
+ private static final int REQUEST_CODE_NOW_PLAYING = 2;
private static final String KEY_INIT_CHANNEL_ID = "com.android.tv.init_channel_id";
@@ -240,14 +245,13 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
private final DurationTimer mTuneDurationTimer = new DurationTimer();
private DvrManager mDvrManager;
private ConflictChecker mDvrConflictChecker;
+ private SetupUtils mSetupUtils;
private View mContentView;
private TunableTvView mTvView;
private Bundle mTuneParams;
- @Nullable
- private Uri mInitChannelUri;
- @Nullable
- private String mParentInputIdWhenScreenOff;
+ @Nullable private Uri mInitChannelUri;
+ @Nullable private String mParentInputIdWhenScreenOff;
private boolean mScreenOffIntentReceived;
private boolean mShowProgramGuide;
private boolean mShowSelectInputView;
@@ -274,6 +278,7 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
private boolean mOtherActivityLaunched;
private PerformanceMonitor mPerformanceMonitor;
+ private boolean mIsInPIPMode;
private boolean mIsFilmModeSet;
private float mDefaultRefreshRate;
@@ -302,69 +307,74 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
private final Handler mHandler = new MainActivityHandler(this);
private final Set<OnActionClickListener> mOnActionClickListeners = new ArraySet<>();
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- switch (intent.getAction()) {
- case Intent.ACTION_SCREEN_OFF:
- if (DEBUG) Log.d(TAG, "Received ACTION_SCREEN_OFF");
- // We need to stop TvView, when the screen is turned off. If not and TIS uses
- // MediaPlayer, a device may not go to the sleep mode and audio can be heard,
- // because MediaPlayer keeps playing media by its wake lock.
- mScreenOffIntentReceived = true;
- markCurrentChannelDuringScreenOff();
- stopAll(true);
- break;
- case Intent.ACTION_SCREEN_ON:
- if (DEBUG) Log.d(TAG, "Received ACTION_SCREEN_ON");
- if (!mActivityResumed && mVisibleBehind) {
- // ACTION_SCREEN_ON is usually called after onResume. But, if media is
- // played under launcher with requestVisibleBehind(true), onResume will
- // not be called. In this case, we need to resume TvView explicitly.
- resumeTvIfNeeded();
- }
- break;
- case TvInputManager.ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED:
- if (DEBUG) Log.d(TAG, "Received parental control settings change");
- applyParentalControlSettings();
- checkChannelLockNeeded(mTvView, null);
- break;
- case Intent.ACTION_TIME_CHANGED:
- // Re-tune the current channel to prevent incorrect behavior of trick-play.
- // See: b/37393628
- if (mChannelTuner.getCurrentChannel() != null) {
- tune(true);
+ private final BroadcastReceiver mBroadcastReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case Intent.ACTION_SCREEN_OFF:
+ if (DEBUG) Log.d(TAG, "Received ACTION_SCREEN_OFF");
+ // We need to stop TvView, when the screen is turned off. If not and TIS
+ // uses MediaPlayer, a device may not go to the sleep mode and audio
+ // can be heard, because MediaPlayer keeps playing media by its wake
+ // lock.
+ mScreenOffIntentReceived = true;
+ markCurrentChannelDuringScreenOff();
+ stopAll(true);
+ break;
+ case Intent.ACTION_SCREEN_ON:
+ if (DEBUG) Log.d(TAG, "Received ACTION_SCREEN_ON");
+ if (!mActivityResumed && mVisibleBehind) {
+ // ACTION_SCREEN_ON is usually called after onResume. But, if media
+ // is played under launcher with requestVisibleBehind(true),
+ // onResume will not be called. In this case, we need to resume
+ // TvView explicitly.
+ resumeTvIfNeeded();
+ }
+ break;
+ case TvInputManager.ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED:
+ if (DEBUG) Log.d(TAG, "Received parental control settings change");
+ applyParentalControlSettings();
+ checkChannelLockNeeded(mTvView, null);
+ break;
+ case Intent.ACTION_TIME_CHANGED:
+ // Re-tune the current channel to prevent incorrect behavior of
+ // trick-play.
+ // See: b/37393628
+ if (mChannelTuner.getCurrentChannel() != null) {
+ tune(true);
+ }
+ break;
+ default: // fall out
}
- break;
- }
- }
- };
+ }
+ };
private final OnCurrentProgramUpdatedListener mOnCurrentProgramUpdatedListener =
new OnCurrentProgramUpdatedListener() {
- @Override
- public void onCurrentProgramUpdated(long channelId, Program program) {
- // Do not update channel banner by this notification
- // when the time shifting is available.
- if (mTimeShiftManager.isAvailable()) {
- return;
- }
- Channel channel = mTvView.getCurrentChannel();
- if (channel != null && channel.getId() == channelId) {
- mOverlayManager.updateChannelBannerAndShowIfNeeded(
- TvOverlayManager.UPDATE_CHANNEL_BANNER_REASON_UPDATE_INFO);
- mMediaSessionWrapper.update(mTvView.isBlocked(), channel, program);
- }
- }
- };
+ @Override
+ public void onCurrentProgramUpdated(long channelId, Program program) {
+ // Do not update channel banner by this notification
+ // when the time shifting is available.
+ if (mTimeShiftManager.isAvailable()) {
+ return;
+ }
+ Channel channel = mTvView.getCurrentChannel();
+ if (channel != null && channel.getId() == channelId) {
+ mOverlayManager.updateChannelBannerAndShowIfNeeded(
+ TvOverlayManager.UPDATE_CHANNEL_BANNER_REASON_UPDATE_INFO);
+ mMediaSessionWrapper.update(mTvView.isBlocked(), channel, program);
+ }
+ }
+ };
private final ChannelTuner.Listener mChannelTunerListener =
new ChannelTuner.Listener() {
@Override
public void onLoadFinished() {
- Debug.getTimer(Debug.TAG_START_UP_TIMER).log(
- "MainActivity.mChannelTunerListener.onLoadFinished");
- SetupUtils.getInstance(MainActivity.this).markNewChannelsBrowsable();
+ Debug.getTimer(Debug.TAG_START_UP_TIMER)
+ .log("MainActivity.mChannelTunerListener.onLoadFinished");
+ mSetupUtils.markNewChannelsBrowsable();
if (mActivityResumed) {
resumeTvIfNeeded();
}
@@ -389,30 +399,35 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
public void onChannelChanged(Channel previousChannel, Channel currentChannel) {}
};
- private final Runnable mRestoreMainViewRunnable = new Runnable() {
- @Override
- public void run() {
- restoreMainTvView();
- }
- };
+ private final Runnable mRestoreMainViewRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ restoreMainTvView();
+ }
+ };
private ProgramGuideSearchFragment mSearchFragment;
- private final TvInputCallback mTvInputCallback = new TvInputCallback() {
- @Override
- public void onInputAdded(String inputId) {
- if (Features.TUNER.isEnabled(MainActivity.this) && mTunerInputId.equals(inputId)
- && TunerPreferences.shouldShowSetupActivity(MainActivity.this)) {
- Intent intent = TunerSetupActivity.createSetupActivity(MainActivity.this);
- startActivity(intent);
- TunerPreferences.setShouldShowSetupActivity(MainActivity.this, false);
- SetupUtils.getInstance(MainActivity.this).markAsKnownInput(mTunerInputId);
- }
- }
- };
+ private final TvInputCallback mTvInputCallback =
+ new TvInputCallback() {
+ @Override
+ public void onInputAdded(String inputId) {
+ if (TvFeatures.TUNER.isEnabled(MainActivity.this)
+ && mTunerInputId.equals(inputId)
+ && CommonPreferences.shouldShowSetupActivity(MainActivity.this)) {
+ Intent intent =
+ TvSingletons.getSingletons(MainActivity.this)
+ .getTunerSetupIntent(MainActivity.this);
+ startActivity(intent);
+ CommonPreferences.setShouldShowSetupActivity(MainActivity.this, false);
+ mSetupUtils.markAsKnownInput(mTunerInputId);
+ }
+ }
+ };
private void applyParentalControlSettings() {
- boolean parentalControlEnabled = mTvInputManagerHelper.getParentalControlSettings()
- .isParentalControlsEnabled();
+ boolean parentalControlEnabled =
+ mTvInputManagerHelper.getParentalControlSettings().isParentalControlsEnabled();
mTvView.onParentalControlChanged(parentalControlEnabled);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ChannelPreviewUpdater.getInstance(this).updatePreviewDataForChannelsImmediately();
@@ -421,7 +436,11 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
@Override
protected void onCreate(Bundle savedInstanceState) {
- TimerEvent timer = StubPerformanceMonitor.startBootstrapTimer();
+ mAccessibilityManager =
+ (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE);
+ TvSingletons tvSingletons = TvSingletons.getSingletons(this);
+ mPerformanceMonitor = tvSingletons.getPerformanceMonitor();
+ TimerEvent timer = mPerformanceMonitor.startTimer();
DurationTimer startUpDebugTimer = Debug.getTimer(Debug.TAG_START_UP_TIMER);
if (!startUpDebugTimer.isStarted()
|| startUpDebugTimer.getDuration() > START_UP_TIMER_RESET_THRESHOLD_MS) {
@@ -430,30 +449,33 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
startUpDebugTimer.start();
}
startUpDebugTimer.log("MainActivity.onCreate");
- if (DEBUG) Log.d(TAG,"onCreate()");
- TvApplication.setCurrentRunningProcess(this, true);
+ if (DEBUG) {
+ Log.d(TAG, "onCreate()");
+ }
+ Starter.start(this);
super.onCreate(savedInstanceState);
- ApplicationSingletons applicationSingletons = TvApplication.getSingletons(this);
- if (!applicationSingletons.getTvInputManagerHelper().hasTvInputManager()) {
+ if (!tvSingletons.getTvInputManagerHelper().hasTvInputManager()) {
Log.wtf(TAG, "Stopping because device does not have a TvInputManager");
finishAndRemoveTask();
return;
}
- mPerformanceMonitor = applicationSingletons.getPerformanceMonitor();
+ mPerformanceMonitor = tvSingletons.getPerformanceMonitor();
+ mSetupUtils = tvSingletons.getSetupUtils();
TvApplication tvApplication = (TvApplication) getApplication();
mChannelDataManager = tvApplication.getChannelDataManager();
// In API 23, TvContract.isChannelUriForPassthroughInput is hidden.
boolean isPassthroughInput =
TvContract.isChannelUriForPassthroughInput(getIntent().getData());
- boolean tuneToPassthroughInput = Intent.ACTION_VIEW.equals(getIntent().getAction())
- && isPassthroughInput;
- boolean channelLoadedAndNoChannelAvailable = mChannelDataManager.isDbLoadFinished()
- && mChannelDataManager.getChannelCount() <= 0;
+ boolean tuneToPassthroughInput =
+ Intent.ACTION_VIEW.equals(getIntent().getAction()) && isPassthroughInput;
+ boolean channelLoadedAndNoChannelAvailable =
+ mChannelDataManager.isDbLoadFinished()
+ && mChannelDataManager.getChannelCount() <= 0;
if ((OnboardingUtils.isFirstRunWithCurrentVersion(this)
- || channelLoadedAndNoChannelAvailable)
+ || channelLoadedAndNoChannelAvailable)
&& !tuneToPassthroughInput
- && !TvCommonUtils.isRunningInTest()) {
+ && !CommonUtils.isRunningInTest()) {
startOnboardingActivity();
return;
}
@@ -462,31 +484,38 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
mTvInputManagerHelper = tvApplication.getTvInputManagerHelper();
mTvView = (TunableTvView) findViewById(R.id.main_tunable_tv_view);
mTvView.initialize(mProgramDataManager, mTvInputManagerHelper);
- mTvView.setOnUnhandledInputEventListener(new OnUnhandledInputEventListener() {
- @Override
- public boolean onUnhandledInputEvent(InputEvent event) {
- if (isKeyEventBlocked()) {
- return true;
- }
- if (event instanceof KeyEvent) {
- KeyEvent keyEvent = (KeyEvent) event;
- if (keyEvent.getAction() == KeyEvent.ACTION_DOWN && keyEvent.isLongPress()) {
- if (onKeyLongPress(keyEvent.getKeyCode(), keyEvent)) {
+ mTvView.setOnUnhandledInputEventListener(
+ new OnUnhandledInputEventListener() {
+ @Override
+ public boolean onUnhandledInputEvent(InputEvent event) {
+ if (DEBUG) {
+ Log.d(TAG, "onUnhandledInputEvent " + event);
+ }
+ if (isKeyEventBlocked()) {
return true;
}
+ if (event instanceof KeyEvent) {
+ KeyEvent keyEvent = (KeyEvent) event;
+ if (keyEvent.getAction() == KeyEvent.ACTION_DOWN
+ && keyEvent.isLongPress()) {
+ if (onKeyLongPress(keyEvent.getKeyCode(), keyEvent)) {
+ return true;
+ }
+ }
+ if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
+ return onKeyUp(keyEvent.getKeyCode(), keyEvent);
+ } else if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
+ return onKeyDown(keyEvent.getKeyCode(), keyEvent);
+ }
+ }
+ return false;
}
- if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
- return onKeyUp(keyEvent.getKeyCode(), keyEvent);
- } else if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
- return onKeyDown(keyEvent.getKeyCode(), keyEvent);
- }
- }
- return false;
- }
- });
+ });
+ mTvView.setOnTalkBackDpadKeyListener(keycode -> handleUpDownKeys(keycode, null));
long channelId = Utils.getLastWatchedChannelId(this);
String inputId = Utils.getLastWatchedTunerInputId(this);
- if (!isPassthroughInput && inputId != null
+ if (!isPassthroughInput
+ && inputId != null
&& channelId != Channel.INVALID_ID) {
mTvView.warmUpInput(inputId, TvContract.buildChannelUri(channelId));
}
@@ -496,12 +525,12 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
Toast.makeText(this, "Using Strict Mode for eng builds", Toast.LENGTH_SHORT).show();
}
mTracker = tvApplication.getTracker();
- if (Features.TUNER.isEnabled(this)) {
+ if (TvFeatures.TUNER.isEnabled(this)) {
mTvInputManagerHelper.addCallback(mTvInputCallback);
}
- mTunerInputId = TunerTvInputService.getInputId(this);
- mProgramDataManager.addOnCurrentProgramUpdatedListener(Channel.INVALID_ID,
- mOnCurrentProgramUpdatedListener);
+ mTunerInputId = tvSingletons.getEmbeddedTunerInputId();
+ mProgramDataManager.addOnCurrentProgramUpdatedListener(
+ Channel.INVALID_ID, mOnCurrentProgramUpdatedListener);
mProgramDataManager.setPrefetchEnabled(true);
mChannelTuner = new ChannelTuner(mChannelDataManager, mTvInputManagerHelper);
mChannelTuner.addListener(mChannelTunerListener);
@@ -512,91 +541,126 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
if (CommonFeatures.DVR.isEnabled(this)) {
mDvrManager = tvApplication.getDvrManager();
}
- mTimeShiftManager = new TimeShiftManager(this, mTvView, mProgramDataManager, mTracker,
- new OnCurrentProgramUpdatedListener() {
- @Override
- public void onCurrentProgramUpdated(long channelId, Program program) {
- mMediaSessionWrapper.update(mTvView.isBlocked(), getCurrentChannel(),
- program);
- switch (mTimeShiftManager.getLastActionId()) {
- case TimeShiftManager.TIME_SHIFT_ACTION_ID_REWIND:
- case TimeShiftManager.TIME_SHIFT_ACTION_ID_FAST_FORWARD:
- case TimeShiftManager.TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS:
- case TimeShiftManager.TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT:
- mOverlayManager.updateChannelBannerAndShowIfNeeded(
- TvOverlayManager.UPDATE_CHANNEL_BANNER_REASON_FORCE_SHOW);
- break;
- case TimeShiftManager.TIME_SHIFT_ACTION_ID_PAUSE:
- case TimeShiftManager.TIME_SHIFT_ACTION_ID_PLAY:
- default:
- mOverlayManager.updateChannelBannerAndShowIfNeeded(
- TvOverlayManager.UPDATE_CHANNEL_BANNER_REASON_UPDATE_INFO);
- break;
- }
- }
- });
+ mTimeShiftManager =
+ new TimeShiftManager(
+ this,
+ mTvView,
+ mProgramDataManager,
+ mTracker,
+ new OnCurrentProgramUpdatedListener() {
+ @Override
+ public void onCurrentProgramUpdated(long channelId, Program program) {
+ mMediaSessionWrapper.update(
+ mTvView.isBlocked(), getCurrentChannel(), program);
+ switch (mTimeShiftManager.getLastActionId()) {
+ case TimeShiftManager.TIME_SHIFT_ACTION_ID_REWIND:
+ case TimeShiftManager.TIME_SHIFT_ACTION_ID_FAST_FORWARD:
+ case TimeShiftManager.TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS:
+ case TimeShiftManager.TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT:
+ mOverlayManager.updateChannelBannerAndShowIfNeeded(
+ TvOverlayManager
+ .UPDATE_CHANNEL_BANNER_REASON_FORCE_SHOW);
+ break;
+ case TimeShiftManager.TIME_SHIFT_ACTION_ID_PAUSE:
+ case TimeShiftManager.TIME_SHIFT_ACTION_ID_PLAY:
+ default:
+ mOverlayManager.updateChannelBannerAndShowIfNeeded(
+ TvOverlayManager
+ .UPDATE_CHANNEL_BANNER_REASON_UPDATE_INFO);
+ break;
+ }
+ }
+ });
DisplayManager displayManager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE);
Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
mDefaultRefreshRate = display.getRefreshRate();
if (!PermissionUtils.hasAccessWatchedHistory(this)) {
- WatchedHistoryManager watchedHistoryManager = new WatchedHistoryManager(
- getApplicationContext());
+ WatchedHistoryManager watchedHistoryManager =
+ new WatchedHistoryManager(getApplicationContext());
watchedHistoryManager.start();
mTvView.setWatchedHistoryManager(watchedHistoryManager);
}
- mTvViewUiManager = new TvViewUiManager(this, mTvView,
- (FrameLayout) findViewById(android.R.id.content), mTvOptionsManager);
+ mTvViewUiManager =
+ new TvViewUiManager(
+ this,
+ mTvView,
+ (FrameLayout) findViewById(android.R.id.content),
+ mTvOptionsManager);
mContentView = findViewById(android.R.id.content);
ViewGroup sceneContainer = (ViewGroup) findViewById(R.id.scene_container);
- ChannelBannerView channelBannerView = (ChannelBannerView) getLayoutInflater().inflate(
- R.layout.channel_banner, sceneContainer, false);
- KeypadChannelSwitchView keypadChannelSwitchView = (KeypadChannelSwitchView)
- getLayoutInflater().inflate(R.layout.keypad_channel_switch, sceneContainer, false);
- InputBannerView inputBannerView = (InputBannerView) getLayoutInflater()
- .inflate(R.layout.input_banner, sceneContainer, false);
- SelectInputView selectInputView = (SelectInputView) getLayoutInflater()
- .inflate(R.layout.select_input, sceneContainer, false);
- selectInputView.setOnInputSelectedCallback(new OnInputSelectedCallback() {
- @Override
- public void onTunerInputSelected() {
- Channel currentChannel = mChannelTuner.getCurrentChannel();
- if (currentChannel != null && !currentChannel.isPassthrough()) {
- hideOverlays();
- } else {
- tuneToLastWatchedChannelForTunerInput();
- }
- }
+ ChannelBannerView channelBannerView =
+ (ChannelBannerView)
+ getLayoutInflater().inflate(R.layout.channel_banner, sceneContainer, false);
+ KeypadChannelSwitchView keypadChannelSwitchView =
+ (KeypadChannelSwitchView)
+ getLayoutInflater()
+ .inflate(R.layout.keypad_channel_switch, sceneContainer, false);
+ InputBannerView inputBannerView =
+ (InputBannerView)
+ getLayoutInflater().inflate(R.layout.input_banner, sceneContainer, false);
+ SelectInputView selectInputView =
+ (SelectInputView)
+ getLayoutInflater().inflate(R.layout.select_input, sceneContainer, false);
+ selectInputView.setOnInputSelectedCallback(
+ new OnInputSelectedCallback() {
+ @Override
+ public void onTunerInputSelected() {
+ Channel currentChannel = mChannelTuner.getCurrentChannel();
+ if (currentChannel != null && !currentChannel.isPassthrough()) {
+ hideOverlays();
+ } else {
+ tuneToLastWatchedChannelForTunerInput();
+ }
+ }
- @Override
- public void onPassthroughInputSelected(@NonNull TvInputInfo input) {
- Channel currentChannel = mChannelTuner.getCurrentChannel();
- String currentInputId = currentChannel == null ? null : currentChannel.getInputId();
- if (TextUtils.equals(input.getId(), currentInputId)) {
- hideOverlays();
- } else {
- tuneToChannel(Channel.createPassthroughChannel(input.getId()));
- }
- }
+ @Override
+ public void onPassthroughInputSelected(@NonNull TvInputInfo input) {
+ Channel currentChannel = mChannelTuner.getCurrentChannel();
+ String currentInputId =
+ currentChannel == null ? null : currentChannel.getInputId();
+ if (TextUtils.equals(input.getId(), currentInputId)) {
+ hideOverlays();
+ } else {
+ tuneToChannel(ChannelImpl.createPassthroughChannel(input.getId()));
+ }
+ }
- private void hideOverlays() {
- getOverlayManager().hideOverlays(
- TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_DIALOG
- | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANELS
- | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_PROGRAM_GUIDE
- | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_MENU
- | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_FRAGMENT);
- }
- });
+ private void hideOverlays() {
+ getOverlayManager()
+ .hideOverlays(
+ TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_DIALOG
+ | TvOverlayManager
+ .FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANELS
+ | TvOverlayManager
+ .FLAG_HIDE_OVERLAYS_KEEP_PROGRAM_GUIDE
+ | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_MENU
+ | TvOverlayManager
+ .FLAG_HIDE_OVERLAYS_KEEP_FRAGMENT);
+ }
+ });
mSearchFragment = new ProgramGuideSearchFragment();
- mOverlayManager = new TvOverlayManager(this, mChannelTuner, mTvView, mTvOptionsManager,
- keypadChannelSwitchView, channelBannerView, inputBannerView,
- selectInputView, sceneContainer, mSearchFragment);
+ mOverlayManager =
+ new TvOverlayManager(
+ this,
+ mChannelTuner,
+ mTvView,
+ mTvOptionsManager,
+ keypadChannelSwitchView,
+ channelBannerView,
+ inputBannerView,
+ selectInputView,
+ sceneContainer,
+ mSearchFragment);
+ mAccessibilityManager.addAccessibilityStateChangeListener(mOverlayManager);
mAudioManagerHelper = new AudioManagerHelper(this, mTvView);
- mMediaSessionWrapper = new MediaSessionWrapper(this);
+ Intent nowPlayingIntent = new Intent(this, MainActivity.class);
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(this, REQUEST_CODE_NOW_PLAYING, nowPlayingIntent, 0);
+ mMediaSessionWrapper = new MediaSessionWrapper(this, pendingIntent);
mTvViewUiManager.restoreDisplayMode(false);
if (!handleIntent(getIntent())) {
@@ -604,18 +668,21 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
return;
}
- mAccessibilityManager =
- (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE);
- mSendConfigInfoRecurringRunner = new RecurringRunner(this, TimeUnit.DAYS.toMillis(1),
- new SendConfigInfoRunnable(mTracker, mTvInputManagerHelper), null);
+ mSendConfigInfoRecurringRunner =
+ new RecurringRunner(
+ this,
+ TimeUnit.DAYS.toMillis(1),
+ new SendConfigInfoRunnable(mTracker, mTvInputManagerHelper),
+ null);
mSendConfigInfoRecurringRunner.start();
- mChannelStatusRecurringRunner = SendChannelStatusRunnable
- .startChannelStatusRecurringRunner(this, mTracker, mChannelDataManager);
+ mChannelStatusRecurringRunner =
+ SendChannelStatusRunnable.startChannelStatusRecurringRunner(
+ this, mTracker, mChannelDataManager);
// To avoid not updating Rating systems when changing language.
mTvInputManagerHelper.getContentRatingsManager().update();
if (CommonFeatures.DVR.isEnabled(this)
- && Features.SHOW_UPCOMING_CONFLICT_DIALOG.isEnabled(this)) {
+ && TvFeatures.SHOW_UPCOMING_CONFLICT_DIALOG.isEnabled(this)) {
mDvrConflictChecker = new ConflictChecker(this);
}
initForTest();
@@ -632,13 +699,14 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
float density = getResources().getDisplayMetrics().density;
- mTvViewUiManager.onConfigurationChanged((int) (newConfig.screenWidthDp * density),
+ mTvViewUiManager.onConfigurationChanged(
+ (int) (newConfig.screenWidthDp * density),
(int) (newConfig.screenHeightDp * density));
}
@Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
- @NonNull int[] grantResults) {
+ public void onRequestPermissionsResult(
+ int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == PERMISSIONS_REQUEST_READ_TV_LISTINGS) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Start reload of dependent data
@@ -650,14 +718,18 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
finish();
startActivity(intent);
} else {
- Toast.makeText(this, R.string.msg_read_tv_listing_permission_denied,
- Toast.LENGTH_LONG).show();
+ Toast.makeText(
+ this,
+ R.string.msg_read_tv_listing_permission_denied,
+ Toast.LENGTH_LONG)
+ .show();
finish();
}
}
}
- @BlockScreenType private int getDesiredBlockScreenType() {
+ @BlockScreenType
+ private int getDesiredBlockScreenType() {
if (!mActivityResumed) {
return TunableTvView.BLOCK_SCREEN_TYPE_NO_UI;
}
@@ -689,7 +761,9 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
@Override
protected void onNewIntent(Intent intent) {
- if (DEBUG) Log.d(TAG,"onNewIntent(): " + intent);
+ if (DEBUG) {
+ Log.d(TAG, "onNewIntent(): " + intent);
+ }
if (mOverlayManager == null) {
// It's called before onCreate. The intent will be handled at onCreate. b/30725058
return;
@@ -705,7 +779,9 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
@Override
protected void onStart() {
TimerEvent timer = mPerformanceMonitor.startTimer();
- if (DEBUG) Log.d(TAG,"onStart()");
+ if (DEBUG) {
+ Log.d(TAG, "onStart()");
+ }
super.onStart();
mScreenOffIntentReceived = false;
mActivityStarted = true;
@@ -720,9 +796,9 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
notificationIntent.setAction(NotificationService.ACTION_SHOW_RECOMMENDATION);
startService(notificationIntent);
}
- TunerInputController.executeNetworkTunerDiscoveryAsyncTask(this);
-
- EpgFetcher.getInstance(this).fetchImmediatelyIfNeeded();
+ TvSingletons singletons = TvSingletons.getSingletons(this);
+ singletons.getTunerInputController().executeNetworkTunerDiscoveryAsyncTask(this);
+ singletons.getEpgFetcher().fetchImmediatelyIfNeeded();
mPerformanceMonitor.stopTimer(timer, EventNames.MAIN_ACTIVITY_ONSTART);
}
@@ -732,10 +808,12 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
Debug.getTimer(Debug.TAG_START_UP_TIMER).log("MainActivity.onResume start");
if (DEBUG) Log.d(TAG, "onResume()");
super.onResume();
+ mIsInPIPMode = false;
if (!PermissionUtils.hasAccessAllEpg(this)
&& checkSelfPermission(PERMISSION_READ_TV_LISTINGS)
- != PackageManager.PERMISSION_GRANTED) {
- requestPermissions(new String[]{PERMISSION_READ_TV_LISTINGS},
+ != PackageManager.PERMISSION_GRANTED) {
+ requestPermissions(
+ new String[] {PERMISSION_READ_TV_LISTINGS},
PERMISSIONS_REQUEST_READ_TV_LISTINGS);
}
mTracker.sendScreenView(SCREEN_NAME);
@@ -755,19 +833,20 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
Set<String> failedScheduledRecordingInfoSet =
Utils.getFailedScheduledRecordingInfoSet(getApplicationContext());
if (Utils.hasRecordingFailedReason(
- getApplicationContext(), TvInputManager.RECORDING_ERROR_INSUFFICIENT_SPACE)
+ getApplicationContext(), TvInputManager.RECORDING_ERROR_INSUFFICIENT_SPACE)
&& !failedScheduledRecordingInfoSet.isEmpty()) {
- runAfterAttachedToWindow(new Runnable() {
- @Override
- public void run() {
- DvrUiHelper.showDvrInsufficientSpaceErrorDialog(MainActivity.this,
- failedScheduledRecordingInfoSet);
- }
- });
+ runAfterAttachedToWindow(
+ new Runnable() {
+ @Override
+ public void run() {
+ DvrUiHelper.showDvrInsufficientSpaceErrorDialog(
+ MainActivity.this, failedScheduledRecordingInfoSet);
+ }
+ });
}
if (mChannelTuner.areAllChannelsLoaded()) {
- SetupUtils.getInstance(this).markNewChannelsBrowsable();
+ mSetupUtils.markNewChannelsBrowsable();
resumeTvIfNeeded();
}
mOverlayManager.showMenuWithTimeShiftPauseIfNeeded();
@@ -779,28 +858,29 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
mInputToSetUp = null;
} else if (mShowProgramGuide) {
mShowProgramGuide = false;
- mHandler.post(new Runnable() {
- // This will delay the start of the animation until after the Live Channel app is
- // shown. Without this the animation is completed before it is actually visible on
- // the screen.
- @Override
- public void run() {
- mOverlayManager.showProgramGuide();
- }
- });
+ // This will delay the start of the animation until after the Live Channel app is
+ // shown. Without this the animation is completed before it is actually visible on
+ // the screen.
+ mHandler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ mOverlayManager.showProgramGuide();
+ }
+ });
} else if (mShowSelectInputView) {
mShowSelectInputView = false;
- mHandler.post(new Runnable() {
- // mShowSelectInputView is true when the activity is started/resumed because the
- // TV_INPUT button was pressed in a different app.
- // This will delay the start of the animation until after the Live Channel app is
- // shown. Without this the animation is completed before it is actually visible on
- // the screen.
- @Override
- public void run() {
- mOverlayManager.showSelectInputView();
- }
- });
+ // mShowSelectInputView is true when the activity is started/resumed because the
+ // TV_INPUT button was pressed in a different app. This will delay the start of
+ // the animation until after the Live Channel app is shown. Without this the
+ // animation is completed before it is actually visible on the screen.
+ mHandler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ mOverlayManager.showSelectInputView();
+ }
+ });
}
if (mDvrConflictChecker != null) {
mDvrConflictChecker.start();
@@ -823,25 +903,26 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
mShowLockedChannelsTemporarily = false;
mShouldTuneToTunerChannel = false;
if (!mVisibleBehind) {
- mAudioManagerHelper.abandonAudioFocus();
- mMediaSessionWrapper.setPlaybackState(false);
- mTracker.sendScreenView("");
+ if (mIsInPIPMode) {
+ mTracker.sendScreenView(SCREEN_PIP);
+ } else {
+ mTracker.sendScreenView("");
+ mAudioManagerHelper.abandonAudioFocus();
+ mMediaSessionWrapper.setPlaybackState(false);
+ }
} else {
mTracker.sendScreenView(SCREEN_BEHIND_NAME);
}
+ TvSingletons.getSingletons(this).getExperimentLoader().asyncRefreshExperiments(this);
super.onPause();
}
- /**
- * Returns true if {@link #onResume} is called and {@link #onPause} is not called yet.
- */
+ /** Returns true if {@link #onResume} is called and {@link #onPause} is not called yet. */
public boolean isActivityResumed() {
return mActivityResumed;
}
- /**
- * Returns true if {@link #onStart} is called and {@link #onStop} is not called yet.
- */
+ /** Returns true if {@link #onStart} is called and {@link #onStop} is not called yet. */
public boolean isActivityStarted() {
return mActivityStarted;
}
@@ -867,12 +948,14 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
mTvView.unblockContent(unblockedRating);
break;
case PinDialogFragment.PIN_DIALOG_TYPE_ENTER_PIN:
- mOverlayManager.getSideFragmentManager()
+ mOverlayManager
+ .getSideFragmentManager()
.show(new ParentalControlsFragment(), false);
- // Pass through.
+ // fall through.
case PinDialogFragment.PIN_DIALOG_TYPE_NEW_PIN:
mOverlayManager.getSideFragmentManager().showSidePanel(true);
break;
+ default: // fall out
}
} else if (type == PinDialogFragment.PIN_DIALOG_TYPE_ENTER_PIN) {
mOverlayManager.getSideFragmentManager().hideAll(false);
@@ -881,7 +964,8 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
private void resumeTvIfNeeded() {
if (DEBUG) Log.d(TAG, "resumeTvIfNeeded()");
- if (!mTvView.isPlaying() || mInitChannelUri != null
+ if (!mTvView.isPlaying()
+ || mInitChannelUri != null
|| (mShouldTuneToTunerChannel && mChannelTuner.isCurrentChannelPassthrough())) {
if (TvContract.isChannelUriForPassthroughInput(mInitChannelUri)) {
// The target input may not be ready yet, especially, just after screen on.
@@ -917,9 +1001,11 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
// is playing, we stop the passthrough TV input.
stopTv();
}
- SoftPreconditions.checkState(TvContract.isChannelUriForPassthroughInput(channelUri)
- || mChannelTuner.areAllChannelsLoaded(),
- TAG, "startTV assumes that ChannelDataManager is already loaded.");
+ SoftPreconditions.checkState(
+ TvContract.isChannelUriForPassthroughInput(channelUri)
+ || mChannelTuner.areAllChannelsLoaded(),
+ TAG,
+ "startTV assumes that ChannelDataManager is already loaded.");
if (mTvView.isPlaying()) {
// TV has already started.
if (channelUri == null || channelUri.equals(mChannelTuner.getCurrentChannelUri())) {
@@ -945,15 +1031,19 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
mChannelTuner.moveToChannel(mChannelTuner.findNearestBrowsableChannel(0));
} else {
if (TvContract.isChannelUriForPassthroughInput(channelUri)) {
- Channel channel = Channel.createPassthroughChannel(channelUri);
+ ChannelImpl channel = ChannelImpl.createPassthroughChannel(channelUri);
mChannelTuner.moveToChannel(channel);
} else {
long channelId = ContentUris.parseId(channelUri);
Channel channel = mChannelDataManager.getChannel(channelId);
if (channel == null || !mChannelTuner.moveToChannel(channel)) {
mChannelTuner.moveToChannel(mChannelTuner.findNearestBrowsableChannel(0));
- Log.w(TAG, "The requested channel (id=" + channelId + ") doesn't exist. "
- + "The first channel will be tuned to.");
+ Log.w(
+ TAG,
+ "The requested channel (id="
+ + channelId
+ + ") doesn't exist. "
+ + "The first channel will be tuned to.");
}
}
}
@@ -985,9 +1075,7 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
super.onStop();
}
- /**
- * Handles screen off to keep the current channel for next screen on.
- */
+ /** Handles screen off to keep the current channel for next screen on. */
private void markCurrentChannelDuringScreenOff() {
mInitChannelUri = mChannelTuner.getCurrentChannelUri();
if (mChannelTuner.isCurrentChannelPassthrough()) {
@@ -1015,7 +1103,7 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
* @param calledByPopup If true, startSetupActivity is invoked from the setup fragment.
*/
public void startSetupActivity(TvInputInfo input, boolean calledByPopup) {
- Intent intent = TvCommonUtils.createSetupIntent(input);
+ Intent intent = CommonUtils.createSetupIntent(input);
if (intent == null) {
Toast.makeText(this, R.string.msg_no_setup_activity, Toast.LENGTH_SHORT).show();
return;
@@ -1038,16 +1126,23 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
startActivityForResult(intent, REQUEST_CODE_START_SETUP_ACTIVITY);
} catch (ActivityNotFoundException e) {
mInputIdUnderSetup = null;
- Toast.makeText(this, getString(R.string.msg_unable_to_start_setup_activity,
- input.loadLabel(this)), Toast.LENGTH_SHORT).show();
+ Toast.makeText(
+ this,
+ getString(
+ R.string.msg_unable_to_start_setup_activity,
+ input.loadLabel(this)),
+ Toast.LENGTH_SHORT)
+ .show();
return;
}
if (calledByPopup) {
- mOverlayManager.hideOverlays(TvOverlayManager.FLAG_HIDE_OVERLAYS_WITHOUT_ANIMATION
- | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_FRAGMENT);
+ mOverlayManager.hideOverlays(
+ TvOverlayManager.FLAG_HIDE_OVERLAYS_WITHOUT_ANIMATION
+ | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_FRAGMENT);
} else {
- mOverlayManager.hideOverlays(TvOverlayManager.FLAG_HIDE_OVERLAYS_WITHOUT_ANIMATION
- | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANEL_HISTORY);
+ mOverlayManager.hideOverlays(
+ TvOverlayManager.FLAG_HIDE_OVERLAYS_WITHOUT_ANIMATION
+ | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANEL_HISTORY);
}
}
@@ -1060,8 +1155,11 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
try {
startActivitySafe(intent);
} catch (ActivityNotFoundException e) {
- Toast.makeText(this, getString(R.string.msg_unable_to_start_system_captioning_settings),
- Toast.LENGTH_SHORT).show();
+ Toast.makeText(
+ this,
+ getString(R.string.msg_unable_to_start_system_captioning_settings),
+ Toast.LENGTH_SHORT)
+ .show();
}
}
@@ -1085,16 +1183,12 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
return mTimeShiftManager;
}
- /**
- * Returns the instance of {@link TvOverlayManager}.
- */
+ /** Returns the instance of {@link TvOverlayManager}. */
public TvOverlayManager getOverlayManager() {
return mOverlayManager;
}
- /**
- * Returns the {@link ConflictChecker}.
- */
+ /** Returns the {@link ConflictChecker}. */
@Nullable
public ConflictChecker getDvrConflictChecker() {
return mDvrConflictChecker;
@@ -1109,9 +1203,10 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
}
/**
- * Returns the current program which the user is watching right now.<p>
+ * Returns the current program which the user is watching right now.
*
- * It might be a live program. If the time shifting is available, it can be a past program, too.
+ * <p>It might be a live program. If the time shifting is available, it can be a past program,
+ * too.
*/
public Program getCurrentProgram() {
if (!isChannelChangeKeyDownReceived() && mTimeShiftManager.isAvailable()) {
@@ -1122,9 +1217,9 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
}
/**
- * Returns the current playing time in milliseconds.<p>
+ * Returns the current playing time in milliseconds.
*
- * If the time shifting is available, the time is the playing position of the program,
+ * <p>If the time shifting is available, the time is the playing position of the program,
* otherwise, the system current time.
*/
public long getCurrentPlayingPosition() {
@@ -1152,18 +1247,7 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
LauncherActivity.startActivitySafe(this, intent);
}
- /**
- * Call {@link Activity#startActivityForResult} in a safe way.
- *
- * @see LauncherActivity
- */
- private void startActivityForResultSafe(Intent intent, int requestCode) {
- LauncherActivity.startActivityForResultSafe(this, intent, requestCode);
- }
-
- /**
- * Show settings fragment.
- */
+ /** Show settings fragment. */
public void showSettingsFragment() {
if (!mChannelTuner.areAllChannelsLoaded()) {
// Show ChannelSourcesFragment only if all the channels are loaded.
@@ -1180,8 +1264,8 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
* It is called when shrunken TvView is desired, such as EditChannelFragment and
* ChannelsLockedFragment.
*/
- public void startShrunkenTvView(boolean showLockedChannelsTemporarily,
- boolean willMainViewBeTunerInput) {
+ public void startShrunkenTvView(
+ boolean showLockedChannelsTemporarily, boolean willMainViewBeTunerInput) {
mChannelBeforeShrunkenTvView = mTvView.getCurrentChannel();
mWasChannelUnblockedBeforeShrunkenByUser = mIsCurrentChannelUnblockedByUser;
mAllowedRatingBeforeShrunken = mLastAllowedRatingForCurrentChannel;
@@ -1214,19 +1298,21 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
// The current channel is mTvView.getCurrentChannel() and need to tune to the returnChannel.
if (!Objects.equals(mTvView.getCurrentChannel(), returnChannel)) {
final Channel channel = returnChannel;
- Runnable tuneAction = new Runnable() {
- @Override
- public void run() {
- tuneToChannel(channel);
- if (mChannelBeforeShrunkenTvView == null
- || !mChannelBeforeShrunkenTvView.equals(channel)) {
- Utils.setLastWatchedChannel(MainActivity.this, channel);
- }
- mIsCompletingShrunkenTvView = false;
- mIsCurrentChannelUnblockedByUser = mWasChannelUnblockedBeforeShrunkenByUser;
- mTvView.setBlockScreenType(getDesiredBlockScreenType());
- }
- };
+ Runnable tuneAction =
+ new Runnable() {
+ @Override
+ public void run() {
+ tuneToChannel(channel);
+ if (mChannelBeforeShrunkenTvView == null
+ || !mChannelBeforeShrunkenTvView.equals(channel)) {
+ Utils.setLastWatchedChannel(MainActivity.this, channel);
+ }
+ mIsCompletingShrunkenTvView = false;
+ mIsCurrentChannelUnblockedByUser =
+ mWasChannelUnblockedBeforeShrunkenByUser;
+ mTvView.setBlockScreenType(getDesiredBlockScreenType());
+ }
+ };
mTvViewUiManager.fadeOutTvView(tuneAction);
// Will automatically fade-in when video becomes available.
} else {
@@ -1247,35 +1333,45 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
*/
public boolean isScreenBlockedByResourceConflictOrParentalControl() {
return mTvView.getVideoUnavailableReason()
- == TunableTvView.VIDEO_UNAVAILABLE_REASON_NO_RESOURCE || mTvView.isBlocked();
+ == TunableTvView.VIDEO_UNAVAILABLE_REASON_NO_RESOURCE
+ || mTvView.isBlocked();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == REQUEST_CODE_START_SETUP_ACTIVITY) {
- if (resultCode == RESULT_OK) {
- int count = mChannelDataManager.getChannelCountForInput(mInputIdUnderSetup);
- String text;
- if (count > 0) {
- text = getResources().getQuantityString(R.plurals.msg_channel_added,
- count, count);
+ switch (requestCode) {
+ case REQUEST_CODE_START_SETUP_ACTIVITY:
+ if (resultCode == RESULT_OK) {
+ int count = mChannelDataManager.getChannelCountForInput(mInputIdUnderSetup);
+ String text;
+ if (count > 0) {
+ text =
+ getResources()
+ .getQuantityString(
+ R.plurals.msg_channel_added, count, count);
+ } else {
+ text = getString(R.string.msg_no_channel_added);
+ }
+ Toast.makeText(MainActivity.this, text, Toast.LENGTH_SHORT).show();
+ mInputIdUnderSetup = null;
+ if (mChannelTuner.getCurrentChannel() == null) {
+ mChannelTuner.moveToAdjacentBrowsableChannel(true);
+ }
+ if (mTunePending) {
+ tune(true);
+ }
} else {
- text = getString(R.string.msg_no_channel_added);
+ mInputIdUnderSetup = null;
}
- Toast.makeText(MainActivity.this, text, Toast.LENGTH_SHORT).show();
- mInputIdUnderSetup = null;
- if (mChannelTuner.getCurrentChannel() == null) {
- mChannelTuner.moveToAdjacentBrowsableChannel(true);
+ if (!mIsSetupActivityCalledByPopup) {
+ mOverlayManager.getSideFragmentManager().showSidePanel(false);
}
- if (mTunePending) {
- tune(true);
- }
- } else {
- mInputIdUnderSetup = null;
- }
- if (!mIsSetupActivityCalledByPopup) {
- mOverlayManager.getSideFragmentManager().showSidePanel(false);
- }
+ break;
+ case REQUEST_CODE_NOW_PLAYING:
+ // nothing needs to be done. onResume will restore everything.
+ break;
+ default:
+ // do nothing
}
if (data != null) {
String errorMessage = data.getStringExtra(LauncherActivity.ERROR_MESSAGE);
@@ -1325,16 +1421,15 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
return dispatchKeyEventToSession(event) || super.dispatchKeyEvent(event);
}
- /**
- * Notifies the key input focus is changed to the TV view.
- */
+ /** Notifies the key input focus is changed to the TV view. */
public void updateKeyInputFocus() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mTvView.setBlockScreenType(getDesiredBlockScreenType());
- }
- });
+ mHandler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ mTvView.setBlockScreenType(getDesiredBlockScreenType());
+ }
+ });
}
// It should be called before onResume.
@@ -1360,12 +1455,13 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
}
if (TvInputManager.ACTION_SETUP_INPUTS.equals(intent.getAction())) {
- runAfterAttachedToWindow(new Runnable() {
- @Override
- public void run() {
- mOverlayManager.showSetupFragment();
- }
- });
+ runAfterAttachedToWindow(
+ new Runnable() {
+ @Override
+ public void run() {
+ mOverlayManager.showSetupFragment();
+ }
+ });
} else if (Intent.ACTION_VIEW.equals(intent.getAction())) {
Uri uri = intent.getData();
if (Utils.isProgramsUri(uri)) {
@@ -1388,18 +1484,34 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
}
if ((!Utils.isChannelUriForOneChannel(mInitChannelUri)
&& !Utils.isChannelUriForInput(mInitChannelUri))) {
- Log.w(TAG, "Malformed channel uri " + mInitChannelUri
- + " tuning to default instead");
+ Log.w(
+ TAG,
+ "Malformed channel uri " + mInitChannelUri + " tuning to default instead");
mInitChannelUri = null;
return true;
}
mTuneParams = intent.getExtras();
+ String programUriString = intent.getStringExtra(SearchManager.EXTRA_DATA_KEY);
+ Uri programUriFromIntent =
+ programUriString == null ? null : Uri.parse(programUriString);
+ long channelIdFromIntent = ContentUriUtils.safeParseId(mInitChannelUri);
+ if (programUriFromIntent != null && channelIdFromIntent != Channel.INVALID_ID) {
+ new AsyncQueryProgramTask(
+ TvSingletons.getSingletons(this).getDbExecutor(),
+ getContentResolver(),
+ programUriFromIntent,
+ Program.PROJECTION,
+ null,
+ null,
+ null,
+ channelIdFromIntent)
+ .executeOnDbThread();
+ }
if (mTuneParams == null) {
mTuneParams = new Bundle();
}
if (Utils.isChannelUriForTunerInput(mInitChannelUri)) {
- long channelId = ContentUris.parseId(mInitChannelUri);
- mTuneParams.putLong(KEY_INIT_CHANNEL_ID, channelId);
+ mTuneParams.putLong(KEY_INIT_CHANNEL_ID, channelIdFromIntent);
} else if (TvContract.isChannelUriForPassthroughInput(mInitChannelUri)) {
// If mInitChannelUri is for a passthrough TV input.
String inputId = mInitChannelUri.getPathSegments().get(1);
@@ -1419,16 +1531,20 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
String inputId = mInitChannelUri.getQueryParameter("input");
long channelId = Utils.getLastWatchedChannelIdForInput(this, inputId);
if (channelId == Channel.INVALID_ID) {
- String[] projection = { Channels._ID };
+ String[] projection = {Channels._ID};
long time = System.currentTimeMillis();
- try (Cursor cursor = getContentResolver().query(uri, projection,
- null, null, null)) {
+ try (Cursor cursor =
+ getContentResolver().query(uri, projection, null, null, null)) {
if (cursor != null && cursor.moveToNext()) {
channelId = cursor.getLong(0);
}
}
- Debug.getTimer(Debug.TAG_START_UP_TIMER).log("MainActivity queries DB for "
- + "last channel check (" + (System.currentTimeMillis() - time) + "ms)");
+ Debug.getTimer(Debug.TAG_START_UP_TIMER)
+ .log(
+ "MainActivity queries DB for "
+ + "last channel check ("
+ + (System.currentTimeMillis() - time)
+ + "ms)");
}
if (channelId == Channel.INVALID_ID) {
// Couldn't find any channel probably because the input hasn't been set up.
@@ -1444,6 +1560,63 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
return true;
}
+ private class AsyncQueryProgramTask extends AsyncDbTask.AsyncQueryTask<Program> {
+ private final long mChannelIdFromIntent;
+
+ public AsyncQueryProgramTask(
+ Executor executor,
+ ContentResolver contentResolver,
+ Uri uri,
+ String[] projection,
+ String selection,
+ String[] selectionArgs,
+ String orderBy,
+ long channelId) {
+ super(executor, contentResolver, uri, projection, selection, selectionArgs, orderBy);
+ mChannelIdFromIntent = channelId;
+ }
+
+ @Override
+ protected Program onQuery(Cursor c) {
+ Program program = null;
+ if (c != null && c.moveToNext()) {
+ program = Program.fromCursor(c);
+ }
+ return program;
+ }
+
+ @Override
+ protected void onPostExecute(Program program) {
+ if (program == null || program.getStartTimeUtcMillis() <= System.currentTimeMillis()) {
+ // null or current program
+ return;
+ }
+ Channel channel = mChannelDataManager.getChannel(mChannelIdFromIntent);
+ if (channel != null) {
+ ScheduledRecording scheduledRecording =
+ TvSingletons.getSingletons(MainActivity.this)
+ .getDvrDataManager()
+ .getScheduledRecordingForProgramId(program.getId());
+ DvrUiHelper.checkStorageStatusAndShowErrorMessage(
+ MainActivity.this,
+ channel.getInputId(),
+ new Runnable() {
+ @Override
+ public void run() {
+ if (CommonFeatures.DVR.isEnabled(MainActivity.this)
+ && scheduledRecording == null
+ && mDvrManager.isProgramRecordable(program)) {
+ DvrUiHelper.requestRecordingFutureProgram(
+ MainActivity.this, program, false);
+ } else {
+ DvrUiHelper.showProgramInfoDialog(MainActivity.this, program);
+ }
+ }
+ });
+ }
+ }
+ }
+
private void stopTv() {
stopTv(null, false);
}
@@ -1462,7 +1635,8 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
mAudioManagerHelper.abandonAudioFocus();
mMediaSessionWrapper.setPlaybackState(false);
}
- TvApplication.getSingletons(this).getMainActivityWrapper()
+ TvSingletons.getSingletons(this)
+ .getMainActivityWrapper()
.notifyCurrentChannelChange(this, null);
mChannelTuner.resetCurrentChannel();
mTunePending = false;
@@ -1473,9 +1647,7 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
mHandler.postDelayed(mRestoreMainViewRunnable, TVVIEW_SET_MAIN_TIMEOUT_MS);
}
- /**
- * Says {@code text} when accessibility is turned on.
- */
+ /** Says {@code text} when accessibility is turned on. */
private void sendAccessibilityText(String text) {
if (mAccessibilityManager.isEnabled()) {
AccessibilityEvent event = AccessibilityEvent.obtain();
@@ -1510,8 +1682,8 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
finish();
return;
}
- SetupUtils setupUtils = SetupUtils.getInstance(this);
- if (setupUtils.isFirstTune()) {
+
+ if (mSetupUtils.isFirstTune()) {
if (!mChannelTuner.areAllChannelsLoaded()) {
// tune() will be called, once all channels are loaded.
stopTv("tune()", false);
@@ -1532,29 +1704,33 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
return;
}
if (mTvInputManagerHelper.getTunerTvInputSize() == 1) {
- mOverlayManager.getSideFragmentManager().show(
- new CustomizeChannelListFragment());
+ mOverlayManager
+ .getSideFragmentManager()
+ .show(new CustomizeChannelListFragment());
} else {
mOverlayManager.showSetupFragment();
}
return;
}
- if (!TvCommonUtils.isRunningInTest() && mShowNewSourcesFragment
- && setupUtils.hasUnrecognizedInput(mTvInputManagerHelper)) {
+ if (!CommonUtils.isRunningInTest()
+ && mShowNewSourcesFragment
+ && mSetupUtils.hasUnrecognizedInput(mTvInputManagerHelper)) {
// Show new channel sources fragment.
- runAfterAttachedToWindow(new Runnable() {
- @Override
- public void run() {
- mOverlayManager.runAfterOverlaysAreClosed(new Runnable() {
+ runAfterAttachedToWindow(
+ new Runnable() {
@Override
public void run() {
- mOverlayManager.showNewSourcesFragment();
+ mOverlayManager.runAfterOverlaysAreClosed(
+ new Runnable() {
+ @Override
+ public void run() {
+ mOverlayManager.showNewSourcesFragment();
+ }
+ });
}
});
- }
- });
}
- setupUtils.onTuned();
+ mSetupUtils.onTuned();
if (mTuneParams != null) {
Long initChannelId = mTuneParams.getLong(KEY_INIT_CHANNEL_ID);
if (initChannelId == channel.getId()) {
@@ -1571,9 +1747,11 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
}
// For every tune, we need to inform the tuned channel or input to a user,
// if Talkback is turned on.
- sendAccessibilityText(!mChannelTuner.isCurrentChannelPassthrough() ?
- Utils.loadLabel(this, mTvInputManagerHelper.getTvInputInfo(channel.getInputId()))
- : channel.getDisplayText());
+ sendAccessibilityText(
+ mChannelTuner.isCurrentChannelPassthrough()
+ ? Utils.loadLabel(
+ this, mTvInputManagerHelper.getTvInputInfo(channel.getInputId()))
+ : channel.getDisplayText());
boolean success = mTvView.tuneTo(channel, mTuneParams, mOnTuneListener);
mOnTuneListener.onTune(channel, isUnderShrunkenTvView());
@@ -1592,7 +1770,8 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
addToRecentChannels(channel.getId());
}
Utils.setLastWatchedChannel(this, channel);
- TvApplication.getSingletons(this).getMainActivityWrapper()
+ TvSingletons.getSingletons(this)
+ .getMainActivityWrapper()
.notifyCurrentChannelChange(this, channel);
}
// We have to provide channel here instead of using TvView's channel, because TvView's
@@ -1619,34 +1798,42 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
// If the activity is paused shortly, runnable may not be called because all the fragments
// should be closed when the activity is paused.
private void runAfterAttachedToWindow(final Runnable runnable) {
- final Runnable runOnlyIfActivityIsResumed = new Runnable() {
- @Override
- public void run() {
- if (mActivityResumed) {
- runnable.run();
- }
- }
- };
+ final Runnable runOnlyIfActivityIsResumed =
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mActivityResumed) {
+ runnable.run();
+ }
+ }
+ };
if (mContentView.isAttachedToWindow()) {
mHandler.post(runOnlyIfActivityIsResumed);
} else {
- mContentView.getViewTreeObserver().addOnWindowAttachListener(
- new ViewTreeObserver.OnWindowAttachListener() {
- @Override
- public void onWindowAttached() {
- mContentView.getViewTreeObserver().removeOnWindowAttachListener(this);
- mHandler.post(runOnlyIfActivityIsResumed);
- }
+ mContentView
+ .getViewTreeObserver()
+ .addOnWindowAttachListener(
+ new ViewTreeObserver.OnWindowAttachListener() {
+ @Override
+ public void onWindowAttached() {
+ mContentView
+ .getViewTreeObserver()
+ .removeOnWindowAttachListener(this);
+ mHandler.post(runOnlyIfActivityIsResumed);
+ }
- @Override
- public void onWindowDetached() { }
- });
+ @Override
+ public void onWindowDetached() {}
+ });
}
}
boolean isNowPlayingProgram(Channel channel, Program program) {
- return program == null ? (channel != null && getCurrentProgram() == null
- && channel.equals(getCurrentChannel())) : program.equals(getCurrentProgram());
+ return program == null
+ ? (channel != null
+ && getCurrentProgram() == null
+ && channel.equals(getCurrentChannel()))
+ : program.equals(getCurrentProgram());
}
private void addToRecentChannels(long channelId) {
@@ -1659,9 +1846,7 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
mOverlayManager.getMenu().onRecentChannelsChanged();
}
- /**
- * Returns the recently tuned channels.
- */
+ /** Returns the recently tuned channels. */
public ArrayDeque<Long> getRecentChannels() {
return mRecentChannels;
}
@@ -1694,9 +1879,7 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
}
}
- /**
- * Hide the overlays when tuning to a channel from the menu (e.g. Channels).
- */
+ /** Hide the overlays when tuning to a channel from the menu (e.g. Channels). */
public void hideOverlaysForTune() {
mOverlayManager.hideOverlays(TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_SCENE);
}
@@ -1712,8 +1895,8 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
setPreferredRefreshRate(mDefaultRefreshRate);
mIsFilmModeSet = false;
} else if (!mIsFilmModeSet && is24Fps) {
- DisplayManager displayManager = (DisplayManager) getSystemService(
- Context.DISPLAY_SERVICE);
+ DisplayManager displayManager =
+ (DisplayManager) getSystemService(Context.DISPLAY_SERVICE);
Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
float[] refreshRates = display.getSupportedRefreshRates();
@@ -1745,8 +1928,8 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
String id = TvSettings.getMultiAudioId(this);
String language = TvSettings.getMultiAudioLanguage(this);
int channelCount = TvSettings.getMultiAudioChannelCount(this);
- TvTrackInfo bestTrack = TvTrackInfoUtils
- .getBestTrackInfo(tracks, id, language, channelCount);
+ TvTrackInfo bestTrack =
+ TvTrackInfoUtils.getBestTrackInfo(tracks, id, language, channelCount);
if (bestTrack != null) {
String selectedTrack = getSelectedTrack(TvTrackInfo.TYPE_AUDIO);
if (!bestTrack.getId().equals(selectedTrack)) {
@@ -1787,8 +1970,13 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
mTvOptionsManager.onClosedCaptionsChanged(track, i);
}
if (DEBUG) {
- Log.d(TAG, "Subtitle Track Selected {id=" + track.getId()
- + ", language=" + track.getLanguage() + "}");
+ Log.d(
+ TAG,
+ "Subtitle Track Selected {id="
+ + track.getId()
+ + ", language="
+ + track.getLanguage()
+ + "}");
}
return;
} else if (alternativeTrack == null) {
@@ -1801,12 +1989,17 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
if (!alternativeTrack.getId().equals(selectedTrackId)) {
selectTrack(TvTrackInfo.TYPE_SUBTITLE, alternativeTrack, alternativeTrackIndex);
} else {
- mTvOptionsManager
- .onClosedCaptionsChanged(alternativeTrack, alternativeTrackIndex);
+ mTvOptionsManager.onClosedCaptionsChanged(
+ alternativeTrack, alternativeTrackIndex);
}
if (DEBUG) {
- Log.d(TAG, "Subtitle Track Selected {id=" + alternativeTrack.getId()
- + ", language=" + alternativeTrack.getLanguage() + "}");
+ Log.d(
+ TAG,
+ "Subtitle Track Selected {id="
+ + alternativeTrack.getId()
+ + ", language="
+ + alternativeTrack.getLanguage()
+ + "}");
}
return;
}
@@ -1820,8 +2013,11 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
}
public void showProgramGuideSearchFragment() {
- getFragmentManager().beginTransaction().replace(R.id.fragment_container, mSearchFragment)
- .addToBackStack(null).commit();
+ getFragmentManager()
+ .beginTransaction()
+ .replace(R.id.fragment_container, mSearchFragment)
+ .addToBackStack(null)
+ .commit();
}
@Override
@@ -1853,6 +2049,7 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
}
}
if (mOverlayManager != null) {
+ mAccessibilityManager.removeAccessibilityStateChangeListener(mOverlayManager);
mOverlayManager.release();
}
mMemoryManageables.clear();
@@ -1874,7 +2071,7 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
}
if (mTvInputManagerHelper != null) {
mTvInputManagerHelper.clearTvInputLabels();
- if (Features.TUNER.isEnabled(this)) {
+ if (TvFeatures.TUNER.isEnabled(this)) {
mTvInputManagerHelper.removeCallback(mTvInputCallback);
}
}
@@ -1895,7 +2092,7 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
return false;
case KEY_EVENT_HANDLER_RESULT_PASSTHROUGH:
default:
- // pass through
+ // fall through
}
if (mSearchFragment.isVisible()) {
return super.onKeyDown(keyCode, event);
@@ -1903,35 +2100,51 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
if (!mChannelTuner.areAllChannelsLoaded()) {
return false;
}
+ if (handleUpDownKeys(keyCode, event)) {
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ private boolean handleUpDownKeys(int keyCode, @Nullable KeyEvent event) {
if (!mChannelTuner.isCurrentChannelPassthrough()) {
switch (keyCode) {
case KeyEvent.KEYCODE_CHANNEL_UP:
case KeyEvent.KEYCODE_DPAD_UP:
- if (event.getRepeatCount() == 0
+ if ((event == null || event.getRepeatCount() == 0)
&& mChannelTuner.getBrowsableChannelCount() > 0) {
// message sending should be done before moving channel, because we use the
// existence of message to decide if users are switching channel.
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CHANNEL_UP_PRESSED,
- System.currentTimeMillis()), CHANNEL_CHANGE_INITIAL_DELAY_MILLIS);
+ if (event != null) {
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(
+ MSG_CHANNEL_UP_PRESSED, System.currentTimeMillis()),
+ CHANNEL_CHANGE_INITIAL_DELAY_MILLIS);
+ }
moveToAdjacentChannel(true, false);
mTracker.sendChannelUp();
}
return true;
case KeyEvent.KEYCODE_CHANNEL_DOWN:
case KeyEvent.KEYCODE_DPAD_DOWN:
- if (event.getRepeatCount() == 0
+ if ((event == null || event.getRepeatCount() == 0)
&& mChannelTuner.getBrowsableChannelCount() > 0) {
// message sending should be done before moving channel, because we use the
// existence of message to decide if users are switching channel.
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CHANNEL_DOWN_PRESSED,
- System.currentTimeMillis()), CHANNEL_CHANGE_INITIAL_DELAY_MILLIS);
+ if (event != null) {
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(
+ MSG_CHANNEL_DOWN_PRESSED, System.currentTimeMillis()),
+ CHANNEL_CHANGE_INITIAL_DELAY_MILLIS);
+ }
moveToAdjacentChannel(false, false);
mTracker.sendChannelDown();
}
return true;
+ default: // fall out
}
}
- return super.onKeyDown(keyCode, event);
+ return false;
}
@Override
@@ -1969,7 +2182,7 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
return false;
case KEY_EVENT_HANDLER_RESULT_PASSTHROUGH:
default:
- // pass through
+ // fall through
}
if (mSearchFragment.isVisible()) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
@@ -1999,6 +2212,7 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
case KeyEvent.KEYCODE_MENU:
showSettingsFragment();
return true;
+ default: // fall out
}
} else {
if (KeypadChannelSwitchView.isChannelNumberKey(keyCode)) {
@@ -2009,9 +2223,9 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (!mTvView.isVideoOrAudioAvailable()
&& mTvView.getVideoUnavailableReason()
- == TunableTvView.VIDEO_UNAVAILABLE_REASON_NO_RESOURCE) {
- DvrUiHelper.startSchedulesActivityForTuneConflict(this,
- mChannelTuner.getCurrentChannel());
+ == TunableTvView.VIDEO_UNAVAILABLE_REASON_NO_RESOURCE) {
+ DvrUiHelper.startSchedulesActivityForTuneConflict(
+ this, mChannelTuner.getCurrentChannel());
return true;
}
if (!PermissionUtils.hasModifyParentalControls(this)) {
@@ -2019,16 +2233,18 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
}
PinDialogFragment dialog = null;
if (mTvView.isScreenBlocked()) {
- dialog = PinDialogFragment
- .create(PinDialogFragment.PIN_DIALOG_TYPE_UNLOCK_CHANNEL);
+ dialog =
+ PinDialogFragment.create(
+ PinDialogFragment.PIN_DIALOG_TYPE_UNLOCK_CHANNEL);
} else if (mTvView.isContentBlocked()) {
- dialog = PinDialogFragment
- .create(PinDialogFragment.PIN_DIALOG_TYPE_UNLOCK_PROGRAM,
+ dialog =
+ PinDialogFragment.create(
+ PinDialogFragment.PIN_DIALOG_TYPE_UNLOCK_PROGRAM,
mTvView.getBlockedContentRating().flattenToString());
}
if (dialog != null) {
- mOverlayManager.showDialogFragment(PinDialogFragment.DIALOG_TAG, dialog,
- false);
+ mOverlayManager.showDialogFragment(
+ PinDialogFragment.DIALOG_TAG, dialog, false);
}
return true;
case KeyEvent.KEYCODE_WINDOW:
@@ -2064,49 +2280,56 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
if (!SystemProperties.USE_DEBUG_KEYS.getValue()) {
break;
}
- // Pass through.
- case KeyEvent.KEYCODE_CAPTIONS: {
+ // fall through.
+ case KeyEvent.KEYCODE_CAPTIONS:
mOverlayManager.getSideFragmentManager().show(new ClosedCaptionFragment());
return true;
- }
case KeyEvent.KEYCODE_A:
if (!SystemProperties.USE_DEBUG_KEYS.getValue()) {
break;
}
- // Pass through.
- case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
+ // fall through.
+ case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK:
mOverlayManager.getSideFragmentManager().show(new MultiAudioFragment());
return true;
- }
- case KeyEvent.KEYCODE_INFO: {
+ case KeyEvent.KEYCODE_INFO:
mOverlayManager.showBanner();
return true;
- }
case KeyEvent.KEYCODE_MEDIA_RECORD:
- case KeyEvent.KEYCODE_V: {
+ case KeyEvent.KEYCODE_V:
Channel currentChannel = getCurrentChannel();
if (currentChannel != null && mDvrManager != null) {
boolean isRecording =
mDvrManager.getCurrentRecording(currentChannel.getId()) != null;
if (!isRecording) {
if (!mDvrManager.isChannelRecordable(currentChannel)) {
- Toast.makeText(this, R.string.dvr_msg_cannot_record_program,
- Toast.LENGTH_SHORT).show();
+ Toast.makeText(
+ this,
+ R.string.dvr_msg_cannot_record_program,
+ Toast.LENGTH_SHORT)
+ .show();
} else {
- Program program = mProgramDataManager
- .getCurrentProgram(currentChannel.getId());
- DvrUiHelper.checkStorageStatusAndShowErrorMessage(this,
- currentChannel.getInputId(), new Runnable() {
+ Program program =
+ mProgramDataManager.getCurrentProgram(
+ currentChannel.getId());
+ DvrUiHelper.checkStorageStatusAndShowErrorMessage(
+ this,
+ currentChannel.getInputId(),
+ new Runnable() {
@Override
public void run() {
DvrUiHelper.requestRecordingCurrentProgram(
MainActivity.this,
- currentChannel, program, false);
+ currentChannel,
+ program,
+ false);
}
});
}
} else {
- DvrUiHelper.showStopRecordingDialog(this, currentChannel.getId(),
+ DvrUiHelper.showStopRecordingDialog(
+ this,
+ currentChannel.getId(),
DvrStopRecordingFragment.REASON_USER_STOP,
new HalfSizedDialogFragment.OnActionClickListener() {
@Override
@@ -2124,7 +2347,7 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
}
}
return true;
- }
+ default: // fall out
}
}
if (keyCode == KeyEvent.KEYCODE_WINDOW) {
@@ -2162,6 +2385,7 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
case KeyEvent.KEYCODE_D:
mOverlayManager.getSideFragmentManager().show(new DeveloperOptionFragment());
return true;
+ default: // fall out
}
}
return super.onKeyUp(keyCode, event);
@@ -2196,13 +2420,19 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
// 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();
- }
- });
+ mIsInPIPMode = true;
+ if (mOverlayManager.isOverlayOpened()) {
+ mOverlayManager.hideOverlays(TvOverlayManager.FLAG_HIDE_OVERLAYS_WITHOUT_ANIMATION);
+ mHandler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ MainActivity.super.enterPictureInPictureMode();
+ }
+ });
+ } else {
+ MainActivity.super.enterPictureInPictureMode();
+ }
}
@Override
@@ -2248,7 +2478,8 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
}
if (isKeyEventBlocked()) {
if ((event.getKeyCode() == KeyEvent.KEYCODE_BACK
- || event.getKeyCode() == KeyEvent.KEYCODE_BUTTON_B) && mNeedShowBackKeyGuide) {
+ || event.getKeyCode() == KeyEvent.KEYCODE_BUTTON_B)
+ && mNeedShowBackKeyGuide) {
// KeyEvent.KEYCODE_BUTTON_B is also used like the back button.
Toast.makeText(this, R.string.msg_back_key_guide, Toast.LENGTH_SHORT).show();
mNeedShowBackKeyGuide = false;
@@ -2283,7 +2514,7 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
} else if (channel.equals(mTvView.getCurrentChannel())) {
mOverlayManager.updateChannelBannerAndShowIfNeeded(
TvOverlayManager.UPDATE_CHANNEL_BANNER_REASON_TUNE);
- } else if (channel == mChannelTuner.getCurrentChannel()) {
+ } else if (channel.equals(mChannelTuner.getCurrentChannel())) {
// Channel banner is already updated in moveToAdjacentChannel
tune(false);
} else if (mChannelTuner.moveToChannel(channel)) {
@@ -2296,24 +2527,23 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
}
/**
- * This method just moves the channel in the channel map and updates the channel banner,
- * but doesn't actually tune to the channel.
- * The caller of this method should call {@link #tune} in the end.
+ * This method just moves the channel in the channel map and updates the channel banner, but
+ * doesn't actually tune to the channel. The caller of this method should call {@link #tune} in
+ * the end.
*
* @param channelUp {@code true} for channel up, and {@code false} for channel down.
* @param fastTuning {@code true} if fast tuning is requested.
*/
private void moveToAdjacentChannel(boolean channelUp, boolean fastTuning) {
if (mChannelTuner.moveToAdjacentBrowsableChannel(channelUp)) {
- mOverlayManager.updateChannelBannerAndShowIfNeeded(fastTuning ?
- TvOverlayManager.UPDATE_CHANNEL_BANNER_REASON_TUNE_FAST
- : TvOverlayManager.UPDATE_CHANNEL_BANNER_REASON_TUNE);
+ mOverlayManager.updateChannelBannerAndShowIfNeeded(
+ fastTuning
+ ? TvOverlayManager.UPDATE_CHANNEL_BANNER_REASON_TUNE_FAST
+ : TvOverlayManager.UPDATE_CHANNEL_BANNER_REASON_TUNE);
}
}
- /**
- * Set the main TV view which holds HDMI-CEC active source based on the sound mode
- */
+ /** Set the main TV view which holds HDMI-CEC active source based on the sound mode */
private void restoreMainTvView() {
mTvView.setMain();
}
@@ -2355,8 +2585,8 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
private void selectTrack(int type, TvTrackInfo track, int trackIndex) {
mTvView.selectTrack(type, track == null ? null : track.getId());
if (type == TvTrackInfo.TYPE_AUDIO) {
- mTvOptionsManager.onMultiAudioChanged(track == null ? null :
- Utils.getMultiAudioString(this, track, false));
+ mTvOptionsManager.onMultiAudioChanged(
+ track == null ? null : Utils.getMultiAudioString(this, track, false));
} else if (type == TvTrackInfo.TYPE_SUBTITLE) {
mTvOptionsManager.onClosedCaptionsChanged(track, trackIndex);
}
@@ -2414,7 +2644,8 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
private void updateAvailabilityToast() {
if (mTvView.isVideoAvailable()
- || mTvView.getCurrentChannel() != mChannelTuner.getCurrentChannel()) {
+ || !Objects.equals(
+ mTvView.getCurrentChannel(), mChannelTuner.getCurrentChannel())) {
return;
}
@@ -2428,50 +2659,38 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
return;
case TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN:
default:
- Toast.makeText(this, R.string.msg_channel_unavailable_unknown,
- Toast.LENGTH_SHORT).show();
+ Toast.makeText(this, R.string.msg_channel_unavailable_unknown, Toast.LENGTH_SHORT)
+ .show();
break;
}
}
- /**
- * Returns {@code true} if some overlay UI will be shown when the activity is resumed.
- */
+ /** Returns {@code true} if some overlay UI will be shown when the activity is resumed. */
public boolean willShowOverlayUiWhenResume() {
return mInputToSetUp != null || mShowProgramGuide || mShowSelectInputView;
}
- /**
- * Returns the current parental control settings.
- */
+ /** Returns the current parental control settings. */
public ParentalControlSettings getParentalControlSettings() {
return mTvInputManagerHelper.getParentalControlSettings();
}
- /**
- * Returns a ContentRatingsManager instance.
- */
+ /** Returns a ContentRatingsManager instance. */
public ContentRatingsManager getContentRatingsManager() {
return mTvInputManagerHelper.getContentRatingsManager();
}
- /**
- * Returns the current captioning settings.
- */
+ /** Returns the current captioning settings. */
public CaptionSettings getCaptionSettings() {
return mCaptionSettings;
}
- /**
- * Adds the {@link OnActionClickListener}.
- */
+ /** Adds the {@link OnActionClickListener}. */
public void addOnActionClickListener(OnActionClickListener listener) {
mOnActionClickListeners.add(listener);
}
- /**
- * Removes the {@link OnActionClickListener}.
- */
+ /** Removes the {@link OnActionClickListener}. */
public void removeOnActionClickListener(OnActionClickListener listener) {
mOnActionClickListeners.remove(listener);
}
@@ -2490,7 +2709,7 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
// Initialize TV app for test. The setup process should be finished before the Live TV app is
// started. We only enable all the channels here.
private void initForTest() {
- if (!TvCommonUtils.isRunningInTest()) {
+ if (!CommonUtils.isRunningInTest()) {
return;
}
@@ -2505,16 +2724,18 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
}
mLazyInitialized = true;
// Running initialization.
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- if (mActivityStarted) {
- initAnimations();
- initSideFragments();
- initMenuItemViews();
- }
- }
- }, LAZY_INITIALIZATION_DELAY);
+ mHandler.postDelayed(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mActivityStarted) {
+ initAnimations();
+ initSideFragments();
+ initMenuItemViews();
+ }
+ }
+ },
+ LAZY_INITIALIZATION_DELAY);
}
private void initAnimations() {
@@ -2560,6 +2781,7 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
sendMessageDelayed(Message.obtain(msg), getDelay(startTime));
mainActivity.moveToAdjacentChannel(true, true);
break;
+ default: // fall out
}
}
@@ -2594,17 +2816,19 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
if (mTvView.isFadedOut()) {
mTvView.removeFadeEffect();
}
- Toast.makeText(MainActivity.this, R.string.msg_channel_unavailable_unknown,
- Toast.LENGTH_SHORT).show();
+ Toast.makeText(
+ MainActivity.this,
+ R.string.msg_channel_unavailable_unknown,
+ Toast.LENGTH_SHORT)
+ .show();
}
@Override
public void onStreamInfoChanged(StreamInfo info) {
if (info.isVideoAvailable() && mTuneDurationTimer.isRunning()) {
- mTracker.sendChannelTuneTime(info.getCurrentChannel(),
- mTuneDurationTimer.reset());
+ mTracker.sendChannelTuneTime(info.getCurrentChannel(), mTuneDurationTimer.reset());
}
- if (info.isVideoOrAudioAvailable() && mChannel == getCurrentChannel()) {
+ if (info.isVideoOrAudioAvailable() && mChannel.equals(getCurrentChannel())) {
mOverlayManager.updateChannelBannerAndShowIfNeeded(
TvOverlayManager.UPDATE_CHANNEL_BANNER_REASON_UPDATE_STREAM_INFO);
}
@@ -2629,10 +2853,12 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
return;
}
Channel currentChannel =
- mChannelDataManager.getChannel(ContentUris.parseId(channel));
+ mChannelDataManager.getChannel(ContentUriUtils.safeParseId(channel));
if (currentChannel == null) {
- Log.e(TAG, "onChannelRetuned is called but can't find a channel with the URI "
- + channel);
+ Log.e(
+ TAG,
+ "onChannelRetuned is called but can't find a channel with the URI "
+ + channel);
return;
}
if (isChannelChangeKeyDownReceived()) {
@@ -2647,15 +2873,16 @@ public class MainActivity extends Activity implements OnActionClickListener, OnP
@Override
public void onContentBlocked() {
- Debug.getTimer(Debug.TAG_START_UP_TIMER).log(
- "MainActivity.MyOnTuneListener.onContentBlocked removes timer");
+ Debug.getTimer(Debug.TAG_START_UP_TIMER)
+ .log("MainActivity.MyOnTuneListener.onContentBlocked removes timer");
Debug.removeTimer(Debug.TAG_START_UP_TIMER);
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
+ if (mWasUnderShrunkenTvView
+ && mUnlockAllowedRatingBeforeShrunken
&& mChannelBeforeShrunkenTvView.equals(mChannel)
&& rating.equals(mAllowedRatingBeforeShrunken)) {
mUnlockAllowedRatingBeforeShrunken = isUnderShrunkenTvView();
diff --git a/src/com/android/tv/MainActivityWrapper.java b/src/com/android/tv/MainActivityWrapper.java
index 5af5079f..6cecb436 100644
--- a/src/com/android/tv/MainActivityWrapper.java
+++ b/src/com/android/tv/MainActivityWrapper.java
@@ -20,14 +20,12 @@ import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.ArraySet;
-
-import com.android.tv.data.Channel;
-
+import com.android.tv.data.api.Channel;
import java.util.Set;
/**
- * A wrapper for safely getting the current {@link MainActivity}.
- * Note that this class is not thread-safe. All the public methods should be called on main thread.
+ * A wrapper for safely getting the current {@link MainActivity}. Note that this class is not
+ * thread-safe. All the public methods should be called on main thread.
*/
@MainThread
public final class MainActivityWrapper {
@@ -36,39 +34,31 @@ public final class MainActivityWrapper {
private final Set<OnCurrentChannelChangeListener> mListeners = new ArraySet<>();
/**
- * Returns the current main activity.
- * <b>WARNING</b> do not keep a reference to MainActivity, leaking activities is expensive.
+ * Returns the current main activity. <b>WARNING</b> do not keep a reference to MainActivity,
+ * leaking activities is expensive.
*/
MainActivity getMainActivity() {
return mActivity;
}
- /**
- * Checks if the given {@code activity} is the current main activity.
- */
+ /** Checks if the given {@code activity} is the current main activity. */
boolean isCurrent(MainActivity activity) {
return activity != null && mActivity == activity;
}
- /**
- * Sets the currently created main activity instance.
- */
+ /** Sets the currently created main activity instance. */
public void onMainActivityCreated(@NonNull MainActivity activity) {
mActivity = activity;
}
- /**
- * Unsets the main activity instance.
- */
+ /** Unsets the main activity instance. */
public void onMainActivityDestroyed(@NonNull MainActivity activity) {
if (mActivity == activity) {
mActivity = null;
}
}
- /**
- * Notifies the current channel change.
- */
+ /** Notifies the current channel change. */
void notifyCurrentChannelChange(@NonNull MainActivity caller, @Nullable Channel channel) {
if (mActivity == caller) {
for (OnCurrentChannelChangeListener listener : mListeners) {
@@ -77,48 +67,34 @@ public final class MainActivityWrapper {
}
}
- /**
- * Checks if the main activity is created.
- */
+ /** Checks if the main activity is created. */
public boolean isCreated() {
return mActivity != null;
}
- /**
- * Checks if the main activity is started.
- */
+ /** Checks if the main activity is started. */
public boolean isStarted() {
return mActivity != null && mActivity.isActivityStarted();
}
- /**
- * Checks if the main activity is resumed.
- */
+ /** Checks if the main activity is resumed. */
public boolean isResumed() {
return mActivity != null && mActivity.isActivityResumed();
}
- /**
- * Adds OnCurrentChannelChangeListener.
- */
+ /** Adds OnCurrentChannelChangeListener. */
public void addOnCurrentChannelChangeListener(OnCurrentChannelChangeListener listener) {
mListeners.add(listener);
}
- /**
- * Removes OnCurrentChannelChangeListener.
- */
+ /** Removes OnCurrentChannelChangeListener. */
public void removeOnCurrentChannelChangeListener(OnCurrentChannelChangeListener listener) {
mListeners.remove(listener);
}
- /**
- * Listener for the current channel change in main activity.
- */
+ /** Listener for the current channel change in main activity. */
public interface OnCurrentChannelChangeListener {
- /**
- * Called when the current channel changes.
- */
+ /** Called when the current channel changes. */
void onCurrentChannelChange(@Nullable Channel channel);
}
}
diff --git a/src/com/android/tv/MediaSessionWrapper.java b/src/com/android/tv/MediaSessionWrapper.java
index da6ad2a4..43cd74dd 100644
--- a/src/com/android/tv/MediaSessionWrapper.java
+++ b/src/com/android/tv/MediaSessionWrapper.java
@@ -16,6 +16,7 @@
package com.android.tv;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
@@ -28,12 +29,12 @@ import android.media.tv.TvInputInfo;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
-
-import com.android.tv.data.Channel;
import com.android.tv.data.Program;
-import com.android.tv.util.ImageLoader;
+import com.android.tv.data.api.Channel;
import com.android.tv.util.Utils;
+import com.android.tv.util.images.ImageLoader;
/**
* A wrapper class for {@link MediaSession} to support common operations on media sessions for
@@ -41,34 +42,47 @@ import com.android.tv.util.Utils;
*/
class MediaSessionWrapper {
private static final String MEDIA_SESSION_TAG = "com.android.tv.mediasession";
- private static PlaybackState MEDIA_SESSION_STATE_PLAYING = new PlaybackState.Builder()
- .setState(PlaybackState.STATE_PLAYING, PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1.0f)
- .build();
- private static PlaybackState MEDIA_SESSION_STATE_STOPPED = new PlaybackState.Builder()
- .setState(PlaybackState.STATE_STOPPED, PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f)
- .build();
+
+ private static final PlaybackState MEDIA_SESSION_STATE_PLAYING =
+ new PlaybackState.Builder()
+ .setState(
+ PlaybackState.STATE_PLAYING,
+ PlaybackState.PLAYBACK_POSITION_UNKNOWN,
+ 1.0f)
+ .build();
+
+ private static final PlaybackState MEDIA_SESSION_STATE_STOPPED =
+ new PlaybackState.Builder()
+ .setState(
+ PlaybackState.STATE_STOPPED,
+ PlaybackState.PLAYBACK_POSITION_UNKNOWN,
+ 0.0f)
+ .build();
private final Context mContext;
private final MediaSession mMediaSession;
private int mNowPlayingCardWidth;
private int mNowPlayingCardHeight;
- MediaSessionWrapper(Context context) {
+ MediaSessionWrapper(Context context, PendingIntent pendingIntent) {
mContext = context;
mMediaSession = new MediaSession(context, MEDIA_SESSION_TAG);
- mMediaSession.setCallback(new MediaSession.Callback() {
- @Override
- public boolean onMediaButtonEvent(@NonNull Intent mediaButtonIntent) {
- // Consume the media button event here. Should not send it to other apps.
- return true;
- }
- });
- mMediaSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS |
- MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
- mNowPlayingCardWidth = mContext.getResources().getDimensionPixelSize(
- R.dimen.notif_card_img_max_width);
- mNowPlayingCardHeight = mContext.getResources().getDimensionPixelSize(
- R.dimen.notif_card_img_height);
+ mMediaSession.setCallback(
+ new MediaSession.Callback() {
+ @Override
+ public boolean onMediaButtonEvent(@NonNull Intent mediaButtonIntent) {
+ // Consume the media button event here. Should not send it to other apps.
+ return true;
+ }
+ });
+ mMediaSession.setFlags(
+ MediaSession.FLAG_HANDLES_MEDIA_BUTTONS
+ | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
+ mMediaSession.setSessionActivity(pendingIntent);
+ mNowPlayingCardWidth =
+ mContext.getResources().getDimensionPixelSize(R.dimen.notif_card_img_max_width);
+ mNowPlayingCardHeight =
+ mContext.getResources().getDimensionPixelSize(R.dimen.notif_card_img_height);
}
/**
@@ -90,8 +104,8 @@ class MediaSessionWrapper {
/**
* Updates media session according to the current TV playback status.
*
- * @param blocked {@code true} if the current channel is blocked, either by user settings or
- * the current program's content ratings.
+ * @param blocked {@code true} if the current channel is blocked, either by user settings or the
+ * current program's content ratings.
* @param currentChannel The currently playing channel.
* @param currentProgram The currently playing program.
*/
@@ -103,10 +117,12 @@ class MediaSessionWrapper {
// If the channel is blocked, display a lock and a short text on the Now Playing Card
if (blocked) {
- Bitmap art = BitmapFactory.decodeResource(mContext.getResources(),
- R.drawable.ic_message_lock_preview);
- updateMediaMetadata(mContext.getResources()
- .getString(R.string.channel_banner_locked_channel_title), art);
+ Bitmap art =
+ BitmapFactory.decodeResource(
+ mContext.getResources(), R.drawable.ic_message_lock_preview);
+ updateMediaMetadata(
+ mContext.getResources().getString(R.string.channel_banner_locked_channel_title),
+ art);
setPlaybackState(true);
return;
}
@@ -139,22 +155,32 @@ class MediaSessionWrapper {
private String getChannelName(Channel channel) {
if (channel.isPassthrough()) {
- TvInputInfo input = TvApplication.getSingletons(mContext).getTvInputManagerHelper()
- .getTvInputInfo(channel.getInputId());
+ TvInputInfo input =
+ TvSingletons.getSingletons(mContext)
+ .getTvInputManagerHelper()
+ .getTvInputInfo(channel.getInputId());
return Utils.loadLabel(mContext, input);
} else {
return channel.getDisplayName();
}
}
- private void updatePosterArt(Channel currentChannel, Program currentProgram,
- String cardTitleText, @Nullable Bitmap posterArt, @Nullable String posterArtUri) {
+ private void updatePosterArt(
+ Channel currentChannel,
+ Program currentProgram,
+ String cardTitleText,
+ @Nullable Bitmap posterArt,
+ @Nullable String posterArtUri) {
if (posterArt != null) {
updateMediaMetadata(cardTitleText, posterArt);
} else if (posterArtUri != null) {
- ImageLoader.loadBitmap(mContext, posterArtUri, mNowPlayingCardWidth,
- mNowPlayingCardHeight, new ProgramPosterArtCallback(this, currentChannel,
- currentProgram, cardTitleText));
+ ImageLoader.loadBitmap(
+ mContext,
+ posterArtUri,
+ mNowPlayingCardWidth,
+ mNowPlayingCardHeight,
+ new ProgramPosterArtCallback(
+ this, currentChannel, currentProgram, cardTitleText));
} else {
updateMediaMetadata(cardTitleText, R.drawable.default_now_card);
}
@@ -176,7 +202,7 @@ class MediaSessionWrapper {
}
private void updateMediaMetadata(final String title, final int imageResId) {
- new AsyncTask<Void, Void, Void> () {
+ new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... arg0) {
MediaMetadata.Builder builder = new MediaMetadata.Builder();
@@ -192,14 +218,22 @@ class MediaSessionWrapper {
}.execute();
}
- private static class ProgramPosterArtCallback extends
- ImageLoader.ImageLoaderCallback<MediaSessionWrapper> {
+ @VisibleForTesting
+ MediaSession getMediaSession() {
+ return mMediaSession;
+ }
+
+ private static class ProgramPosterArtCallback
+ extends ImageLoader.ImageLoaderCallback<MediaSessionWrapper> {
private final Channel mChannel;
private final Program mProgram;
private final String mCardTitleText;
- ProgramPosterArtCallback(MediaSessionWrapper sessionWrapper, Channel channel,
- Program program, String cardTitleText) {
+ ProgramPosterArtCallback(
+ MediaSessionWrapper sessionWrapper,
+ Channel channel,
+ Program program,
+ String cardTitleText) {
super(sessionWrapper);
mChannel = channel;
mProgram = program;
diff --git a/src/com/android/tv/SelectInputActivity.java b/src/com/android/tv/SelectInputActivity.java
index c68a1ad0..56747044 100644
--- a/src/com/android/tv/SelectInputActivity.java
+++ b/src/com/android/tv/SelectInputActivity.java
@@ -23,15 +23,12 @@ import android.media.tv.TvInputInfo;
import android.net.Uri;
import android.os.Bundle;
import android.view.KeyEvent;
-
-import com.android.tv.data.Channel;
+import com.android.tv.data.ChannelImpl;
import com.android.tv.ui.SelectInputView;
import com.android.tv.ui.SelectInputView.OnInputSelectedCallback;
import com.android.tv.util.Utils;
-/**
- * An activity to select input.
- */
+/** An activity to select input. */
public class SelectInputActivity extends Activity {
private SelectInputView mSelectInputView;
@@ -41,30 +38,37 @@ public class SelectInputActivity extends Activity {
((TvApplication) getApplicationContext()).setSelectInputActivity(this);
setContentView(R.layout.activity_select_input);
mSelectInputView = (SelectInputView) findViewById(R.id.scene_transition_common);
- mSelectInputView.setOnInputSelectedCallback(new OnInputSelectedCallback() {
- @Override
- public void onTunerInputSelected() {
- startTvWithChannel(TvContract.Channels.CONTENT_URI);
- }
+ mSelectInputView.setOnInputSelectedCallback(
+ new OnInputSelectedCallback() {
+ @Override
+ public void onTunerInputSelected() {
+ startTvWithChannel(TvContract.Channels.CONTENT_URI);
+ }
- @Override
- public void onPassthroughInputSelected(TvInputInfo input) {
- startTvWithChannel(TvContract.buildChannelUriForPassthroughInput(input.getId()));
- }
+ @Override
+ public void onPassthroughInputSelected(TvInputInfo input) {
+ startTvWithChannel(
+ TvContract.buildChannelUriForPassthroughInput(input.getId()));
+ }
- private void startTvWithChannel(Uri channelUri) {
- Intent intent = new Intent(Intent.ACTION_VIEW, channelUri,
- SelectInputActivity.this, MainActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
- finish();
- }
- });
+ private void startTvWithChannel(Uri channelUri) {
+ Intent intent =
+ new Intent(
+ Intent.ACTION_VIEW,
+ channelUri,
+ SelectInputActivity.this,
+ MainActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ finish();
+ }
+ });
String channelUriString = Utils.getLastWatchedChannelUri(this);
if (channelUriString != null) {
Uri channelUri = Uri.parse(channelUriString);
if (TvContract.isChannelUriForPassthroughInput(channelUri)) {
- mSelectInputView.setCurrentChannel(Channel.createPassthroughChannel(channelUri));
+ mSelectInputView.setCurrentChannel(
+ ChannelImpl.createPassthroughChannel(channelUri));
}
// No need to set the tuner channel because it's the default selection.
}
diff --git a/src/com/android/tv/SetupPassthroughActivity.java b/src/com/android/tv/SetupPassthroughActivity.java
index f0f54413..199ea51d 100644
--- a/src/com/android/tv/SetupPassthroughActivity.java
+++ b/src/com/android/tv/SetupPassthroughActivity.java
@@ -26,23 +26,23 @@ import android.os.Handler;
import android.os.Looper;
import android.support.annotation.MainThread;
import android.util.Log;
-
import com.android.tv.common.SoftPreconditions;
-import com.android.tv.common.TvCommonConstants;
+import com.android.tv.common.actions.InputSetupActionUtils;
+import com.android.tv.common.experiments.Experiments;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.ChannelDataManager.Listener;
import com.android.tv.data.epg.EpgFetcher;
-import com.android.tv.experiments.Experiments;
+import com.android.tv.data.epg.EpgInputWhiteList;
import com.android.tv.util.SetupUtils;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.Utils;
-
+import com.google.android.tv.partner.support.EpgContract;
import java.util.concurrent.TimeUnit;
/**
* An activity to launch a TV input setup activity.
*
- * <p> After setup activity is finished, all channels will be browsable.
+ * <p>After setup activity is finished, all channels will be browsable.
*/
public class SetupPassthroughActivity extends Activity {
private static final String TAG = "SetupPassthroughAct";
@@ -55,35 +55,45 @@ public class SetupPassthroughActivity extends Activity {
private TvInputInfo mTvInputInfo;
private Intent mActivityAfterCompletion;
private boolean mEpgFetcherDuringScan;
+ private EpgInputWhiteList mEpgInputWhiteList;
@Override
public void onCreate(Bundle savedInstanceState) {
if (DEBUG) Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
- ApplicationSingletons appSingletons = TvApplication.getSingletons(this);
- TvInputManagerHelper inputManager = appSingletons.getTvInputManagerHelper();
+ TvSingletons tvSingletons = TvSingletons.getSingletons(this);
+ TvInputManagerHelper inputManager = tvSingletons.getTvInputManagerHelper();
Intent intent = getIntent();
- String inputId = intent.getStringExtra(TvCommonConstants.EXTRA_INPUT_ID);
+ String inputId = intent.getStringExtra(InputSetupActionUtils.EXTRA_INPUT_ID);
mTvInputInfo = inputManager.getTvInputInfo(inputId);
- mActivityAfterCompletion = intent.getParcelableExtra(
- TvCommonConstants.EXTRA_ACTIVITY_AFTER_COMPLETION);
- boolean needToFetchEpg = Utils.isInternalTvInput(this, mTvInputInfo.getId())
- && Experiments.CLOUD_EPG.get();
+ mEpgInputWhiteList = new EpgInputWhiteList(tvSingletons.getRemoteConfig());
+ mActivityAfterCompletion = InputSetupActionUtils.getExtraActivityAfter(intent);
+ boolean needToFetchEpg =
+ mTvInputInfo != null
+ && Utils.isInternalTvInput(this, mTvInputInfo.getId())
+ && Experiments.CLOUD_EPG.get();
if (needToFetchEpg) {
// In case when the activity is restored, this flag should be restored as well.
mEpgFetcherDuringScan = true;
}
if (savedInstanceState == null) {
- SoftPreconditions.checkState(
- intent.getAction().equals(TvCommonConstants.INTENT_ACTION_INPUT_SETUP));
+ SoftPreconditions.checkArgument(
+ InputSetupActionUtils.hasInputSetupAction(intent),
+ TAG,
+ "Unsupported action %s",
+ intent.getAction());
if (DEBUG) Log.d(TAG, "TvInputId " + inputId + " / TvInputInfo " + mTvInputInfo);
if (mTvInputInfo == null) {
Log.w(TAG, "There is no input with the ID " + inputId + ".");
finish();
return;
}
- Intent setupIntent =
- intent.getExtras().getParcelable(TvCommonConstants.EXTRA_SETUP_INTENT);
+ if (intent.getExtras() == null) {
+ Log.w(TAG, "There is no extra info in the intent");
+ finish();
+ return;
+ }
+ Intent setupIntent = InputSetupActionUtils.getExtraSetupIntent(intent);
if (DEBUG) Log.d(TAG, "Setup activity launch intent: " + setupIntent);
if (setupIntent == null) {
Log.w(TAG, "The input (" + mTvInputInfo.getId() + ") doesn't have setup.");
@@ -95,7 +105,7 @@ public class SetupPassthroughActivity extends Activity {
// If EXTRA_SETUP_INTENT is not removed, an infinite recursion happens during
// setupIntent.putExtras(intent.getExtras()).
Bundle extras = intent.getExtras();
- extras.remove(TvCommonConstants.EXTRA_SETUP_INTENT);
+ InputSetupActionUtils.removeSetupIntent(extras);
setupIntent.putExtras(extras);
try {
startActivityForResult(setupIntent, REQUEST_START_SETUP_ACTIVITY);
@@ -109,48 +119,54 @@ public class SetupPassthroughActivity extends Activity {
sScanTimeoutMonitor = new ScanTimeoutMonitor(this);
}
sScanTimeoutMonitor.startMonitoring();
- EpgFetcher.getInstance(this).onChannelScanStarted();
+ TvSingletons.getSingletons(this).getEpgFetcher().onChannelScanStarted();
}
}
}
@Override
public void onActivityResult(int requestCode, final int resultCode, final Intent data) {
- if (DEBUG) Log.d(TAG, "onActivityResult");
+ if (DEBUG)
+ Log.d(TAG, "onActivityResult(" + requestCode + ", " + resultCode + ", " + data + ")");
if (sScanTimeoutMonitor != null) {
sScanTimeoutMonitor.stopMonitoring();
}
// Note: It's not guaranteed that this method is always called after scanning.
- boolean setupComplete = requestCode == REQUEST_START_SETUP_ACTIVITY
- && resultCode == Activity.RESULT_OK;
+ boolean setupComplete =
+ requestCode == REQUEST_START_SETUP_ACTIVITY && resultCode == Activity.RESULT_OK;
// Tells EpgFetcher that channel source setup is finished.
+ EpgFetcher epgFetcher = TvSingletons.getSingletons(this).getEpgFetcher();
if (mEpgFetcherDuringScan) {
- EpgFetcher.getInstance(this).onChannelScanFinished();
+ epgFetcher.onChannelScanFinished();
}
if (!setupComplete) {
setResult(resultCode, data);
finish();
return;
}
- SetupUtils.getInstance(this).onTvInputSetupFinished(mTvInputInfo.getId(), new Runnable() {
- @Override
- public void run() {
- if (mActivityAfterCompletion != null) {
- try {
- startActivity(mActivityAfterCompletion);
- } catch (ActivityNotFoundException e) {
- Log.w(TAG, "Activity launch failed", e);
- }
- }
- setResult(resultCode, data);
- finish();
- }
- });
+ TvSingletons.getSingletons(this)
+ .getSetupUtils()
+ .onTvInputSetupFinished(
+ mTvInputInfo.getId(),
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mActivityAfterCompletion != null) {
+ try {
+ startActivity(mActivityAfterCompletion);
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "Activity launch failed", e);
+ }
+ }
+ setResult(resultCode, data);
+ finish();
+ }
+ });
}
/**
- * Monitors the scan progress and notifies the timeout of the scanning.
- * The purpose of this monitor is to call EpgFetcher.onChannelScanFinished() in case when
+ * Monitors the scan progress and notifies the timeout of the scanning. The purpose of this
+ * monitor is to call EpgFetcher.onChannelScanFinished() in case when
* SetupPassthroughActivity.onActivityResult() is not called properly. b/36008534
*/
@MainThread
@@ -161,33 +177,37 @@ public class SetupPassthroughActivity extends Activity {
private final Context mContext;
private final ChannelDataManager mChannelDataManager;
private final Handler mHandler = new Handler(Looper.getMainLooper());
- private final Runnable mScanTimeoutRunnable = new Runnable() {
- @Override
- public void run() {
- Log.w(TAG, "No channels has been added for a while." +
- " The scan might have finished unexpectedly.");
- onScanTimedOut();
- }
- };
- private final Listener mChannelDataManagerListener = new Listener() {
- @Override
- public void onLoadFinished() {
- setupTimer();
- }
+ private final Runnable mScanTimeoutRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ Log.w(
+ TAG,
+ "No channels has been added for a while."
+ + " The scan might have finished unexpectedly.");
+ onScanTimedOut();
+ }
+ };
+ private final Listener mChannelDataManagerListener =
+ new Listener() {
+ @Override
+ public void onLoadFinished() {
+ setupTimer();
+ }
- @Override
- public void onChannelListUpdated() {
- setupTimer();
- }
+ @Override
+ public void onChannelListUpdated() {
+ setupTimer();
+ }
- @Override
- public void onChannelBrowsableChanged() { }
- };
+ @Override
+ public void onChannelBrowsableChanged() {}
+ };
private boolean mStarted;
private ScanTimeoutMonitor(Context context) {
mContext = context.getApplicationContext();
- mChannelDataManager = TvApplication.getSingletons(context).getChannelDataManager();
+ mChannelDataManager = TvSingletons.getSingletons(context).getChannelDataManager();
}
private void startMonitoring() {
@@ -215,7 +235,7 @@ public class SetupPassthroughActivity extends Activity {
private void onScanTimedOut() {
stopMonitoring();
- EpgFetcher.getInstance(mContext).onChannelScanFinished();
+ TvSingletons.getSingletons(mContext).getEpgFetcher().onChannelScanFinished();
}
}
}
diff --git a/src/com/android/tv/Starter.java b/src/com/android/tv/Starter.java
new file mode 100644
index 00000000..22fda0bd
--- /dev/null
+++ b/src/com/android/tv/Starter.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.content.Context;
+import android.util.Log;
+
+/** Initializes TvApplication. */
+public interface Starter {
+
+ /**
+ * Initializes TvApplication.
+ *
+ * <p>Note: it should be called at the beginning of any Service.onCreate, Activity.onCreate, or
+ * BroadcastReceiver.onCreate.
+ */
+ static void start(Context context) {
+ // TODO(b/63064354) TvApplication should not have to know if it is "the main process"
+ if (context.getApplicationContext() instanceof Starter) {
+ Starter starter = (Starter) context.getApplicationContext();
+ starter.start();
+ } else {
+ // Application context can be MockTvApplication.
+ Log.w("Start", "It is not a context of TvApplication");
+ }
+ }
+
+ void start();
+}
diff --git a/src/com/android/tv/TimeShiftManager.java b/src/com/android/tv/TimeShiftManager.java
index 70885936..bb3574d7 100644
--- a/src/com/android/tv/TimeShiftManager.java
+++ b/src/com/android/tv/TimeShiftManager.java
@@ -27,20 +27,18 @@ import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
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.data.Channel;
import com.android.tv.data.OnCurrentProgramUpdatedListener;
import com.android.tv.data.Program;
import com.android.tv.data.ProgramDataManager;
+import com.android.tv.data.api.Channel;
import com.android.tv.ui.TunableTvView;
-import com.android.tv.ui.TunableTvView.TimeShiftListener;
+import com.android.tv.ui.TunableTvViewPlayingApi.TimeShiftListener;
import com.android.tv.util.AsyncDbTask;
import com.android.tv.util.TimeShiftUtils;
import com.android.tv.util.Utils;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -53,11 +51,11 @@ import java.util.Queue;
import java.util.concurrent.TimeUnit;
/**
- * A class which manages the time shift feature in Live TV. It consists of two parts.
- * {@link PlayController} controls the playback such as play/pause, rewind and fast-forward using
- * {@link TunableTvView} which communicates with TvInputService through
- * {@link android.media.tv.TvInputService.Session}.
- * {@link ProgramManager} loads programs of the current channel in the background.
+ * A class which manages the time shift feature in Live TV. It consists of two parts. {@link
+ * PlayController} controls the playback such as play/pause, rewind and fast-forward using {@link
+ * TunableTvView} which communicates with TvInputService through {@link
+ * android.media.tv.TvInputService.Session}. {@link ProgramManager} loads programs of the current
+ * channel in the background.
*/
public class TimeShiftManager {
private static final String TAG = "TimeShiftManager";
@@ -66,12 +64,14 @@ public class TimeShiftManager {
@Retention(RetentionPolicy.SOURCE)
@IntDef({PLAY_STATUS_PAUSED, PLAY_STATUS_PLAYING})
public @interface PlayStatus {}
- public static final int PLAY_STATUS_PAUSED = 0;
+
+ public static final int PLAY_STATUS_PAUSED = 0;
public static final int PLAY_STATUS_PLAYING = 1;
@Retention(RetentionPolicy.SOURCE)
@IntDef({PLAY_SPEED_1X, PLAY_SPEED_2X, PLAY_SPEED_3X, PLAY_SPEED_4X, PLAY_SPEED_5X})
- public @interface PlaySpeed{}
+ public @interface PlaySpeed {}
+
public static final int PLAY_SPEED_1X = 1;
public static final int PLAY_SPEED_2X = 2;
public static final int PLAY_SPEED_3X = 3;
@@ -80,15 +80,25 @@ public class TimeShiftManager {
@Retention(RetentionPolicy.SOURCE)
@IntDef({PLAY_DIRECTION_FORWARD, PLAY_DIRECTION_BACKWARD})
- public @interface PlayDirection{}
- public static final int PLAY_DIRECTION_FORWARD = 0;
+ public @interface PlayDirection {}
+
+ public static final int PLAY_DIRECTION_FORWARD = 0;
public static final int PLAY_DIRECTION_BACKWARD = 1;
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, value = {TIME_SHIFT_ACTION_ID_PLAY, TIME_SHIFT_ACTION_ID_PAUSE,
- TIME_SHIFT_ACTION_ID_REWIND, TIME_SHIFT_ACTION_ID_FAST_FORWARD,
- TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS, TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT})
- public @interface TimeShiftActionId{}
+ @IntDef(
+ flag = true,
+ value = {
+ TIME_SHIFT_ACTION_ID_PLAY,
+ TIME_SHIFT_ACTION_ID_PAUSE,
+ TIME_SHIFT_ACTION_ID_REWIND,
+ TIME_SHIFT_ACTION_ID_FAST_FORWARD,
+ TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS,
+ TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT
+ }
+ )
+ public @interface TimeShiftActionId {}
+
public static final int TIME_SHIFT_ACTION_ID_PLAY = 1;
public static final int TIME_SHIFT_ACTION_ID_PAUSE = 1 << 1;
public static final int TIME_SHIFT_ACTION_ID_REWIND = 1 << 2;
@@ -100,8 +110,7 @@ public class TimeShiftManager {
private static final int MSG_PREFETCH_PROGRAM = 1001;
private static final long REQUEST_CURRENT_POSITION_INTERVAL = TimeUnit.SECONDS.toMillis(1);
private static final long MAX_DUMMY_PROGRAM_DURATION = TimeUnit.MINUTES.toMillis(30);
- @VisibleForTesting
- static final long INVALID_TIME = -1;
+ @VisibleForTesting static final long INVALID_TIME = -1;
static final long CURRENT_TIME = -2;
private static final long PREFETCH_TIME_OFFSET_FROM_PROGRAM_END = TimeUnit.MINUTES.toMillis(1);
private static final long PREFETCH_DURATION_FOR_NEXT = TimeUnit.HOURS.toMillis(2);
@@ -109,57 +118,57 @@ public class TimeShiftManager {
private static final long ALLOWED_START_TIME_OFFSET = TimeUnit.DAYS.toMillis(14);
private static final long TWO_WEEKS_MS = TimeUnit.DAYS.toMillis(14);
- @VisibleForTesting
- static final long REQUEST_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(3);
+ @VisibleForTesting static final long REQUEST_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(3);
/**
* If the user presses the {@link android.view.KeyEvent#KEYCODE_MEDIA_PREVIOUS} button within
* this threshold from the program start time, the play position moves to the start of the
- * previous program.
- * Otherwise, the play position moves to the start of the current program.
+ * previous program. Otherwise, the play position moves to the start of the current program.
* This value is specified in the UX document.
*/
private static final long PROGRAM_START_TIME_THRESHOLD = TimeUnit.SECONDS.toMillis(3);
/**
* If the current position enters within this range from the recording start time, rewind action
- * and jump to previous action is disabled.
- * Similarly, if the current position enters within this range from the current system time,
- * fast forward action and jump to next action is disabled.
- * It must be three times longer than {@link #REQUEST_CURRENT_POSITION_INTERVAL} at least.
+ * and jump to previous action is disabled. Similarly, if the current position enters within
+ * this range from the current system time, fast forward action and jump to next action is
+ * disabled. It must be three times longer than {@link #REQUEST_CURRENT_POSITION_INTERVAL} at
+ * least.
*/
private static final long DISABLE_ACTION_THRESHOLD = 3 * REQUEST_CURRENT_POSITION_INTERVAL;
/**
* If the current position goes out of this range from the recording start time, rewind action
- * and jump to previous action is enabled.
- * Similarly, if the current position goes out of this range from the current system time,
- * fast forward action and jump to next action is enabled.
- * Enable threshold and disable threshold must be different because the current position
- * does not have the continuous value. It changes every one second.
+ * and jump to previous action is enabled. Similarly, if the current position goes out of this
+ * range from the current system time, fast forward action and jump to next action is enabled.
+ * Enable threshold and disable threshold must be different because the current position does
+ * not have the continuous value. It changes every one second.
*/
private static final long ENABLE_ACTION_THRESHOLD =
DISABLE_ACTION_THRESHOLD + 3 * REQUEST_CURRENT_POSITION_INTERVAL;
/**
- * The current position sent from TIS can not be exactly the same as the current system time
- * due to the elapsed time to pass the message from TIS to Live TV.
- * So the boundary threshold is necessary.
- * The same goes for the recording start time.
- * It's the same {@link #REQUEST_CURRENT_POSITION_INTERVAL}.
+ * The current position sent from TIS can not be exactly the same as the current system time due
+ * to the elapsed time to pass the message from TIS to Live TV. So the boundary threshold
+ * is necessary. The same goes for the recording start time. It's the same {@link
+ * #REQUEST_CURRENT_POSITION_INTERVAL}.
*/
private static final long RECORDING_BOUNDARY_THRESHOLD = REQUEST_CURRENT_POSITION_INTERVAL;
private final PlayController mPlayController;
private final ProgramManager mProgramManager;
private final Tracker mTracker;
+
@VisibleForTesting
final CurrentPositionMediator mCurrentPositionMediator = new CurrentPositionMediator();
private Listener mListener;
private final OnCurrentProgramUpdatedListener mOnCurrentProgramUpdatedListener;
- private int mEnabledActionIds = TIME_SHIFT_ACTION_ID_PLAY | TIME_SHIFT_ACTION_ID_PAUSE
- | TIME_SHIFT_ACTION_ID_REWIND | TIME_SHIFT_ACTION_ID_FAST_FORWARD
- | TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS | TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT;
- @TimeShiftActionId
- private int mLastActionId = 0;
+ private int mEnabledActionIds =
+ TIME_SHIFT_ACTION_ID_PLAY
+ | TIME_SHIFT_ACTION_ID_PAUSE
+ | TIME_SHIFT_ACTION_ID_REWIND
+ | TIME_SHIFT_ACTION_ID_FAST_FORWARD
+ | TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS
+ | TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT;
+ @TimeShiftActionId private int mLastActionId = 0;
private final Context mContext;
@@ -169,8 +178,11 @@ public class TimeShiftManager {
private final Handler mHandler = new TimeShiftHandler(this);
- public TimeShiftManager(Context context, TunableTvView tvView,
- ProgramDataManager programDataManager, Tracker tracker,
+ public TimeShiftManager(
+ Context context,
+ TunableTvView tvView,
+ ProgramDataManager programDataManager,
+ Tracker tracker,
OnCurrentProgramUpdatedListener onCurrentProgramUpdatedListener) {
mContext = context;
mPlayController = new PlayController(tvView);
@@ -179,23 +191,17 @@ public class TimeShiftManager {
mOnCurrentProgramUpdatedListener = onCurrentProgramUpdatedListener;
}
- /**
- * Sets a listener which will receive events from this class.
- */
+ /** Sets a listener which will receive events from this class. */
public void setListener(Listener listener) {
mListener = listener;
}
- /**
- * Checks if the trick play is available for the current channel.
- */
+ /** Checks if the trick play is available for the current channel. */
public boolean isAvailable() {
return mPlayController.mAvailable;
}
- /**
- * Returns the current time position in milliseconds.
- */
+ /** Returns the current time position in milliseconds. */
public long getCurrentPositionMs() {
return mCurrentPositionMediator.mCurrentPositionMs;
}
@@ -204,18 +210,15 @@ public class TimeShiftManager {
mCurrentPositionMediator.onCurrentPositionChanged(currentTimeMs);
}
- /**
- * Returns the start time of the recording in milliseconds.
- */
+ /** Returns the start time of the recording in milliseconds. */
public long getRecordStartTimeMs() {
long oldestProgramStartTime = mProgramManager.getOldestProgramStartTime();
- return oldestProgramStartTime == INVALID_TIME ? INVALID_TIME
+ return oldestProgramStartTime == INVALID_TIME
+ ? INVALID_TIME
: mPlayController.mRecordStartTimeMs;
}
- /**
- * Returns the end time of the recording in milliseconds.
- */
+ /** Returns the end time of the recording in milliseconds. */
public long getRecordEndTimeMs() {
if (mPlayController.mRecordEndTimeMs == CURRENT_TIME) {
return System.currentTimeMillis();
@@ -264,9 +267,9 @@ public class TimeShiftManager {
}
/**
- * Plays the media in backward direction. The playback speed is increased by 1x each time
- * this is called. The range of the speed is from 2x to 5x.
- * If the playing position is considered the same as the record start time, it does nothing
+ * Plays the media in backward direction. The playback speed is increased by 1x each time this
+ * is called. The range of the speed is from 2x to 5x. If the playing position is considered the
+ * same as the record start time, it does nothing
*
* @throws IllegalStateException if the trick play is not available.
*/
@@ -281,9 +284,9 @@ public class TimeShiftManager {
}
/**
- * Plays the media in forward direction. The playback speed is increased by 1x each time
- * this is called. The range of the speed is from 2x to 5x.
- * If the playing position is the same as the current time, it does nothing.
+ * Plays the media in forward direction. The playback speed is increased by 1x each time this is
+ * called. The range of the speed is from 2x to 5x. If the playing position is the same as the
+ * current time, it does nothing.
*
* @throws IllegalStateException if the trick play is not available.
*/
@@ -298,11 +301,10 @@ public class TimeShiftManager {
}
/**
- * Jumps to the start of the current program.
- * If the currently playing position is within 3 seconds
- * (={@link #PROGRAM_START_TIME_THRESHOLD})from the start time of the program, it goes to
- * the start of the previous program if exists.
- * If the playing position is the same as the record start time, it does nothing.
+ * Jumps to the start of the current program. If the currently playing position is within 3
+ * seconds (={@link #PROGRAM_START_TIME_THRESHOLD})from the start time of the program, it goes
+ * to the start of the previous program if exists. If the playing position is the same as the
+ * record start time, it does nothing.
*
* @throws IllegalStateException if the trick play is not available.
*/
@@ -310,8 +312,9 @@ public class TimeShiftManager {
if (!isActionEnabled(TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS)) {
return;
}
- Program program = mProgramManager.getProgramAt(
- mCurrentPositionMediator.mCurrentPositionMs - PROGRAM_START_TIME_THRESHOLD);
+ Program program =
+ mProgramManager.getProgramAt(
+ mCurrentPositionMediator.mCurrentPositionMs - PROGRAM_START_TIME_THRESHOLD);
if (program == null) {
return;
}
@@ -325,9 +328,9 @@ public class TimeShiftManager {
}
/**
- * Jumps to the start of the next program if exists.
- * If there's no next program, it jumps to the current system time and shows the live TV.
- * If the playing position is considered the same as the current time, it does nothing.
+ * Jumps to the start of the next program if exists. If there's no next program, it jumps to the
+ * current system time and shows the live TV. If the playing position is considered the same as
+ * the current time, it does nothing.
*
* @throws IllegalStateException if the trick play is not available.
*/
@@ -335,8 +338,8 @@ public class TimeShiftManager {
if (!isActionEnabled(TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT)) {
return;
}
- Program currentProgram = mProgramManager.getProgramAt(
- mCurrentPositionMediator.mCurrentPositionMs);
+ Program currentProgram =
+ mProgramManager.getProgramAt(mCurrentPositionMediator.mCurrentPositionMs);
if (currentProgram == null) {
return;
}
@@ -362,10 +365,9 @@ public class TimeShiftManager {
updateActions();
}
- /**
- * Returns the playback status. The value is PLAY_STATUS_PAUSED or PLAY_STATUS_PLAYING.
- */
- @PlayStatus public int getPlayStatus() {
+ /** Returns the playback status. The value is PLAY_STATUS_PAUSED or PLAY_STATUS_PLAYING. */
+ @PlayStatus
+ public int getPlayStatus() {
return mPlayController.mPlayStatus;
}
@@ -373,27 +375,26 @@ public class TimeShiftManager {
* Returns the displayed playback speed. The value is one of PLAY_SPEED_1X, PLAY_SPEED_2X,
* PLAY_SPEED_3X, PLAY_SPEED_4X and PLAY_SPEED_5X.
*/
- @PlaySpeed public int getDisplayedPlaySpeed() {
+ @PlaySpeed
+ public int getDisplayedPlaySpeed() {
return mPlayController.mDisplayedPlaySpeed;
}
/**
* Returns the playback speed. The value is PLAY_DIRECTION_FORWARD or PLAY_DIRECTION_BACKWARD.
*/
- @PlayDirection public int getPlayDirection() {
+ @PlayDirection
+ public int getPlayDirection() {
return mPlayController.mPlayDirection;
}
- /**
- * Returns the ID of the last action..
- */
- @TimeShiftActionId public int getLastActionId() {
+ /** Returns the ID of the last action.. */
+ @TimeShiftActionId
+ public int getLastActionId() {
return mLastActionId;
}
- /**
- * Enables or disables the time-shift actions.
- */
+ /** Enables or disables the time-shift actions. */
@VisibleForTesting
void enableAction(@TimeShiftActionId int actionId, boolean enable) {
int oldEnabledActionIds = mEnabledActionIds;
@@ -402,8 +403,7 @@ public class TimeShiftManager {
} else {
mEnabledActionIds &= ~actionId;
}
- if (mNotificationEnabled && mListener != null
- && oldEnabledActionIds != mEnabledActionIds) {
+ if (mNotificationEnabled && mListener != null && oldEnabledActionIds != mEnabledActionIds) {
mListener.onActionEnabledChanged(actionId, enable);
}
}
@@ -417,17 +417,22 @@ public class TimeShiftManager {
enableAction(TIME_SHIFT_ACTION_ID_PLAY, true);
enableAction(TIME_SHIFT_ACTION_ID_PAUSE, true);
// Rewind action and jump to previous action.
- long threshold = isActionEnabled(TIME_SHIFT_ACTION_ID_REWIND)
- ? DISABLE_ACTION_THRESHOLD : ENABLE_ACTION_THRESHOLD;
- boolean enabled = mCurrentPositionMediator.mCurrentPositionMs
- - mPlayController.mRecordStartTimeMs > threshold;
+ long threshold =
+ isActionEnabled(TIME_SHIFT_ACTION_ID_REWIND)
+ ? DISABLE_ACTION_THRESHOLD
+ : ENABLE_ACTION_THRESHOLD;
+ boolean enabled =
+ mCurrentPositionMediator.mCurrentPositionMs - mPlayController.mRecordStartTimeMs
+ > threshold;
enableAction(TIME_SHIFT_ACTION_ID_REWIND, enabled);
enableAction(TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS, enabled);
// Fast forward action and jump to next action
- threshold = isActionEnabled(TIME_SHIFT_ACTION_ID_FAST_FORWARD)
- ? DISABLE_ACTION_THRESHOLD : ENABLE_ACTION_THRESHOLD;
- enabled = getRecordEndTimeMs() - mCurrentPositionMediator.mCurrentPositionMs
- > threshold;
+ threshold =
+ isActionEnabled(TIME_SHIFT_ACTION_ID_FAST_FORWARD)
+ ? DISABLE_ACTION_THRESHOLD
+ : ENABLE_ACTION_THRESHOLD;
+ enabled =
+ getRecordEndTimeMs() - mCurrentPositionMediator.mCurrentPositionMs > threshold;
enableAction(TIME_SHIFT_ACTION_ID_FAST_FORWARD, enabled);
enableAction(TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT, enabled);
} else {
@@ -444,7 +449,7 @@ public class TimeShiftManager {
SoftPreconditions.checkState(isAvailable(), TAG, "Time shift is not available");
SoftPreconditions.checkState(mCurrentPositionMediator.mCurrentPositionMs != INVALID_TIME);
Program currentProgram = getProgramAt(mCurrentPositionMediator.mCurrentPositionMs);
- if (!Program.isValid(currentProgram)) {
+ if (!Program.isProgramValid(currentProgram)) {
currentProgram = null;
}
if (!Objects.equals(mCurrentProgram, currentProgram)) {
@@ -453,8 +458,8 @@ public class TimeShiftManager {
if (mNotificationEnabled && mOnCurrentProgramUpdatedListener != null) {
Channel channel = mPlayController.getCurrentChannel();
if (channel != null) {
- mOnCurrentProgramUpdatedListener.onCurrentProgramUpdated(channel.getId(),
- mCurrentProgram);
+ mOnCurrentProgramUpdatedListener.onCurrentProgramUpdated(
+ channel.getId(), mCurrentProgram);
mPlayController.onCurrentProgramChanged();
}
}
@@ -472,16 +477,12 @@ public class TimeShiftManager {
&& mPlayController.mDisplayedPlaySpeed == PLAY_SPEED_1X;
}
- /**
- * Checks if the trick play is available and it's playback status is paused.
- */
+ /** Checks if the trick play is available and it's playback status is paused. */
public boolean isPaused() {
return mPlayController.mAvailable && mPlayController.mPlayStatus == PLAY_STATUS_PAUSED;
}
- /**
- * Returns the program which airs at the given time.
- */
+ /** Returns the program which airs at the given time. */
@NonNull
public Program getProgramAt(long timeMs) {
Program program = mProgramManager.getProgramAt(timeMs);
@@ -495,8 +496,10 @@ public class TimeShiftManager {
void onAvailabilityChanged() {
mCurrentPositionMediator.initialize(mPlayController.mRecordStartTimeMs);
- mProgramManager.onAvailabilityChanged(mPlayController.mAvailable,
- mPlayController.getCurrentChannel(), mPlayController.mRecordStartTimeMs);
+ mProgramManager.onAvailabilityChanged(
+ mPlayController.mAvailable,
+ mPlayController.getCurrentChannel(),
+ mPlayController.mRecordStartTimeMs);
updateActions();
// Availability change notification should be always sent
// even if mNotificationEnabled is false.
@@ -507,8 +510,8 @@ public class TimeShiftManager {
void onRecordTimeRangeChanged() {
if (mPlayController.mAvailable) {
- mProgramManager.onRecordTimeRangeChanged(mPlayController.mRecordStartTimeMs,
- mPlayController.mRecordEndTimeMs);
+ mProgramManager.onRecordTimeRangeChanged(
+ mPlayController.mRecordStartTimeMs, mPlayController.mRecordEndTimeMs);
}
updateActions();
if (mNotificationEnabled && mListener != null) {
@@ -538,10 +541,10 @@ public class TimeShiftManager {
}
/**
- * Returns the current program which airs right now.<p>
+ * Returns the current program which airs right now.
*
- * If the program is a dummy program, which means there's no program information,
- * returns {@code null}.
+ * <p>If the program is a dummy program, which means there's no program information, returns
+ * {@code null}.
*/
@Nullable
public Program getCurrentProgram() {
@@ -558,8 +561,10 @@ public class TimeShiftManager {
long durationMs =
(getCurrentProgram() == null ? 0 : getCurrentProgram().getDurationMillis());
if (mPlayController.mDisplayedPlaySpeed > PLAY_SPEED_5X) {
- Log.w(TAG, "Unknown displayed play speed is chosen : "
- + mPlayController.mDisplayedPlaySpeed);
+ Log.w(
+ TAG,
+ "Unknown displayed play speed is chosen : "
+ + mPlayController.mDisplayedPlaySpeed);
return TimeShiftUtils.getMaxPlaybackSpeed(durationMs);
} else {
return TimeShiftUtils.getPlaybackSpeed(
@@ -568,9 +573,7 @@ public class TimeShiftManager {
}
}
- /**
- * A class which controls the trick play.
- */
+ /** A class which controls the trick play. */
private class PlayController {
private final TunableTvView mTvView;
@@ -585,69 +588,87 @@ public class TimeShiftManager {
private boolean mAvailable;
/**
- * Indicates that the trick play is not playing the current time position.
- * It is set true when {@link PlayController#pause}, {@link PlayController#rewind},
- * {@link PlayController#fastForward} and {@link PlayController#seekTo}
- * is called.
- * If it is true, the current time is equal to System.currentTimeMillis().
+ * Indicates that the trick play is not playing the current time position. It is set true
+ * when {@link PlayController#pause}, {@link PlayController#rewind}, {@link
+ * PlayController#fastForward} and {@link PlayController#seekTo} is called. If it is true,
+ * the current time is equal to System.currentTimeMillis().
*/
private boolean mIsPlayOffsetChanged;
PlayController(TunableTvView tvView) {
mTvView = tvView;
- mTvView.setTimeShiftListener(new TimeShiftListener() {
- @Override
- public void onAvailabilityChanged() {
- if (DEBUG) {
- Log.d(TAG, "onAvailabilityChanged(available="
- + mTvView.isTimeShiftAvailable() + ")");
- }
- PlayController.this.onAvailabilityChanged();
- }
+ mTvView.setTimeShiftListener(
+ new TimeShiftListener() {
+ @Override
+ public void onAvailabilityChanged() {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "onAvailabilityChanged(available="
+ + mTvView.isTimeShiftAvailable()
+ + ")");
+ }
+ PlayController.this.onAvailabilityChanged();
+ }
- @Override
- public void onRecordStartTimeChanged(long recordStartTimeMs) {
- if (!SoftPreconditions.checkState(mAvailable, TAG,
- "Trick play is not available.")) {
- return;
- }
- if (recordStartTimeMs < mAvailablityChangedTimeMs - ALLOWED_START_TIME_OFFSET) {
- Log.e(TAG, "The start time is too earlier than the time of availability: {"
- + "startTime: " + recordStartTimeMs + ", availability: "
- + mAvailablityChangedTimeMs);
- return;
- }
- if (recordStartTimeMs > System.currentTimeMillis()) {
- // The time reported by TvInputService might not consistent with system
- // clock,, use system's current time instead.
- Log.e(TAG, "The start time should not be earlier than the current time, "
- + "reset the start time to the system's current time: {"
- + "startTime: " + recordStartTimeMs + ", current time: "
- + System.currentTimeMillis());
- recordStartTimeMs = System.currentTimeMillis();
- }
- if (mRecordStartTimeMs == recordStartTimeMs) {
- return;
- }
- mRecordStartTimeMs = recordStartTimeMs;
- TimeShiftManager.this.onRecordTimeRangeChanged();
-
- // According to the UX guidelines, the stream should be resumed if the
- // recording buffer fills up while paused, which means that the current time
- // position is the same as or before the recording start time.
- // But, for this application and the TIS, it's an erroneous and confusing
- // situation if the current time position is before the recording start time.
- // So, we recommend the TIS to keep the current time position greater than or
- // equal to the recording start time.
- // And here, we assume that the buffer is full if the current time position
- // is nearly equal to the recording start time.
- if (mPlayStatus == PLAY_STATUS_PAUSED &&
- getCurrentPositionMs() - mRecordStartTimeMs
- < RECORDING_BOUNDARY_THRESHOLD) {
- TimeShiftManager.this.play();
- }
- }
- });
+ @Override
+ public void onRecordStartTimeChanged(long recordStartTimeMs) {
+ if (!SoftPreconditions.checkState(
+ mAvailable, TAG, "Trick play is not available.")) {
+ return;
+ }
+ if (recordStartTimeMs
+ < mAvailablityChangedTimeMs - ALLOWED_START_TIME_OFFSET) {
+ Log.e(
+ TAG,
+ "The start time is too earlier than the time of availability: {"
+ + "startTime: "
+ + recordStartTimeMs
+ + ", availability: "
+ + mAvailablityChangedTimeMs);
+ return;
+ }
+ if (recordStartTimeMs > System.currentTimeMillis()) {
+ // The time reported by TvInputService might not consistent with
+ // system
+ // clock,, use system's current time instead.
+ Log.e(
+ TAG,
+ "The start time should not be earlier than the current time, "
+ + "reset the start time to the system's current time: {"
+ + "startTime: "
+ + recordStartTimeMs
+ + ", current time: "
+ + System.currentTimeMillis());
+ recordStartTimeMs = System.currentTimeMillis();
+ }
+ if (mRecordStartTimeMs == recordStartTimeMs) {
+ return;
+ }
+ mRecordStartTimeMs = recordStartTimeMs;
+ TimeShiftManager.this.onRecordTimeRangeChanged();
+
+ // According to the UX guidelines, the stream should be resumed if the
+ // recording buffer fills up while paused, which means that the current
+ // time
+ // position is the same as or before the recording start time.
+ // But, for this application and the TIS, it's an erroneous and
+ // confusing
+ // situation if the current time position is before the recording start
+ // time.
+ // So, we recommend the TIS to keep the current time position greater
+ // than or
+ // equal to the recording start time.
+ // And here, we assume that the buffer is full if the current time
+ // position
+ // is nearly equal to the recording start time.
+ if (mPlayStatus == PLAY_STATUS_PAUSED
+ && getCurrentPositionMs() - mRecordStartTimeMs
+ < RECORDING_BOUNDARY_THRESHOLD) {
+ TimeShiftManager.this.play();
+ }
+ }
+ });
}
void onAvailabilityChanged() {
@@ -672,8 +693,8 @@ public class TimeShiftManager {
mRecordEndTimeMs = CURRENT_TIME;
// When the media availability message has come.
mPlayController.setPlayStatus(PLAY_STATUS_PLAYING);
- mHandler.sendEmptyMessageDelayed(MSG_GET_CURRENT_POSITION,
- REQUEST_CURRENT_POSITION_INTERVAL);
+ mHandler.sendEmptyMessageDelayed(
+ MSG_GET_CURRENT_POSITION, REQUEST_CURRENT_POSITION_INTERVAL);
} else {
mAvailablityChangedTimeMs = INVALID_TIME;
mIsPlayOffsetChanged = false;
@@ -688,11 +709,14 @@ public class TimeShiftManager {
void handleGetCurrentPosition() {
if (mIsPlayOffsetChanged) {
- long currentTimeMs = mRecordEndTimeMs == CURRENT_TIME ? System.currentTimeMillis()
- : mRecordEndTimeMs;
- long currentPositionMs = Math.max(
- Math.min(mTvView.timeshiftGetCurrentPositionMs(), currentTimeMs),
- mRecordStartTimeMs);
+ long currentTimeMs =
+ mRecordEndTimeMs == CURRENT_TIME
+ ? System.currentTimeMillis()
+ : mRecordEndTimeMs;
+ long currentPositionMs =
+ Math.max(
+ Math.min(mTvView.timeshiftGetCurrentPositionMs(), currentTimeMs),
+ mRecordStartTimeMs);
boolean isCurrentTime =
currentTimeMs - currentPositionMs < RECORDING_BOUNDARY_THRESHOLD;
long newCurrentPositionMs;
@@ -708,8 +732,8 @@ public class TimeShiftManager {
}
} else {
newCurrentPositionMs = currentPositionMs;
- boolean isRecordStartTime = currentPositionMs - mRecordStartTimeMs
- < RECORDING_BOUNDARY_THRESHOLD;
+ boolean isRecordStartTime =
+ currentPositionMs - mRecordStartTimeMs < RECORDING_BOUNDARY_THRESHOLD;
if (isRecordStartTime && isRewinding()) {
TimeShiftManager.this.play();
}
@@ -721,8 +745,8 @@ public class TimeShiftManager {
}
// Need to send message here just in case there is no or invalid response
// for the current time position request from TIS.
- mHandler.sendEmptyMessageDelayed(MSG_GET_CURRENT_POSITION,
- REQUEST_CURRENT_POSITION_INTERVAL);
+ mHandler.sendEmptyMessageDelayed(
+ MSG_GET_CURRENT_POSITION, REQUEST_CURRENT_POSITION_INTERVAL);
}
void play() {
@@ -777,12 +801,13 @@ public class TimeShiftManager {
mIsPlayOffsetChanged = true;
}
- /**
- * Moves to the specified time.
- */
+ /** Moves to the specified time. */
void seekTo(long timeMs) {
- mTvView.timeshiftSeekTo(Math.min(mRecordEndTimeMs == CURRENT_TIME
- ? System.currentTimeMillis() : mRecordEndTimeMs,
+ mTvView.timeshiftSeekTo(
+ Math.min(
+ mRecordEndTimeMs == CURRENT_TIME
+ ? System.currentTimeMillis()
+ : mRecordEndTimeMs,
Math.max(mRecordStartTimeMs, timeMs)));
mIsPlayOffsetChanged = true;
}
@@ -853,8 +878,15 @@ public class TimeShiftManager {
void onAvailabilityChanged(boolean available, Channel channel, long currentPositionMs) {
if (DEBUG) {
- Log.d(TAG, "onAvailabilityChanged(" + available + "+," + channel + ", "
- + currentPositionMs + ")");
+ Log.d(
+ TAG,
+ "onAvailabilityChanged("
+ + available
+ + "+,"
+ + channel
+ + ", "
+ + currentPositionMs
+ + ")");
}
mProgramLoadQueue.clear();
@@ -875,12 +907,14 @@ public class TimeShiftManager {
mPrograms.add(program);
prefetchStartTimeMs = program.getEndTimeUtcMillis();
} else {
- prefetchStartTimeMs = Utils.floorTime(currentPositionMs,
- MAX_DUMMY_PROGRAM_DURATION);
+ prefetchStartTimeMs =
+ Utils.floorTime(currentPositionMs, MAX_DUMMY_PROGRAM_DURATION);
}
// Create dummy program
- mPrograms.addAll(createDummyPrograms(prefetchStartTimeMs,
- currentPositionMs + PREFETCH_DURATION_FOR_NEXT));
+ mPrograms.addAll(
+ createDummyPrograms(
+ prefetchStartTimeMs,
+ currentPositionMs + PREFETCH_DURATION_FOR_NEXT));
schedulePrefetchPrograms();
TimeShiftManager.this.onProgramInfoChanged();
}
@@ -895,8 +929,9 @@ public class TimeShiftManager {
}
long fetchStartTimeMs = Utils.floorTime(startTimeMs, MAX_DUMMY_PROGRAM_DURATION);
- long fetchEndTimeMs = Utils.ceilTime(endTimeMs + PREFETCH_DURATION_FOR_NEXT,
- MAX_DUMMY_PROGRAM_DURATION);
+ long fetchEndTimeMs =
+ Utils.ceilTime(
+ endTimeMs + PREFETCH_DURATION_FOR_NEXT, MAX_DUMMY_PROGRAM_DURATION);
removeOutdatedPrograms(fetchStartTimeMs);
boolean needToLoad = addDummyPrograms(fetchStartTimeMs, fetchEndTimeMs);
if (needToLoad) {
@@ -934,16 +969,16 @@ public class TimeShiftManager {
Range<Long> next = mProgramLoadQueue.poll();
// Extend next to include any overlapping Ranges.
Iterator<Range<Long>> i = mProgramLoadQueue.iterator();
- while(i.hasNext()) {
+ while (i.hasNext()) {
Range<Long> r = i.next();
- if(next.contains(r.getLower()) || next.contains(r.getUpper())){
+ if (next.contains(r.getLower()) || next.contains(r.getUpper())) {
i.remove();
next = next.extend(r);
}
}
if (mChannel != null) {
- mProgramLoadTask = new LoadProgramsForCurrentChannelTask(
- mContext.getContentResolver(), next);
+ mProgramLoadTask =
+ new LoadProgramsForCurrentChannelTask(mContext.getContentResolver(), next);
mProgramLoadTask.executeOnDbThread();
}
}
@@ -969,10 +1004,12 @@ public class TimeShiftManager {
if (!firstProgram.isValid()) {
// Already the firstProgram is dummy.
mPrograms.remove(0);
- mPrograms.addAll(0,
+ mPrograms.addAll(
+ 0,
createDummyPrograms(startTimeMs, firstProgram.getEndTimeUtcMillis()));
} else {
- mPrograms.addAll(0,
+ mPrograms.addAll(
+ 0,
createDummyPrograms(startTimeMs, firstProgram.getStartTimeUtcMillis()));
}
added = true;
@@ -1055,10 +1092,12 @@ public class TimeShiftManager {
// to show the time-line duration of {@link MAX_DUMMY_PROGRAM_DURATION} at most
// for a dummy program.
private List<Program> createDummyPrograms(long startTimeMs, long endTimeMs) {
- SoftPreconditions.checkArgument(endTimeMs - startTimeMs <= TWO_WEEKS_MS, TAG,
- "createDummyProgram: long duration of dummy programs are requested ("
- + Utils.toTimeString(startTimeMs) + ", "
- + Utils.toTimeString(endTimeMs));
+ SoftPreconditions.checkArgument(
+ endTimeMs - startTimeMs <= TWO_WEEKS_MS,
+ TAG,
+ "createDummyProgram: long duration of dummy programs are requested ( %s , %s)",
+ Utils.toTimeString(startTimeMs),
+ Utils.toTimeString(endTimeMs));
if (startTimeMs >= endTimeMs) {
return Collections.emptyList();
}
@@ -1066,17 +1105,19 @@ public class TimeShiftManager {
long start = startTimeMs;
long end = Utils.ceilTime(startTimeMs, MAX_DUMMY_PROGRAM_DURATION);
while (end < endTimeMs) {
- programs.add(new Program.Builder()
- .setStartTimeUtcMillis(start)
- .setEndTimeUtcMillis(end)
- .build());
+ programs.add(
+ new Program.Builder()
+ .setStartTimeUtcMillis(start)
+ .setEndTimeUtcMillis(end)
+ .build());
start = end;
end += MAX_DUMMY_PROGRAM_DURATION;
}
- programs.add(new Program.Builder()
- .setStartTimeUtcMillis(start)
- .setEndTimeUtcMillis(endTimeMs)
- .build());
+ programs.add(
+ new Program.Builder()
+ .setStartTimeUtcMillis(start)
+ .setEndTimeUtcMillis(endTimeMs)
+ .build());
return programs;
}
@@ -1093,7 +1134,7 @@ public class TimeShiftManager {
if (program.getStartTimeUtcMillis() > timeMs) {
return getProgramAt(timeMs, start, mid - 1);
} else if (program.getEndTimeUtcMillis() <= timeMs) {
- return getProgramAt(timeMs, mid+1, end);
+ return getProgramAt(timeMs, mid + 1, end);
} else {
return program;
}
@@ -1125,8 +1166,10 @@ public class TimeShiftManager {
if (DEBUG) Log.d(TAG, "Last valid program = " + lastValidProgram);
final long delay;
if (lastValidProgram != null) {
- delay = lastValidProgram.getEndTimeUtcMillis()
- - PREFETCH_TIME_OFFSET_FROM_PROGRAM_END - System.currentTimeMillis();
+ delay =
+ lastValidProgram.getEndTimeUtcMillis()
+ - PREFETCH_TIME_OFFSET_FROM_PROGRAM_END
+ - System.currentTimeMillis();
} else {
// Since there might not be any program data delay the retry 5 seconds,
// then 30 seconds then 5 minutes
@@ -1145,7 +1188,8 @@ public class TimeShiftManager {
break;
}
if (DEBUG) {
- Log.d(TAG,
+ Log.d(
+ TAG,
"No last valid program. Already tried " + mEmptyFetchCount + " times");
}
}
@@ -1165,8 +1209,13 @@ public class TimeShiftManager {
long endTimeMs = System.currentTimeMillis() + PREFETCH_DURATION_FOR_NEXT;
if (startTimeMs <= endTimeMs) {
if (DEBUG) {
- Log.d(TAG, "Prefetch task starts: {startTime=" + Utils.toTimeString(startTimeMs)
- + ", endTime=" + Utils.toTimeString(endTimeMs) + "}");
+ Log.d(
+ TAG,
+ "Prefetch task starts: {startTime="
+ + Utils.toTimeString(startTimeMs)
+ + ", endTime="
+ + Utils.toTimeString(endTimeMs)
+ + "}");
}
mProgramLoadQueue.add(Range.create(startTimeMs, endTimeMs));
}
@@ -1176,20 +1225,28 @@ public class TimeShiftManager {
private class LoadProgramsForCurrentChannelTask
extends AsyncDbTask.LoadProgramsForChannelTask {
- LoadProgramsForCurrentChannelTask(ContentResolver contentResolver,
- Range<Long> period) {
- super(contentResolver, mChannel.getId(), period);
+ LoadProgramsForCurrentChannelTask(ContentResolver contentResolver, Range<Long> period) {
+ super(
+ TvSingletons.getSingletons(mContext).getDbExecutor(),
+ contentResolver,
+ mChannel.getId(),
+ period);
}
@Override
protected void onPostExecute(List<Program> programs) {
if (DEBUG) {
- Log.d(TAG, "Programs are loaded {channelId=" + mChannelId +
- ", from=" + Utils.toTimeString(mPeriod.getLower()) +
- ", to=" + Utils.toTimeString(mPeriod.getUpper()) +
- "}");
+ Log.d(
+ TAG,
+ "Programs are loaded {channelId="
+ + mChannelId
+ + ", from="
+ + Utils.toTimeString(mPeriod.getLower())
+ + ", to="
+ + Utils.toTimeString(mPeriod.getUpper())
+ + "}");
}
- //remove pending tasks that are fully satisfied by this query.
+ // remove pending tasks that are fully satisfied by this query.
Iterator<Range<Long>> it = mProgramLoadQueue.iterator();
while (it.hasNext()) {
Range<Long> r = it.next();
@@ -1207,14 +1264,14 @@ public class TimeShiftManager {
return;
}
mEmptyFetchCount = 0;
- if(!mPrograms.isEmpty()) {
+ 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()) {
+ while (program.getStartTimeUtcMillis()
+ > loadedProgram.getStartTimeUtcMillis()) {
mPrograms.add(i++, loadedProgram);
programs.remove(0);
if (programs.isEmpty()) {
@@ -1234,10 +1291,15 @@ public class TimeShiftManager {
@Override
protected void onCancelled(List<Program> programs) {
if (DEBUG) {
- Log.d(TAG, "Program loading has been canceled {channelId=" + (mChannel == null
- ? "null" : mChannelId) + ", from=" + Utils
- .toTimeString(mPeriod.getLower()) + ", to=" + Utils
- .toTimeString(mPeriod.getUpper()) + "}");
+ Log.d(
+ TAG,
+ "Program loading has been canceled {channelId="
+ + (mChannel == null ? "null" : mChannelId)
+ + ", from="
+ + Utils.toTimeString(mPeriod.getLower())
+ + ", to="
+ + Utils.toTimeString(mPeriod.getUpper())
+ + "}");
}
startNextLoadingIfNeeded();
}
@@ -1247,12 +1309,13 @@ public class TimeShiftManager {
mProgramLoadTask = null;
}
// Need to post to handler, because the task is still running.
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- startTaskIfNeeded();
- }
- });
+ mHandler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ startTaskIfNeeded();
+ }
+ });
}
boolean overlaps(Queue<Range<Long>> programLoadQueue) {
@@ -1299,11 +1362,11 @@ public class TimeShiftManager {
} else {
if (getPlayStatus() == PLAY_STATUS_PLAYING) {
if (getPlayDirection() == PLAY_DIRECTION_FORWARD) {
- mCurrentPositionMs += (currentTimeMs - mSeekRequestTimeMs)
- * getPlaybackSpeed();
+ mCurrentPositionMs +=
+ (currentTimeMs - mSeekRequestTimeMs) * getPlaybackSpeed();
} else {
- mCurrentPositionMs -= (currentTimeMs - mSeekRequestTimeMs)
- * getPlaybackSpeed();
+ mCurrentPositionMs -=
+ (currentTimeMs - mSeekRequestTimeMs) * getPlaybackSpeed();
}
}
TimeShiftManager.this.onCurrentPositionChanged();
@@ -1311,9 +1374,7 @@ public class TimeShiftManager {
}
}
- /**
- * The listener used to receive the events by the time-shift manager
- */
+ /** The listener used to receive the events by the time-shift manager */
public interface Listener {
/**
* Called when the availability of the time-shift for the current channel has been changed.
@@ -1323,31 +1384,23 @@ public class TimeShiftManager {
void onAvailabilityChanged();
/**
- * Called when the play status is changed between {@link #PLAY_STATUS_PLAYING} and
- * {@link #PLAY_STATUS_PAUSED}
+ * Called when the play status is changed between {@link #PLAY_STATUS_PLAYING} and {@link
+ * #PLAY_STATUS_PAUSED}
*
* @param status The new play state.
*/
void onPlayStatusChanged(int status);
- /**
- * Called when the recordStartTime has been changed.
- */
+ /** Called when the recordStartTime has been changed. */
void onRecordTimeRangeChanged();
- /**
- * Called when the current position is changed.
- */
+ /** Called when the current position is changed. */
void onCurrentPositionChanged();
- /**
- * Called when the program information is updated.
- */
+ /** Called when the program information is updated. */
void onProgramInfoChanged();
- /**
- * Called when an action becomes enabled or disabled.
- */
+ /** Called when an action becomes enabled or disabled. */
void onActionEnabledChanged(@TimeShiftActionId int actionId, boolean enabled);
}
diff --git a/src/com/android/tv/TvActivity.java b/src/com/android/tv/TvActivity.java
index 9a1cea55..aa0f0e8c 100644
--- a/src/com/android/tv/TvActivity.java
+++ b/src/com/android/tv/TvActivity.java
@@ -18,7 +18,6 @@ package com.android.tv;
import android.app.Activity;
import android.content.Intent;
-
import com.android.tv.util.Utils;
public class TvActivity extends Activity {
diff --git a/src/com/android/tv/TvApplication.java b/src/com/android/tv/TvApplication.java
index 0c7c0fd1..826317b9 100644
--- a/src/com/android/tv/TvApplication.java
+++ b/src/com/android/tv/TvApplication.java
@@ -18,11 +18,9 @@ package com.android.tv;
import android.annotation.TargetApi;
import android.app.Activity;
-import android.app.Application;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -30,32 +28,25 @@ import android.media.tv.TvContract;
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager;
import android.media.tv.TvInputManager.TvInputCallback;
-import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
-import android.os.StrictMode;
import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
-
-import com.android.tv.analytics.Analytics;
-import com.android.tv.analytics.StubAnalytics;
-import com.android.tv.analytics.StubAnalytics;
-import com.android.tv.analytics.Tracker;
-import com.android.tv.common.BuildConfig;
-import com.android.tv.common.SharedPreferencesUtils;
-import com.android.tv.common.SoftPreconditions;
-import com.android.tv.common.TvCommonUtils;
+import com.android.tv.common.BaseApplication;
+import com.android.tv.common.concurrent.NamedThreadFactory;
import com.android.tv.common.feature.CommonFeatures;
+import com.android.tv.common.recording.RecordingStorageStatusManager;
import com.android.tv.common.ui.setup.animation.SetupAnimationHelper;
-import com.android.tv.config.DefaultConfigManager;
-import com.android.tv.config.RemoteConfig;
+import com.android.tv.common.util.Clock;
+import com.android.tv.common.util.Debug;
+import com.android.tv.common.util.SharedPreferencesUtils;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.PreviewDataManager;
import com.android.tv.data.ProgramDataManager;
import com.android.tv.data.epg.EpgFetcher;
+import com.android.tv.data.epg.EpgFetcherImpl;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrDataManagerImpl;
import com.android.tv.dvr.DvrManager;
@@ -63,93 +54,81 @@ import com.android.tv.dvr.DvrScheduleManager;
import com.android.tv.dvr.DvrStorageStatusManager;
import com.android.tv.dvr.DvrWatchedPositionManager;
import com.android.tv.dvr.recorder.RecordingScheduler;
-import com.android.tv.perf.EventNames;
-import com.android.tv.perf.PerformanceMonitor;
-import com.android.tv.perf.StubPerformanceMonitor;
-import com.android.tv.perf.TimerEvent;
+import com.android.tv.dvr.ui.browse.DvrBrowseActivity;
import com.android.tv.recommendation.ChannelPreviewUpdater;
import com.android.tv.recommendation.RecordedProgramPreviewUpdater;
import com.android.tv.tuner.TunerInputController;
-import com.android.tv.tuner.TunerPreferences;
-import com.android.tv.tuner.tvinput.TunerTvInputService;
import com.android.tv.tuner.util.TunerInputInfoUtils;
-import com.android.tv.util.AccountHelper;
-import com.android.tv.util.Clock;
-import com.android.tv.util.Debug;
-import com.android.tv.util.PermissionUtils;
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 java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
-public class TvApplication extends Application implements ApplicationSingletons {
+/**
+ * Live TV application.
+ *
+ * <p>This includes all the Google specific hooks.
+ */
+public abstract class TvApplication extends BaseApplication implements TvSingletons, Starter {
private static final String TAG = "TvApplication";
private static final boolean DEBUG = false;
- private static final TimerEvent sAppStartTimer = StubPerformanceMonitor.startBootstrapTimer();
- /**
- * An instance of {@link ApplicationSingletons}. Note that this can be set directly only for the
- * test purpose.
- */
- @VisibleForTesting
- public static ApplicationSingletons sAppSingletons;
+ /** Namespace for LiveChannels configs. LiveChannels configs are kept in piper. */
+ public static final String CONFIGNS_P4 = "configns:p4";
/**
- * Broadcast Action: The user has updated LC to a new version that supports tuner input.
- * {@link com.android.tv.tuner.TunerInputController} will recevice this intent to check
- * the existence of tuner input when the new version is first launched.
+ * Broadcast Action: The user has updated LC to a new version that supports tuner input. {@link
+ * TunerInputController} will receive this intent to check the existence of tuner input when the
+ * new version is first launched.
*/
public static final String ACTION_APPLICATION_FIRST_LAUNCHED =
- "com.android.tv.action.APPLICATION_FIRST_LAUNCHED";
+ " com.android.tv.action.APPLICATION_FIRST_LAUNCHED";
+
private static final String PREFERENCE_IS_FIRST_LAUNCH = "is_first_launch";
- private RemoteConfig mRemoteConfig;
+ private static final NamedThreadFactory THREAD_FACTORY = new NamedThreadFactory("tv-app-db");
+ private static final ExecutorService DB_EXECUTOR =
+ Executors.newSingleThreadExecutor(THREAD_FACTORY);
+
private String mVersionName = "";
private final MainActivityWrapper mMainActivityWrapper = new MainActivityWrapper();
private SelectInputActivity mSelectInputActivity;
- private Analytics mAnalytics;
- private Tracker mTracker;
- private TvInputManagerHelper mTvInputManagerHelper;
private ChannelDataManager mChannelDataManager;
private volatile ProgramDataManager mProgramDataManager;
private PreviewDataManager mPreviewDataManager;
private DvrManager mDvrManager;
private DvrScheduleManager mDvrScheduleManager;
private DvrDataManager mDvrDataManager;
- private DvrStorageStatusManager mDvrStorageStatusManager;
private DvrWatchedPositionManager mDvrWatchedPositionManager;
private RecordingScheduler mRecordingScheduler;
- @Nullable
- private InputSessionManager mInputSessionManager;
- private AccountHelper mAccountHelper;
+ private RecordingStorageStatusManager mDvrStorageStatusManager;
+ @Nullable private InputSessionManager mInputSessionManager;
+ // STOP-SHIP: Remove this variable when Tuner Process is split to another application.
// When this variable is null, we don't know in which process TvApplication runs.
private Boolean mRunningInMainProcess;
- private PerformanceMonitor mPerformanceMonitor;
+ private TvInputManagerHelper mTvInputManagerHelper;
+ private boolean mStarted;
+ private EpgFetcher mEpgFetcher;
+ private TunerInputController mTunerInputController;
@Override
public void onCreate() {
super.onCreate();
- if (!PermissionUtils.hasInternet(this)) {
- // When an isolated process starts, just skip all the initialization.
- return;
- }
- Debug.getTimer(Debug.TAG_START_UP_TIMER).start();
- Debug.getTimer(Debug.TAG_START_UP_TIMER).log("Start TvApplication.onCreate");
- SharedPreferencesUtils.initialize(this, new Runnable() {
- @Override
- public void run() {
- if (mRunningInMainProcess != null && mRunningInMainProcess) {
- checkTunerServiceOnFirstLaunch();
- }
- }
- });
- // TunerPreferences is used to enable/disable the tuner input even when TUNER feature is
- // disabled.
- TunerPreferences.initialize(this);
+ SharedPreferencesUtils.initialize(
+ this,
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mRunningInMainProcess != null && mRunningInMainProcess) {
+ checkTunerServiceOnFirstLaunch();
+ }
+ }
+ });
try {
PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
mVersionName = pInfo.versionName;
@@ -159,73 +138,47 @@ public class TvApplication extends Application implements ApplicationSingletons
}
Log.i(TAG, "Starting Live TV " + getVersionName());
- // Only set StrictMode for ENG builds because the build server only produces userdebug
- // builds.
- if (BuildConfig.ENG && SystemProperties.ALLOW_STRICT_MODE.getValue()) {
- StrictMode.ThreadPolicy.Builder threadPolicyBuilder =
- new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog();
- StrictMode.VmPolicy.Builder vmPolicyBuilder =
- new StrictMode.VmPolicy.Builder().detectAll().penaltyDeath();
- if (!TvCommonUtils.isRunningInTest()) {
- threadPolicyBuilder.penaltyDialog();
- }
- StrictMode.setThreadPolicy(threadPolicyBuilder.build());
- StrictMode.setVmPolicy(vmPolicyBuilder.build());
- }
- if (BuildConfig.ENG && !SystemProperties.ALLOW_ANALYTICS_IN_ENG.getValue()) {
- mAnalytics = StubAnalytics.getInstance(this);
- } else {
- mAnalytics = StubAnalytics.getInstance(this);
- }
- mTracker = mAnalytics.getDefaultTracker();
- getTvInputManagerHelper();
// In SetupFragment, transitions are set in the constructor. Because the fragment can be
// created in Activity.onCreate() by the framework, SetupAnimationHelper should be
// initialized here before Activity.onCreate() is called.
+ mEpgFetcher = EpgFetcherImpl.create(this);
SetupAnimationHelper.initialize(this);
-
+ getTvInputManagerHelper();
Log.i(TAG, "Started Live TV " + mVersionName);
Debug.getTimer(Debug.TAG_START_UP_TIMER).log("finish TvApplication.onCreate");
- getPerformanceMonitor().stopTimer(sAppStartTimer, EventNames.APPLICATION_ONCREATE);
}
- private void setCurrentRunningProcess(boolean isMainProcess) {
- if (mRunningInMainProcess != null) {
- SoftPreconditions.checkState(isMainProcess == mRunningInMainProcess);
+ /** Initializes application. It is a noop if called twice. */
+ @Override
+ public void start() {
+ if (mStarted) {
return;
}
- Debug.getTimer(Debug.TAG_START_UP_TIMER).log(
- "start TvApplication.setCurrentRunningProcess");
- mRunningInMainProcess = isMainProcess;
- if (CommonFeatures.DVR.isEnabled(this)) {
- mDvrStorageStatusManager = new DvrStorageStatusManager(this, mRunningInMainProcess);
- }
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- // Fetch remote config
- getRemoteConfig().fetch(null);
- return null;
- }
- }.execute();
+ mStarted = true;
+ mRunningInMainProcess = true;
+ Debug.getTimer(Debug.TAG_START_UP_TIMER).log("start TvApplication.start");
if (mRunningInMainProcess) {
- getTvInputManagerHelper().addCallback(new TvInputCallback() {
- @Override
- public void onInputAdded(String inputId) {
- if (Features.TUNER.isEnabled(TvApplication.this) && TextUtils.equals(inputId,
- TunerTvInputService.getInputId(TvApplication.this))) {
- TunerInputInfoUtils.updateTunerInputInfo(TvApplication.this);
- }
- handleInputCountChanged();
- }
-
- @Override
- public void onInputRemoved(String inputId) {
- handleInputCountChanged();
- }
- });
- if (Features.TUNER.isEnabled(this)) {
+ getTvInputManagerHelper()
+ .addCallback(
+ new TvInputCallback() {
+ @Override
+ public void onInputAdded(String inputId) {
+ if (TvFeatures.TUNER.isEnabled(TvApplication.this)
+ && TextUtils.equals(
+ inputId, getEmbeddedTunerInputId())) {
+ TunerInputInfoUtils.updateTunerInputInfo(
+ TvApplication.this);
+ }
+ handleInputCountChanged();
+ }
+
+ @Override
+ public void onInputRemoved(String inputId) {
+ handleInputCountChanged();
+ }
+ });
+ if (TvFeatures.TUNER.isEnabled(this)) {
// If the tuner input service is added before the app is started, we need to
// handle it here.
TunerInputInfoUtils.updateTunerInputInfo(TvApplication.this);
@@ -235,58 +188,61 @@ public class TvApplication extends Application implements ApplicationSingletons
mDvrManager = new DvrManager(this);
mRecordingScheduler = RecordingScheduler.createScheduler(this);
}
- EpgFetcher.getInstance(this).startRoutineService();
+ mEpgFetcher.startRoutineService();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ChannelPreviewUpdater.getInstance(this).startRoutineService();
RecordedProgramPreviewUpdater.getInstance(this)
.updatePreviewDataForRecordedPrograms();
}
}
- Debug.getTimer(Debug.TAG_START_UP_TIMER).log(
- "finish TvApplication.setCurrentRunningProcess");
+ Debug.getTimer(Debug.TAG_START_UP_TIMER).log("finish TvApplication.start");
}
private void checkTunerServiceOnFirstLaunch() {
- SharedPreferences sharedPreferences = this.getSharedPreferences(
- SharedPreferencesUtils.SHARED_PREF_FEATURES, Context.MODE_PRIVATE);
+ SharedPreferences sharedPreferences =
+ this.getSharedPreferences(
+ SharedPreferencesUtils.SHARED_PREF_FEATURES, Context.MODE_PRIVATE);
boolean isFirstLaunch = sharedPreferences.getBoolean(PREFERENCE_IS_FIRST_LAUNCH, true);
if (isFirstLaunch) {
if (DEBUG) Log.d(TAG, "Congratulations, it's the first launch!");
- TunerInputController.onCheckingUsbTunerStatus(this, ACTION_APPLICATION_FIRST_LAUNCHED);
+ getTunerInputController()
+ .onCheckingUsbTunerStatus(this, ACTION_APPLICATION_FIRST_LAUNCHED);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putBoolean(PREFERENCE_IS_FIRST_LAUNCH, false);
editor.apply();
}
}
- /**
- * Returns the {@link DvrManager}.
- */
+ @Override
+ public EpgFetcher getEpgFetcher() {
+ return mEpgFetcher;
+ }
+
+ @Override
+ public synchronized SetupUtils getSetupUtils() {
+ return SetupUtils.createForTvSingletons(this);
+ }
+
+ /** Returns the {@link DvrManager}. */
@Override
public DvrManager getDvrManager() {
return mDvrManager;
}
- /**
- * Returns the {@link DvrScheduleManager}.
- */
+ /** Returns the {@link DvrScheduleManager}. */
@Override
public DvrScheduleManager getDvrScheduleManager() {
return mDvrScheduleManager;
}
- /**
- * Returns the {@link RecordingScheduler}.
- */
+ /** Returns the {@link RecordingScheduler}. */
@Override
@Nullable
public RecordingScheduler getRecordingScheduler() {
return mRecordingScheduler;
}
- /**
- * Returns the {@link DvrWatchedPositionManager}.
- */
+ /** Returns the {@link DvrWatchedPositionManager}. */
@Override
public DvrWatchedPositionManager getDvrWatchedPositionManager() {
if (mDvrWatchedPositionManager == null) {
@@ -304,25 +260,7 @@ public class TvApplication extends Application implements ApplicationSingletons
return mInputSessionManager;
}
- /**
- * Returns the {@link Analytics}.
- */
- @Override
- public Analytics getAnalytics() {
- return mAnalytics;
- }
-
- /**
- * Returns the default tracker.
- */
- @Override
- public Tracker getTracker() {
- return mTracker;
- }
-
- /**
- * Returns {@link ChannelDataManager}.
- */
+ /** Returns {@link ChannelDataManager}. */
@Override
public ChannelDataManager getChannelDataManager() {
if (mChannelDataManager == null) {
@@ -337,23 +275,22 @@ public class TvApplication extends Application implements ApplicationSingletons
return mChannelDataManager != null && mChannelDataManager.isDbLoadFinished();
}
- /**
- * Returns {@link ProgramDataManager}.
- */
+ /** Returns {@link ProgramDataManager}. */
@Override
public ProgramDataManager getProgramDataManager() {
if (mProgramDataManager != null) {
return mProgramDataManager;
}
- Utils.runInMainThreadAndWait(new Runnable() {
- @Override
- public void run() {
- if (mProgramDataManager == null) {
- mProgramDataManager = new ProgramDataManager(TvApplication.this);
- mProgramDataManager.start();
- }
- }
- });
+ Utils.runInMainThreadAndWait(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mProgramDataManager == null) {
+ mProgramDataManager = new ProgramDataManager(TvApplication.this);
+ mProgramDataManager.start();
+ }
+ }
+ });
return mProgramDataManager;
}
@@ -362,9 +299,7 @@ public class TvApplication extends Application implements ApplicationSingletons
return mProgramDataManager != null && mProgramDataManager.isCurrentProgramsLoadFinished();
}
- /**
- * Returns {@link PreviewDataManager}.
- */
+ /** Returns {@link PreviewDataManager}. */
@TargetApi(Build.VERSION_CODES.O)
@Override
public PreviewDataManager getPreviewDataManager() {
@@ -375,9 +310,7 @@ public class TvApplication extends Application implements ApplicationSingletons
return mPreviewDataManager;
}
- /**
- * Returns {@link DvrDataManager}.
- */
+ /** Returns {@link DvrDataManager}. */
@TargetApi(Build.VERSION_CODES.N)
@Override
public DvrDataManager getDvrDataManager() {
@@ -389,53 +322,39 @@ public class TvApplication extends Application implements ApplicationSingletons
return mDvrDataManager;
}
- /**
- * Returns {@link DvrStorageStatusManager}.
- */
- @TargetApi(Build.VERSION_CODES.N)
- @Override
- public DvrStorageStatusManager getDvrStorageStatusManager() {
- return mDvrStorageStatusManager;
- }
-
- /**
- * Returns {@link TvInputManagerHelper}.
- */
@Override
- public TvInputManagerHelper getTvInputManagerHelper() {
- if (mTvInputManagerHelper == null) {
- mTvInputManagerHelper = new TvInputManagerHelper(this);
- mTvInputManagerHelper.start();
+ @TargetApi(Build.VERSION_CODES.N)
+ public RecordingStorageStatusManager getRecordingStorageStatusManager() {
+ if (mDvrStorageStatusManager == null) {
+ mDvrStorageStatusManager = new DvrStorageStatusManager(this);
}
- return mTvInputManagerHelper;
+ return mDvrStorageStatusManager;
}
- /**
- * Returns the main activity information.
- */
+ /** Returns the main activity information. */
@Override
public MainActivityWrapper getMainActivityWrapper() {
return mMainActivityWrapper;
}
- /**
- * Returns the {@link AccountHelper}.
- */
+ /** Returns {@link TvInputManagerHelper}. */
@Override
- public AccountHelper getAccountHelper() {
- if (mAccountHelper == null) {
- mAccountHelper = new AccountHelper(getApplicationContext());
+ public TvInputManagerHelper getTvInputManagerHelper() {
+ if (mTvInputManagerHelper == null) {
+ mTvInputManagerHelper = new TvInputManagerHelper(this);
+ mTvInputManagerHelper.start();
}
- return mAccountHelper;
+ return mTvInputManagerHelper;
}
@Override
- public RemoteConfig getRemoteConfig() {
- if (mRemoteConfig == null) {
- // No need to synchronize this, it does not hurt to create two and throw one away.
- mRemoteConfig = DefaultConfigManager.createInstance(this).getRemoteConfig();
+ public synchronized TunerInputController getTunerInputController() {
+ if (mTunerInputController == null) {
+ mTunerInputController =
+ new TunerInputController(
+ ComponentName.unflattenFromString(getEmbeddedTunerInputId()));
}
- return mRemoteConfig;
+ return mTunerInputController;
}
@Override
@@ -443,17 +362,9 @@ public class TvApplication extends Application implements ApplicationSingletons
return mRunningInMainProcess != null && mRunningInMainProcess;
}
- @Override
- public PerformanceMonitor getPerformanceMonitor() {
- if (mPerformanceMonitor == null) {
- mPerformanceMonitor = StubPerformanceMonitor.initialize(this);
- }
- return mPerformanceMonitor;
- }
-
/**
- * SelectInputActivity is set in {@link SelectInputActivity#onCreate} and cleared in
- * {@link SelectInputActivity#onDestroy}.
+ * SelectInputActivity is set in {@link SelectInputActivity#onCreate} and cleared in {@link
+ * SelectInputActivity#onDestroy}.
*/
public void setSelectInputActivity(SelectInputActivity activity) {
mSelectInputActivity = activity;
@@ -467,18 +378,19 @@ public class TvApplication extends Application implements ApplicationSingletons
}
}
- /**
- * Handles the global key KEYCODE_TV.
- */
+ /** Handles the global key KEYCODE_TV. */
public void handleTvKey() {
if (!mMainActivityWrapper.isResumed()) {
startMainActivity(null);
}
}
- /**
- * Handles the global key KEYCODE_TV_INPUT.
- */
+ /** Handles the global key KEYCODE_DVR. */
+ public void handleDvrKey() {
+ startActivity(new Intent(this, DvrBrowseActivity.class));
+ }
+
+ /** Handles the global key KEYCODE_TV_INPUT. */
public void handleTvInputKey() {
TvInputManager tvInputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE);
List<TvInputInfo> tvInputs = tvInputManager.getTvInputList();
@@ -497,22 +409,25 @@ public class TvApplication extends Application implements ApplicationSingletons
if (inputCount < 2) {
return;
}
- Activity activityToHandle = mMainActivityWrapper.isResumed()
- ? mMainActivityWrapper.getMainActivity() : mSelectInputActivity;
+ Activity activityToHandle =
+ mMainActivityWrapper.isResumed()
+ ? mMainActivityWrapper.getMainActivity()
+ : mSelectInputActivity;
if (activityToHandle != null) {
// If startActivity is called, MainActivity.onPause is unnecessarily called. To
// prevent it, MainActivity.dispatchKeyEvent is directly called.
activityToHandle.dispatchKeyEvent(
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TV_INPUT));
- activityToHandle.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP,
- KeyEvent.KEYCODE_TV_INPUT));
+ activityToHandle.dispatchKeyEvent(
+ new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_TV_INPUT));
} else if (mMainActivityWrapper.isStarted()) {
Bundle extras = new Bundle();
extras.putString(Utils.EXTRA_KEY_ACTION, Utils.EXTRA_ACTION_SHOW_TV_INPUT);
startMainActivity(extras);
} else {
- startActivity(new Intent(this, SelectInputActivity.class).setFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK));
+ startActivity(
+ new Intent(this, SelectInputActivity.class)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
}
@@ -520,8 +435,8 @@ public class TvApplication extends Application implements ApplicationSingletons
// The use of FLAG_ACTIVITY_NEW_TASK enables arbitrary applications to access the intent
// sent to the root activity. Having said that, we should be fine here since such an intent
// does not carry any important user data.
- Intent intent = new Intent(this, MainActivity.class)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ Intent intent =
+ new Intent(this, MainActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (extras != null) {
intent.putExtras(extras);
}
@@ -538,36 +453,39 @@ public class TvApplication extends Application implements ApplicationSingletons
}
/**
- * Checks the input counts and enable/disable TvActivity. Also updates the input list in
- * {@link SetupUtils}.
+ * Checks the input counts and enable/disable TvActivity. Also upda162 the input list in {@link
+ * SetupUtils}.
*/
+ @Override
public void handleInputCountChanged() {
handleInputCountChanged(false, false, false);
}
/**
- * Checks the input counts and enable/disable TvActivity. Also updates the input list in
- * {@link SetupUtils}.
+ * Checks the input counts and enable/disable TvActivity. Also updates the input list in {@link
+ * SetupUtils}.
*
- * @param calledByTunerServiceChanged true if it is called when TunerTvInputService
- * is enabled or disabled.
+ * @param calledByTunerServiceChanged true if it is called when BaseTunerTvInputService 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.
+ * @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) {
+ public void handleInputCountChanged(
+ boolean calledByTunerServiceChanged, boolean tunerServiceEnabled, boolean dontKillApp) {
TvInputManager inputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE);
- boolean enable = (calledByTunerServiceChanged && tunerServiceEnabled)
- || Features.UNHIDE.isEnabled(TvApplication.this);
+ boolean enable =
+ (calledByTunerServiceChanged && tunerServiceEnabled)
+ || TvFeatures.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.
if (!skipTunerInputCheck) {
for (TvInputInfo input : inputs) {
- if (calledByTunerServiceChanged && !tunerServiceEnabled
- && TunerTvInputService.getInputId(this).equals(input.getId())) {
+ if (calledByTunerServiceChanged
+ && !tunerServiceEnabled
+ && getEmbeddedTunerInputId().equals(input.getId())) {
continue;
}
if (input.getType() == TvInputInfo.TYPE_TUNER) {
@@ -580,43 +498,20 @@ public class TvApplication extends Application implements ApplicationSingletons
}
PackageManager packageManager = getPackageManager();
ComponentName name = new ComponentName(this, TvActivity.class);
- int newState = enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+ int newState =
+ enable
+ ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+ : PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
if (packageManager.getComponentEnabledSetting(name) != newState) {
- packageManager.setComponentEnabledSetting(name, newState,
- dontKillApp ? PackageManager.DONT_KILL_APP : 0);
+ packageManager.setComponentEnabledSetting(
+ name, newState, dontKillApp ? PackageManager.DONT_KILL_APP : 0);
Log.i(TAG, (enable ? "Un-hide" : "Hide") + " Live TV.");
}
- SetupUtils.getInstance(TvApplication.this).onInputListUpdated(inputManager);
- }
-
- /**
- * Returns the @{@link ApplicationSingletons} using the application context.
- */
- public static ApplicationSingletons getSingletons(Context context) {
- // No need to be "synchronized" because this doesn't create any instance.
- if (sAppSingletons == null) {
- sAppSingletons = (ApplicationSingletons) context.getApplicationContext();
- }
- return sAppSingletons;
+ getSetupUtils().onInputListUpdated(inputManager);
}
- /**
- * Sets true, if TvApplication is running on the main process. If TvApplication runs on
- * tuner process or other process, it sets false.
- *
- * Note: it should be called at the beginning of Service.onCreate Activity.onCreate, or
- * BroadcastReceiver.onCreate. When it is firstly called after launch, it runs process
- * specific initializations.
- */
- public static void setCurrentRunningProcess(Context context, boolean isMainProcess) {
- // TODO(b/63064354) TvApplication should not have to know if it is "the main process"
- if (context.getApplicationContext() instanceof TvApplication) {
- TvApplication tvApplication = (TvApplication) context.getApplicationContext();
- tvApplication.setCurrentRunningProcess(isMainProcess);
- } else {
- // Application context can be MockTvApplication.
- Log.w(TAG, "It is not a context of TvApplication");
- }
+ @Override
+ public Executor getDbExecutor() {
+ return DB_EXECUTOR;
}
}
diff --git a/src/com/android/tv/TvFeatures.java b/src/com/android/tv/TvFeatures.java
new file mode 100644
index 00000000..d2cf76e7
--- /dev/null
+++ b/src/com/android/tv/TvFeatures.java
@@ -0,0 +1,104 @@
+/*
+ * 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;
+
+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.OFF;
+import static com.android.tv.common.feature.FeatureUtils.ON;
+import static com.android.tv.common.feature.FeatureUtils.OR;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.support.annotation.VisibleForTesting;
+import com.android.tv.common.experiments.Experiments;
+import com.android.tv.common.feature.CommonFeatures;
+import com.android.tv.common.feature.ExperimentFeature;
+import com.android.tv.common.feature.Feature;
+import com.android.tv.common.feature.FeatureUtils;
+import com.android.tv.common.feature.GServiceFeature;
+import com.android.tv.common.feature.PropertyFeature;
+import com.android.tv.common.feature.Sdk;
+import com.android.tv.common.feature.TestableFeature;
+import com.android.tv.common.util.PermissionUtils;
+
+import com.google.android.tv.partner.support.PartnerCustomizations;
+
+/**
+ * List of {@link Feature} for the Live TV App.
+ *
+ * <p>Remove the {@code Feature} once it is launched.
+ */
+public final class TvFeatures extends CommonFeatures {
+
+ /** When enabled use system setting for turning on analytics. */
+ public static final Feature ANALYTICS_OPT_IN =
+ ExperimentFeature.from(Experiments.ENABLE_ANALYTICS_VIA_CHECKBOX);
+ /** When enabled shows a list of failed recordings */
+ public static final Feature DVR_FAILED_LIST = 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 final Feature ANALYTICS_V2 = AND(ON, ANALYTICS_OPT_IN);
+
+ public static final Feature EPG_SEARCH =
+ PropertyFeature.create("feature_tv_use_epg_search", false);
+
+ 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 final Feature UNHIDE =
+ 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);
+ }
+ });
+
+ public static final Feature PICTURE_IN_PICTURE =
+ new Feature() {
+ private Boolean mEnabled;
+
+ @Override
+ public boolean isEnabled(Context context) {
+ if (mEnabled == null) {
+ mEnabled =
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
+ && context.getPackageManager()
+ .hasSystemFeature(
+ PackageManager.FEATURE_PICTURE_IN_PICTURE);
+ }
+ return mEnabled;
+ }
+ };
+
+ /** Enable a conflict dialog between currently watched channel and upcoming recording. */
+ public static final Feature SHOW_UPCOMING_CONFLICT_DIALOG = OFF;
+
+ /** Use input blacklist to disable partner's tuner input. */
+ public static final Feature USE_PARTNER_INPUT_BLACKLIST = ON;
+
+ @VisibleForTesting
+ public static final Feature TEST_FEATURE = PropertyFeature.create("test_feature", false);
+
+ private TvFeatures() {}
+}
diff --git a/src/com/android/tv/TvOptionsManager.java b/src/com/android/tv/TvOptionsManager.java
index 493e039c..4e0636ff 100644
--- a/src/com/android/tv/TvOptionsManager.java
+++ b/src/com/android/tv/TvOptionsManager.java
@@ -20,9 +20,7 @@ import android.content.Context;
import android.media.tv.TvTrackInfo;
import android.support.annotation.IntDef;
import android.util.SparseArray;
-
import com.android.tv.data.DisplayMode;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Locale;
@@ -33,9 +31,17 @@ import java.util.Locale;
*/
public class TvOptionsManager {
@Retention(RetentionPolicy.SOURCE)
- @IntDef({OPTION_CLOSED_CAPTIONS, OPTION_DISPLAY_MODE, OPTION_SYSTEMWIDE_PIP, OPTION_MULTI_AUDIO,
- OPTION_MORE_CHANNELS, OPTION_DEVELOPER, OPTION_SETTINGS})
+ @IntDef({
+ OPTION_CLOSED_CAPTIONS,
+ OPTION_DISPLAY_MODE,
+ OPTION_SYSTEMWIDE_PIP,
+ OPTION_MULTI_AUDIO,
+ OPTION_MORE_CHANNELS,
+ OPTION_DEVELOPER,
+ OPTION_SETTINGS
+ })
public @interface OptionType {}
+
public static final int OPTION_CLOSED_CAPTIONS = 0;
public static final int OPTION_DISPLAY_MODE = 1;
public static final int OPTION_SYSTEMWIDE_PIP = 2;
@@ -57,6 +63,7 @@ public class TvOptionsManager {
/**
* Returns a suitable displayed string for the given option type under current settings.
+ *
* @param option the type of option, should be one of {@link OptionType}.
*/
public String getOptionString(@OptionType int option) {
@@ -67,8 +74,9 @@ public class TvOptionsManager {
}
return new Locale(mClosedCaptionsLanguage).getDisplayName();
case OPTION_DISPLAY_MODE:
- return ((MainActivity) mContext).getTvViewUiManager()
- .isDisplayModeAvailable(mDisplayMode)
+ return ((MainActivity) mContext)
+ .getTvViewUiManager()
+ .isDisplayModeAvailable(mDisplayMode)
? DisplayMode.getLabel(mDisplayMode, mContext)
: DisplayMode.getLabel(DisplayMode.MODE_NORMAL, mContext);
case OPTION_MULTI_AUDIO:
@@ -77,27 +85,25 @@ public class TvOptionsManager {
return "";
}
- /**
- * Handles changing selection of closed caption.
- */
+ /** Handles changing selection of closed caption. */
public void onClosedCaptionsChanged(TvTrackInfo track, int trackIndex) {
- mClosedCaptionsLanguage = (track == null) ?
- null : (track.getLanguage() != null) ? track.getLanguage()
- : mContext.getString(R.string.closed_caption_unknown_language, trackIndex + 1);
+ mClosedCaptionsLanguage =
+ (track == null)
+ ? null
+ : (track.getLanguage() != null)
+ ? track.getLanguage()
+ : mContext.getString(
+ R.string.closed_caption_unknown_language, trackIndex + 1);
notifyOptionChanged(OPTION_CLOSED_CAPTIONS);
}
- /**
- * Handles changing selection of display mode.
- */
+ /** Handles changing selection of display mode. */
public void onDisplayModeChanged(int displayMode) {
mDisplayMode = displayMode;
notifyOptionChanged(OPTION_DISPLAY_MODE);
}
- /**
- * Handles changing selection of multi-audio.
- */
+ /** Handles changing selection of multi-audio. */
public void onMultiAudioChanged(String multiAudio) {
mMultiAudio = multiAudio;
notifyOptionChanged(OPTION_MULTI_AUDIO);
@@ -110,17 +116,13 @@ public class TvOptionsManager {
}
}
- /**
- * Sets listeners to changes of the given option type.
- */
+ /** Sets listeners to changes of the given option type. */
public void setOptionChangedListener(int option, OptionChangedListener listener) {
mOptionChangedListeners.put(option, listener);
}
- /**
- * An interface used to monitor option changes.
- */
+ /** An interface used to monitor option changes. */
public interface OptionChangedListener {
void onOptionChanged(@OptionType int optionType, String newString);
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/ApplicationSingletons.java b/src/com/android/tv/TvSingletons.java
index ac7d4c4a..0c7f78a3 100644
--- a/src/com/android/tv/ApplicationSingletons.java
+++ b/src/com/android/tv/TvSingletons.java
@@ -16,29 +16,42 @@
package com.android.tv;
+import android.content.Context;
import com.android.tv.analytics.Analytics;
import com.android.tv.analytics.Tracker;
-import com.android.tv.config.RemoteConfig;
+import com.android.tv.common.BaseApplication;
+import com.android.tv.common.BaseSingletons;
+import com.android.tv.common.experiments.ExperimentLoader;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.PreviewDataManager;
import com.android.tv.data.ProgramDataManager;
+import com.android.tv.data.epg.EpgFetcher;
+import com.android.tv.data.epg.EpgReader;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.DvrScheduleManager;
-import com.android.tv.dvr.DvrStorageStatusManager;
import com.android.tv.dvr.DvrWatchedPositionManager;
import com.android.tv.dvr.recorder.RecordingScheduler;
import com.android.tv.perf.PerformanceMonitor;
-import com.android.tv.util.AccountHelper;
+import com.android.tv.tuner.TunerInputController;
+import com.android.tv.util.SetupUtils;
import com.android.tv.util.TvInputManagerHelper;
+import com.android.tv.util.account.AccountHelper;
+import java.util.concurrent.Executor;
+import javax.inject.Provider;
-/**
- * Interface with getters for application scoped singletons.
- */
-public interface ApplicationSingletons {
+/** Interface with getters for application scoped singletons. */
+public interface TvSingletons extends BaseSingletons {
+
+ /** Returns the @{@link TvSingletons} using the application context. */
+ static TvSingletons getSingletons(Context context) {
+ return (TvSingletons) BaseApplication.getSingletons(context);
+ }
Analytics getAnalytics();
+ void handleInputCountChanged();
+
ChannelDataManager getChannelDataManager();
/**
@@ -59,8 +72,6 @@ public interface ApplicationSingletons {
DvrDataManager getDvrDataManager();
- DvrStorageStatusManager getDvrStorageStatusManager();
-
DvrScheduleManager getDvrScheduleManager();
DvrManager getDvrManager();
@@ -73,15 +84,25 @@ public interface ApplicationSingletons {
Tracker getTracker();
- TvInputManagerHelper getTvInputManagerHelper();
-
MainActivityWrapper getMainActivityWrapper();
AccountHelper getAccountHelper();
- RemoteConfig getRemoteConfig();
-
boolean isRunningInMainProcess();
PerformanceMonitor getPerformanceMonitor();
+
+ TvInputManagerHelper getTvInputManagerHelper();
+
+ Provider<EpgReader> providesEpgReader();
+
+ EpgFetcher getEpgFetcher();
+
+ SetupUtils getSetupUtils();
+
+ TunerInputController getTunerInputController();
+
+ ExperimentLoader getExperimentLoader();
+
+ Executor getDbExecutor();
}
diff --git a/src/com/android/tv/analytics/Analytics.java b/src/com/android/tv/analytics/Analytics.java
index 27085de7..e0626423 100644
--- a/src/com/android/tv/analytics/Analytics.java
+++ b/src/com/android/tv/analytics/Analytics.java
@@ -16,21 +16,17 @@
package com.android.tv.analytics;
-/**
- * Provides Trackers used for user activity analysis.
- */
+/** Provides Trackers used for user activity analysis. */
public interface Analytics {
Tracker getDefaultTracker();
- /**
- * Returns whether the state of the application-level opt is on.
- */
+ /** Returns whether the state of the application-level opt is on. */
boolean isAppOptOut();
/**
- * Sets or resets the application-level opt out flag. If set, no hits will be sent.
- * The value of this flag will <i>not</i> persist across application starts. The
- * correct value should thus be set in application initialization code.
+ * Sets or resets the application-level opt out flag. If set, no hits will be sent. The value of
+ * this flag will <i>not</i> persist across application starts. The correct value should thus be
+ * set in application initialization code.
*
* @param optOut {@code true} if application-level opt out should be enforced.
*/
diff --git a/src/com/android/tv/analytics/ConfigurationInfo.java b/src/com/android/tv/analytics/ConfigurationInfo.java
index 41e8baeb..b6bfc5aa 100644
--- a/src/com/android/tv/analytics/ConfigurationInfo.java
+++ b/src/com/android/tv/analytics/ConfigurationInfo.java
@@ -16,9 +16,7 @@
package com.android.tv.analytics;
-/**
- * Data useful for tracking that doesn't change often.
- */
+/** Data useful for tracking that doesn't change often. */
public class ConfigurationInfo {
public final int systemInputCount;
public final int nonSystemInputCount;
diff --git a/src/com/android/tv/analytics/HasTrackerLabel.java b/src/com/android/tv/analytics/HasTrackerLabel.java
index 566e5f1a..04896850 100644
--- a/src/com/android/tv/analytics/HasTrackerLabel.java
+++ b/src/com/android/tv/analytics/HasTrackerLabel.java
@@ -23,8 +23,6 @@ package com.android.tv.analytics;
*/
public interface HasTrackerLabel {
- /**
- * Returns the label.
- */
+ /** Returns the label. */
String getTrackerLabel();
}
diff --git a/src/com/android/tv/analytics/SendChannelStatusRunnable.java b/src/com/android/tv/analytics/SendChannelStatusRunnable.java
index b5b5805c..4a84434c 100644
--- a/src/com/android/tv/analytics/SendChannelStatusRunnable.java
+++ b/src/com/android/tv/analytics/SendChannelStatusRunnable.java
@@ -20,11 +20,9 @@ import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.MainThread;
-
-import com.android.tv.data.Channel;
import com.android.tv.data.ChannelDataManager;
+import com.android.tv.data.api.Channel;
import com.android.tv.util.RecurringRunner;
-
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -32,53 +30,63 @@ import java.util.concurrent.TimeUnit;
* Periodically sends analytics data with the channel count.
*
* <p>
- * <p>This should only be started from a user activity
- * like {@link com.android.tv.MainActivity}.
+ *
+ * <p>This should only be started from a user activity like {@link com.android.tv.MainActivity}.
*/
@MainThread
public class SendChannelStatusRunnable implements Runnable {
private static final long SEND_CHANNEL_STATUS_INTERVAL_MS = TimeUnit.DAYS.toMillis(1);
- public static RecurringRunner startChannelStatusRecurringRunner(Context context,
- Tracker tracker, ChannelDataManager channelDataManager) {
+ public static RecurringRunner startChannelStatusRecurringRunner(
+ Context context, Tracker tracker, ChannelDataManager channelDataManager) {
- final SendChannelStatusRunnable sendChannelStatusRunnable = new SendChannelStatusRunnable(
- channelDataManager, tracker);
+ final SendChannelStatusRunnable sendChannelStatusRunnable =
+ new SendChannelStatusRunnable(channelDataManager, tracker);
- Runnable onStopRunnable = new Runnable() {
- @Override
- public void run() {
- sendChannelStatusRunnable.setDbLoadListener(null);
- }
- };
- final RecurringRunner recurringRunner = new RecurringRunner(context,
- SEND_CHANNEL_STATUS_INTERVAL_MS, sendChannelStatusRunnable, onStopRunnable);
+ Runnable onStopRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ sendChannelStatusRunnable.setDbLoadListener(null);
+ }
+ };
+ final RecurringRunner recurringRunner =
+ new RecurringRunner(
+ context,
+ SEND_CHANNEL_STATUS_INTERVAL_MS,
+ sendChannelStatusRunnable,
+ onStopRunnable);
if (channelDataManager.isDbLoadFinished()) {
sendChannelStatusRunnable.setDbLoadListener(null);
recurringRunner.start();
} else {
- //Start the recurring runnable after the channel DB is finished loading.
- sendChannelStatusRunnable.setDbLoadListener(new ChannelDataManager.Listener() {
- @Override
- public void onLoadFinished() {
- // This is called inside an iterator of Listeners so the remove step is done
- // via a post on the main thread
- new Handler(Looper.getMainLooper()).post(new Runnable() {
+ // Start the recurring runnable after the channel DB is finished loading.
+ sendChannelStatusRunnable.setDbLoadListener(
+ new ChannelDataManager.Listener() {
@Override
- public void run() {
- sendChannelStatusRunnable.setDbLoadListener(null);
+ public void onLoadFinished() {
+ // This is called inside an iterator of Listeners so the remove step is
+ // done
+ // via a post on the main thread
+ new Handler(Looper.getMainLooper())
+ .post(
+ new Runnable() {
+ @Override
+ public void run() {
+ sendChannelStatusRunnable.setDbLoadListener(
+ null);
+ }
+ });
+ recurringRunner.start();
}
- });
- recurringRunner.start();
- }
- @Override
- public void onChannelListUpdated() { }
+ @Override
+ public void onChannelListUpdated() {}
- @Override
- public void onChannelBrowsableChanged() { }
- });
+ @Override
+ public void onChannelBrowsableChanged() {}
+ });
}
return recurringRunner;
}
diff --git a/src/com/android/tv/analytics/SendConfigInfoRunnable.java b/src/com/android/tv/analytics/SendConfigInfoRunnable.java
index 41392a6d..d4674086 100644
--- a/src/com/android/tv/analytics/SendConfigInfoRunnable.java
+++ b/src/com/android/tv/analytics/SendConfigInfoRunnable.java
@@ -17,14 +17,10 @@
package com.android.tv.analytics;
import android.media.tv.TvInputInfo;
-
import com.android.tv.util.TvInputManagerHelper;
-
import java.util.List;
-/**
- * Sends ConfigurationInfo once a day.
- */
+/** Sends ConfigurationInfo once a day. */
public class SendConfigInfoRunnable implements Runnable {
private final Tracker mTracker;
private final TvInputManagerHelper mTvInputManagerHelper;
@@ -46,8 +42,8 @@ public class SendConfigInfoRunnable implements Runnable {
nonSystemInputCount++;
}
}
- ConfigurationInfo configurationInfo = new ConfigurationInfo(systemInputCount,
- nonSystemInputCount);
+ ConfigurationInfo configurationInfo =
+ new ConfigurationInfo(systemInputCount, nonSystemInputCount);
mTracker.sendConfigurationInfo(configurationInfo);
}
}
diff --git a/src/com/android/tv/analytics/StubAnalytics.java b/src/com/android/tv/analytics/StubAnalytics.java
index 99c10d94..2c58b9b8 100644
--- a/src/com/android/tv/analytics/StubAnalytics.java
+++ b/src/com/android/tv/analytics/StubAnalytics.java
@@ -19,9 +19,7 @@ package com.android.tv.analytics;
import android.app.Application;
import android.content.Context;
-/**
- * An implementation of {@link Analytics} that returns a {@link StubTracker}.
- */
+/** An implementation of {@link Analytics} that returns a {@link StubTracker}. */
public final class StubAnalytics implements Analytics {
public static StubAnalytics getInstance(Application application) {
return new StubAnalytics(application);
@@ -30,8 +28,7 @@ public final class StubAnalytics implements Analytics {
private final Tracker mTracker = new StubTracker();
private boolean mOptOut = true;
- private StubAnalytics(Context context) {
- }
+ private StubAnalytics(Context context) {}
@Override
public Tracker getDefaultTracker() {
diff --git a/src/com/android/tv/analytics/StubTracker.java b/src/com/android/tv/analytics/StubTracker.java
index 6e64ebca..e11b91c2 100644
--- a/src/com/android/tv/analytics/StubTracker.java
+++ b/src/com/android/tv/analytics/StubTracker.java
@@ -17,111 +17,108 @@
package com.android.tv.analytics;
import android.support.annotation.VisibleForTesting;
-
import com.android.tv.TimeShiftManager;
-import com.android.tv.data.Channel;
+import com.android.tv.data.api.Channel;
-/**
- * A implementation of Tracker that does nothing.
- */
+/** A implementation of Tracker that does nothing. */
@VisibleForTesting
public class StubTracker implements Tracker {
@Override
- public void sendChannelCount(int browsableChannelCount, int totalChannelCount) { }
+ public void sendChannelCount(int browsableChannelCount, int totalChannelCount) {}
@Override
- public void sendConfigurationInfo(ConfigurationInfo info) { }
+ public void sendConfigurationInfo(ConfigurationInfo info) {}
@Override
- public void sendMainStart() { }
+ public void sendMainStart() {}
@Override
- public void sendMainStop(long durationMs) { }
+ public void sendMainStop(long durationMs) {}
@Override
- public void sendScreenView(String screenName) { }
+ public void sendScreenView(String screenName) {}
@Override
- public void sendChannelViewStart(Channel channel, boolean tunedByRecommendation) { }
+ public void sendChannelViewStart(Channel channel, boolean tunedByRecommendation) {}
@Override
- public void sendChannelTuneTime(Channel channel, long durationMs) { }
+ public void sendChannelTuneTime(Channel channel, long durationMs) {}
@Override
- public void sendChannelViewStop(Channel channel, long durationMs) { }
+ public void sendChannelViewStop(Channel channel, long durationMs) {}
@Override
- public void sendChannelUp() { }
+ public void sendChannelUp() {}
@Override
- public void sendChannelDown() { }
+ public void sendChannelDown() {}
@Override
- public void sendShowMenu() { }
+ public void sendShowMenu() {}
@Override
- public void sendHideMenu(long durationMs) { }
+ public void sendHideMenu(long durationMs) {}
@Override
- public void sendMenuClicked(String label) { }
+ public void sendMenuClicked(String label) {}
@Override
- public void sendMenuClicked(int labelResId) { }
+ public void sendMenuClicked(int labelResId) {}
@Override
- public void sendShowEpg() { }
+ public void sendShowEpg() {}
@Override
- public void sendEpgItemClicked() { }
+ public void sendEpgItemClicked() {}
@Override
- public void sendHideEpg(long durationMs) { }
+ public void sendHideEpg(long durationMs) {}
@Override
- public void sendShowChannelSwitch() { }
+ public void sendShowChannelSwitch() {}
@Override
- public void sendHideChannelSwitch(long durationMs) { }
+ public void sendHideChannelSwitch(long durationMs) {}
@Override
- public void sendChannelNumberInput() { }
+ public void sendChannelNumberInput() {}
@Override
- public void sendChannelInputNavigated() { }
+ public void sendChannelInputNavigated() {}
@Override
- public void sendChannelNumberItemClicked() { }
+ public void sendChannelNumberItemClicked() {}
@Override
- public void sendChannelNumberItemChosenByTimeout() { }
+ public void sendChannelNumberItemChosenByTimeout() {}
@Override
- public void sendChannelVideoUnavailable(Channel channel, int reason) { }
+ public void sendChannelVideoUnavailable(Channel channel, int reason) {}
@Override
- public void sendAc3PassthroughCapabilities(boolean isSupported) { }
+ public void sendAc3PassthroughCapabilities(boolean isSupported) {}
@Override
- public void sendInputConnectionFailure(String inputId) { }
+ public void sendInputConnectionFailure(String inputId) {}
@Override
- public void sendInputDisconnected(String inputId) { }
+ public void sendInputDisconnected(String inputId) {}
@Override
- public void sendShowInputSelection() { }
+ public void sendShowInputSelection() {}
@Override
- public void sendHideInputSelection(long durationMs) { }
+ public void sendHideInputSelection(long durationMs) {}
@Override
- public void sendInputSelected(String inputLabel) { }
+ public void sendInputSelected(String inputLabel) {}
@Override
- public void sendShowSidePanel(HasTrackerLabel trackerLabel) { }
+ public void sendShowSidePanel(HasTrackerLabel trackerLabel) {}
@Override
- public void sendHideSidePanel(HasTrackerLabel trackerLabel, long durationMs) { }
+ public void sendHideSidePanel(HasTrackerLabel trackerLabel, long durationMs) {}
@Override
- public void sendTimeShiftAction(@TimeShiftManager.TimeShiftActionId int actionId) { }
+ public void sendTimeShiftAction(@TimeShiftManager.TimeShiftActionId int actionId) {}
}
diff --git a/src/com/android/tv/analytics/Tracker.java b/src/com/android/tv/analytics/Tracker.java
index 291fc9ce..0fcef5dc 100644
--- a/src/com/android/tv/analytics/Tracker.java
+++ b/src/com/android/tv/analytics/Tracker.java
@@ -17,11 +17,9 @@
package com.android.tv.analytics;
import com.android.tv.TimeShiftManager;
-import com.android.tv.data.Channel;
+import com.android.tv.data.api.Channel;
-/**
- * Interface for sending user activity for analysis.
- */
+/** Interface for sending user activity for analysis. */
public interface Tracker {
/**
@@ -45,9 +43,7 @@ public interface Tracker {
*/
void sendConfigurationInfo(ConfigurationInfo info);
- /**
- * Sends tracking information for starting the MainActivity.
- */
+ /** Sends tracking information for starting the MainActivity. */
void sendMainStart();
/**
@@ -55,11 +51,9 @@ public interface Tracker {
*
* @param durationMs The time main activity was "started" in milliseconds.
*/
- void sendMainStop( long durationMs);
+ void sendMainStop(long durationMs);
- /**
- * Sets the screen name and sends a ScreenView hit.
- */
+ /** Sets the screen name and sends a ScreenView hit. */
void sendScreenView(String screenName);
/**
@@ -86,19 +80,13 @@ public interface Tracker {
*/
void sendChannelViewStop(Channel channel, long durationMs);
- /**
- * Sends tracking information for pressing channel up.
- */
+ /** Sends tracking information for pressing channel up. */
void sendChannelUp();
- /**
- * Sends tracking information for pressing channel down.
- */
+ /** Sends tracking information for pressing channel down. */
void sendChannelDown();
- /**
- * Sends tracking information for showing the main menu.
- */
+ /** Sends tracking information for showing the main menu. */
void sendShowMenu();
/**
@@ -126,14 +114,10 @@ public interface Tracker {
*/
void sendMenuClicked(int labelResId);
- /**
- * Sends tracking information for showing the Electronic Program Guide (EPG).
- */
+ /** Sends tracking information for showing the Electronic Program Guide (EPG). */
void sendShowEpg();
- /**
- * Sends tracking information for clicking an Electronic Program Guide (EPG) item.
- */
+ /** Sends tracking information for clicking an Electronic Program Guide (EPG) item. */
void sendEpgItemClicked();
/**
@@ -143,9 +127,7 @@ public interface Tracker {
*/
void sendHideEpg(long durationMs);
- /**
- * Sends tracking information for showing the channel switch view.
- */
+ /** Sends tracking information for showing the channel switch view. */
void sendShowChannelSwitch();
/**
@@ -155,9 +137,7 @@ public interface Tracker {
*/
void sendHideChannelSwitch(long durationMs);
- /**
- * Sends tracking for each channel number or delimiter pressed.
- */
+ /** Sends tracking for each channel number or delimiter pressed. */
void sendChannelNumberInput();
/**
@@ -167,19 +147,13 @@ public interface Tracker {
*/
void sendChannelInputNavigated();
- /**
- * Sends tracking for channel clicked.
- */
+ /** Sends tracking for channel clicked. */
void sendChannelNumberItemClicked();
- /**
- * Sends tracking for channel chosen (tuned) because the channel switch view timed out.
- */
+ /** Sends tracking for channel chosen (tuned) because the channel switch view timed out. */
void sendChannelNumberItemChosenByTimeout();
- /**
- * Sends tracking for the reason video is unavailable on a channel.
- */
+ /** Sends tracking for the reason video is unavailable on a channel. */
void sendChannelVideoUnavailable(Channel channel, int reason);
/**
@@ -191,6 +165,7 @@ public interface Tracker {
/**
* Sends tracking for input a connection failure.
+ *
* <p><strong>WARNING</strong> callers must ensure no PII is included in the inputId.
*
* @param inputId the input the failure happened on
@@ -199,15 +174,14 @@ public interface Tracker {
/**
* Sends tracking for input disconnected.
+ *
* <p><strong>WARNING</strong> callers must ensure no PII is included in the inputId.
*
* @param inputId the input the failure happened on
*/
void sendInputDisconnected(String inputId);
- /**
- * Sends tracking information for showing the input selection view.
- */
+ /** Sends tracking information for showing the input selection view. */
void sendShowInputSelection();
/**
diff --git a/src/com/android/tv/app/LiveTvApplication.java b/src/com/android/tv/app/LiveTvApplication.java
new file mode 100644
index 00000000..461331d5
--- /dev/null
+++ b/src/com/android/tv/app/LiveTvApplication.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2017 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.app;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.media.tv.TvContract;
+import com.android.tv.TvApplication;
+import com.android.tv.analytics.Analytics;
+import com.android.tv.analytics.StubAnalytics;
+import com.android.tv.analytics.Tracker;
+import com.android.tv.common.CommonConstants;
+import com.android.tv.common.actions.InputSetupActionUtils;
+import com.android.tv.common.config.DefaultConfigManager;
+import com.android.tv.common.config.api.RemoteConfig;
+import com.android.tv.common.experiments.ExperimentLoader;
+import com.android.tv.common.util.CommonUtils;
+import com.android.tv.data.epg.EpgReader;
+import com.android.tv.data.epg.StubEpgReader;
+import com.android.tv.perf.PerformanceMonitor;
+import com.android.tv.perf.StubPerformanceMonitor;
+import com.android.tv.tuner.livetuner.LiveTvTunerTvInputService;
+import com.android.tv.tuner.setup.LiveTvTunerSetupActivity;
+import com.android.tv.util.account.AccountHelper;
+import com.android.tv.util.account.AccountHelperImpl;
+import javax.inject.Provider;
+
+/** The top level application for Live TV. */
+public class LiveTvApplication extends TvApplication {
+ protected static final String TV_ACTIVITY_CLASS_NAME =
+ CommonConstants.BASE_PACKAGE + ".TvActivity";
+
+ private final StubPerformanceMonitor performanceMonitor = new StubPerformanceMonitor();
+ private final Provider<EpgReader> mEpgReaderProvider =
+ new Provider<EpgReader>() {
+
+ @Override
+ public EpgReader get() {
+ return new StubEpgReader(LiveTvApplication.this);
+ }
+ };
+
+ private AccountHelper mAccountHelper;
+ private Analytics mAnalytics;
+ private Tracker mTracker;
+ private String mEmbeddedInputId;
+ private RemoteConfig mRemoteConfig;
+ private ExperimentLoader mExperimentLoader;
+
+ /** Returns the {@link AccountHelperImpl}. */
+ @Override
+ public AccountHelper getAccountHelper() {
+ if (mAccountHelper == null) {
+ mAccountHelper = new AccountHelperImpl(getApplicationContext());
+ }
+ return mAccountHelper;
+ }
+
+ @Override
+ public synchronized PerformanceMonitor getPerformanceMonitor() {
+ return performanceMonitor;
+ }
+
+ @Override
+ public Provider<EpgReader> providesEpgReader() {
+ return mEpgReaderProvider;
+ }
+
+ @Override
+ public ExperimentLoader getExperimentLoader() {
+ mExperimentLoader = new ExperimentLoader();
+ return mExperimentLoader;
+ }
+
+ /** Returns the {@link Analytics}. */
+ @Override
+ public synchronized Analytics getAnalytics() {
+ if (mAnalytics == null) {
+ mAnalytics = StubAnalytics.getInstance(this);
+ }
+ return mAnalytics;
+ }
+
+ /** Returns the default tracker. */
+ @Override
+ public synchronized Tracker getTracker() {
+ if (mTracker == null) {
+ mTracker = getAnalytics().getDefaultTracker();
+ }
+ return mTracker;
+ }
+
+ @Override
+ public Intent getTunerSetupIntent(Context context) {
+ // Make an intent to launch the setup activity of TV tuner input.
+ Intent intent =
+ CommonUtils.createSetupIntent(
+ new Intent(context, LiveTvTunerSetupActivity.class), mEmbeddedInputId);
+ intent.putExtra(InputSetupActionUtils.EXTRA_INPUT_ID, mEmbeddedInputId);
+ Intent tvActivityIntent = new Intent();
+ tvActivityIntent.setComponent(new ComponentName(context, TV_ACTIVITY_CLASS_NAME));
+ intent.putExtra(InputSetupActionUtils.EXTRA_ACTIVITY_AFTER_COMPLETION, tvActivityIntent);
+ return intent;
+ }
+
+ @Override
+ public synchronized String getEmbeddedTunerInputId() {
+ if (mEmbeddedInputId == null) {
+ mEmbeddedInputId =
+ TvContract.buildInputId(
+ new ComponentName(this, LiveTvTunerTvInputService.class));
+ }
+ return mEmbeddedInputId;
+ }
+
+ @Override
+ public RemoteConfig getRemoteConfig() {
+ if (mRemoteConfig == null) {
+ // No need to synchronize this, it does not hurt to create two and throw one away.
+ mRemoteConfig = DefaultConfigManager.createInstance(this).getRemoteConfig();
+ }
+ return mRemoteConfig;
+ }
+}
diff --git a/src/com/android/tv/config/RemoteConfigUtils.java b/src/com/android/tv/config/RemoteConfigUtils.java
deleted file mode 100644
index 09d85239..00000000
--- a/src/com/android/tv/config/RemoteConfigUtils.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2017 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.config;
-
-import android.content.Context;
-import android.util.Log;
-import com.android.tv.TvApplication;
-
-/** A utility class to get the remote config. */
-public class RemoteConfigUtils {
- private static final String TAG = "RemoteConfigUtils";
- private static final boolean DEBUG = false;
-
- private RemoteConfigUtils() {}
-
- public static long getRemoteConfig(Context context, String key, long defaultValue) {
- RemoteConfig remoteConfig = TvApplication.getSingletons(context).getRemoteConfig();
- try {
- long remoteValue = remoteConfig.getLong(key);
- if (DEBUG) Log.d(TAG, "Got " + key + " from remote: " + remoteValue);
- return remoteValue;
- } catch (Exception e) {
- Log.w(TAG, "Cannot get " + key + " from RemoteConfig", e);
- }
- if (DEBUG) Log.d(TAG, "Use default value " + defaultValue);
- return defaultValue;
- }
-}
diff --git a/src/com/android/tv/data/BaseProgram.java b/src/com/android/tv/data/BaseProgram.java
index 4e36c80a..0fb1e58d 100644
--- a/src/com/android/tv/data/BaseProgram.java
+++ b/src/com/android/tv/data/BaseProgram.java
@@ -20,14 +20,12 @@ import android.content.Context;
import android.media.tv.TvContentRating;
import android.support.annotation.Nullable;
import android.text.TextUtils;
-
import com.android.tv.R;
-
import java.util.Comparator;
/**
- * Base class for {@link com.android.tv.data.Program} and
- * {@link com.android.tv.dvr.data.RecordedProgram}.
+ * Base class for {@link com.android.tv.data.Program} and {@link
+ * com.android.tv.dvr.data.RecordedProgram}.
*/
public abstract class BaseProgram {
/**
@@ -35,8 +33,7 @@ public abstract class BaseProgram {
* If a program's season or episode number is null, it will be consider "smaller" than programs
* with season or episode numbers.
*/
- public static final Comparator<BaseProgram> EPISODE_COMPARATOR =
- new EpisodeComparator(false);
+ public static final Comparator<BaseProgram> EPISODE_COMPARATOR = new EpisodeComparator(false);
/**
* Comparator used to compare {@link BaseProgram} according to its season and episodes number
@@ -58,8 +55,7 @@ public abstract class BaseProgram {
if (lhs == rhs) {
return 0;
}
- int seasonNumberCompare =
- numberCompare(lhs.getSeasonNumber(), rhs.getSeasonNumber());
+ int seasonNumberCompare = numberCompare(lhs.getSeasonNumber(), rhs.getSeasonNumber());
if (seasonNumberCompare != 0) {
return mReversedSeason ? -seasonNumberCompare : seasonNumberCompare;
} else {
@@ -68,9 +64,7 @@ public abstract class BaseProgram {
}
}
- /**
- * Compares two strings represent season numbers or episode numbers of programs.
- */
+ /** Compares two strings represent season numbers or episode numbers of programs. */
public static int numberCompare(String s1, String s2) {
if (s1 == s2) {
return 0;
@@ -88,121 +82,120 @@ public abstract class BaseProgram {
}
}
- /**
- * Returns ID of the program.
- */
- abstract public long getId();
+ /** Returns ID of the program. */
+ public abstract long getId();
- /**
- * Returns the title of the program.
- */
- abstract public String getTitle();
+ /** Returns the title of the program. */
+ public abstract String getTitle();
- /**
- * Returns the episode title.
- */
- abstract public String getEpisodeTitle();
+ /** Returns the episode title. */
+ public abstract String getEpisodeTitle();
- /**
- * Returns the displayed title of the program episode.
- */
+ /** Returns the displayed title of the program episode. */
public String getEpisodeDisplayTitle(Context context) {
- if (!TextUtils.isEmpty(getEpisodeNumber())) {
- String episodeTitle = getEpisodeTitle() == null ? "" : getEpisodeTitle();
- if (TextUtils.equals(getSeasonNumber(), "0")) {
+ String episodeNumber = getEpisodeNumber();
+ String episodeTitle = getEpisodeTitle();
+ if (!TextUtils.isEmpty(episodeNumber)) {
+ episodeTitle = episodeTitle == null ? "" : episodeTitle;
+ String seasonNumber = getSeasonNumber();
+ if (TextUtils.isEmpty(seasonNumber) || TextUtils.equals(seasonNumber, "0")) {
// Do not show "S0: ".
- return String.format(context.getResources().getString(
- R.string.display_episode_title_format_no_season_number),
- getEpisodeNumber(), episodeTitle);
+ return context.getResources()
+ .getString(
+ R.string.display_episode_title_format_no_season_number,
+ episodeNumber,
+ episodeTitle);
} else {
- return String.format(context.getResources().getString(
- R.string.display_episode_title_format),
- getSeasonNumber(), getEpisodeNumber(), episodeTitle);
+ return context.getResources()
+ .getString(
+ R.string.display_episode_title_format,
+ seasonNumber,
+ episodeNumber,
+ episodeTitle);
}
}
- return getEpisodeTitle();
+ return episodeTitle;
}
/**
- * Returns the description of the program.
- */
- abstract public String getDescription();
+ * Returns the content description of the program episode, suitable for being spoken by an
+ * accessibility service.
+ */
+ public String getEpisodeContentDescription(Context context) {
+ String episodeNumber = getEpisodeNumber();
+ String episodeTitle = getEpisodeTitle();
+ if (!TextUtils.isEmpty(episodeNumber)) {
+ episodeTitle = episodeTitle == null ? "" : episodeTitle;
+ String seasonNumber = getSeasonNumber();
+ if (TextUtils.isEmpty(seasonNumber) || TextUtils.equals(seasonNumber, "0")) {
+ // Do not list season if it is empty or 0
+ return context.getResources()
+ .getString(
+ R.string.content_description_episode_format_no_season_number,
+ episodeNumber,
+ episodeTitle);
+ } else {
+ return context.getResources()
+ .getString(
+ R.string.content_description_episode_format,
+ seasonNumber,
+ episodeNumber,
+ episodeTitle);
+ }
+ }
+ return episodeTitle;
+ }
- /**
- * Returns the long description of the program.
- */
- abstract public String getLongDescription();
+ /** Returns the description of the program. */
+ public abstract String getDescription();
- /**
- * Returns the start time of the program in Milliseconds.
- */
- abstract public long getStartTimeUtcMillis();
+ /** Returns the long description of the program. */
+ public abstract String getLongDescription();
- /**
- * Returns the end time of the program in Milliseconds.
- */
- abstract public long getEndTimeUtcMillis();
+ /** Returns the start time of the program in Milliseconds. */
+ public abstract long getStartTimeUtcMillis();
- /**
- * Returns the duration of the program in Milliseconds.
- */
- abstract public long getDurationMillis();
+ /** Returns the end time of the program in Milliseconds. */
+ public abstract long getEndTimeUtcMillis();
- /**
- * Returns the series ID.
- */
- abstract public String getSeriesId();
+ /** Returns the duration of the program in Milliseconds. */
+ public abstract long getDurationMillis();
- /**
- * Returns the season number.
- */
- abstract public String getSeasonNumber();
+ /** Returns the series ID. */
+ public abstract String getSeriesId();
- /**
- * Returns the episode number.
- */
- abstract public String getEpisodeNumber();
+ /** Returns the season number. */
+ public abstract String getSeasonNumber();
- /**
- * Returns URI of the program's poster.
- */
- abstract public String getPosterArtUri();
+ /** Returns the episode number. */
+ public abstract String getEpisodeNumber();
- /**
- * Returns URI of the program's thumbnail.
- */
- abstract public String getThumbnailUri();
+ /** Returns URI of the program's poster. */
+ public abstract String getPosterArtUri();
- /**
- * Returns the array of the ID's of the canonical genres.
- */
- abstract public int[] getCanonicalGenreIds();
+ /** Returns URI of the program's thumbnail. */
+ public abstract String getThumbnailUri();
+
+ /** Returns the array of the ID's of the canonical genres. */
+ public abstract int[] getCanonicalGenreIds();
/** Returns the array of content ratings. */
@Nullable
- abstract public TvContentRating[] getContentRatings();
+ public abstract TvContentRating[] getContentRatings();
- /**
- * Returns channel's ID of the program.
- */
- abstract public long getChannelId();
+ /** Returns channel's ID of the program. */
+ public abstract long getChannelId();
- /**
- * Returns if the program is valid.
- */
- abstract public boolean isValid();
+ /** Returns if the program is valid. */
+ public abstract boolean isValid();
- /**
- * Checks whether the program is episodic or not.
- */
+ /** Checks whether the program is episodic or not. */
public boolean isEpisodic() {
return getSeriesId() != null;
}
- /**
- * Generates the series ID for the other inputs than the tuner TV input.
- */
+ /** Generates the series ID for the other inputs than the tuner TV input. */
public static String generateSeriesId(String packageName, String title) {
return packageName + "/" + title;
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/data/ChannelDataManager.java b/src/com/android/tv/data/ChannelDataManager.java
index 6f93fbd1..1dfcf125 100644
--- a/src/com/android/tv/data/ChannelDataManager.java
+++ b/src/com/android/tv/data/ChannelDataManager.java
@@ -38,15 +38,15 @@ import android.support.annotation.VisibleForTesting;
import android.util.ArraySet;
import android.util.Log;
import android.util.MutableInt;
-
-import com.android.tv.common.SharedPreferencesUtils;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
import com.android.tv.common.WeakHandler;
+import com.android.tv.common.util.PermissionUtils;
+import com.android.tv.common.util.SharedPreferencesUtils;
+import com.android.tv.data.api.Channel;
import com.android.tv.util.AsyncDbTask;
-import com.android.tv.util.PermissionUtils;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.Utils;
-
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
@@ -56,13 +56,13 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.Executor;
/**
- * The class to manage channel data.
- * Basic features: reading channel list and each channel's current program, and updating
- * the values of {@link Channels#COLUMN_BROWSABLE}, {@link Channels#COLUMN_LOCKED}.
- * This class is not thread-safe and under an assumption that its public methods are called in
- * only the main thread.
+ * The class to manage channel data. Basic features: reading channel list and each channel's current
+ * program, and updating the values of {@link Channels#COLUMN_BROWSABLE}, {@link
+ * Channels#COLUMN_LOCKED}. This class is not thread-safe and under an assumption that its public
+ * methods are called in only the main thread.
*/
@AnyThread
public class ChannelDataManager {
@@ -73,6 +73,7 @@ public class ChannelDataManager {
private final Context mContext;
private final TvInputManagerHelper mInputManager;
+ private final Executor mDbExecutor;
private boolean mStarted;
private boolean mDbLoadFinished;
private QueryAllChannelsTask mChannelsUpdateTask;
@@ -81,8 +82,8 @@ public class ChannelDataManager {
private final Set<Listener> mListeners = new CopyOnWriteArraySet<>();
// Use container class to support multi-thread safety. This value can be set only on the main
// thread.
- volatile private UnmodifiableChannelData mData = new UnmodifiableChannelData();
- private final Channel.DefaultComparator mChannelComparator;
+ private volatile UnmodifiableChannelData mData = new UnmodifiableChannelData();
+ private final ChannelImpl.DefaultComparator mChannelComparator;
private final Handler mHandler;
private final Set<Long> mBrowsableUpdateChannelIds = new HashSet<>();
@@ -93,81 +94,92 @@ public class ChannelDataManager {
private final boolean mStoreBrowsableInSharedPreferences;
private final SharedPreferences mBrowsableSharedPreferences;
- private final TvInputCallback mTvInputCallback = new TvInputCallback() {
- @Override
- public void onInputAdded(String inputId) {
- boolean channelAdded = false;
- ChannelData data = new ChannelData(mData);
- for (ChannelWrapper channel : mData.channelWrapperMap.values()) {
- if (channel.mChannel.getInputId().equals(inputId)) {
- channel.mInputRemoved = false;
- addChannel(data, channel.mChannel);
- channelAdded = true;
+ private final TvInputCallback mTvInputCallback =
+ new TvInputCallback() {
+ @Override
+ public void onInputAdded(String inputId) {
+ boolean channelAdded = false;
+ ChannelData data = new ChannelData(mData);
+ for (ChannelWrapper channel : mData.channelWrapperMap.values()) {
+ if (channel.mChannel.getInputId().equals(inputId)) {
+ channel.mInputRemoved = false;
+ addChannel(data, channel.mChannel);
+ channelAdded = true;
+ }
+ }
+ if (channelAdded) {
+ Collections.sort(data.channels, mChannelComparator);
+ mData = new UnmodifiableChannelData(data);
+ notifyChannelListUpdated();
+ }
}
- }
- if (channelAdded) {
- Collections.sort(data.channels, mChannelComparator);
- mData = new UnmodifiableChannelData(data);
- notifyChannelListUpdated();
- }
- }
- @Override
- public void onInputRemoved(String inputId) {
- boolean channelRemoved = false;
- ArrayList<ChannelWrapper> removedChannels = new ArrayList<>();
- for (ChannelWrapper channel : mData.channelWrapperMap.values()) {
- if (channel.mChannel.getInputId().equals(inputId)) {
- channel.mInputRemoved = true;
- channelRemoved = true;
- removedChannels.add(channel);
- }
- }
- if (channelRemoved) {
- ChannelData data = new ChannelData();
- data.channelWrapperMap.putAll(mData.channelWrapperMap);
- for (ChannelWrapper channelWrapper : data.channelWrapperMap.values()) {
- if (!channelWrapper.mInputRemoved) {
- addChannel(data, channelWrapper.mChannel);
+ @Override
+ public void onInputRemoved(String inputId) {
+ boolean channelRemoved = false;
+ ArrayList<ChannelWrapper> removedChannels = new ArrayList<>();
+ for (ChannelWrapper channel : mData.channelWrapperMap.values()) {
+ if (channel.mChannel.getInputId().equals(inputId)) {
+ channel.mInputRemoved = true;
+ channelRemoved = true;
+ removedChannels.add(channel);
+ }
+ }
+ if (channelRemoved) {
+ ChannelData data = new ChannelData();
+ data.channelWrapperMap.putAll(mData.channelWrapperMap);
+ for (ChannelWrapper channelWrapper : data.channelWrapperMap.values()) {
+ if (!channelWrapper.mInputRemoved) {
+ addChannel(data, channelWrapper.mChannel);
+ }
+ }
+ Collections.sort(data.channels, mChannelComparator);
+ mData = new UnmodifiableChannelData(data);
+ notifyChannelListUpdated();
+ for (ChannelWrapper channel : removedChannels) {
+ channel.notifyChannelRemoved();
+ }
}
}
- Collections.sort(data.channels, mChannelComparator);
- mData = new UnmodifiableChannelData(data);
- notifyChannelListUpdated();
- for (ChannelWrapper channel : removedChannels) {
- channel.notifyChannelRemoved();
- }
- }
- }
- };
+ };
@MainThread
public ChannelDataManager(Context context, TvInputManagerHelper inputManager) {
- this(context, inputManager, context.getContentResolver());
+ this(
+ context,
+ inputManager,
+ TvSingletons.getSingletons(context).getDbExecutor(),
+ context.getContentResolver());
}
@MainThread
@VisibleForTesting
- ChannelDataManager(Context context, TvInputManagerHelper inputManager,
+ ChannelDataManager(
+ Context context,
+ TvInputManagerHelper inputManager,
+ Executor executor,
ContentResolver contentResolver) {
mContext = context;
mInputManager = inputManager;
+ mDbExecutor = executor;
mContentResolver = contentResolver;
- mChannelComparator = new Channel.DefaultComparator(context, inputManager);
+ mChannelComparator = new ChannelImpl.DefaultComparator(context, inputManager);
// Detect duplicate channels while sorting.
mChannelComparator.setDetectDuplicatesEnabled(true);
mHandler = new ChannelDataManagerHandler(this);
- mChannelObserver = new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange) {
- if (!mHandler.hasMessages(MSG_UPDATE_CHANNELS)) {
- mHandler.sendEmptyMessage(MSG_UPDATE_CHANNELS);
- }
- }
- };
+ mChannelObserver =
+ new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ if (!mHandler.hasMessages(MSG_UPDATE_CHANNELS)) {
+ mHandler.sendEmptyMessage(MSG_UPDATE_CHANNELS);
+ }
+ }
+ };
mStoreBrowsableInSharedPreferences = !PermissionUtils.hasAccessAllEpg(mContext);
- mBrowsableSharedPreferences = context.getSharedPreferences(
- SharedPreferencesUtils.SHARED_PREF_BROWSABLE, Context.MODE_PRIVATE);
+ mBrowsableSharedPreferences =
+ context.getSharedPreferences(
+ SharedPreferencesUtils.SHARED_PREF_BROWSABLE, Context.MODE_PRIVATE);
}
@VisibleForTesting
@@ -175,9 +187,7 @@ public class ChannelDataManager {
return mChannelObserver;
}
- /**
- * Starts the manager. If data is ready, {@link Listener#onLoadFinished()} will be called.
- */
+ /** Starts the manager. If data is ready, {@link Listener#onLoadFinished()} will be called. */
@MainThread
public void start() {
if (mStarted) {
@@ -187,8 +197,8 @@ public class ChannelDataManager {
// Should be called directly instead of posting MSG_UPDATE_CHANNELS message to the handler.
// If not, other DB tasks can be executed before channel loading.
handleUpdateChannels();
- mContentResolver.registerContentObserver(TvContract.Channels.CONTENT_URI, true,
- mChannelObserver);
+ mContentResolver.registerContentObserver(
+ TvContract.Channels.CONTENT_URI, true, mChannelObserver);
mInputManager.addCallback(mTvInputCallback);
}
@@ -218,9 +228,7 @@ public class ChannelDataManager {
applyUpdatedValuesToDb();
}
- /**
- * Adds a {@link Listener}.
- */
+ /** Adds a {@link Listener}. */
public void addListener(Listener listener) {
if (DEBUG) Log.d(TAG, "addListener " + listener);
SoftPreconditions.checkNotNull(listener);
@@ -229,9 +237,7 @@ public class ChannelDataManager {
}
}
- /**
- * Removes a {@link Listener}.
- */
+ /** Removes a {@link Listener}. */
public void removeListener(Listener listener) {
if (DEBUG) Log.d(TAG, "removeListener " + listener);
SoftPreconditions.checkNotNull(listener);
@@ -252,8 +258,8 @@ public class ChannelDataManager {
}
/**
- * Removes a {@link ChannelListener} for a specific channel with the channel ID
- * {@code channelId}.
+ * Removes a {@link ChannelListener} for a specific channel with the channel ID {@code
+ * channelId}.
*/
public void removeChannelListener(Long channelId, ChannelListener listener) {
ChannelWrapper channelWrapper = mData.channelWrapperMap.get(channelId);
@@ -263,30 +269,22 @@ public class ChannelDataManager {
channelWrapper.removeListener(listener);
}
- /**
- * Checks whether data is ready.
- */
+ /** Checks whether data is ready. */
public boolean isDbLoadFinished() {
return mDbLoadFinished;
}
- /**
- * Returns the number of channels.
- */
+ /** Returns the number of channels. */
public int getChannelCount() {
return mData.channels.size();
}
- /**
- * Returns a list of channels.
- */
+ /** Returns a list of channels. */
public List<Channel> getChannelList() {
return new ArrayList<>(mData.channels);
}
- /**
- * Returns a list of browsable channels.
- */
+ /** Returns a list of browsable channels. */
public List<Channel> getBrowsableChannelList() {
List<Channel> channels = new ArrayList<>();
for (Channel channel : mData.channels) {
@@ -329,9 +327,7 @@ public class ChannelDataManager {
return true;
}
- /**
- * Gets the channel with the channel ID {@code channelId}.
- */
+ /** Gets the channel with the channel ID {@code channelId}. */
public Channel getChannel(Long channelId) {
ChannelWrapper channelWrapper = mData.channelWrapperMap.get(channelId);
if (channelWrapper == null || channelWrapper.mInputRemoved) {
@@ -340,9 +336,7 @@ public class ChannelDataManager {
return channelWrapper.mChannel;
}
- /**
- * The value change will be applied to DB when applyPendingDbOperation is called.
- */
+ /** The value change will be applied to DB when applyPendingDbOperation is called. */
public void updateBrowsable(Long channelId, boolean browsable) {
updateBrowsable(channelId, browsable, false);
}
@@ -351,12 +345,12 @@ public class ChannelDataManager {
* The value change will be applied to DB when applyPendingDbOperation is called.
*
* @param skipNotifyChannelBrowsableChanged If it's true, {@link Listener
- * #onChannelBrowsableChanged()} is not called, when this method is called.
- * {@link #notifyChannelBrowsableChanged} should be directly called, once browsable
- * update is completed.
+ * #onChannelBrowsableChanged()} is not called, when this method is called. {@link
+ * #notifyChannelBrowsableChanged} should be directly called, once browsable update is
+ * completed.
*/
- public void updateBrowsable(Long channelId, boolean browsable,
- boolean skipNotifyChannelBrowsableChanged) {
+ public void updateBrowsable(
+ Long channelId, boolean browsable, boolean skipNotifyChannelBrowsableChanged) {
ChannelWrapper channelWrapper = mData.channelWrapperMap.get(channelId);
if (channelWrapper == null) {
return;
@@ -396,10 +390,7 @@ public class ChannelDataManager {
}
}
- /**
- * Updates channels from DB. Once the update is done, {@code postRunnable} will
- * be called.
- */
+ /** Updates channels from DB. Once the update is done, {@code postRunnable} will be called. */
public void updateChannels(Runnable postRunnable) {
if (mChannelsUpdateTask != null) {
mChannelsUpdateTask.cancel(true);
@@ -411,9 +402,7 @@ public class ChannelDataManager {
}
}
- /**
- * The value change will be applied to DB when applyPendingDbOperation is called.
- */
+ /** The value change will be applied to DB when applyPendingDbOperation is called. */
public void updateLocked(Long channelId, boolean locked) {
ChannelWrapper channelWrapper = mData.channelWrapperMap.get(channelId);
if (channelWrapper == null) {
@@ -430,10 +419,7 @@ public class ChannelDataManager {
}
}
- /**
- * Applies the changed values by {@link #updateBrowsable} and {@link #updateLocked}
- * to DB.
- */
+ /** Applies the changed values by {@link #updateBrowsable} and {@link #updateLocked} to DB. */
public void applyUpdatedValuesToDb() {
ChannelData data = mData;
ArrayList<Long> browsableIds = new ArrayList<>();
@@ -493,11 +479,17 @@ public class ChannelDataManager {
}
mLockedUpdateChannelIds.clear();
if (DEBUG) {
- Log.d(TAG, "applyUpdatedValuesToDb"
- + "\n browsableIds size:" + browsableIds.size()
- + "\n unbrowsableIds size:" + unbrowsableIds.size()
- + "\n lockedIds size:" + lockedIds.size()
- + "\n unlockedIds size:" + unlockedIds.size());
+ Log.d(
+ TAG,
+ "applyUpdatedValuesToDb"
+ + "\n browsableIds size:"
+ + browsableIds.size()
+ + "\n unbrowsableIds size:"
+ + unbrowsableIds.size()
+ + "\n lockedIds size:"
+ + lockedIds.size()
+ + "\n unlockedIds size:"
+ + unlockedIds.size());
}
}
@@ -527,48 +519,34 @@ public class ChannelDataManager {
mChannelsUpdateTask.executeOnDbThread();
}
- /**
- * Reloads channel data.
- */
+ /** Reloads channel data. */
public void reload() {
if (mDbLoadFinished && !mHandler.hasMessages(MSG_UPDATE_CHANNELS)) {
mHandler.sendEmptyMessage(MSG_UPDATE_CHANNELS);
}
}
- /**
- * A listener for ChannelDataManager. The callbacks are called on the main thread.
- */
+ /** A listener for ChannelDataManager. The callbacks are called on the main thread. */
public interface Listener {
- /**
- * Called when data load is finished.
- */
+ /** Called when data load is finished. */
void onLoadFinished();
/**
- * Called when channels are added, deleted, or updated. But, when browsable is changed,
- * it won't be called. Instead, {@link #onChannelBrowsableChanged} will be called.
+ * Called when channels are added, deleted, or updated. But, when browsable is changed, it
+ * won't be called. Instead, {@link #onChannelBrowsableChanged} will be called.
*/
void onChannelListUpdated();
- /**
- * Called when browsable of channels are changed.
- */
+ /** Called when browsable of channels are changed. */
void onChannelBrowsableChanged();
}
- /**
- * A listener for individual channel change. The callbacks are called on the main thread.
- */
+ /** A listener for individual channel change. The callbacks are called on the main thread. */
public interface ChannelListener {
- /**
- * Called when the channel has been removed in DB.
- */
+ /** Called when the channel has been removed in DB. */
void onChannelRemoved(Channel channel);
- /**
- * Called when values of the channel has been changed.
- */
+ /** Called when values of the channel has been changed. */
void onChannelUpdated(Channel channel);
}
@@ -616,8 +594,10 @@ public class ChannelDataManager {
@Override
protected Boolean doInBackground(Void... params) {
- try (AssetFileDescriptor f = mContext.getContentResolver().openAssetFileDescriptor(
- TvContract.buildChannelLogoUri(mChannel.getId()), "r")) {
+ try (AssetFileDescriptor f =
+ mContext.getContentResolver()
+ .openAssetFileDescriptor(
+ TvContract.buildChannelLogoUri(mChannel.getId()), "r")) {
return true;
} catch (SQLiteException | IOException | NullPointerException e) {
// File not found or asset file not found.
@@ -637,7 +617,7 @@ public class ChannelDataManager {
private final class QueryAllChannelsTask extends AsyncDbTask.AsyncChannelQueryTask {
QueryAllChannelsTask(ContentResolver contentResolver) {
- super(contentResolver);
+ super(mDbExecutor, contentResolver);
}
@Override
@@ -663,8 +643,8 @@ public class ChannelDataManager {
for (Channel channel : channels) {
if (mStoreBrowsableInSharedPreferences) {
String browsableKey = getBrowsableKey(channel);
- channel.setBrowsable(mBrowsableSharedPreferences.getBoolean(browsableKey,
- false));
+ channel.setBrowsable(
+ mBrowsableSharedPreferences.getBoolean(browsableKey, false));
deletedBrowsableMap.remove(browsableKey);
}
long channelId = channel.getId();
@@ -698,7 +678,8 @@ public class ChannelDataManager {
}
}
}
- if (mStoreBrowsableInSharedPreferences && !deletedBrowsableMap.isEmpty()
+ if (mStoreBrowsableInSharedPreferences
+ && !deletedBrowsableMap.isEmpty()
&& PermissionUtils.hasReadTvListings(mContext)) {
// If hasReadTvListings(mContext) is false, the given channel list would
// empty. In this case, we skip the browsable data clean up process.
@@ -745,24 +726,26 @@ public class ChannelDataManager {
}
/**
- * Updates a column {@code columnName} of DB table {@code uri} with the value
- * {@code columnValue}. The selective rows in the ID list {@code ids} will be updated.
- * The DB operations will run on {@link AsyncDbTask#getExecutor()}.
+ * Updates a column {@code columnName} of DB table {@code uri} with the value {@code
+ * columnValue}. The selective rows in the ID list {@code ids} will be updated. The DB
+ * operations will run on {@link TvSingletons#getDbExecutor()}.
*/
private void updateOneColumnValue(
final String columnName, final int columnValue, final List<Long> ids) {
if (!PermissionUtils.hasAccessAllEpg(mContext)) {
return;
}
- AsyncDbTask.executeOnDbThread(new Runnable() {
- @Override
- public void run() {
- String selection = Utils.buildSelectionForIds(Channels._ID, ids);
- ContentValues values = new ContentValues();
- values.put(columnName, columnValue);
- mContentResolver.update(TvContract.Channels.CONTENT_URI, values, selection, null);
- }
- });
+ mDbExecutor.execute(
+ new Runnable() {
+ @Override
+ public void run() {
+ String selection = Utils.buildSelectionForIds(Channels._ID, ids);
+ ContentValues values = new ContentValues();
+ values.put(columnName, columnValue);
+ mContentResolver.update(
+ TvContract.Channels.CONTENT_URI, values, selection, null);
+ }
+ });
}
private String getBrowsableKey(Channel channel) {
@@ -784,9 +767,8 @@ public class ChannelDataManager {
}
/**
- * Container class which includes channel data that needs to be synced. This class is
- * modifiable and used for changing channel data.
- * e.g. TvInputCallback, or AsyncDbTask.onPostExecute.
+ * Container class which includes channel data that needs to be synced. This class is modifiable
+ * and used for changing channel data. e.g. TvInputCallback, or AsyncDbTask.onPostExecute.
*/
@MainThread
private static class ChannelData {
@@ -806,8 +788,10 @@ public class ChannelDataManager {
channels = new ArrayList<>(data.channels);
}
- ChannelData(Map<Long, ChannelWrapper> channelWrapperMap,
- Map<String, MutableInt> channelCountMap, List<Channel> channels) {
+ ChannelData(
+ Map<Long, ChannelWrapper> channelWrapperMap,
+ Map<String, MutableInt> channelCountMap,
+ List<Channel> channels) {
this.channelWrapperMap = channelWrapperMap;
this.channelCountMap = channelCountMap;
this.channels = channels;
@@ -818,13 +802,15 @@ public class ChannelDataManager {
@MainThread
private static class UnmodifiableChannelData extends ChannelData {
UnmodifiableChannelData() {
- super(Collections.unmodifiableMap(new HashMap<>()),
+ super(
+ Collections.unmodifiableMap(new HashMap<>()),
Collections.unmodifiableMap(new HashMap<>()),
Collections.unmodifiableList(new ArrayList<>()));
}
UnmodifiableChannelData(ChannelData data) {
- super(Collections.unmodifiableMap(data.channelWrapperMap),
+ super(
+ Collections.unmodifiableMap(data.channelWrapperMap),
Collections.unmodifiableMap(data.channelCountMap),
Collections.unmodifiableList(data.channels));
}
diff --git a/src/com/android/tv/data/Channel.java b/src/com/android/tv/data/ChannelImpl.java
index 4a391ae7..703f69c9 100644
--- a/src/com/android/tv/data/Channel.java
+++ b/src/com/android/tv/data/ChannelImpl.java
@@ -28,93 +28,63 @@ import android.support.annotation.UiThread;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import android.util.Log;
-
-import com.android.tv.common.TvCommonConstants;
-import com.android.tv.util.ImageLoader;
+import com.android.tv.common.CommonConstants;
+import com.android.tv.common.util.CommonUtils;
+import com.android.tv.data.api.Channel;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.Utils;
-
+import com.android.tv.util.images.ImageLoader;
import java.net.URISyntaxException;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
-/**
- * A convenience class to create and insert channel entries into the database.
- */
-public final class Channel {
- private static final String TAG = "Channel";
+/** A convenience class to create and insert channel entries into the database. */
+public final class ChannelImpl implements Channel {
+ private static final String TAG = "ChannelImpl";
- public static final long INVALID_ID = -1;
- public static final int LOAD_IMAGE_TYPE_CHANNEL_LOGO = 1;
- public static final int LOAD_IMAGE_TYPE_APP_LINK_ICON = 2;
- public static final int LOAD_IMAGE_TYPE_APP_LINK_POSTER_ART = 3;
-
- /**
- * Compares the channel numbers of channels which belong to the same input.
- */
- public static final Comparator<Channel> CHANNEL_NUMBER_COMPARATOR = new Comparator<Channel>() {
- @Override
- public int compare(Channel lhs, Channel rhs) {
- return ChannelNumber.compare(lhs.getDisplayNumber(), rhs.getDisplayNumber());
- }
- };
-
- /**
- * When a TIS doesn't provide any information about app link, and it doesn't have a leanback
- * launch intent, there will be no app link card for the TIS.
- */
- public static final int APP_LINK_TYPE_NONE = -1;
- /**
- * When a TIS provide a specific app link information, the app link card will be
- * {@code APP_LINK_TYPE_CHANNEL} which contains all the provided information.
- */
- public static final int APP_LINK_TYPE_CHANNEL = 1;
- /**
- * When a TIS doesn't provide a specific app link information, but the app has a leanback launch
- * intent, the app link card will be {@code APP_LINK_TYPE_APP} which launches the application.
- */
- public static final int APP_LINK_TYPE_APP = 2;
+ /** Compares the channel numbers of channels which belong to the same input. */
+ public static final Comparator<Channel> CHANNEL_NUMBER_COMPARATOR =
+ new Comparator<Channel>() {
+ @Override
+ public int compare(Channel lhs, Channel rhs) {
+ return ChannelNumber.compare(lhs.getDisplayNumber(), rhs.getDisplayNumber());
+ }
+ };
private static final int APP_LINK_TYPE_NOT_SET = 0;
private static final String INVALID_PACKAGE_NAME = "packageName";
public static final String[] PROJECTION = {
- // 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_SEARCHABLE,
- TvContract.Channels.COLUMN_LOCKED,
- TvContract.Channels.COLUMN_APP_LINK_TEXT,
- TvContract.Channels.COLUMN_APP_LINK_COLOR,
- TvContract.Channels.COLUMN_APP_LINK_ICON_URI,
- TvContract.Channels.COLUMN_APP_LINK_POSTER_ART_URI,
- TvContract.Channels.COLUMN_APP_LINK_INTENT_URI,
- TvContract.Channels.COLUMN_INTERNAL_PROVIDER_FLAG2, // Only used in bundled input
+ // Columns must match what is read in ChannelImpl.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_SEARCHABLE,
+ TvContract.Channels.COLUMN_LOCKED,
+ TvContract.Channels.COLUMN_APP_LINK_TEXT,
+ TvContract.Channels.COLUMN_APP_LINK_COLOR,
+ TvContract.Channels.COLUMN_APP_LINK_ICON_URI,
+ TvContract.Channels.COLUMN_APP_LINK_POSTER_ART_URI,
+ TvContract.Channels.COLUMN_APP_LINK_INTENT_URI,
+ TvContract.Channels.COLUMN_INTERNAL_PROVIDER_FLAG2, // Only used in bundled input
};
/**
- * Channel number delimiter between major and minor parts.
- */
- public static final char CHANNEL_NUMBER_DELIMITER = '-';
-
- /**
- * Creates {@code Channel} object from cursor.
+ * Creates {@code ChannelImpl} object from cursor.
*
* <p>The query that created the cursor MUST use {@link #PROJECTION}
- *
*/
- public static Channel fromCursor(Cursor cursor) {
+ public static ChannelImpl fromCursor(Cursor cursor) {
// Columns read must match the order of {@link #PROJECTION}
- Channel channel = new Channel();
+ ChannelImpl channel = new ChannelImpl();
int index = 0;
channel.mId = cursor.getLong(index++);
channel.mPackageName = Utils.intern(cursor.getString(index++));
@@ -132,21 +102,20 @@ public final class Channel {
channel.mAppLinkIconUri = cursor.getString(index++);
channel.mAppLinkPosterArtUri = cursor.getString(index++);
channel.mAppLinkIntentUri = cursor.getString(index++);
- if (Utils.isBundledInput(channel.mInputId)) {
+ if (CommonUtils.isBundledInput(channel.mInputId)) {
channel.mRecordingProhibited = cursor.getInt(index++) != 0;
}
return channel;
}
- /**
- * Replaces the channel number separator with dash('-').
- */
+ /** Replaces the channel number separator with dash('-'). */
public static String normalizeDisplayNumber(String string) {
if (!TextUtils.isEmpty(string)) {
int length = string.length();
for (int i = 0; i < length; i++) {
char c = string.charAt(i);
- if (c == '.' || Character.isWhitespace(c)
+ if (c == '.'
+ || Character.isWhitespace(c)
|| Character.getType(c) == Character.DASH_PUNCTUATION) {
StringBuilder sb = new StringBuilder(string);
sb.setCharAt(i, CHANNEL_NUMBER_DELIMITER);
@@ -183,14 +152,16 @@ public final class Channel {
private boolean mChannelLogoExist;
- private Channel() {
+ private ChannelImpl() {
// Do nothing.
}
+ @Override
public long getId() {
return mId;
}
+ @Override
public Uri getUri() {
if (isPassthrough()) {
return TvContract.buildChannelUriForPassthroughInput(mInputId);
@@ -199,97 +170,110 @@ public final class Channel {
}
}
+ @Override
public String getPackageName() {
return mPackageName;
}
+ @Override
public String getInputId() {
return mInputId;
}
+ @Override
public String getType() {
return mType;
}
+ @Override
public String getDisplayNumber() {
return mDisplayNumber;
}
+ @Override
@Nullable
public String getDisplayName() {
return mDisplayName;
}
+ @Override
public String getDescription() {
return mDescription;
}
+ @Override
public String getVideoFormat() {
return mVideoFormat;
}
+ @Override
public boolean isPassthrough() {
return mIsPassthrough;
}
/**
- * Gets identification text for displaying or debugging.
- * It's made from Channels' display number plus their display name.
+ * Gets identification text for displaying or debugging. It's made from Channels' display number
+ * plus their display name.
*/
+ @Override
public String getDisplayText() {
- return TextUtils.isEmpty(mDisplayName) ? mDisplayNumber
+ return TextUtils.isEmpty(mDisplayName)
+ ? mDisplayNumber
: mDisplayNumber + " " + mDisplayName;
}
+ @Override
public String getAppLinkText() {
return mAppLinkText;
}
+ @Override
public int getAppLinkColor() {
return mAppLinkColor;
}
+ @Override
public String getAppLinkIconUri() {
return mAppLinkIconUri;
}
+ @Override
public String getAppLinkPosterArtUri() {
return mAppLinkPosterArtUri;
}
+ @Override
public String getAppLinkIntentUri() {
return mAppLinkIntentUri;
}
- /**
- * Returns channel logo uri which is got from cloud, it's used only for ChannelLogoFetcher.
- */
+ /** Returns channel logo uri which is got from cloud, it's used only for ChannelLogoFetcher. */
+ @Override
public String getLogoUri() {
return mLogoUri;
}
+ @Override
public boolean isRecordingProhibited() {
return mRecordingProhibited;
}
- /**
- * Checks whether this channel is physical tuner channel or not.
- */
+ /** Checks whether this channel is physical tuner channel or not. */
+ @Override
public boolean isPhysicalTunerChannel() {
return !TextUtils.isEmpty(mType) && !TvContract.Channels.TYPE_OTHER.equals(mType);
}
- /**
- * Checks if two channels equal by checking ids.
- */
+ /** Checks if two channels equal by checking ids. */
@Override
public boolean equals(Object o) {
- if (!(o instanceof Channel)) {
+ if (!(o instanceof ChannelImpl)) {
return false;
}
- Channel other = (Channel) o;
+ ChannelImpl other = (ChannelImpl) o;
// All pass-through TV channels have INVALID_ID value for mId.
- return mId == other.mId && TextUtils.equals(mInputId, other.mInputId)
+ return mId == other.mId
+ && TextUtils.equals(mInputId, other.mInputId)
&& mIsPassthrough == other.mIsPassthrough;
}
@@ -298,15 +282,18 @@ public final class Channel {
return Objects.hash(mId, mInputId, mIsPassthrough);
}
+ @Override
public boolean isBrowsable() {
return mBrowsable;
}
/** Checks whether this channel is searchable or not. */
+ @Override
public boolean isSearchable() {
return mSearchable;
}
+ @Override
public boolean isLocked() {
return mLocked;
}
@@ -319,9 +306,7 @@ public final class Channel {
mLocked = locked;
}
- /**
- * Sets channel logo uri which is got from cloud.
- */
+ /** Sets channel logo uri which is got from cloud. */
public void setLogoUri(String logoUri) {
mLogoUri = logoUri;
}
@@ -331,45 +316,91 @@ public final class Channel {
* channels have same logos. It also excludes browsable and locked, because two fields are
* changed by TV app.
*/
+ @Override
public boolean hasSameReadOnlyInfo(Channel other) {
return other != null
- && Objects.equals(mId, other.mId)
- && Objects.equals(mPackageName, other.mPackageName)
- && Objects.equals(mInputId, other.mInputId)
- && Objects.equals(mType, other.mType)
- && Objects.equals(mDisplayNumber, other.mDisplayNumber)
- && Objects.equals(mDisplayName, other.mDisplayName)
- && Objects.equals(mDescription, other.mDescription)
- && Objects.equals(mVideoFormat, other.mVideoFormat)
- && mIsPassthrough == other.mIsPassthrough
- && Objects.equals(mAppLinkText, other.mAppLinkText)
- && mAppLinkColor == other.mAppLinkColor
- && Objects.equals(mAppLinkIconUri, other.mAppLinkIconUri)
- && Objects.equals(mAppLinkPosterArtUri, other.mAppLinkPosterArtUri)
- && Objects.equals(mAppLinkIntentUri, other.mAppLinkIntentUri)
- && Objects.equals(mRecordingProhibited, other.mRecordingProhibited);
+ && Objects.equals(mId, other.getId())
+ && Objects.equals(mPackageName, other.getPackageName())
+ && Objects.equals(mInputId, other.getInputId())
+ && Objects.equals(mType, other.getType())
+ && Objects.equals(mDisplayNumber, other.getDisplayNumber())
+ && Objects.equals(mDisplayName, other.getDisplayName())
+ && Objects.equals(mDescription, other.getDescription())
+ && Objects.equals(mVideoFormat, other.getVideoFormat())
+ && mIsPassthrough == other.isPassthrough()
+ && Objects.equals(mAppLinkText, other.getAppLinkText())
+ && mAppLinkColor == other.getAppLinkColor()
+ && Objects.equals(mAppLinkIconUri, other.getAppLinkIconUri())
+ && Objects.equals(mAppLinkPosterArtUri, other.getAppLinkPosterArtUri())
+ && Objects.equals(mAppLinkIntentUri, other.getAppLinkIntentUri())
+ && Objects.equals(mRecordingProhibited, other.isRecordingProhibited());
}
@Override
public String toString() {
return "Channel{"
- + "id=" + mId
- + ", packageName=" + mPackageName
- + ", inputId=" + mInputId
- + ", type=" + mType
- + ", displayNumber=" + mDisplayNumber
- + ", displayName=" + mDisplayName
- + ", description=" + mDescription
- + ", videoFormat=" + mVideoFormat
- + ", isPassthrough=" + mIsPassthrough
- + ", browsable=" + mBrowsable
- + ", searchable=" + mSearchable
- + ", locked=" + mLocked
- + ", appLinkText=" + mAppLinkText
- + ", recordingProhibited=" + mRecordingProhibited + "}";
- }
-
- void copyFrom(Channel other) {
+ + "id="
+ + mId
+ + ", packageName="
+ + mPackageName
+ + ", inputId="
+ + mInputId
+ + ", type="
+ + mType
+ + ", displayNumber="
+ + mDisplayNumber
+ + ", displayName="
+ + mDisplayName
+ + ", description="
+ + mDescription
+ + ", videoFormat="
+ + mVideoFormat
+ + ", isPassthrough="
+ + mIsPassthrough
+ + ", browsable="
+ + mBrowsable
+ + ", searchable="
+ + mSearchable
+ + ", locked="
+ + mLocked
+ + ", appLinkText="
+ + mAppLinkText
+ + ", recordingProhibited="
+ + mRecordingProhibited
+ + "}";
+ }
+
+ @Override
+ public void copyFrom(Channel channel) {
+ if (channel instanceof ChannelImpl) {
+ copyFrom((ChannelImpl) channel);
+ } else {
+ // copy what we can
+ mId = channel.getId();
+ mPackageName = channel.getPackageName();
+ mInputId = channel.getInputId();
+ mType = channel.getType();
+ mDisplayNumber = channel.getDisplayNumber();
+ mDisplayName = channel.getDisplayName();
+ mDescription = channel.getDescription();
+ mVideoFormat = channel.getVideoFormat();
+ mIsPassthrough = channel.isPassthrough();
+ mBrowsable = channel.isBrowsable();
+ mSearchable = channel.isSearchable();
+ mLocked = channel.isLocked();
+ mAppLinkText = channel.getAppLinkText();
+ mAppLinkColor = channel.getAppLinkColor();
+ mAppLinkIconUri = channel.getAppLinkIconUri();
+ mAppLinkPosterArtUri = channel.getAppLinkPosterArtUri();
+ mAppLinkIntentUri = channel.getAppLinkIntentUri();
+ mRecordingProhibited = channel.isRecordingProhibited();
+ mChannelLogoExist = channel.channelLogoExists();
+ }
+ }
+
+ @SuppressWarnings("ReferenceEquality")
+ public void copyFrom(ChannelImpl channel) {
+ ChannelImpl other = (ChannelImpl) channel;
if (this == other) {
return;
}
@@ -396,10 +427,8 @@ public final class Channel {
mChannelLogoExist = other.mChannelLogoExist;
}
- /**
- * Creates a channel for a passthrough TV input.
- */
- public static Channel createPassthroughChannel(Uri uri) {
+ /** Creates a channel for a passthrough TV input. */
+ public static ChannelImpl createPassthroughChannel(Uri uri) {
if (!TvContract.isChannelUriForPassthroughInput(uri)) {
throw new IllegalArgumentException("URI is not a passthrough channel URI");
}
@@ -407,33 +436,25 @@ public final class Channel {
return createPassthroughChannel(inputId);
}
- /**
- * Creates a channel for a passthrough TV input with {@code inputId}.
- */
- public static Channel createPassthroughChannel(String inputId) {
- return new Builder()
- .setInputId(inputId)
- .setPassthrough(true)
- .build();
+ /** Creates a channel for a passthrough TV input with {@code inputId}. */
+ public static ChannelImpl createPassthroughChannel(String inputId) {
+ return new Builder().setInputId(inputId).setPassthrough(true).build();
}
- /**
- * Checks whether the channel is valid or not.
- */
+ /** Checks whether the channel is valid or not. */
public static boolean isValid(Channel channel) {
- return channel != null && (channel.mId != INVALID_ID || channel.mIsPassthrough);
+ return channel != null && (channel.getId() != INVALID_ID || channel.isPassthrough());
}
/**
- * Builder class for {@code Channel}.
- * Suppress using this outside of ChannelDataManager
- * so Channels could be managed by ChannelDataManager.
+ * Builder class for {@code ChannelImpl}. Suppress using this outside of ChannelDataManager so
+ * Channels could be managed by ChannelDataManager.
*/
public static final class Builder {
- private final Channel mChannel;
+ private final ChannelImpl mChannel;
public Builder() {
- mChannel = new Channel();
+ mChannel = new ChannelImpl();
// Fill initial data.
mChannel.mId = INVALID_ID;
mChannel.mPackageName = INVALID_PACKAGE_NAME;
@@ -447,7 +468,7 @@ public final class Channel {
}
public Builder(Channel other) {
- mChannel = new Channel();
+ mChannel = new ChannelImpl();
mChannel.copyFrom(other);
}
@@ -548,16 +569,14 @@ public final class Channel {
return this;
}
- public Channel build() {
- Channel channel = new Channel();
+ public ChannelImpl build() {
+ ChannelImpl channel = new ChannelImpl();
channel.copyFrom(mChannel);
return channel;
}
}
- /**
- * Prefetches the images for this channel.
- */
+ /** Prefetches the images for this channel. */
public void prefetchImage(Context context, int type, int maxWidth, int maxHeight) {
String uriString = getImageUriString(type);
if (!TextUtils.isEmpty(uriString)) {
@@ -566,46 +585,49 @@ public final class Channel {
}
/**
- * Loads the bitmap of this channel and returns it via {@code callback}.
- * The loaded bitmap will be cached and resized with given params.
- * <p>
- * Note that it may directly call {@code callback} if the bitmap is already loaded.
+ * Loads the bitmap of this channel and returns it via {@code callback}. The loaded bitmap will
+ * be cached and resized with given params.
+ *
+ * <p>Note that it may directly call {@code callback} if the bitmap is already loaded.
*
* @param context A context.
- * @param type The type of bitmap which will be loaded. It should be one of follows:
- * {@link #LOAD_IMAGE_TYPE_CHANNEL_LOGO}, {@link #LOAD_IMAGE_TYPE_APP_LINK_ICON}, or
- * {@link #LOAD_IMAGE_TYPE_APP_LINK_POSTER_ART}.
+ * @param type The type of bitmap which will be loaded. It should be one of follows: {@link
+ * Channel#LOAD_IMAGE_TYPE_CHANNEL_LOGO}, {@link Channel#LOAD_IMAGE_TYPE_APP_LINK_ICON}, or
+ * {@link Channel#LOAD_IMAGE_TYPE_APP_LINK_POSTER_ART}.
* @param maxWidth The max width of the loaded bitmap.
* @param maxHeight The max height of the loaded bitmap.
* @param callback A callback which will be called after the loading finished.
*/
@UiThread
- public void loadBitmap(Context context, final int type, int maxWidth, int maxHeight,
+ public void loadBitmap(
+ Context context,
+ final int type,
+ int maxWidth,
+ int maxHeight,
ImageLoader.ImageLoaderCallback callback) {
String uriString = getImageUriString(type);
ImageLoader.loadBitmap(context, uriString, maxWidth, maxHeight, callback);
}
/**
- * Sets if the channel logo exists. This method should be only called from
- * {@link ChannelDataManager}.
+ * Sets if the channel logo exists. This method should be only called from {@link
+ * ChannelDataManager}.
*/
- void setChannelLogoExist(boolean exist) {
+ @Override
+ public void setChannelLogoExist(boolean exist) {
mChannelLogoExist = exist;
}
- /**
- * Returns if channel logo exists.
- */
+ /** Returns if channel logo exists. */
public boolean channelLogoExists() {
return mChannelLogoExist;
}
/**
- * Returns the type of app link for this channel.
- * It returns {@link #APP_LINK_TYPE_CHANNEL} if the channel has a non null app link text and
- * a valid app link intent, it returns {@link #APP_LINK_TYPE_APP} if the input service which
- * holds the channel has leanback launch intent, and it returns {@link #APP_LINK_TYPE_NONE}
+ * Returns the type of app link for this channel. It returns {@link
+ * Channel#APP_LINK_TYPE_CHANNEL} if the channel has a non null app link text and a valid app
+ * link intent, it returns {@link Channel#APP_LINK_TYPE_APP} if the input service which holds
+ * the channel has leanback launch intent, and it returns {@link Channel#APP_LINK_TYPE_NONE}
* otherwise.
*/
public int getAppLinkType(Context context) {
@@ -616,8 +638,8 @@ public final class Channel {
}
/**
- * Returns the app link intent for this channel.
- * If the type of app link is {@link #APP_LINK_TYPE_NONE}, it returns {@code null}.
+ * Returns the app link intent for this channel. If the type of app link is {@link
+ * Channel#APP_LINK_TYPE_NONE}, it returns {@code null}.
*/
public Intent getAppLinkIntent(Context context) {
if (mAppLinkType == APP_LINK_TYPE_NOT_SET) {
@@ -635,8 +657,8 @@ public final class Channel {
Intent intent = Intent.parseUri(mAppLinkIntentUri, Intent.URI_INTENT_SCHEME);
if (intent.resolveActivityInfo(pm, 0) != null) {
mAppLinkIntent = intent;
- mAppLinkIntent.putExtra(TvCommonConstants.EXTRA_APP_LINK_CHANNEL_URI,
- getUri().toString());
+ mAppLinkIntent.putExtra(
+ CommonConstants.EXTRA_APP_LINK_CHANNEL_URI, getUri().toString());
mAppLinkType = APP_LINK_TYPE_CHANNEL;
return;
} else {
@@ -652,8 +674,8 @@ public final class Channel {
}
mAppLinkIntent = pm.getLeanbackLaunchIntentForPackage(mPackageName);
if (mAppLinkIntent != null) {
- mAppLinkIntent.putExtra(TvCommonConstants.EXTRA_APP_LINK_CHANNEL_URI,
- getUri().toString());
+ mAppLinkIntent.putExtra(
+ CommonConstants.EXTRA_APP_LINK_CHANNEL_URI, getUri().toString());
mAppLinkType = APP_LINK_TYPE_APP;
}
}
@@ -670,6 +692,17 @@ public final class Channel {
return null;
}
+ /**
+ * Default Channel ordering.
+ *
+ * <p>Ordering
+ * <li>{@link TvInputManagerHelper#isPartnerInput(String)}
+ * <li>{@link #getInputLabelForChannel(Channel)}
+ * <li>{@link #getInputId()}
+ * <li>{@link ChannelNumber#compare(String, String)}
+ * <li>
+ * </ol>
+ */
public static class DefaultComparator implements Comparator<Channel> {
private final Context mContext;
private final TvInputManagerHelper mInputManager;
@@ -685,6 +718,7 @@ public final class Channel {
mDetectDuplicatesEnabled = detectDuplicatesEnabled;
}
+ @SuppressWarnings("ReferenceEquality")
@Override
public int compare(Channel lhs, Channel rhs) {
if (lhs == rhs) {
@@ -699,8 +733,10 @@ public final class Channel {
// Compare the input labels.
String lhsLabel = getInputLabelForChannel(lhs);
String rhsLabel = getInputLabelForChannel(rhs);
- int result = lhsLabel == null ? (rhsLabel == null ? 0 : 1) : rhsLabel == null ? -1
- : lhsLabel.compareTo(rhsLabel);
+ int result =
+ lhsLabel == null
+ ? (rhsLabel == null ? 0 : 1)
+ : rhsLabel == null ? -1 : lhsLabel.compareTo(rhsLabel);
if (result != 0) {
return result;
}
@@ -712,8 +748,13 @@ public final class Channel {
// Compare the channel numbers if both channels belong to the same input.
result = ChannelNumber.compare(lhs.getDisplayNumber(), rhs.getDisplayNumber());
if (mDetectDuplicatesEnabled && result == 0) {
- Log.w(TAG, "Duplicate channels detected! - \""
- + lhs.getDisplayText() + "\" and \"" + rhs.getDisplayText() + "\"");
+ Log.w(
+ TAG,
+ "Duplicate channels detected! - \""
+ + lhs.getDisplayText()
+ + "\" and \""
+ + rhs.getDisplayText()
+ + "\"");
}
return result;
}
@@ -733,4 +774,4 @@ public final class Channel {
return label;
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/data/ChannelLogoFetcher.java b/src/com/android/tv/data/ChannelLogoFetcher.java
index 132cab7a..89d1e36c 100644
--- a/src/com/android/tv/data/ChannelLogoFetcher.java
+++ b/src/com/android/tv/data/ChannelLogoFetcher.java
@@ -28,17 +28,16 @@ import android.os.RemoteException;
import android.support.annotation.MainThread;
import android.text.TextUtils;
import android.util.Log;
-
-import com.android.tv.common.SharedPreferencesUtils;
-import com.android.tv.util.BitmapUtils;
-import com.android.tv.util.BitmapUtils.ScaledBitmapInfo;
-import com.android.tv.util.PermissionUtils;
-
+import com.android.tv.common.util.PermissionUtils;
+import com.android.tv.common.util.SharedPreferencesUtils;
+import com.android.tv.data.api.Channel;
+import com.android.tv.util.images.BitmapUtils;
+import com.android.tv.util.images.BitmapUtils.ScaledBitmapInfo;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
-import java.util.Map;
import java.util.List;
+import java.util.Map;
/**
* Fetches channel logos from the cloud into the database. It's for the channels which have no logos
@@ -54,12 +53,11 @@ public class ChannelLogoFetcher {
private static FetchLogoTask sFetchTask;
/**
- * Fetches the channel logos from the cloud data and insert them into TvProvider.
- * The previous task is canceled and a new task starts.
+ * Fetches the channel logos from the cloud data and insert them into TvProvider. The previous
+ * task is canceled and a new task starts.
*/
@MainThread
- public static void startFetchingChannelLogos(
- Context context, List<Channel> channels) {
+ public static void startFetchingChannelLogos(Context context, List<Channel> channels) {
if (!PermissionUtils.hasAccessAllEpg(context)) {
// TODO: support this feature for non-system LC app. b/23939816
return;
@@ -76,8 +74,7 @@ public class ChannelLogoFetcher {
sFetchTask.execute();
}
- private ChannelLogoFetcher() {
- }
+ private ChannelLogoFetcher() {}
private static final class FetchLogoTask extends AsyncTask<Void, Void, Void> {
private final Context mContext;
@@ -105,8 +102,8 @@ public class ChannelLogoFetcher {
Context.MODE_PRIVATE);
SharedPreferences.Editor sharedPreferencesEditor = sharedPreferences.edit();
Map<String, ?> uncheckedChannels = sharedPreferences.getAll();
- boolean isFirstTimeFetchChannelLogo = sharedPreferences.getBoolean(
- PREF_KEY_IS_FIRST_TIME_FETCH_CHANNEL_LOGO, true);
+ boolean isFirstTimeFetchChannelLogo =
+ sharedPreferences.getBoolean(PREF_KEY_IS_FIRST_TIME_FETCH_CHANNEL_LOGO, true);
// Iterating channels.
for (Channel channel : mChannels) {
String channelIdString = Long.toString(channel.getId());
@@ -117,7 +114,7 @@ public class ChannelLogoFetcher {
sharedPreferencesEditor.putString(channelIdString, channel.getLogoUri());
} else if (TextUtils.isEmpty(channel.getLogoUri())
&& (!TextUtils.isEmpty(storedChannelLogoUri)
- || isFirstTimeFetchChannelLogo)) {
+ || isFirstTimeFetchChannelLogo)) {
channelsToRemove.add(channel);
sharedPreferencesEditor.remove(channelIdString);
}
@@ -136,11 +133,18 @@ public class ChannelLogoFetcher {
}
// Downloads the channel logo.
String logoUri = channel.getLogoUri();
- ScaledBitmapInfo bitmapInfo = BitmapUtils.decodeSampledBitmapFromUriString(
- mContext, logoUri, Integer.MAX_VALUE, Integer.MAX_VALUE);
+ ScaledBitmapInfo bitmapInfo =
+ BitmapUtils.decodeSampledBitmapFromUriString(
+ mContext, logoUri, Integer.MAX_VALUE, Integer.MAX_VALUE);
if (bitmapInfo == null) {
- Log.e(TAG, "Failed to load bitmap. {channelName=" + channel.getDisplayName()
- + ", " + "logoUri=" + logoUri + "}");
+ Log.e(
+ TAG,
+ "Failed to load bitmap. {channelName="
+ + channel.getDisplayName()
+ + ", "
+ + "logoUri="
+ + logoUri
+ + "}");
continue;
}
if (isCancelled()) {
@@ -160,8 +164,13 @@ public class ChannelLogoFetcher {
continue;
}
if (DEBUG) {
- Log.d(TAG, "Inserting logo file to DB succeeded. {from=" + logoUri + ", to="
- + dstLogoUri + "}");
+ Log.d(
+ TAG,
+ "Inserting logo file to DB succeeded. {from="
+ + logoUri
+ + ", to="
+ + dstLogoUri
+ + "}");
}
}
@@ -171,8 +180,10 @@ public class ChannelLogoFetcher {
if (!channelsToRemove.isEmpty()) {
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
for (Channel channel : channelsToRemove) {
- ops.add(ContentProviderOperation.newDelete(
- TvContract.buildChannelLogoUri(channel.getId())).build());
+ ops.add(
+ ContentProviderOperation.newDelete(
+ TvContract.buildChannelLogoUri(channel.getId()))
+ .build());
}
try {
mContext.getContentResolver().applyBatch(TvContract.AUTHORITY, ops);
diff --git a/src/com/android/tv/data/ChannelNumber.java b/src/com/android/tv/data/ChannelNumber.java
index 29054aa5..afdcc580 100644
--- a/src/com/android/tv/data/ChannelNumber.java
+++ b/src/com/android/tv/data/ChannelNumber.java
@@ -19,18 +19,18 @@ package com.android.tv.data;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.view.KeyEvent;
-
-import com.android.tv.util.StringUtils;
-
+import com.android.tv.common.util.StringUtils;
+import com.android.tv.data.api.Channel;
import java.util.Objects;
-/**
- * A convenience class to handle channel number.
- */
+/** A convenience class to handle channel number. */
public final class ChannelNumber implements Comparable<ChannelNumber> {
private static final int[] CHANNEL_DELIMITER_KEYCODES = {
- KeyEvent.KEYCODE_MINUS, KeyEvent.KEYCODE_NUMPAD_SUBTRACT, KeyEvent.KEYCODE_PERIOD,
- KeyEvent.KEYCODE_NUMPAD_DOT, KeyEvent.KEYCODE_SPACE
+ KeyEvent.KEYCODE_MINUS,
+ KeyEvent.KEYCODE_NUMPAD_SUBTRACT,
+ KeyEvent.KEYCODE_PERIOD,
+ KeyEvent.KEYCODE_NUMPAD_DOT,
+ KeyEvent.KEYCODE_SPACE
};
/** The major part of the channel number. */
@@ -44,6 +44,23 @@ public final class ChannelNumber implements Comparable<ChannelNumber> {
reset();
}
+ /**
+ * {@code lhs} and {@code rhs} are equivalent if {@link ChannelNumber#compare(String, String)}
+ * is 0 or if only one has a delimiter and both {@link ChannelNumber#majorNumber} equals.
+ */
+ public static boolean equivalent(String lhs, String rhs) {
+ if (compare(lhs, rhs) == 0) {
+ return true;
+ }
+ // Match if only one has delimiter
+ ChannelNumber lhsNumber = parseChannelNumber(lhs);
+ ChannelNumber rhsNumber = parseChannelNumber(rhs);
+ return lhsNumber != null
+ && rhsNumber != null
+ && lhsNumber.hasDelimiter != rhsNumber.hasDelimiter
+ && lhsNumber.majorNumber.equals(rhsNumber.majorNumber);
+ }
+
public void reset() {
setChannelNumber("", false, "");
}
@@ -68,8 +85,7 @@ public final class ChannelNumber implements Comparable<ChannelNumber> {
int minor = hasDelimiter ? Integer.parseInt(minorNumber) : 0;
int opponentMajor = Integer.parseInt(another.majorNumber);
- int opponentMinor = another.hasDelimiter
- ? Integer.parseInt(another.minorNumber) : 0;
+ int opponentMinor = another.hasDelimiter ? Integer.parseInt(another.minorNumber) : 0;
if (major == opponentMajor) {
return minor - opponentMinor;
}
@@ -103,10 +119,10 @@ public final class ChannelNumber implements Comparable<ChannelNumber> {
/**
* Returns the ChannelNumber instance.
- * <p>
- * Note that all the channel number argument should be normalized by
- * {@link Channel#normalizeDisplayNumber}. The channels retrieved from
- * {@link ChannelDataManager} are already normalized.
+ *
+ * <p>Note that all the channel number argument should be normalized by {@link
+ * ChannelImpl#normalizeDisplayNumber}. The channels retrieved from {@link ChannelDataManager}
+ * are already normalized.
*/
public static ChannelNumber parseChannelNumber(String number) {
if (number == null) {
@@ -134,10 +150,10 @@ public final class ChannelNumber implements Comparable<ChannelNumber> {
/**
* Compares the channel numbers.
- * <p>
- * Note that all the channel number arguments should be normalized by
- * {@link Channel#normalizeDisplayNumber}. The channels retrieved from
- * {@link ChannelDataManager} are already normalized.
+ *
+ * <p>Note that all the channel number arguments should be normalized by {@link
+ * ChannelImpl#normalizeDisplayNumber}. The channels retrieved from {@link ChannelDataManager}
+ * are already normalized.
*/
public static int compare(String lhs, String rhs) {
ChannelNumber lhsNumber = parseChannelNumber(lhs);
@@ -156,7 +172,7 @@ public final class ChannelNumber implements Comparable<ChannelNumber> {
private static boolean isInteger(String string) {
try {
Integer.parseInt(string);
- } catch(NumberFormatException | NullPointerException e) {
+ } catch (NumberFormatException | NullPointerException e) {
return false;
}
return true;
diff --git a/src/com/android/tv/data/DisplayMode.java b/src/com/android/tv/data/DisplayMode.java
index ccba5480..dbcca010 100644
--- a/src/com/android/tv/data/DisplayMode.java
+++ b/src/com/android/tv/data/DisplayMode.java
@@ -17,7 +17,6 @@
package com.android.tv.data;
import android.content.Context;
-
import com.android.tv.R;
public class DisplayMode {
@@ -28,12 +27,10 @@ public class DisplayMode {
public static final int MODE_ZOOM = 2;
public static final int SIZE_OF_RATIO_TYPES = MODE_ZOOM + 1;
- /**
- * Constant to indicate that any mode is not set yet.
- */
+ /** Constant to indicate that any mode is not set yet. */
public static final int MODE_NOT_DEFINED = -1;
- private DisplayMode() { }
+ private DisplayMode() {}
public static String getLabel(int mode, Context context) {
return context.getResources().getStringArray(R.array.display_mode_labels)[mode];
diff --git a/src/com/android/tv/data/GenreItems.java b/src/com/android/tv/data/GenreItems.java
index b12fd1aa..dfecb63c 100644
--- a/src/com/android/tv/data/GenreItems.java
+++ b/src/com/android/tv/data/GenreItems.java
@@ -18,13 +18,10 @@ package com.android.tv.data;
import android.content.Context;
import android.media.tv.TvContract.Programs.Genres;
-
import com.android.tv.R;
public class GenreItems {
- /**
- * Genre ID indicating all channels.
- */
+ /** Genre ID indicating all channels. */
public static final int ID_ALL_CHANNELS = 0;
private static final String[] CANONICAL_GENRES = {
@@ -48,11 +45,9 @@ public class GenreItems {
Genres.TECH_SCIENCE
};
- private GenreItems() { }
+ private GenreItems() {}
- /**
- * Returns array of all genre labels.
- */
+ /** Returns array of all genre labels. */
public static String[] getLabels(Context context) {
String[] items = context.getResources().getStringArray(R.array.genre_labels);
if (items.length != CANONICAL_GENRES.length) {
@@ -61,16 +56,14 @@ public class GenreItems {
return items;
}
- /**
- * Returns the number of genres including all channels.
- */
+ /** Returns the number of genres including all channels. */
public static int getGenreCount() {
return CANONICAL_GENRES.length;
}
/**
- * Returns the canonical genre for the given id.
- * If the id is invalid, {@code null} will be returned instead.
+ * Returns the canonical genre for the given id. If the id is invalid, {@code null} will be
+ * returned instead.
*/
public static String getCanonicalGenre(int id) {
if (id < 0 || id >= CANONICAL_GENRES.length) {
@@ -80,8 +73,8 @@ public class GenreItems {
}
/**
- * Returns id for the given canonical genre.
- * If the genre is invalid, {@link #ID_ALL_CHANNELS} will be returned instead.
+ * Returns id for the given canonical genre. If the genre is invalid, {@link #ID_ALL_CHANNELS}
+ * will be returned instead.
*/
public static int getId(String canonicalGenre) {
if (canonicalGenre == null) {
diff --git a/src/com/android/tv/data/InternalDataUtils.java b/src/com/android/tv/data/InternalDataUtils.java
index e33ca18f..4c30d395 100644
--- a/src/com/android/tv/data/InternalDataUtils.java
+++ b/src/com/android/tv/data/InternalDataUtils.java
@@ -19,10 +19,8 @@ package com.android.tv.data;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
-
import com.android.tv.data.Program.CriticScore;
import com.android.tv.dvr.data.RecordedProgram;
-
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -31,22 +29,22 @@ import java.io.ObjectOutputStream;
import java.util.List;
/**
- * A utility class to parse and store data from the
- * {@link android.media.tv.TvContract.Programs#COLUMN_INTERNAL_PROVIDER_DATA} field in the
- * {@link android.media.tv.TvContract.Programs}.
+ * A utility class to parse and store data from the {@link
+ * android.media.tv.TvContract.Programs#COLUMN_INTERNAL_PROVIDER_DATA} field in the {@link
+ * android.media.tv.TvContract.Programs}.
*/
public final class InternalDataUtils {
private static final boolean DEBUG = false;
private static final String TAG = "InternalDataUtils";
private InternalDataUtils() {
- //do nothing
+ // do nothing
}
/**
* Deserializes a byte array into objects to be stored in the Program class.
*
- * <p> Series ID and critic scores are loaded from the bytes.
+ * <p>Series ID and critic scores are loaded from the bytes.
*
* @param bytes the bytes to be deserialized
* @param builder the builder for the Program class
@@ -70,6 +68,7 @@ public final class InternalDataUtils {
/**
* Convenience method for converting relevant data in Program class to a serialized blob type
* for storage in internal_provider_data field.
+ *
* @param program the program which contains the objects to be serialized
* @return serialized blob-type data
*/
@@ -83,8 +82,10 @@ public final class InternalDataUtils {
return bos.toByteArray();
}
} catch (IOException e) {
- Log.e(TAG, "Could not serialize internal provider contents for program: "
- + program.getTitle());
+ Log.e(
+ TAG,
+ "Could not serialize internal provider contents for program: "
+ + program.getTitle());
}
return null;
}
@@ -92,13 +93,13 @@ public final class InternalDataUtils {
/**
* Deserializes a byte array into objects to be stored in the RecordedProgram class.
*
- * <p> Series ID is loaded from the bytes.
+ * <p>Series ID is loaded from the bytes.
*
* @param bytes the bytes to be deserialized
* @param builder the builder for the RecordedProgram class
*/
- public static void deserializeInternalProviderData(byte[] bytes,
- RecordedProgram.Builder builder) {
+ public static void deserializeInternalProviderData(
+ byte[] bytes, RecordedProgram.Builder builder) {
if (bytes == null || bytes.length == 0) {
return;
}
@@ -115,6 +116,7 @@ public final class InternalDataUtils {
/**
* Serializes relevant objects in {@link android.media.tv.TvContract.Programs} to byte array.
+ *
* @return the serialized byte array
*/
public static byte[] serializeInternalProviderData(RecordedProgram program) {
@@ -125,9 +127,11 @@ public final class InternalDataUtils {
return bos.toByteArray();
}
} catch (IOException e) {
- Log.e(TAG, "Could not serialize internal provider contents for program: "
- + program.getTitle());
+ Log.e(
+ TAG,
+ "Could not serialize internal provider contents for program: "
+ + program.getTitle());
}
return null;
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/data/Lineup.java b/src/com/android/tv/data/Lineup.java
index d0e9d7ba..4393cd3d 100644
--- a/src/com/android/tv/data/Lineup.java
+++ b/src/com/android/tv/data/Lineup.java
@@ -17,78 +17,94 @@
package com.android.tv.data;
import android.support.annotation.IntDef;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.List;
-/**
- * A class that represents a lineup.
- */
+/** A class that represents a lineup. */
public class Lineup {
- /**
- * The ID of this lineup.
- */
- public final String id;
+ /** The ID of this lineup. */
+ public String getId() {
+ return id;
+ }
- /**
- * The type associated with this lineup.
- */
- public final int type;
+ /** The type associated with this lineup. */
+ public int getType() {
+ return type;
+ }
- /**
- * The human readable name associated with this lineup.
- */
- public final String name;
+ /** The human readable name associated with this lineup. */
+ public String getName() {
+ return name;
+ }
- /**
- * Location this lineup can be found.
- * This is a human readable description of a geographic location.
- */
- public final String location;
+ /** The human readable name associated with this lineup. */
+ public String getLocation() {
+ return location;
+ }
+
+ /** An unmodifiable list of channel numbers that this lineup has. */
+ public List<String> getChannels() {
+ return channels;
+ }
+
+ private final String id;
+
+ private final int type;
+
+ private final String name;
+
+ private final String location;
+
+ private final List<String> channels;
@Retention(RetentionPolicy.SOURCE)
- @IntDef({LINEUP_CABLE, LINEUP_SATELLITE, LINEUP_BROADCAST_DIGITAL, LINEUP_BROADCAST_ANALOG,
- LINEUP_IPTV, LINEUP_MVPD})
+ @IntDef({
+ LINEUP_CABLE,
+ LINEUP_SATELLITE,
+ LINEUP_BROADCAST_DIGITAL,
+ LINEUP_BROADCAST_ANALOG,
+ LINEUP_IPTV,
+ LINEUP_MVPD,
+ LINEUP_INTERNET,
+ LINEUP_OTHER
+ })
public @interface LineupType {}
- /**
- * Lineup type for cable.
- */
+ /** Lineup type for cable. */
public static final int LINEUP_CABLE = 0;
- /**
- * Lineup type for satelite.
- */
+ /** Lineup type for satelite. */
public static final int LINEUP_SATELLITE = 1;
- /**
- * Lineup type for broadcast digital.
- */
+ /** Lineup type for broadcast digital. */
public static final int LINEUP_BROADCAST_DIGITAL = 2;
- /**
- * Lineup type for broadcast analog.
- */
+ /** Lineup type for broadcast analog. */
public static final int LINEUP_BROADCAST_ANALOG = 3;
- /**
- * Lineup type for IPTV.
- */
+ /** Lineup type for IPTV. */
public static final int LINEUP_IPTV = 4;
/**
- * Indicates the lineup is either satelite, cable or IPTV but we are not sure which specific
+ * Indicates the lineup is either satellite, cable or IPTV but we are not sure which specific
* type.
- */
+ */
public static final int LINEUP_MVPD = 5;
- /**
- * Creates a lineup.
- */
- public Lineup(String id, int type, String name, String location) {
+ /** Lineup type for Internet. */
+ public static final int LINEUP_INTERNET = 6;
+
+ /** Lineup type for other. */
+ public static final int LINEUP_OTHER = 7;
+
+ /** Creates a lineup. */
+ public Lineup(String id, int type, String name, String location, List<String> channels) {
this.id = id;
this.type = type;
this.name = name;
this.location = location;
+ this.channels = Collections.unmodifiableList(channels);
}
}
diff --git a/src/com/android/tv/data/OnCurrentProgramUpdatedListener.java b/src/com/android/tv/data/OnCurrentProgramUpdatedListener.java
index 77b6c9b8..edb33556 100644
--- a/src/com/android/tv/data/OnCurrentProgramUpdatedListener.java
+++ b/src/com/android/tv/data/OnCurrentProgramUpdatedListener.java
@@ -17,8 +17,6 @@
package com.android.tv.data;
public interface OnCurrentProgramUpdatedListener {
- /**
- * Called when the current program is updated.
- */
+ /** Called when the current program is updated. */
void onCurrentProgramUpdated(long channelId, Program program);
}
diff --git a/src/com/android/tv/data/ParcelableList.java b/src/com/android/tv/data/ParcelableList.java
index 78f444e4..1c1f5f29 100644
--- a/src/com/android/tv/data/ParcelableList.java
+++ b/src/com/android/tv/data/ParcelableList.java
@@ -18,18 +18,13 @@ package com.android.tv.data;
import android.os.Parcel;
import android.os.Parcelable;
-
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
-/**
- * A convenience class for the list of {@link Parcelable}s.
- */
+/** A convenience class for the list of {@link Parcelable}s. */
public final class ParcelableList<T extends Parcelable> implements Parcelable {
- /**
- * Create instance from {@link Parcel}.
- */
+ /** Create instance from {@link Parcel}. */
public static ParcelableList fromParcel(Parcel in) {
ParcelableList list = new ParcelableList();
int length = in.readInt();
@@ -41,32 +36,29 @@ public final class ParcelableList<T extends Parcelable> implements Parcelable {
return list;
}
- /**
- * A creator for {@link ParcelableList}.
- */
- public static final Creator<ParcelableList> CREATOR = new Creator<ParcelableList>() {
- @Override
- public ParcelableList createFromParcel(Parcel in) {
- return ParcelableList.fromParcel(in);
- }
+ /** A creator for {@link ParcelableList}. */
+ public static final Creator<ParcelableList> CREATOR =
+ new Creator<ParcelableList>() {
+ @Override
+ public ParcelableList createFromParcel(Parcel in) {
+ return ParcelableList.fromParcel(in);
+ }
- @Override
- public ParcelableList[] newArray(int size) {
- return new ParcelableList[size];
- }
- };
+ @Override
+ public ParcelableList[] newArray(int size) {
+ return new ParcelableList[size];
+ }
+ };
private final List<T> mList = new ArrayList<>();
- private ParcelableList() { }
+ private ParcelableList() {}
public ParcelableList(Collection<T> initialList) {
mList.addAll(initialList);
}
- /**
- * Returns the list.
- */
+ /** Returns the list. */
public List<T> getList() {
return new ArrayList<T>(mList);
}
diff --git a/src/com/android/tv/data/PreviewDataManager.java b/src/com/android/tv/data/PreviewDataManager.java
index 01a58520..44664dcf 100644
--- a/src/com/android/tv/data/PreviewDataManager.java
+++ b/src/com/android/tv/data/PreviewDataManager.java
@@ -35,10 +35,8 @@ import android.support.media.tv.ChannelLogoUtils;
import android.support.media.tv.PreviewProgram;
import android.util.Log;
import android.util.Pair;
-
import com.android.tv.R;
-import com.android.tv.util.PermissionUtils;
-
+import com.android.tv.common.util.PermissionUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
@@ -46,32 +44,24 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
-/**
- * Class to manage the preview data.
- */
+/** Class to manage the preview data. */
@TargetApi(Build.VERSION_CODES.O)
@MainThread
public class PreviewDataManager {
private static final String TAG = "PreviewDataManager";
- // STOPSHIP: set it to false.
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
- /**
- * Invalid preview channel ID.
- */
+ /** Invalid preview channel ID. */
public static final long INVALID_PREVIEW_CHANNEL_ID = -1;
+
@IntDef({TYPE_DEFAULT_PREVIEW_CHANNEL, TYPE_RECORDED_PROGRAM_PREVIEW_CHANNEL})
@Retention(RetentionPolicy.SOURCE)
- public @interface PreviewChannelType{}
+ public @interface PreviewChannelType {}
- /**
- * Type of default preview channel
- */
- public static final long TYPE_DEFAULT_PREVIEW_CHANNEL = 1;
- /**
- * Type of recorded program channel
- */
- public static final long TYPE_RECORDED_PROGRAM_PREVIEW_CHANNEL = 2;
+ /** Type of default preview channel */
+ public static final int TYPE_DEFAULT_PREVIEW_CHANNEL = 1;
+ /** Type of recorded program channel */
+ public static final int TYPE_RECORDED_PROGRAM_PREVIEW_CHANNEL = 2;
private final Context mContext;
private final ContentResolver mContentResolver;
@@ -80,8 +70,7 @@ public class PreviewDataManager {
private final Set<PreviewDataListener> mPreviewDataListeners = new CopyOnWriteArraySet<>();
private QueryPreviewDataTask mQueryPreviewTask;
- private final Map<Long, CreatePreviewChannelTask> mCreatePreviewChannelTasks =
- new HashMap<>();
+ private final Map<Long, CreatePreviewChannelTask> mCreatePreviewChannelTasks = new HashMap<>();
private final Map<Long, UpdatePreviewProgramTask> mUpdatePreviewProgramTasks = new HashMap<>();
private final int mPreviewChannelLogoWidth;
@@ -90,15 +79,13 @@ public class PreviewDataManager {
public PreviewDataManager(Context context) {
mContext = context.getApplicationContext();
mContentResolver = context.getContentResolver();
- mPreviewChannelLogoWidth = mContext.getResources().getDimensionPixelSize(
- R.dimen.preview_channel_logo_width);
- mPreviewChannelLogoHeight = mContext.getResources().getDimensionPixelSize(
- R.dimen.preview_channel_logo_height);
+ mPreviewChannelLogoWidth =
+ mContext.getResources().getDimensionPixelSize(R.dimen.preview_channel_logo_width);
+ mPreviewChannelLogoHeight =
+ mContext.getResources().getDimensionPixelSize(R.dimen.preview_channel_logo_height);
}
- /**
- * Starts the preview data manager.
- */
+ /** Starts the preview data manager. */
public void start() {
if (mQueryPreviewTask == null) {
mQueryPreviewTask = new QueryPreviewDataTask();
@@ -106,19 +93,17 @@ public class PreviewDataManager {
}
}
- /**
- * Stops the preview data manager.
- */
+ /** Stops the preview data manager. */
public void stop() {
if (mQueryPreviewTask != null) {
mQueryPreviewTask.cancel(true);
}
- for (CreatePreviewChannelTask createPreviewChannelTask
- : mCreatePreviewChannelTasks.values()) {
+ for (CreatePreviewChannelTask createPreviewChannelTask :
+ mCreatePreviewChannelTasks.values()) {
createPreviewChannelTask.cancel(true);
}
- for (UpdatePreviewProgramTask updatePreviewProgramTask
- : mUpdatePreviewProgramTasks.values()) {
+ for (UpdatePreviewProgramTask updatePreviewProgramTask :
+ mUpdatePreviewProgramTasks.values()) {
updatePreviewProgramTask.cancel(true);
}
@@ -127,31 +112,26 @@ public class PreviewDataManager {
mUpdatePreviewProgramTasks.clear();
}
- /**
- * Gets preview channel ID from the preview channel type.
- */
+ /** Gets preview channel ID from the preview channel type. */
public @PreviewChannelType long getPreviewChannelId(long previewChannelType) {
return mPreviewData.getPreviewChannelId(previewChannelType);
}
- /**
- * Creates default preview channel.
- */
+ /** Creates default preview channel. */
public void createDefaultPreviewChannel(
OnPreviewChannelCreationResultListener onPreviewChannelCreationResultListener) {
createPreviewChannel(TYPE_DEFAULT_PREVIEW_CHANNEL, onPreviewChannelCreationResultListener);
}
- /**
- * Creates a preview channel for specific channel type.
- */
- public void createPreviewChannel(@PreviewChannelType long previewChannelType,
+ /** Creates a preview channel for specific channel type. */
+ public void createPreviewChannel(
+ @PreviewChannelType long previewChannelType,
OnPreviewChannelCreationResultListener onPreviewChannelCreationResultListener) {
CreatePreviewChannelTask currentRunningCreateTask =
mCreatePreviewChannelTasks.get(previewChannelType);
if (currentRunningCreateTask == null) {
- CreatePreviewChannelTask createPreviewChannelTask = new CreatePreviewChannelTask(
- previewChannelType);
+ CreatePreviewChannelTask createPreviewChannelTask =
+ new CreatePreviewChannelTask(previewChannelType);
createPreviewChannelTask.addOnPreviewChannelCreationResultListener(
onPreviewChannelCreationResultListener);
createPreviewChannelTask.execute();
@@ -162,32 +142,26 @@ public class PreviewDataManager {
}
}
- /**
- * Returns {@code true} if the preview data is loaded.
- */
+ /** Returns {@code true} if the preview data is loaded. */
public boolean isLoadFinished() {
return mLoadFinished;
}
- /**
- * Adds listener.
- */
+ /** Adds listener. */
public void addListener(PreviewDataListener previewDataListener) {
mPreviewDataListeners.add(previewDataListener);
}
- /**
- * Removes listener.
- */
+ /** Removes listener. */
public void removeListener(PreviewDataListener previewDataListener) {
mPreviewDataListeners.remove(previewDataListener);
}
- /**
- * Updates the preview programs table for a specific preview channel.
- */
- public void updatePreviewProgramsForChannel(long previewChannelId,
- Set<PreviewProgramContent> programs, PreviewDataListener previewDataListener) {
+ /** Updates the preview programs table for a specific preview channel. */
+ public void updatePreviewProgramsForChannel(
+ long previewChannelId,
+ Set<PreviewProgramContent> programs,
+ PreviewDataListener previewDataListener) {
UpdatePreviewProgramTask currentRunningUpdateTask =
mUpdatePreviewProgramTasks.get(previewChannelId);
if (currentRunningUpdateTask != null
@@ -215,22 +189,19 @@ public class PreviewDataManager {
}
public interface PreviewDataListener {
- /**
- * Called when the preview data is loaded.
- */
+ /** Called when the preview data is loaded. */
void onPreviewDataLoadFinished();
- /**
- * Called when the preview data is updated.
- */
+ /** Called when the preview data is updated. */
void onPreviewDataUpdateFinished();
}
public interface OnPreviewChannelCreationResultListener {
/**
* Called when the creation of preview channel is finished.
- * @param createdPreviewChannelId The preview channel ID if created successfully,
- * otherwise it's {@value #INVALID_PREVIEW_CHANNEL_ID}.
+ *
+ * @param createdPreviewChannelId The preview channel ID if created successfully, otherwise
+ * it's {@value #INVALID_PREVIEW_CHANNEL_ID}.
*/
void onPreviewChannelCreationResult(long createdPreviewChannelId);
}
@@ -283,7 +254,7 @@ public class PreviewDataManager {
android.support.media.tv.Channel previewChannel =
android.support.media.tv.Channel.fromCursor(cursor);
Long previewChannelType = previewChannel.getInternalProviderFlag1();
- if (previewChannel.getPackageName() == packageName
+ if (packageName.equals(previewChannel.getPackageName())
&& previewChannelType != null) {
previewData.addPreviewChannelId(
previewChannelType, previewChannel.getId());
@@ -352,9 +323,11 @@ public class PreviewDataManager {
if (DEBUG) Log.d(TAG, "CreatePreviewChannelTask.doInBackground");
long previewChannelId;
try {
- Uri channelUri = mContentResolver.insert(TvContract.Channels.CONTENT_URI,
- PreviewDataUtils.createPreviewChannel(mContext, mPreviewChannelType)
- .toContentValues());
+ Uri channelUri =
+ mContentResolver.insert(
+ TvContract.Channels.CONTENT_URI,
+ PreviewDataUtils.createPreviewChannel(mContext, mPreviewChannelType)
+ .toContentValues());
if (channelUri != null) {
previewChannelId = ContentUris.parseId(channelUri);
} else {
@@ -367,9 +340,14 @@ public class PreviewDataManager {
}
Drawable appIcon = mContext.getApplicationInfo().loadIcon(mContext.getPackageManager());
if (appIcon != null && appIcon instanceof BitmapDrawable) {
- ChannelLogoUtils.storeChannelLogo(mContext, previewChannelId,
- Bitmap.createScaledBitmap(((BitmapDrawable) appIcon).getBitmap(),
- mPreviewChannelLogoWidth, mPreviewChannelLogoHeight, false));
+ ChannelLogoUtils.storeChannelLogo(
+ mContext,
+ previewChannelId,
+ Bitmap.createScaledBitmap(
+ ((BitmapDrawable) appIcon).getBitmap(),
+ mPreviewChannelLogoWidth,
+ mPreviewChannelLogoHeight,
+ false));
}
return previewChannelId;
}
@@ -380,8 +358,8 @@ public class PreviewDataManager {
if (result != INVALID_PREVIEW_CHANNEL_ID) {
mPreviewData.addPreviewChannelId(mPreviewChannelType, result);
}
- for (OnPreviewChannelCreationResultListener onPreviewChannelCreationResultListener
- : mOnPreviewChannelCreationResultListeners) {
+ for (OnPreviewChannelCreationResultListener onPreviewChannelCreationResultListener :
+ mOnPreviewChannelCreationResultListeners) {
onPreviewChannelCreationResultListener.onPreviewChannelCreationResult(result);
}
mCreatePreviewChannelTasks.remove(mPreviewChannelType);
@@ -389,8 +367,8 @@ public class PreviewDataManager {
}
/**
- * Updates the whole data which belongs to the package in preview programs table for a
- * specific preview channel with a set of {@link PreviewProgramContent}.
+ * Updates the whole data which belongs to the package in preview programs table for a specific
+ * preview channel with a set of {@link PreviewProgramContent}.
*/
private final class UpdatePreviewProgramTask extends AsyncTask<Void, Void, Void> {
private long mPreviewChannelId;
@@ -398,15 +376,15 @@ public class PreviewDataManager {
private Map<Long, Long> mCurrentProgramId2PreviewProgramId;
private Set<PreviewDataListener> mPreviewDataListeners = new CopyOnWriteArraySet<>();
- public UpdatePreviewProgramTask(long previewChannelId,
- Set<PreviewProgramContent> programs) {
+ public UpdatePreviewProgramTask(
+ long previewChannelId, Set<PreviewProgramContent> programs) {
mPreviewChannelId = previewChannelId;
mPrograms = programs;
if (mPreviewData.getPreviewProgramIds(previewChannelId) == null) {
mCurrentProgramId2PreviewProgramId = new HashMap<>();
} else {
- mCurrentProgramId2PreviewProgramId = new HashMap<>(
- mPreviewData.getPreviewProgramIds(previewChannelId));
+ mCurrentProgramId2PreviewProgramId =
+ new HashMap<>(mPreviewData.getPreviewProgramIds(previewChannelId));
}
}
@@ -440,14 +418,22 @@ public class PreviewDataManager {
}
Long existingPreviewProgramId = uncheckedPrograms.remove(program.getId());
if (existingPreviewProgramId != null) {
- if (DEBUG) Log.d(TAG, "Preview program " + existingPreviewProgramId + " " +
- "already exists for program " + program.getId());
+ if (DEBUG)
+ Log.d(
+ TAG,
+ "Preview program "
+ + existingPreviewProgramId
+ + " "
+ + "already exists for program "
+ + program.getId());
continue;
}
try {
- Uri programUri = mContentResolver.insert(TvContract.PreviewPrograms.CONTENT_URI,
- PreviewDataUtils.createPreviewProgramFromContent(program)
- .toContentValues());
+ Uri programUri =
+ mContentResolver.insert(
+ TvContract.PreviewPrograms.CONTENT_URI,
+ PreviewDataUtils.createPreviewProgramFromContent(program)
+ .toContentValues());
if (programUri != null) {
long previewProgramId = ContentUris.parseId(programUri);
mCurrentProgramId2PreviewProgramId.put(program.getId(), previewProgramId);
@@ -466,8 +452,10 @@ public class PreviewDataManager {
}
try {
if (DEBUG) Log.d(TAG, "Remove preview program " + uncheckedPrograms.get(key));
- mContentResolver.delete(TvContract.buildPreviewProgramUri(
- uncheckedPrograms.get(key)), null, null);
+ mContentResolver.delete(
+ TvContract.buildPreviewProgramUri(uncheckedPrograms.get(key)),
+ null,
+ null);
mCurrentProgramId2PreviewProgramId.remove(key);
} catch (Exception e) {
Log.e(TAG, "Fail to remove preview program " + uncheckedPrograms.get(key));
@@ -493,9 +481,7 @@ public class PreviewDataManager {
}
}
- /**
- * Class to store the query result of preview data.
- */
+ /** Class to store the query result of preview data. */
private static final class PreviewData {
private Map<Long, Long> mPreviewChannelType2Id = new HashMap<>();
private Map<Long, Map<Long, Long>> mProgramId2PreviewProgramId = new HashMap<>();
@@ -565,13 +551,9 @@ public class PreviewDataManager {
}
}
- /**
- * A utils class for preview data.
- */
- public final static class PreviewDataUtils {
- /**
- * Creates a preview channel.
- */
+ /** A utils class for preview data. */
+ public static final class PreviewDataUtils {
+ /** Creates a preview channel. */
public static android.support.media.tv.Channel createPreviewChannel(
Context context, @PreviewChannelType long previewChannelType) {
if (previewChannelType == TYPE_RECORDED_PROGRAM_PREVIEW_CHANNEL) {
@@ -590,7 +572,7 @@ public class PreviewDataManager {
context.getApplicationInfo().loadDescription(context.getPackageManager());
builder.setType(TvContract.Channels.TYPE_PREVIEW)
.setDisplayName(appLabel == null ? null : appLabel.toString())
- .setDescription(appDescription == null ? null : appDescription.toString())
+ .setDescription(appDescription == null ? null : appDescription.toString())
.setAppLinkIntentUri(TvContract.Channels.CONTENT_URI)
.setInternalProviderFlag1(previewChannelType);
return builder.build();
@@ -601,16 +583,15 @@ public class PreviewDataManager {
android.support.media.tv.Channel.Builder builder =
new android.support.media.tv.Channel.Builder();
builder.setType(TvContract.Channels.TYPE_PREVIEW)
- .setDisplayName(context.getResources().getString(
- R.string.recorded_programs_preview_channel))
+ .setDisplayName(
+ context.getResources()
+ .getString(R.string.recorded_programs_preview_channel))
.setAppLinkIntentUri(TvContract.Channels.CONTENT_URI)
.setInternalProviderFlag1(previewChannelType);
return builder.build();
}
- /**
- * Creates a preview program.
- */
+ /** Creates a preview program. */
public static PreviewProgram createPreviewProgramFromContent(
PreviewProgramContent program) {
PreviewProgram.Builder builder = new PreviewProgram.Builder();
@@ -622,13 +603,12 @@ public class PreviewDataManager {
.setPosterArtUri(program.getPosterArtUri())
.setIntentUri(program.getIntentUri())
.setPreviewVideoUri(program.getPreviewVideoUri())
- .setInternalProviderId(Long.toString(program.getId()));
+ .setInternalProviderId(Long.toString(program.getId()))
+ .setContentId(program.getIntentUri().toString());
return builder.build();
}
- /**
- * Appends query parameters to a Uri.
- */
+ /** Appends query parameters to a Uri. */
public static Uri addQueryParamToUri(Uri uri, Pair<String, String> param) {
return uri.buildUpon().appendQueryParameter(param.first, param.second).build();
}
diff --git a/src/com/android/tv/data/PreviewProgramContent.java b/src/com/android/tv/data/PreviewProgramContent.java
index 39f5051d..b5156408 100644
--- a/src/com/android/tv/data/PreviewProgramContent.java
+++ b/src/com/android/tv/data/PreviewProgramContent.java
@@ -17,21 +17,19 @@
package com.android.tv.data;
import android.content.Context;
-import android.media.tv.TvContract;
import android.net.Uri;
+import android.support.annotation.VisibleForTesting;
+import android.support.media.tv.TvContractCompat;
import android.text.TextUtils;
import android.util.Pair;
-
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
+import com.android.tv.data.api.Channel;
import com.android.tv.dvr.data.RecordedProgram;
-
import java.util.Objects;
-/**
- * A class to store the content of preview programs.
- */
+/** A class to store the content of preview programs. */
public class PreviewProgramContent {
- private final static String PARAM_INPUT = "input";
+ @VisibleForTesting static final String PARAM_INPUT = "input";
private long mId;
private long mPreviewChannelId;
@@ -43,59 +41,71 @@ public class PreviewProgramContent {
private Uri mIntentUri;
private Uri mPreviewVideoUri;
- /**
- * Create preview program content from {@link Program}
- */
- public static PreviewProgramContent createFromProgram(Context context,
- long previewChannelId, Program program) {
- Channel channel = TvApplication.getSingletons(context).getChannelDataManager()
- .getChannel(program.getChannelId());
- if (channel == null) {
- return null;
- }
+ /** Create preview program content from {@link Program} */
+ public static PreviewProgramContent createFromProgram(
+ Context context, long previewChannelId, Program program) {
+ Channel channel =
+ TvSingletons.getSingletons(context)
+ .getChannelDataManager()
+ .getChannel(program.getChannelId());
+ return channel == null ? null : createFromProgram(previewChannelId, program, channel);
+ }
+
+ /** Create preview program content from {@link RecordedProgram} */
+ public static PreviewProgramContent createFromRecordedProgram(
+ Context context, long previewChannelId, RecordedProgram recordedProgram) {
+ Channel channel =
+ TvSingletons.getSingletons(context)
+ .getChannelDataManager()
+ .getChannel(recordedProgram.getChannelId());
+ return createFromRecordedProgram(previewChannelId, recordedProgram, channel);
+ }
+
+ @VisibleForTesting
+ static PreviewProgramContent createFromProgram(
+ long previewChannelId, Program program, Channel channel) {
String channelDisplayName = channel.getDisplayName();
return new PreviewProgramContent.Builder()
.setId(program.getId())
.setPreviewChannelId(previewChannelId)
- .setType(TvContract.PreviewPrograms.TYPE_CHANNEL)
+ .setType(TvContractCompat.PreviewPrograms.TYPE_CHANNEL)
.setLive(true)
.setTitle(program.getTitle())
- .setDescription(!TextUtils.isEmpty(channelDisplayName)
- ? channelDisplayName : channel.getDisplayNumber())
+ .setDescription(
+ !TextUtils.isEmpty(channelDisplayName)
+ ? channelDisplayName
+ : channel.getDisplayNumber())
.setPosterArtUri(Uri.parse(program.getPosterArtUri()))
.setIntentUri(channel.getUri())
- .setPreviewVideoUri(PreviewDataManager.PreviewDataUtils.addQueryParamToUri(
- channel.getUri(), new Pair<>(PARAM_INPUT, channel.getInputId())))
+ .setPreviewVideoUri(
+ PreviewDataManager.PreviewDataUtils.addQueryParamToUri(
+ channel.getUri(), new Pair<>(PARAM_INPUT, channel.getInputId())))
.build();
}
- /**
- * Create preview program content from {@link RecordedProgram}
- */
- public static PreviewProgramContent createFromRecordedProgram(
- Context context, long previewChannelId, RecordedProgram recordedProgram) {
- Channel channel = TvApplication.getSingletons(context).getChannelDataManager()
- .getChannel(recordedProgram.getChannelId());
- String channelDisplayName = null;
- if (channel != null) {
- channelDisplayName = channel.getDisplayName();
- }
- Uri recordedProgramUri = TvContract.buildRecordedProgramUri(recordedProgram.getId());
+ @VisibleForTesting
+ static PreviewProgramContent createFromRecordedProgram(
+ long previewChannelId, RecordedProgram recordedProgram, Channel channel) {
+ String channelDisplayName = channel == null ? null : channel.getDisplayName();
+ Uri recordedProgramUri = TvContractCompat.buildRecordedProgramUri(recordedProgram.getId());
return new PreviewProgramContent.Builder()
.setId(recordedProgram.getId())
.setPreviewChannelId(previewChannelId)
- .setType(TvContract.PreviewPrograms.TYPE_CLIP)
+ .setType(TvContractCompat.PreviewPrograms.TYPE_CLIP)
.setTitle(recordedProgram.getTitle())
.setDescription(channelDisplayName != null ? channelDisplayName : "")
.setPosterArtUri(Uri.parse(recordedProgram.getPosterArtUri()))
.setIntentUri(recordedProgramUri)
- .setPreviewVideoUri(PreviewDataManager.PreviewDataUtils.addQueryParamToUri(
- recordedProgramUri, new Pair<>(PARAM_INPUT, recordedProgram.getInputId())))
+ .setPreviewVideoUri(
+ PreviewDataManager.PreviewDataUtils.addQueryParamToUri(
+ recordedProgramUri,
+ new Pair<>(PARAM_INPUT, recordedProgram.getInputId())))
.build();
}
- private PreviewProgramContent() { }
+ private PreviewProgramContent() {}
+ @SuppressWarnings("ReferenceEquality")
public void copyFrom(PreviewProgramContent other) {
if (this == other) {
return;
@@ -119,58 +129,42 @@ public class PreviewProgramContent {
return mId;
}
- /**
- * Returns the preview channel id which the preview program belongs to.
- */
+ /** Returns the preview channel id which the preview program belongs to. */
public long getPreviewChannelId() {
return mPreviewChannelId;
}
- /**
- * Returns the type of the preview program.
- */
+ /** Returns the type of the preview program. */
public int getType() {
return mType;
}
- /**
- * Returns whether the preview program is live or not.
- */
+ /** Returns whether the preview program is live or not. */
public boolean getLive() {
return mLive;
}
- /**
- * Returns the title of the preview program.
- */
+ /** Returns the title of the preview program. */
public String getTitle() {
return mTitle;
}
- /**
- * Returns the description of the preview program.
- */
+ /** Returns the description of the preview program. */
public String getDescription() {
return mDescription;
}
- /**
- * Returns the poster art uri of the preview program.
- */
+ /** Returns the poster art uri of the preview program. */
public Uri getPosterArtUri() {
return mPosterArtUri;
}
- /**
- * Returns the intent uri of the preview program.
- */
+ /** Returns the intent uri of the preview program. */
public Uri getIntentUri() {
return mIntentUri;
}
- /**
- * Returns the preview video uri of the preview program.
- */
+ /** Returns the preview video uri of the preview program. */
public Uri getPreviewVideoUri() {
return mPreviewVideoUri;
}
@@ -194,8 +188,16 @@ public class PreviewProgramContent {
@Override
public int hashCode() {
- return Objects.hash(mId, mPreviewChannelId, mType, mLive, mTitle, mDescription,
- mPosterArtUri, mIntentUri, mPreviewVideoUri);
+ return Objects.hash(
+ mId,
+ mPreviewChannelId,
+ mType,
+ mLive,
+ mTitle,
+ mDescription,
+ mPosterArtUri,
+ mIntentUri,
+ mPreviewVideoUri);
}
public static final class Builder {
diff --git a/src/com/android/tv/data/Program.java b/src/com/android/tv/data/Program.java
index 071c7024..2c64cdbb 100644
--- a/src/com/android/tv/data/Program.java
+++ b/src/com/android/tv/data/Program.java
@@ -32,59 +32,56 @@ import android.support.annotation.UiThread;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import android.util.Log;
-
import com.android.tv.common.BuildConfig;
-import com.android.tv.common.CollectionUtils;
import com.android.tv.common.TvContentRatingCache;
-import com.android.tv.util.ImageLoader;
+import com.android.tv.common.util.CollectionUtils;
+import com.android.tv.common.util.CommonUtils;
+import com.android.tv.data.api.Channel;
import com.android.tv.util.Utils;
-
+import com.android.tv.util.images.ImageLoader;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
-/**
- * A convenience class to create and insert program information entries into the database.
- */
+/** A convenience class to create and insert program information entries into the database. */
public final class Program extends BaseProgram implements Comparable<Program>, Parcelable {
private static final boolean DEBUG = false;
private static final boolean DEBUG_DUMP_DESCRIPTION = false;
private static final String TAG = "Program";
private static final String[] PROJECTION_BASE = {
- // Columns must match what is read in Program.fromCursor()
- TvContract.Programs._ID,
- TvContract.Programs.COLUMN_PACKAGE_NAME,
- TvContract.Programs.COLUMN_CHANNEL_ID,
- TvContract.Programs.COLUMN_TITLE,
- TvContract.Programs.COLUMN_EPISODE_TITLE,
- TvContract.Programs.COLUMN_SHORT_DESCRIPTION,
- TvContract.Programs.COLUMN_LONG_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,
- TvContract.Programs.COLUMN_INTERNAL_PROVIDER_DATA
+ // Columns must match what is read in Program.fromCursor()
+ TvContract.Programs._ID,
+ TvContract.Programs.COLUMN_PACKAGE_NAME,
+ TvContract.Programs.COLUMN_CHANNEL_ID,
+ TvContract.Programs.COLUMN_TITLE,
+ TvContract.Programs.COLUMN_EPISODE_TITLE,
+ TvContract.Programs.COLUMN_SHORT_DESCRIPTION,
+ TvContract.Programs.COLUMN_LONG_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,
+ TvContract.Programs.COLUMN_INTERNAL_PROVIDER_DATA
};
// Columns which is deprecated in NYC
@SuppressWarnings("deprecation")
private static final String[] PROJECTION_DEPRECATED_IN_NYC = {
- TvContract.Programs.COLUMN_SEASON_NUMBER,
- TvContract.Programs.COLUMN_EPISODE_NUMBER
+ 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,
- TvContract.Programs.COLUMN_RECORDING_PROHIBITED
+ TvContract.Programs.COLUMN_SEASON_DISPLAY_NUMBER,
+ TvContract.Programs.COLUMN_SEASON_TITLE,
+ TvContract.Programs.COLUMN_EPISODE_DISPLAY_NUMBER,
+ TvContract.Programs.COLUMN_RECORDING_PROHIBITED
};
public static final String[] PROJECTION = createProjection();
@@ -97,9 +94,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
: PROJECTION_DEPRECATED_IN_NYC);
}
- /**
- * Returns the column index for {@code column}, -1 if the column doesn't exist.
- */
+ /** Returns the column index for {@code column}, -1 if the column doesn't exist. */
public static int getColumnIndex(String column) {
for (int i = 0; i < PROJECTION.length; ++i) {
if (PROJECTION[i].equals(column)) {
@@ -135,7 +130,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
builder.setEndTimeUtcMillis(cursor.getLong(index++));
builder.setVideoWidth((int) cursor.getLong(index++));
builder.setVideoHeight((int) cursor.getLong(index++));
- if (Utils.isInBundledPackageSet(packageName)) {
+ if (CommonUtils.isInBundledPackageSet(packageName)) {
InternalDataUtils.deserializeInternalProviderData(cursor.getBlob(index), builder);
}
index++;
@@ -183,17 +178,18 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
return program;
}
- public static final Parcelable.Creator<Program> CREATOR = new Parcelable.Creator<Program>() {
- @Override
- public Program createFromParcel(Parcel in) {
- return Program.fromParcel(in);
- }
+ public static final Parcelable.Creator<Program> CREATOR =
+ new Parcelable.Creator<Program>() {
+ @Override
+ public Program createFromParcel(Parcel in) {
+ return Program.fromParcel(in);
+ }
- @Override
- public Program[] newArray(int size) {
- return new Program[size];
- }
- };
+ @Override
+ public Program[] newArray(int size) {
+ return new Program[size];
+ }
+ };
private long mId;
private String mPackageName;
@@ -225,9 +221,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
return mId;
}
- /**
- * Returns the package name of this program.
- */
+ /** Returns the package name of this program. */
public String getPackageName() {
return mPackageName;
}
@@ -236,18 +230,14 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
return mChannelId;
}
- /**
- * Returns {@code true} if this program is valid or {@code false} otherwise.
- */
+ /** Returns {@code true} if this program is valid or {@code false} otherwise. */
@Override
public boolean isValid() {
return mChannelId >= 0;
}
- /**
- * Returns {@code true} if the program is valid and {@code false} otherwise.
- */
- public static boolean isValid(Program program) {
+ /** Returns {@code true} if the program is valid and {@code false} otherwise. */
+ public static boolean isProgramValid(Program program) {
return program != null && program.isValid();
}
@@ -256,17 +246,13 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
return mTitle;
}
- /**
- * Returns the series ID.
- */
+ /** Returns the series ID. */
@Override
public String getSeriesId() {
return mSeriesId;
}
- /**
- * Returns the episode title.
- */
+ /** Returns the episode title. */
@Override
public String getEpisodeTitle() {
return mEpisodeTitle;
@@ -292,9 +278,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
return mEndTimeUtcMillis;
}
- /**
- * Returns the program duration.
- */
+ /** Returns the program duration. */
@Override
public long getDurationMillis() {
return mEndTimeUtcMillis - mStartTimeUtcMillis;
@@ -318,9 +302,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
return mVideoHeight;
}
- /**
- * Returns the list of Critic Scores for this program
- */
+ /** Returns the list of Critic Scores for this program */
@Nullable
public List<CriticScore> getCriticScores() {
return mCriticScores;
@@ -342,17 +324,12 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
return mThumbnailUri;
}
- /**
- * Returns {@code true} if the recording of this program is prohibited.
- */
+ /** Returns {@code true} if the recording of this program is prohibited. */
public boolean isRecordingProhibited() {
return mRecordingProhibited;
}
- /**
- * Returns array of canonical genres for this program.
- * This is expected to be called rarely.
- */
+ /** Returns array of canonical genres for this program. This is expected to be called rarely. */
@Nullable
public String[] getCanonicalGenres() {
if (mCanonicalGenreIds == null) {
@@ -365,17 +342,13 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
return genres;
}
- /**
- * Returns array of canonical genre ID's for this program.
- */
+ /** Returns array of canonical genre ID's for this program. */
@Override
public int[] getCanonicalGenreIds() {
return mCanonicalGenreIds;
}
- /**
- * Returns if this program has the genre.
- */
+ /** Returns if this program has the genre. */
public boolean hasGenre(int genreId) {
if (genreId == GenreItems.ID_ALL_CHANNELS) {
return true;
@@ -393,10 +366,24 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
@Override
public int hashCode() {
// Hash with all the properties because program ID can be invalid for the dummy programs.
- return Objects.hash(mChannelId, mStartTimeUtcMillis, mEndTimeUtcMillis,
- mTitle, mSeriesId, mEpisodeTitle, mDescription, mLongDescription, mVideoWidth,
- mVideoHeight, mPosterArtUri, mThumbnailUri, Arrays.hashCode(mContentRatings),
- Arrays.hashCode(mCanonicalGenreIds), mSeasonNumber, mSeasonTitle, mEpisodeNumber,
+ return Objects.hash(
+ mChannelId,
+ mStartTimeUtcMillis,
+ mEndTimeUtcMillis,
+ mTitle,
+ mSeriesId,
+ mEpisodeTitle,
+ mDescription,
+ mLongDescription,
+ mVideoWidth,
+ mVideoHeight,
+ mPosterArtUri,
+ mThumbnailUri,
+ Arrays.hashCode(mContentRatings),
+ Arrays.hashCode(mCanonicalGenreIds),
+ mSeasonNumber,
+ mSeasonTitle,
+ mEpisodeNumber,
mRecordingProhibited);
}
@@ -436,28 +423,47 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
- builder.append("Program[").append(mId)
- .append("]{channelId=").append(mChannelId)
- .append(", packageName=").append(mPackageName)
- .append(", title=").append(mTitle)
- .append(", seriesId=").append(mSeriesId)
- .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))
- .append(", videoWidth=").append(mVideoWidth)
- .append(", videoHeight=").append(mVideoHeight)
+ builder.append("Program[")
+ .append(mId)
+ .append("]{channelId=")
+ .append(mChannelId)
+ .append(", packageName=")
+ .append(mPackageName)
+ .append(", title=")
+ .append(mTitle)
+ .append(", seriesId=")
+ .append(mSeriesId)
+ .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))
+ .append(", videoWidth=")
+ .append(mVideoWidth)
+ .append(", videoHeight=")
+ .append(mVideoHeight)
.append(", contentRatings=")
.append(TvContentRatingCache.contentRatingsToString(mContentRatings))
- .append(", posterArtUri=").append(mPosterArtUri)
- .append(", thumbnailUri=").append(mThumbnailUri)
- .append(", canonicalGenres=").append(Arrays.toString(mCanonicalGenreIds))
- .append(", recordingProhibited=").append(mRecordingProhibited);
+ .append(", posterArtUri=")
+ .append(mPosterArtUri)
+ .append(", thumbnailUri=")
+ .append(mThumbnailUri)
+ .append(", canonicalGenres=")
+ .append(Arrays.toString(mCanonicalGenreIds))
+ .append(", recordingProhibited=")
+ .append(mRecordingProhibited);
if (DEBUG_DUMP_DESCRIPTION) {
- builder.append(", description=").append(mDescription)
- .append(", longDescription=").append(mLongDescription);
+ builder.append(", description=")
+ .append(mDescription)
+ .append(", longDescription=")
+ .append(mLongDescription);
}
return builder.append("}").toString();
}
@@ -471,12 +477,19 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
public static ContentValues toContentValues(Program program) {
ContentValues values = new ContentValues();
values.put(TvContract.Programs.COLUMN_CHANNEL_ID, program.getChannelId());
+ if (!TextUtils.isEmpty(program.getPackageName())) {
+ values.put(Programs.COLUMN_PACKAGE_NAME, program.getPackageName());
+ }
putValue(values, TvContract.Programs.COLUMN_TITLE, program.getTitle());
putValue(values, TvContract.Programs.COLUMN_EPISODE_TITLE, program.getEpisodeTitle());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- putValue(values, TvContract.Programs.COLUMN_SEASON_DISPLAY_NUMBER,
+ putValue(
+ values,
+ TvContract.Programs.COLUMN_SEASON_DISPLAY_NUMBER,
program.getSeasonNumber());
- putValue(values, TvContract.Programs.COLUMN_EPISODE_DISPLAY_NUMBER,
+ putValue(
+ values,
+ TvContract.Programs.COLUMN_EPISODE_DISPLAY_NUMBER,
program.getEpisodeNumber());
} else {
putValue(values, TvContract.Programs.COLUMN_SEASON_NUMBER, program.getSeasonNumber());
@@ -488,17 +501,23 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
putValue(values, TvContract.Programs.COLUMN_THUMBNAIL_URI, program.getThumbnailUri());
String[] canonicalGenres = program.getCanonicalGenres();
if (canonicalGenres != null && canonicalGenres.length > 0) {
- putValue(values, TvContract.Programs.COLUMN_CANONICAL_GENRE,
+ putValue(
+ values,
+ TvContract.Programs.COLUMN_CANONICAL_GENRE,
TvContract.Programs.Genres.encode(canonicalGenres));
} else {
putValue(values, TvContract.Programs.COLUMN_CANONICAL_GENRE, "");
}
- putValue(values, Programs.COLUMN_CONTENT_RATING,
+ putValue(
+ values,
+ Programs.COLUMN_CONTENT_RATING,
TvContentRatingCache.contentRatingsToString(program.getContentRatings()));
- values.put(TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS,
- program.getStartTimeUtcMillis());
+ values.put(
+ TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, program.getStartTimeUtcMillis());
values.put(TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS, program.getEndTimeUtcMillis());
- putValue(values, TvContract.Programs.COLUMN_INTERNAL_PROVIDER_DATA,
+ putValue(
+ values,
+ TvContract.Programs.COLUMN_INTERNAL_PROVIDER_DATA,
InternalDataUtils.serializeInternalProviderData(program));
return values;
}
@@ -547,15 +566,11 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
mRecordingProhibited = other.mRecordingProhibited;
}
- /**
- * A Builder for the Program class
- */
+ /** A Builder for the Program class */
public static final class Builder {
private final Program mProgram;
- /**
- * Creates a Builder for this Program class
- */
+ /** Creates a Builder for this Program class */
public Builder() {
mProgram = new Program();
// Fill initial data.
@@ -574,8 +589,9 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
}
/**
- * Creates a builder for this Program class
- * by setting default values equivalent to another Program
+ * Creates a builder for this Program class by setting default values equivalent to another
+ * Program
+ *
* @param other the program to be copied
*/
@VisibleForTesting
@@ -586,6 +602,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
/**
* Sets the ID of this program
+ *
* @param id the ID
* @return a reference to this object
*/
@@ -596,16 +613,18 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
/**
* Sets the package name for this program
+ *
* @param packageName the package name
* @return a reference to this object
*/
- public Builder setPackageName(String packageName){
+ public Builder setPackageName(String packageName) {
mProgram.mPackageName = packageName;
return this;
}
/**
* Sets the channel ID for this program
+ *
* @param channelId the channel ID
* @return a reference to this object
*/
@@ -616,6 +635,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
/**
* Sets the program title
+ *
* @param title the title
* @return a reference to this object
*/
@@ -626,6 +646,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
/**
* Sets the series ID.
+ *
* @param seriesId the series ID
* @return a reference to this object
*/
@@ -636,6 +657,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
/**
* Sets the episode title if this is a series program
+ *
* @param episodeTitle the episode title
* @return a reference to this object
*/
@@ -646,6 +668,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
/**
* Sets the season number if this is a series program
+ *
* @param seasonNumber the season number
* @return a reference to this object
*/
@@ -654,9 +677,9 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
return this;
}
-
/**
* Sets the season title if this is a series program
+ *
* @param seasonTitle the season title
* @return a reference to this object
*/
@@ -667,6 +690,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
/**
* Sets the episode number if this is a series program
+ *
* @param episodeNumber the episode number
* @return a reference to this object
*/
@@ -677,6 +701,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
/**
* Sets the start time of this program
+ *
* @param startTimeUtcMillis the start time in UTC milliseconds
* @return a reference to this object
*/
@@ -687,6 +712,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
/**
* Sets the end time of this program
+ *
* @param endTimeUtcMillis the end time in UTC milliseconds
* @return a reference to this object
*/
@@ -697,6 +723,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
/**
* Sets a description
+ *
* @param description the description
* @return a reference to this object
*/
@@ -707,6 +734,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
/**
* Sets a long description
+ *
* @param longDescription the long description
* @return a reference to this object
*/
@@ -717,6 +745,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
/**
* Defines the video width of this program
+ *
* @param width
* @return a reference to this object
*/
@@ -727,6 +756,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
/**
* Defines the video height of this program
+ *
* @param height
* @return a reference to this object
*/
@@ -737,6 +767,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
/**
* Sets the content ratings for this program
+ *
* @param contentRatings the content ratings
* @return a reference to this object
*/
@@ -747,6 +778,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
/**
* Sets the poster art URI
+ *
* @param posterArtUri the poster art URI
* @return a reference to this object
*/
@@ -757,6 +789,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
/**
* Sets the thumbnail URI
+ *
* @param thumbnailUri the thumbnail URI
* @return a reference to this object
*/
@@ -767,6 +800,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
/**
* Sets the canonical genres by id
+ *
* @param genres the genres
* @return a reference to this object
*/
@@ -777,6 +811,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
/**
* Sets the recording prohibited flag
+ *
* @param recordingProhibited recording prohibited flag
* @return a reference to this object
*/
@@ -787,6 +822,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
/**
* Adds a critic score
+ *
* @param criticScore the critic score
* @return a reference to this object
*/
@@ -802,6 +838,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
/**
* Sets the critic scores
+ *
* @param criticScores the critic scores
* @return a reference to this objects
*/
@@ -812,6 +849,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
/**
* Returns a reference to the Program object being constructed
+ *
* @return the Program object constructed
*/
public Program build() {
@@ -831,7 +869,9 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
}
/**
- * Prefetches the program poster art.<p>
+ * Prefetches the program poster art.
+ *
+ * <p>
*/
public void prefetchPosterArt(Context context, int posterArtWidth, int posterArtHeight) {
if (mPosterArtUri == null) {
@@ -842,13 +882,17 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
/**
* Loads the program poster art and returns it via {@code callback}.
- * <p>
- * Note that it may directly call {@code callback} if the program poster art already is loaded.
+ *
+ * <p>Note that it may directly call {@code callback} if the program poster art already is
+ * loaded.
*
* @return {@code true} if the load is complete and the callback is executed.
*/
@UiThread
- public boolean loadPosterArt(Context context, int posterArtWidth, int posterArtHeight,
+ public boolean loadPosterArt(
+ Context context,
+ int posterArtWidth,
+ int posterArtHeight,
ImageLoader.ImageLoaderCallback callback) {
if (mPosterArtUri == null) {
return false;
@@ -861,12 +905,18 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
if (p1 == null || p2 == null) {
return false;
}
- boolean isDuplicate = p1.getChannelId() == p2.getChannelId()
- && p1.getStartTimeUtcMillis() == p2.getStartTimeUtcMillis()
- && p1.getEndTimeUtcMillis() == p2.getEndTimeUtcMillis();
+ boolean isDuplicate =
+ p1.getChannelId() == p2.getChannelId()
+ && p1.getStartTimeUtcMillis() == p2.getStartTimeUtcMillis()
+ && p1.getEndTimeUtcMillis() == p2.getEndTimeUtcMillis();
if (DEBUG && BuildConfig.ENG && isDuplicate) {
- Log.w(TAG, "Duplicate programs detected! - \"" + p1.getTitle() + "\" and \""
- + p2.getTitle() + "\"");
+ Log.w(
+ TAG,
+ "Duplicate programs detected! - \""
+ + p1.getTitle()
+ + "\" and \""
+ + p2.getTitle()
+ + "\"");
}
return isDuplicate;
}
@@ -906,21 +956,13 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
out.writeByte((byte) (mRecordingProhibited ? 1 : 0));
}
- /**
- * Holds one type of critic score and its source.
- */
+ /** Holds one type of critic score and its source. */
public static final class CriticScore implements Serializable, Parcelable {
- /**
- * The source of the rating.
- */
+ /** The source of the rating. */
public final String source;
- /**
- * The score.
- */
+ /** The score. */
public final String score;
- /**
- * The url of the logo image
- */
+ /** The url of the logo image */
public final String logoUrl;
public static final Parcelable.Creator<CriticScore> CREATOR =
@@ -929,7 +971,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
public CriticScore createFromParcel(Parcel in) {
String source = in.readString();
String score = in.readString();
- String logoUri = in.readString();
+ String logoUri = in.readString();
return new CriticScore(source, score, logoUri);
}
@@ -941,6 +983,7 @@ public final class Program extends BaseProgram implements Comparable<Program>, P
/**
* Constructor for this class.
+ *
* @param source the source of the rating
* @param score the score
*/
diff --git a/src/com/android/tv/data/ProgramDataManager.java b/src/com/android/tv/data/ProgramDataManager.java
index 8cb5e74a..4631806c 100644
--- a/src/com/android/tv/data/ProgramDataManager.java
+++ b/src/com/android/tv/data/ProgramDataManager.java
@@ -33,14 +33,16 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.LruCache;
-
-import com.android.tv.common.MemoryManageable;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
+import com.android.tv.common.config.api.RemoteConfig;
+import com.android.tv.common.config.api.RemoteConfigValue;
+import com.android.tv.common.memory.MemoryManageable;
+import com.android.tv.common.util.Clock;
+import com.android.tv.data.api.Channel;
import com.android.tv.util.AsyncDbTask;
-import com.android.tv.util.Clock;
import com.android.tv.util.MultiLongSparseArray;
import com.android.tv.util.Utils;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -51,6 +53,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@MainThread
@@ -60,23 +63,28 @@ public class ProgramDataManager implements MemoryManageable {
// To prevent from too many program update operations at the same time, we give random interval
// between PERIODIC_PROGRAM_UPDATE_MIN_MS and PERIODIC_PROGRAM_UPDATE_MAX_MS.
- private static final long PERIODIC_PROGRAM_UPDATE_MIN_MS = TimeUnit.MINUTES.toMillis(5);
+ @VisibleForTesting
+ static final long PERIODIC_PROGRAM_UPDATE_MIN_MS = TimeUnit.MINUTES.toMillis(5);
+
private static final long PERIODIC_PROGRAM_UPDATE_MAX_MS = TimeUnit.MINUTES.toMillis(10);
private static final long PROGRAM_PREFETCH_UPDATE_WAIT_MS = TimeUnit.SECONDS.toMillis(5);
// TODO: need to optimize consecutive DB updates.
private static final long CURRENT_PROGRAM_UPDATE_WAIT_MS = TimeUnit.SECONDS.toMillis(5);
- @VisibleForTesting
- static final long PROGRAM_GUIDE_SNAP_TIME_MS = TimeUnit.MINUTES.toMillis(30);
- @VisibleForTesting
- static final long PROGRAM_GUIDE_MAX_TIME_RANGE = TimeUnit.DAYS.toMillis(2);
+ @VisibleForTesting static final long PROGRAM_GUIDE_SNAP_TIME_MS = TimeUnit.MINUTES.toMillis(30);
+ private static final RemoteConfigValue<Long> PROGRAM_GUIDE_MAX_HOURS =
+ RemoteConfigValue.create("live_channels_program_guide_max_hours", 48);
// TODO: Use TvContract constants, once they become public.
private static final String PARAM_START_TIME = "start_time";
private static final String PARAM_END_TIME = "end_time";
// COLUMN_CHANNEL_ID, COLUMN_END_TIME_UTC_MILLIS are added to detect duplicated programs.
// Duplicated programs are always consecutive by the sorting order.
- private static final String SORT_BY_TIME = Programs.COLUMN_START_TIME_UTC_MILLIS + ", "
- + Programs.COLUMN_CHANNEL_ID + ", " + Programs.COLUMN_END_TIME_UTC_MILLIS;
+ private static final String SORT_BY_TIME =
+ Programs.COLUMN_START_TIME_UTC_MILLIS
+ + ", "
+ + Programs.COLUMN_CHANNEL_ID
+ + ", "
+ + Programs.COLUMN_END_TIME_UTC_MILLIS;
private static final int MSG_UPDATE_CURRENT_PROGRAMS = 1000;
private static final int MSG_UPDATE_ONE_CURRENT_PROGRAM = 1001;
@@ -84,6 +92,8 @@ public class ProgramDataManager implements MemoryManageable {
private final Clock mClock;
private final ContentResolver mContentResolver;
+ private final Executor mDbExecutor;
+ private final RemoteConfig mRemoteConfig;
private boolean mStarted;
// Updated only on the main thread.
private volatile boolean mCurrentProgramsLoadFinished;
@@ -114,32 +124,47 @@ public class ProgramDataManager implements MemoryManageable {
@MainThread
public ProgramDataManager(Context context) {
- this(context.getContentResolver(), Clock.SYSTEM, Looper.myLooper());
+ this(
+ TvSingletons.getSingletons(context).getDbExecutor(),
+ context.getContentResolver(),
+ Clock.SYSTEM,
+ Looper.myLooper(),
+ TvSingletons.getSingletons(context).getRemoteConfig());
}
@VisibleForTesting
- ProgramDataManager(ContentResolver contentResolver, Clock time, Looper looper) {
+ ProgramDataManager(
+ Executor executor,
+ ContentResolver contentResolver,
+ Clock time,
+ Looper looper,
+ RemoteConfig remoteConfig) {
+ mDbExecutor = executor;
mClock = time;
mContentResolver = contentResolver;
mHandler = new MyHandler(looper);
- mProgramObserver = new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange) {
- if (!mHandler.hasMessages(MSG_UPDATE_CURRENT_PROGRAMS)) {
- mHandler.sendEmptyMessage(MSG_UPDATE_CURRENT_PROGRAMS);
- }
- if (isProgramUpdatePaused()) {
- return;
- }
- 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.
- mHandler.removeMessages(MSG_UPDATE_PREFETCH_PROGRAM);
- mHandler.sendEmptyMessage(MSG_UPDATE_PREFETCH_PROGRAM);
- }
- }
- };
+ mRemoteConfig = remoteConfig;
+ mProgramObserver =
+ new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ if (!mHandler.hasMessages(MSG_UPDATE_CURRENT_PROGRAMS)) {
+ mHandler.sendEmptyMessage(MSG_UPDATE_CURRENT_PROGRAMS);
+ }
+ if (isProgramUpdatePaused()) {
+ return;
+ }
+ 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.
+ mHandler.removeMessages(MSG_UPDATE_PREFETCH_PROGRAM);
+ mHandler.sendEmptyMessage(MSG_UPDATE_PREFETCH_PROGRAM);
+ }
+ }
+ };
mProgramPrefetchUpdateWaitMs = PROGRAM_PREFETCH_UPDATE_WAIT_MS;
}
@@ -149,18 +174,16 @@ public class ProgramDataManager implements MemoryManageable {
}
/**
- * Set the program prefetch update wait which gives the delay to query all programs from DB
- * to prevent from too frequent DB queries.
- * Default value is {@link #PROGRAM_PREFETCH_UPDATE_WAIT_MS}
+ * Set the program prefetch update wait which gives the delay to query all programs from DB to
+ * prevent from too frequent DB queries. Default value is {@link
+ * #PROGRAM_PREFETCH_UPDATE_WAIT_MS}
*/
@VisibleForTesting
void setProgramPrefetchUpdateWait(long programPrefetchUpdateWaitMs) {
mProgramPrefetchUpdateWaitMs = programPrefetchUpdateWaitMs;
}
- /**
- * Starts the manager.
- */
+ /** Starts the manager. */
public void start() {
if (mStarted) {
return;
@@ -172,8 +195,7 @@ public class ProgramDataManager implements MemoryManageable {
if (mPrefetchEnabled) {
mHandler.sendEmptyMessage(MSG_UPDATE_PREFETCH_PROGRAM);
}
- mContentResolver.registerContentObserver(Programs.CONTENT_URI,
- true, mProgramObserver);
+ mContentResolver.registerContentObserver(Programs.CONTENT_URI, true, mProgramObserver);
}
/**
@@ -214,9 +236,7 @@ public class ProgramDataManager implements MemoryManageable {
return new ArrayList<>(mChannelIdCurrentProgramMap.values());
}
- /**
- * Reloads program data.
- */
+ /** Reloads program data. */
public void reload() {
if (!mHandler.hasMessages(MSG_UPDATE_CURRENT_PROGRAMS)) {
mHandler.sendEmptyMessage(MSG_UPDATE_CURRENT_PROGRAMS);
@@ -226,35 +246,27 @@ public class ProgramDataManager implements MemoryManageable {
}
}
- /**
- * A listener interface to receive notification on program data retrieval from DB.
- */
+ /** A listener interface to receive notification on program data retrieval from DB. */
public interface Listener {
/**
- * Called when a Program data is now available through getProgram()
- * after the DB operation is done which wasn't before.
- * This would be called only if fetched data is around the selected program.
- **/
+ * Called when a Program data is now available through getProgram() after the DB operation
+ * is done which wasn't before. This would be called only if fetched data is around the
+ * selected program.
+ */
void onProgramUpdated();
}
- /**
- * Adds the {@link Listener}.
- */
+ /** Adds the {@link Listener}. */
public void addListener(Listener listener) {
mListeners.add(listener);
}
- /**
- * Removes the {@link Listener}.
- */
+ /** Removes the {@link Listener}. */
public void removeListener(Listener listener) {
mListeners.remove(listener);
}
- /**
- * Enables or Disables program prefetch.
- */
+ /** Enables or Disables program prefetch. */
public void setPrefetchEnabled(boolean enable) {
if (mPrefetchEnabled == enable) {
return;
@@ -276,10 +288,10 @@ public class ProgramDataManager implements MemoryManageable {
/**
* Returns the programs for the given channel which ends after the given start time.
*
- * <p> Prefetch should be enabled to call it.
+ * <p>Prefetch should be enabled to call it.
*
* @return {@link List} with Programs. It may includes dummy program if the entry needs DB
- * operations to get.
+ * operations to get.
*/
public List<Program> getPrograms(long channelId, long startTime) {
SoftPreconditions.checkState(mPrefetchEnabled, TAG, "Prefetch is disabled.");
@@ -292,9 +304,12 @@ public class ProgramDataManager implements MemoryManageable {
cachedPrograms.subList(startIndex, cachedPrograms.size()));
}
- // Returns the index of program that is played at the specified time.
- // If there isn't, return the first program among programs that starts after the given time
- // if returnNextProgram is {@code true}.
+ /**
+ * Returns the index of program that is played at the specified time.
+ *
+ * <p>If there isn't, return the first program among programs that starts after the given time
+ * if returnNextProgram is {@code true}.
+ */
private int getProgramIndexAt(List<Program> programs, long time) {
Program key = mZeroLengthProgramCache.get(time);
if (key == null) {
@@ -321,38 +336,38 @@ public class ProgramDataManager implements MemoryManageable {
* Adds the listener to be notified if current program is updated for a channel.
*
* @param channelId A channel ID to get notified. If it's {@link Channel#INVALID_ID}, the
- * listener would be called whenever a current program is updated.
+ * listener would be called whenever a current program is updated.
*/
public void addOnCurrentProgramUpdatedListener(
long channelId, OnCurrentProgramUpdatedListener listener) {
- mChannelId2ProgramUpdatedListeners
- .put(channelId, listener);
+ mChannelId2ProgramUpdatedListeners.put(channelId, listener);
}
/**
- * Removes the listener previously added by
- * {@link #addOnCurrentProgramUpdatedListener(long, OnCurrentProgramUpdatedListener)}.
+ * Removes the listener previously added by {@link #addOnCurrentProgramUpdatedListener(long,
+ * OnCurrentProgramUpdatedListener)}.
*/
public void removeOnCurrentProgramUpdatedListener(
long channelId, OnCurrentProgramUpdatedListener listener) {
- mChannelId2ProgramUpdatedListeners
- .remove(channelId, listener);
+ mChannelId2ProgramUpdatedListeners.remove(channelId, listener);
}
private void notifyCurrentProgramUpdate(long channelId, Program program) {
- for (OnCurrentProgramUpdatedListener listener : mChannelId2ProgramUpdatedListeners
- .get(channelId)) {
+ for (OnCurrentProgramUpdatedListener listener :
+ mChannelId2ProgramUpdatedListeners.get(channelId)) {
listener.onCurrentProgramUpdated(channelId, program);
}
- for (OnCurrentProgramUpdatedListener listener : mChannelId2ProgramUpdatedListeners
- .get(Channel.INVALID_ID)) {
+ for (OnCurrentProgramUpdatedListener listener :
+ mChannelId2ProgramUpdatedListeners.get(Channel.INVALID_ID)) {
listener.onCurrentProgramUpdated(channelId, program);
}
}
private void updateCurrentProgram(long channelId, Program program) {
- Program previousProgram = program == null ? mChannelIdCurrentProgramMap.remove(channelId)
- : mChannelIdCurrentProgramMap.put(channelId, program);
+ Program previousProgram =
+ program == null
+ ? mChannelIdCurrentProgramMap.remove(channelId)
+ : mChannelIdCurrentProgramMap.put(channelId, program);
if (!Objects.equals(program, previousProgram)) {
if (mPrefetchEnabled) {
removePreviousProgramsAndUpdateCurrentProgramInCache(channelId, program);
@@ -362,20 +377,23 @@ public class ProgramDataManager implements MemoryManageable {
long delayedTime;
if (program == null) {
- delayedTime = PERIODIC_PROGRAM_UPDATE_MIN_MS
- + (long) (Math.random() * (PERIODIC_PROGRAM_UPDATE_MAX_MS
- - PERIODIC_PROGRAM_UPDATE_MIN_MS));
+ delayedTime =
+ PERIODIC_PROGRAM_UPDATE_MIN_MS
+ + (long)
+ (Math.random()
+ * (PERIODIC_PROGRAM_UPDATE_MAX_MS
+ - PERIODIC_PROGRAM_UPDATE_MIN_MS));
} else {
delayedTime = program.getEndTimeUtcMillis() - mClock.currentTimeMillis();
}
- mHandler.sendMessageDelayed(mHandler.obtainMessage(
- MSG_UPDATE_ONE_CURRENT_PROGRAM, channelId), delayedTime);
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(MSG_UPDATE_ONE_CURRENT_PROGRAM, channelId), delayedTime);
}
private void removePreviousProgramsAndUpdateCurrentProgramInCache(
long channelId, Program currentProgram) {
SoftPreconditions.checkState(mPrefetchEnabled, TAG, "Prefetch is disabled.");
- if (!Program.isValid(currentProgram)) {
+ if (!Program.isProgramValid(currentProgram)) {
return;
}
ArrayList<Program> cachedPrograms = mChannelIdProgramCache.remove(channelId);
@@ -391,27 +409,29 @@ public class ProgramDataManager implements MemoryManageable {
continue;
}
- if (cachedProgram.getEndTimeUtcMillis() <= currentProgram
- .getStartTimeUtcMillis()) {
+ if (cachedProgram.getEndTimeUtcMillis() <= currentProgram.getStartTimeUtcMillis()) {
// Keep the programs that ends earlier than current program
// but later than mPrefetchTimeRangeStartMs.
continue;
}
// Update dummy program around current program if any.
- if (cachedProgram.getStartTimeUtcMillis() < currentProgram
- .getStartTimeUtcMillis()) {
+ if (cachedProgram.getStartTimeUtcMillis() < currentProgram.getStartTimeUtcMillis()) {
// The dummy program starts earlier than the current program. Adjust its end time.
- i.set(createDummyProgram(cachedProgram.getStartTimeUtcMillis(),
- currentProgram.getStartTimeUtcMillis()));
+ i.set(
+ createDummyProgram(
+ cachedProgram.getStartTimeUtcMillis(),
+ currentProgram.getStartTimeUtcMillis()));
i.add(currentProgram);
} else {
i.set(currentProgram);
}
if (currentProgram.getEndTimeUtcMillis() < cachedProgram.getEndTimeUtcMillis()) {
// The dummy program ends later than the current program. Adjust its start time.
- i.add(createDummyProgram(currentProgram.getEndTimeUtcMillis(),
- cachedProgram.getEndTimeUtcMillis()));
+ i.add(
+ createDummyProgram(
+ currentProgram.getEndTimeUtcMillis(),
+ cachedProgram.getEndTimeUtcMillis()));
}
break;
}
@@ -425,8 +445,8 @@ public class ProgramDataManager implements MemoryManageable {
private void handleUpdateCurrentPrograms() {
if (mProgramsUpdateTask != null) {
- mHandler.sendEmptyMessageDelayed(MSG_UPDATE_CURRENT_PROGRAMS,
- CURRENT_PROGRAM_UPDATE_WAIT_MS);
+ mHandler.sendEmptyMessageDelayed(
+ MSG_UPDATE_CURRENT_PROGRAMS, CURRENT_PROGRAM_UPDATE_WAIT_MS);
return;
}
clearTask(mProgramUpdateTaskMap);
@@ -443,10 +463,13 @@ public class ProgramDataManager implements MemoryManageable {
private boolean mSuccess;
public ProgramsPrefetchTask() {
+ super(mDbExecutor);
long time = mClock.currentTimeMillis();
- mStartTimeMs = Utils
- .floorTime(time - PROGRAM_GUIDE_SNAP_TIME_MS, PROGRAM_GUIDE_SNAP_TIME_MS);
- mEndTimeMs = mStartTimeMs + PROGRAM_GUIDE_MAX_TIME_RANGE;
+ mStartTimeMs =
+ Utils.floorTime(time - PROGRAM_GUIDE_SNAP_TIME_MS, PROGRAM_GUIDE_SNAP_TIME_MS);
+ mEndTimeMs =
+ mStartTimeMs
+ + TimeUnit.HOURS.toMillis(PROGRAM_GUIDE_MAX_HOURS.get(mRemoteConfig));
mSuccess = false;
}
@@ -454,12 +477,19 @@ public class ProgramDataManager implements MemoryManageable {
protected Map<Long, ArrayList<Program>> doInBackground(Void... params) {
Map<Long, ArrayList<Program>> programMap = new HashMap<>();
if (DEBUG) {
- Log.d(TAG, "Starts programs prefetch. " + Utils.toTimeString(mStartTimeMs) + "-"
- + Utils.toTimeString(mEndTimeMs));
+ Log.d(
+ TAG,
+ "Starts programs prefetch. "
+ + Utils.toTimeString(mStartTimeMs)
+ + "-"
+ + Utils.toTimeString(mEndTimeMs));
}
- Uri uri = Programs.CONTENT_URI.buildUpon()
- .appendQueryParameter(PARAM_START_TIME, String.valueOf(mStartTimeMs))
- .appendQueryParameter(PARAM_END_TIME, String.valueOf(mEndTimeMs)).build();
+ Uri uri =
+ Programs.CONTENT_URI
+ .buildUpon()
+ .appendQueryParameter(PARAM_START_TIME, String.valueOf(mStartTimeMs))
+ .appendQueryParameter(PARAM_END_TIME, String.valueOf(mEndTimeMs))
+ .build();
final int RETRY_COUNT = 3;
Program lastReadProgram = null;
for (int retryCount = RETRY_COUNT; retryCount > 0; retryCount--) {
@@ -467,8 +497,8 @@ public class ProgramDataManager implements MemoryManageable {
return null;
}
programMap.clear();
- try (Cursor c = mContentResolver.query(uri, Program.PROJECTION, null, null,
- SORT_BY_TIME)) {
+ try (Cursor c =
+ mContentResolver.query(uri, Program.PROJECTION, null, null, SORT_BY_TIME)) {
if (c == null) {
continue;
}
@@ -527,14 +557,16 @@ public class ProgramDataManager implements MemoryManageable {
long currentTime = mClock.currentTimeMillis();
mLastPrefetchTaskRunMs = currentTime;
nextMessageDelayedTime =
- Utils.floorTime(mLastPrefetchTaskRunMs + PROGRAM_GUIDE_SNAP_TIME_MS,
- PROGRAM_GUIDE_SNAP_TIME_MS) - currentTime;
+ Utils.floorTime(
+ mLastPrefetchTaskRunMs + PROGRAM_GUIDE_SNAP_TIME_MS,
+ PROGRAM_GUIDE_SNAP_TIME_MS)
+ - currentTime;
} else {
nextMessageDelayedTime = PERIODIC_PROGRAM_UPDATE_MIN_MS;
}
if (!mHandler.hasMessages(MSG_UPDATE_PREFETCH_PROGRAM)) {
- mHandler.sendEmptyMessageDelayed(MSG_UPDATE_PREFETCH_PROGRAM,
- nextMessageDelayedTime);
+ mHandler.sendEmptyMessageDelayed(
+ MSG_UPDATE_PREFETCH_PROGRAM, nextMessageDelayedTime);
}
}
}
@@ -547,10 +579,18 @@ public class ProgramDataManager implements MemoryManageable {
private class ProgramsUpdateTask extends AsyncDbTask.AsyncQueryTask<List<Program>> {
public ProgramsUpdateTask(ContentResolver contentResolver, long time) {
- super(contentResolver, Programs.CONTENT_URI.buildUpon()
+ super(
+ mDbExecutor,
+ contentResolver,
+ Programs.CONTENT_URI
+ .buildUpon()
.appendQueryParameter(PARAM_START_TIME, String.valueOf(time))
- .appendQueryParameter(PARAM_END_TIME, String.valueOf(time)).build(),
- Program.PROJECTION, null, null, SORT_BY_TIME);
+ .appendQueryParameter(PARAM_END_TIME, String.valueOf(time))
+ .build(),
+ Program.PROJECTION,
+ null,
+ null,
+ SORT_BY_TIME);
}
@Override
@@ -604,10 +644,17 @@ public class ProgramDataManager implements MemoryManageable {
private class UpdateCurrentProgramForChannelTask extends AsyncDbTask.AsyncQueryTask<Program> {
private final long mChannelId;
- private UpdateCurrentProgramForChannelTask(ContentResolver contentResolver, long channelId,
- long time) {
- super(contentResolver, TvContract.buildProgramsUriForChannel(channelId, time, time),
- Program.PROJECTION, null, null, SORT_BY_TIME);
+
+ private UpdateCurrentProgramForChannelTask(
+ ContentResolver contentResolver, long channelId, long time) {
+ super(
+ mDbExecutor,
+ contentResolver,
+ TvContract.buildProgramsUriForChannel(channelId, time, time),
+ Program.PROJECTION,
+ null,
+ null,
+ SORT_BY_TIME);
mChannelId = channelId;
}
@@ -638,48 +685,55 @@ public class ProgramDataManager implements MemoryManageable {
case MSG_UPDATE_CURRENT_PROGRAMS:
handleUpdateCurrentPrograms();
break;
- case MSG_UPDATE_ONE_CURRENT_PROGRAM: {
- long channelId = (Long) msg.obj;
- UpdateCurrentProgramForChannelTask oldTask = mProgramUpdateTaskMap
- .get(channelId);
- if (oldTask != null) {
- oldTask.cancel(true);
- }
- UpdateCurrentProgramForChannelTask
- task = new UpdateCurrentProgramForChannelTask(
- mContentResolver, channelId, mClock.currentTimeMillis());
- mProgramUpdateTaskMap.put(channelId, task);
- task.executeOnDbThread();
- break;
- }
- case MSG_UPDATE_PREFETCH_PROGRAM: {
- if (isProgramUpdatePaused()) {
- return;
- }
- if (mProgramsPrefetchTask != null) {
- mHandler.sendEmptyMessageDelayed(msg.what, mProgramPrefetchUpdateWaitMs);
- return;
+ case MSG_UPDATE_ONE_CURRENT_PROGRAM:
+ {
+ long channelId = (Long) msg.obj;
+ UpdateCurrentProgramForChannelTask oldTask =
+ mProgramUpdateTaskMap.get(channelId);
+ if (oldTask != null) {
+ oldTask.cancel(true);
+ }
+ UpdateCurrentProgramForChannelTask task =
+ new UpdateCurrentProgramForChannelTask(
+ mContentResolver, channelId, mClock.currentTimeMillis());
+ mProgramUpdateTaskMap.put(channelId, task);
+ task.executeOnDbThread();
+ break;
}
- long delayMillis = mLastPrefetchTaskRunMs + mProgramPrefetchUpdateWaitMs
- - mClock.currentTimeMillis();
- if (delayMillis > 0) {
- mHandler.sendEmptyMessageDelayed(MSG_UPDATE_PREFETCH_PROGRAM, delayMillis);
- } else {
- mProgramsPrefetchTask = new ProgramsPrefetchTask();
- mProgramsPrefetchTask.executeOnDbThread();
+ case MSG_UPDATE_PREFETCH_PROGRAM:
+ {
+ if (isProgramUpdatePaused()) {
+ return;
+ }
+ if (mProgramsPrefetchTask != null) {
+ mHandler.sendEmptyMessageDelayed(
+ msg.what, mProgramPrefetchUpdateWaitMs);
+ return;
+ }
+ long delayMillis =
+ mLastPrefetchTaskRunMs
+ + mProgramPrefetchUpdateWaitMs
+ - mClock.currentTimeMillis();
+ if (delayMillis > 0) {
+ mHandler.sendEmptyMessageDelayed(
+ MSG_UPDATE_PREFETCH_PROGRAM, delayMillis);
+ } else {
+ mProgramsPrefetchTask = new ProgramsPrefetchTask();
+ mProgramsPrefetchTask.executeOnDbThread();
+ }
+ break;
}
- break;
- }
+ default:
+ // Do nothing
}
}
}
/**
- * Pause program update.
- * Updating program data will result in UI refresh,
- * but UI is fragile to handle it so we'd better disable it for a while.
+ * Pause program update. Updating program data will result in UI refresh, but UI is fragile to
+ * handle it so we'd better disable it for a while.
*
- * <p> Prefetch should be enabled to call it.
+ * <p>Prefetch should be enabled to call it.
*/
public void setPauseProgramUpdate(boolean pauseProgramUpdate) {
SoftPreconditions.checkState(mPrefetchEnabled, TAG, "Prefetch is disabled.");
@@ -700,11 +754,10 @@ public class ProgramDataManager implements MemoryManageable {
}
/**
- * Sets program data prefetch time range.
- * Any program data that ends before the start time will be removed from the cache later.
- * Note that there's no limit for end time.
+ * Sets program data prefetch time range. Any program data that ends before the start time will
+ * be removed from the cache later. Note that there's no limit for end time.
*
- * <p> Prefetch should be enabled to call it.
+ * <p>Prefetch should be enabled to call it.
*/
public void setPrefetchTimeRange(long startTimeMs) {
SoftPreconditions.checkState(mPrefetchEnabled, TAG, "Prefetch is disabled.");
@@ -736,7 +789,8 @@ public class ProgramDataManager implements MemoryManageable {
return new Program.Builder()
.setChannelId(Channel.INVALID_ID)
.setStartTimeUtcMillis(startTimeMs)
- .setEndTimeUtcMillis(endTimeMs).build();
+ .setEndTimeUtcMillis(endTimeMs)
+ .build();
}
@Override
diff --git a/src/com/android/tv/data/StreamInfo.java b/src/com/android/tv/data/StreamInfo.java
index 709863cf..e4237bf4 100644
--- a/src/com/android/tv/data/StreamInfo.java
+++ b/src/com/android/tv/data/StreamInfo.java
@@ -17,6 +17,7 @@
package com.android.tv.data;
import android.media.tv.TvContentRating;
+import com.android.tv.data.api.Channel;
public interface StreamInfo {
int VIDEO_DEFINITION_LEVEL_UNKNOWN = 0;
@@ -28,19 +29,26 @@ public interface StreamInfo {
int AUDIO_CHANNEL_COUNT_UNKNOWN = 0;
Channel getCurrentChannel();
+
TvContentRating getBlockedContentRating();
int getVideoWidth();
+
int getVideoHeight();
+
float getVideoFrameRate();
+
float getVideoDisplayAspectRatio();
+
int getVideoDefinitionLevel();
+
int getAudioChannelCount();
+
boolean hasClosedCaption();
+
boolean isVideoAvailable();
- /**
- * Returns true, if video or audio is available.
- */
+ /** Returns true, if video or audio is available. */
boolean isVideoOrAudioAvailable();
+
int getVideoUnavailableReason();
}
diff --git a/src/com/android/tv/data/TvInputNewComparator.java b/src/com/android/tv/data/TvInputNewComparator.java
index acc3e38a..effca970 100644
--- a/src/com/android/tv/data/TvInputNewComparator.java
+++ b/src/com/android/tv/data/TvInputNewComparator.java
@@ -17,15 +17,11 @@
package com.android.tv.data;
import android.media.tv.TvInputInfo;
-
import com.android.tv.util.SetupUtils;
import com.android.tv.util.TvInputManagerHelper;
-
import java.util.Comparator;
-/**
- * Compares TV input such that the new input comes first.
- */
+/** Compares TV input such that the new input comes first. */
public class TvInputNewComparator implements Comparator<TvInputInfo> {
private final SetupUtils mSetupUtils;
private final TvInputManagerHelper mInputManager;
diff --git a/src/com/android/tv/data/WatchedHistoryManager.java b/src/com/android/tv/data/WatchedHistoryManager.java
index 3edd7b1a..7187efd1 100644
--- a/src/com/android/tv/data/WatchedHistoryManager.java
+++ b/src/com/android/tv/data/WatchedHistoryManager.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2017 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.content.Context;
@@ -12,9 +27,8 @@ import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import android.support.annotation.WorkerThread;
import android.util.Log;
-
-import com.android.tv.common.SharedPreferencesUtils;
-
+import com.android.tv.common.util.SharedPreferencesUtils;
+import com.android.tv.data.api.Channel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -25,13 +39,14 @@ import java.util.concurrent.TimeUnit;
/**
* A class to manage watched history.
*
- * <p>When there is no access to watched table of TvProvider,
- * this class is used to build up watched history and to compute recent channels.
+ * <p>When there is no access to watched table of TvProvider, this class is used to build up watched
+ * history and to compute recent channels.
+ *
* <p>Note that this class is not thread safe. Please use this on one thread.
*/
public class WatchedHistoryManager {
- private final static String TAG = "WatchedHistoryManager";
- private final static boolean DEBUG = false;
+ private static final String TAG = "WatchedHistoryManager";
+ private static final boolean DEBUG = false;
private static final int MAX_HISTORY_SIZE = 10000;
private static final String PREF_KEY_LAST_INDEX = "last_index";
@@ -47,8 +62,8 @@ public class WatchedHistoryManager {
new OnSharedPreferenceChangeListener() {
@Override
@MainThread
- public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
- String key) {
+ public void onSharedPreferenceChanged(
+ SharedPreferences sharedPreferences, String key) {
if (key.equals(PREF_KEY_LAST_INDEX)) {
final long lastIndex = mSharedPreferences.getLong(PREF_KEY_LAST_INDEX, -1);
if (lastIndex <= mLastIndex) {
@@ -57,23 +72,26 @@ public class WatchedHistoryManager {
// onSharedPreferenceChanged is always called in a main thread.
// onNewRecordAdded will be called in the same thread as the thread
// which created this instance.
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- for (long i = mLastIndex + 1; i <= lastIndex; ++i) {
- WatchedRecord record = decode(
- mSharedPreferences.getString(getSharedPreferencesKey(i),
- null));
- if (record != null) {
- mWatchedHistory.add(record);
- if (mListener != null) {
- mListener.onNewRecordAdded(record);
+ mHandler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ for (long i = mLastIndex + 1; i <= lastIndex; ++i) {
+ WatchedRecord record =
+ decode(
+ mSharedPreferences.getString(
+ getSharedPreferencesKey(i),
+ null));
+ if (record != null) {
+ mWatchedHistory.add(record);
+ if (mListener != null) {
+ mListener.onNewRecordAdded(record);
+ }
+ }
}
+ mLastIndex = lastIndex;
}
- }
- mLastIndex = lastIndex;
- }
- });
+ });
}
}
};
@@ -94,9 +112,7 @@ public class WatchedHistoryManager {
mHandler = new Handler();
}
- /**
- * Starts the manager. It loads history data from {@link SharedPreferences}.
- */
+ /** Starts the manager. It loads history data from {@link SharedPreferences}. */
public void start() {
if (mStarted) {
return;
@@ -123,22 +139,22 @@ public class WatchedHistoryManager {
@WorkerThread
private void loadWatchedHistory() {
- mSharedPreferences = mContext.getSharedPreferences(
- SharedPreferencesUtils.SHARED_PREF_WATCHED_HISTORY, Context.MODE_PRIVATE);
+ mSharedPreferences =
+ mContext.getSharedPreferences(
+ SharedPreferencesUtils.SHARED_PREF_WATCHED_HISTORY, Context.MODE_PRIVATE);
mLastIndex = mSharedPreferences.getLong(PREF_KEY_LAST_INDEX, -1);
if (mLastIndex >= 0 && mLastIndex < mMaxHistorySize) {
for (int i = 0; i <= mLastIndex; ++i) {
WatchedRecord record =
- decode(mSharedPreferences.getString(getSharedPreferencesKey(i),
- null));
+ decode(mSharedPreferences.getString(getSharedPreferencesKey(i), null));
if (record != null) {
mWatchedHistory.add(record);
}
}
} else if (mLastIndex >= mMaxHistorySize) {
for (long i = mLastIndex - mMaxHistorySize + 1; i <= mLastIndex; ++i) {
- WatchedRecord record = decode(mSharedPreferences.getString(
- getSharedPreferencesKey(i), null));
+ WatchedRecord record =
+ decode(mSharedPreferences.getString(getSharedPreferencesKey(i), null));
if (record != null) {
mWatchedHistory.add(record);
}
@@ -173,9 +189,7 @@ public class WatchedHistoryManager {
return mLoaded;
}
- /**
- * Logs the record of the watched channel.
- */
+ /** Logs the record of the watched channel. */
public void logChannelViewStop(Channel channel, long endTime, long duration) {
if (duration < MIN_DURATION_MS) {
return;
@@ -185,7 +199,8 @@ public class WatchedHistoryManager {
if (DEBUG) Log.d(TAG, "Log a watched record. " + record);
mWatchedHistory.add(record);
++mLastIndex;
- mSharedPreferences.edit()
+ mSharedPreferences
+ .edit()
.putString(getSharedPreferencesKey(mLastIndex), encode(record))
.putLong(PREF_KEY_LAST_INDEX, mLastIndex)
.apply();
@@ -197,16 +212,14 @@ public class WatchedHistoryManager {
}
}
- /**
- * Sets {@link Listener}.
- */
+ /** Sets {@link Listener}. */
public void setListener(Listener listener) {
mListener = listener;
}
/**
- * 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.
+ * 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() {
@@ -242,8 +255,12 @@ public class WatchedHistoryManager {
@Override
public String toString() {
- return "WatchedRecord: id=" + channelId + ",watchedStartTime=" + watchedStartTime
- + ",duration=" + duration;
+ return "WatchedRecord: id="
+ + channelId
+ + ",watchedStartTime="
+ + watchedStartTime
+ + ",duration="
+ + duration;
}
@Override
@@ -281,10 +298,9 @@ public class WatchedHistoryManager {
}
public interface Listener {
- /**
- * Called when history is loaded.
- */
+ /** Called when history is loaded. */
void onLoadFinished();
+
void onNewRecordAdded(WatchedRecord watchedRecord);
}
}
diff --git a/src/com/android/tv/data/api/Channel.java b/src/com/android/tv/data/api/Channel.java
new file mode 100644
index 00000000..496331cf
--- /dev/null
+++ b/src/com/android/tv/data/api/Channel.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2018 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.api;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.support.annotation.Nullable;
+import com.android.tv.util.images.ImageLoader.ImageLoaderCallback;
+
+/**
+ * Interface for {@link com.android.tv.data.ChannelImpl}.
+ *
+ * <p><b>NOTE</b> Normally you should not use an interface for a data object like {@code
+ * ChannelImpl}, however there are many circular dependencies. An interface is the easiest way to
+ * break the cycles.
+ */
+public interface Channel {
+
+ long INVALID_ID = -1;
+ int LOAD_IMAGE_TYPE_CHANNEL_LOGO = 1;
+ int LOAD_IMAGE_TYPE_APP_LINK_ICON = 2;
+ int LOAD_IMAGE_TYPE_APP_LINK_POSTER_ART = 3;
+ /**
+ * When a TIS doesn't provide any information about app link, and it doesn't have a leanback
+ * launch intent, there will be no app link card for the TIS.
+ */
+ int APP_LINK_TYPE_NONE = -1;
+ /**
+ * When a TIS provide a specific app link information, the app link card will be {@code
+ * APP_LINK_TYPE_CHANNEL} which contains all the provided information.
+ */
+ int APP_LINK_TYPE_CHANNEL = 1;
+ /**
+ * When a TIS doesn't provide a specific app link information, but the app has a leanback launch
+ * intent, the app link card will be {@code APP_LINK_TYPE_APP} which launches the application.
+ */
+ int APP_LINK_TYPE_APP = 2;
+ /** Channel number delimiter between major and minor parts. */
+ char CHANNEL_NUMBER_DELIMITER = '-';
+
+ long getId();
+
+ Uri getUri();
+
+ String getPackageName();
+
+ String getInputId();
+
+ String getType();
+
+ String getDisplayNumber();
+
+ @Nullable
+ String getDisplayName();
+
+ String getDescription();
+
+ String getVideoFormat();
+
+ boolean isPassthrough();
+
+ String getDisplayText();
+
+ String getAppLinkText();
+
+ int getAppLinkColor();
+
+ String getAppLinkIconUri();
+
+ String getAppLinkPosterArtUri();
+
+ String getAppLinkIntentUri();
+
+ String getLogoUri();
+
+ boolean isRecordingProhibited();
+
+ boolean isPhysicalTunerChannel();
+
+ boolean isBrowsable();
+
+ boolean isSearchable();
+
+ boolean isLocked();
+
+ boolean hasSameReadOnlyInfo(Channel mCurrentChannel);
+
+ void setChannelLogoExist(boolean result);
+
+ void setBrowsable(boolean browsable);
+
+ void setLocked(boolean locked);
+
+ void copyFrom(Channel channel);
+
+ void setLogoUri(String logoUri);
+
+ boolean channelLogoExists();
+
+ void loadBitmap(
+ Context context,
+ int loadImageTypeChannelLogo,
+ int mChannelLogoImageViewWidth,
+ int mChannelLogoImageViewHeight,
+ ImageLoaderCallback<?> channelLogoCallback);
+
+ int getAppLinkType(Context context);
+
+ Intent getAppLinkIntent(Context context);
+
+ void prefetchImage(
+ Context mContext,
+ int loadImageTypeChannelLogo,
+ int mPosterArtWidth,
+ int mPosterArtHeight);
+}
diff --git a/src/com/android/tv/data/epg/AutoValue_EpgReader_EpgChannel.java b/src/com/android/tv/data/epg/AutoValue_EpgReader_EpgChannel.java
new file mode 100644
index 00000000..795ad5c4
--- /dev/null
+++ b/src/com/android/tv/data/epg/AutoValue_EpgReader_EpgChannel.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 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 com.android.tv.data.api.Channel;
+
+/**
+ * Hand copy of generated Autovalue class.
+ *
+ * TODO get autovalue working
+ */
+final class AutoValue_EpgReader_EpgChannel extends EpgReader.EpgChannel {
+
+ private final Channel channel;
+ private final String epgChannelId;
+
+ AutoValue_EpgReader_EpgChannel(
+ Channel channel,
+ String epgChannelId) {
+ if (channel == null) {
+ throw new NullPointerException("Null channel");
+ }
+ this.channel = channel;
+ if (epgChannelId == null) {
+ throw new NullPointerException("Null epgChannelId");
+ }
+ this.epgChannelId = epgChannelId;
+ }
+
+ @Override
+ public Channel getChannel() {
+ return channel;
+ }
+
+ @Override
+ public String getEpgChannelId() {
+ return epgChannelId;
+ }
+
+ @Override
+ public String toString() {
+ return "EpgChannel{"
+ + "channel=" + channel + ", "
+ + "epgChannelId=" + epgChannelId
+ + "}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof EpgReader.EpgChannel) {
+ EpgReader.EpgChannel that = (EpgReader.EpgChannel) o;
+ return (this.channel.equals(that.getChannel()))
+ && (this.epgChannelId.equals(that.getEpgChannelId()));
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int h = 1;
+ h *= 1000003;
+ h ^= this.channel.hashCode();
+ h *= 1000003;
+ h ^= this.epgChannelId.hashCode();
+ return h;
+ }
+
+}
+
diff --git a/src/com/android/tv/data/epg/EpgFetchHelper.java b/src/com/android/tv/data/epg/EpgFetchHelper.java
index 5693c877..3c7112ec 100644
--- a/src/com/android/tv/data/epg/EpgFetchHelper.java
+++ b/src/com/android/tv/data/epg/EpgFetchHelper.java
@@ -27,15 +27,15 @@ import android.preference.PreferenceManager;
import android.support.annotation.WorkerThread;
import android.text.TextUtils;
import android.util.Log;
-
+import com.android.tv.common.CommonConstants;
+import com.android.tv.common.util.Clock;
import com.android.tv.data.Program;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
-/** The helper class for {@link com.android.tv.data.epg.EpgFetcher} */
+/** The helper class for {@link EpgFetcher} */
class EpgFetchHelper {
private static final String TAG = "EpgFetchHelper";
private static final boolean DEBUG = false;
@@ -45,15 +45,15 @@ class EpgFetchHelper {
// Value: Long
private static final String KEY_LAST_UPDATED_EPG_TIMESTAMP =
- "com.android.tv.data.epg.EpgFetcher.LastUpdatedEpgTimestamp";
+ CommonConstants.BASE_PACKAGE + ".data.epg.EpgFetcher.LastUpdatedEpgTimestamp";
// Value: String
private static final String KEY_LAST_LINEUP_ID =
- "com.android.tv.data.epg.EpgFetcher.LastLineupId";
+ CommonConstants.BASE_PACKAGE + ".data.epg.EpgFetcher.LastLineupId";
private static long sLastEpgUpdatedTimestamp = -1;
private static String sLastLineupId;
- private EpgFetchHelper() { }
+ private EpgFetchHelper() {}
/**
* Updates newly fetched EPG data for the given channel to local providers. The method will
@@ -61,18 +61,19 @@ class EpgFetchHelper {
* of that channel in the database one by one. It will update the matched old program, or insert
* the new program if there is no matching program can be found in the database and at the same
* time remove those old programs which conflicts with the inserted one.
-
+ *
* @param channelId the target channel ID.
* @param fetchedPrograms the newly fetched program data.
* @return {@code true} if new program data are successfully updated. Otherwise {@code false}.
*/
- static boolean updateEpgData(Context context, long channelId, List<Program> fetchedPrograms) {
+ static boolean updateEpgData(
+ Context context, Clock clock, long channelId, List<Program> fetchedPrograms) {
final int fetchedProgramsCount = fetchedPrograms.size();
if (fetchedProgramsCount == 0) {
return false;
}
boolean updated = false;
- long startTimeMs = System.currentTimeMillis();
+ long startTimeMs = clock.currentTimeMillis();
long endTimeMs = startTimeMs + PROGRAM_QUERY_DURATION_MS;
List<Program> oldPrograms = queryPrograms(context, channelId, startTimeMs, endTimeMs);
int oldProgramsIndex = 0;
@@ -82,8 +83,10 @@ class EpgFetchHelper {
// or insert new program if there is no matching program in the database.
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
while (newProgramsIndex < fetchedProgramsCount) {
- Program oldProgram = oldProgramsIndex < oldPrograms.size()
- ? oldPrograms.get(oldProgramsIndex) : null;
+ Program oldProgram =
+ oldProgramsIndex < oldPrograms.size()
+ ? oldPrograms.get(oldProgramsIndex)
+ : null;
Program newProgram = fetchedPrograms.get(newProgramsIndex);
boolean addNewProgram = false;
if (oldProgram != null) {
@@ -95,18 +98,20 @@ class EpgFetchHelper {
// 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(Program.toContentValues(newProgram))
- .build());
+ ops.add(
+ ContentProviderOperation.newUpdate(
+ TvContract.buildProgramUri(oldProgram.getId()))
+ .withValues(Program.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());
+ 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
@@ -120,10 +125,10 @@ class EpgFetchHelper {
newProgramsIndex++;
}
if (addNewProgram) {
- ops.add(ContentProviderOperation
- .newInsert(Programs.CONTENT_URI)
- .withValues(Program.toContentValues(newProgram))
- .build());
+ ops.add(
+ ContentProviderOperation.newInsert(Programs.CONTENT_URI)
+ .withValues(Program.toContentValues(newProgram))
+ .build());
}
// Throttle the batch operation not to cause TransactionTooLargeException.
if (ops.size() > BATCH_OPERATION_COUNT || newProgramsIndex >= fetchedProgramsCount) {
@@ -150,11 +155,17 @@ class EpgFetchHelper {
return updated;
}
- private static List<Program> queryPrograms(Context context, long channelId,
- long startTimeMs, long endTimeMs) {
- try (Cursor c = context.getContentResolver().query(
- TvContract.buildProgramsUriForChannel(channelId, startTimeMs, endTimeMs),
- Program.PROJECTION, null, null, Programs.COLUMN_START_TIME_UTC_MILLIS)) {
+ private static List<Program> queryPrograms(
+ Context context, long channelId, long startTimeMs, long endTimeMs) {
+ try (Cursor c =
+ context.getContentResolver()
+ .query(
+ TvContract.buildProgramsUriForChannel(
+ channelId, startTimeMs, endTimeMs),
+ Program.PROJECTION,
+ null,
+ null,
+ Programs.COLUMN_START_TIME_UTC_MILLIS)) {
if (c == null) {
return Collections.emptyList();
}
@@ -167,8 +178,8 @@ class EpgFetchHelper {
}
/**
- * Returns {@code true} if the {@code oldProgram} needs to be updated with the
- * {@code newProgram}.
+ * Returns {@code true} if the {@code oldProgram} needs to be updated with the {@code
+ * newProgram}.
*/
private static boolean hasSameTitleAndOverlap(Program oldProgram, Program newProgram) {
// NOTE: Here, we update the old program if it has the same title and overlaps with the
@@ -186,24 +197,25 @@ class EpgFetchHelper {
* every time when it needs to fetch EPG data.
*/
@WorkerThread
- synchronized static void setLastLineupId(Context context, String lineupId) {
+ static synchronized void setLastLineupId(Context context, String lineupId) {
if (DEBUG) {
if (lineupId == null) {
Log.d(TAG, "Clear stored lineup id: " + sLastLineupId);
}
}
sLastLineupId = lineupId;
- PreferenceManager.getDefaultSharedPreferences(context).edit()
- .putString(KEY_LAST_LINEUP_ID, lineupId).apply();
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putString(KEY_LAST_LINEUP_ID, lineupId)
+ .apply();
}
- /**
- * Gets the last known lineup ID from shared preferences.
- */
- synchronized static String getLastLineupId(Context context) {
+ /** Gets the last known lineup ID from shared preferences. */
+ static synchronized String getLastLineupId(Context context) {
if (sLastLineupId == null) {
- sLastLineupId = PreferenceManager.getDefaultSharedPreferences(context)
- .getString(KEY_LAST_LINEUP_ID, null);
+ sLastLineupId =
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .getString(KEY_LAST_LINEUP_ID, null);
}
if (DEBUG) Log.d(TAG, "Last lineup is " + sLastLineupId);
return sLastLineupId;
@@ -214,20 +226,21 @@ class EpgFetchHelper {
* out-dated, it's not necessary for EPG fetcher to fetch EPG again.
*/
@WorkerThread
- synchronized static void setLastEpgUpdatedTimestamp(Context context, long timestamp) {
+ static synchronized void setLastEpgUpdatedTimestamp(Context context, long timestamp) {
sLastEpgUpdatedTimestamp = timestamp;
- PreferenceManager.getDefaultSharedPreferences(context).edit().putLong(
- KEY_LAST_UPDATED_EPG_TIMESTAMP, timestamp).apply();
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putLong(KEY_LAST_UPDATED_EPG_TIMESTAMP, timestamp)
+ .apply();
}
- /**
- * Gets the last updated timestamp of EPG data.
- */
- synchronized static long getLastEpgUpdatedTimestamp(Context context) {
+ /** Gets the last updated timestamp of EPG data. */
+ static synchronized long getLastEpgUpdatedTimestamp(Context context) {
if (sLastEpgUpdatedTimestamp < 0) {
- sLastEpgUpdatedTimestamp = PreferenceManager.getDefaultSharedPreferences(context)
- .getLong(KEY_LAST_UPDATED_EPG_TIMESTAMP, 0);
+ sLastEpgUpdatedTimestamp =
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .getLong(KEY_LAST_UPDATED_EPG_TIMESTAMP, 0);
}
return sLastEpgUpdatedTimestamp;
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/data/epg/EpgFetchService.java b/src/com/android/tv/data/epg/EpgFetchService.java
new file mode 100644
index 00000000..aa4f3588
--- /dev/null
+++ b/src/com/android/tv/data/epg/EpgFetchService.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 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.app.job.JobParameters;
+import android.app.job.JobService;
+import com.android.tv.Starter;
+import com.android.tv.TvSingletons;
+import com.android.tv.data.ChannelDataManager;
+
+/** JobService to Fetch EPG data. */
+public class EpgFetchService extends JobService {
+ private EpgFetcher mEpgFetcher;
+ private ChannelDataManager mChannelDataManager;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Starter.start(this);
+ TvSingletons tvSingletons = TvSingletons.getSingletons(getApplicationContext());
+ mEpgFetcher = tvSingletons.getEpgFetcher();
+ mChannelDataManager = tvSingletons.getChannelDataManager();
+ }
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ if (!mChannelDataManager.isDbLoadFinished()) {
+ mChannelDataManager.addListener(
+ new ChannelDataManager.Listener() {
+ @Override
+ public void onLoadFinished() {
+ mChannelDataManager.removeListener(this);
+ if (!mEpgFetcher.executeFetchTaskIfPossible(
+ EpgFetchService.this, params)) {
+ jobFinished(params, false);
+ }
+ }
+
+ @Override
+ public void onChannelListUpdated() {}
+
+ @Override
+ public void onChannelBrowsableChanged() {}
+ });
+ return true;
+ } else {
+ return mEpgFetcher.executeFetchTaskIfPossible(this, params);
+ }
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ mEpgFetcher.stopFetchingJob();
+ return false;
+ }
+}
diff --git a/src/com/android/tv/data/epg/EpgFetcher.java b/src/com/android/tv/data/epg/EpgFetcher.java
index 24f8b826..9c24613d 100644
--- a/src/com/android/tv/data/epg/EpgFetcher.java
+++ b/src/com/android/tv/data/epg/EpgFetcher.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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,720 +16,44 @@
package com.android.tv.data.epg;
-import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
-import android.content.ComponentName;
-import android.content.Context;
-import android.media.tv.TvInputInfo;
-import android.net.TrafficStats;
-import android.os.AsyncTask;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.support.annotation.AnyThread;
import android.support.annotation.MainThread;
-import android.support.annotation.Nullable;
-import android.support.annotation.WorkerThread;
-import android.text.TextUtils;
-import android.util.Log;
-import com.android.tv.ApplicationSingletons;
-import com.android.tv.Features;
-import com.android.tv.TvApplication;
-import com.android.tv.common.SoftPreconditions;
-import com.android.tv.common.TvCommonUtils;
-import com.android.tv.config.RemoteConfigUtils;
-import com.android.tv.data.Channel;
-import com.android.tv.data.ChannelDataManager;
-import com.android.tv.data.ChannelLogoFetcher;
-import com.android.tv.data.Lineup;
-import com.android.tv.data.Program;
-import com.android.tv.perf.EventNames;
-import com.android.tv.perf.PerformanceMonitor;
-import com.android.tv.perf.TimerEvent;
-import com.android.tv.tuner.util.PostalCodeUtils;
-import com.android.tv.util.LocationUtils;
-import com.android.tv.util.NetworkTrafficTags;
-import com.android.tv.util.Utils;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-/**
- * The service class to fetch EPG routinely or on-demand during channel scanning
- *
- * <p>Since the default executor of {@link AsyncTask} is {@link AsyncTask#SERIAL_EXECUTOR}, only one
- * task can run at a time. Because fetching EPG takes long time, the fetching task shouldn't run on
- * the serial executor. Instead, it should run on the {@link AsyncTask#THREAD_POOL_EXECUTOR}.
- */
-public class EpgFetcher {
- private static final String TAG = "EpgFetcher";
- private static final boolean DEBUG = false;
-
- private static final int EPG_ROUTINELY_FETCHING_JOB_ID = 101;
-
- private static final long INITIAL_BACKOFF_MS = TimeUnit.SECONDS.toMillis(10);
-
- private static final int REASON_EPG_READER_NOT_READY = 1;
- private static final int REASON_LOCATION_INFO_UNAVAILABLE = 2;
- private static final int REASON_LOCATION_PERMISSION_NOT_GRANTED = 3;
- private static final int REASON_NO_EPG_DATA_RETURNED = 4;
- private static final int REASON_NO_NEW_EPG = 5;
-
- private static final long FETCH_DURING_SCAN_WAIT_TIME_MS = TimeUnit.SECONDS.toMillis(10);
-
- private static final long FETCH_DURING_SCAN_DURATION_SEC = TimeUnit.HOURS.toSeconds(3);
- private static final long FAST_FETCH_DURATION_SEC = TimeUnit.DAYS.toSeconds(2);
-
- private static final int DEFAULT_ROUTINE_INTERVAL_HOUR = 4;
- private static final String KEY_ROUTINE_INTERVAL = "live_channels_epg_fetcher_interval_hour";
-
- private static final int MSG_PREPARE_FETCH_DURING_SCAN = 1;
- private static final int MSG_CHANNEL_UPDATED_DURING_SCAN = 2;
- private static final int MSG_FINISH_FETCH_DURING_SCAN = 3;
- private static final int MSG_RETRY_PREPARE_FETCH_DURING_SCAN = 4;
-
- private static final int QUERY_CHANNEL_COUNT = 50;
- private static final int MINIMUM_CHANNELS_TO_DECIDE_LINEUP = 3;
-
- private static EpgFetcher sInstance;
-
- private final Context mContext;
- private final ChannelDataManager mChannelDataManager;
- private final EpgReader mEpgReader;
- private final PerformanceMonitor mPerformanceMonitor;
- private FetchAsyncTask mFetchTask;
- private FetchDuringScanHandler mFetchDuringScanHandler;
- private long mEpgTimeStamp;
- private List<Lineup> mPossibleLineups;
- private final Object mPossibleLineupsLock = new Object();
- private final Object mFetchDuringScanHandlerLock = new Object();
- // A flag to block the re-entrance of onChannelScanStarted and onChannelScanFinished.
- private boolean mScanStarted;
-
- private final long mRoutineIntervalMs;
- private final long mEpgDataExpiredTimeLimitMs;
- private final long mFastFetchDurationSec;
-
- public static EpgFetcher getInstance(Context context) {
- if (sInstance == null) {
- sInstance = new EpgFetcher(context);
- }
- return sInstance;
- }
-
- /** Creates and returns {@link EpgReader}. */
- public static EpgReader createEpgReader(Context context, String region) {
- return new StubEpgReader(context);
- }
-
- private EpgFetcher(Context context) {
- mContext = context.getApplicationContext();
- ApplicationSingletons applicationSingletons = TvApplication.getSingletons(mContext);
- mChannelDataManager = applicationSingletons.getChannelDataManager();
- mPerformanceMonitor = applicationSingletons.getPerformanceMonitor();
- mEpgReader = createEpgReader(mContext, LocationUtils.getCurrentCountry(mContext));
-
- int remoteInteval =
- (int) RemoteConfigUtils.getRemoteConfig(
- context, KEY_ROUTINE_INTERVAL, DEFAULT_ROUTINE_INTERVAL_HOUR);
- mRoutineIntervalMs =
- remoteInteval < 0
- ? TimeUnit.HOURS.toMillis(DEFAULT_ROUTINE_INTERVAL_HOUR)
- : TimeUnit.HOURS.toMillis(remoteInteval);
- mEpgDataExpiredTimeLimitMs = mRoutineIntervalMs * 2;
- mFastFetchDurationSec = FAST_FETCH_DURATION_SEC + mRoutineIntervalMs / 1000;
- }
+/** Fetch EPG routinely or on-demand during channel scanning */
+public interface EpgFetcher {
/**
* Starts the routine service of EPG fetching. It use {@link JobScheduler} to schedule the EPG
- * fetching routine. The EPG fetching routine will be started roughly every 4 hours, unless
- * the channel scanning of tuner input is started.
- */
- @MainThread
- public void startRoutineService() {
- JobScheduler jobScheduler =
- (JobScheduler) mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE);
- for (JobInfo job : jobScheduler.getAllPendingJobs()) {
- if (job.getId() == EPG_ROUTINELY_FETCHING_JOB_ID) {
- return;
- }
- }
- JobInfo job =
- new JobInfo.Builder(
- EPG_ROUTINELY_FETCHING_JOB_ID,
- new ComponentName(mContext, EpgFetchService.class))
- .setPeriodic(mRoutineIntervalMs)
- .setBackoffCriteria(INITIAL_BACKOFF_MS, JobInfo.BACKOFF_POLICY_EXPONENTIAL)
- .setPersisted(true)
- .build();
- jobScheduler.schedule(job);
- Log.i(TAG, "EPG fetching routine service started.");
- }
-
- /**
- * Fetches EPG immediately if current EPG data are out-dated, i.e., not successfully updated
- * by routine fetching service due to various reasons.
+ * fetching routine. The EPG fetching routine will be started roughly every 4 hours, unless the
+ * channel scanning of tuner input is started.
*/
@MainThread
- public void fetchImmediatelyIfNeeded() {
- if (TvCommonUtils.isRunningInTest()) {
- // Do not run EpgFetcher in test.
- return;
- }
- new AsyncTask<Void, Void, Long>() {
- @Override
- protected Long doInBackground(Void... args) {
- return EpgFetchHelper.getLastEpgUpdatedTimestamp(mContext);
- }
-
- @Override
- protected void onPostExecute(Long result) {
- if (System.currentTimeMillis() - EpgFetchHelper.getLastEpgUpdatedTimestamp(mContext)
- > mEpgDataExpiredTimeLimitMs) {
- Log.i(TAG, "EPG data expired. Start fetching immediately.");
- fetchImmediately();
- }
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
-
- /**
- * Fetches EPG immediately.
- */
- @MainThread
- public void fetchImmediately() {
- if (!mChannelDataManager.isDbLoadFinished()) {
- mChannelDataManager.addListener(new ChannelDataManager.Listener() {
- @Override
- public void onLoadFinished() {
- mChannelDataManager.removeListener(this);
- executeFetchTaskIfPossible(null, null);
- }
-
- @Override
- public void onChannelListUpdated() { }
-
- @Override
- public void onChannelBrowsableChanged() { }
- });
- } else {
- executeFetchTaskIfPossible(null, null);
- }
- }
+ void startRoutineService();
/**
- * Notifies EPG fetch service that channel scanning is started.
+ * Fetches EPG immediately if current EPG data are out-dated, i.e., not successfully updated by
+ * routine fetching service due to various reasons.
*/
@MainThread
- public void onChannelScanStarted() {
- if (mScanStarted || !Features.ENABLE_CLOUD_EPG_REGION.isEnabled(mContext)) {
- return;
- }
- mScanStarted = true;
- stopFetchingJob();
- synchronized (mFetchDuringScanHandlerLock) {
- if (mFetchDuringScanHandler == null) {
- HandlerThread thread = new HandlerThread("EpgFetchDuringScan");
- thread.start();
- mFetchDuringScanHandler = new FetchDuringScanHandler(thread.getLooper());
- }
- mFetchDuringScanHandler.sendEmptyMessage(MSG_PREPARE_FETCH_DURING_SCAN);
- }
- Log.i(TAG, "EPG fetching on channel scanning started.");
- }
+ void fetchImmediatelyIfNeeded();
- /**
- * Notifies EPG fetch service that channel scanning is finished.
- */
+ /** Fetches EPG immediately. */
@MainThread
- public void onChannelScanFinished() {
- if (!mScanStarted) {
- return;
- }
- mScanStarted = false;
- mFetchDuringScanHandler.sendEmptyMessage(MSG_FINISH_FETCH_DURING_SCAN);
- }
+ void fetchImmediately();
+ /** Notifies EPG fetch service that channel scanning is started. */
@MainThread
- private void stopFetchingJob() {
- if (DEBUG) Log.d(TAG, "Try to stop routinely fetching job...");
- if (mFetchTask != null) {
- mFetchTask.cancel(true);
- mFetchTask = null;
- Log.i(TAG, "EPG routinely fetching job stopped.");
- }
- }
+ void onChannelScanStarted();
+ /** Notifies EPG fetch service that channel scanning is finished. */
@MainThread
- private boolean executeFetchTaskIfPossible(JobService service, JobParameters params) {
- SoftPreconditions.checkState(mChannelDataManager.isDbLoadFinished());
- if (!TvCommonUtils.isRunningInTest() && checkFetchPrerequisite()) {
- mFetchTask = new FetchAsyncTask(service, params);
- mFetchTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- return true;
- }
- return false;
- }
+ void onChannelScanFinished();
@MainThread
- private boolean checkFetchPrerequisite() {
- if (DEBUG) Log.d(TAG, "Check prerequisite of routinely fetching job.");
- if (!Features.ENABLE_CLOUD_EPG_REGION.isEnabled(mContext)) {
- Log.i(TAG, "Cannot start routine service: country not supported: "
- + LocationUtils.getCurrentCountry(mContext));
- return false;
- }
- if (mFetchTask != null) {
- // Fetching job is already running or ready to run, no need to start again.
- return false;
- }
- if (mFetchDuringScanHandler != null) {
- if (DEBUG) Log.d(TAG, "Cannot start routine service: scanning channels.");
- return false;
- }
- if (getTunerChannelCount() == 0) {
- if (DEBUG) Log.d(TAG, "Cannot start routine service: no internal tuner channels.");
- return false;
- }
- if (!TextUtils.isEmpty(EpgFetchHelper.getLastLineupId(mContext))) {
- return true;
- }
- if (!TextUtils.isEmpty(PostalCodeUtils.getLastPostalCode(mContext))) {
- return true;
- }
- return true;
- }
+ boolean executeFetchTaskIfPossible(JobService jobService, JobParameters params);
@MainThread
- private int getTunerChannelCount() {
- for (TvInputInfo input : TvApplication.getSingletons(mContext)
- .getTvInputManagerHelper().getTvInputInfos(true, true)) {
- String inputId = input.getId();
- if (Utils.isInternalTvInput(mContext, inputId)) {
- return mChannelDataManager.getChannelCountForInput(inputId);
- }
- }
- return 0;
- }
-
- @AnyThread
- private void clearUnusedLineups(@Nullable String lineupId) {
- synchronized (mPossibleLineupsLock) {
- if (mPossibleLineups == null) {
- return;
- }
- for (Lineup lineup : mPossibleLineups) {
- if (!TextUtils.equals(lineupId, lineup.id)) {
- mEpgReader.clearCachedChannels(lineup.id);
- }
- }
- mPossibleLineups = null;
- }
- }
-
- @WorkerThread
- private Integer prepareFetchEpg(boolean forceUpdatePossibleLineups) {
- if (!mEpgReader.isAvailable()) {
- Log.i(TAG, "EPG reader is temporarily unavailable.");
- return REASON_EPG_READER_NOT_READY;
- }
- // Checks the EPG Timestamp.
- mEpgTimeStamp = mEpgReader.getEpgTimestamp();
- if (mEpgTimeStamp <= EpgFetchHelper.getLastEpgUpdatedTimestamp(mContext)) {
- if (DEBUG) Log.d(TAG, "No new EPG.");
- return REASON_NO_NEW_EPG;
- }
- // Updates postal code.
- boolean postalCodeChanged = false;
- try {
- postalCodeChanged = PostalCodeUtils.updatePostalCode(mContext);
- } catch (IOException e) {
- if (DEBUG) Log.d(TAG, "Couldn't get the current location.", e);
- if (TextUtils.isEmpty(PostalCodeUtils.getLastPostalCode(mContext))) {
- return REASON_LOCATION_INFO_UNAVAILABLE;
- }
- } catch (SecurityException e) {
- Log.w(TAG, "No permission to get the current location.");
- if (TextUtils.isEmpty(PostalCodeUtils.getLastPostalCode(mContext))) {
- return REASON_LOCATION_PERMISSION_NOT_GRANTED;
- }
- } catch (PostalCodeUtils.NoPostalCodeException e) {
- Log.i(TAG, "Cannot get address or postal code.");
- return REASON_LOCATION_INFO_UNAVAILABLE;
- }
- // Updates possible lineups if necessary.
- SoftPreconditions.checkState(mPossibleLineups == null, TAG, "Possible lineups not reset.");
- if (postalCodeChanged || forceUpdatePossibleLineups
- || EpgFetchHelper.getLastLineupId(mContext) == null) {
- // To prevent main thread being blocked, though theoretically it should not happen.
- List<Lineup> possibleLineups =
- mEpgReader.getLineups(PostalCodeUtils.getLastPostalCode(mContext));
- if (possibleLineups.isEmpty()) {
- return REASON_NO_EPG_DATA_RETURNED;
- }
- for (Lineup lineup : possibleLineups) {
- mEpgReader.preloadChannels(lineup.id);
- }
- synchronized (mPossibleLineupsLock) {
- mPossibleLineups = possibleLineups;
- }
- EpgFetchHelper.setLastLineupId(mContext, null);
- }
- return null;
- }
-
- @WorkerThread
- private void batchFetchEpg(List<Channel> channels, long durationSec) {
- Log.i(TAG, "Start batch fetching (" + durationSec + ")...." + channels.size());
- if (channels.size() == 0) {
- return;
- }
- List<Long> queryChannelIds = new ArrayList<>(QUERY_CHANNEL_COUNT);
- for (Channel channel : channels) {
- queryChannelIds.add(channel.getId());
- if (queryChannelIds.size() >= QUERY_CHANNEL_COUNT) {
- batchUpdateEpg(mEpgReader.getPrograms(queryChannelIds, durationSec));
- queryChannelIds.clear();
- }
- }
- if (!queryChannelIds.isEmpty()) {
- batchUpdateEpg(mEpgReader.getPrograms(queryChannelIds, durationSec));
- }
- }
-
- @WorkerThread
- private void batchUpdateEpg(Map<Long, List<Program>> allPrograms) {
- for (Map.Entry<Long, List<Program>> entry : allPrograms.entrySet()) {
- List<Program> programs = entry.getValue();
- if (programs == null) {
- continue;
- }
- Collections.sort(programs);
- Log.i(TAG, "Batch fetched " + programs.size() + " programs for channel "
- + entry.getKey());
- EpgFetchHelper.updateEpgData(mContext, entry.getKey(), programs);
- }
- }
-
- @Nullable
- @WorkerThread
- private String pickBestLineupId(List<Channel> currentChannelList) {
- String maxLineupId = null;
- synchronized (mPossibleLineupsLock) {
- if (mPossibleLineups == null) {
- return null;
- }
- int maxCount = 0;
- for (Lineup lineup : mPossibleLineups) {
- int count = getMatchedChannelCount(lineup.id, currentChannelList);
- Log.i(TAG, lineup.name + " (" + lineup.id + ") - " + count + " matches");
- if (count > maxCount) {
- maxCount = count;
- maxLineupId = lineup.id;
- }
- }
- }
- return maxLineupId;
- }
-
- @WorkerThread
- private int getMatchedChannelCount(String lineupId, List<Channel> currentChannelList) {
- // Construct a list of display numbers for existing channels.
- if (currentChannelList.isEmpty()) {
- if (DEBUG) Log.d(TAG, "No existing channel to compare");
- return 0;
- }
- List<String> numbers = new ArrayList<>(currentChannelList.size());
- for (Channel channel : currentChannelList) {
- // We only support channels from internal tuner inputs.
- if (Utils.isInternalTvInput(mContext, channel.getInputId())) {
- numbers.add(channel.getDisplayNumber());
- }
- }
- numbers.retainAll(mEpgReader.getChannelNumbers(lineupId));
- return numbers.size();
- }
-
- public static class EpgFetchService extends JobService {
- private EpgFetcher mEpgFetcher;
-
- @Override
- public void onCreate() {
- super.onCreate();
- TvApplication.setCurrentRunningProcess(this, true);
- mEpgFetcher = EpgFetcher.getInstance(this);
- }
-
- @Override
- public boolean onStartJob(JobParameters params) {
- if (!mEpgFetcher.mChannelDataManager.isDbLoadFinished()) {
- mEpgFetcher.mChannelDataManager.addListener(new ChannelDataManager.Listener() {
- @Override
- public void onLoadFinished() {
- mEpgFetcher.mChannelDataManager.removeListener(this);
- if (!mEpgFetcher.executeFetchTaskIfPossible(EpgFetchService.this, params)) {
- jobFinished(params, false);
- }
- }
-
- @Override
- public void onChannelListUpdated() { }
-
- @Override
- public void onChannelBrowsableChanged() { }
- });
- return true;
- } else {
- return mEpgFetcher.executeFetchTaskIfPossible(this, params);
- }
- }
-
- @Override
- public boolean onStopJob(JobParameters params) {
- mEpgFetcher.stopFetchingJob();
- return false;
- }
- }
-
- private class FetchAsyncTask extends AsyncTask<Void, Void, Integer> {
- private final JobService mService;
- private final JobParameters mParams;
- private List<Channel> mCurrentChannelList;
- private TimerEvent mTimerEvent;
-
- private FetchAsyncTask(JobService service, JobParameters params) {
- mService = service;
- mParams = params;
- }
-
- @Override
- protected void onPreExecute() {
- mTimerEvent = mPerformanceMonitor.startTimer();
- mCurrentChannelList = mChannelDataManager.getChannelList();
- }
-
- @Override
- protected Integer doInBackground(Void... args) {
- final int oldTag = TrafficStats.getThreadStatsTag();
- TrafficStats.setThreadStatsTag(NetworkTrafficTags.EPG_FETCH);
- try {
- if (DEBUG) Log.d(TAG, "Start EPG routinely fetching.");
- Integer failureReason = prepareFetchEpg(false);
- // InterruptedException might be caught by RPC, we should check it here.
- if (failureReason != null || this.isCancelled()) {
- return failureReason;
- }
- String lineupId = EpgFetchHelper.getLastLineupId(mContext);
- lineupId = lineupId == null ? pickBestLineupId(mCurrentChannelList) : lineupId;
- if (lineupId != null) {
- Log.i(TAG, "Selecting the lineup " + lineupId);
- // During normal fetching process, the lineup ID should be confirmed since all
- // channels are known, clear up possible lineups to save resources.
- EpgFetchHelper.setLastLineupId(mContext, lineupId);
- clearUnusedLineups(lineupId);
- } else {
- Log.i(TAG, "Failed to get lineup id");
- return REASON_NO_EPG_DATA_RETURNED;
- }
- final List<Channel> channels = mEpgReader.getChannels(lineupId);
- // InterruptedException might be caught by RPC, we should check it here.
- if (this.isCancelled()) {
- return null;
- }
- if (channels.isEmpty()) {
- Log.i(TAG, "Failed to get EPG channels.");
- return REASON_NO_EPG_DATA_RETURNED;
- }
- if (System.currentTimeMillis() - EpgFetchHelper.getLastEpgUpdatedTimestamp(mContext)
- > mEpgDataExpiredTimeLimitMs) {
- batchFetchEpg(channels, mFastFetchDurationSec);
- }
- new Handler(mContext.getMainLooper())
- .post(
- new Runnable() {
- @Override
- public void run() {
- ChannelLogoFetcher.startFetchingChannelLogos(
- mContext, channels);
- }
- });
- for (Channel channel : channels) {
- if (this.isCancelled()) {
- return null;
- }
- long channelId = channel.getId();
- List<Program> programs = new ArrayList<>(mEpgReader.getPrograms(channelId));
- // InterruptedException might be caught by RPC, we should check it here.
- Collections.sort(programs);
- Log.i(TAG, "Fetched " + programs.size() + " programs for channel " + channelId);
- EpgFetchHelper.updateEpgData(mContext, channelId, programs);
- }
- EpgFetchHelper.setLastEpgUpdatedTimestamp(mContext, mEpgTimeStamp);
- if (DEBUG) Log.d(TAG, "Fetching EPG is finished.");
- return null;
- } finally {
- TrafficStats.setThreadStatsTag(oldTag);
- }
- }
-
- @Override
- protected void onPostExecute(Integer failureReason) {
- mFetchTask = null;
- if (failureReason == null || failureReason == REASON_LOCATION_PERMISSION_NOT_GRANTED
- || failureReason == REASON_NO_NEW_EPG) {
- jobFinished(false);
- } else {
- // Applies back-off policy
- jobFinished(true);
- }
- mPerformanceMonitor.stopTimer(mTimerEvent, EventNames.FETCH_EPG_TASK);
- mPerformanceMonitor.recordMemory(EventNames.FETCH_EPG_TASK);
- }
-
- @Override
- protected void onCancelled(Integer failureReason) {
- clearUnusedLineups(null);
- jobFinished(false);
- }
-
- private void jobFinished(boolean reschedule) {
- if (mService != null && mParams != null) {
- // Task is executed from JobService, need to report jobFinished.
- mService.jobFinished(mParams, reschedule);
- }
- }
- }
-
- @WorkerThread
- private class FetchDuringScanHandler extends Handler {
- private final Set<Long> mFetchedChannelIdsDuringScan = new HashSet<>();
- private String mPossibleLineupId;
-
- private final ChannelDataManager.Listener mDuringScanChannelListener =
- new ChannelDataManager.Listener() {
- @Override
- public void onLoadFinished() {
- if (DEBUG) Log.d(TAG, "ChannelDataManager.onLoadFinished()");
- if (getTunerChannelCount() >= MINIMUM_CHANNELS_TO_DECIDE_LINEUP
- && !hasMessages(MSG_CHANNEL_UPDATED_DURING_SCAN)) {
- Message.obtain(FetchDuringScanHandler.this,
- MSG_CHANNEL_UPDATED_DURING_SCAN, new ArrayList<>(
- mChannelDataManager.getChannelList())).sendToTarget();
- }
- }
-
- @Override
- public void onChannelListUpdated() {
- if (DEBUG) Log.d(TAG, "ChannelDataManager.onChannelListUpdated()");
- if (getTunerChannelCount() >= MINIMUM_CHANNELS_TO_DECIDE_LINEUP
- && !hasMessages(MSG_CHANNEL_UPDATED_DURING_SCAN)) {
- Message.obtain(FetchDuringScanHandler.this,
- MSG_CHANNEL_UPDATED_DURING_SCAN,
- mChannelDataManager.getChannelList()).sendToTarget();
- }
- }
-
- @Override
- public void onChannelBrowsableChanged() {
- // Do nothing
- }
- };
-
- @AnyThread
- private FetchDuringScanHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_PREPARE_FETCH_DURING_SCAN:
- case MSG_RETRY_PREPARE_FETCH_DURING_SCAN:
- onPrepareFetchDuringScan();
- break;
- case MSG_CHANNEL_UPDATED_DURING_SCAN:
- if (!hasMessages(MSG_CHANNEL_UPDATED_DURING_SCAN)) {
- onChannelUpdatedDuringScan((List<Channel>) msg.obj);
- }
- break;
- case MSG_FINISH_FETCH_DURING_SCAN:
- removeMessages(MSG_RETRY_PREPARE_FETCH_DURING_SCAN);
- if (hasMessages(MSG_CHANNEL_UPDATED_DURING_SCAN)) {
- sendEmptyMessage(MSG_FINISH_FETCH_DURING_SCAN);
- } else {
- onFinishFetchDuringScan();
- }
- break;
- }
- }
-
- private void onPrepareFetchDuringScan() {
- Integer failureReason = prepareFetchEpg(true);
- if (failureReason != null) {
- sendEmptyMessageDelayed(
- MSG_RETRY_PREPARE_FETCH_DURING_SCAN, FETCH_DURING_SCAN_WAIT_TIME_MS);
- return;
- }
- mChannelDataManager.addListener(mDuringScanChannelListener);
- }
-
- private void onChannelUpdatedDuringScan(List<Channel> currentChannelList) {
- String lineupId = pickBestLineupId(currentChannelList);
- Log.i(TAG, "Fast fetch channels for lineup ID: " + lineupId);
- if (TextUtils.isEmpty(lineupId)) {
- if (TextUtils.isEmpty(mPossibleLineupId)) {
- return;
- }
- } else if (!TextUtils.equals(lineupId, mPossibleLineupId)) {
- mFetchedChannelIdsDuringScan.clear();
- mPossibleLineupId = lineupId;
- }
- List<Long> currentChannelIds = new ArrayList<>();
- for (Channel channel : currentChannelList) {
- currentChannelIds.add(channel.getId());
- }
- mFetchedChannelIdsDuringScan.retainAll(currentChannelIds);
- List<Channel> newChannels = new ArrayList<>();
- for (Channel channel : mEpgReader.getChannels(mPossibleLineupId)) {
- if (!mFetchedChannelIdsDuringScan.contains(channel.getId())) {
- newChannels.add(channel);
- mFetchedChannelIdsDuringScan.add(channel.getId());
- }
- }
- batchFetchEpg(newChannels, FETCH_DURING_SCAN_DURATION_SEC);
- }
-
- private void onFinishFetchDuringScan() {
- mChannelDataManager.removeListener(mDuringScanChannelListener);
- EpgFetchHelper.setLastLineupId(mContext, mPossibleLineupId);
- clearUnusedLineups(null);
- mFetchedChannelIdsDuringScan.clear();
- synchronized (mFetchDuringScanHandlerLock) {
- if (!hasMessages(MSG_PREPARE_FETCH_DURING_SCAN)) {
- removeCallbacksAndMessages(null);
- getLooper().quit();
- mFetchDuringScanHandler = null;
- }
- }
- // Clear timestamp to make routine service start right away.
- EpgFetchHelper.setLastEpgUpdatedTimestamp(mContext, 0);
- Log.i(TAG, "EPG Fetching during channel scanning finished.");
- new Handler(Looper.getMainLooper()).post(new Runnable() {
- @Override
- public void run() {
- fetchImmediately();
- }
- });
- }
- }
+ void stopFetchingJob();
}
diff --git a/src/com/android/tv/data/epg/EpgFetcherImpl.java b/src/com/android/tv/data/epg/EpgFetcherImpl.java
new file mode 100644
index 00000000..2aaaa5b2
--- /dev/null
+++ b/src/com/android/tv/data/epg/EpgFetcherImpl.java
@@ -0,0 +1,811 @@
+/*
+ * 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.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.database.Cursor;
+import android.media.tv.TvContract;
+import android.media.tv.TvInputInfo;
+import android.net.TrafficStats;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.support.annotation.AnyThread;
+import android.support.annotation.MainThread;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.support.annotation.WorkerThread;
+import android.text.TextUtils;
+import android.util.Log;
+import com.android.tv.TvFeatures;
+import com.android.tv.TvSingletons;
+import com.android.tv.common.BuildConfig;
+import com.android.tv.common.SoftPreconditions;
+import com.android.tv.common.config.api.RemoteConfigValue;
+import com.android.tv.common.util.Clock;
+import com.android.tv.common.util.CommonUtils;
+import com.android.tv.common.util.LocationUtils;
+import com.android.tv.common.util.NetworkTrafficTags;
+import com.android.tv.common.util.PermissionUtils;
+import com.android.tv.common.util.PostalCodeUtils;
+import com.android.tv.data.ChannelDataManager;
+import com.android.tv.data.ChannelImpl;
+import com.android.tv.data.ChannelLogoFetcher;
+import com.android.tv.data.Lineup;
+import com.android.tv.data.Program;
+import com.android.tv.data.api.Channel;
+import com.android.tv.perf.EventNames;
+import com.android.tv.perf.PerformanceMonitor;
+import com.android.tv.perf.TimerEvent;
+import com.android.tv.util.Utils;
+import com.google.android.tv.partner.support.EpgInput;
+import com.google.android.tv.partner.support.EpgInputs;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The service class to fetch EPG routinely or on-demand during channel scanning
+ *
+ * <p>Since the default executor of {@link AsyncTask} is {@link AsyncTask#SERIAL_EXECUTOR}, only one
+ * task can run at a time. Because fetching EPG takes long time, the fetching task shouldn't run on
+ * the serial executor. Instead, it should run on the {@link AsyncTask#THREAD_POOL_EXECUTOR}.
+ */
+public class EpgFetcherImpl implements EpgFetcher {
+ private static final String TAG = "EpgFetcherImpl";
+ private static final boolean DEBUG = false;
+
+ private static final int EPG_ROUTINELY_FETCHING_JOB_ID = 101;
+
+ private static final long INITIAL_BACKOFF_MS = TimeUnit.SECONDS.toMillis(10);
+
+ @VisibleForTesting static final int REASON_EPG_READER_NOT_READY = 1;
+ @VisibleForTesting static final int REASON_LOCATION_INFO_UNAVAILABLE = 2;
+ @VisibleForTesting static final int REASON_LOCATION_PERMISSION_NOT_GRANTED = 3;
+ @VisibleForTesting static final int REASON_NO_EPG_DATA_RETURNED = 4;
+ @VisibleForTesting static final int REASON_NO_NEW_EPG = 5;
+ @VisibleForTesting static final int REASON_ERROR = 6;
+ @VisibleForTesting static final int REASON_CLOUD_EPG_FAILURE = 7;
+ @VisibleForTesting static final int REASON_NO_BUILT_IN_CHANNELS = 8;
+
+ private static final long FETCH_DURING_SCAN_WAIT_TIME_MS = TimeUnit.SECONDS.toMillis(10);
+
+ private static final long FETCH_DURING_SCAN_DURATION_SEC = TimeUnit.HOURS.toSeconds(3);
+ private static final long FAST_FETCH_DURATION_SEC = TimeUnit.DAYS.toSeconds(2);
+
+ private static final RemoteConfigValue<Long> ROUTINE_INTERVAL_HOUR =
+ RemoteConfigValue.create("live_channels_epg_fetcher_interval_hour", 4);
+
+ private static final int MSG_PREPARE_FETCH_DURING_SCAN = 1;
+ private static final int MSG_CHANNEL_UPDATED_DURING_SCAN = 2;
+ private static final int MSG_FINISH_FETCH_DURING_SCAN = 3;
+ private static final int MSG_RETRY_PREPARE_FETCH_DURING_SCAN = 4;
+
+ private static final int QUERY_CHANNEL_COUNT = 50;
+ private static final int MINIMUM_CHANNELS_TO_DECIDE_LINEUP = 3;
+
+ private final Context mContext;
+ private final ChannelDataManager mChannelDataManager;
+ private final EpgReader mEpgReader;
+ private final PerformanceMonitor mPerformanceMonitor;
+ private FetchAsyncTask mFetchTask;
+ private FetchDuringScanHandler mFetchDuringScanHandler;
+ private long mEpgTimeStamp;
+ private List<Lineup> mPossibleLineups;
+ private final Object mPossibleLineupsLock = new Object();
+ private final Object mFetchDuringScanHandlerLock = new Object();
+ // A flag to block the re-entrance of onChannelScanStarted and onChannelScanFinished.
+ private boolean mScanStarted;
+
+ private final long mRoutineIntervalMs;
+ private final long mEpgDataExpiredTimeLimitMs;
+ private final long mFastFetchDurationSec;
+ private Clock mClock;
+
+ public static EpgFetcher create(Context context) {
+ context = context.getApplicationContext();
+ TvSingletons tvSingletons = TvSingletons.getSingletons(context);
+ ChannelDataManager channelDataManager = tvSingletons.getChannelDataManager();
+ PerformanceMonitor performanceMonitor = tvSingletons.getPerformanceMonitor();
+ EpgReader epgReader = tvSingletons.providesEpgReader().get();
+ Clock clock = tvSingletons.getClock();
+ long routineIntervalMs = ROUTINE_INTERVAL_HOUR.get(tvSingletons.getRemoteConfig());
+
+ return new EpgFetcherImpl(
+ context,
+ channelDataManager,
+ epgReader,
+ performanceMonitor,
+ clock,
+ routineIntervalMs);
+ }
+
+ @VisibleForTesting
+ EpgFetcherImpl(
+ Context context,
+ ChannelDataManager channelDataManager,
+ EpgReader epgReader,
+ PerformanceMonitor performanceMonitor,
+ Clock clock,
+ long routineIntervalMs) {
+ mContext = context;
+ mChannelDataManager = channelDataManager;
+ mEpgReader = epgReader;
+ mPerformanceMonitor = performanceMonitor;
+ mClock = clock;
+ mRoutineIntervalMs =
+ routineIntervalMs <= 0
+ ? TimeUnit.HOURS.toMillis(ROUTINE_INTERVAL_HOUR.getDefaultValue())
+ : TimeUnit.HOURS.toMillis(routineIntervalMs);
+ mEpgDataExpiredTimeLimitMs = routineIntervalMs * 2;
+ mFastFetchDurationSec = FAST_FETCH_DURATION_SEC + routineIntervalMs / 1000;
+ }
+
+ private static Set<Channel> getExistingChannelsForMyPackage(Context context) {
+ HashSet<Channel> channels = new HashSet<>();
+ String selection = null;
+ String[] selectionArgs = null;
+ String myPackageName = context.getPackageName();
+ if (PermissionUtils.hasAccessAllEpg(context)) {
+ selection = "package_name=?";
+ selectionArgs = new String[] {myPackageName};
+ }
+ try (Cursor c =
+ context.getContentResolver()
+ .query(
+ TvContract.Channels.CONTENT_URI,
+ ChannelImpl.PROJECTION,
+ selection,
+ selectionArgs,
+ null)) {
+ if (c != null) {
+ while (c.moveToNext()) {
+ Channel channel = ChannelImpl.fromCursor(c);
+ if (DEBUG) Log.d(TAG, "Found " + channel);
+ if (myPackageName.equals(channel.getPackageName())) {
+ channels.add(channel);
+ }
+ }
+ }
+ }
+ if (DEBUG)
+ Log.d(TAG, "Found " + channels.size() + " channels for package " + myPackageName);
+ return channels;
+ }
+
+ @Override
+ @MainThread
+ public void startRoutineService() {
+ JobScheduler jobScheduler =
+ (JobScheduler) mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+ for (JobInfo job : jobScheduler.getAllPendingJobs()) {
+ if (job.getId() == EPG_ROUTINELY_FETCHING_JOB_ID) {
+ return;
+ }
+ }
+ JobInfo job =
+ new JobInfo.Builder(
+ EPG_ROUTINELY_FETCHING_JOB_ID,
+ new ComponentName(mContext, EpgFetchService.class))
+ .setPeriodic(mRoutineIntervalMs)
+ .setBackoffCriteria(INITIAL_BACKOFF_MS, JobInfo.BACKOFF_POLICY_EXPONENTIAL)
+ .setPersisted(true)
+ .build();
+ jobScheduler.schedule(job);
+ Log.i(TAG, "EPG fetching routine service started.");
+ }
+
+ @Override
+ @MainThread
+ public void fetchImmediatelyIfNeeded() {
+ if (CommonUtils.isRunningInTest()) {
+ // Do not run EpgFetcher in test.
+ return;
+ }
+ new AsyncTask<Void, Void, Long>() {
+ @Override
+ protected Long doInBackground(Void... args) {
+ return EpgFetchHelper.getLastEpgUpdatedTimestamp(mContext);
+ }
+
+ @Override
+ protected void onPostExecute(Long result) {
+ if (mClock.currentTimeMillis() - EpgFetchHelper.getLastEpgUpdatedTimestamp(mContext)
+ > mEpgDataExpiredTimeLimitMs) {
+ Log.i(TAG, "EPG data expired. Start fetching immediately.");
+ fetchImmediately();
+ }
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+
+ @Override
+ @MainThread
+ public void fetchImmediately() {
+ if (DEBUG) Log.d(TAG, "fetchImmediately");
+ if (!mChannelDataManager.isDbLoadFinished()) {
+ mChannelDataManager.addListener(
+ new ChannelDataManager.Listener() {
+ @Override
+ public void onLoadFinished() {
+ mChannelDataManager.removeListener(this);
+ executeFetchTaskIfPossible(null, null);
+ }
+
+ @Override
+ public void onChannelListUpdated() {}
+
+ @Override
+ public void onChannelBrowsableChanged() {}
+ });
+ } else {
+ executeFetchTaskIfPossible(null, null);
+ }
+ }
+
+ @Override
+ @MainThread
+ public void onChannelScanStarted() {
+ if (mScanStarted || !TvFeatures.ENABLE_CLOUD_EPG_REGION.isEnabled(mContext)) {
+ return;
+ }
+ mScanStarted = true;
+ stopFetchingJob();
+ synchronized (mFetchDuringScanHandlerLock) {
+ if (mFetchDuringScanHandler == null) {
+ HandlerThread thread = new HandlerThread("EpgFetchDuringScan");
+ thread.start();
+ mFetchDuringScanHandler = new FetchDuringScanHandler(thread.getLooper());
+ }
+ mFetchDuringScanHandler.sendEmptyMessage(MSG_PREPARE_FETCH_DURING_SCAN);
+ }
+ Log.i(TAG, "EPG fetching on channel scanning started.");
+ }
+
+ @Override
+ @MainThread
+ public void onChannelScanFinished() {
+ if (!mScanStarted) {
+ return;
+ }
+ mScanStarted = false;
+ mFetchDuringScanHandler.sendEmptyMessage(MSG_FINISH_FETCH_DURING_SCAN);
+ }
+
+ @MainThread
+ @Override
+ public void stopFetchingJob() {
+ if (DEBUG) Log.d(TAG, "Try to stop routinely fetching job...");
+ if (mFetchTask != null) {
+ mFetchTask.cancel(true);
+ mFetchTask = null;
+ Log.i(TAG, "EPG routinely fetching job stopped.");
+ }
+ }
+
+ @MainThread
+ @Override
+ public boolean executeFetchTaskIfPossible(JobService service, JobParameters params) {
+ if (DEBUG) Log.d(TAG, "executeFetchTaskIfPossible");
+ SoftPreconditions.checkState(mChannelDataManager.isDbLoadFinished());
+ if (!CommonUtils.isRunningInTest() && checkFetchPrerequisite()) {
+ mFetchTask = createFetchTask(service, params);
+ mFetchTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ return true;
+ }
+ return false;
+ }
+
+ @VisibleForTesting
+ FetchAsyncTask createFetchTask(JobService service, JobParameters params) {
+ return new FetchAsyncTask(service, params);
+ }
+
+ @MainThread
+ private boolean checkFetchPrerequisite() {
+ if (DEBUG) Log.d(TAG, "Check prerequisite of routinely fetching job.");
+ if (!TvFeatures.ENABLE_CLOUD_EPG_REGION.isEnabled(mContext)) {
+ Log.i(
+ TAG,
+ "Cannot start routine service: country not supported: "
+ + LocationUtils.getCurrentCountry(mContext));
+ return false;
+ }
+ if (mFetchTask != null) {
+ // Fetching job is already running or ready to run, no need to start again.
+ return false;
+ }
+ if (mFetchDuringScanHandler != null) {
+ if (DEBUG) Log.d(TAG, "Cannot start routine service: scanning channels.");
+ return false;
+ }
+ return true;
+ }
+
+ @MainThread
+ private int getTunerChannelCount() {
+ for (TvInputInfo input :
+ TvSingletons.getSingletons(mContext)
+ .getTvInputManagerHelper()
+ .getTvInputInfos(true, true)) {
+ String inputId = input.getId();
+ if (Utils.isInternalTvInput(mContext, inputId)) {
+ return mChannelDataManager.getChannelCountForInput(inputId);
+ }
+ }
+ return 0;
+ }
+
+ @AnyThread
+ private void clearUnusedLineups(@Nullable String lineupId) {
+ synchronized (mPossibleLineupsLock) {
+ if (mPossibleLineups == null) {
+ return;
+ }
+ for (Lineup lineup : mPossibleLineups) {
+ if (!TextUtils.equals(lineupId, lineup.getId())) {
+ mEpgReader.clearCachedChannels(lineup.getId());
+ }
+ }
+ mPossibleLineups = null;
+ }
+ }
+
+ @WorkerThread
+ private Integer prepareFetchEpg(boolean forceUpdatePossibleLineups) {
+ if (!mEpgReader.isAvailable()) {
+ Log.i(TAG, "EPG reader is temporarily unavailable.");
+ return REASON_EPG_READER_NOT_READY;
+ }
+ // Checks the EPG Timestamp.
+ mEpgTimeStamp = mEpgReader.getEpgTimestamp();
+ if (mEpgTimeStamp <= EpgFetchHelper.getLastEpgUpdatedTimestamp(mContext)) {
+ if (DEBUG) Log.d(TAG, "No new EPG.");
+ return REASON_NO_NEW_EPG;
+ }
+ // Updates postal code.
+ boolean postalCodeChanged = false;
+ try {
+ postalCodeChanged = PostalCodeUtils.updatePostalCode(mContext);
+ } catch (IOException e) {
+ if (DEBUG) Log.d(TAG, "Couldn't get the current location.", e);
+ if (TextUtils.isEmpty(PostalCodeUtils.getLastPostalCode(mContext))) {
+ return REASON_LOCATION_INFO_UNAVAILABLE;
+ }
+ } catch (SecurityException e) {
+ Log.w(TAG, "No permission to get the current location.");
+ if (TextUtils.isEmpty(PostalCodeUtils.getLastPostalCode(mContext))) {
+ return REASON_LOCATION_PERMISSION_NOT_GRANTED;
+ }
+ } catch (PostalCodeUtils.NoPostalCodeException e) {
+ Log.i(TAG, "Cannot get address or postal code.");
+ return REASON_LOCATION_INFO_UNAVAILABLE;
+ }
+ // Updates possible lineups if necessary.
+ SoftPreconditions.checkState(mPossibleLineups == null, TAG, "Possible lineups not reset.");
+ if (postalCodeChanged
+ || forceUpdatePossibleLineups
+ || EpgFetchHelper.getLastLineupId(mContext) == null) {
+ // To prevent main thread being blocked, though theoretically it should not happen.
+ String lastPostalCode = PostalCodeUtils.getLastPostalCode(mContext);
+ List<Lineup> possibleLineups = mEpgReader.getLineups(lastPostalCode);
+ if (possibleLineups.isEmpty()) {
+ Log.i(TAG, "No lineups found for " + lastPostalCode);
+ return REASON_NO_EPG_DATA_RETURNED;
+ }
+ for (Lineup lineup : possibleLineups) {
+ mEpgReader.preloadChannels(lineup.getId());
+ }
+ synchronized (mPossibleLineupsLock) {
+ mPossibleLineups = possibleLineups;
+ }
+ EpgFetchHelper.setLastLineupId(mContext, null);
+ }
+ return null;
+ }
+
+ @WorkerThread
+ private void batchFetchEpg(Set<EpgReader.EpgChannel> epgChannels, long durationSec) {
+ Log.i(TAG, "Start batch fetching (" + durationSec + ")...." + epgChannels.size());
+ if (epgChannels.size() == 0) {
+ return;
+ }
+ Set<EpgReader.EpgChannel> batch = new HashSet<>(QUERY_CHANNEL_COUNT);
+ for (EpgReader.EpgChannel epgChannel : epgChannels) {
+ batch.add(epgChannel);
+ if (batch.size() >= QUERY_CHANNEL_COUNT) {
+ batchUpdateEpg(mEpgReader.getPrograms(batch, durationSec));
+ batch.clear();
+ }
+ }
+ if (!batch.isEmpty()) {
+ batchUpdateEpg(mEpgReader.getPrograms(batch, durationSec));
+ }
+ }
+
+ @WorkerThread
+ private void batchUpdateEpg(Map<EpgReader.EpgChannel, Collection<Program>> allPrograms) {
+ for (Map.Entry<EpgReader.EpgChannel, Collection<Program>> entry : allPrograms.entrySet()) {
+ List<Program> programs = new ArrayList(entry.getValue());
+ if (programs == null) {
+ continue;
+ }
+ Collections.sort(programs);
+ Log.i(
+ TAG,
+ "Batch fetched " + programs.size() + " programs for channel " + entry.getKey());
+ EpgFetchHelper.updateEpgData(
+ mContext, mClock, entry.getKey().getChannel().getId(), programs);
+ }
+ }
+
+ @Nullable
+ @WorkerThread
+ private String pickBestLineupId(Set<Channel> currentChannels) {
+ String maxLineupId = null;
+ synchronized (mPossibleLineupsLock) {
+ if (mPossibleLineups == null) {
+ return null;
+ }
+ int maxCount = 0;
+ for (Lineup lineup : mPossibleLineups) {
+ int count = getMatchedChannelCount(lineup.getId(), currentChannels);
+ Log.i(TAG, lineup.getName() + " (" + lineup.getId() + ") - " + count + " matches");
+ if (count > maxCount) {
+ maxCount = count;
+ maxLineupId = lineup.getId();
+ }
+ }
+ }
+ return maxLineupId;
+ }
+
+ @WorkerThread
+ private int getMatchedChannelCount(String lineupId, Set<Channel> currentChannels) {
+ // Construct a list of display numbers for existing channels.
+ if (currentChannels.isEmpty()) {
+ if (DEBUG) Log.d(TAG, "No existing channel to compare");
+ return 0;
+ }
+ List<String> numbers = new ArrayList<>(currentChannels.size());
+ for (Channel channel : currentChannels) {
+ // We only support channels from internal tuner inputs.
+ if (Utils.isInternalTvInput(mContext, channel.getInputId())) {
+ numbers.add(channel.getDisplayNumber());
+ }
+ }
+ numbers.retainAll(mEpgReader.getChannelNumbers(lineupId));
+ return numbers.size();
+ }
+
+ @VisibleForTesting
+ class FetchAsyncTask extends AsyncTask<Void, Void, Integer> {
+ private final JobService mService;
+ private final JobParameters mParams;
+ private Set<Channel> mCurrentChannels;
+ private TimerEvent mTimerEvent;
+
+ private FetchAsyncTask(JobService service, JobParameters params) {
+ mService = service;
+ mParams = params;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ mTimerEvent = mPerformanceMonitor.startTimer();
+ mCurrentChannels = new HashSet<>(mChannelDataManager.getChannelList());
+ }
+
+ @Override
+ protected Integer doInBackground(Void... args) {
+ final int oldTag = TrafficStats.getThreadStatsTag();
+ TrafficStats.setThreadStatsTag(NetworkTrafficTags.EPG_FETCH);
+ try {
+ if (DEBUG) Log.d(TAG, "Start EPG routinely fetching.");
+ Integer builtInResult = fetchEpgForBuiltInTuner();
+ boolean anyCloudEpgFailure = false;
+ boolean anyCloudEpgSuccess = false;
+ return builtInResult;
+ } finally {
+ TrafficStats.setThreadStatsTag(oldTag);
+ }
+ }
+
+ private Set<Channel> getExistingChannelsFor(String inputId) {
+ Set<Channel> result = new HashSet<>();
+ try (Cursor cursor =
+ mContext.getContentResolver()
+ .query(
+ TvContract.buildChannelsUriForInput(inputId),
+ ChannelImpl.PROJECTION,
+ null,
+ null,
+ null)) {
+ while (cursor.moveToNext()) {
+ result.add(ChannelImpl.fromCursor(cursor));
+ }
+ return result;
+ }
+ }
+
+ private Integer fetchEpgForBuiltInTuner() {
+ try {
+ Integer failureReason = prepareFetchEpg(false);
+ // InterruptedException might be caught by RPC, we should check it here.
+ if (failureReason != null || this.isCancelled()) {
+ return failureReason;
+ }
+ String lineupId = EpgFetchHelper.getLastLineupId(mContext);
+ lineupId = lineupId == null ? pickBestLineupId(mCurrentChannels) : lineupId;
+ if (lineupId != null) {
+ Log.i(TAG, "Selecting the lineup " + lineupId);
+ // During normal fetching process, the lineup ID should be confirmed since all
+ // channels are known, clear up possible lineups to save resources.
+ EpgFetchHelper.setLastLineupId(mContext, lineupId);
+ clearUnusedLineups(lineupId);
+ } else {
+ Log.i(TAG, "Failed to get lineup id");
+ return REASON_NO_EPG_DATA_RETURNED;
+ }
+ Set<Channel> existingChannelsForMyPackage =
+ getExistingChannelsForMyPackage(mContext);
+ if (existingChannelsForMyPackage.isEmpty()) {
+ return REASON_NO_BUILT_IN_CHANNELS;
+ }
+ return fetchEpgFor(lineupId, existingChannelsForMyPackage);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to update EPG for builtin tuner", e);
+ return REASON_ERROR;
+ }
+ }
+
+ @Nullable
+ private Integer fetchEpgFor(String lineupId, Set<Channel> existingChannels) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "Starting Fetching EPG is for "
+ + lineupId
+ + " with channelCount "
+ + existingChannels.size());
+ }
+ final Set<EpgReader.EpgChannel> channels =
+ mEpgReader.getChannels(existingChannels, lineupId);
+ // InterruptedException might be caught by RPC, we should check it here.
+ if (this.isCancelled()) {
+ return null;
+ }
+ if (channels.isEmpty()) {
+ Log.i(TAG, "Failed to get EPG channels for " + lineupId);
+ return REASON_NO_EPG_DATA_RETURNED;
+ }
+ if (mClock.currentTimeMillis() - EpgFetchHelper.getLastEpgUpdatedTimestamp(mContext)
+ > mEpgDataExpiredTimeLimitMs) {
+ batchFetchEpg(channels, mFastFetchDurationSec);
+ }
+ new Handler(mContext.getMainLooper())
+ .post(
+ new Runnable() {
+ @Override
+ public void run() {
+ ChannelLogoFetcher.startFetchingChannelLogos(
+ mContext, asChannelList(channels));
+ }
+ });
+ for (EpgReader.EpgChannel epgChannel : channels) {
+ if (this.isCancelled()) {
+ return null;
+ }
+ List<Program> programs = new ArrayList<>(mEpgReader.getPrograms(epgChannel));
+ // InterruptedException might be caught by RPC, we should check it here.
+ Collections.sort(programs);
+ Log.i(
+ TAG,
+ "Fetched "
+ + programs.size()
+ + " programs for channel "
+ + epgChannel.getChannel());
+ EpgFetchHelper.updateEpgData(
+ mContext, mClock, epgChannel.getChannel().getId(), programs);
+ }
+ EpgFetchHelper.setLastEpgUpdatedTimestamp(mContext, mEpgTimeStamp);
+ if (DEBUG) Log.d(TAG, "Fetching EPG is for " + lineupId);
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Integer failureReason) {
+ mFetchTask = null;
+ if (failureReason == null
+ || failureReason == REASON_LOCATION_PERMISSION_NOT_GRANTED
+ || failureReason == REASON_NO_NEW_EPG) {
+ jobFinished(false);
+ } else {
+ // Applies back-off policy
+ jobFinished(true);
+ }
+ mPerformanceMonitor.stopTimer(mTimerEvent, EventNames.FETCH_EPG_TASK);
+ mPerformanceMonitor.recordMemory(EventNames.FETCH_EPG_TASK);
+ }
+
+ @Override
+ protected void onCancelled(Integer failureReason) {
+ clearUnusedLineups(null);
+ jobFinished(false);
+ }
+
+ private void jobFinished(boolean reschedule) {
+ if (mService != null && mParams != null) {
+ // Task is executed from JobService, need to report jobFinished.
+ mService.jobFinished(mParams, reschedule);
+ }
+ }
+ }
+
+ private List<Channel> asChannelList(Set<EpgReader.EpgChannel> epgChannels) {
+ List<Channel> result = new ArrayList<>(epgChannels.size());
+ for (EpgReader.EpgChannel epgChannel : epgChannels) {
+ result.add(epgChannel.getChannel());
+ }
+ return result;
+ }
+
+ @WorkerThread
+ private class FetchDuringScanHandler extends Handler {
+ private final Set<Long> mFetchedChannelIdsDuringScan = new HashSet<>();
+ private String mPossibleLineupId;
+
+ private final ChannelDataManager.Listener mDuringScanChannelListener =
+ new ChannelDataManager.Listener() {
+ @Override
+ public void onLoadFinished() {
+ if (DEBUG) Log.d(TAG, "ChannelDataManager.onLoadFinished()");
+ if (getTunerChannelCount() >= MINIMUM_CHANNELS_TO_DECIDE_LINEUP
+ && !hasMessages(MSG_CHANNEL_UPDATED_DURING_SCAN)) {
+ Message.obtain(
+ FetchDuringScanHandler.this,
+ MSG_CHANNEL_UPDATED_DURING_SCAN,
+ getExistingChannelsForMyPackage(mContext))
+ .sendToTarget();
+ }
+ }
+
+ @Override
+ public void onChannelListUpdated() {
+ if (DEBUG) Log.d(TAG, "ChannelDataManager.onChannelListUpdated()");
+ if (getTunerChannelCount() >= MINIMUM_CHANNELS_TO_DECIDE_LINEUP
+ && !hasMessages(MSG_CHANNEL_UPDATED_DURING_SCAN)) {
+ Message.obtain(
+ FetchDuringScanHandler.this,
+ MSG_CHANNEL_UPDATED_DURING_SCAN,
+ getExistingChannelsForMyPackage(mContext))
+ .sendToTarget();
+ }
+ }
+
+ @Override
+ public void onChannelBrowsableChanged() {
+ // Do nothing
+ }
+ };
+
+ @AnyThread
+ private FetchDuringScanHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_PREPARE_FETCH_DURING_SCAN:
+ case MSG_RETRY_PREPARE_FETCH_DURING_SCAN:
+ onPrepareFetchDuringScan();
+ break;
+ case MSG_CHANNEL_UPDATED_DURING_SCAN:
+ if (!hasMessages(MSG_CHANNEL_UPDATED_DURING_SCAN)) {
+ onChannelUpdatedDuringScan((Set<Channel>) msg.obj);
+ }
+ break;
+ case MSG_FINISH_FETCH_DURING_SCAN:
+ removeMessages(MSG_RETRY_PREPARE_FETCH_DURING_SCAN);
+ if (hasMessages(MSG_CHANNEL_UPDATED_DURING_SCAN)) {
+ sendEmptyMessage(MSG_FINISH_FETCH_DURING_SCAN);
+ } else {
+ onFinishFetchDuringScan();
+ }
+ break;
+ default:
+ // do nothing
+ }
+ }
+
+ private void onPrepareFetchDuringScan() {
+ Integer failureReason = prepareFetchEpg(true);
+ if (failureReason != null) {
+ sendEmptyMessageDelayed(
+ MSG_RETRY_PREPARE_FETCH_DURING_SCAN, FETCH_DURING_SCAN_WAIT_TIME_MS);
+ return;
+ }
+ mChannelDataManager.addListener(mDuringScanChannelListener);
+ }
+
+ private void onChannelUpdatedDuringScan(Set<Channel> currentChannels) {
+ String lineupId = pickBestLineupId(currentChannels);
+ Log.i(TAG, "Fast fetch channels for lineup ID: " + lineupId);
+ if (TextUtils.isEmpty(lineupId)) {
+ if (TextUtils.isEmpty(mPossibleLineupId)) {
+ return;
+ }
+ } else if (!TextUtils.equals(lineupId, mPossibleLineupId)) {
+ mFetchedChannelIdsDuringScan.clear();
+ mPossibleLineupId = lineupId;
+ }
+ List<Long> currentChannelIds = new ArrayList<>();
+ for (Channel channel : currentChannels) {
+ currentChannelIds.add(channel.getId());
+ }
+ mFetchedChannelIdsDuringScan.retainAll(currentChannelIds);
+ Set<EpgReader.EpgChannel> newChannels = new HashSet<>();
+ for (EpgReader.EpgChannel epgChannel :
+ mEpgReader.getChannels(currentChannels, mPossibleLineupId)) {
+ if (!mFetchedChannelIdsDuringScan.contains(epgChannel.getChannel().getId())) {
+ newChannels.add(epgChannel);
+ mFetchedChannelIdsDuringScan.add(epgChannel.getChannel().getId());
+ }
+ }
+ batchFetchEpg(newChannels, FETCH_DURING_SCAN_DURATION_SEC);
+ }
+
+ private void onFinishFetchDuringScan() {
+ mChannelDataManager.removeListener(mDuringScanChannelListener);
+ EpgFetchHelper.setLastLineupId(mContext, mPossibleLineupId);
+ clearUnusedLineups(null);
+ mFetchedChannelIdsDuringScan.clear();
+ synchronized (mFetchDuringScanHandlerLock) {
+ if (!hasMessages(MSG_PREPARE_FETCH_DURING_SCAN)) {
+ removeCallbacksAndMessages(null);
+ getLooper().quit();
+ mFetchDuringScanHandler = null;
+ }
+ }
+ // Clear timestamp to make routine service start right away.
+ EpgFetchHelper.setLastEpgUpdatedTimestamp(mContext, 0);
+ Log.i(TAG, "EPG Fetching during channel scanning finished.");
+ new Handler(Looper.getMainLooper())
+ .post(
+ new Runnable() {
+ @Override
+ public void run() {
+ fetchImmediately();
+ }
+ });
+ }
+ }
+}
diff --git a/src/com/android/tv/data/epg/EpgInputWhiteList.java b/src/com/android/tv/data/epg/EpgInputWhiteList.java
new file mode 100644
index 00000000..eada8b24
--- /dev/null
+++ b/src/com/android/tv/data/epg/EpgInputWhiteList.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2017 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.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import android.util.Log;
+import com.android.tv.common.BuildConfig;
+import com.android.tv.common.config.api.RemoteConfig;
+import com.android.tv.common.experiments.Experiments;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/** Checks if a package or a input is white listed. */
+public final class EpgInputWhiteList {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "EpgInputWhiteList";
+ @VisibleForTesting public static final String KEY = "live_channels_3rd_party_epg_inputs";
+ private static final String QA_DEV_INPUTS =
+ "com.example.partnersupportsampletvinput/.SampleTvInputService,"
+ + "com.android.tv.tuner.sample.dvb/.tvinput.SampleDvbTunerTvInputService";
+
+ /** Returns the package portion of a inputId */
+ @Nullable
+ public static String getPackageFromInput(@Nullable String inputId) {
+ return inputId == null ? null : inputId.substring(0, inputId.indexOf("/"));
+ }
+
+ private final RemoteConfig remoteConfig;
+
+ public EpgInputWhiteList(RemoteConfig remoteConfig) {
+ this.remoteConfig = remoteConfig;
+ }
+
+ public boolean isInputWhiteListed(String inputId) {
+ return getWhiteListedInputs().contains(inputId);
+ }
+
+ public boolean isPackageWhiteListed(String packageName) {
+ if (DEBUG) Log.d(TAG, "isPackageWhiteListed " + packageName);
+ Set<String> whiteList = getWhiteListedInputs();
+ for (String good : whiteList) {
+ try {
+ String goodPackage = getPackageFromInput(good);
+ if (goodPackage.equals(packageName)) {
+ return true;
+ }
+ } catch (Exception e) {
+ if (DEBUG) Log.d(TAG, "Error parsing package name of " + good, e);
+ continue;
+ }
+ }
+ return false;
+ }
+
+ private Set<String> getWhiteListedInputs() {
+ Set<String> result = toInputSet(remoteConfig.getString(KEY));
+ if (BuildConfig.ENG || Experiments.ENABLE_QA_FEATURES.get()) {
+ HashSet<String> moreInputs = new HashSet<>(toInputSet(QA_DEV_INPUTS));
+ if (result.isEmpty()) {
+ result = moreInputs;
+ } else {
+ result.addAll(moreInputs);
+ }
+ }
+ if (DEBUG) Log.d(TAG, "getWhiteListedInputs " + result);
+ return result;
+ }
+
+ @VisibleForTesting
+ static Set<String> toInputSet(String value) {
+ if (TextUtils.isEmpty(value)) {
+ return Collections.emptySet();
+ }
+ List<String> strings = Arrays.asList(value.split(","));
+ Set<String> result = new HashSet<>(strings.size());
+ for (String s : strings) {
+ String trimmed = s.trim();
+ if (!TextUtils.isEmpty(trimmed)) {
+ result.add(trimmed);
+ }
+ }
+ return result;
+ }
+}
diff --git a/src/com/android/tv/data/epg/EpgReader.java b/src/com/android/tv/data/epg/EpgReader.java
index c5aeca27..7147905a 100644
--- a/src/com/android/tv/data/epg/EpgReader.java
+++ b/src/com/android/tv/data/epg/EpgReader.java
@@ -19,28 +19,37 @@ package com.android.tv.data.epg;
import android.support.annotation.AnyThread;
import android.support.annotation.NonNull;
import android.support.annotation.WorkerThread;
-
-import com.android.tv.data.Channel;
import com.android.tv.data.Lineup;
import com.android.tv.data.Program;
+import com.android.tv.data.api.Channel;
import com.android.tv.dvr.data.SeriesInfo;
-
+import java.util.Collection;
import java.util.List;
import java.util.Map;
+import java.util.Set;
-/**
- * An interface used to retrieve the EPG data. This class should be used in worker thread.
- */
+/** 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.
- */
+
+ /** Value class that holds a EpgChannelId and its corresponding {@link Channel} */
+ // TODO(b/72052568): Get autovalue to work in aosp master
+ abstract class EpgChannel {
+ public static EpgChannel createEpgChannel(Channel channel, String epgChannelId) {
+ return new AutoValue_EpgReader_EpgChannel(channel, epgChannelId);
+ }
+
+ public abstract Channel getChannel();
+
+ public abstract String getEpgChannelId();
+ }
+
+ /** 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
+ * Returns the timestamp of the current EPG. The format should be YYYYMMDDHHmmSS as a long
+ * value. ex) 20160308141500
*/
long getEpgTimestamp();
@@ -61,31 +70,30 @@ public interface EpgReader {
* Returns the list of channels for the given lineup. The returned channels should map into the
* existing channels on the device. This method is usually called after selecting the lineup.
*/
- List<Channel> getChannels(@NonNull String lineupId);
+ Set<EpgChannel> getChannels(Set<Channel> inputChannels, @NonNull String lineupId);
/** Pre-loads and caches channels for a given lineup. */
void preloadChannels(@NonNull String lineupId);
- /**
- * Clears cached channels for a given lineup.
- */
+ /** Clears cached channels for a given lineup. */
@AnyThread
void clearCachedChannels(@NonNull String lineupId);
/**
- * Returns the programs for the given channel. Must call {@link #getChannels(String)}
+ * Returns the programs for the given channel. Must call {@link #getChannels(Set, String)}
* beforehand. Note that the {@code Program} doesn't have valid program ID because it's not
* retrieved from TvProvider.
*/
- List<Program> getPrograms(long channelId);
+ List<Program> getPrograms(EpgChannel epgChannel);
/**
* Returns the programs for the given channels. Note that the {@code Program} doesn't have valid
* program ID because it's not retrieved from TvProvider. This method is only used to get
* programs for a short duration typically.
*/
- Map<Long, List<Program>> getPrograms(@NonNull List<Long> channelIds, long duration);
+ Map<EpgChannel, Collection<Program>> getPrograms(
+ @NonNull Set<EpgChannel> epgChannels, long duration);
/** Returns the series information for the given series ID. */
SeriesInfo getSeriesInfo(@NonNull String seriesId);
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/data/epg/StubEpgReader.java b/src/com/android/tv/data/epg/StubEpgReader.java
index ab6935ad..3b001481 100644
--- a/src/com/android/tv/data/epg/StubEpgReader.java
+++ b/src/com/android/tv/data/epg/StubEpgReader.java
@@ -17,23 +17,20 @@
package com.android.tv.data.epg;
import android.content.Context;
-
import android.support.annotation.NonNull;
-import com.android.tv.data.Channel;
import com.android.tv.data.Lineup;
import com.android.tv.data.Program;
+import com.android.tv.data.api.Channel;
import com.android.tv.dvr.data.SeriesInfo;
-
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Set;
-/**
- * A stub class to read EPG.
- */
-public class StubEpgReader implements EpgReader{
- public StubEpgReader(@SuppressWarnings("unused") Context context) {
- }
+/** A stub class to read EPG. */
+public class StubEpgReader implements EpgReader {
+ public StubEpgReader(@SuppressWarnings("unused") Context context) {}
@Override
public boolean isAvailable() {
@@ -61,8 +58,8 @@ public class StubEpgReader implements EpgReader{
}
@Override
- public List<Channel> getChannels(@NonNull String lineupId) {
- return Collections.emptyList();
+ public Set<EpgChannel> getChannels(Set<Channel> inputChannels, @NonNull String lineupId) {
+ return Collections.emptySet();
}
@Override
@@ -76,12 +73,13 @@ public class StubEpgReader implements EpgReader{
}
@Override
- public List<Program> getPrograms(long channelId) {
+ public List<Program> getPrograms(EpgChannel epgChannel) {
return Collections.emptyList();
}
@Override
- public Map<Long, List<Program>> getPrograms(@NonNull List<Long> channelIds, long duration) {
+ public Map<EpgChannel, Collection<Program>> getPrograms(
+ @NonNull Set<EpgChannel> channels, long duration) {
return Collections.emptyMap();
}
@@ -89,4 +87,4 @@ public class StubEpgReader implements EpgReader{
public SeriesInfo getSeriesInfo(@NonNull String seriesId) {
return null;
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dialog/DvrHistoryDialogFragment.java b/src/com/android/tv/dialog/DvrHistoryDialogFragment.java
index d686e6e6..7e36591f 100644
--- a/src/com/android/tv/dialog/DvrHistoryDialogFragment.java
+++ b/src/com/android/tv/dialog/DvrHistoryDialogFragment.java
@@ -30,25 +30,21 @@ import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
-
-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.TvSingletons;
import com.android.tv.data.ChannelDataManager;
+import com.android.tv.data.api.Channel;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.data.ScheduledRecording.RecordingState;
import com.android.tv.dvr.ui.DvrUiHelper;
import com.android.tv.util.Utils;
-
import java.util.ArrayList;
import java.util.List;
-/**
- * Displays the DVR history.
- */
+/** Displays the DVR history. */
@TargetApi(VERSION_CODES.N)
+@SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated
public class DvrHistoryDialogFragment extends SafeDismissDialogFragment {
public static final String DIALOG_TAG = DvrHistoryDialogFragment.class.getSimpleName();
@@ -57,7 +53,7 @@ public class DvrHistoryDialogFragment extends SafeDismissDialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
- ApplicationSingletons singletons = TvApplication.getSingletons(getContext());
+ TvSingletons singletons = TvSingletons.getSingletons(getContext());
DvrDataManager dataManager = singletons.getDvrDataManager();
ChannelDataManager channelDataManager = singletons.getChannelDataManager();
for (ScheduledRecording schedule : dataManager.getAllScheduledRecordings()) {
@@ -67,60 +63,78 @@ public class DvrHistoryDialogFragment extends SafeDismissDialogFragment {
}
mSchedules.sort(ScheduledRecording.START_TIME_COMPARATOR.reversed());
LayoutInflater inflater = LayoutInflater.from(getContext());
- ArrayAdapter adapter = new ArrayAdapter<ScheduledRecording>(getContext(),
- R.layout.list_item_dvr_history, ScheduledRecording.toArray(mSchedules)) {
- @NonNull
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- View view = inflater.inflate(R.layout.list_item_dvr_history, parent, false);
- ScheduledRecording schedule = mSchedules.get(position);
- setText(view, R.id.state, getStateString(schedule.getState()));
- setText(view, R.id.schedule_time, getRecordingTimeText(schedule));
- setText(view, R.id.program_title, DvrUiHelper.getStyledTitleWithEpisodeNumber(
- getContext(), schedule, 0));
- setText(view, R.id.channel_name, getChannelNameText(schedule));
- return view;
- }
+ ArrayAdapter adapter =
+ new ArrayAdapter<ScheduledRecording>(
+ getContext(),
+ R.layout.list_item_dvr_history,
+ ScheduledRecording.toArray(mSchedules)) {
+ @NonNull
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View view = inflater.inflate(R.layout.list_item_dvr_history, parent, false);
+ ScheduledRecording schedule = mSchedules.get(position);
+ setText(view, R.id.state, getStateString(schedule.getState()));
+ setText(view, R.id.schedule_time, getRecordingTimeText(schedule));
+ setText(
+ view,
+ R.id.program_title,
+ DvrUiHelper.getStyledTitleWithEpisodeNumber(
+ getContext(), schedule, 0));
+ setText(view, R.id.channel_name, getChannelNameText(schedule));
+ return view;
+ }
- private void setText(View view, int id, CharSequence text) {
- ((TextView) view.findViewById(id)).setText(text);
- }
+ private void setText(View view, int id, CharSequence text) {
+ ((TextView) view.findViewById(id)).setText(text);
+ }
- private void setText(View view, int id, int text) {
- ((TextView) view.findViewById(id)).setText(text);
- }
+ private void setText(View view, int id, int text) {
+ ((TextView) view.findViewById(id)).setText(text);
+ }
- @SuppressLint("SwitchIntDef")
- private int getStateString(@RecordingState int state) {
- switch (state) {
- case ScheduledRecording.STATE_RECORDING_CLIPPED:
- return R.string.dvr_history_dialog_state_clip;
- case ScheduledRecording.STATE_RECORDING_FAILED:
- return R.string.dvr_history_dialog_state_fail;
- case ScheduledRecording.STATE_RECORDING_FINISHED:
- return R.string.dvr_history_dialog_state_success;
- default:
- break;
- }
- return 0;
- }
+ @SuppressLint("SwitchIntDef")
+ private int getStateString(@RecordingState int state) {
+ switch (state) {
+ case ScheduledRecording.STATE_RECORDING_CLIPPED:
+ return R.string.dvr_history_dialog_state_clip;
+ case ScheduledRecording.STATE_RECORDING_FAILED:
+ return R.string.dvr_history_dialog_state_fail;
+ case ScheduledRecording.STATE_RECORDING_FINISHED:
+ return R.string.dvr_history_dialog_state_success;
+ default:
+ break;
+ }
+ return 0;
+ }
- private String getChannelNameText(ScheduledRecording schedule) {
- Channel channel = channelDataManager.getChannel(schedule.getChannelId());
- return channel == null ? null :
- TextUtils.isEmpty(channel.getDisplayName()) ? channel.getDisplayNumber() :
- channel.getDisplayName().trim() + " " + channel.getDisplayNumber();
- }
+ private String getChannelNameText(ScheduledRecording schedule) {
+ Channel channel = channelDataManager.getChannel(schedule.getChannelId());
+ return channel == null
+ ? null
+ : TextUtils.isEmpty(channel.getDisplayName())
+ ? channel.getDisplayNumber()
+ : channel.getDisplayName().trim()
+ + " "
+ + channel.getDisplayNumber();
+ }
- private String getRecordingTimeText(ScheduledRecording schedule) {
- return Utils.getDurationString(getContext(), schedule.getStartTimeMs(),
- schedule.getEndTimeMs(), true, true, true, 0);
- }
- };
+ private String getRecordingTimeText(ScheduledRecording schedule) {
+ return Utils.getDurationString(
+ getContext(),
+ schedule.getStartTimeMs(),
+ schedule.getEndTimeMs(),
+ true,
+ true,
+ true,
+ 0);
+ }
+ };
ListView listView = new ListView(getActivity());
listView.setAdapter(adapter);
- return new AlertDialog.Builder(getActivity()).setTitle(R.string.dvr_history_dialog_title)
- .setView(listView).create();
+ return new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.dvr_history_dialog_title)
+ .setView(listView)
+ .create();
}
@Override
diff --git a/src/com/android/tv/dialog/FullscreenDialogFragment.java b/src/com/android/tv/dialog/FullscreenDialogFragment.java
index d00422a7..53adb308 100644
--- a/src/com/android/tv/dialog/FullscreenDialogFragment.java
+++ b/src/com/android/tv/dialog/FullscreenDialogFragment.java
@@ -23,21 +23,18 @@ import android.os.Bundle;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
-
import com.android.tv.MainActivity;
import com.android.tv.R;
-/**
- * Dialog fragment with full screen.
- */
+/** Dialog fragment with full screen. */
public class FullscreenDialogFragment extends SafeDismissDialogFragment {
public static final String DIALOG_TAG = FullscreenDialogFragment.class.getSimpleName();
public static final String VIEW_LAYOUT_ID = "viewLayoutId";
public static final String TRACKER_LABEL = "trackerLabel";
/**
- * Creates a FullscreenDialogFragment. View class of viewLayoutResId should
- * implement {@link DialogView}.
+ * Creates a FullscreenDialogFragment. View class of viewLayoutResId should implement {@link
+ * DialogView}.
*/
public static FullscreenDialogFragment newInstance(int viewLayoutResId, String trackerLabel) {
FullscreenDialogFragment f = new FullscreenDialogFragment();
@@ -100,21 +97,13 @@ public class FullscreenDialogFragment extends SafeDismissDialogFragment {
}
}
- /**
- * Interface for the view of {@link FullscreenDialogFragment}.
- */
+ /** Interface for the view of {@link FullscreenDialogFragment}. */
public interface DialogView {
- /**
- * Called after the view is inflated and attached to the dialog.
- */
+ /** Called after the view is inflated and attached to the dialog. */
void initialize(MainActivity activity, Dialog dialog);
- /**
- * Called when a back key is pressed.
- */
+ /** Called when a back key is pressed. */
void onBackPressed();
- /**
- * Called when {@link DialogFragment#onDestroy} is called.
- */
+ /** Called when {@link DialogFragment#onDestroy} is called. */
void onDestroy();
}
}
diff --git a/src/com/android/tv/dialog/HalfSizedDialogFragment.java b/src/com/android/tv/dialog/HalfSizedDialogFragment.java
index 315c6a93..aed75655 100644
--- a/src/com/android/tv/dialog/HalfSizedDialogFragment.java
+++ b/src/com/android/tv/dialog/HalfSizedDialogFragment.java
@@ -24,9 +24,7 @@ import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-
import com.android.tv.R;
-
import java.util.concurrent.TimeUnit;
public class HalfSizedDialogFragment extends SafeDismissDialogFragment {
@@ -38,16 +36,17 @@ public class HalfSizedDialogFragment extends SafeDismissDialogFragment {
private OnActionClickListener mOnActionClickListener;
private Handler mHandler = new Handler();
- private Runnable mAutoDismisser = new Runnable() {
- @Override
- public void run() {
- dismiss();
- }
- };
+ private Runnable mAutoDismisser =
+ new Runnable() {
+ @Override
+ public void run() {
+ dismiss();
+ }
+ };
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.halfsized_dialog, container, false);
}
@@ -76,13 +75,14 @@ public class HalfSizedDialogFragment extends SafeDismissDialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState);
- dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
- public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent keyEvent) {
- mHandler.removeCallbacks(mAutoDismisser);
- mHandler.postDelayed(mAutoDismisser, AUTO_DISMISS_TIME_THRESHOLD_MS);
- return false;
- }
- });
+ dialog.setOnKeyListener(
+ new DialogInterface.OnKeyListener() {
+ public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent keyEvent) {
+ mHandler.removeCallbacks(mAutoDismisser);
+ mHandler.postDelayed(mAutoDismisser, AUTO_DISMISS_TIME_THRESHOLD_MS);
+ return false;
+ }
+ });
return dialog;
}
@@ -98,26 +98,24 @@ public class HalfSizedDialogFragment extends SafeDismissDialogFragment {
/**
* Sets {@link OnActionClickListener} for the dialog fragment. If listener is set, the dialog
- * will be automatically closed when it's paused to prevent the fragment being re-created by
- * the framework, which will result the listener being forgotten.
+ * will be automatically closed when it's paused to prevent the fragment being re-created by the
+ * framework, which will result the listener being forgotten.
*/
public void setOnActionClickListener(OnActionClickListener listener) {
mOnActionClickListener = listener;
}
- /**
- * Returns {@link OnActionClickListener} for sub-classes or any inner fragments.
- */
+ /** Returns {@link OnActionClickListener} for sub-classes or any inner fragments. */
protected OnActionClickListener getOnActionClickListener() {
return mOnActionClickListener;
}
/**
* An interface to provide callbacks for half-sized dialogs. Subclasses or inner fragments
- * should invoke {@link OnActionClickListener#onActionClick(long)} and provide the identifier
- * of the action user clicked.
+ * should invoke {@link OnActionClickListener#onActionClick(long)} and provide the identifier of
+ * the action user clicked.
*/
public interface OnActionClickListener {
void onActionClick(long actionId);
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dialog/PinDialogFragment.java b/src/com/android/tv/dialog/PinDialogFragment.java
index d5c154da..71f45fbe 100644
--- a/src/com/android/tv/dialog/PinDialogFragment.java
+++ b/src/com/android/tv/dialog/PinDialogFragment.java
@@ -44,43 +44,34 @@ import android.view.ViewGroup.LayoutParams;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
import com.android.tv.util.TvSettings;
public class PinDialogFragment extends SafeDismissDialogFragment {
private static final String TAG = "PinDialogFragment";
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
- /**
- * PIN code dialog for unlock channel
- */
+ /** PIN code dialog for unlock channel */
public static final int PIN_DIALOG_TYPE_UNLOCK_CHANNEL = 0;
/**
- * PIN code dialog for unlock content.
- * Only difference between {@code PIN_DIALOG_TYPE_UNLOCK_CHANNEL} is it's title.
+ * PIN code dialog for unlock content. Only difference between {@code
+ * PIN_DIALOG_TYPE_UNLOCK_CHANNEL} is it's title.
*/
public static final int PIN_DIALOG_TYPE_UNLOCK_PROGRAM = 1;
- /**
- * PIN code dialog for change parental control settings
- */
+ /** PIN code dialog for change parental control settings */
public static final int PIN_DIALOG_TYPE_ENTER_PIN = 2;
- /**
- * PIN code dialog for set new PIN
- */
+ /** PIN code dialog for set new PIN */
public static final int PIN_DIALOG_TYPE_NEW_PIN = 3;
- // PIN code dialog for checking old PIN. This is internal only.
+ // PIN code dialog for checking old PIN. Only used in this class.
private static final int PIN_DIALOG_TYPE_OLD_PIN = 4;
- /**
- * PIN code dialog for unlocking DVR playback
- */
+ /** PIN code dialog for unlocking DVR playback */
public static final int PIN_DIALOG_TYPE_UNLOCK_DVR = 5;
private static final int MAX_WRONG_PIN_COUNT = 5;
@@ -94,7 +85,8 @@ public class PinDialogFragment extends SafeDismissDialogFragment {
public static final String DIALOG_TAG = PinDialogFragment.class.getName();
private static final int NUMBER_PICKERS_RES_ID[] = {
- R.id.first, R.id.second, R.id.third, R.id.fourth };
+ R.id.first, R.id.second, R.id.third, R.id.fourth
+ };
private int mType;
private int mRequestType;
@@ -164,15 +156,16 @@ public class PinDialogFragment extends SafeDismissDialogFragment {
// So apply view size to window after the DialogFragment.onStart() where dialog is shown.
Dialog dlg = getDialog();
if (dlg != null) {
- dlg.getWindow().setLayout(
- getResources().getDimensionPixelSize(R.dimen.pin_dialog_width),
- LayoutParams.WRAP_CONTENT);
+ dlg.getWindow()
+ .setLayout(
+ getResources().getDimensionPixelSize(R.dimen.pin_dialog_width),
+ LayoutParams.WRAP_CONTENT);
}
}
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View v = inflater.inflate(R.layout.pin_dialog, container, false);
mWrongPinView = (TextView) v.findViewById(R.id.wrong_pin);
@@ -199,7 +192,7 @@ public class PinDialogFragment extends SafeDismissDialogFragment {
mTitleView.setText(
getString(
R.string.pin_enter_unlock_dvr,
- TvApplication.getSingletons(getContext())
+ TvSingletons.getSingletons(getContext())
.getTvInputManagerHelper()
.getContentRatingsManager()
.getDisplayNameForRating(tvContentRating)));
@@ -234,12 +227,13 @@ public class PinDialogFragment extends SafeDismissDialogFragment {
return v;
}
- private final Runnable mUpdateEnterPinRunnable = new Runnable() {
- @Override
- public void run() {
- updateWrongPin();
- }
- };
+ private final Runnable mUpdateEnterPinRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ updateWrongPin();
+ }
+ };
private void updateWrongPin() {
if (getActivity() == null) {
@@ -257,8 +251,12 @@ public class PinDialogFragment extends SafeDismissDialogFragment {
} else {
mEnterPinView.setVisibility(View.INVISIBLE);
mWrongPinView.setVisibility(View.VISIBLE);
- mWrongPinView.setText(getResources().getQuantityString(R.plurals.pin_enter_countdown,
- remainingSeconds, remainingSeconds));
+ mWrongPinView.setText(
+ getResources()
+ .getQuantityString(
+ R.plurals.pin_enter_countdown,
+ remainingSeconds,
+ remainingSeconds));
mHandler.postDelayed(mUpdateEnterPinRunnable, 1000);
}
}
@@ -280,8 +278,8 @@ public class PinDialogFragment extends SafeDismissDialogFragment {
if (DEBUG) Log.d(TAG, "onDismiss: mPinChecked=" + mPinChecked);
SoftPreconditions.checkState(getActivity() instanceof OnPinCheckedListener);
if (!mDismissSilently && getActivity() instanceof OnPinCheckedListener) {
- ((OnPinCheckedListener) getActivity()).onPinChecked(
- mPinChecked, mRequestType, mRatingString);
+ ((OnPinCheckedListener) getActivity())
+ .onPinChecked(mPinChecked, mRequestType, mRatingString);
}
mDismissSilently = false;
}
@@ -391,7 +389,8 @@ public class PinDialogFragment extends SafeDismissDialogFragment {
R.id.previous_number,
R.id.current_number,
R.id.next_number,
- R.id.next2_number };
+ R.id.next2_number
+ };
private static final int CURRENT_NUMBER_VIEW_INDEX = 2;
private static final int NOT_INITIALIZED = Integer.MIN_VALUE;
@@ -436,8 +435,8 @@ public class PinDialogFragment extends SafeDismissDialogFragment {
this(context, attrs, defStyleAttr, 0);
}
- public PinNumberPicker(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
+ public PinNumberPicker(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
View view = inflate(context, R.layout.pin_number_picker, this);
mNumberViewHolder = view.findViewById(R.id.number_view_holder);
@@ -447,118 +446,149 @@ public class PinDialogFragment extends SafeDismissDialogFragment {
mNumberViews[i] = (TextView) view.findViewById(NUMBER_VIEWS_RES_ID[i]);
}
Resources resources = context.getResources();
- mNumberViewHeight = resources.getDimensionPixelSize(
- R.dimen.pin_number_picker_text_view_height);
-
- mNumberViewHolder.setOnFocusChangeListener(new OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- updateFocus(true);
- }
- });
-
- mNumberViewHolder.setOnKeyListener(new OnKeyListener() {
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_DPAD_UP:
- case KeyEvent.KEYCODE_DPAD_DOWN: {
- if (mCancelAnimation) {
- mScrollAnimatorSet.end();
+ mNumberViewHeight =
+ resources.getDimensionPixelSize(R.dimen.pin_number_picker_text_view_height);
+
+ mNumberViewHolder.setOnFocusChangeListener(
+ new OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ updateFocus(true);
+ }
+ });
+
+ mNumberViewHolder.setOnKeyListener(
+ new OnKeyListener() {
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_UP:
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ {
+ if (mCancelAnimation) {
+ mScrollAnimatorSet.end();
+ }
+ if (!mScrollAnimatorSet.isRunning()) {
+ mCancelAnimation = false;
+ if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
+ mNextValue =
+ adjustValueInValidRange(
+ mCurrentValue + 1);
+ startScrollAnimation(true);
+ } else {
+ mNextValue =
+ adjustValueInValidRange(
+ mCurrentValue - 1);
+ startScrollAnimation(false);
+ }
+ }
+ return true;
+ }
}
- if (!mScrollAnimatorSet.isRunning()) {
- mCancelAnimation = false;
- if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
- mNextValue = adjustValueInValidRange(mCurrentValue + 1);
- startScrollAnimation(true);
- } else {
- mNextValue = adjustValueInValidRange(mCurrentValue - 1);
- startScrollAnimation(false);
- }
+ } else if (event.getAction() == KeyEvent.ACTION_UP) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_UP:
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ {
+ mCancelAnimation = true;
+ return true;
+ }
}
- return true;
- }
- }
- } else if (event.getAction() == KeyEvent.ACTION_UP) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_DPAD_UP:
- case KeyEvent.KEYCODE_DPAD_DOWN: {
- mCancelAnimation = true;
- return true;
}
+ return false;
}
- }
- return false;
- }
- });
+ });
mNumberViewHolder.setScrollY(mNumberViewHeight);
mFocusGainAnimator = new AnimatorSet();
mFocusGainAnimator.playTogether(
- ObjectAnimator.ofFloat(mNumberViews[CURRENT_NUMBER_VIEW_INDEX - 1],
- "alpha", 0f, sAlphaForAdjacentNumber),
- ObjectAnimator.ofFloat(mNumberViews[CURRENT_NUMBER_VIEW_INDEX],
- "alpha", sAlphaForFocusedNumber, 0f),
- ObjectAnimator.ofFloat(mNumberViews[CURRENT_NUMBER_VIEW_INDEX + 1],
- "alpha", 0f, sAlphaForAdjacentNumber),
+ ObjectAnimator.ofFloat(
+ mNumberViews[CURRENT_NUMBER_VIEW_INDEX - 1],
+ "alpha",
+ 0f,
+ sAlphaForAdjacentNumber),
+ ObjectAnimator.ofFloat(
+ mNumberViews[CURRENT_NUMBER_VIEW_INDEX],
+ "alpha",
+ sAlphaForFocusedNumber,
+ 0f),
+ ObjectAnimator.ofFloat(
+ mNumberViews[CURRENT_NUMBER_VIEW_INDEX + 1],
+ "alpha",
+ 0f,
+ sAlphaForAdjacentNumber),
ObjectAnimator.ofFloat(mBackgroundView, "alpha", 0f, 1f));
- mFocusGainAnimator.setDuration(context.getResources().getInteger(
- android.R.integer.config_shortAnimTime));
- mFocusGainAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animator) {
- mNumberViews[CURRENT_NUMBER_VIEW_INDEX].setText(mBackgroundView.getText());
- mNumberViews[CURRENT_NUMBER_VIEW_INDEX].setAlpha(sAlphaForFocusedNumber);
- mBackgroundView.setText("");
- }
- });
+ mFocusGainAnimator.setDuration(
+ context.getResources().getInteger(android.R.integer.config_shortAnimTime));
+ mFocusGainAnimator.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ mNumberViews[CURRENT_NUMBER_VIEW_INDEX].setText(
+ mBackgroundView.getText());
+ mNumberViews[CURRENT_NUMBER_VIEW_INDEX].setAlpha(
+ sAlphaForFocusedNumber);
+ mBackgroundView.setText("");
+ }
+ });
mFocusLossAnimator = new AnimatorSet();
mFocusLossAnimator.playTogether(
- ObjectAnimator.ofFloat(mNumberViews[CURRENT_NUMBER_VIEW_INDEX - 1],
- "alpha", sAlphaForAdjacentNumber, 0f),
- ObjectAnimator.ofFloat(mNumberViews[CURRENT_NUMBER_VIEW_INDEX + 1],
- "alpha", sAlphaForAdjacentNumber, 0f),
+ ObjectAnimator.ofFloat(
+ mNumberViews[CURRENT_NUMBER_VIEW_INDEX - 1],
+ "alpha",
+ sAlphaForAdjacentNumber,
+ 0f),
+ ObjectAnimator.ofFloat(
+ mNumberViews[CURRENT_NUMBER_VIEW_INDEX + 1],
+ "alpha",
+ sAlphaForAdjacentNumber,
+ 0f),
ObjectAnimator.ofFloat(mBackgroundView, "alpha", 1f, 0f));
- mFocusLossAnimator.setDuration(context.getResources().getInteger(
- android.R.integer.config_shortAnimTime));
+ mFocusLossAnimator.setDuration(
+ context.getResources().getInteger(android.R.integer.config_shortAnimTime));
mScrollAnimatorSet = new AnimatorSet();
- mScrollAnimatorSet.setDuration(context.getResources().getInteger(
- R.integer.pin_number_scroll_duration));
- mScrollAnimatorSet.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // Set mCurrent value when scroll animation is finished.
- mCurrentValue = mNextValue;
- updateText();
- mNumberViewHolder.setScrollY(mNumberViewHeight);
- mNumberViews[CURRENT_NUMBER_VIEW_INDEX - 1].setAlpha(sAlphaForAdjacentNumber);
- mNumberViews[CURRENT_NUMBER_VIEW_INDEX].setAlpha(sAlphaForFocusedNumber);
- mNumberViews[CURRENT_NUMBER_VIEW_INDEX + 1].setAlpha(sAlphaForAdjacentNumber);
- }
- });
+ mScrollAnimatorSet.setDuration(
+ context.getResources().getInteger(R.integer.pin_number_scroll_duration));
+ mScrollAnimatorSet.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Set mCurrent value when scroll animation is finished.
+ mCurrentValue = mNextValue;
+ updateText();
+ mNumberViewHolder.setScrollY(mNumberViewHeight);
+ mNumberViews[CURRENT_NUMBER_VIEW_INDEX - 1].setAlpha(
+ sAlphaForAdjacentNumber);
+ mNumberViews[CURRENT_NUMBER_VIEW_INDEX].setAlpha(
+ sAlphaForFocusedNumber);
+ mNumberViews[CURRENT_NUMBER_VIEW_INDEX + 1].setAlpha(
+ sAlphaForAdjacentNumber);
+ }
+ });
}
static void loadResources(Context context) {
if (sFocusedNumberEnterAnimator == null) {
TypedValue outValue = new TypedValue();
- context.getResources().getValue(
- R.dimen.pin_alpha_for_focused_number, outValue, true);
+ context.getResources()
+ .getValue(R.dimen.pin_alpha_for_focused_number, outValue, true);
sAlphaForFocusedNumber = outValue.getFloat();
- context.getResources().getValue(
- R.dimen.pin_alpha_for_adjacent_number, outValue, true);
+ context.getResources()
+ .getValue(R.dimen.pin_alpha_for_adjacent_number, outValue, true);
sAlphaForAdjacentNumber = outValue.getFloat();
- sFocusedNumberEnterAnimator = AnimatorInflater.loadAnimator(context,
- R.animator.pin_focused_number_enter);
- sFocusedNumberExitAnimator = AnimatorInflater.loadAnimator(context,
- R.animator.pin_focused_number_exit);
- sAdjacentNumberEnterAnimator = AnimatorInflater.loadAnimator(context,
- R.animator.pin_adjacent_number_enter);
- sAdjacentNumberExitAnimator = AnimatorInflater.loadAnimator(context,
- R.animator.pin_adjacent_number_exit);
+ sFocusedNumberEnterAnimator =
+ AnimatorInflater.loadAnimator(context, R.animator.pin_focused_number_enter);
+ sFocusedNumberExitAnimator =
+ AnimatorInflater.loadAnimator(context, R.animator.pin_focused_number_exit);
+ sAdjacentNumberEnterAnimator =
+ AnimatorInflater.loadAnimator(
+ context, R.animator.pin_adjacent_number_enter);
+ sAdjacentNumberExitAnimator =
+ AnimatorInflater.loadAnimator(context, R.animator.pin_adjacent_number_exit);
}
}
@@ -588,15 +618,16 @@ public class PinDialogFragment extends SafeDismissDialogFragment {
void startScrollAnimation(boolean scrollUp) {
mFocusGainAnimator.end();
mFocusLossAnimator.end();
- final ValueAnimator scrollAnimator = ValueAnimator.ofInt(
- 0, scrollUp ? mNumberViewHeight : -mNumberViewHeight);
- scrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- int value = (Integer) animation.getAnimatedValue();
- mNumberViewHolder.setScrollY(value + mNumberViewHeight);
- }
- });
+ final ValueAnimator scrollAnimator =
+ ValueAnimator.ofInt(0, scrollUp ? mNumberViewHeight : -mNumberViewHeight);
+ scrollAnimator.addUpdateListener(
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ int value = (Integer) animation.getAnimatedValue();
+ mNumberViewHolder.setScrollY(value + mNumberViewHeight);
+ }
+ });
scrollAnimator.setDuration(
getResources().getInteger(R.integer.pin_number_scroll_duration));
@@ -612,9 +643,12 @@ public class PinDialogFragment extends SafeDismissDialogFragment {
sAdjacentNumberExitAnimator.setTarget(mNumberViews[CURRENT_NUMBER_VIEW_INDEX + 1]);
}
- mScrollAnimatorSet.playTogether(scrollAnimator,
- sAdjacentNumberExitAnimator, sFocusedNumberExitAnimator,
- sFocusedNumberEnterAnimator, sAdjacentNumberEnterAnimator);
+ mScrollAnimatorSet.playTogether(
+ scrollAnimator,
+ sAdjacentNumberExitAnimator,
+ sFocusedNumberExitAnimator,
+ sFocusedNumberEnterAnimator,
+ sAdjacentNumberEnterAnimator);
mScrollAnimatorSet.start();
}
@@ -688,8 +722,10 @@ public class PinDialogFragment extends SafeDismissDialogFragment {
// In order to show the text change animation, keep the text of
// mNumberViews[CURRENT_NUMBER_VIEW_INDEX].
} else {
- mNumberViews[i].setText(String.valueOf(adjustValueInValidRange(
- mCurrentValue - CURRENT_NUMBER_VIEW_INDEX + i)));
+ mNumberViews[i].setText(
+ String.valueOf(
+ adjustValueInValidRange(
+ mCurrentValue - CURRENT_NUMBER_VIEW_INDEX + i)));
}
}
}
@@ -698,10 +734,11 @@ public class PinDialogFragment extends SafeDismissDialogFragment {
private int adjustValueInValidRange(int value) {
int interval = mMaxValue - mMinValue + 1;
if (value < mMinValue - interval || value > mMaxValue + interval) {
- throw new IllegalArgumentException("The value( " + value
- + ") is too small or too big to adjust");
+ throw new IllegalArgumentException(
+ "The value( " + value + ") is too small or too big to adjust");
}
- return (value < mMinValue) ? value + interval
+ return (value < mMinValue)
+ ? value + interval
: (value > mMaxValue) ? value - interval : value;
}
}
@@ -714,8 +751,8 @@ public class PinDialogFragment extends SafeDismissDialogFragment {
/**
* Called when {@link PinDialogFragment} is dismissed.
*
- * @param checked {@code true} if the pin code entered is checked to be correct,
- * otherwise {@code false}.
+ * @param checked {@code true} if the pin code entered is checked to be correct, otherwise
+ * {@code false}.
* @param type The dialog type regarding to what pin entering is for.
* @param rating The target rating to unblock for.
*/
diff --git a/src/com/android/tv/dialog/RecentlyWatchedDialogFragment.java b/src/com/android/tv/dialog/RecentlyWatchedDialogFragment.java
index 1875f411..eb6940fb 100644
--- a/src/com/android/tv/dialog/RecentlyWatchedDialogFragment.java
+++ b/src/com/android/tv/dialog/RecentlyWatchedDialogFragment.java
@@ -29,17 +29,14 @@ import android.view.View;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
-
import com.android.tv.MainActivity;
import com.android.tv.R;
-import com.android.tv.data.Channel;
import com.android.tv.data.ChannelDataManager;
+import com.android.tv.data.api.Channel;
-/**
- * Displays the watch history
- */
-public class RecentlyWatchedDialogFragment extends SafeDismissDialogFragment implements
- LoaderManager.LoaderCallbacks<Cursor> {
+/** Displays the watch history */
+public class RecentlyWatchedDialogFragment extends SafeDismissDialogFragment
+ implements LoaderManager.LoaderCallbacks<Cursor> {
public static final String DIALOG_TAG = RecentlyWatchedDialogFragment.class.getSimpleName();
private static final String EMPTY_STRING = "";
@@ -55,47 +52,57 @@ public class RecentlyWatchedDialogFragment extends SafeDismissDialogFragment imp
((MainActivity) getActivity()).getChannelDataManager();
String[] from = {
- TvContract.WatchedPrograms._ID,
- TvContract.WatchedPrograms.COLUMN_CHANNEL_ID,
- TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
- TvContract.WatchedPrograms.COLUMN_TITLE };
+ TvContract.WatchedPrograms._ID,
+ TvContract.WatchedPrograms.COLUMN_CHANNEL_ID,
+ TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
+ TvContract.WatchedPrograms.COLUMN_TITLE
+ };
int[] to = {
- R.id.watched_program_id,
- R.id.watched_program_channel_id,
- R.id.watched_program_watch_time,
- R.id.watched_program_title};
- mAdapter = new SimpleCursorAdapter(getActivity(), R.layout.list_item_watched_program, null,
- from, to, 0);
- mAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
- @Override
- public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
- String name = cursor.getColumnName(columnIndex);
- if (TvContract.WatchedPrograms.COLUMN_CHANNEL_ID.equals(name)) {
- long channelId = cursor.getLong(columnIndex);
- ((TextView) view).setText(String.valueOf(channelId));
- // Update display number
- String displayNumber;
- Channel channel = dataChannelManager.getChannel(channelId);
- if (channel == null) {
- displayNumber = EMPTY_STRING;
- } else {
- displayNumber = channel.getDisplayNumber();
+ R.id.watched_program_id,
+ R.id.watched_program_channel_id,
+ R.id.watched_program_watch_time,
+ R.id.watched_program_title
+ };
+ mAdapter =
+ new SimpleCursorAdapter(
+ getActivity(), R.layout.list_item_watched_program, null, from, to, 0);
+ mAdapter.setViewBinder(
+ new SimpleCursorAdapter.ViewBinder() {
+ @Override
+ public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
+ String name = cursor.getColumnName(columnIndex);
+ if (TvContract.WatchedPrograms.COLUMN_CHANNEL_ID.equals(name)) {
+ long channelId = cursor.getLong(columnIndex);
+ ((TextView) view).setText(String.valueOf(channelId));
+ // Update display number
+ String displayNumber;
+ Channel channel = dataChannelManager.getChannel(channelId);
+ if (channel == null) {
+ displayNumber = EMPTY_STRING;
+ } else {
+ displayNumber = channel.getDisplayNumber();
+ }
+ TextView displayNumberView =
+ ((TextView)
+ ((View) view.getParent())
+ .findViewById(
+ R.id.watched_program_channel_display_number));
+ displayNumberView.setText(displayNumber);
+ return true;
+ } else if (TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS
+ .equals(name)) {
+ long time = cursor.getLong(columnIndex);
+ CharSequence timeString =
+ DateUtils.getRelativeTimeSpanString(
+ time,
+ System.currentTimeMillis(),
+ DateUtils.SECOND_IN_MILLIS);
+ ((TextView) view).setText(String.valueOf(timeString));
+ return true;
+ }
+ return false;
}
- TextView displayNumberView = ((TextView) ((View) view.getParent())
- .findViewById(R.id.watched_program_channel_display_number));
- displayNumberView.setText(displayNumber);
- return true;
- } else if (TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS.equals(
- name)) {
- long time = cursor.getLong(columnIndex);
- CharSequence timeString = DateUtils.getRelativeTimeSpanString(time,
- System.currentTimeMillis(), DateUtils.SECOND_IN_MILLIS);
- ((TextView) view).setText(String.valueOf(timeString));
- return true;
- }
- return false;
- }
- });
+ });
ListView listView = new ListView(getActivity());
listView.setAdapter(mAdapter);
@@ -121,12 +128,18 @@ public class RecentlyWatchedDialogFragment extends SafeDismissDialogFragment imp
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String[] projection = {
- TvContract.WatchedPrograms._ID,
- TvContract.WatchedPrograms.COLUMN_CHANNEL_ID,
- TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
- TvContract.WatchedPrograms.COLUMN_TITLE };
- return new CursorLoader(getActivity(), TvContract.WatchedPrograms.CONTENT_URI, projection,
- null, null, TvContract.WatchedPrograms._ID + " DESC");
+ TvContract.WatchedPrograms._ID,
+ TvContract.WatchedPrograms.COLUMN_CHANNEL_ID,
+ TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
+ TvContract.WatchedPrograms.COLUMN_TITLE
+ };
+ return new CursorLoader(
+ getActivity(),
+ TvContract.WatchedPrograms.CONTENT_URI,
+ projection,
+ null,
+ null,
+ TvContract.WatchedPrograms._ID + " DESC");
}
@Override
diff --git a/src/com/android/tv/dialog/SafeDismissDialogFragment.java b/src/com/android/tv/dialog/SafeDismissDialogFragment.java
index e3390b0a..6eb67dfd 100644
--- a/src/com/android/tv/dialog/SafeDismissDialogFragment.java
+++ b/src/com/android/tv/dialog/SafeDismissDialogFragment.java
@@ -18,17 +18,13 @@ package com.android.tv.dialog;
import android.app.Activity;
import android.app.DialogFragment;
-
import com.android.tv.MainActivity;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.analytics.HasTrackerLabel;
import com.android.tv.analytics.Tracker;
-/**
- * Provides the safe dismiss feature regardless of the DialogFragment's life cycle.
- */
-public abstract class SafeDismissDialogFragment extends DialogFragment
- implements HasTrackerLabel {
+/** Provides the safe dismiss feature regardless of the DialogFragment's life cycle. */
+public abstract class SafeDismissDialogFragment extends DialogFragment implements HasTrackerLabel {
private MainActivity mActivity;
private boolean mAttached = false;
private boolean mDismissPending = false;
@@ -41,7 +37,7 @@ public abstract class SafeDismissDialogFragment extends DialogFragment
if (activity instanceof MainActivity) {
mActivity = (MainActivity) activity;
}
- mTracker = TvApplication.getSingletons(activity).getTracker();
+ mTracker = TvSingletons.getSingletons(activity).getTracker();
if (mDismissPending) {
mDismissPending = false;
dismiss();
@@ -69,9 +65,7 @@ public abstract class SafeDismissDialogFragment extends DialogFragment
mTracker = null;
}
- /**
- * Dismiss safely regardless of the DialogFragment's life cycle.
- */
+ /** Dismiss safely regardless of the DialogFragment's life cycle. */
@Override
public void dismiss() {
if (!mAttached) {
diff --git a/src/com/android/tv/dialog/WebDialogFragment.java b/src/com/android/tv/dialog/WebDialogFragment.java
index 171a256b..5266f760 100644
--- a/src/com/android/tv/dialog/WebDialogFragment.java
+++ b/src/com/android/tv/dialog/WebDialogFragment.java
@@ -28,9 +28,7 @@ import android.view.ViewGroup.LayoutParams;
import android.webkit.WebView;
import android.webkit.WebViewClient;
-/**
- * A DialogFragment that shows a web view.
- */
+/** A DialogFragment that shows a web view. */
public class WebDialogFragment extends SafeDismissDialogFragment {
private static final String TAG = "WebDialogFragment";
private static final String URL = "URL";
@@ -43,11 +41,11 @@ public class WebDialogFragment extends SafeDismissDialogFragment {
/**
* Create a new WebDialogFragment to show a particular web page.
*
- * @param url The URL of the content to show.
+ * @param url The URL of the content to show.
* @param title Optional title for the dialog.
*/
- public static WebDialogFragment newInstance(String url, @Nullable String title,
- String trackerLabel) {
+ public static WebDialogFragment newInstance(
+ String url, @Nullable String title, String trackerLabel) {
WebDialogFragment f = new WebDialogFragment();
Bundle args = new Bundle();
args.putString(URL, url);
@@ -62,15 +60,17 @@ public class WebDialogFragment extends SafeDismissDialogFragment {
super.onCreate(savedInstanceState);
String title = getArguments().getString(TITLE);
mTrackerLabel = getArguments().getString(TRACKER_LABEL);
- int style = TextUtils.isEmpty(title) ? DialogFragment.STYLE_NO_TITLE
- : DialogFragment.STYLE_NORMAL;
+ int style =
+ TextUtils.isEmpty(title)
+ ? DialogFragment.STYLE_NO_TITLE
+ : DialogFragment.STYLE_NORMAL;
setStyle(style, 0);
}
@Nullable
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
String title = getArguments().getString(TITLE);
getDialog().setTitle(title);
diff --git a/src/com/android/tv/dvr/BaseDvrDataManager.java b/src/com/android/tv/dvr/BaseDvrDataManager.java
index a8637449..b8bffa18 100644
--- a/src/com/android/tv/dvr/BaseDvrDataManager.java
+++ b/src/com/android/tv/dvr/BaseDvrDataManager.java
@@ -23,15 +23,13 @@ import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.util.ArraySet;
import android.util.Log;
-
import com.android.tv.common.SoftPreconditions;
import com.android.tv.common.feature.CommonFeatures;
+import com.android.tv.common.util.Clock;
import com.android.tv.dvr.data.RecordedProgram;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.data.ScheduledRecording.RecordingState;
import com.android.tv.dvr.data.SeriesRecording;
-import com.android.tv.util.Clock;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -42,14 +40,12 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
-/**
- * Base implementation of @{link DataManagerInternal}.
- */
+/** 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;
+ private static final String TAG = "BaseDvrDataManager";
+ private static final boolean DEBUG = false;
protected final Clock mClock;
private final Set<OnDvrScheduleLoadFinishedListener> mOnDvrScheduleLoadFinishedListeners =
@@ -61,7 +57,7 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager {
private final Set<RecordedProgramListener> mRecordedProgramListeners = new ArraySet<>();
private final HashMap<Long, ScheduledRecording> mDeletedScheduleMap = new HashMap<>();
- BaseDvrDataManager(Context context, Clock clock) {
+ public BaseDvrDataManager(Context context, Clock clock) {
SoftPreconditions.checkFeatureEnabled(context, CommonFeatures.DVR, TAG);
mClock = clock;
}
@@ -129,8 +125,8 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager {
}
/**
- * Calls {@link OnRecordedProgramLoadFinishedListener#onRecordedProgramLoadFinished()}
- * for each listener.
+ * Calls {@link OnRecordedProgramLoadFinishedListener#onRecordedProgramLoadFinished()} for each
+ * listener.
*/
protected final void notifyRecordedProgramLoadFinished() {
for (OnRecordedProgramLoadFinishedListener l : mOnRecordedProgramLoadFinishedListeners) {
@@ -139,10 +135,7 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager {
}
}
- /**
- * Calls {@link RecordedProgramListener#onRecordedProgramsAdded}
- * for each listener.
- */
+ /** Calls {@link RecordedProgramListener#onRecordedProgramsAdded} for each listener. */
protected final void notifyRecordedProgramsAdded(RecordedProgram... recordedPrograms) {
for (RecordedProgramListener l : mRecordedProgramListeners) {
if (DEBUG) Log.d(TAG, "notify " + l + " added " + Arrays.asList(recordedPrograms));
@@ -150,10 +143,7 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager {
}
}
- /**
- * Calls {@link RecordedProgramListener#onRecordedProgramsChanged}
- * for each listener.
- */
+ /** Calls {@link RecordedProgramListener#onRecordedProgramsChanged} for each listener. */
protected final void notifyRecordedProgramsChanged(RecordedProgram... recordedPrograms) {
for (RecordedProgramListener l : mRecordedProgramListeners) {
if (DEBUG) Log.d(TAG, "notify " + l + " changed " + Arrays.asList(recordedPrograms));
@@ -161,10 +151,7 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager {
}
}
- /**
- * Calls {@link RecordedProgramListener#onRecordedProgramsRemoved}
- * for each listener.
- */
+ /** Calls {@link RecordedProgramListener#onRecordedProgramsRemoved} for each listener. */
protected final void notifyRecordedProgramsRemoved(RecordedProgram... recordedPrograms) {
for (RecordedProgramListener l : mRecordedProgramListeners) {
if (DEBUG) Log.d(TAG, "notify " + l + " removed " + Arrays.asList(recordedPrograms));
@@ -172,10 +159,7 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager {
}
}
- /**
- * Calls {@link SeriesRecordingListener#onSeriesRecordingAdded}
- * for each listener.
- */
+ /** Calls {@link SeriesRecordingListener#onSeriesRecordingAdded} for each listener. */
protected final void notifySeriesRecordingAdded(SeriesRecording... seriesRecordings) {
for (SeriesRecordingListener l : mSeriesRecordingListeners) {
if (DEBUG) Log.d(TAG, "notify " + l + " added " + Arrays.asList(seriesRecordings));
@@ -183,10 +167,7 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager {
}
}
- /**
- * Calls {@link SeriesRecordingListener#onSeriesRecordingRemoved}
- * for each listener.
- */
+ /** Calls {@link SeriesRecordingListener#onSeriesRecordingRemoved} for each listener. */
protected final void notifySeriesRecordingRemoved(SeriesRecording... seriesRecordings) {
for (SeriesRecordingListener l : mSeriesRecordingListeners) {
if (DEBUG) Log.d(TAG, "notify " + l + " removed " + Arrays.asList(seriesRecordings));
@@ -194,11 +175,7 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager {
}
}
- /**
- * Calls
- * {@link SeriesRecordingListener#onSeriesRecordingChanged}
- * for each listener.
- */
+ /** Calls {@link SeriesRecordingListener#onSeriesRecordingChanged} for each listener. */
protected final void notifySeriesRecordingChanged(SeriesRecording... seriesRecordings) {
for (SeriesRecordingListener l : mSeriesRecordingListeners) {
if (DEBUG) Log.d(TAG, "notify " + l + " changed " + Arrays.asList(seriesRecordings));
@@ -206,10 +183,7 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager {
}
}
- /**
- * Calls {@link ScheduledRecordingListener#onScheduledRecordingAdded}
- * for each listener.
- */
+ /** Calls {@link ScheduledRecordingListener#onScheduledRecordingAdded} for each listener. */
protected final void notifyScheduledRecordingAdded(ScheduledRecording... scheduledRecording) {
for (ScheduledRecordingListener l : mScheduledRecordingListeners) {
if (DEBUG) Log.d(TAG, "notify " + l + " added " + Arrays.asList(scheduledRecording));
@@ -217,10 +191,7 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager {
}
}
- /**
- * Calls {@link ScheduledRecordingListener#onScheduledRecordingRemoved}
- * for each listener.
- */
+ /** Calls {@link ScheduledRecordingListener#onScheduledRecordingRemoved} for each listener. */
protected final void notifyScheduledRecordingRemoved(ScheduledRecording... scheduledRecording) {
for (ScheduledRecordingListener l : mScheduledRecordingListeners) {
if (DEBUG) Log.d(TAG, "notify " + l + " removed " + Arrays.asList(scheduledRecording));
@@ -229,9 +200,7 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager {
}
/**
- * Calls
- * {@link ScheduledRecordingListener#onScheduledRecordingStatusChanged}
- * for each listener.
+ * Calls {@link ScheduledRecordingListener#onScheduledRecordingStatusChanged} for each listener.
*/
protected final void notifyScheduledRecordingStatusChanged(
ScheduledRecording... scheduledRecording) {
@@ -257,28 +226,47 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager {
@Override
public List<ScheduledRecording> getAvailableScheduledRecordings() {
- return filterEndTimeIsPast(getRecordingsWithState(
- ScheduledRecording.STATE_RECORDING_IN_PROGRESS,
- ScheduledRecording.STATE_RECORDING_NOT_STARTED));
+ return filterEndTimeIsPast(
+ getRecordingsWithState(
+ ScheduledRecording.STATE_RECORDING_IN_PROGRESS,
+ ScheduledRecording.STATE_RECORDING_NOT_STARTED));
}
@Override
public List<ScheduledRecording> getStartedRecordings() {
- return filterEndTimeIsPast(getRecordingsWithState(
- ScheduledRecording.STATE_RECORDING_IN_PROGRESS));
+ return filterEndTimeIsPast(
+ getRecordingsWithState(ScheduledRecording.STATE_RECORDING_IN_PROGRESS));
}
@Override
public List<ScheduledRecording> getNonStartedScheduledRecordings() {
- return filterEndTimeIsPast(getRecordingsWithState(
- ScheduledRecording.STATE_RECORDING_NOT_STARTED));
+ return filterEndTimeIsPast(
+ getRecordingsWithState(ScheduledRecording.STATE_RECORDING_NOT_STARTED));
+ }
+
+ @Override
+ public List<ScheduledRecording> getFailedScheduledRecordings() {
+ return getRecordingsWithState(ScheduledRecording.STATE_RECORDING_FAILED);
}
@Override
public void changeState(ScheduledRecording scheduledRecording, @RecordingState int newState) {
if (scheduledRecording.getState() != newState) {
- updateScheduledRecording(ScheduledRecording.buildFrom(scheduledRecording)
- .setState(newState).build());
+ updateScheduledRecording(
+ ScheduledRecording.buildFrom(scheduledRecording).setState(newState).build());
+ }
+ }
+
+ @Override
+ public void changeState(
+ ScheduledRecording scheduledRecording, @RecordingState int newState, int reason) {
+ if (scheduledRecording.getState() != newState) {
+ ScheduledRecording.Builder builder =
+ ScheduledRecording.buildFrom(scheduledRecording).setState(newState);
+ if (newState == ScheduledRecording.STATE_RECORDING_FAILED) {
+ builder.setFailedReason(reason);
+ }
+ updateScheduledRecording(builder.build());
}
}
@@ -300,9 +288,7 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager {
return mDeletedScheduleMap;
}
- /**
- * Returns the schedules whose state is contained by states.
- */
+ /** Returns the schedules whose state is contained by states. */
protected abstract List<ScheduledRecording> getRecordingsWithState(int... states);
@Override
@@ -357,5 +343,5 @@ public abstract class BaseDvrDataManager implements WritableDvrDataManager {
}
@Override
- public void forgetStorage(String inputId) { }
+ public void forgetStorage(String inputId) {}
}
diff --git a/src/com/android/tv/dvr/DvrDataManager.java b/src/com/android/tv/dvr/DvrDataManager.java
index 6d400b82..10dfc4c9 100644
--- a/src/com/android/tv/dvr/DvrDataManager.java
+++ b/src/com/android/tv/dvr/DvrDataManager.java
@@ -20,48 +20,36 @@ import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Range;
-
import com.android.tv.dvr.data.RecordedProgram;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.data.ScheduledRecording.RecordingState;
import com.android.tv.dvr.data.SeriesRecording;
-
import java.util.Collection;
import java.util.List;
-/**
- * Read only data manager.
- */
+/** Read only data manager. */
@MainThread
public interface DvrDataManager {
long NEXT_START_TIME_NOT_FOUND = -1;
boolean isInitialized();
- /**
- * Returns {@code true} if the schedules were loaded, otherwise {@code false}.
- */
+ /** Returns {@code true} if the schedules were loaded, otherwise {@code false}. */
boolean isDvrScheduleLoadFinished();
- /**
- * Returns {@code true} if the recorded programs were loaded, otherwise {@code false}.
- */
+ /** Returns {@code true} if the recorded programs were loaded, otherwise {@code false}. */
boolean isRecordedProgramLoadFinished();
- /**
- * Returns past recordings.
- */
+ /** Returns past recordings. */
List<RecordedProgram> getRecordedPrograms();
- /**
- * Returns past recorded programs in the given series.
- */
+ /** Returns past recorded programs in the given series. */
List<RecordedProgram> getRecordedPrograms(long seriesRecordingId);
/**
* Returns all {@link ScheduledRecording} regardless of state.
- * <p>
- * The result doesn't contain the deleted schedules.
+ *
+ * <p>The result doesn't contain the deleted schedules.
*/
List<ScheduledRecording> getAllScheduledRecordings();
@@ -71,29 +59,24 @@ public interface DvrDataManager {
*/
List<ScheduledRecording> getAvailableScheduledRecordings();
- /**
- * Returns started recordings that expired.
- */
+ /** Returns started recordings that expired. */
List<ScheduledRecording> getStartedRecordings();
- /**
- * Returns scheduled but not started recordings that have not expired.
- */
+ /** Returns scheduled but not started recordings that have not expired. */
List<ScheduledRecording> getNonStartedScheduledRecordings();
- /**
- * Returns series recordings.
- */
+ /** Returns failed recordings. */
+ List<ScheduledRecording> getFailedScheduledRecordings();
+
+ /** Returns series recordings. */
List<SeriesRecording> getSeriesRecordings();
- /**
- * Returns series recordings from the given input.
- */
+ /** Returns series recordings from the given input. */
List<SeriesRecording> getSeriesRecordings(String inputId);
/**
- * Returns the next start time after {@code time} or {@link #NEXT_START_TIME_NOT_FOUND}
- * if none is found.
+ * Returns the next start time after {@code time} or {@link #NEXT_START_TIME_NOT_FOUND} if none
+ * is found.
*
* @param time time milliseconds
*/
@@ -103,73 +86,48 @@ public interface DvrDataManager {
* Returns a list of the schedules with a overlap with the given time period inclusive and with
* the given state.
*
- * <p> A recording overlaps with a period when
- * {@code recording.getStartTime() <= period.getUpper() &&
- * recording.getEndTime() >= period.getLower()}.
+ * <p>A recording overlaps with a period when {@code recording.getStartTime() <=
+ * period.getUpper() && recording.getEndTime() >= period.getLower()}.
*
* @param period a time period in milliseconds.
* @param state the state of the schedule.
*/
List<ScheduledRecording> getScheduledRecordings(Range<Long> period, @RecordingState int state);
- /**
- * Returns a list of the schedules in the given series.
- */
+ /** Returns a list of the schedules in the given series. */
List<ScheduledRecording> getScheduledRecordings(long seriesRecordingId);
- /**
- * Returns a list of the schedules from the given input.
- */
+ /** Returns a list of the schedules from the given input. */
List<ScheduledRecording> getScheduledRecordings(String inputId);
- /**
- * Add a {@link OnDvrScheduleLoadFinishedListener}.
- */
+ /** Add a {@link OnDvrScheduleLoadFinishedListener}. */
void addDvrScheduleLoadFinishedListener(OnDvrScheduleLoadFinishedListener listener);
- /**
- * Remove a {@link OnDvrScheduleLoadFinishedListener}.
- */
+ /** Remove a {@link OnDvrScheduleLoadFinishedListener}. */
void removeDvrScheduleLoadFinishedListener(OnDvrScheduleLoadFinishedListener listener);
- /**
- * Add a {@link OnRecordedProgramLoadFinishedListener}.
- */
+ /** Add a {@link OnRecordedProgramLoadFinishedListener}. */
void addRecordedProgramLoadFinishedListener(OnRecordedProgramLoadFinishedListener listener);
- /**
- * Remove a {@link OnRecordedProgramLoadFinishedListener}.
- */
+ /** Remove a {@link OnRecordedProgramLoadFinishedListener}. */
void removeRecordedProgramLoadFinishedListener(OnRecordedProgramLoadFinishedListener listener);
- /**
- * Add a {@link ScheduledRecordingListener}.
- */
+ /** Add a {@link ScheduledRecordingListener}. */
void addScheduledRecordingListener(ScheduledRecordingListener scheduledRecordingListener);
- /**
- * Remove a {@link ScheduledRecordingListener}.
- */
+ /** Remove a {@link ScheduledRecordingListener}. */
void removeScheduledRecordingListener(ScheduledRecordingListener scheduledRecordingListener);
- /**
- * Add a {@link RecordedProgramListener}.
- */
+ /** Add a {@link RecordedProgramListener}. */
void addRecordedProgramListener(RecordedProgramListener listener);
- /**
- * Remove a {@link RecordedProgramListener}.
- */
+ /** Remove a {@link RecordedProgramListener}. */
void removeRecordedProgramListener(RecordedProgramListener listener);
- /**
- * Add a {@link ScheduledRecordingListener}.
- */
+ /** Add a {@link ScheduledRecordingListener}. */
void addSeriesRecordingListener(SeriesRecordingListener seriesRecordingListener);
- /**
- * Remove a {@link ScheduledRecordingListener}.
- */
+ /** Remove a {@link ScheduledRecordingListener}. */
void removeSeriesRecordingListener(SeriesRecordingListener seriesRecordingListener);
/**
@@ -178,65 +136,47 @@ public interface DvrDataManager {
@Nullable
ScheduledRecording getScheduledRecording(long recordingId);
- /**
- * Returns the scheduled recording program with the given programId or null if is not found.
- */
+ /** Returns the scheduled recording program with the given programId or null if is not found. */
@Nullable
ScheduledRecording getScheduledRecordingForProgramId(long programId);
- /**
- * Returns the recorded program 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
RecordedProgram getRecordedProgram(long recordingId);
- /**
- * Returns the series recording with the given seriesId or null if is not found.
- */
+ /** Returns the series recording with the given seriesId or null if is not found. */
@Nullable
SeriesRecording getSeriesRecording(long seriesRecordingId);
- /**
- * Returns the series recording with the given series ID or {@code null} if not found.
- */
+ /** Returns the series recording with the given series ID or {@code null} if not found. */
@Nullable
SeriesRecording getSeriesRecording(String seriesId);
- /**
- * Returns the schedules which are marked deleted.
- */
+ /** Returns the schedules which are marked deleted. */
Collection<ScheduledRecording> getDeletedSchedules();
- /**
- * Returns the program IDs which is not allowed to make a schedule automatically.
- */
+ /** Returns the program IDs which is not allowed to make a schedule automatically. */
@NonNull
Collection<Long> getDisallowedProgramIds();
/**
- * Checks each of the give series recordings to see if it's empty, i.e., it doesn't contains
- * any available schedules or recorded programs, and it's status is
- * {@link SeriesRecording#STATE_SERIES_STOPPED}; and removes those empty series recordings.
+ * Checks each of the give series recordings to see if it's empty, i.e., it doesn't contains any
+ * available schedules or recorded programs, and it's status is {@link
+ * SeriesRecording#STATE_SERIES_STOPPED}; and removes those empty series recordings.
*/
void checkAndRemoveEmptySeriesRecording(long... seriesRecordingIds);
- /**
- * Listens for the DVR schedules loading finished.
- */
+ /** Listens for the DVR schedules loading finished. */
interface OnDvrScheduleLoadFinishedListener {
void onDvrScheduleLoadFinished();
}
- /**
- * Listens for the recorded program loading finished.
- */
+ /** Listens for the recorded program loading finished. */
interface OnRecordedProgramLoadFinishedListener {
void onRecordedProgramLoadFinished();
}
- /**
- * Listens for changes to {@link ScheduledRecording}s.
- */
+ /** Listens for changes to {@link ScheduledRecording}s. */
interface ScheduledRecordingListener {
void onScheduledRecordingAdded(ScheduledRecording... scheduledRecordings);
@@ -250,9 +190,7 @@ public interface DvrDataManager {
void onScheduledRecordingStatusChanged(ScheduledRecording... scheduledRecordings);
}
- /**
- * Listens for changes to {@link SeriesRecording}s.
- */
+ /** Listens for changes to {@link SeriesRecording}s. */
interface SeriesRecordingListener {
void onSeriesRecordingAdded(SeriesRecording... seriesRecordings);
@@ -261,9 +199,7 @@ public interface DvrDataManager {
void onSeriesRecordingChanged(SeriesRecording... seriesRecordings);
}
- /**
- * Listens for changes to {@link RecordedProgram}s.
- */
+ /** Listens for changes to {@link RecordedProgram}s. */
interface RecordedProgramListener {
void onRecordedProgramsAdded(RecordedProgram... recordedPrograms);
diff --git a/src/com/android/tv/dvr/DvrDataManagerImpl.java b/src/com/android/tv/dvr/DvrDataManagerImpl.java
index 6094ca72..2b4ecbf5 100644
--- a/src/com/android/tv/dvr/DvrDataManagerImpl.java
+++ b/src/com/android/tv/dvr/DvrDataManagerImpl.java
@@ -38,10 +38,12 @@ import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import android.util.Range;
-
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
-import com.android.tv.dvr.DvrStorageStatusManager.OnStorageMountChangedListener;
+import com.android.tv.common.recording.RecordingStorageStatusManager;
+import com.android.tv.common.recording.RecordingStorageStatusManager.OnStorageMountChangedListener;
+import com.android.tv.common.util.Clock;
+import com.android.tv.common.util.CommonUtils;
import com.android.tv.dvr.data.IdGenerator;
import com.android.tv.dvr.data.RecordedProgram;
import com.android.tv.dvr.data.ScheduledRecording;
@@ -59,12 +61,9 @@ import com.android.tv.dvr.provider.DvrDbSync;
import com.android.tv.dvr.recorder.SeriesRecordingScheduler;
import com.android.tv.util.AsyncDbTask;
import com.android.tv.util.AsyncDbTask.AsyncRecordedProgramQueryTask;
-import com.android.tv.util.Clock;
import com.android.tv.util.Filter;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.TvUriMatcher;
-import com.android.tv.util.Utils;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -73,10 +72,9 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
+import java.util.concurrent.Executor;
-/**
- * DVR Data manager to handle recordings and schedules.
- */
+/** DVR Data manager to handle recordings and schedules. */
@MainThread
@TargetApi(Build.VERSION_CODES.N)
public class DvrDataManagerImpl extends BaseDvrDataManager {
@@ -98,52 +96,54 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
private final HashMap<Long, SeriesRecording> mSeriesRecordingsForRemovedInput = new HashMap<>();
private final Context mContext;
- private final ContentObserver mContentObserver = new ContentObserver(new Handler(
- Looper.getMainLooper())) {
- @Override
- public void onChange(boolean selfChange) {
- onChange(selfChange, null);
- }
+ private Executor mDbExecutor;
+ private final ContentObserver mContentObserver =
+ new ContentObserver(new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange) {
+ onChange(selfChange, null);
+ }
- @Override
- public void onChange(boolean selfChange, final @Nullable Uri uri) {
- RecordedProgramsQueryTask task = new RecordedProgramsQueryTask(
- mContext.getContentResolver(), uri);
- task.executeOnDbThread();
- mPendingTasks.add(task);
- }
- };
+ @Override
+ public void onChange(boolean selfChange, final @Nullable Uri uri) {
+ RecordedProgramsQueryTask task =
+ new RecordedProgramsQueryTask(mContext.getContentResolver(), uri);
+ task.executeOnDbThread();
+ mPendingTasks.add(task);
+ }
+ };
private boolean mDvrLoadFinished;
private boolean mRecordedProgramLoadFinished;
private final Set<AsyncTask> mPendingTasks = new ArraySet<>();
private DvrDbSync mDbSync;
- private DvrStorageStatusManager mStorageStatusManager;
+ private RecordingStorageStatusManager mStorageStatusManager;
- private final TvInputCallback mInputCallback = new TvInputCallback() {
- @Override
- public void onInputAdded(String inputId) {
- if (DEBUG) Log.d(TAG, "onInputAdded " + inputId);
- if (!isInputAvailable(inputId)) {
- if (DEBUG) Log.d(TAG, "Not available for recording");
- return;
- }
- unhideInput(inputId);
- }
+ private final TvInputCallback mInputCallback =
+ new TvInputCallback() {
+ @Override
+ public void onInputAdded(String inputId) {
+ if (DEBUG) Log.d(TAG, "onInputAdded " + inputId);
+ if (!isInputAvailable(inputId)) {
+ if (DEBUG) Log.d(TAG, "Not available for recording");
+ return;
+ }
+ unhideInput(inputId);
+ }
- @Override
- public void onInputRemoved(String inputId) {
- if (DEBUG) Log.d(TAG, "onInputRemoved " + inputId);
- hideInput(inputId);
- }
- };
+ @Override
+ public void onInputRemoved(String inputId) {
+ if (DEBUG) Log.d(TAG, "onInputRemoved " + inputId);
+ hideInput(inputId);
+ }
+ };
private final OnStorageMountChangedListener mStorageMountChangedListener =
new OnStorageMountChangedListener() {
@Override
public void onStorageMountChanged(boolean storageMounted) {
for (TvInputInfo input : mInputManager.getTvInputInfos(true, true)) {
- if (Utils.isBundledInput(input.getId())) {
+ if (CommonUtils.isBundledInput(input.getId())) {
if (storageMounted) {
unhideInput(input.getId());
} else {
@@ -154,8 +154,8 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
}
};
- private static <T> List<T> moveElements(HashMap<Long, T> from, HashMap<Long, T> to,
- Filter<T> filter) {
+ private static <T> List<T> moveElements(
+ HashMap<Long, T> from, HashMap<Long, T> to, Filter<T> filter) {
List<T> moved = new ArrayList<>();
Iterator<Entry<Long, T>> iter = from.entrySet().iterator();
while (iter.hasNext()) {
@@ -172,119 +172,139 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
public DvrDataManagerImpl(Context context, Clock clock) {
super(context, clock);
mContext = context;
- mInputManager = TvApplication.getSingletons(context).getTvInputManagerHelper();
- mStorageStatusManager = TvApplication.getSingletons(context).getDvrStorageStatusManager();
+ TvSingletons tvSingletons = TvSingletons.getSingletons(context);
+ mInputManager = tvSingletons.getTvInputManagerHelper();
+ mStorageStatusManager = tvSingletons.getRecordingStorageStatusManager();
+ mDbExecutor = tvSingletons.getDbExecutor();
}
public void start() {
mInputManager.addCallback(mInputCallback);
mStorageStatusManager.addListener(mStorageMountChangedListener);
- AsyncDvrQuerySeriesRecordingTask dvrQuerySeriesRecordingTask
- = new AsyncDvrQuerySeriesRecordingTask(mContext) {
- @Override
- protected void onCancelled(List<SeriesRecording> seriesRecordings) {
- mPendingTasks.remove(this);
- }
+ AsyncDvrQuerySeriesRecordingTask dvrQuerySeriesRecordingTask =
+ new AsyncDvrQuerySeriesRecordingTask(mContext) {
+ @Override
+ protected void onCancelled(List<SeriesRecording> seriesRecordings) {
+ mPendingTasks.remove(this);
+ }
- @Override
- protected void onPostExecute(List<SeriesRecording> seriesRecordings) {
- mPendingTasks.remove(this);
- long maxId = 0;
- HashSet<String> seriesIds = new HashSet<>();
- for (SeriesRecording r : seriesRecordings) {
- if (SoftPreconditions.checkState(!seriesIds.contains(r.getSeriesId()), TAG,
- "Skip loading series recording with duplicate series ID: " + r)) {
- seriesIds.add(r.getSeriesId());
- if (isInputAvailable(r.getInputId())) {
- mSeriesRecordings.put(r.getId(), r);
- mSeriesId2SeriesRecordings.put(r.getSeriesId(), r);
- } else {
- mSeriesRecordingsForRemovedInput.put(r.getId(), r);
+ @Override
+ protected void onPostExecute(List<SeriesRecording> seriesRecordings) {
+ mPendingTasks.remove(this);
+ long maxId = 0;
+ HashSet<String> seriesIds = new HashSet<>();
+ for (SeriesRecording r : seriesRecordings) {
+ if (SoftPreconditions.checkState(
+ !seriesIds.contains(r.getSeriesId()),
+ TAG,
+ "Skip loading series recording with duplicate series ID: "
+ + r)) {
+ seriesIds.add(r.getSeriesId());
+ if (isInputAvailable(r.getInputId())) {
+ mSeriesRecordings.put(r.getId(), r);
+ mSeriesId2SeriesRecordings.put(r.getSeriesId(), r);
+ } else {
+ mSeriesRecordingsForRemovedInput.put(r.getId(), r);
+ }
+ }
+ if (maxId < r.getId()) {
+ maxId = r.getId();
+ }
}
+ IdGenerator.SERIES_RECORDING.setMaxId(maxId);
}
- if (maxId < r.getId()) {
- maxId = r.getId();
- }
- }
- IdGenerator.SERIES_RECORDING.setMaxId(maxId);
- }
- };
+ };
dvrQuerySeriesRecordingTask.executeOnDbThread();
mPendingTasks.add(dvrQuerySeriesRecordingTask);
- AsyncDvrQueryScheduleTask dvrQueryScheduleTask
- = new AsyncDvrQueryScheduleTask(mContext) {
- @Override
- protected void onCancelled(List<ScheduledRecording> scheduledRecordings) {
- mPendingTasks.remove(this);
- }
+ AsyncDvrQueryScheduleTask dvrQueryScheduleTask =
+ new AsyncDvrQueryScheduleTask(mContext) {
+ @Override
+ protected void onCancelled(List<ScheduledRecording> scheduledRecordings) {
+ mPendingTasks.remove(this);
+ }
- @SuppressLint("SwitchIntDef")
- @Override
- protected void onPostExecute(List<ScheduledRecording> result) {
- mPendingTasks.remove(this);
- long maxId = 0;
- List<SeriesRecording> seriesRecordingsToAdd = new ArrayList<>();
- List<ScheduledRecording> toUpdate = new ArrayList<>();
- List<ScheduledRecording> toDelete = new ArrayList<>();
- for (ScheduledRecording r : result) {
- if (!isInputAvailable(r.getInputId())) {
- mScheduledRecordingsForRemovedInput.put(r.getId(), r);
- } else if (r.getState() == ScheduledRecording.STATE_RECORDING_DELETED) {
- getDeletedScheduleMap().put(r.getProgramId(), r);
- } else {
- mScheduledRecordings.put(r.getId(), r);
- if (r.getProgramId() != ScheduledRecording.ID_NOT_SET) {
- mProgramId2ScheduledRecordings.put(r.getProgramId(), r);
- }
- // Adjust the state of the schedules before DB loading is finished.
- switch (r.getState()) {
- case ScheduledRecording.STATE_RECORDING_IN_PROGRESS:
- if (r.getEndTimeMs() <= mClock.currentTimeMillis()) {
- toUpdate.add(ScheduledRecording.buildFrom(r)
- .setState(ScheduledRecording.STATE_RECORDING_FAILED)
- .build());
- } else {
- toUpdate.add(ScheduledRecording.buildFrom(r)
- .setState(
- ScheduledRecording.STATE_RECORDING_NOT_STARTED)
- .build());
+ @SuppressLint("SwitchIntDef")
+ @Override
+ protected void onPostExecute(List<ScheduledRecording> result) {
+ mPendingTasks.remove(this);
+ long maxId = 0;
+ int reasonNotStarted =
+ ScheduledRecording
+ .FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED;
+ List<ScheduledRecording> toUpdate = new ArrayList<>();
+ List<ScheduledRecording> toDelete = new ArrayList<>();
+ for (ScheduledRecording r : result) {
+ if (!isInputAvailable(r.getInputId())) {
+ mScheduledRecordingsForRemovedInput.put(r.getId(), r);
+ } else if (r.getState() == ScheduledRecording.STATE_RECORDING_DELETED) {
+ getDeletedScheduleMap().put(r.getProgramId(), r);
+ } else {
+ mScheduledRecordings.put(r.getId(), r);
+ if (r.getProgramId() != ScheduledRecording.ID_NOT_SET) {
+ mProgramId2ScheduledRecordings.put(r.getProgramId(), r);
}
- break;
- case ScheduledRecording.STATE_RECORDING_NOT_STARTED:
- if (r.getEndTimeMs() <= mClock.currentTimeMillis()) {
- toUpdate.add(ScheduledRecording.buildFrom(r)
- .setState(ScheduledRecording.STATE_RECORDING_FAILED)
- .build());
+ // Adjust the state of the schedules before DB loading is finished.
+ switch (r.getState()) {
+ case ScheduledRecording.STATE_RECORDING_IN_PROGRESS:
+ if (r.getEndTimeMs() <= mClock.currentTimeMillis()) {
+ int reason =
+ ScheduledRecording.FAILED_REASON_NOT_FINISHED;
+ toUpdate.add(
+ ScheduledRecording.buildFrom(r)
+ .setState(
+ ScheduledRecording
+ .STATE_RECORDING_FAILED)
+ .setFailedReason(reason)
+ .build());
+ } else {
+ toUpdate.add(
+ ScheduledRecording.buildFrom(r)
+ .setState(
+ ScheduledRecording
+ .STATE_RECORDING_NOT_STARTED)
+ .build());
+ }
+ break;
+ case ScheduledRecording.STATE_RECORDING_NOT_STARTED:
+ if (r.getEndTimeMs() <= mClock.currentTimeMillis()) {
+ toUpdate.add(
+ ScheduledRecording.buildFrom(r)
+ .setState(
+ ScheduledRecording
+ .STATE_RECORDING_FAILED)
+ .setFailedReason(reasonNotStarted)
+ .build());
+ }
+ break;
+ case ScheduledRecording.STATE_RECORDING_CANCELED:
+ toDelete.add(r);
+ break;
+ default: // fall out
}
- break;
- case ScheduledRecording.STATE_RECORDING_CANCELED:
- toDelete.add(r);
- break;
+ }
+ if (maxId < r.getId()) {
+ maxId = r.getId();
+ }
+ }
+ if (!toUpdate.isEmpty()) {
+ updateScheduledRecording(ScheduledRecording.toArray(toUpdate));
+ }
+ if (!toDelete.isEmpty()) {
+ removeScheduledRecording(ScheduledRecording.toArray(toDelete));
+ }
+ IdGenerator.SCHEDULED_RECORDING.setMaxId(maxId);
+ if (mRecordedProgramLoadFinished) {
+ validateSeriesRecordings();
+ }
+ mDvrLoadFinished = true;
+ notifyDvrScheduleLoadFinished();
+ if (isInitialized()) {
+ mDbSync = new DvrDbSync(mContext, DvrDataManagerImpl.this);
+ mDbSync.start();
+ SeriesRecordingScheduler.getInstance(mContext).start();
}
}
- if (maxId < r.getId()) {
- maxId = r.getId();
- }
- }
- if (!toUpdate.isEmpty()) {
- updateScheduledRecording(ScheduledRecording.toArray(toUpdate));
- }
- if (!toDelete.isEmpty()) {
- removeScheduledRecording(ScheduledRecording.toArray(toDelete));
- }
- IdGenerator.SCHEDULED_RECORDING.setMaxId(maxId);
- if (mRecordedProgramLoadFinished) {
- validateSeriesRecordings();
- }
- mDvrLoadFinished = true;
- notifyDvrScheduleLoadFinished();
- if (isInitialized()) {
- mDbSync = new DvrDbSync(mContext, DvrDataManagerImpl.this);
- mDbSync.start();
- SeriesRecordingScheduler.getInstance(mContext).start();
- }
- }
- };
+ };
dvrQueryScheduleTask.executeOnDbThread();
mPendingTasks.add(dvrQueryScheduleTask);
RecordedProgramsQueryTask mRecordedProgramQueryTask =
@@ -341,8 +361,8 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
mRecordedProgramsForRemovedInput.clear();
notifyRecordedProgramsRemoved(RecordedProgram.toArray(oldRecordedPrograms));
} else {
- HashMap<Long, RecordedProgram> oldRecordedPrograms
- = new HashMap<>(mRecordedPrograms);
+ HashMap<Long, RecordedProgram> oldRecordedPrograms =
+ new HashMap<>(mRecordedPrograms);
mRecordedPrograms.clear();
mRecordedProgramsForRemovedInput.clear();
List<RecordedProgram> addedRecordedPrograms = new ArrayList<>();
@@ -492,7 +512,8 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
}
@VisibleForTesting
- static long getNextStartTimeAfter(List<ScheduledRecording> scheduledRecordings, long startTime) {
+ static long getNextStartTimeAfter(
+ List<ScheduledRecording> scheduledRecordings, long startTime) {
int start = 0;
int end = scheduledRecordings.size() - 1;
while (start <= end) {
@@ -503,13 +524,14 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
end = mid - 1;
}
}
- return start < scheduledRecordings.size() ? scheduledRecordings.get(start).getStartTimeMs()
+ return start < scheduledRecordings.size()
+ ? scheduledRecordings.get(start).getStartTimeMs()
: NEXT_START_TIME_NOT_FOUND;
}
@Override
- public List<ScheduledRecording> getScheduledRecordings(Range<Long> period,
- @RecordingState int state) {
+ public List<ScheduledRecording> getScheduledRecordings(
+ Range<Long> period, @RecordingState int state) {
List<ScheduledRecording> result = new ArrayList<>();
for (ScheduledRecording r : mScheduledRecordings.values()) {
if (r.isOverLapping(period) && r.getState() == state) {
@@ -595,8 +617,11 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
r.setId(IdGenerator.SERIES_RECORDING.newId());
mSeriesRecordings.put(r.getId(), r);
SeriesRecording previousSeries = mSeriesId2SeriesRecordings.put(r.getSeriesId(), r);
- SoftPreconditions.checkArgument(previousSeries == null, TAG, "Attempt to add series"
- + " recording with the duplicate series ID: " + r.getSeriesId());
+ SoftPreconditions.checkArgument(
+ previousSeries == null,
+ TAG,
+ "Attempt to add series" + " recording with the duplicate series ID: %s",
+ r.getSeriesId());
}
if (mDvrLoadFinished) {
notifySeriesRecordingAdded(seriesRecordings);
@@ -620,20 +645,23 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
mProgramId2ScheduledRecordings.remove(r.getProgramId());
if (r.getSeriesRecordingId() != SeriesRecording.ID_NOT_SET
&& (r.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED
- || r.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS)) {
+ || r.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS)) {
seriesRecordingIdsToCheck.add(r.getSeriesRecordingId());
}
boolean isScheduleForRemovedInput =
mScheduledRecordingsForRemovedInput.remove(r.getProgramId()) != null;
// If it belongs to the series recording and it's not started yet, just mark delete
// instead of deleting it.
- if (!isScheduleForRemovedInput && !forceRemove
+ if (!isScheduleForRemovedInput
+ && !forceRemove
&& r.getSeriesRecordingId() != SeriesRecording.ID_NOT_SET
&& (r.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED
- || r.getState() == ScheduledRecording.STATE_RECORDING_CANCELED)) {
+ || r.getState() == ScheduledRecording.STATE_RECORDING_CANCELED)) {
SoftPreconditions.checkState(r.getProgramId() != ScheduledRecording.ID_NOT_SET);
- ScheduledRecording deleted = ScheduledRecording.buildFrom(r)
- .setState(ScheduledRecording.STATE_RECORDING_DELETED).build();
+ ScheduledRecording deleted =
+ ScheduledRecording.buildFrom(r)
+ .setState(ScheduledRecording.STATE_RECORDING_DELETED)
+ .build();
getDeletedScheduleMap().put(deleted.getProgramId(), deleted);
schedulesNotToDelete.add(deleted);
} else {
@@ -655,12 +683,12 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
}
}
if (!schedulesToDelete.isEmpty()) {
- new AsyncDeleteScheduleTask(mContext).executeOnDbThread(
- ScheduledRecording.toArray(schedulesToDelete));
+ new AsyncDeleteScheduleTask(mContext)
+ .executeOnDbThread(ScheduledRecording.toArray(schedulesToDelete));
}
if (!schedulesNotToDelete.isEmpty()) {
- new AsyncUpdateScheduleTask(mContext).executeOnDbThread(
- ScheduledRecording.toArray(schedulesNotToDelete));
+ new AsyncUpdateScheduleTask(mContext)
+ .executeOnDbThread(ScheduledRecording.toArray(schedulesNotToDelete));
}
}
@@ -680,8 +708,10 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
if (r.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED) {
toDelete.add(r);
} else {
- toUpdate.add(ScheduledRecording.buildFrom(r)
- .setSeriesRecordingId(SeriesRecording.ID_NOT_SET).build());
+ toUpdate.add(
+ ScheduledRecording.buildFrom(r)
+ .setSeriesRecordingId(SeriesRecording.ID_NOT_SET)
+ .build());
}
}
}
@@ -709,7 +739,9 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
List<ScheduledRecording> toUpdate = new ArrayList<>();
Set<Long> seriesRecordingIdsToCheck = new HashSet<>();
for (ScheduledRecording r : schedules) {
- if (!SoftPreconditions.checkState(mScheduledRecordings.containsKey(r.getId()), TAG,
+ if (!SoftPreconditions.checkState(
+ mScheduledRecordings.containsKey(r.getId()),
+ TAG,
"Recording not found for: " + r)) {
continue;
}
@@ -720,8 +752,8 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
long programId = r.getProgramId();
if (oldScheduledRecording.getProgramId() != programId
&& oldScheduledRecording.getProgramId() != ScheduledRecording.ID_NOT_SET) {
- ScheduledRecording oldValueForProgramId = mProgramId2ScheduledRecordings
- .get(oldScheduledRecording.getProgramId());
+ ScheduledRecording oldValueForProgramId =
+ mProgramId2ScheduledRecordings.get(oldScheduledRecording.getProgramId());
if (oldValueForProgramId.getId() == r.getId()) {
// Only remove the old ScheduledRecording if it has the same ID as the new one.
mProgramId2ScheduledRecordings.remove(oldScheduledRecording.getProgramId());
@@ -755,14 +787,17 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
@Override
public void updateSeriesRecording(final SeriesRecording... seriesRecordings) {
for (SeriesRecording r : seriesRecordings) {
- if (!SoftPreconditions.checkArgument(mSeriesRecordings.containsKey(r.getId()), TAG,
- "Non Existing Series ID: " + r)) {
+ if (!SoftPreconditions.checkArgument(
+ mSeriesRecordings.containsKey(r.getId()),
+ TAG,
+ "Non Existing Series ID: %s",
+ r)) {
continue;
}
SeriesRecording old1 = mSeriesRecordings.put(r.getId(), r);
SeriesRecording old2 = mSeriesId2SeriesRecordings.put(r.getSeriesId(), r);
- SoftPreconditions.checkArgument(old1.equals(old2), TAG, "Series ID cannot be"
- + " updated: " + r);
+ SoftPreconditions.checkArgument(
+ old1.equals(old2), TAG, "Series ID cannot be updated: %s", r);
}
if (mDvrLoadFinished) {
notifySeriesRecordingChanged(seriesRecordings);
@@ -772,7 +807,8 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
private boolean isInputAvailable(String inputId) {
return mInputManager.hasTvInputInfo(inputId)
- && (!Utils.isBundledInput(inputId) || mStorageStatusManager.isStorageMounted());
+ && (!CommonUtils.isBundledInput(inputId)
+ || mStorageStatusManager.isStorageMounted());
}
private void removeDeletedSchedules(ScheduledRecording... addedSchedules) {
@@ -784,8 +820,8 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
}
}
if (!schedulesToDelete.isEmpty()) {
- new AsyncDeleteScheduleTask(mContext).executeOnDbThread(
- ScheduledRecording.toArray(schedulesToDelete));
+ new AsyncDeleteScheduleTask(mContext)
+ .executeOnDbThread(ScheduledRecording.toArray(schedulesToDelete));
}
}
@@ -805,15 +841,17 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
}
}
if (!schedulesToDelete.isEmpty()) {
- new AsyncDeleteScheduleTask(mContext).executeOnDbThread(
- ScheduledRecording.toArray(schedulesToDelete));
+ new AsyncDeleteScheduleTask(mContext)
+ .executeOnDbThread(ScheduledRecording.toArray(schedulesToDelete));
}
}
private void unhideInput(String inputId) {
if (DEBUG) Log.d(TAG, "unhideInput " + inputId);
List<ScheduledRecording> movedSchedules =
- moveElements(mScheduledRecordingsForRemovedInput, mScheduledRecordings,
+ moveElements(
+ mScheduledRecordingsForRemovedInput,
+ mScheduledRecordings,
new Filter<ScheduledRecording>() {
@Override
public boolean filter(ScheduledRecording r) {
@@ -821,7 +859,9 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
}
});
List<RecordedProgram> movedRecordedPrograms =
- moveElements(mRecordedProgramsForRemovedInput, mRecordedPrograms,
+ moveElements(
+ mRecordedProgramsForRemovedInput,
+ mRecordedPrograms,
new Filter<RecordedProgram>() {
@Override
public boolean filter(RecordedProgram r) {
@@ -830,7 +870,9 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
});
List<SeriesRecording> removedSeriesRecordings = new ArrayList<>();
List<SeriesRecording> movedSeriesRecordings =
- moveElements(mSeriesRecordingsForRemovedInput, mSeriesRecordings,
+ moveElements(
+ mSeriesRecordingsForRemovedInput,
+ mSeriesRecordings,
new Filter<SeriesRecording>() {
@Override
public boolean filter(SeriesRecording r) {
@@ -856,8 +898,8 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
for (SeriesRecording r : removedSeriesRecordings) {
mSeriesRecordingsForRemovedInput.remove(r.getId());
}
- new AsyncDeleteSeriesRecordingTask(mContext).executeOnDbThread(
- SeriesRecording.toArray(removedSeriesRecordings));
+ new AsyncDeleteSeriesRecordingTask(mContext)
+ .executeOnDbThread(SeriesRecording.toArray(removedSeriesRecordings));
// Notify after all the data are moved.
if (!movedSchedules.isEmpty()) {
notifyScheduledRecordingAdded(ScheduledRecording.toArray(movedSchedules));
@@ -873,7 +915,9 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
private void hideInput(String inputId) {
if (DEBUG) Log.d(TAG, "hideInput " + inputId);
List<ScheduledRecording> movedSchedules =
- moveElements(mScheduledRecordings, mScheduledRecordingsForRemovedInput,
+ moveElements(
+ mScheduledRecordings,
+ mScheduledRecordingsForRemovedInput,
new Filter<ScheduledRecording>() {
@Override
public boolean filter(ScheduledRecording r) {
@@ -881,7 +925,9 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
}
});
List<SeriesRecording> movedSeriesRecordings =
- moveElements(mSeriesRecordings, mSeriesRecordingsForRemovedInput,
+ moveElements(
+ mSeriesRecordings,
+ mSeriesRecordingsForRemovedInput,
new Filter<SeriesRecording>() {
@Override
public boolean filter(SeriesRecording r) {
@@ -889,7 +935,9 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
}
});
List<RecordedProgram> movedRecordedPrograms =
- moveElements(mRecordedPrograms, mRecordedProgramsForRemovedInput,
+ moveElements(
+ mRecordedPrograms,
+ mRecordedProgramsForRemovedInput,
new Filter<RecordedProgram>() {
@Override
public boolean filter(RecordedProgram r) {
@@ -931,7 +979,8 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
public void forgetStorage(String inputId) {
List<ScheduledRecording> schedulesToDelete = new ArrayList<>();
for (Iterator<ScheduledRecording> i =
- mScheduledRecordingsForRemovedInput.values().iterator(); i.hasNext(); ) {
+ mScheduledRecordingsForRemovedInput.values().iterator();
+ i.hasNext(); ) {
ScheduledRecording r = i.next();
if (inputId.equals(r.getInputId())) {
schedulesToDelete.add(r);
@@ -939,32 +988,34 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
}
}
List<SeriesRecording> seriesRecordingsToDelete = new ArrayList<>();
- for (Iterator<SeriesRecording> i =
- mSeriesRecordingsForRemovedInput.values().iterator(); i.hasNext(); ) {
+ for (Iterator<SeriesRecording> i = mSeriesRecordingsForRemovedInput.values().iterator();
+ i.hasNext(); ) {
SeriesRecording r = i.next();
if (inputId.equals(r.getInputId())) {
seriesRecordingsToDelete.add(r);
i.remove();
}
}
- for (Iterator<RecordedProgram> i =
- mRecordedProgramsForRemovedInput.values().iterator(); i.hasNext(); ) {
+ for (Iterator<RecordedProgram> i = mRecordedProgramsForRemovedInput.values().iterator();
+ i.hasNext(); ) {
if (inputId.equals(i.next().getInputId())) {
i.remove();
}
}
- new AsyncDeleteScheduleTask(mContext).executeOnDbThread(
- ScheduledRecording.toArray(schedulesToDelete));
- new AsyncDeleteSeriesRecordingTask(mContext).executeOnDbThread(
- SeriesRecording.toArray(seriesRecordingsToDelete));
- new AsyncDbTask<Void, Void, Void>() {
+ new AsyncDeleteScheduleTask(mContext)
+ .executeOnDbThread(ScheduledRecording.toArray(schedulesToDelete));
+ new AsyncDeleteSeriesRecordingTask(mContext)
+ .executeOnDbThread(SeriesRecording.toArray(seriesRecordingsToDelete));
+ new AsyncDbTask<Void, Void, Void>(mDbExecutor) {
@Override
protected Void doInBackground(Void... params) {
ContentResolver resolver = mContext.getContentResolver();
- String args[] = { inputId };
+ String[] args = {inputId};
try {
- resolver.delete(RecordedPrograms.CONTENT_URI,
- RecordedPrograms.COLUMN_INPUT_ID + " = ?", args);
+ resolver.delete(
+ RecordedPrograms.CONTENT_URI,
+ RecordedPrograms.COLUMN_INPUT_ID + " = ?",
+ args);
} catch (SQLiteException e) {
Log.e(TAG, "Failed to delete recorded programs for inputId: " + inputId, e);
}
@@ -996,7 +1047,7 @@ public class DvrDataManagerImpl extends BaseDvrDataManager {
private final Uri mUri;
public RecordedProgramsQueryTask(ContentResolver contentResolver, Uri uri) {
- super(contentResolver, uri == null ? RecordedPrograms.CONTENT_URI : uri);
+ super(mDbExecutor, contentResolver, uri == null ? RecordedPrograms.CONTENT_URI : uri);
mUri = uri;
}
diff --git a/src/com/android/tv/dvr/DvrManager.java b/src/com/android/tv/dvr/DvrManager.java
index d222003d..63a245a3 100644
--- a/src/com/android/tv/dvr/DvrManager.java
+++ b/src/com/android/tv/dvr/DvrManager.java
@@ -36,13 +36,12 @@ import android.support.annotation.VisibleForTesting;
import android.support.annotation.WorkerThread;
import android.util.Log;
import android.util.Range;
-
-import com.android.tv.ApplicationSingletons;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
import com.android.tv.common.feature.CommonFeatures;
-import com.android.tv.data.Channel;
+import com.android.tv.common.util.CommonUtils;
import com.android.tv.data.Program;
+import com.android.tv.data.api.Channel;
import com.android.tv.dvr.DvrDataManager.OnRecordedProgramLoadFinishedListener;
import com.android.tv.dvr.DvrDataManager.RecordedProgramListener;
import com.android.tv.dvr.DvrScheduleManager.OnInitializeListener;
@@ -51,7 +50,6 @@ import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.data.SeriesRecording;
import com.android.tv.util.AsyncDbTask;
import com.android.tv.util.Utils;
-
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
@@ -60,6 +58,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.concurrent.Executor;
/**
* DVR manager class to add and remove recordings. UI can modify recording list through this class,
@@ -76,13 +75,15 @@ public class DvrManager {
// @GuardedBy("mListener")
private final Map<Listener, Handler> mListener = new HashMap<>();
private final Context mAppContext;
+ private final Executor mDbExecutor;
public DvrManager(Context context) {
SoftPreconditions.checkFeatureEnabled(context, CommonFeatures.DVR, TAG);
mAppContext = context.getApplicationContext();
- ApplicationSingletons appSingletons = TvApplication.getSingletons(context);
- mDataManager = (WritableDvrDataManager) appSingletons.getDvrDataManager();
- mScheduleManager = appSingletons.getDvrScheduleManager();
+ TvSingletons tvSingletons = TvSingletons.getSingletons(context);
+ mDbExecutor = tvSingletons.getDbExecutor();
+ mDataManager = (WritableDvrDataManager) tvSingletons.getDvrDataManager();
+ mScheduleManager = tvSingletons.getDvrScheduleManager();
if (mDataManager.isInitialized() && mScheduleManager.isInitialized()) {
createSeriesRecordingsForRecordedProgramsIfNeeded(mDataManager.getRecordedPrograms());
} else {
@@ -103,37 +104,41 @@ public class DvrManager {
});
}
if (!mScheduleManager.isInitialized()) {
- mScheduleManager.addOnInitializeListener(new OnInitializeListener() {
+ mScheduleManager.addOnInitializeListener(
+ new OnInitializeListener() {
+ @Override
+ public void onInitialize() {
+ mScheduleManager.removeOnInitializeListener(this);
+ if (mDataManager.isInitialized()
+ && mScheduleManager.isInitialized()) {
+ createSeriesRecordingsForRecordedProgramsIfNeeded(
+ mDataManager.getRecordedPrograms());
+ }
+ }
+ });
+ }
+ }
+ mDataManager.addRecordedProgramListener(
+ new RecordedProgramListener() {
@Override
- public void onInitialize() {
- mScheduleManager.removeOnInitializeListener(this);
- if (mDataManager.isInitialized() && mScheduleManager.isInitialized()) {
- createSeriesRecordingsForRecordedProgramsIfNeeded(
- mDataManager.getRecordedPrograms());
+ public void onRecordedProgramsAdded(RecordedProgram... recordedPrograms) {
+ if (!mDataManager.isInitialized() || !mScheduleManager.isInitialized()) {
+ return;
+ }
+ for (RecordedProgram recordedProgram : recordedPrograms) {
+ createSeriesRecordingForRecordedProgramIfNeeded(recordedProgram);
}
}
- });
- }
- }
- mDataManager.addRecordedProgramListener(new RecordedProgramListener() {
- @Override
- public void onRecordedProgramsAdded(RecordedProgram... recordedPrograms) {
- if (!mDataManager.isInitialized() || !mScheduleManager.isInitialized()) {
- return;
- }
- for (RecordedProgram recordedProgram : recordedPrograms) {
- createSeriesRecordingForRecordedProgramIfNeeded(recordedProgram);
- }
- }
- @Override
- public void onRecordedProgramsChanged(RecordedProgram... recordedPrograms) { }
+ @Override
+ public void onRecordedProgramsChanged(RecordedProgram... recordedPrograms) {}
- @Override
- public void onRecordedProgramsRemoved(RecordedProgram... recordedPrograms) {
- // Removing series recording is handled in the SeriesRecordingDetailsFragment.
- }
- });
+ @Override
+ public void onRecordedProgramsRemoved(RecordedProgram... recordedPrograms) {
+ // Removing series recording is handled in the
+ // SeriesRecordingDetailsFragment.
+ }
+ });
}
private void createSeriesRecordingsForRecordedProgramsIfNeeded(
@@ -153,33 +158,38 @@ public class DvrManager {
}
}
- /**
- * Schedules a recording for {@code program}.
- */
+ /** Schedules a recording for {@code program}. */
public ScheduledRecording addSchedule(Program program) {
if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
return null;
}
SeriesRecording seriesRecording = getSeriesRecording(program);
- return addSchedule(program, seriesRecording == null
- ? mScheduleManager.suggestNewPriority()
- : seriesRecording.getPriority());
+ return addSchedule(
+ program,
+ seriesRecording == null
+ ? mScheduleManager.suggestNewPriority()
+ : seriesRecording.getPriority());
}
/**
- * Schedules a recording for {@code program} with the highest priority so that the schedule
- * can be recorded.
+ * Schedules a recording for {@code program} with the highest priority so that the schedule can
+ * be recorded.
*/
public ScheduledRecording addScheduleWithHighestPriority(Program program) {
if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
return null;
}
SeriesRecording seriesRecording = getSeriesRecording(program);
- return addSchedule(program, seriesRecording == null
- ? mScheduleManager.suggestNewPriority()
- : mScheduleManager.suggestHighestPriority(seriesRecording.getInputId(),
- new Range(program.getStartTimeUtcMillis(), program.getEndTimeUtcMillis()),
- seriesRecording.getPriority()));
+ return addSchedule(
+ program,
+ seriesRecording == null
+ ? mScheduleManager.suggestNewPriority()
+ : mScheduleManager.suggestHighestPriority(
+ seriesRecording.getInputId(),
+ new Range(
+ program.getStartTimeUtcMillis(),
+ program.getEndTimeUtcMillis()),
+ seriesRecording.getPriority()));
}
private ScheduledRecording addSchedule(Program program, long priority) {
@@ -190,21 +200,28 @@ public class DvrManager {
}
ScheduledRecording schedule;
SeriesRecording seriesRecording = getSeriesRecording(program);
- schedule = createScheduledRecordingBuilder(input.getId(), program)
- .setPriority(priority)
- .setSeriesRecordingId(seriesRecording == null ? SeriesRecording.ID_NOT_SET
- : seriesRecording.getId())
- .build();
+ schedule =
+ createScheduledRecordingBuilder(input.getId(), program)
+ .setPriority(priority)
+ .setSeriesRecordingId(
+ seriesRecording == null
+ ? SeriesRecording.ID_NOT_SET
+ : seriesRecording.getId())
+ .build();
mDataManager.addScheduledRecording(schedule);
return schedule;
}
- /**
- * Adds a recording schedule with a time range.
- */
+ /** Adds a recording schedule with a time range. */
public void addSchedule(Channel channel, long startTime, long endTime) {
- Log.i(TAG, "Adding scheduled recording of channel " + channel + " starting at " +
- Utils.toTimeString(startTime) + " and ending at " + Utils.toTimeString(endTime));
+ Log.i(
+ TAG,
+ "Adding scheduled recording of channel "
+ + channel
+ + " starting at "
+ + Utils.toTimeString(startTime)
+ + " and ending at "
+ + Utils.toTimeString(endTime));
if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
return;
}
@@ -216,9 +233,7 @@ public class DvrManager {
addScheduleInternal(input.getId(), channel.getId(), startTime, endTime);
}
- /**
- * Adds the schedule.
- */
+ /** Adds the schedule. */
public void addSchedule(ScheduledRecording schedule) {
if (mDataManager.isDvrScheduleLoadFinished()) {
mDataManager.addScheduledRecording(schedule);
@@ -226,19 +241,23 @@ public class DvrManager {
}
private void addScheduleInternal(String inputId, long channelId, long startTime, long endTime) {
- mDataManager.addScheduledRecording(ScheduledRecording
- .builder(inputId, channelId, startTime, endTime)
- .setPriority(mScheduleManager.suggestNewPriority())
- .build());
+ mDataManager.addScheduledRecording(
+ ScheduledRecording.builder(inputId, channelId, startTime, endTime)
+ .setPriority(mScheduleManager.suggestNewPriority())
+ .build());
}
- /**
- * Adds a new series recording and schedules for the programs with the initial state.
- */
- public SeriesRecording addSeriesRecording(Program selectedProgram,
- List<Program> programsToSchedule, @SeriesRecording.SeriesState int initialState) {
- Log.i(TAG, "Adding series recording for program " + selectedProgram + ", and schedules: "
- + programsToSchedule);
+ /** Adds a new series recording and schedules for the programs with the initial state. */
+ public SeriesRecording addSeriesRecording(
+ Program selectedProgram,
+ List<Program> programsToSchedule,
+ @SeriesRecording.SeriesState int initialState) {
+ Log.i(
+ TAG,
+ "Adding series recording for program "
+ + selectedProgram
+ + ", and schedules: "
+ + programsToSchedule);
if (!SoftPreconditions.checkState(mDataManager.isInitialized())) {
return null;
}
@@ -247,10 +266,11 @@ public class DvrManager {
Log.e(TAG, "Can't find input for program: " + selectedProgram);
return null;
}
- SeriesRecording seriesRecording = SeriesRecording.builder(input.getId(), selectedProgram)
- .setPriority(mScheduleManager.suggestNewSeriesPriority())
- .setState(initialState)
- .build();
+ SeriesRecording seriesRecording =
+ SeriesRecording.builder(input.getId(), selectedProgram)
+ .setPriority(mScheduleManager.suggestNewSeriesPriority())
+ .setState(initialState)
+ .build();
mDataManager.addSeriesRecording(seriesRecording);
// The schedules for the recorded programs should be added not to create the schedule the
// duplicate episodes.
@@ -279,9 +299,11 @@ public class DvrManager {
// Duplicate schedules can exist, but they will be deleted in a few days. And it's
// also guaranteed that the schedules don't belong to any series recordings because
// there are no more than one series recordings which have the same program title.
- toAdd.add(ScheduledRecording.builder(recordedProgram)
- .setPriority(series.getPriority())
- .setSeriesRecordingId(series.getId()).build());
+ toAdd.add(
+ ScheduledRecording.builder(recordedProgram)
+ .setPriority(series.getPriority())
+ .setSeriesRecordingId(series.getId())
+ .build());
}
}
if (!toAdd.isEmpty()) {
@@ -291,11 +313,11 @@ public class DvrManager {
/**
* Adds {@link ScheduledRecording}s for the series recording.
- * <p>
- * This method doesn't add the series recording.
+ *
+ * <p>This method doesn't add the series recording.
*/
- public void addScheduleToSeriesRecording(SeriesRecording series,
- List<Program> programsToSchedule) {
+ public void addScheduleToSeriesRecording(
+ SeriesRecording series, List<Program> programsToSchedule) {
if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
return;
}
@@ -311,18 +333,20 @@ public class DvrManager {
mDataManager.getScheduledRecordingForProgramId(program.getId());
if (scheduleWithSameProgram != null) {
if (scheduleWithSameProgram.isNotStarted()) {
- ScheduledRecording r = ScheduledRecording.buildFrom(scheduleWithSameProgram)
- .setSeriesRecordingId(series.getId())
- .build();
+ ScheduledRecording r =
+ ScheduledRecording.buildFrom(scheduleWithSameProgram)
+ .setSeriesRecordingId(series.getId())
+ .build();
if (!r.equals(scheduleWithSameProgram)) {
toUpdate.add(r);
}
}
} else {
- toAdd.add(createScheduledRecordingBuilder(input.getId(), program)
- .setPriority(series.getPriority())
- .setSeriesRecordingId(series.getId())
- .build());
+ toAdd.add(
+ createScheduledRecordingBuilder(input.getId(), program)
+ .setPriority(series.getPriority())
+ .setSeriesRecordingId(series.getId())
+ .build());
}
}
if (!toAdd.isEmpty()) {
@@ -333,9 +357,7 @@ public class DvrManager {
}
}
- /**
- * Updates the series recording.
- */
+ /** Updates the series recording. */
public void updateSeriesRecording(SeriesRecording series) {
if (SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
SeriesRecording previousSeries = mDataManager.getSeriesRecording(series.getId());
@@ -344,7 +366,7 @@ public class DvrManager {
// schedules will be added by SeriesRecordingScheduler or by SeriesSettingsFragment.
if (previousSeries.getChannelOption() != series.getChannelOption()
|| (previousSeries.getChannelOption() == SeriesRecording.OPTION_CHANNEL_ONE
- && previousSeries.getChannelId() != series.getChannelId())) {
+ && previousSeries.getChannelId() != series.getChannelId())) {
List<ScheduledRecording> schedules =
mDataManager.getScheduledRecordings(series.getId());
List<ScheduledRecording> schedulesToRemove = new ArrayList<>();
@@ -365,20 +387,21 @@ public class DvrManager {
schedulesToRemove.add(deletedSchedule);
}
}
- mDataManager.removeScheduledRecording(true,
- ScheduledRecording.toArray(schedulesToRemove));
+ mDataManager.removeScheduledRecording(
+ true, ScheduledRecording.toArray(schedulesToRemove));
}
}
mDataManager.updateSeriesRecording(series);
- if (previousSeries == null
- || previousSeries.getPriority() != series.getPriority()) {
+ if (previousSeries == null || previousSeries.getPriority() != series.getPriority()) {
long priority = series.getPriority();
List<ScheduledRecording> schedulesToUpdate = new ArrayList<>();
- for (ScheduledRecording schedule
- : mDataManager.getScheduledRecordings(series.getId())) {
+ for (ScheduledRecording schedule :
+ mDataManager.getScheduledRecordings(series.getId())) {
if (schedule.isNotStarted() || schedule.isInProgress()) {
- schedulesToUpdate.add(ScheduledRecording.buildFrom(schedule)
- .setPriority(priority).build());
+ schedulesToUpdate.add(
+ ScheduledRecording.buildFrom(schedule)
+ .setPriority(priority)
+ .build());
}
}
if (!schedulesToUpdate.isEmpty()) {
@@ -411,28 +434,26 @@ public class DvrManager {
mDataManager.removeSeriesRecording(series);
}
- /**
- * Stops the currently recorded program
- */
+ /** Stops the currently recorded program */
public void stopRecording(final ScheduledRecording recording) {
if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
return;
}
synchronized (mListener) {
for (final Entry<Listener, Handler> entry : mListener.entrySet()) {
- entry.getValue().post(new Runnable() {
- @Override
- public void run() {
- entry.getKey().onStopRecordingRequested(recording);
- }
- });
+ entry.getValue()
+ .post(
+ new Runnable() {
+ @Override
+ public void run() {
+ entry.getKey().onStopRecordingRequested(recording);
+ }
+ });
}
}
}
- /**
- * Removes scheduled recordings or an existing recordings.
- */
+ /** Removes scheduled recordings or an existing recordings. */
public void removeScheduledRecording(ScheduledRecording... schedules) {
Log.i(TAG, "Removing " + Arrays.asList(schedules));
if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
@@ -447,9 +468,7 @@ public class DvrManager {
}
}
- /**
- * Removes scheduled recordings without changing to the DELETED state.
- */
+ /** Removes scheduled recordings without changing to the DELETED state. */
public void forceRemoveScheduledRecording(ScheduledRecording... schedules) {
Log.i(TAG, "Force removing " + Arrays.asList(schedules));
if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
@@ -464,9 +483,7 @@ public class DvrManager {
}
}
- /**
- * Removes the recorded program. It deletes the file if possible.
- */
+ /** Removes the recorded program. It deletes the file if possible. */
public void removeRecordedProgram(Uri recordedProgramUri) {
if (!SoftPreconditions.checkState(mDataManager.isInitialized())) {
return;
@@ -474,9 +491,7 @@ public class DvrManager {
removeRecordedProgram(ContentUris.parseId(recordedProgramUri));
}
- /**
- * Removes the recorded program. It deletes the file if possible.
- */
+ /** Removes the recorded program. It deletes the file if possible. */
public void removeRecordedProgram(long recordedProgramId) {
if (!SoftPreconditions.checkState(mDataManager.isInitialized())) {
return;
@@ -487,14 +502,12 @@ public class DvrManager {
}
}
- /**
- * Removes the recorded program. It deletes the file if possible.
- */
+ /** Removes the recorded program. It deletes the file if possible. */
public void removeRecordedProgram(final RecordedProgram recordedProgram) {
if (!SoftPreconditions.checkState(mDataManager.isInitialized())) {
return;
}
- new AsyncDbTask<Void, Void, Integer>() {
+ new AsyncDbTask<Void, Void, Integer>(mDbExecutor) {
@Override
protected Integer doInBackground(Void... params) {
ContentResolver resolver = mAppContext.getContentResolver();
@@ -526,7 +539,7 @@ public class DvrManager {
dbOperations.add(ContentProviderOperation.newDelete(r.getUri()).build());
}
}
- new AsyncDbTask<Void, Void, Boolean>() {
+ new AsyncDbTask<Void, Void, Boolean>(mDbExecutor) {
@Override
protected Boolean doInBackground(Void... params) {
ContentResolver resolver = mAppContext.getContentResolver();
@@ -556,9 +569,7 @@ public class DvrManager {
}.executeOnDbThread();
}
- /**
- * Updates the scheduled recording.
- */
+ /** Updates the scheduled recording. */
public void updateScheduledRecording(ScheduledRecording recording) {
if (SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
mDataManager.updateScheduledRecording(recording);
@@ -566,8 +577,8 @@ public class DvrManager {
}
/**
- * Returns priority ordered list of all scheduled recordings that will not be recorded if
- * this program is.
+ * Returns priority ordered list of all scheduled recordings that will not be recorded if this
+ * program is.
*
* @see DvrScheduleManager#getConflictingSchedules(Program)
*/
@@ -579,13 +590,13 @@ public class DvrManager {
}
/**
- * Returns priority ordered list of all scheduled recordings that will not be recorded if
- * this channel is.
+ * Returns priority ordered list of all scheduled recordings that will not be recorded if this
+ * channel is.
*
* @see DvrScheduleManager#getConflictingSchedules(long, long, long)
*/
- public List<ScheduledRecording> getConflictingSchedules(long channelId, long startTimeMs,
- long endTimeMs) {
+ public List<ScheduledRecording> getConflictingSchedules(
+ long channelId, long startTimeMs, long endTimeMs) {
if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
return Collections.emptyList();
}
@@ -595,8 +606,8 @@ public class DvrManager {
/**
* Checks if the schedule is conflicting.
*
- * <p>Note that the {@code schedule} should be the existing one. If not, this returns
- * {@code false}.
+ * <p>Note that the {@code schedule} should be the existing one. If not, this returns {@code
+ * false}.
*/
public boolean isConflicting(ScheduledRecording schedule) {
return schedule != null
@@ -605,8 +616,8 @@ public class DvrManager {
}
/**
- * Returns priority ordered list of all scheduled recording that will not be recorded if
- * this channel is tuned to.
+ * Returns priority ordered list of all scheduled recording that will not be recorded if this
+ * channel is tuned to.
*
* @see DvrScheduleManager#getConflictingSchedulesForTune
*/
@@ -617,22 +628,18 @@ public class DvrManager {
return mScheduleManager.getConflictingSchedulesForTune(channelId);
}
- /**
- * Sets the highest priority to the schedule.
- */
+ /** Sets the highest priority to the schedule. */
public void setHighestPriority(ScheduledRecording schedule) {
if (SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
long newPriority = mScheduleManager.suggestHighestPriority(schedule);
if (newPriority != schedule.getPriority()) {
- mDataManager.updateScheduledRecording(ScheduledRecording.buildFrom(schedule)
- .setPriority(newPriority).build());
+ mDataManager.updateScheduledRecording(
+ ScheduledRecording.buildFrom(schedule).setPriority(newPriority).build());
}
}
}
- /**
- * Suggests the higher priority than the schedules which overlap with {@code schedule}.
- */
+ /** Suggests the higher priority than the schedules which overlap with {@code schedule}. */
public long suggestHighestPriority(ScheduledRecording schedule) {
if (SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
return mScheduleManager.suggestHighestPriority(schedule);
@@ -642,9 +649,9 @@ public class DvrManager {
/**
* Returns {@code true} if the channel can be recorded.
- * <p>
- * Note that this method doesn't check the conflict of the schedule or available tuners.
- * This can be called from the UI before the schedules are loaded.
+ *
+ * <p>Note that this method doesn't check the conflict of the schedule or available tuners. This
+ * can be called from the UI before the schedules are loaded.
*/
public boolean isChannelRecordable(Channel channel) {
if (!mDataManager.isDvrScheduleLoadFinished() || channel == null) {
@@ -661,23 +668,27 @@ public class DvrManager {
if (!info.canRecord()) {
return false;
}
- Program program = TvApplication.getSingletons(mAppContext).getProgramDataManager()
- .getCurrentProgram(channel.getId());
+ Program program =
+ TvSingletons.getSingletons(mAppContext)
+ .getProgramDataManager()
+ .getCurrentProgram(channel.getId());
return program == null || !program.isRecordingProhibited();
}
/**
* Returns {@code true} if the program can be recorded.
- * <p>
- * Note that this method doesn't check the conflict of the schedule or available tuners.
- * This can be called from the UI before the schedules are loaded.
+ *
+ * <p>Note that this method doesn't check the conflict of the schedule or available tuners. This
+ * can be called from the UI before the schedules are loaded.
*/
public boolean isProgramRecordable(Program program) {
if (!mDataManager.isInitialized()) {
return false;
}
- Channel channel = TvApplication.getSingletons(mAppContext).getChannelDataManager()
- .getChannel(program.getChannelId());
+ Channel channel =
+ TvSingletons.getSingletons(mAppContext)
+ .getChannelDataManager()
+ .getChannel(program.getChannelId());
if (channel == null || channel.isRecordingProhibited()) {
return false;
}
@@ -691,8 +702,8 @@ public class DvrManager {
/**
* Returns the current recording for the channel.
- * <p>
- * This can be called from the UI before the schedules are loaded.
+ *
+ * <p>This can be called from the UI before the schedules are loaded.
*/
public ScheduledRecording getCurrentRecording(long channelId) {
if (!mDataManager.isDvrScheduleLoadFinished()) {
@@ -707,8 +718,8 @@ public class DvrManager {
}
/**
- * Returns schedules which is available (i.e., isNotStarted or isInProgress) and belongs to
- * the series recording {@code seriesRecordingId}.
+ * Returns schedules which is available (i.e., isNotStarted or isInProgress) and belongs to the
+ * series recording {@code seriesRecordingId}.
*/
public List<ScheduledRecording> getAvailableScheduledRecording(long seriesRecordingId) {
if (!mDataManager.isDvrScheduleLoadFinished()) {
@@ -723,9 +734,7 @@ public class DvrManager {
return schedules;
}
- /**
- * Returns the series recording related to the program.
- */
+ /** Returns the series recording related to the program. */
@Nullable
public SeriesRecording getSeriesRecording(Program program) {
if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
@@ -735,8 +744,8 @@ public class DvrManager {
}
/**
- * Returns if there are valid items. Valid item contains {@link RecordedProgram},
- * available {@link ScheduledRecording} and {@link SeriesRecording}.
+ * Returns if there are valid items. Valid item contains {@link RecordedProgram}, available
+ * {@link ScheduledRecording} and {@link SeriesRecording}.
*/
public boolean hasValidItems() {
return !(mDataManager.getRecordedPrograms().isEmpty()
@@ -768,8 +777,8 @@ public class DvrManager {
* Returns ScheduledRecording.builder based on {@code program}. If program is already started,
* recording started time is clipped to the current time.
*/
- private ScheduledRecording.Builder createScheduledRecordingBuilder(String inputId,
- Program program) {
+ private ScheduledRecording.Builder createScheduledRecordingBuilder(
+ String inputId, Program program) {
ScheduledRecording.Builder builder = ScheduledRecording.builder(inputId, program);
long time = System.currentTimeMillis();
if (program.getStartTimeUtcMillis() < time && time < program.getEndTimeUtcMillis()) {
@@ -778,13 +787,13 @@ public class DvrManager {
return builder;
}
- /**
- * Returns a schedule which matches to the given episode.
- */
- public ScheduledRecording getScheduledRecording(String title, String seasonNumber,
- String episodeNumber) {
- if (!SoftPreconditions.checkState(mDataManager.isInitialized()) || title == null
- || seasonNumber == null || episodeNumber == null) {
+ /** Returns a schedule which matches to the given episode. */
+ public ScheduledRecording getScheduledRecording(
+ String title, String seasonNumber, String episodeNumber) {
+ if (!SoftPreconditions.checkState(mDataManager.isInitialized())
+ || title == null
+ || seasonNumber == null
+ || episodeNumber == null) {
return null;
}
for (ScheduledRecording r : mDataManager.getAllScheduledRecordings()) {
@@ -797,13 +806,13 @@ public class DvrManager {
return null;
}
- /**
- * Returns a recorded program which is the same episode as the given {@code program}.
- */
- public RecordedProgram getRecordedProgram(String title, String seasonNumber,
- String episodeNumber) {
- if (!SoftPreconditions.checkState(mDataManager.isInitialized()) || title == null
- || seasonNumber == null || episodeNumber == null) {
+ /** Returns a recorded program which is the same episode as the given {@code program}. */
+ public RecordedProgram getRecordedProgram(
+ String title, String seasonNumber, String episodeNumber) {
+ if (!SoftPreconditions.checkState(mDataManager.isInitialized())
+ || title == null
+ || seasonNumber == null
+ || episodeNumber == null) {
return null;
}
for (RecordedProgram r : mDataManager.getRecordedPrograms()) {
@@ -820,13 +829,14 @@ public class DvrManager {
@WorkerThread
private void removeRecordedData(Uri dataUri) {
try {
- if (dataUri != null && ContentResolver.SCHEME_FILE.equals(dataUri.getScheme())
+ if (dataUri != null
+ && ContentResolver.SCHEME_FILE.equals(dataUri.getScheme())
&& dataUri.getPath() != null) {
File recordedProgramPath = new File(dataUri.getPath());
if (!recordedProgramPath.exists()) {
if (DEBUG) Log.d(TAG, "File to delete not exist: " + recordedProgramPath);
} else {
- Utils.deleteDirOrFile(recordedProgramPath);
+ CommonUtils.deleteDirOrFile(recordedProgramPath);
if (DEBUG) {
Log.d(TAG, "Sucessfully deleted files of the recorded program: " + dataUri);
}
@@ -834,16 +844,19 @@ public class DvrManager {
}
} catch (SecurityException e) {
if (DEBUG) {
- Log.d(TAG, "To delete this recorded program, please manually delete video data at"
- + "\nadb shell rm -rf " + dataUri);
+ Log.d(
+ TAG,
+ "To delete this recorded program, please manually delete video data at"
+ + "\nadb shell rm -rf "
+ + dataUri);
}
}
}
/**
* Remove all the records related to the input.
- * <p>
- * Note that this should be called after the input was removed.
+ *
+ * <p>Note that this should be called after the input was removed.
*/
public void forgetStorage(String inputId) {
if (mDataManager.isInitialized()) {
diff --git a/src/com/android/tv/dvr/DvrScheduleManager.java b/src/com/android/tv/dvr/DvrScheduleManager.java
index b72117aa..d5126b12 100644
--- a/src/com/android/tv/dvr/DvrScheduleManager.java
+++ b/src/com/android/tv/dvr/DvrScheduleManager.java
@@ -25,21 +25,18 @@ import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import android.util.ArraySet;
import android.util.Range;
-
-import com.android.tv.ApplicationSingletons;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
-import com.android.tv.data.Channel;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.Program;
+import com.android.tv.data.api.Channel;
import com.android.tv.dvr.DvrDataManager.OnDvrScheduleLoadFinishedListener;
import com.android.tv.dvr.DvrDataManager.ScheduledRecordingListener;
-import com.android.tv.dvr.recorder.InputTaskScheduler;
-import com.android.tv.util.CompositeComparator;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.data.SeriesRecording;
+import com.android.tv.dvr.recorder.InputTaskScheduler;
+import com.android.tv.util.CompositeComparator;
import com.android.tv.util.Utils;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -50,21 +47,16 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
-/**
- * A class to manage the schedules.
- */
+/** A class to manage the schedules. */
@TargetApi(Build.VERSION_CODES.N)
@MainThread
+@SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated
public class DvrScheduleManager {
private static final String TAG = "DvrScheduleManager";
- /**
- * The default priority of scheduled recording.
- */
+ /** The default priority of scheduled recording. */
public static final long DEFAULT_PRIORITY = Long.MAX_VALUE >> 1;
- /**
- * The default priority of series recording.
- */
+ /** The default priority of series recording. */
public static final long DEFAULT_SERIES_PRIORITY = DEFAULT_PRIORITY >> 1;
// The new priority will have the offset from the existing one.
private static final long PRIORITY_OFFSET = 1024;
@@ -102,9 +94,9 @@ public class DvrScheduleManager {
public DvrScheduleManager(Context context) {
mContext = context;
- ApplicationSingletons appSingletons = TvApplication.getSingletons(context);
- mDataManager = (DvrDataManagerImpl) appSingletons.getDvrDataManager();
- mChannelDataManager = appSingletons.getChannelDataManager();
+ TvSingletons tvSingletons = TvSingletons.getSingletons(context);
+ mDataManager = (DvrDataManagerImpl) tvSingletons.getDvrDataManager();
+ mChannelDataManager = tvSingletons.getChannelDataManager();
if (mDataManager.isDvrScheduleLoadFinished() && mChannelDataManager.isDbLoadFinished()) {
buildData();
} else {
@@ -119,146 +111,151 @@ public class DvrScheduleManager {
}
});
}
- ScheduledRecordingListener scheduledRecordingListener = new ScheduledRecordingListener() {
- @Override
- public void onScheduledRecordingAdded(ScheduledRecording... scheduledRecordings) {
- if (!mInitialized) {
- return;
- }
- for (ScheduledRecording schedule : scheduledRecordings) {
- if (!schedule.isNotStarted() && !schedule.isInProgress()) {
- continue;
- }
- TvInputInfo input = Utils
- .getTvInputInfoForInputId(mContext, schedule.getInputId());
- if (!SoftPreconditions.checkArgument(input != null, TAG,
- "Input was removed for : " + schedule)) {
- // Input removed.
- mInputScheduleMap.remove(schedule.getInputId());
- mInputConflictInfoMap.remove(schedule.getInputId());
- continue;
- }
- String inputId = input.getId();
- List<ScheduledRecording> schedules = mInputScheduleMap.get(inputId);
- if (schedules == null) {
- schedules = new ArrayList<>();
- mInputScheduleMap.put(inputId, schedules);
+ ScheduledRecordingListener scheduledRecordingListener =
+ new ScheduledRecordingListener() {
+ @Override
+ public void onScheduledRecordingAdded(
+ ScheduledRecording... scheduledRecordings) {
+ if (!mInitialized) {
+ return;
+ }
+ for (ScheduledRecording schedule : scheduledRecordings) {
+ if (!schedule.isNotStarted() && !schedule.isInProgress()) {
+ continue;
+ }
+ TvInputInfo input =
+ Utils.getTvInputInfoForInputId(mContext, schedule.getInputId());
+ if (!SoftPreconditions.checkArgument(
+ input != null, TAG, "Input was removed for : %s", schedule)) {
+ // Input removed.
+ mInputScheduleMap.remove(schedule.getInputId());
+ mInputConflictInfoMap.remove(schedule.getInputId());
+ continue;
+ }
+ String inputId = input.getId();
+ List<ScheduledRecording> schedules = mInputScheduleMap.get(inputId);
+ if (schedules == null) {
+ schedules = new ArrayList<>();
+ mInputScheduleMap.put(inputId, schedules);
+ }
+ schedules.add(schedule);
+ }
+ onSchedulesChanged();
+ notifyScheduledRecordingAdded(scheduledRecordings);
}
- schedules.add(schedule);
- }
- onSchedulesChanged();
- notifyScheduledRecordingAdded(scheduledRecordings);
- }
- @Override
- public void onScheduledRecordingRemoved(ScheduledRecording... scheduledRecordings) {
- if (!mInitialized) {
- return;
- }
- for (ScheduledRecording schedule : scheduledRecordings) {
- TvInputInfo input = Utils
- .getTvInputInfoForInputId(mContext, schedule.getInputId());
- if (input == null) {
- // Input removed.
- mInputScheduleMap.remove(schedule.getInputId());
- mInputConflictInfoMap.remove(schedule.getInputId());
- continue;
- }
- String inputId = input.getId();
- List<ScheduledRecording> schedules = mInputScheduleMap.get(inputId);
- if (schedules != null) {
- schedules.remove(schedule);
- if (schedules.isEmpty()) {
- mInputScheduleMap.remove(inputId);
+ @Override
+ public void onScheduledRecordingRemoved(
+ ScheduledRecording... scheduledRecordings) {
+ if (!mInitialized) {
+ return;
}
- }
- Map<Long, ConflictInfo> conflictInfo = mInputConflictInfoMap.get(inputId);
- if (conflictInfo != null) {
- conflictInfo.remove(schedule.getId());
- if (conflictInfo.isEmpty()) {
- mInputConflictInfoMap.remove(inputId);
+ for (ScheduledRecording schedule : scheduledRecordings) {
+ TvInputInfo input =
+ Utils.getTvInputInfoForInputId(mContext, schedule.getInputId());
+ if (input == null) {
+ // Input removed.
+ mInputScheduleMap.remove(schedule.getInputId());
+ mInputConflictInfoMap.remove(schedule.getInputId());
+ continue;
+ }
+ String inputId = input.getId();
+ List<ScheduledRecording> schedules = mInputScheduleMap.get(inputId);
+ if (schedules != null) {
+ schedules.remove(schedule);
+ if (schedules.isEmpty()) {
+ mInputScheduleMap.remove(inputId);
+ }
+ }
+ Map<Long, ConflictInfo> conflictInfo =
+ mInputConflictInfoMap.get(inputId);
+ if (conflictInfo != null) {
+ conflictInfo.remove(schedule.getId());
+ if (conflictInfo.isEmpty()) {
+ mInputConflictInfoMap.remove(inputId);
+ }
+ }
}
+ onSchedulesChanged();
+ notifyScheduledRecordingRemoved(scheduledRecordings);
}
- }
- onSchedulesChanged();
- notifyScheduledRecordingRemoved(scheduledRecordings);
- }
- @Override
- public void onScheduledRecordingStatusChanged(
- ScheduledRecording... scheduledRecordings) {
- if (!mInitialized) {
- return;
- }
- for (ScheduledRecording schedule : scheduledRecordings) {
- TvInputInfo input = Utils
- .getTvInputInfoForInputId(mContext, schedule.getInputId());
- if (!SoftPreconditions.checkArgument(input != null, TAG,
- "Input was removed for : " + schedule)) {
- // Input removed.
- mInputScheduleMap.remove(schedule.getInputId());
- mInputConflictInfoMap.remove(schedule.getInputId());
- continue;
- }
- String inputId = input.getId();
- List<ScheduledRecording> schedules = mInputScheduleMap.get(inputId);
- if (schedules == null) {
- schedules = new ArrayList<>();
- mInputScheduleMap.put(inputId, schedules);
- }
- // Compare ID because ScheduledRecording.equals() doesn't work if the state
- // is changed.
- for (Iterator<ScheduledRecording> i = schedules.iterator(); i.hasNext(); ) {
- if (i.next().getId() == schedule.getId()) {
- i.remove();
- break;
+ @Override
+ public void onScheduledRecordingStatusChanged(
+ ScheduledRecording... scheduledRecordings) {
+ if (!mInitialized) {
+ return;
}
- }
- if (schedule.isNotStarted() || schedule.isInProgress()) {
- schedules.add(schedule);
- }
- if (schedules.isEmpty()) {
- mInputScheduleMap.remove(inputId);
- }
- // Update conflict list as well
- Map<Long, ConflictInfo> conflictInfo = mInputConflictInfoMap.get(inputId);
- if (conflictInfo != null) {
- ConflictInfo oldConflictInfo = conflictInfo.get(schedule.getId());
- if (oldConflictInfo != null) {
- oldConflictInfo.schedule = schedule;
+ for (ScheduledRecording schedule : scheduledRecordings) {
+ TvInputInfo input =
+ Utils.getTvInputInfoForInputId(mContext, schedule.getInputId());
+ if (!SoftPreconditions.checkArgument(
+ input != null, TAG, "Input was removed for : %s", schedule)) {
+ // Input removed.
+ mInputScheduleMap.remove(schedule.getInputId());
+ mInputConflictInfoMap.remove(schedule.getInputId());
+ continue;
+ }
+ String inputId = input.getId();
+ List<ScheduledRecording> schedules = mInputScheduleMap.get(inputId);
+ if (schedules == null) {
+ schedules = new ArrayList<>();
+ mInputScheduleMap.put(inputId, schedules);
+ }
+ // Compare ID because ScheduledRecording.equals() doesn't work if the
+ // state
+ // is changed.
+ for (Iterator<ScheduledRecording> i = schedules.iterator();
+ i.hasNext(); ) {
+ if (i.next().getId() == schedule.getId()) {
+ i.remove();
+ break;
+ }
+ }
+ if (schedule.isNotStarted() || schedule.isInProgress()) {
+ schedules.add(schedule);
+ }
+ if (schedules.isEmpty()) {
+ mInputScheduleMap.remove(inputId);
+ }
+ // Update conflict list as well
+ Map<Long, ConflictInfo> conflictInfo =
+ mInputConflictInfoMap.get(inputId);
+ if (conflictInfo != null) {
+ ConflictInfo oldConflictInfo = conflictInfo.get(schedule.getId());
+ if (oldConflictInfo != null) {
+ oldConflictInfo.schedule = schedule;
+ }
+ }
}
+ onSchedulesChanged();
+ notifyScheduledRecordingStatusChanged(scheduledRecordings);
}
- }
- onSchedulesChanged();
- notifyScheduledRecordingStatusChanged(scheduledRecordings);
- }
- };
+ };
mDataManager.addScheduledRecordingListener(scheduledRecordingListener);
- ChannelDataManager.Listener channelDataManagerListener = new ChannelDataManager.Listener() {
- @Override
- public void onLoadFinished() {
- if (mDataManager.isDvrScheduleLoadFinished() && !mInitialized) {
- buildData();
- }
- }
+ ChannelDataManager.Listener channelDataManagerListener =
+ new ChannelDataManager.Listener() {
+ @Override
+ public void onLoadFinished() {
+ if (mDataManager.isDvrScheduleLoadFinished() && !mInitialized) {
+ buildData();
+ }
+ }
- @Override
- public void onChannelListUpdated() {
- if (mDataManager.isDvrScheduleLoadFinished()) {
- buildData();
- }
- }
+ @Override
+ public void onChannelListUpdated() {
+ if (mDataManager.isDvrScheduleLoadFinished()) {
+ buildData();
+ }
+ }
- @Override
- public void onChannelBrowsableChanged() {
- }
- };
+ @Override
+ public void onChannelBrowsableChanged() {}
+ };
mChannelDataManager.addListener(channelDataManagerListener);
}
- /**
- * Returns the started recordings for the given input.
- */
+ /** Returns the started recordings for the given input. */
private List<ScheduledRecording> getStartedRecordings(String inputId) {
if (!SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet")) {
return Collections.emptyList();
@@ -337,39 +334,29 @@ public class DvrScheduleManager {
}
}
- /**
- * Returns {@code true} if this class has been initialized.
- */
+ /** Returns {@code true} if this class has been initialized. */
public boolean isInitialized() {
return mInitialized;
}
- /**
- * Adds a {@link ScheduledRecordingListener}.
- */
+ /** Adds a {@link ScheduledRecordingListener}. */
public final void addScheduledRecordingListener(ScheduledRecordingListener listener) {
mScheduledRecordingListeners.add(listener);
}
- /**
- * Removes a {@link ScheduledRecordingListener}.
- */
+ /** Removes a {@link ScheduledRecordingListener}. */
public final void removeScheduledRecordingListener(ScheduledRecordingListener listener) {
mScheduledRecordingListeners.remove(listener);
}
- /**
- * Calls {@link ScheduledRecordingListener#onScheduledRecordingAdded} for each listener.
- */
+ /** Calls {@link ScheduledRecordingListener#onScheduledRecordingAdded} for each listener. */
private void notifyScheduledRecordingAdded(ScheduledRecording... scheduledRecordings) {
for (ScheduledRecordingListener l : mScheduledRecordingListeners) {
l.onScheduledRecordingAdded(scheduledRecordings);
}
}
- /**
- * Calls {@link ScheduledRecordingListener#onScheduledRecordingRemoved} for each listener.
- */
+ /** Calls {@link ScheduledRecordingListener#onScheduledRecordingRemoved} for each listener. */
private void notifyScheduledRecordingRemoved(ScheduledRecording... scheduledRecordings) {
for (ScheduledRecordingListener l : mScheduledRecordingListeners) {
l.onScheduledRecordingRemoved(scheduledRecordings);
@@ -385,48 +372,36 @@ public class DvrScheduleManager {
}
}
- /**
- * Adds a {@link OnInitializeListener}.
- */
+ /** Adds a {@link OnInitializeListener}. */
public final void addOnInitializeListener(OnInitializeListener listener) {
mOnInitializeListeners.add(listener);
}
- /**
- * Removes a {@link OnInitializeListener}.
- */
+ /** Removes a {@link OnInitializeListener}. */
public final void removeOnInitializeListener(OnInitializeListener listener) {
mOnInitializeListeners.remove(listener);
}
- /**
- * Calls {@link OnInitializeListener#onInitialize} for each listener.
- */
+ /** Calls {@link OnInitializeListener#onInitialize} for each listener. */
private void notifyInitialize() {
for (OnInitializeListener l : mOnInitializeListeners) {
l.onInitialize();
}
}
- /**
- * Adds a {@link OnConflictStateChangeListener}.
- */
+ /** Adds a {@link OnConflictStateChangeListener}. */
public final void addOnConflictStateChangeListener(OnConflictStateChangeListener listener) {
mOnConflictStateChangeListeners.add(listener);
}
- /**
- * Removes a {@link OnConflictStateChangeListener}.
- */
+ /** Removes a {@link OnConflictStateChangeListener}. */
public final void removeOnConflictStateChangeListener(OnConflictStateChangeListener listener) {
mOnConflictStateChangeListeners.remove(listener);
}
- /**
- * Calls {@link OnConflictStateChangeListener#onConflictStateChange} for each listener.
- */
- private void notifyConflictStateChange(boolean conflict,
- ScheduledRecording... scheduledRecordings) {
+ /** Calls {@link OnConflictStateChangeListener#onConflictStateChange} for each listener. */
+ private void notifyConflictStateChange(
+ boolean conflict, ScheduledRecording... scheduledRecordings) {
for (OnConflictStateChangeListener l : mOnConflictStateChangeListeners) {
l.onConflictStateChange(conflict, scheduledRecordings);
}
@@ -434,8 +409,8 @@ public class DvrScheduleManager {
/**
* Returns the priority for the program if it is recorded.
- * <p>
- * The recording will have the higher priority than the existing ones.
+ *
+ * <p>The recording will have the higher priority than the existing ones.
*/
public long suggestNewPriority() {
if (!SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet")) {
@@ -454,9 +429,7 @@ public class DvrScheduleManager {
return highestPriority + PRIORITY_OFFSET;
}
- /**
- * Suggests the higher priority than the schedules which overlap with {@code schedule}.
- */
+ /** Suggests the higher priority than the schedules which overlap with {@code schedule}. */
public long suggestHighestPriority(ScheduledRecording schedule) {
List<ScheduledRecording> schedules = mInputScheduleMap.get(schedule.getInputId());
if (schedules == null) {
@@ -464,7 +437,8 @@ public class DvrScheduleManager {
}
long highestPriority = Long.MIN_VALUE;
for (ScheduledRecording r : schedules) {
- if (!r.equals(schedule) && r.isOverLapping(schedule)
+ if (!r.equals(schedule)
+ && r.isOverLapping(schedule)
&& r.getPriority() > highestPriority) {
highestPriority = r.getPriority();
}
@@ -475,9 +449,7 @@ public class DvrScheduleManager {
return highestPriority + PRIORITY_OFFSET;
}
- /**
- * Suggests the higher priority than the schedules which overlap with {@code schedule}.
- */
+ /** Suggests the higher priority than the schedules which overlap with {@code schedule}. */
public long suggestHighestPriority(String inputId, Range<Long> peroid, long basePriority) {
List<ScheduledRecording> schedules = mInputScheduleMap.get(inputId);
if (schedules == null) {
@@ -497,8 +469,8 @@ public class DvrScheduleManager {
/**
* Returns the priority for a series recording.
- * <p>
- * The recording will have the higher priority than the existing series.
+ *
+ * <p>The recording will have the higher priority than the existing series.
*/
public long suggestNewSeriesPriority() {
if (!SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet")) {
@@ -510,7 +482,7 @@ public class DvrScheduleManager {
/**
* Returns the priority for a series recording by order of series recording priority.
*
- * Higher order will have higher priority.
+ * <p>Higher order will have higher priority.
*/
public static long suggestSeriesPriority(int order) {
return DEFAULT_SERIES_PRIORITY + order * PRIORITY_OFFSET;
@@ -527,21 +499,23 @@ public class DvrScheduleManager {
}
/**
- * Returns a sorted list of all scheduled recordings that will not be recorded if
- * this program is going to be recorded, with their priorities in decending order.
- * <p>
- * An empty list means there is no conflicts. If there is conflict, a priority higher than
+ * Returns a sorted list of all scheduled recordings that will not be recorded if this program
+ * is going to be recorded, with their priorities in decending order.
+ *
+ * <p>An empty list means there is no conflicts. If there is conflict, a priority higher than
* the first recording in the returned list should be assigned to the new schedule of this
* program to guarantee the program would be completely recorded.
*/
public List<ScheduledRecording> getConflictingSchedules(Program program) {
SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet");
- SoftPreconditions.checkState(Program.isValid(program), TAG,
- "Program is invalid: " + program);
SoftPreconditions.checkState(
- program.getStartTimeUtcMillis() < program.getEndTimeUtcMillis(), TAG,
+ Program.isProgramValid(program), TAG, "Program is invalid: " + program);
+ SoftPreconditions.checkState(
+ program.getStartTimeUtcMillis() < program.getEndTimeUtcMillis(),
+ TAG,
"Program duration is empty: " + program);
- if (!mInitialized || !Program.isValid(program)
+ if (!mInitialized
+ || !Program.isProgramValid(program)
|| program.getStartTimeUtcMillis() >= program.getEndTimeUtcMillis()) {
return Collections.emptyList();
}
@@ -549,17 +523,19 @@ public class DvrScheduleManager {
if (input == null || !input.canRecord() || input.getTunerCount() <= 0) {
return Collections.emptyList();
}
- return getConflictingSchedules(input, Collections.singletonList(
- ScheduledRecording.builder(input.getId(), program)
- .setPriority(suggestHighestPriority())
- .build()));
+ return getConflictingSchedules(
+ input,
+ Collections.singletonList(
+ ScheduledRecording.builder(input.getId(), program)
+ .setPriority(suggestHighestPriority())
+ .build()));
}
/**
* Returns list of all conflicting scheduled recordings for the given {@code seriesRecording}
* recording.
- * <p>
- * Any empty list means there is no conflicts.
+ *
+ * <p>Any empty list means there is no conflicts.
*/
public List<ScheduledRecording> getConflictingSchedules(SeriesRecording seriesRecording) {
SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet");
@@ -571,8 +547,8 @@ public class DvrScheduleManager {
if (input == null || !input.canRecord() || input.getTunerCount() <= 0) {
return Collections.emptyList();
}
- List<ScheduledRecording> scheduledRecordingForSeries = mDataManager.getScheduledRecordings(
- seriesRecording.getId());
+ List<ScheduledRecording> scheduledRecordingForSeries =
+ mDataManager.getScheduledRecordings(seriesRecording.getId());
List<ScheduledRecording> availableScheduledRecordingForSeries = new ArrayList<>();
for (ScheduledRecording scheduledRecording : scheduledRecordingForSeries) {
if (scheduledRecording.isNotStarted() || scheduledRecording.isInProgress()) {
@@ -586,15 +562,15 @@ public class DvrScheduleManager {
}
/**
- * Returns a sorted list of all scheduled recordings that will not be recorded if
- * this channel is going to be recorded, with their priority in decending order.
- * <p>
- * An empty list means there is no conflicts. If there is conflict, a priority higher than
+ * Returns a sorted list of all scheduled recordings that will not be recorded if this channel
+ * is going to be recorded, with their priority in decending order.
+ *
+ * <p>An empty list means there is no conflicts. If there is conflict, a priority higher than
* the first recording in the returned list should be assigned to the new schedule of this
* channel to guarantee the channel would be completely recorded in the designated time range.
*/
- public List<ScheduledRecording> getConflictingSchedules(long channelId, long startTimeMs,
- long endTimeMs) {
+ public List<ScheduledRecording> getConflictingSchedules(
+ long channelId, long startTimeMs, long endTimeMs) {
SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet");
SoftPreconditions.checkState(channelId != Channel.INVALID_ID, TAG, "Invalid channel ID");
SoftPreconditions.checkState(startTimeMs < endTimeMs, TAG, "Recording duration is empty.");
@@ -605,10 +581,12 @@ public class DvrScheduleManager {
if (input == null || !input.canRecord() || input.getTunerCount() <= 0) {
return Collections.emptyList();
}
- return getConflictingSchedules(input, Collections.singletonList(
- ScheduledRecording.builder(input.getId(), channelId, startTimeMs, endTimeMs)
- .setPriority(suggestHighestPriority())
- .build()));
+ return getConflictingSchedules(
+ input,
+ Collections.singletonList(
+ ScheduledRecording.builder(input.getId(), channelId, startTimeMs, endTimeMs)
+ .setPriority(suggestHighestPriority())
+ .build()));
}
/**
@@ -633,14 +611,14 @@ public class DvrScheduleManager {
/**
* Checks if the schedule is conflicting.
*
- * <p>Note that the {@code schedule} should be the existing one. If not, this returns
- * {@code false}.
+ * <p>Note that the {@code schedule} should be the existing one. If not, this returns {@code
+ * false}.
*/
public boolean isConflicting(ScheduledRecording schedule) {
SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet");
TvInputInfo input = Utils.getTvInputInfoForInputId(mContext, schedule.getInputId());
- SoftPreconditions.checkState(input != null, TAG, "Can't find input for channel ID : "
- + schedule.getChannelId());
+ SoftPreconditions.checkState(
+ input != null, TAG, "Can't find input for channel ID : " + schedule.getChannelId());
if (!mInitialized || input == null) {
return false;
}
@@ -651,15 +629,15 @@ public class DvrScheduleManager {
/**
* Checks if the schedule is partially conflicting, i.e., part of the scheduled program might be
* recorded even if the priority of the schedule is not raised.
- * <p>
- * If the given schedule is not conflicting or is totally conflicting, i.e., cannot be recorded
- * at all, this method returns {@code false} in both cases.
+ *
+ * <p>If the given schedule is not conflicting or is totally conflicting, i.e., cannot be
+ * recorded at all, this method returns {@code false} in both cases.
*/
public boolean isPartiallyConflicting(@NonNull ScheduledRecording schedule) {
SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet");
TvInputInfo input = Utils.getTvInputInfoForInputId(mContext, schedule.getInputId());
- SoftPreconditions.checkState(input != null, TAG, "Can't find input for channel ID : "
- + schedule.getChannelId());
+ SoftPreconditions.checkState(
+ input != null, TAG, "Can't find input for channel ID : " + schedule.getChannelId());
if (!mInitialized || input == null) {
return false;
}
@@ -672,27 +650,35 @@ public class DvrScheduleManager {
}
/**
- * Returns priority ordered list of all scheduled recordings that will not be recorded if
- * this channel is tuned to.
+ * Returns priority ordered list of all scheduled recordings that will not be recorded if this
+ * channel is tuned to.
*/
public List<ScheduledRecording> getConflictingSchedulesForTune(long channelId) {
SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet");
SoftPreconditions.checkState(channelId != Channel.INVALID_ID, TAG, "Invalid channel ID");
TvInputInfo input = Utils.getTvInputInfoForChannelId(mContext, channelId);
- SoftPreconditions.checkState(input != null, TAG, "Can't find input for channel ID: "
- + channelId);
+ SoftPreconditions.checkState(
+ input != null, TAG, "Can't find input for channel ID: " + channelId);
if (!mInitialized || channelId == Channel.INVALID_ID || input == null) {
return Collections.emptyList();
}
- return getConflictingSchedulesForTune(input.getId(), channelId, System.currentTimeMillis(),
- suggestHighestPriority(), getStartedRecordings(input.getId()),
+ return getConflictingSchedulesForTune(
+ input.getId(),
+ channelId,
+ System.currentTimeMillis(),
+ suggestHighestPriority(),
+ getStartedRecordings(input.getId()),
input.getTunerCount());
}
@VisibleForTesting
- public static List<ScheduledRecording> getConflictingSchedulesForTune(String inputId,
- long channelId, long currentTimeMs, long newPriority,
- List<ScheduledRecording> startedRecordings, int tunerCount) {
+ public static List<ScheduledRecording> getConflictingSchedulesForTune(
+ String inputId,
+ long channelId,
+ long currentTimeMs,
+ long newPriority,
+ List<ScheduledRecording> startedRecordings,
+ int tunerCount) {
boolean channelFound = false;
for (ScheduledRecording schedule : startedRecordings) {
if (schedule.getChannelId() == channelId) {
@@ -704,10 +690,10 @@ public class DvrScheduleManager {
if (!channelFound) {
// The current channel is not being recorded.
schedules = new ArrayList<>(startedRecordings);
- schedules.add(ScheduledRecording
- .builder(inputId, channelId, currentTimeMs, currentTimeMs + 1)
- .setPriority(newPriority)
- .build());
+ schedules.add(
+ ScheduledRecording.builder(inputId, channelId, currentTimeMs, currentTimeMs + 1)
+ .setPriority(newPriority)
+ .build());
} else {
schedules = startedRecordings;
}
@@ -715,17 +701,17 @@ public class DvrScheduleManager {
}
/**
- * Returns priority ordered list of all scheduled recordings that will not be recorded if
- * the user keeps watching this channel.
- * <p>
- * Note that if the user keeps watching the channel, the channel can be recorded.
+ * Returns priority ordered list of all scheduled recordings that will not be recorded if the
+ * user keeps watching this channel.
+ *
+ * <p>Note that if the user keeps watching the channel, the channel can be recorded.
*/
public List<ScheduledRecording> getConflictingSchedulesForWatching(long channelId) {
SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet");
SoftPreconditions.checkState(channelId != Channel.INVALID_ID, TAG, "Invalid channel ID");
TvInputInfo input = Utils.getTvInputInfoForChannelId(mContext, channelId);
- SoftPreconditions.checkState(input != null, TAG, "Can't find input for channel ID: "
- + channelId);
+ SoftPreconditions.checkState(
+ input != null, TAG, "Can't find input for channel ID: " + channelId);
if (!mInitialized || channelId == Channel.INVALID_ID || input == null) {
return Collections.emptyList();
}
@@ -733,12 +719,17 @@ public class DvrScheduleManager {
if (schedules == null || schedules.isEmpty()) {
return Collections.emptyList();
}
- return getConflictingSchedulesForWatching(input.getId(), channelId,
- System.currentTimeMillis(), suggestNewPriority(), schedules, input.getTunerCount());
+ return getConflictingSchedulesForWatching(
+ input.getId(),
+ channelId,
+ System.currentTimeMillis(),
+ suggestNewPriority(),
+ schedules,
+ input.getTunerCount());
}
- private List<ScheduledRecording> getConflictingSchedules(TvInputInfo input,
- List<ScheduledRecording> schedulesToAdd) {
+ private List<ScheduledRecording> getConflictingSchedules(
+ TvInputInfo input, List<ScheduledRecording> schedulesToAdd) {
SoftPreconditions.checkNotNull(input);
if (input == null || !input.canRecord() || input.getTunerCount() <= 0) {
return Collections.emptyList();
@@ -751,9 +742,13 @@ public class DvrScheduleManager {
}
@VisibleForTesting
- static List<ScheduledRecording> getConflictingSchedulesForWatching(String inputId,
- long channelId, long currentTimeMs, long newPriority,
- @NonNull List<ScheduledRecording> schedules, int tunerCount) {
+ static List<ScheduledRecording> getConflictingSchedulesForWatching(
+ String inputId,
+ long channelId,
+ long currentTimeMs,
+ long newPriority,
+ @NonNull List<ScheduledRecording> schedules,
+ int tunerCount) {
List<ScheduledRecording> schedulesToCheck = new ArrayList<>(schedules);
List<ScheduledRecording> schedulesSameChannel = new ArrayList<>();
for (ScheduledRecording schedule : schedules) {
@@ -763,10 +758,10 @@ public class DvrScheduleManager {
}
}
// Assume that the user will watch the current channel forever.
- schedulesToCheck.add(ScheduledRecording
- .builder(inputId, channelId, currentTimeMs, Long.MAX_VALUE)
- .setPriority(newPriority)
- .build());
+ schedulesToCheck.add(
+ ScheduledRecording.builder(inputId, channelId, currentTimeMs, Long.MAX_VALUE)
+ .setPriority(newPriority)
+ .build());
List<ScheduledRecording> result = new ArrayList<>();
result.addAll(getConflictingSchedules(schedulesSameChannel, 1));
result.addAll(getConflictingSchedules(schedulesToCheck, tunerCount));
@@ -775,8 +770,10 @@ public class DvrScheduleManager {
}
@VisibleForTesting
- static List<ScheduledRecording> getConflictingSchedules(List<ScheduledRecording> schedulesToAdd,
- List<ScheduledRecording> currentSchedules, int tunerCount) {
+ static List<ScheduledRecording> getConflictingSchedules(
+ List<ScheduledRecording> schedulesToAdd,
+ List<ScheduledRecording> currentSchedules,
+ int tunerCount) {
List<ScheduledRecording> schedulesToCheck = new ArrayList<>(currentSchedules);
// When the duplicate schedule is to be added, remove the current duplicate recording.
for (Iterator<ScheduledRecording> iter = schedulesToCheck.iterator(); iter.hasNext(); ) {
@@ -805,9 +802,7 @@ public class DvrScheduleManager {
return getConflictingSchedules(schedulesToCheck, tunerCount, ranges);
}
- /**
- * Returns all conflicting scheduled recordings for the given schedules and count of tuner.
- */
+ /** Returns all conflicting scheduled recordings for the given schedules and count of tuner. */
public static List<ScheduledRecording> getConflictingSchedules(
List<ScheduledRecording> schedules, int tunerCount) {
return getConflictingSchedules(schedules, tunerCount, null);
@@ -825,21 +820,21 @@ public class DvrScheduleManager {
}
@VisibleForTesting
- static List<ConflictInfo> getConflictingSchedulesInfo(List<ScheduledRecording> schedules,
- int tunerCount) {
+ static List<ConflictInfo> getConflictingSchedulesInfo(
+ List<ScheduledRecording> schedules, int tunerCount) {
return getConflictingSchedulesInfo(schedules, tunerCount, null);
}
/**
* This is the core method to calculate all the conflicting schedules (in given periods).
- * <p>
- * Note that this method will ignore duplicated schedules with a same hash code. (Please refer
- * to {@link ScheduledRecording#hashCode}.)
+ *
+ * <p>Note that this method will ignore duplicated schedules with a same hash code. (Please
+ * refer to {@link ScheduledRecording#hashCode}.)
*
* @return A {@link HashMap} from {@link ScheduledRecording} to {@link Boolean}. The boolean
- * value denotes if the scheduled recording is partially conflicting, i.e., is possible
- * to be partially recorded under the given schedules and tuner count {@code true},
- * or not {@code false}.
+ * value denotes if the scheduled recording is partially conflicting, i.e., is possible to
+ * be partially recorded under the given schedules and tuner count {@code true}, or not
+ * {@code false}.
*/
private static List<ConflictInfo> getConflictingSchedulesInfo(
List<ScheduledRecording> schedules, int tunerCount, List<Range<Long>> periods) {
@@ -886,14 +881,19 @@ public class DvrScheduleManager {
if (earliestEndTime < schedule.getEndTimeMs()) {
// The schedule can starts when other recording ends even though it's
// clipped.
- ScheduledRecording modifiedSchedule = ScheduledRecording.buildFrom(schedule)
- .setStartTimeMs(earliestEndTime).build();
+ ScheduledRecording modifiedSchedule =
+ ScheduledRecording.buildFrom(schedule)
+ .setStartTimeMs(earliestEndTime)
+ .build();
ScheduledRecording originalSchedule =
modified2OriginalSchedules.getOrDefault(schedule, schedule);
modified2OriginalSchedules.put(modifiedSchedule, originalSchedule);
- int insertPosition = Collections.binarySearch(schedulesToCheck,
- modifiedSchedule,
- ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR);
+ int insertPosition =
+ Collections.binarySearch(
+ schedulesToCheck,
+ modifiedSchedule,
+ ScheduledRecording
+ .START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR);
if (insertPosition >= 0) {
schedulesToCheck.add(insertPosition, modifiedSchedule);
} else {
@@ -921,17 +921,19 @@ public class DvrScheduleManager {
}
}
List<ConflictInfo> result = new ArrayList<>(conflicts.values());
- Collections.sort(result, new Comparator<ConflictInfo>() {
- @Override
- public int compare(ConflictInfo lhs, ConflictInfo rhs) {
- return RESULT_COMPARATOR.compare(lhs.schedule, rhs.schedule);
- }
- });
+ Collections.sort(
+ result,
+ new Comparator<ConflictInfo>() {
+ @Override
+ public int compare(ConflictInfo lhs, ConflictInfo rhs) {
+ return RESULT_COMPARATOR.compare(lhs.schedule, rhs.schedule);
+ }
+ });
return result;
}
- private static void removeFinishedRecordings(List<ScheduledRecording> recordings,
- long currentTimeMs) {
+ private static void removeFinishedRecordings(
+ List<ScheduledRecording> recordings, long currentTimeMs) {
for (Iterator<ScheduledRecording> iter = recordings.iterator(); iter.hasNext(); ) {
if (iter.next().getEndTimeMs() <= currentTimeMs) {
iter.remove();
@@ -939,11 +941,9 @@ public class DvrScheduleManager {
}
}
- /**
- * @see InputTaskScheduler#getReplacableTask
- */
- private static ScheduledRecording findReplaceableRecording(List<ScheduledRecording> recordings,
- ScheduledRecording schedule) {
+ /** @see InputTaskScheduler#getReplacableTask */
+ private static ScheduledRecording findReplaceableRecording(
+ List<ScheduledRecording> recordings, ScheduledRecording schedule) {
// Returns the recording with the following priority.
// 1. The recording with the lowest priority is returned.
// 2. If the priorities are the same, the recording which finishes early is returned.
@@ -980,30 +980,24 @@ public class DvrScheduleManager {
}
}
- /**
- * A listener which is notified the initialization of schedule manager.
- */
+ /** A listener which is notified the initialization of schedule manager. */
public interface OnInitializeListener {
- /**
- * Called when the schedule manager has been initialized.
- */
+ /** Called when the schedule manager has been initialized. */
void onInitialize();
}
- /**
- * A listener which is notified the conflict state change of the schedules.
- */
+ /** A listener which is notified the conflict state change of the schedules. */
public interface OnConflictStateChangeListener {
/**
* Called when the conflicting schedules change.
- * <p>
- * Note that this can be called before
- * {@link ScheduledRecordingListener#onScheduledRecordingAdded} is called.
+ *
+ * <p>Note that this can be called before {@link
+ * ScheduledRecordingListener#onScheduledRecordingAdded} is called.
*
* @param conflict {@code true} if the {@code schedules} are the new conflicts, otherwise
- * {@code false}.
+ * {@code false}.
* @param schedules the schedules
*/
void onConflictStateChange(boolean conflict, ScheduledRecording... schedules);
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/DvrStorageStatusManager.java b/src/com/android/tv/dvr/DvrStorageStatusManager.java
index 2d41d732..ed8d6903 100644
--- a/src/com/android/tv/dvr/DvrStorageStatusManager.java
+++ b/src/com/android/tv/dvr/DvrStorageStatusManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -11,291 +11,55 @@
* 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
+ * limitations under the License.
*/
-
package com.android.tv.dvr;
-import android.content.BroadcastReceiver;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.OperationApplicationException;
import android.database.Cursor;
-import android.media.tv.TvContract;
import android.media.tv.TvInputInfo;
import android.net.Uri;
import android.os.AsyncTask;
-import android.os.Environment;
-import android.os.Looper;
import android.os.RemoteException;
-import android.os.StatFs;
-import android.support.annotation.AnyThread;
-import android.support.annotation.IntDef;
-import android.support.annotation.WorkerThread;
+import android.support.media.tv.TvContractCompat;
import android.util.Log;
-
-import com.android.tv.TvApplication;
-import com.android.tv.common.SoftPreconditions;
-import com.android.tv.common.feature.CommonFeatures;
-import com.android.tv.tuner.tvinput.TunerTvInputService;
+import com.android.tv.TvSingletons;
+import com.android.tv.common.recording.RecordingStorageStatusManager;
+import com.android.tv.common.util.CommonUtils;
import com.android.tv.util.TvInputManagerHelper;
-import com.android.tv.util.Utils;
-
import java.io.File;
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.CopyOnWriteArraySet;
-/**
- * Signals DVR storage status change such as plugging/unplugging.
- */
-public class DvrStorageStatusManager {
+/** A class for extending TV app-specific function to {@link RecordingStorageStatusManager}. */
+public class DvrStorageStatusManager extends RecordingStorageStatusManager {
private static final String TAG = "DvrStorageStatusManager";
- private static final boolean DEBUG = false;
-
- /**
- * Minimum storage size to support DVR
- */
- public static final long MIN_STORAGE_SIZE_FOR_DVR_IN_BYTES = 50 * 1024 * 1024 * 1024L; // 50GB
- private static final long MIN_FREE_STORAGE_SIZE_FOR_DVR_IN_BYTES
- = 10 * 1024 * 1024 * 1024L; // 10GB
- private static final String RECORDING_DATA_SUB_PATH = "/recording";
-
- private static final String[] PROJECTION = {
- TvContract.RecordedPrograms._ID,
- TvContract.RecordedPrograms.COLUMN_PACKAGE_NAME,
- TvContract.RecordedPrograms.COLUMN_RECORDING_DATA_URI
- };
- private final static int BATCH_OPERATION_COUNT = 100;
-
- @IntDef({STORAGE_STATUS_OK, STORAGE_STATUS_TOTAL_CAPACITY_TOO_SMALL,
- STORAGE_STATUS_FREE_SPACE_INSUFFICIENT, STORAGE_STATUS_MISSING})
- @Retention(RetentionPolicy.SOURCE)
- public @interface StorageStatus {
- }
-
- /**
- * Current storage is OK to record a program.
- */
- public static final int STORAGE_STATUS_OK = 0;
-
- /**
- * Current storage's total capacity is smaller than DVR requirement.
- */
- public static final int STORAGE_STATUS_TOTAL_CAPACITY_TOO_SMALL = 1;
-
- /**
- * Current storage's free space is insufficient to record programs.
- */
- public static final int STORAGE_STATUS_FREE_SPACE_INSUFFICIENT = 2;
-
- /**
- * Current storage is missing.
- */
- public static final int STORAGE_STATUS_MISSING = 3;
private final Context mContext;
- private final Set<OnStorageMountChangedListener> mOnStorageMountChangedListeners =
- new CopyOnWriteArraySet<>();
- private final boolean mRunningInMainProcess;
- private MountedStorageStatus mMountedStorageStatus;
- private boolean mStorageValid;
private CleanUpDbTask mCleanUpDbTask;
- private class MountedStorageStatus {
- private final boolean mStorageMounted;
- private final File mStorageMountedDir;
- private final long mStorageMountedCapacity;
-
- private MountedStorageStatus(boolean mounted, File mountedDir, long capacity) {
- mStorageMounted = mounted;
- mStorageMountedDir = mountedDir;
- mStorageMountedCapacity = capacity;
- }
-
- private boolean isValidForDvr() {
- return mStorageMounted && mStorageMountedCapacity >= MIN_STORAGE_SIZE_FOR_DVR_IN_BYTES;
- }
-
- @Override
- public boolean equals(Object other) {
- if (!(other instanceof MountedStorageStatus)) {
- return false;
- }
- MountedStorageStatus status = (MountedStorageStatus) other;
- return mStorageMounted == status.mStorageMounted
- && Objects.equals(mStorageMountedDir, status.mStorageMountedDir)
- && mStorageMountedCapacity == status.mStorageMountedCapacity;
- }
- }
-
- public interface OnStorageMountChangedListener {
-
- /**
- * Listener for DVR storage status change.
- *
- * @param storageMounted {@code true} when DVR possible storage is mounted,
- * {@code false} otherwise.
- */
- void onStorageMountChanged(boolean storageMounted);
- }
-
- private final class StorageStatusBroadcastReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- MountedStorageStatus result = getStorageStatusInternal();
- if (mMountedStorageStatus.equals(result)) {
- return;
- }
- mMountedStorageStatus = result;
- if (result.mStorageMounted && mRunningInMainProcess) {
- // Cleans up DB in LC process.
- // Tuner process is not always on.
- if (mCleanUpDbTask != null) {
- mCleanUpDbTask.cancel(true);
- }
- mCleanUpDbTask = new CleanUpDbTask();
- mCleanUpDbTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
- boolean valid = result.isValidForDvr();
- if (valid == mStorageValid) {
- return;
- }
- mStorageValid = valid;
- for (OnStorageMountChangedListener l : mOnStorageMountChangedListeners) {
- l.onStorageMountChanged(valid);
- }
- }
- }
+ private static final String[] PROJECTION = {
+ TvContractCompat.RecordedPrograms._ID,
+ TvContractCompat.RecordedPrograms.COLUMN_PACKAGE_NAME,
+ TvContractCompat.RecordedPrograms.COLUMN_RECORDING_DATA_URI
+ };
+ private static final int BATCH_OPERATION_COUNT = 100;
- /**
- * Creates DvrStorageStatusManager.
- *
- * @param context {@link Context}
- */
- public DvrStorageStatusManager(final Context context, boolean runningInMainProcess) {
+ public DvrStorageStatusManager(Context context) {
+ super(context);
mContext = context;
- mRunningInMainProcess = runningInMainProcess;
- mMountedStorageStatus = getStorageStatusInternal();
- mStorageValid = mMountedStorageStatus.isValidForDvr();
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_MEDIA_MOUNTED);
- filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
- filter.addAction(Intent.ACTION_MEDIA_EJECT);
- filter.addAction(Intent.ACTION_MEDIA_REMOVED);
- filter.addAction(Intent.ACTION_MEDIA_BAD_REMOVAL);
- filter.addDataScheme(ContentResolver.SCHEME_FILE);
- mContext.registerReceiver(new StorageStatusBroadcastReceiver(), filter);
}
- /**
- * Adds the listener for receiving storage status change.
- *
- * @param listener
- */
- public void addListener(OnStorageMountChangedListener listener) {
- mOnStorageMountChangedListeners.add(listener);
- }
-
- /**
- * Removes the current listener.
- */
- public void removeListener(OnStorageMountChangedListener listener) {
- mOnStorageMountChangedListeners.remove(listener);
- }
-
- /**
- * Returns true if a storage is mounted.
- */
- public boolean isStorageMounted() {
- return mMountedStorageStatus.mStorageMounted;
- }
-
- /**
- * Returns the path to DVR recording data directory.
- * This can take for a while sometimes.
- */
- @WorkerThread
- public File getRecordingRootDataDirectory() {
- SoftPreconditions.checkState(Looper.myLooper() != Looper.getMainLooper());
- if (mMountedStorageStatus.mStorageMountedDir == null) {
- return null;
- }
- File root = mContext.getExternalFilesDir(null);
- String rootPath;
- try {
- rootPath = root != null ? root.getCanonicalPath() : null;
- } catch (IOException | SecurityException e) {
- return null;
- }
- return rootPath == null ? null : new File(rootPath + RECORDING_DATA_SUB_PATH);
- }
-
- /**
- * Returns the current storage status for DVR recordings.
- *
- * @return {@link StorageStatus}
- */
- @AnyThread
- public @StorageStatus int getDvrStorageStatus() {
- MountedStorageStatus status = mMountedStorageStatus;
- if (status.mStorageMountedDir == null) {
- return STORAGE_STATUS_MISSING;
- }
- if (CommonFeatures.FORCE_RECORDING_UNTIL_NO_SPACE.isEnabled(mContext)) {
- return STORAGE_STATUS_OK;
- }
- if (status.mStorageMountedCapacity < MIN_STORAGE_SIZE_FOR_DVR_IN_BYTES) {
- return STORAGE_STATUS_TOTAL_CAPACITY_TOO_SMALL;
- }
- try {
- StatFs statFs = new StatFs(status.mStorageMountedDir.toString());
- if (statFs.getAvailableBytes() < MIN_FREE_STORAGE_SIZE_FOR_DVR_IN_BYTES) {
- return STORAGE_STATUS_FREE_SPACE_INSUFFICIENT;
- }
- } catch (IllegalArgumentException e) {
- // In rare cases, storage status change was not notified yet.
- SoftPreconditions.checkState(false);
- return STORAGE_STATUS_FREE_SPACE_INSUFFICIENT;
- }
- return STORAGE_STATUS_OK;
- }
-
- /**
- * Returns whether the storage has sufficient storage.
- *
- * @return {@code true} when there is sufficient storage, {@code false} otherwise
- */
- public boolean isStorageSufficient() {
- return getDvrStorageStatus() == STORAGE_STATUS_OK;
- }
-
- private MountedStorageStatus getStorageStatusInternal() {
- boolean storageMounted =
- Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
- File storageMountedDir = storageMounted ? Environment.getExternalStorageDirectory() : null;
- storageMounted = storageMounted && storageMountedDir != null;
- long storageMountedCapacity = 0L;
- if (storageMounted) {
- try {
- StatFs statFs = new StatFs(storageMountedDir.toString());
- storageMountedCapacity = statFs.getTotalBytes();
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Storage mount status was changed.");
- storageMounted = false;
- storageMountedDir = null;
- }
+ @Override
+ protected void cleanUpDbIfNeeded() {
+ if (mCleanUpDbTask != null) {
+ mCleanUpDbTask.cancel(true);
}
- return new MountedStorageStatus(
- storageMounted, storageMountedDir, storageMountedCapacity);
+ mCleanUpDbTask = new CleanUpDbTask();
+ mCleanUpDbTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private class CleanUpDbTask extends AsyncTask<Void, Void, Boolean> {
@@ -307,26 +71,29 @@ public class DvrStorageStatusManager {
@Override
protected Boolean doInBackground(Void... params) {
- @DvrStorageStatusManager.StorageStatus int storageStatus = getDvrStorageStatus();
- if (storageStatus == DvrStorageStatusManager.STORAGE_STATUS_MISSING) {
+ @StorageStatus int storageStatus = getDvrStorageStatus();
+ if (storageStatus == STORAGE_STATUS_MISSING) {
return null;
}
- if (storageStatus == DvrStorageStatusManager.STORAGE_STATUS_TOTAL_CAPACITY_TOO_SMALL) {
+ if (storageStatus == STORAGE_STATUS_TOTAL_CAPACITY_TOO_SMALL) {
return true;
}
List<ContentProviderOperation> ops = getDeleteOps();
if (ops == null || ops.isEmpty()) {
return null;
}
- Log.i(TAG, "New device storage mounted. # of recordings to be forgotten : "
- + ops.size());
- for (int i = 0 ; i < ops.size() && !isCancelled() ; i += BATCH_OPERATION_COUNT) {
- int toIndex = (i + BATCH_OPERATION_COUNT) > ops.size()
- ? ops.size() : (i + BATCH_OPERATION_COUNT);
+ Log.i(
+ TAG,
+ "New device storage mounted. # of recordings to be forgotten : " + ops.size());
+ for (int i = 0; i < ops.size() && !isCancelled(); i += BATCH_OPERATION_COUNT) {
+ int toIndex =
+ (i + BATCH_OPERATION_COUNT) > ops.size()
+ ? ops.size()
+ : (i + BATCH_OPERATION_COUNT);
ArrayList<ContentProviderOperation> batchOps =
new ArrayList<>(ops.subList(i, toIndex));
try {
- mContext.getContentResolver().applyBatch(TvContract.AUTHORITY, batchOps);
+ mContext.getContentResolver().applyBatch(TvContractCompat.AUTHORITY, batchOps);
} catch (RemoteException | OperationApplicationException e) {
Log.e(TAG, "Failed to clean up RecordedPrograms.", e);
}
@@ -337,16 +104,16 @@ public class DvrStorageStatusManager {
@Override
protected void onPostExecute(Boolean forgetStorage) {
if (forgetStorage != null && forgetStorage == true) {
- DvrManager dvrManager = TvApplication.getSingletons(mContext).getDvrManager();
+ DvrManager dvrManager = TvSingletons.getSingletons(mContext).getDvrManager();
TvInputManagerHelper tvInputManagerHelper =
- TvApplication.getSingletons(mContext).getTvInputManagerHelper();
+ TvSingletons.getSingletons(mContext).getTvInputManagerHelper();
List<TvInputInfo> tvInputInfoList =
tvInputManagerHelper.getTvInputInfos(true, false);
if (tvInputInfoList == null || tvInputInfoList.isEmpty()) {
return;
}
for (TvInputInfo info : tvInputInfoList) {
- if (Utils.isBundledInput(info.getId())) {
+ if (CommonUtils.isBundledInput(info.getId())) {
dvrManager.forgetStorage(info.getId());
}
}
@@ -359,16 +126,19 @@ public class DvrStorageStatusManager {
private List<ContentProviderOperation> getDeleteOps() {
List<ContentProviderOperation> ops = new ArrayList<>();
- try (Cursor c = mContentResolver.query(
- TvContract.RecordedPrograms.CONTENT_URI, PROJECTION, null, null, null)) {
+ try (Cursor c =
+ mContentResolver.query(
+ TvContractCompat.RecordedPrograms.CONTENT_URI,
+ PROJECTION,
+ null,
+ null,
+ null)) {
if (c == null) {
return null;
}
while (c.moveToNext()) {
- @DvrStorageStatusManager.StorageStatus int storageStatus =
- getDvrStorageStatus();
- if (isCancelled()
- || storageStatus == DvrStorageStatusManager.STORAGE_STATUS_MISSING) {
+ @StorageStatus int storageStatus = getDvrStorageStatus();
+ if (isCancelled() || storageStatus == STORAGE_STATUS_MISSING) {
ops.clear();
break;
}
@@ -379,15 +149,19 @@ public class DvrStorageStatusManager {
continue;
}
Uri dataUri = Uri.parse(dataUriString);
- if (!Utils.isInBundledPackageSet(packageName)
- || dataUri == null || dataUri.getPath() == null
+ if (!CommonUtils.isInBundledPackageSet(packageName)
+ || dataUri == null
+ || dataUri.getPath() == null
|| !ContentResolver.SCHEME_FILE.equals(dataUri.getScheme())) {
continue;
}
File recordedProgramDir = new File(dataUri.getPath());
if (!recordedProgramDir.exists()) {
- ops.add(ContentProviderOperation.newDelete(
- TvContract.buildRecordedProgramUri(Long.parseLong(id))).build());
+ ops.add(
+ ContentProviderOperation.newDelete(
+ TvContractCompat.buildRecordedProgramUri(
+ Long.parseLong(id)))
+ .build());
}
}
return ops;
diff --git a/src/com/android/tv/dvr/DvrWatchedPositionManager.java b/src/com/android/tv/dvr/DvrWatchedPositionManager.java
index da6ddb1a..8616962f 100644
--- a/src/com/android/tv/dvr/DvrWatchedPositionManager.java
+++ b/src/com/android/tv/dvr/DvrWatchedPositionManager.java
@@ -20,10 +20,8 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.media.tv.TvInputManager;
import android.support.annotation.IntDef;
-
-import com.android.tv.common.SharedPreferencesUtils;
+import com.android.tv.common.util.SharedPreferencesUtils;
import com.android.tv.dvr.data.RecordedProgram;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -33,8 +31,8 @@ import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
- * A class to manage DVR watched state.
- * It will remember and provides previous watched position of DVR playback.
+ * A class to manage DVR watched state. It will remember and provides previous watched position of
+ * DVR playback.
*/
public class DvrWatchedPositionManager {
private SharedPreferences mWatchedPositions;
@@ -49,55 +47,46 @@ public class DvrWatchedPositionManager {
@Retention(RetentionPolicy.SOURCE)
@IntDef({DVR_WATCHED_STATUS_NEW, DVR_WATCHED_STATUS_WATCHING, DVR_WATCHED_STATUS_WATCHED})
public @interface DvrWatchedStatus {}
- /**
- * The status indicates the recorded program has not been watched at all.
- */
+ /** The status indicates the recorded program has not been watched at all. */
public static final int DVR_WATCHED_STATUS_NEW = 0;
- /**
- * The status indicates the recorded program is being watched.
- */
+ /** The status indicates the recorded program is being watched. */
public static final int DVR_WATCHED_STATUS_WATCHING = 1;
- /**
- * The status indicates the recorded program was completely watched.
- */
+ /** The status indicates the recorded program was completely watched. */
public static final int DVR_WATCHED_STATUS_WATCHED = 2;
public DvrWatchedPositionManager(Context context) {
- mWatchedPositions = context.getSharedPreferences(
- SharedPreferencesUtils.SHARED_PREF_DVR_WATCHED_POSITION, Context.MODE_PRIVATE);
+ mWatchedPositions =
+ context.getSharedPreferences(
+ SharedPreferencesUtils.SHARED_PREF_DVR_WATCHED_POSITION,
+ Context.MODE_PRIVATE);
}
- /**
- * Sets the watched position of the give program.
- */
+ /** Sets the watched position of the give program. */
public void setWatchedPosition(long recordedProgramId, long positionMs) {
mWatchedPositions.edit().putLong(Long.toString(recordedProgramId), positionMs).apply();
notifyWatchedPositionChanged(recordedProgramId, positionMs);
}
- /**
- * Gets the watched position of the give program.
- */
+ /** Gets the watched position of the give program. */
public long getWatchedPosition(long recordedProgramId) {
- return mWatchedPositions.getLong(Long.toString(recordedProgramId),
- TvInputManager.TIME_SHIFT_INVALID_TIME);
+ return mWatchedPositions.getLong(
+ Long.toString(recordedProgramId), TvInputManager.TIME_SHIFT_INVALID_TIME);
}
- @DvrWatchedStatus public int getWatchedStatus(RecordedProgram recordedProgram) {
+ @DvrWatchedStatus
+ public int getWatchedStatus(RecordedProgram recordedProgram) {
long watchedPosition = getWatchedPosition(recordedProgram.getId());
if (watchedPosition == TvInputManager.TIME_SHIFT_INVALID_TIME) {
return DVR_WATCHED_STATUS_NEW;
- } else if (watchedPosition > recordedProgram
- .getDurationMillis() * DVR_WATCHED_THRESHOLD_RATE) {
+ } else if (watchedPosition
+ > recordedProgram.getDurationMillis() * DVR_WATCHED_THRESHOLD_RATE) {
return DVR_WATCHED_STATUS_WATCHED;
} else {
return DVR_WATCHED_STATUS_WATCHING;
}
}
- /**
- * Adds {@link WatchedPositionChangedListener}.
- */
+ /** Adds {@link WatchedPositionChangedListener}. */
public void addListener(WatchedPositionChangedListener listener, long recordedProgramId) {
if (recordedProgramId == RecordedProgram.ID_NOT_SET) {
return;
@@ -110,18 +99,14 @@ public class DvrWatchedPositionManager {
listenerSet.add(listener);
}
- /**
- * Removes {@link WatchedPositionChangedListener}.
- */
+ /** Removes {@link WatchedPositionChangedListener}. */
public void removeListener(WatchedPositionChangedListener listener) {
for (long recordedProgramId : new ArrayList<>(mListeners.keySet())) {
removeListener(listener, recordedProgramId);
}
}
- /**
- * Removes {@link WatchedPositionChangedListener}.
- */
+ /** Removes {@link WatchedPositionChangedListener}. */
public void removeListener(WatchedPositionChangedListener listener, long recordedProgramId) {
Set<WatchedPositionChangedListener> listenerSet = mListeners.get(recordedProgramId);
if (listenerSet == null) {
@@ -144,9 +129,7 @@ public class DvrWatchedPositionManager {
}
public interface WatchedPositionChangedListener {
- /**
- * Called when the watched position of some program is changed.
- */
+ /** Called when the watched position of some program is changed. */
void onWatchedPositionChanged(long recordedProgramId, long positionMs);
}
}
diff --git a/src/com/android/tv/dvr/WritableDvrDataManager.java b/src/com/android/tv/dvr/WritableDvrDataManager.java
index 129ba153..1b505e80 100644
--- a/src/com/android/tv/dvr/WritableDvrDataManager.java
+++ b/src/com/android/tv/dvr/WritableDvrDataManager.java
@@ -17,7 +17,6 @@
package com.android.tv.dvr;
import android.support.annotation.MainThread;
-
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.data.ScheduledRecording.RecordingState;
import com.android.tv.dvr.data.SeriesRecording;
@@ -30,19 +29,13 @@ import com.android.tv.dvr.data.SeriesRecording;
*/
@MainThread
public interface WritableDvrDataManager extends DvrDataManager {
- /**
- * Adds new recordings.
- */
+ /** Adds new recordings. */
void addScheduledRecording(ScheduledRecording... scheduledRecordings);
- /**
- * Adds new series recordings.
- */
+ /** Adds new series recordings. */
void addSeriesRecording(SeriesRecording... seriesRecordings);
- /**
- * Removes recordings.
- */
+ /** Removes recordings. */
void removeScheduledRecording(ScheduledRecording... scheduledRecordings);
/**
@@ -51,30 +44,30 @@ public interface WritableDvrDataManager extends DvrDataManager {
*/
void removeScheduledRecording(boolean forceRemove, ScheduledRecording... scheduledRecordings);
- /**
- * Removes series recordings.
- */
+ /** Removes series recordings. */
void removeSeriesRecording(SeriesRecording... seasonSchedules);
- /**
- * Updates existing recordings.
- */
+ /** Updates existing recordings. */
void updateScheduledRecording(ScheduledRecording... scheduledRecordings);
- /**
- * Updates existing series recordings.
- */
+ /** Updates existing series recordings. */
void updateSeriesRecording(SeriesRecording... seriesRecordings);
+ /** Changes the state of the recording. */
+ void changeState(ScheduledRecording scheduledRecording, @RecordingState int newState);
+
/**
* Changes the state of the recording.
+ *
+ * @param reason the reason of this change
*/
- void changeState(ScheduledRecording scheduledRecording, @RecordingState int newState);
+ void changeState(
+ ScheduledRecording scheduledRecording, @RecordingState int newState, int reason);
/**
* Remove all the records related to the input.
- * <p>
- * Note that this should be called after the input was removed.
+ *
+ * <p>Note that this should be called after the input was removed.
*/
void forgetStorage(String inputId);
}
diff --git a/src/com/android/tv/dvr/data/IdGenerator.java b/src/com/android/tv/dvr/data/IdGenerator.java
index 2ade1dad..496651ba 100644
--- a/src/com/android/tv/dvr/data/IdGenerator.java
+++ b/src/com/android/tv/dvr/data/IdGenerator.java
@@ -18,32 +18,22 @@ package com.android.tv.dvr.data;
import java.util.concurrent.atomic.AtomicLong;
-/**
- * A class which generate the ID which increases sequentially.
- */
+/** A class which generate the ID which increases sequentially. */
public class IdGenerator {
- /**
- * ID generator for the scheduled recording.
- */
+ /** ID generator for the scheduled recording. */
public static final IdGenerator SCHEDULED_RECORDING = new IdGenerator();
- /**
- * ID generator for the series recording.
- */
+ /** ID generator for the series recording. */
public static final IdGenerator SERIES_RECORDING = new IdGenerator();
private final AtomicLong mMaxId = new AtomicLong(0);
- /**
- * Sets the new maximum ID.
- */
+ /** Sets the new maximum ID. */
public void setMaxId(long maxId) {
mMaxId.set(maxId);
}
- /**
- * Returns the new ID which is greater than the existing maximum ID by 1.
- */
+ /** Returns the new ID which is greater than the existing maximum ID by 1. */
public long newId() {
return mMaxId.incrementAndGet();
}
diff --git a/src/com/android/tv/dvr/data/RecordedProgram.java b/src/com/android/tv/dvr/data/RecordedProgram.java
index 2e953a52..e1fbca8c 100644
--- a/src/com/android/tv/dvr/data/RecordedProgram.java
+++ b/src/com/android/tv/dvr/data/RecordedProgram.java
@@ -28,99 +28,97 @@ import android.net.Uri;
import android.os.Build;
import android.support.annotation.Nullable;
import android.text.TextUtils;
-
import com.android.tv.common.R;
import com.android.tv.common.TvContentRatingCache;
+import com.android.tv.common.util.CommonUtils;
import com.android.tv.data.BaseProgram;
import com.android.tv.data.GenreItems;
import com.android.tv.data.InternalDataUtils;
-import com.android.tv.util.Utils;
-
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
-/**
- * Immutable instance of {@link android.media.tv.TvContract.RecordedPrograms}.
- */
+/** Immutable instance of {@link android.media.tv.TvContract.RecordedPrograms}. */
@TargetApi(Build.VERSION_CODES.N)
public class RecordedProgram extends BaseProgram {
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_PACKAGE_NAME,
- 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_FLAG1,
- RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2,
- RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3,
- RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4,
- RecordedPrograms.COLUMN_VERSION_NUMBER,
- RecordedPrograms.COLUMN_INTERNAL_PROVIDER_DATA,
+ public static final String[] PROJECTION = {
+ // These are in exactly the order listed in RecordedPrograms
+ RecordedPrograms._ID,
+ RecordedPrograms.COLUMN_PACKAGE_NAME,
+ 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_FLAG1,
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2,
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3,
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4,
+ RecordedPrograms.COLUMN_VERSION_NUMBER,
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_DATA,
};
public static RecordedProgram fromCursor(Cursor cursor) {
int index = 0;
- Builder builder = builder()
- .setId(cursor.getLong(index++))
- .setPackageName(cursor.getString(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++))
- .setContentRatings(
- TvContentRatingCache.getInstance().getRatings(cursor.getString(index++)))
- .setPosterArtUri(cursor.getString(index++))
- .setThumbnailUri(cursor.getString(index++))
- .setSearchable(cursor.getInt(index++) == 1)
- .setDataUri(cursor.getString(index++))
- .setDataBytes(cursor.getLong(index++))
- .setDurationMillis(cursor.getLong(index++))
- .setExpireTimeUtcMillis(cursor.getLong(index++))
- .setInternalProviderFlag1(cursor.getInt(index++))
- .setInternalProviderFlag2(cursor.getInt(index++))
- .setInternalProviderFlag3(cursor.getInt(index++))
- .setInternalProviderFlag4(cursor.getInt(index++))
- .setVersionNumber(cursor.getInt(index++));
- if (Utils.isInBundledPackageSet(builder.mPackageName)) {
+ Builder builder =
+ builder()
+ .setId(cursor.getLong(index++))
+ .setPackageName(cursor.getString(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++))
+ .setContentRatings(
+ TvContentRatingCache.getInstance()
+ .getRatings(cursor.getString(index++)))
+ .setPosterArtUri(cursor.getString(index++))
+ .setThumbnailUri(cursor.getString(index++))
+ .setSearchable(cursor.getInt(index++) == 1)
+ .setDataUri(cursor.getString(index++))
+ .setDataBytes(cursor.getLong(index++))
+ .setDurationMillis(cursor.getLong(index++))
+ .setExpireTimeUtcMillis(cursor.getLong(index++))
+ .setInternalProviderFlag1(cursor.getInt(index++))
+ .setInternalProviderFlag2(cursor.getInt(index++))
+ .setInternalProviderFlag3(cursor.getInt(index++))
+ .setInternalProviderFlag4(cursor.getInt(index++))
+ .setVersionNumber(cursor.getInt(index++));
+ if (CommonUtils.isInBundledPackageSet(builder.mPackageName)) {
InternalDataUtils.deserializeInternalProviderData(cursor.getBlob(index), builder);
}
return builder.build();
@@ -138,12 +136,14 @@ public class RecordedProgram extends BaseProgram {
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_START_TIME_UTC_MILLIS, recordedProgram.mStartTimeUtcMillis);
values.put(RecordedPrograms.COLUMN_END_TIME_UTC_MILLIS, recordedProgram.mEndTimeUtcMillis);
- values.put(RecordedPrograms.COLUMN_BROADCAST_GENRE,
+ values.put(
+ RecordedPrograms.COLUMN_BROADCAST_GENRE,
safeEncode(recordedProgram.mBroadcastGenres));
- values.put(RecordedPrograms.COLUMN_CANONICAL_GENRE,
+ 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);
@@ -158,33 +158,40 @@ public class RecordedProgram extends BaseProgram {
values.put(RecordedPrograms.COLUMN_VIDEO_HEIGHT, recordedProgram.mVideoHeight);
}
values.put(RecordedPrograms.COLUMN_AUDIO_LANGUAGE, recordedProgram.mAudioLanguage);
- values.put(RecordedPrograms.COLUMN_CONTENT_RATING,
+ values.put(
+ RecordedPrograms.COLUMN_CONTENT_RATING,
TvContentRatingCache.contentRatingsToString(recordedProgram.mContentRatings));
values.put(RecordedPrograms.COLUMN_POSTER_ART_URI, recordedProgram.mPosterArtUri);
values.put(RecordedPrograms.COLUMN_THUMBNAIL_URI, recordedProgram.mThumbnailUri);
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_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,
+ 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,
+ values.put(
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_DATA,
InternalDataUtils.serializeInternalProviderData(recordedProgram));
- values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG1,
+ values.put(
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG1,
recordedProgram.mInternalProviderFlag1);
- values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2,
+ values.put(
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2,
recordedProgram.mInternalProviderFlag2);
- values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3,
+ values.put(
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3,
recordedProgram.mInternalProviderFlag3);
- values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4,
+ values.put(
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4,
recordedProgram.mInternalProviderFlag4);
values.put(RecordedPrograms.COLUMN_VERSION_NUMBER, recordedProgram.mVersionNumber);
return values;
}
- public static class Builder{
+ public static class Builder {
private long mId = ID_NOT_SET;
private String mPackageName;
private String mInputId;
@@ -414,18 +421,45 @@ public class RecordedProgram extends BaseProgram {
// If series ID is not set, generate it for the episodic program of other TV input.
setSeriesId(BaseProgram.generateSeriesId(mPackageName, mTitle));
}
- return new RecordedProgram(mId, mPackageName, mInputId, mChannelId, mTitle, mSeriesId,
- mSeasonNumber, mSeasonTitle, mEpisodeNumber, mEpisodeTitle, mStartTimeUtcMillis,
- mEndTimeUtcMillis, mBroadcastGenres, mCanonicalGenres, mShortDescription,
- mLongDescription, mVideoWidth, mVideoHeight, mAudioLanguage, mContentRatings,
- mPosterArtUri, mThumbnailUri, mSearchable, mDataUri, mDataBytes,
- mDurationMillis, mExpireTimeUtcMillis, mInternalProviderFlag1,
- mInternalProviderFlag2, mInternalProviderFlag3, mInternalProviderFlag4,
+ return new RecordedProgram(
+ mId,
+ mPackageName,
+ mInputId,
+ mChannelId,
+ mTitle,
+ mSeriesId,
+ mSeasonNumber,
+ mSeasonTitle,
+ mEpisodeNumber,
+ mEpisodeTitle,
+ mStartTimeUtcMillis,
+ mEndTimeUtcMillis,
+ mBroadcastGenres,
+ mCanonicalGenres,
+ mShortDescription,
+ mLongDescription,
+ mVideoWidth,
+ mVideoHeight,
+ mAudioLanguage,
+ mContentRatings,
+ mPosterArtUri,
+ mThumbnailUri,
+ mSearchable,
+ mDataUri,
+ mDataBytes,
+ mDurationMillis,
+ mExpireTimeUtcMillis,
+ mInternalProviderFlag1,
+ mInternalProviderFlag2,
+ mInternalProviderFlag3,
+ mInternalProviderFlag4,
mVersionNumber);
}
}
- public static Builder builder() { return new Builder(); }
+ public static Builder builder() {
+ return new Builder();
+ }
public static Builder buildFrom(RecordedProgram orig) {
return builder()
@@ -470,7 +504,7 @@ public class RecordedProgram extends BaseProgram {
}
return Long.compare(lhs.mId, rhs.mId);
}
- };
+ };
private static final long CLIPPED_THRESHOLD_MS = TimeUnit.MINUTES.toMillis(5);
@@ -507,15 +541,38 @@ public class RecordedProgram extends BaseProgram {
private final int mInternalProviderFlag4;
private final int mVersionNumber;
- private RecordedProgram(long id, String packageName, String inputId, long channelId,
- String title, String seriesId, 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, TvContentRating[] contentRatings, String posterArtUri,
- String thumbnailUri, boolean searchable, Uri dataUri, long dataBytes,
- long durationMillis, long expireTimeUtcMillis, int internalProviderFlag1,
- int internalProviderFlag2, int internalProviderFlag3, int internalProviderFlag4,
+ private RecordedProgram(
+ long id,
+ String packageName,
+ String inputId,
+ long channelId,
+ String title,
+ String seriesId,
+ 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,
+ TvContentRating[] contentRatings,
+ String posterArtUri,
+ String thumbnailUri,
+ boolean searchable,
+ Uri dataUri,
+ long dataBytes,
+ long durationMillis,
+ long expireTimeUtcMillis,
+ int internalProviderFlag1,
+ int internalProviderFlag2,
+ int internalProviderFlag3,
+ int internalProviderFlag4,
int versionNumber) {
mId = id;
mPackageName = packageName;
@@ -564,9 +621,7 @@ public class RecordedProgram extends BaseProgram {
return mCanonicalGenres;
}
- /**
- * Returns array of canonical genre ID's for this recorded program.
- */
+ /** Returns array of canonical genre ID's for this recorded program. */
@Override
public int[] getCanonicalGenreIds() {
if (mCanonicalGenres == null) {
@@ -623,11 +678,15 @@ public class RecordedProgram extends BaseProgram {
if (!TextUtils.isEmpty(mEpisodeNumber)) {
if (TextUtils.equals(mSeasonNumber, "0")) {
// Do not show "S0: ".
- return String.format(context.getResources().getString(
- R.string.display_episode_number_format_no_season_number), mEpisodeNumber);
+ return String.format(
+ context.getResources()
+ .getString(R.string.display_episode_number_format_no_season_number),
+ mEpisodeNumber);
} else {
- return String.format(context.getResources().getString(
- R.string.display_episode_number_format), mSeasonNumber, mEpisodeNumber);
+ return String.format(
+ context.getResources().getString(R.string.display_episode_number_format),
+ mSeasonNumber,
+ mEpisodeNumber);
}
}
return null;
@@ -734,9 +793,7 @@ public class RecordedProgram extends BaseProgram {
return mVideoWidth;
}
- /**
- * Checks whether the recording has been clipped or not.
- */
+ /** Checks whether the recording has been clipped or not. */
public boolean isClipped() {
return mEndTimeUtcMillis - mStartTimeUtcMillis - mDurationMillis > CLIPPED_THRESHOLD_MS;
}
@@ -746,40 +803,38 @@ public class RecordedProgram extends BaseProgram {
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(mSeriesId, that.mSeriesId) &&
- 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) &&
- Arrays.equals(mContentRatings, that.mContentRatings) &&
- Objects.equals(mPosterArtUri, that.mPosterArtUri) &&
- Objects.equals(mThumbnailUri, that.mThumbnailUri);
- }
-
- /**
- * Hashes based on the ID.
- */
+ return Objects.equals(mId, that.mId)
+ && Objects.equals(mChannelId, that.mChannelId)
+ && Objects.equals(mSeriesId, that.mSeriesId)
+ && 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)
+ && Arrays.equals(mContentRatings, that.mContentRatings)
+ && Objects.equals(mPosterArtUri, that.mPosterArtUri)
+ && Objects.equals(mThumbnailUri, that.mThumbnailUri);
+ }
+
+ /** Hashes based on the ID. */
@Override
public int hashCode() {
return Objects.hash(mId);
@@ -788,42 +843,80 @@ public class RecordedProgram extends BaseProgram {
@Override
public String toString() {
return "RecordedProgram"
- + "[" + mId +
- "]{ mPackageName=" + mPackageName +
- ", mInputId='" + mInputId + '\'' +
- ", mChannelId='" + mChannelId + '\'' +
- ", mTitle='" + mTitle + '\'' +
- ", mSeriesId='" + mSeriesId + '\'' +
- ", 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 + '\'' +
- ", mContentRatings='" +
- TvContentRatingCache.contentRatingsToString(mContentRatings) + '\'' +
- ", mPosterArtUri=" + mPosterArtUri +
- ", mThumbnailUri=" + mThumbnailUri +
- ", mSearchable=" + mSearchable +
- ", mDataUri=" + mDataUri +
- ", mDataBytes=" + mDataBytes +
- ", mDurationMillis=" + mDurationMillis +
- ", mExpireTimeUtcMillis=" + mExpireTimeUtcMillis +
- ", mInternalProviderFlag1=" + mInternalProviderFlag1 +
- ", mInternalProviderFlag2=" + mInternalProviderFlag2 +
- ", mInternalProviderFlag3=" + mInternalProviderFlag3 +
- ", mInternalProviderFlag4=" + mInternalProviderFlag4 +
- ", mSeasonNumber=" + mSeasonNumber +
- ", mSeasonTitle=" + mSeasonTitle +
- ", mVersionNumber=" + mVersionNumber +
- '}';
+ + "["
+ + mId
+ + "]{ mPackageName="
+ + mPackageName
+ + ", mInputId='"
+ + mInputId
+ + '\''
+ + ", mChannelId='"
+ + mChannelId
+ + '\''
+ + ", mTitle='"
+ + mTitle
+ + '\''
+ + ", mSeriesId='"
+ + mSeriesId
+ + '\''
+ + ", 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
+ + '\''
+ + ", mContentRatings='"
+ + TvContentRatingCache.contentRatingsToString(mContentRatings)
+ + '\''
+ + ", mPosterArtUri="
+ + mPosterArtUri
+ + ", mThumbnailUri="
+ + mThumbnailUri
+ + ", mSearchable="
+ + mSearchable
+ + ", mDataUri="
+ + mDataUri
+ + ", mDataBytes="
+ + mDataBytes
+ + ", mDurationMillis="
+ + mDurationMillis
+ + ", mExpireTimeUtcMillis="
+ + mExpireTimeUtcMillis
+ + ", mInternalProviderFlag1="
+ + mInternalProviderFlag1
+ + ", mInternalProviderFlag2="
+ + mInternalProviderFlag2
+ + ", mInternalProviderFlag3="
+ + mInternalProviderFlag3
+ + ", mInternalProviderFlag4="
+ + mInternalProviderFlag4
+ + ", mSeasonNumber="
+ + mSeasonNumber
+ + ", mSeasonTitle="
+ + mSeasonTitle
+ + ", mVersionNumber="
+ + mVersionNumber
+ + '}';
}
@Nullable
@@ -836,9 +929,7 @@ public class RecordedProgram extends BaseProgram {
return genres == null ? null : TvContract.Programs.Genres.encode(genres);
}
- /**
- * Returns an array containing all of the elements in the list.
- */
+ /** Returns an array containing all of the elements in the list. */
public static RecordedProgram[] toArray(Collection<RecordedProgram> recordedPrograms) {
return recordedPrograms.toArray(new RecordedProgram[recordedPrograms.size()]);
}
diff --git a/src/com/android/tv/dvr/data/ScheduledRecording.java b/src/com/android/tv/dvr/data/ScheduledRecording.java
index 5d11c0f3..7c2d12d9 100644
--- a/src/com/android/tv/dvr/data/ScheduledRecording.java
+++ b/src/com/android/tv/dvr/data/ScheduledRecording.java
@@ -16,107 +16,97 @@
package com.android.tv.dvr.data;
+import android.annotation.TargetApi;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.IntDef;
+import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Range;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
-import com.android.tv.data.Channel;
+import com.android.tv.common.util.CommonUtils;
import com.android.tv.data.Program;
+import com.android.tv.data.api.Channel;
import com.android.tv.dvr.DvrScheduleManager;
import com.android.tv.dvr.provider.DvrContract.Schedules;
import com.android.tv.util.CompositeComparator;
-import com.android.tv.util.Utils;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collection;
import java.util.Comparator;
import java.util.Objects;
-/**
- * A data class for one recording contents.
- */
+/** A data class for one recording contents. */
+@TargetApi(Build.VERSION_CODES.N)
+@SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated
public final class ScheduledRecording implements Parcelable {
private static final String TAG = "ScheduledRecording";
- /**
- * Indicates that the ID is not assigned yet.
- */
+ /** Indicates that the ID is not assigned yet. */
public static final long ID_NOT_SET = 0;
- /**
- * The default priority of the recording.
- */
+ /** The default priority of the recording. */
public static final long DEFAULT_PRIORITY = Long.MAX_VALUE >> 1;
- /**
- * Compares the start time in ascending order.
- */
- public static final Comparator<ScheduledRecording> START_TIME_COMPARATOR
- = new Comparator<ScheduledRecording>() {
- @Override
- public int compare(ScheduledRecording lhs, ScheduledRecording rhs) {
- return Long.compare(lhs.mStartTimeMs, rhs.mStartTimeMs);
- }
- };
-
- /**
- * Compares the end time in ascending order.
- */
- public static final Comparator<ScheduledRecording> END_TIME_COMPARATOR
- = new Comparator<ScheduledRecording>() {
- @Override
- public int compare(ScheduledRecording lhs, ScheduledRecording rhs) {
- return Long.compare(lhs.mEndTimeMs, rhs.mEndTimeMs);
- }
- };
-
- /**
- * Compares ID in ascending order. The schedule with the larger ID was created later.
- */
- public static final Comparator<ScheduledRecording> ID_COMPARATOR
- = new Comparator<ScheduledRecording>() {
- @Override
- public int compare(ScheduledRecording lhs, ScheduledRecording rhs) {
- return Long.compare(lhs.mId, rhs.mId);
- }
- };
-
- /**
- * Compares the priority in ascending order.
- */
- public static final Comparator<ScheduledRecording> PRIORITY_COMPARATOR
- = new Comparator<ScheduledRecording>() {
- @Override
- public int compare(ScheduledRecording lhs, ScheduledRecording rhs) {
- return Long.compare(lhs.mPriority, rhs.mPriority);
- }
- };
+ /** Compares the start time in ascending order. */
+ public static final Comparator<ScheduledRecording> START_TIME_COMPARATOR =
+ new Comparator<ScheduledRecording>() {
+ @Override
+ public int compare(ScheduledRecording lhs, ScheduledRecording rhs) {
+ return Long.compare(lhs.mStartTimeMs, rhs.mStartTimeMs);
+ }
+ };
+
+ /** Compares the end time in ascending order. */
+ public static final Comparator<ScheduledRecording> END_TIME_COMPARATOR =
+ new Comparator<ScheduledRecording>() {
+ @Override
+ public int compare(ScheduledRecording lhs, ScheduledRecording rhs) {
+ return Long.compare(lhs.mEndTimeMs, rhs.mEndTimeMs);
+ }
+ };
+
+ /** Compares ID in ascending order. The schedule with the larger ID was created later. */
+ public static final Comparator<ScheduledRecording> ID_COMPARATOR =
+ new Comparator<ScheduledRecording>() {
+ @Override
+ public int compare(ScheduledRecording lhs, ScheduledRecording rhs) {
+ return Long.compare(lhs.mId, rhs.mId);
+ }
+ };
+
+ /** Compares the priority in ascending order. */
+ public static final Comparator<ScheduledRecording> PRIORITY_COMPARATOR =
+ new Comparator<ScheduledRecording>() {
+ @Override
+ public int compare(ScheduledRecording lhs, ScheduledRecording rhs) {
+ return Long.compare(lhs.mPriority, rhs.mPriority);
+ }
+ };
/**
* Compares start time in ascending order and then priority in descending order and then ID in
* descending order.
*/
- public static final Comparator<ScheduledRecording> START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR
- = new CompositeComparator<>(START_TIME_COMPARATOR, PRIORITY_COMPARATOR.reversed(),
- ID_COMPARATOR.reversed());
+ public static final Comparator<ScheduledRecording> START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR =
+ new CompositeComparator<>(
+ START_TIME_COMPARATOR,
+ PRIORITY_COMPARATOR.reversed(),
+ ID_COMPARATOR.reversed());
- /**
- * Builds scheduled recordings from programs.
- */
+ /** Builds scheduled recordings from programs. */
public static Builder builder(String inputId, Program p) {
return new Builder()
.setInputId(inputId)
.setChannelId(p.getChannelId())
- .setStartTimeMs(p.getStartTimeUtcMillis()).setEndTimeMs(p.getEndTimeUtcMillis())
+ .setStartTimeMs(p.getStartTimeUtcMillis())
+ .setEndTimeMs(p.getEndTimeUtcMillis())
.setProgramId(p.getId())
.setProgramTitle(p.getTitle())
.setSeasonNumber(p.getSeasonNumber())
@@ -138,9 +128,7 @@ public final class ScheduledRecording implements Parcelable {
.setType(TYPE_TIMED);
}
- /**
- * Creates a new Builder with the values set from the {@link RecordedProgram}.
- */
+ /** Creates a new Builder with the values set from the {@link RecordedProgram}. */
public static Builder builder(RecordedProgram p) {
boolean isProgramRecording = !TextUtils.isEmpty(p.getTitle());
return new Builder()
@@ -157,7 +145,8 @@ public final class ScheduledRecording implements Parcelable {
.setProgramLongDescription(p.getLongDescription())
.setProgramPosterArtUri(p.getPosterArtUri())
.setProgramThumbnailUri(p.getThumbnailUri())
- .setState(STATE_RECORDING_FINISHED);
+ .setState(STATE_RECORDING_FINISHED)
+ .setRecordedProgramId(p.getId());
}
public static final class Builder {
@@ -179,8 +168,10 @@ public final class ScheduledRecording implements Parcelable {
private String mProgramThumbnailUri;
private @RecordingState int mState;
private long mSeriesRecordingId = ID_NOT_SET;
+ private Long mRecodedProgramId;
+ private Integer mFailedReason;
- private Builder() { }
+ private Builder() {}
public Builder setId(long id) {
mId = id;
@@ -272,17 +263,42 @@ public final class ScheduledRecording implements Parcelable {
return this;
}
+ public Builder setRecordedProgramId(Long recordedProgramId) {
+ mRecodedProgramId = recordedProgramId;
+ return this;
+ }
+
+ public Builder setFailedReason(Integer reason) {
+ mFailedReason = reason;
+ return this;
+ }
+
public ScheduledRecording build() {
- return new ScheduledRecording(mId, mPriority, mInputId, mChannelId, mProgramId,
- mProgramTitle, mType, mStartTimeMs, mEndTimeMs, mSeasonNumber, mEpisodeNumber,
- mEpisodeTitle, mProgramDescription, mProgramLongDescription,
- mProgramPosterArtUri, mProgramThumbnailUri, mState, mSeriesRecordingId);
+ return new ScheduledRecording(
+ mId,
+ mPriority,
+ mInputId,
+ mChannelId,
+ mProgramId,
+ mProgramTitle,
+ mType,
+ mStartTimeMs,
+ mEndTimeMs,
+ mSeasonNumber,
+ mEpisodeNumber,
+ mEpisodeTitle,
+ mProgramDescription,
+ mProgramLongDescription,
+ mProgramPosterArtUri,
+ mProgramThumbnailUri,
+ mState,
+ mSeriesRecordingId,
+ mRecodedProgramId,
+ mFailedReason);
}
}
- /**
- * Creates {@link Builder} object from the given original {@code Recording}.
- */
+ /** Creates {@link Builder} object from the given original {@code Recording}. */
public static Builder buildFrom(ScheduledRecording orig) {
return new Builder()
.setId(orig.mId)
@@ -301,14 +317,23 @@ public final class ScheduledRecording implements Parcelable {
.setProgramLongDescription(orig.getProgramLongDescription())
.setProgramPosterArtUri(orig.getProgramPosterArtUri())
.setProgramThumbnailUri(orig.getProgramThumbnailUri())
- .setState(orig.mState).setType(orig.mType);
+ .setState(orig.mState)
+ .setFailedReason(orig.getFailedReason())
+ .setType(orig.mType);
}
@Retention(RetentionPolicy.SOURCE)
- @IntDef({STATE_RECORDING_NOT_STARTED, STATE_RECORDING_IN_PROGRESS, STATE_RECORDING_FINISHED,
- STATE_RECORDING_FAILED, STATE_RECORDING_CLIPPED, STATE_RECORDING_DELETED,
- STATE_RECORDING_CANCELED})
+ @IntDef({
+ STATE_RECORDING_NOT_STARTED,
+ STATE_RECORDING_IN_PROGRESS,
+ STATE_RECORDING_FINISHED,
+ STATE_RECORDING_FAILED,
+ STATE_RECORDING_CLIPPED,
+ STATE_RECORDING_DELETED,
+ STATE_RECORDING_CANCELED
+ })
public @interface RecordingState {}
+
public static final int STATE_RECORDING_NOT_STARTED = 0;
public static final int STATE_RECORDING_IN_PROGRESS = 1;
public static final int STATE_RECORDING_FINISHED = 2;
@@ -317,48 +342,74 @@ public final class ScheduledRecording implements Parcelable {
public static final int STATE_RECORDING_DELETED = 5;
public static final int STATE_RECORDING_CANCELED = 6;
+ /** The reasons of failed recordings */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ FAILED_REASON_OTHER,
+ FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED,
+ FAILED_REASON_NOT_FINISHED,
+ FAILED_REASON_SCHEDULER_STOPPED,
+ FAILED_REASON_INVALID_CHANNEL,
+ FAILED_REASON_MESSAGE_NOT_SENT,
+ FAILED_REASON_CONNECTION_FAILED,
+ FAILED_REASON_RESOURCE_BUSY,
+ FAILED_REASON_INPUT_UNAVAILABLE,
+ FAILED_REASON_INPUT_DVR_UNSUPPORTED,
+ FAILED_REASON_INSUFFICIENT_SPACE
+ })
+ public @interface RecordingFailedReason {}
+
+ public static final int FAILED_REASON_OTHER = 0;
+ public static final int FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED = 1;
+ public static final int FAILED_REASON_NOT_FINISHED = 2;
+ public static final int FAILED_REASON_SCHEDULER_STOPPED = 3;
+ public static final int FAILED_REASON_INVALID_CHANNEL = 4;
+ public static final int FAILED_REASON_MESSAGE_NOT_SENT = 5;
+ public static final int FAILED_REASON_CONNECTION_FAILED = 6;
+ public static final int FAILED_REASON_RESOURCE_BUSY = 7;
+ // For the following reasons, show advice to users
+ public static final int FAILED_REASON_INPUT_UNAVAILABLE = 8;
+ public static final int FAILED_REASON_INPUT_DVR_UNSUPPORTED = 9;
+ public static final int FAILED_REASON_INSUFFICIENT_SPACE = 10;
+
@Retention(RetentionPolicy.SOURCE)
@IntDef({TYPE_TIMED, TYPE_PROGRAM})
public @interface RecordingType {}
- /**
- * Record with given time range.
- */
+ /** Record with given time range. */
public static final int TYPE_TIMED = 1;
- /**
- * Record with a given program.
- */
+ /** Record with a given program. */
public static final int TYPE_PROGRAM = 2;
@RecordingType private final int mType;
/**
- * Use this projection if you want to create {@link ScheduledRecording} 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 #fromCursor
- Schedules._ID,
- Schedules.COLUMN_PRIORITY,
- Schedules.COLUMN_TYPE,
- Schedules.COLUMN_INPUT_ID,
- Schedules.COLUMN_CHANNEL_ID,
- Schedules.COLUMN_PROGRAM_ID,
- Schedules.COLUMN_PROGRAM_TITLE,
- Schedules.COLUMN_START_TIME_UTC_MILLIS,
- Schedules.COLUMN_END_TIME_UTC_MILLIS,
- Schedules.COLUMN_SEASON_NUMBER,
- Schedules.COLUMN_EPISODE_NUMBER,
- Schedules.COLUMN_EPISODE_TITLE,
- Schedules.COLUMN_PROGRAM_DESCRIPTION,
- Schedules.COLUMN_PROGRAM_LONG_DESCRIPTION,
- Schedules.COLUMN_PROGRAM_POST_ART_URI,
- Schedules.COLUMN_PROGRAM_THUMBNAIL_URI,
- Schedules.COLUMN_STATE,
- Schedules.COLUMN_SERIES_RECORDING_ID};
+ // Columns must match what is read in #fromCursor
+ Schedules._ID,
+ Schedules.COLUMN_PRIORITY,
+ Schedules.COLUMN_TYPE,
+ Schedules.COLUMN_INPUT_ID,
+ Schedules.COLUMN_CHANNEL_ID,
+ Schedules.COLUMN_PROGRAM_ID,
+ Schedules.COLUMN_PROGRAM_TITLE,
+ Schedules.COLUMN_START_TIME_UTC_MILLIS,
+ Schedules.COLUMN_END_TIME_UTC_MILLIS,
+ Schedules.COLUMN_SEASON_NUMBER,
+ Schedules.COLUMN_EPISODE_NUMBER,
+ Schedules.COLUMN_EPISODE_TITLE,
+ Schedules.COLUMN_PROGRAM_DESCRIPTION,
+ Schedules.COLUMN_PROGRAM_LONG_DESCRIPTION,
+ Schedules.COLUMN_PROGRAM_POST_ART_URI,
+ Schedules.COLUMN_PROGRAM_THUMBNAIL_URI,
+ Schedules.COLUMN_STATE,
+ Schedules.COLUMN_FAILED_REASON,
+ Schedules.COLUMN_SERIES_RECORDING_ID
+ };
- /**
- * Creates {@link ScheduledRecording} object from the given {@link Cursor}.
- */
+ /** Creates {@link ScheduledRecording} object from the given {@link Cursor}. */
public static ScheduledRecording fromCursor(Cursor c) {
int index = -1;
return new Builder()
@@ -379,6 +430,7 @@ public final class ScheduledRecording implements Parcelable {
.setProgramPosterArtUri(c.getString(++index))
.setProgramThumbnailUri(c.getString(++index))
.setState(recordingState(c.getString(++index)))
+ .setFailedReason(recordingFailedReason(c.getString(++index)))
.setSeriesRecordingId(c.getLong(++index))
.build();
}
@@ -403,6 +455,7 @@ public final class ScheduledRecording implements Parcelable {
values.put(Schedules.COLUMN_PROGRAM_POST_ART_URI, r.getProgramPosterArtUri());
values.put(Schedules.COLUMN_PROGRAM_THUMBNAIL_URI, r.getProgramThumbnailUri());
values.put(Schedules.COLUMN_STATE, recordingState(r.getState()));
+ values.put(Schedules.COLUMN_FAILED_REASON, recordingFailedReason(r.getFailedReason()));
values.put(Schedules.COLUMN_TYPE, recordingType(r.getType()));
if (r.getSeriesRecordingId() != ID_NOT_SET) {
values.put(Schedules.COLUMN_SERIES_RECORDING_ID, r.getSeriesRecordingId());
@@ -431,42 +484,40 @@ public final class ScheduledRecording implements Parcelable {
.setProgramPosterArtUri(in.readString())
.setProgramThumbnailUri(in.readString())
.setState(in.readInt())
+ .setFailedReason(recordingFailedReason(in.readString()))
.setSeriesRecordingId(in.readLong())
.build();
}
public static final Parcelable.Creator<ScheduledRecording> CREATOR =
new Parcelable.Creator<ScheduledRecording>() {
- @Override
- public ScheduledRecording createFromParcel(Parcel in) {
- return ScheduledRecording.fromParcel(in);
- }
-
- @Override
- public ScheduledRecording[] newArray(int size) {
- return new ScheduledRecording[size];
- }
- };
-
- /**
- * The ID internal to Live TV
- */
+ @Override
+ public ScheduledRecording createFromParcel(Parcel in) {
+ return ScheduledRecording.fromParcel(in);
+ }
+
+ @Override
+ public ScheduledRecording[] newArray(int size) {
+ return new ScheduledRecording[size];
+ }
+ };
+
+ /** The ID internal to Live TV */
private long mId;
/**
* The priority of this recording.
*
- * <p> The highest number is recorded first. If there is a tie in priority then the higher id
+ * <p>The highest number is recorded first. If there is a tie in priority then the higher id
* wins.
*/
private final long mPriority;
private final String mInputId;
private final long mChannelId;
- /**
- * Optional id of the associated program.
- */
+ /** Optional id of the associated program. */
private final long mProgramId;
+
private final String mProgramTitle;
private final long mStartTimeMs;
@@ -480,12 +531,30 @@ public final class ScheduledRecording implements Parcelable {
private final String mProgramThumbnailUri;
@RecordingState private final int mState;
private final long mSeriesRecordingId;
-
- private ScheduledRecording(long id, long priority, String inputId, long channelId, long programId,
- String programTitle, @RecordingType int type, long startTime, long endTime,
- String seasonNumber, String episodeNumber, String episodeTitle,
- String programDescription, String programLongDescription, String programPosterArtUri,
- String programThumbnailUri, @RecordingState int state, long seriesRecordingId) {
+ private final Long mRecordedProgramId;
+ private final Integer mFailedReason;
+
+ private ScheduledRecording(
+ long id,
+ long priority,
+ String inputId,
+ long channelId,
+ long programId,
+ String programTitle,
+ @RecordingType int type,
+ long startTime,
+ long endTime,
+ String seasonNumber,
+ String episodeNumber,
+ String episodeTitle,
+ String programDescription,
+ String programLongDescription,
+ String programPosterArtUri,
+ String programThumbnailUri,
+ @RecordingState int state,
+ long seriesRecordingId,
+ Long recordedProgramId,
+ Integer failedReason) {
mId = id;
mPriority = priority;
mInputId = inputId;
@@ -504,139 +573,122 @@ public final class ScheduledRecording implements Parcelable {
mProgramThumbnailUri = programThumbnailUri;
mState = state;
mSeriesRecordingId = seriesRecordingId;
+ mRecordedProgramId = recordedProgramId;
+ mFailedReason = failedReason;
}
/**
- * Returns recording schedule type. The possible types are {@link #TYPE_PROGRAM} and
- * {@link #TYPE_TIMED}.
+ * Returns recording schedule type. The possible types are {@link #TYPE_PROGRAM} and {@link
+ * #TYPE_TIMED}.
*/
@RecordingType
public int getType() {
return mType;
}
- /**
- * Returns schedules' input id.
- */
+ /** Returns schedules' input id. */
public String getInputId() {
return mInputId;
}
- /**
- * Returns recorded {@link Channel}.
- */
+ /** Returns recorded {@link Channel}. */
public long getChannelId() {
return mChannelId;
}
- /**
- * Return the optional program id
- */
+ /** Return the optional program id */
public long getProgramId() {
return mProgramId;
}
- /**
- * Return the optional program Title
- */
+ /** Return the optional program Title */
public String getProgramTitle() {
return mProgramTitle;
}
- /**
- * Returns started time.
- */
+ /** Returns started time. */
public long getStartTimeMs() {
return mStartTimeMs;
}
- /**
- * Returns ended time.
- */
+ /** Returns ended time. */
public long getEndTimeMs() {
return mEndTimeMs;
}
- /**
- * Returns the season number.
- */
+ /** Returns the season number. */
public String getSeasonNumber() {
return mSeasonNumber;
}
- /**
- * Returns the episode number.
- */
+ /** Returns the episode number. */
public String getEpisodeNumber() {
return mEpisodeNumber;
}
- /**
- * Returns the episode title.
- */
+ /** Returns the episode title. */
public String getEpisodeTitle() {
return mEpisodeTitle;
}
- /**
- * Returns the description of program.
- */
+ /** Returns the description of program. */
public String getProgramDescription() {
return mProgramDescription;
}
- /**
- * Returns the long description of program.
- */
+ /** Returns the long description of program. */
public String getProgramLongDescription() {
return mProgramLongDescription;
}
- /**
- * Returns the poster uri of program.
- */
+ /** Returns the poster uri of program. */
public String getProgramPosterArtUri() {
return mProgramPosterArtUri;
}
- /**
- * Returns the thumb nail uri of program.
- */
+ /** Returns the thumb nail uri of program. */
public String getProgramThumbnailUri() {
return mProgramThumbnailUri;
}
- /**
- * Returns duration.
- */
+ /** Returns duration. */
public long getDuration() {
return mEndTimeMs - mStartTimeMs;
}
/**
- * Returns the state. The possible states are {@link #STATE_RECORDING_NOT_STARTED},
- * {@link #STATE_RECORDING_IN_PROGRESS}, {@link #STATE_RECORDING_FINISHED},
- * {@link #STATE_RECORDING_FAILED}, {@link #STATE_RECORDING_CLIPPED} and
- * {@link #STATE_RECORDING_DELETED}.
+ * Returns the state. The possible states are {@link #STATE_RECORDING_NOT_STARTED}, {@link
+ * #STATE_RECORDING_IN_PROGRESS}, {@link #STATE_RECORDING_FINISHED}, {@link
+ * #STATE_RECORDING_FAILED}, {@link #STATE_RECORDING_CLIPPED} and {@link
+ * #STATE_RECORDING_DELETED}.
*/
- @RecordingState public int getState() {
+ @RecordingState
+ public int getState() {
return mState;
}
- /**
- * Returns the ID of the {@link SeriesRecording} including this schedule.
- */
+ /** Returns the ID of the {@link SeriesRecording} including this schedule. */
public long getSeriesRecordingId() {
return mSeriesRecordingId;
}
+ /** Returns the ID of the corresponding {@link RecordedProgram}. */
+ @Nullable
+ public Long getRecordedProgramId() {
+ return mRecordedProgramId;
+ }
+
+ /** Returns the failed reason of the {@link ScheduledRecording}. */
+ @Nullable @RecordingFailedReason
+ public Integer getFailedReason() {
+ return mFailedReason;
+ }
+
public long getId() {
return mId;
}
- /**
- * Sets the ID;
- */
+ /** Sets the ID; */
public void setId(long id) {
mId = id;
}
@@ -645,21 +697,23 @@ public final class ScheduledRecording implements Parcelable {
return mPriority;
}
- /**
- * Returns season number, episode number and episode title for display.
- */
+ /** Returns season number, episode number and episode title for display. */
public String getEpisodeDisplayTitle(Context context) {
if (!TextUtils.isEmpty(mEpisodeNumber)) {
String episodeTitle = mEpisodeTitle == null ? "" : mEpisodeTitle;
if (TextUtils.equals(mSeasonNumber, "0")) {
// Do not show "S0: ".
- return String.format(context.getResources().getString(
- R.string.display_episode_title_format_no_season_number),
- mEpisodeNumber, episodeTitle);
+ return String.format(
+ context.getResources()
+ .getString(R.string.display_episode_title_format_no_season_number),
+ mEpisodeNumber,
+ episodeTitle);
} else {
- return String.format(context.getResources().getString(
- R.string.display_episode_title_format),
- mSeasonNumber, mEpisodeNumber, episodeTitle);
+ return String.format(
+ context.getResources().getString(R.string.display_episode_title_format),
+ mSeasonNumber,
+ mEpisodeNumber,
+ episodeTitle);
}
}
return mEpisodeTitle;
@@ -673,15 +727,14 @@ public final class ScheduledRecording implements Parcelable {
if (!TextUtils.isEmpty(mProgramTitle)) {
return mProgramTitle;
}
- Channel channel = TvApplication.getSingletons(context).getChannelDataManager()
- .getChannel(mChannelId);
- return channel != null ? channel.getDisplayName()
+ Channel channel =
+ TvSingletons.getSingletons(context).getChannelDataManager().getChannel(mChannelId);
+ return channel != null
+ ? channel.getDisplayName()
: context.getString(R.string.no_program_information);
}
- /**
- * Converts a string to a @RecordingType int, defaulting to {@link #TYPE_TIMED}.
- */
+ /** Converts a string to a @RecordingType int, defaulting to {@link #TYPE_TIMED}. */
private static @RecordingType int recordingType(String type) {
switch (type) {
case Schedules.TYPE_TIMED:
@@ -689,14 +742,12 @@ public final class ScheduledRecording implements Parcelable {
case Schedules.TYPE_PROGRAM:
return TYPE_PROGRAM;
default:
- SoftPreconditions.checkArgument(false, TAG, "Unknown recording type " + type);
+ SoftPreconditions.checkArgument(false, TAG, "Unknown recording type %s", type);
return TYPE_TIMED;
}
}
- /**
- * Converts a @RecordingType int to a string, defaulting to {@link Schedules#TYPE_TIMED}.
- */
+ /** Converts a @RecordingType int to a string, defaulting to {@link Schedules#TYPE_TIMED}. */
private static String recordingType(@RecordingType int type) {
switch (type) {
case TYPE_TIMED:
@@ -704,14 +755,14 @@ public final class ScheduledRecording implements Parcelable {
case TYPE_PROGRAM:
return Schedules.TYPE_PROGRAM;
default:
- SoftPreconditions.checkArgument(false, TAG, "Unknown recording type " + type);
+ SoftPreconditions.checkArgument(false, TAG, "Unknown recording type %s", type);
return Schedules.TYPE_TIMED;
}
}
/**
- * Converts a string to a @RecordingState int, defaulting to
- * {@link #STATE_RECORDING_NOT_STARTED}.
+ * Converts a string to a @RecordingState int, defaulting to {@link
+ * #STATE_RECORDING_NOT_STARTED}.
*/
private static @RecordingState int recordingState(String state) {
switch (state) {
@@ -730,14 +781,14 @@ public final class ScheduledRecording implements Parcelable {
case Schedules.STATE_RECORDING_CANCELED:
return STATE_RECORDING_CANCELED;
default:
- SoftPreconditions.checkArgument(false, TAG, "Unknown recording state" + state);
+ SoftPreconditions.checkArgument(false, TAG, "Unknown recording state %s", state);
return STATE_RECORDING_NOT_STARTED;
}
}
/**
- * Converts a @RecordingState int to string, defaulting to
- * {@link Schedules#STATE_RECORDING_NOT_STARTED}.
+ * Converts a @RecordingState int to string, defaulting to {@link
+ * Schedules#STATE_RECORDING_NOT_STARTED}.
*/
private static String recordingState(@RecordingState int state) {
switch (state) {
@@ -756,46 +807,138 @@ public final class ScheduledRecording implements Parcelable {
case STATE_RECORDING_CANCELED:
return Schedules.STATE_RECORDING_CANCELED;
default:
- SoftPreconditions.checkArgument(false, TAG, "Unknown recording state" + state);
+ SoftPreconditions.checkArgument(false, TAG, "Unknown recording state %s", state);
return Schedules.STATE_RECORDING_NOT_STARTED;
}
}
/**
- * Checks if the {@code period} overlaps with the recording time.
+ * Converts a string to a failed reason integer, defaulting to {@link
+ * #FAILED_REASON_OTHER}.
*/
- public boolean isOverLapping(Range<Long> period) {
- return mStartTimeMs < period.getUpper() && mEndTimeMs > period.getLower();
+ private static Integer recordingFailedReason(String reason) {
+ if (TextUtils.isEmpty(reason)) {
+ return null;
+ }
+ switch (reason) {
+ case Schedules.FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED:
+ return FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED;
+ case Schedules.FAILED_REASON_NOT_FINISHED:
+ return FAILED_REASON_NOT_FINISHED;
+ case Schedules.FAILED_REASON_SCHEDULER_STOPPED:
+ return FAILED_REASON_SCHEDULER_STOPPED;
+ case Schedules.FAILED_REASON_INVALID_CHANNEL:
+ return FAILED_REASON_INVALID_CHANNEL;
+ case Schedules.FAILED_REASON_MESSAGE_NOT_SENT:
+ return FAILED_REASON_MESSAGE_NOT_SENT;
+ case Schedules.FAILED_REASON_CONNECTION_FAILED:
+ return FAILED_REASON_CONNECTION_FAILED;
+ case Schedules.FAILED_REASON_RESOURCE_BUSY:
+ return FAILED_REASON_RESOURCE_BUSY;
+ case Schedules.FAILED_REASON_INPUT_UNAVAILABLE:
+ return FAILED_REASON_INPUT_UNAVAILABLE;
+ case Schedules.FAILED_REASON_INPUT_DVR_UNSUPPORTED:
+ return FAILED_REASON_INPUT_DVR_UNSUPPORTED;
+ case Schedules.FAILED_REASON_INSUFFICIENT_SPACE:
+ return FAILED_REASON_INSUFFICIENT_SPACE;
+ case Schedules.FAILED_REASON_OTHER:
+ default:
+ return FAILED_REASON_OTHER;
+ }
}
/**
- * Checks if the {@code schedule} overlaps with this schedule.
+ * Converts a failed reason integer to string, defaulting to {@link
+ * Schedules#FAILED_REASON_OTHER}.
*/
+ private static String recordingFailedReason(Integer reason) {
+ if (reason == null) {
+ return null;
+ }
+ switch (reason) {
+ case FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED:
+ return Schedules.FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED;
+ case FAILED_REASON_NOT_FINISHED:
+ return Schedules.FAILED_REASON_NOT_FINISHED;
+ case FAILED_REASON_SCHEDULER_STOPPED:
+ return Schedules.FAILED_REASON_SCHEDULER_STOPPED;
+ case FAILED_REASON_INVALID_CHANNEL:
+ return Schedules.FAILED_REASON_INVALID_CHANNEL;
+ case FAILED_REASON_MESSAGE_NOT_SENT:
+ return Schedules.FAILED_REASON_MESSAGE_NOT_SENT;
+ case FAILED_REASON_CONNECTION_FAILED:
+ return Schedules.FAILED_REASON_CONNECTION_FAILED;
+ case FAILED_REASON_RESOURCE_BUSY:
+ return Schedules.FAILED_REASON_RESOURCE_BUSY;
+ case FAILED_REASON_INPUT_UNAVAILABLE:
+ return Schedules.FAILED_REASON_INPUT_UNAVAILABLE;
+ case FAILED_REASON_INPUT_DVR_UNSUPPORTED:
+ return Schedules.FAILED_REASON_INPUT_DVR_UNSUPPORTED;
+ case FAILED_REASON_INSUFFICIENT_SPACE:
+ return Schedules.FAILED_REASON_INSUFFICIENT_SPACE;
+ case FAILED_REASON_OTHER: // fall through
+ default:
+ return Schedules.FAILED_REASON_OTHER;
+ }
+ }
+
+ /** Checks if the {@code period} overlaps with the recording time. */
+ public boolean isOverLapping(Range<Long> period) {
+ return mStartTimeMs < period.getUpper() && mEndTimeMs > period.getLower();
+ }
+
+ /** Checks if the {@code schedule} overlaps with this schedule. */
public boolean isOverLapping(ScheduledRecording schedule) {
return mStartTimeMs < schedule.getEndTimeMs() && mEndTimeMs > schedule.getStartTimeMs();
}
@Override
public String toString() {
- return "ScheduledRecording[" + mId
+ return "ScheduledRecording["
+ + mId
+ "]"
- + "(inputId=" + mInputId
- + ",channelId=" + mChannelId
- + ",programId=" + mProgramId
- + ",programTitle=" + mProgramTitle
- + ",type=" + mType
- + ",startTime=" + Utils.toIsoDateTimeString(mStartTimeMs) + "(" + mStartTimeMs + ")"
- + ",endTime=" + Utils.toIsoDateTimeString(mEndTimeMs) + "(" + mEndTimeMs + ")"
- + ",seasonNumber=" + mSeasonNumber
- + ",episodeNumber=" + mEpisodeNumber
- + ",episodeTitle=" + mEpisodeTitle
- + ",programDescription=" + mProgramDescription
- + ",programLongDescription=" + mProgramLongDescription
- + ",programPosterArtUri=" + mProgramPosterArtUri
- + ",programThumbnailUri=" + mProgramThumbnailUri
- + ",state=" + mState
- + ",priority=" + mPriority
- + ",seriesRecordingId=" + mSeriesRecordingId
+ + "(inputId="
+ + mInputId
+ + ",channelId="
+ + mChannelId
+ + ",programId="
+ + mProgramId
+ + ",programTitle="
+ + mProgramTitle
+ + ",type="
+ + mType
+ + ",startTime="
+ + CommonUtils.toIsoDateTimeString(mStartTimeMs)
+ + "("
+ + mStartTimeMs
+ + ")"
+ + ",endTime="
+ + CommonUtils.toIsoDateTimeString(mEndTimeMs)
+ + "("
+ + mEndTimeMs
+ + ")"
+ + ",seasonNumber="
+ + mSeasonNumber
+ + ",episodeNumber="
+ + mEpisodeNumber
+ + ",episodeTitle="
+ + mEpisodeTitle
+ + ",programDescription="
+ + mProgramDescription
+ + ",programLongDescription="
+ + mProgramLongDescription
+ + ",programPosterArtUri="
+ + mProgramPosterArtUri
+ + ",programThumbnailUri="
+ + mProgramThumbnailUri
+ + ",state="
+ + mState
+ + ",failedReason="
+ + mFailedReason
+ + ",priority="
+ + mPriority
+ + ",seriesRecordingId="
+ + mSeriesRecordingId
+ ")";
}
@@ -823,23 +966,25 @@ public final class ScheduledRecording implements Parcelable {
out.writeString(mProgramPosterArtUri);
out.writeString(mProgramThumbnailUri);
out.writeInt(mState);
+ out.writeString(recordingFailedReason(mFailedReason));
out.writeLong(mSeriesRecordingId);
}
- /**
- * Returns {@code true} if the recording is not started yet, otherwise @{code false}.
- */
+ /** Returns {@code true} if the recording is not started yet, otherwise @{code false}. */
public boolean isNotStarted() {
return mState == STATE_RECORDING_NOT_STARTED;
}
- /**
- * Returns {@code true} if the recording is in progress, otherwise @{code false}.
- */
+ /** Returns {@code true} if the recording is in progress, otherwise @{code false}. */
public boolean isInProgress() {
return mState == STATE_RECORDING_IN_PROGRESS;
}
+ /** Returns {@code true} if the recording is finished, otherwise @{code false}. */
+ public boolean isFinished() {
+ return mState == STATE_RECORDING_FINISHED;
+ }
+
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ScheduledRecording)) {
@@ -862,20 +1007,34 @@ public final class ScheduledRecording implements Parcelable {
&& Objects.equals(mProgramPosterArtUri, r.getProgramPosterArtUri())
&& Objects.equals(mProgramThumbnailUri, r.getProgramThumbnailUri())
&& mState == r.mState
+ && Objects.equals(mFailedReason, r.mFailedReason)
&& mSeriesRecordingId == r.mSeriesRecordingId;
}
@Override
public int hashCode() {
- return Objects.hash(mId, mPriority, mChannelId, mProgramId, mProgramTitle, mType,
- mStartTimeMs, mEndTimeMs, mSeasonNumber, mEpisodeNumber, mEpisodeTitle,
- mProgramDescription, mProgramLongDescription, mProgramPosterArtUri,
- mProgramThumbnailUri, mState, mSeriesRecordingId);
+ return Objects.hash(
+ mId,
+ mPriority,
+ mChannelId,
+ mProgramId,
+ mProgramTitle,
+ mType,
+ mStartTimeMs,
+ mEndTimeMs,
+ mSeasonNumber,
+ mEpisodeNumber,
+ mEpisodeTitle,
+ mProgramDescription,
+ mProgramLongDescription,
+ mProgramPosterArtUri,
+ mProgramThumbnailUri,
+ mState,
+ mFailedReason,
+ mSeriesRecordingId);
}
- /**
- * Returns an array containing all of the elements in the list.
- */
+ /** Returns an array containing all of the elements in the list. */
public static ScheduledRecording[] toArray(Collection<ScheduledRecording> schedules) {
return schedules.toArray(new ScheduledRecording[schedules.size()]);
}
diff --git a/src/com/android/tv/dvr/data/SeasonEpisodeNumber.java b/src/com/android/tv/dvr/data/SeasonEpisodeNumber.java
index 89533dbb..c697451a 100644
--- a/src/com/android/tv/dvr/data/SeasonEpisodeNumber.java
+++ b/src/com/android/tv/dvr/data/SeasonEpisodeNumber.java
@@ -17,20 +17,15 @@
package com.android.tv.dvr.data;
import android.text.TextUtils;
-
import java.util.Objects;
-/**
- * A plain java object which includes the season/episode number for the series recording.
- */
+/** A plain java object which includes the season/episode number for the series recording. */
public class SeasonEpisodeNumber {
public final long seriesRecordingId;
public final String seasonNumber;
public final String episodeNumber;
- /**
- * Creates a new Builder with the values set from an existing {@link ScheduledRecording}.
- */
+ /** Creates a new Builder with the values set from an existing {@link ScheduledRecording}. */
public SeasonEpisodeNumber(ScheduledRecording r) {
this(r.getSeriesRecordingId(), r.getSeasonNumber(), r.getEpisodeNumber());
}
@@ -47,7 +42,8 @@ public class SeasonEpisodeNumber {
return true;
}
if (!(o instanceof SeasonEpisodeNumber)
- || TextUtils.isEmpty(seasonNumber) || TextUtils.isEmpty(episodeNumber)) {
+ || TextUtils.isEmpty(seasonNumber)
+ || TextUtils.isEmpty(episodeNumber)) {
return false;
}
SeasonEpisodeNumber that = (SeasonEpisodeNumber) o;
@@ -63,10 +59,13 @@ public class SeasonEpisodeNumber {
@Override
public String toString() {
- return "SeasonEpisodeNumber{" +
- "seriesRecordingId=" + seriesRecordingId +
- ", seasonNumber='" + seasonNumber +
- ", episodeNumber=" + episodeNumber +
- '}';
+ return "SeasonEpisodeNumber{"
+ + "seriesRecordingId="
+ + seriesRecordingId
+ + ", seasonNumber='"
+ + seasonNumber
+ + ", episodeNumber="
+ + episodeNumber
+ + '}';
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/data/SeriesInfo.java b/src/com/android/tv/dvr/data/SeriesInfo.java
index a0dec4a4..aa622c3d 100644
--- a/src/com/android/tv/dvr/data/SeriesInfo.java
+++ b/src/com/android/tv/dvr/data/SeriesInfo.java
@@ -16,9 +16,7 @@
package com.android.tv.dvr.data;
-/**
- * Series information.
- */
+/** Series information. */
public class SeriesInfo {
private final String mId;
private final String mTitle;
@@ -28,8 +26,14 @@ public class SeriesInfo {
private final String mPosterUri;
private final String mPhotoUri;
- public SeriesInfo(String id, String title, String description, String longDescription,
- int[] canonicalGenreIds, String posterUri, String photoUri) {
+ public SeriesInfo(
+ String id,
+ String title,
+ String description,
+ String longDescription,
+ int[] canonicalGenreIds,
+ String posterUri,
+ String photoUri) {
this.mId = id;
this.mTitle = title;
this.mDescription = description;
@@ -39,37 +43,37 @@ public class SeriesInfo {
this.mPhotoUri = photoUri;
}
- /** Returns the ID. **/
+ /** Returns the ID. * */
public String getId() {
return mId;
}
- /** Returns the title. **/
+ /** Returns the title. * */
public String getTitle() {
return mTitle;
}
- /** Returns the description. **/
+ /** Returns the description. * */
public String getDescription() {
return mDescription;
}
- /** Returns the description. **/
+ /** Returns the description. * */
public String getLongDescription() {
return mLongDescription;
}
- /** Returns the canonical genre IDs. **/
+ /** Returns the canonical genre IDs. * */
public int[] getCanonicalGenreIds() {
return mCanonicalGenreIds;
}
- /** Returns the poster URI. **/
+ /** Returns the poster URI. * */
public String getPosterUri() {
return mPosterUri;
}
- /** Returns the photo URI. **/
+ /** Returns the photo URI. * */
public String getPhotoUri() {
return mPhotoUri;
}
diff --git a/src/com/android/tv/dvr/data/SeriesRecording.java b/src/com/android/tv/dvr/data/SeriesRecording.java
index 822e7320..96b3425a 100644
--- a/src/com/android/tv/dvr/data/SeriesRecording.java
+++ b/src/com/android/tv/dvr/data/SeriesRecording.java
@@ -21,15 +21,12 @@ import android.database.Cursor;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.IntDef;
-import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
-
import com.android.tv.data.BaseProgram;
import com.android.tv.data.Program;
import com.android.tv.dvr.DvrScheduleManager;
import com.android.tv.dvr.provider.DvrContract.SeriesRecordings;
import com.android.tv.util.Utils;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
@@ -40,68 +37,55 @@ import java.util.Objects;
/**
* Schedules the recording of a Series of Programs.
*
- * <p>
- * Contains the data needed to create new ScheduleRecordings as the programs become available in
+ * <p>Contains the data needed to create new ScheduleRecordings as the programs become available in
* the EPG.
*/
public class SeriesRecording implements Parcelable {
- /**
- * Indicates that the ID is not assigned yet.
- */
+ /** Indicates that the ID is not assigned yet. */
public static final long ID_NOT_SET = 0;
- /**
- * The default priority of this recording.
- */
+ /** The default priority of this recording. */
public static final long DEFAULT_PRIORITY = Long.MAX_VALUE >> 1;
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true,
- value = {OPTION_CHANNEL_ONE, OPTION_CHANNEL_ALL})
+ @IntDef(
+ flag = true,
+ value = {OPTION_CHANNEL_ONE, OPTION_CHANNEL_ALL}
+ )
public @interface ChannelOption {}
- /**
- * An option which indicates that the episodes in one channel are recorded.
- */
+ /** An option which indicates that the episodes in one channel are recorded. */
public static final int OPTION_CHANNEL_ONE = 0;
- /**
- * An option which indicates that the episodes in all the channels are recorded.
- */
+ /** An option which indicates that the episodes in all the channels are recorded. */
public static final int OPTION_CHANNEL_ALL = 1;
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true,
- value = {STATE_SERIES_NORMAL, STATE_SERIES_STOPPED})
+ @IntDef(
+ flag = true,
+ value = {STATE_SERIES_NORMAL, STATE_SERIES_STOPPED}
+ )
public @interface SeriesState {}
- /**
- * The state indicates that the series recording is a normal one.
- */
+ /** The state indicates that the series recording is a normal one. */
public static final int STATE_SERIES_NORMAL = 0;
- /**
- * The state indicates that the series recording is stopped.
- */
+ /** The state indicates that the series recording is stopped. */
public static final int STATE_SERIES_STOPPED = 1;
- /**
- * Compare priority in descending order.
- */
+ /** Compare priority in descending order. */
public static final Comparator<SeriesRecording> PRIORITY_COMPARATOR =
new Comparator<SeriesRecording>() {
- @Override
- public int compare(SeriesRecording lhs, SeriesRecording rhs) {
- int value = Long.compare(rhs.mPriority, lhs.mPriority);
- if (value == 0) {
- // New recording has the higher priority.
- value = Long.compare(rhs.mId, lhs.mId);
- }
- return value;
- }
- };
+ @Override
+ public int compare(SeriesRecording lhs, SeriesRecording rhs) {
+ int value = Long.compare(rhs.mPriority, lhs.mPriority);
+ if (value == 0) {
+ // New recording has the higher priority.
+ value = Long.compare(rhs.mId, lhs.mId);
+ }
+ return value;
+ }
+ };
- /**
- * Compare ID in ascending order.
- */
+ /** Compare ID in ascending order. */
public static final Comparator<SeriesRecording> ID_COMPARATOR =
new Comparator<SeriesRecording>() {
@Override
@@ -126,9 +110,7 @@ public class SeriesRecording implements Parcelable {
.setPhotoUri(p.getThumbnailUri());
}
- /**
- * Creates a new Builder with the values set from an existing {@link SeriesRecording}.
- */
+ /** Creates a new Builder with the values set from an existing {@link SeriesRecording}. */
public static Builder buildFrom(SeriesRecording r) {
return new Builder()
.setId(r.mId)
@@ -149,30 +131,28 @@ public class SeriesRecording implements Parcelable {
}
/**
- * Use this projection if you want to create {@link SeriesRecording} object using
- * {@link #fromCursor}.
+ * Use this projection if you want to create {@link SeriesRecording} object using {@link
+ * #fromCursor}.
*/
public static final String[] PROJECTION = {
- // Columns must match what is read in fromCursor()
- SeriesRecordings._ID,
- SeriesRecordings.COLUMN_INPUT_ID,
- SeriesRecordings.COLUMN_CHANNEL_ID,
- SeriesRecordings.COLUMN_PRIORITY,
- SeriesRecordings.COLUMN_TITLE,
- SeriesRecordings.COLUMN_SHORT_DESCRIPTION,
- SeriesRecordings.COLUMN_LONG_DESCRIPTION,
- SeriesRecordings.COLUMN_SERIES_ID,
- SeriesRecordings.COLUMN_START_FROM_EPISODE,
- SeriesRecordings.COLUMN_START_FROM_SEASON,
- SeriesRecordings.COLUMN_CHANNEL_OPTION,
- SeriesRecordings.COLUMN_CANONICAL_GENRE,
- SeriesRecordings.COLUMN_POSTER_URI,
- SeriesRecordings.COLUMN_PHOTO_URI,
- SeriesRecordings.COLUMN_STATE
+ // Columns must match what is read in fromCursor()
+ SeriesRecordings._ID,
+ SeriesRecordings.COLUMN_INPUT_ID,
+ SeriesRecordings.COLUMN_CHANNEL_ID,
+ SeriesRecordings.COLUMN_PRIORITY,
+ SeriesRecordings.COLUMN_TITLE,
+ SeriesRecordings.COLUMN_SHORT_DESCRIPTION,
+ SeriesRecordings.COLUMN_LONG_DESCRIPTION,
+ SeriesRecordings.COLUMN_SERIES_ID,
+ SeriesRecordings.COLUMN_START_FROM_EPISODE,
+ SeriesRecordings.COLUMN_START_FROM_SEASON,
+ SeriesRecordings.COLUMN_CHANNEL_OPTION,
+ SeriesRecordings.COLUMN_CANONICAL_GENRE,
+ SeriesRecordings.COLUMN_POSTER_URI,
+ SeriesRecordings.COLUMN_PHOTO_URI,
+ SeriesRecordings.COLUMN_STATE
};
- /**
- * Creates {@link SeriesRecording} object from the given {@link Cursor}.
- */
+ /** Creates {@link SeriesRecording} object from the given {@link Cursor}. */
public static SeriesRecording fromCursor(Cursor c) {
int index = -1;
return new Builder()
@@ -195,8 +175,8 @@ public class SeriesRecording implements Parcelable {
}
/**
- * Returns the ContentValues with keys as the columns specified in {@link SeriesRecordings}
- * and the values from {@code r}.
+ * Returns the ContentValues with keys as the columns specified in {@link SeriesRecordings} and
+ * the values from {@code r}.
*/
public static ContentValues toContentValues(SeriesRecording r) {
ContentValues values = new ContentValues();
@@ -214,9 +194,9 @@ public class SeriesRecording implements Parcelable {
values.put(SeriesRecordings.COLUMN_SERIES_ID, r.getSeriesId());
values.put(SeriesRecordings.COLUMN_START_FROM_EPISODE, r.getStartFromEpisode());
values.put(SeriesRecordings.COLUMN_START_FROM_SEASON, r.getStartFromSeason());
- values.put(SeriesRecordings.COLUMN_CHANNEL_OPTION,
- channelOption(r.getChannelOption()));
- values.put(SeriesRecordings.COLUMN_CANONICAL_GENRE,
+ values.put(SeriesRecordings.COLUMN_CHANNEL_OPTION, channelOption(r.getChannelOption()));
+ values.put(
+ SeriesRecordings.COLUMN_CANONICAL_GENRE,
Utils.getCanonicalGenre(r.getCanonicalGenreIds()));
values.put(SeriesRecordings.COLUMN_POSTER_URI, r.getPosterUri());
values.put(SeriesRecordings.COLUMN_PHOTO_URI, r.getPhotoUri());
@@ -234,7 +214,8 @@ public class SeriesRecording implements Parcelable {
return SeriesRecordings.OPTION_CHANNEL_ONE;
}
- @ChannelOption private static int channelOption(String option) {
+ @ChannelOption
+ private static int channelOption(String option) {
switch (option) {
case SeriesRecordings.OPTION_CHANNEL_ONE:
return OPTION_CHANNEL_ONE;
@@ -254,7 +235,8 @@ public class SeriesRecording implements Parcelable {
return SeriesRecordings.STATE_SERIES_NORMAL;
}
- @SeriesState private static int seriesRecordingState(String state) {
+ @SeriesState
+ private static int seriesRecordingState(String state) {
switch (state) {
case SeriesRecordings.STATE_SERIES_NORMAL:
return STATE_SERIES_NORMAL;
@@ -264,9 +246,7 @@ public class SeriesRecording implements Parcelable {
return STATE_SERIES_NORMAL;
}
- /**
- * Builder for {@link SeriesRecording}.
- */
+ /** Builder for {@link SeriesRecording}. */
public static class Builder {
private long mId = ID_NOT_SET;
private long mPriority = DvrScheduleManager.DEFAULT_SERIES_PRIORITY;
@@ -284,141 +264,120 @@ public class SeriesRecording implements Parcelable {
private String mPhotoUri;
private int mState = SeriesRecording.STATE_SERIES_NORMAL;
- /**
- * @see #getId()
- */
+ /** @see #getId() */
public Builder setId(long id) {
mId = id;
return this;
}
- /**
- * @see #getPriority() ()
- */
+ /** @see #getPriority() () */
public Builder setPriority(long priority) {
mPriority = priority;
return this;
}
- /**
- * @see #getTitle()
- */
+ /** @see #getTitle() */
public Builder setTitle(String title) {
mTitle = title;
return this;
}
- /**
- * @see #getDescription()
- */
+ /** @see #getDescription() */
public Builder setDescription(String description) {
mDescription = description;
return this;
}
- /**
- * @see #getLongDescription()
- */
+ /** @see #getLongDescription() */
public Builder setLongDescription(String longDescription) {
mLongDescription = longDescription;
return this;
}
- /**
- * @see #getInputId()
- */
+ /** @see #getInputId() */
public Builder setInputId(String inputId) {
mInputId = inputId;
return this;
}
- /**
- * @see #getChannelId()
- */
+ /** @see #getChannelId() */
public Builder setChannelId(long channelId) {
mChannelId = channelId;
return this;
}
- /**
- * @see #getSeriesId()
- */
+ /** @see #getSeriesId() */
public Builder setSeriesId(String seriesId) {
mSeriesId = seriesId;
return this;
}
- /**
- * @see #getStartFromSeason()
- */
+ /** @see #getStartFromSeason() */
public Builder setStartFromSeason(int startFromSeason) {
mStartFromSeason = startFromSeason;
return this;
}
- /**
- * @see #getChannelOption()
- */
+ /** @see #getChannelOption() */
public Builder setChannelOption(@ChannelOption int option) {
mChannelOption = option;
return this;
}
- /**
- * @see #getStartFromEpisode()
- */
+ /** @see #getStartFromEpisode() */
public Builder setStartFromEpisode(int startFromEpisode) {
mStartFromEpisode = startFromEpisode;
return this;
}
- /**
- * @see #getCanonicalGenreIds()
- */
+ /** @see #getCanonicalGenreIds() */
public Builder setCanonicalGenreIds(String genres) {
mCanonicalGenreIds = Utils.getCanonicalGenreIds(genres);
return this;
}
- /**
- * @see #getCanonicalGenreIds()
- */
+ /** @see #getCanonicalGenreIds() */
public Builder setCanonicalGenreIds(int[] canonicalGenreIds) {
mCanonicalGenreIds = canonicalGenreIds;
return this;
}
- /**
- * @see #getPosterUri()
- */
+ /** @see #getPosterUri() */
public Builder setPosterUri(String posterUri) {
mPosterUri = posterUri;
return this;
}
- /**
- * @see #getPhotoUri()
- */
+ /** @see #getPhotoUri() */
public Builder setPhotoUri(String photoUri) {
mPhotoUri = photoUri;
return this;
}
- /**
- * @see #getState()
- */
+ /** @see #getState() */
public Builder setState(@SeriesState int state) {
mState = state;
return this;
}
- /**
- * Creates a new {@link SeriesRecording}.
- */
+ /** Creates a new {@link SeriesRecording}. */
public SeriesRecording build() {
- return new SeriesRecording(mId, mPriority, mTitle, mDescription, mLongDescription,
- mInputId, mChannelId, mSeriesId, mStartFromSeason, mStartFromEpisode,
- mChannelOption, mCanonicalGenreIds, mPosterUri, mPhotoUri, mState);
+ return new SeriesRecording(
+ mId,
+ mPriority,
+ mTitle,
+ mDescription,
+ mLongDescription,
+ mInputId,
+ mChannelId,
+ mSeriesId,
+ mStartFromSeason,
+ mStartFromEpisode,
+ mChannelOption,
+ mCanonicalGenreIds,
+ mPosterUri,
+ mPhotoUri,
+ mState);
}
}
@@ -444,16 +403,16 @@ public class SeriesRecording implements Parcelable {
public static final Parcelable.Creator<SeriesRecording> CREATOR =
new Parcelable.Creator<SeriesRecording>() {
- @Override
- public SeriesRecording createFromParcel(Parcel in) {
- return SeriesRecording.fromParcel(in);
- }
+ @Override
+ public SeriesRecording createFromParcel(Parcel in) {
+ return SeriesRecording.fromParcel(in);
+ }
- @Override
- public SeriesRecording[] newArray(int size) {
- return new SeriesRecording[size];
- }
- };
+ @Override
+ public SeriesRecording[] newArray(int size) {
+ return new SeriesRecording[size];
+ }
+ };
private long mId;
private final long mPriority;
@@ -471,9 +430,7 @@ public class SeriesRecording implements Parcelable {
private final String mPhotoUri;
@SeriesState private int mState;
- /**
- * The input id of this SeriesRecording.
- */
+ /** The input id of this SeriesRecording. */
public String getInputId() {
return mInputId;
}
@@ -485,16 +442,12 @@ public class SeriesRecording implements Parcelable {
return mChannelId;
}
- /**
- * The id of this SeriesRecording.
- */
+ /** The id of this SeriesRecording. */
public long getId() {
return mId;
}
- /**
- * Sets the ID.
- */
+ /** Sets the ID. */
public void setId(long id) {
mId = id;
}
@@ -502,30 +455,24 @@ public class SeriesRecording implements Parcelable {
/**
* The priority of this recording.
*
- * <p> The highest number is recorded first. If there is a tie in mPriority then the higher mId
+ * <p>The highest number is recorded first. If there is a tie in mPriority then the higher mId
* wins.
*/
public long getPriority() {
return mPriority;
}
- /**
- * The series title.
- */
+ /** The series title. */
public String getTitle() {
return mTitle;
}
- /**
- * The series description.
- */
+ /** The series description. */
public String getDescription() {
return mDescription;
}
- /**
- * The long series description.
- */
+ /** The long series description. */
public String getLongDescription() {
return mLongDescription;
}
@@ -555,44 +502,34 @@ public class SeriesRecording implements Parcelable {
return mStartFromSeason;
}
- /**
- * Returns the channel recording option.
- */
- @ChannelOption public int getChannelOption() {
+ /** Returns the channel recording option. */
+ @ChannelOption
+ public int getChannelOption() {
return mChannelOption;
}
- /**
- * Returns the canonical genre ID's.
- */
+ /** Returns the canonical genre ID's. */
public int[] getCanonicalGenreIds() {
return mCanonicalGenreIds;
}
- /**
- * Returns the poster URI.
- */
+ /** Returns the poster URI. */
public String getPosterUri() {
return mPosterUri;
}
- /**
- * Returns the photo URI.
- */
+ /** Returns the photo URI. */
public String getPhotoUri() {
return mPhotoUri;
}
- /**
- * Returns the state of series recording.
- */
- @SeriesState public int getState() {
+ /** Returns the state of series recording. */
+ @SeriesState
+ public int getState() {
return mState;
}
- /**
- * Checks whether the series recording is stopped or not.
- */
+ /** Checks whether the series recording is stopped or not. */
public boolean isStopped() {
return mState == STATE_SERIES_STOPPED;
}
@@ -620,35 +557,77 @@ public class SeriesRecording implements Parcelable {
@Override
public int hashCode() {
- return Objects.hash(mPriority, mChannelId, mStartFromSeason, mStartFromEpisode, mId,
- mTitle, mDescription, mLongDescription, mSeriesId, mChannelOption,
- mCanonicalGenreIds, mPosterUri, mPhotoUri, mState);
+ return Objects.hash(
+ mPriority,
+ mChannelId,
+ mStartFromSeason,
+ mStartFromEpisode,
+ mId,
+ mTitle,
+ mDescription,
+ mLongDescription,
+ mSeriesId,
+ mChannelOption,
+ Arrays.hashCode(mCanonicalGenreIds),
+ mPosterUri,
+ mPhotoUri,
+ mState);
}
@Override
public String toString() {
- return "SeriesRecording{" +
- "inputId=" + mInputId +
- ", channelId=" + mChannelId +
- ", id='" + mId + '\'' +
- ", priority=" + mPriority +
- ", title='" + mTitle + '\'' +
- ", description='" + mDescription + '\'' +
- ", longDescription='" + mLongDescription + '\'' +
- ", startFromSeason=" + mStartFromSeason +
- ", startFromEpisode=" + mStartFromEpisode +
- ", channelOption=" + mChannelOption +
- ", canonicalGenreIds=" + Arrays.toString(mCanonicalGenreIds) +
- ", posterUri=" + mPosterUri +
- ", photoUri=" + mPhotoUri +
- ", state=" + mState +
- '}';
- }
-
- private SeriesRecording(long id, long priority, String title, String description,
- String longDescription, String inputId, long channelId, String seriesId,
- int startFromSeason, int startFromEpisode, int channelOption, int[] canonicalGenreIds,
- String posterUri, String photoUri, int state) {
+ return "SeriesRecording{"
+ + "inputId="
+ + mInputId
+ + ", channelId="
+ + mChannelId
+ + ", id='"
+ + mId
+ + '\''
+ + ", priority="
+ + mPriority
+ + ", title='"
+ + mTitle
+ + '\''
+ + ", description='"
+ + mDescription
+ + '\''
+ + ", longDescription='"
+ + mLongDescription
+ + '\''
+ + ", startFromSeason="
+ + mStartFromSeason
+ + ", startFromEpisode="
+ + mStartFromEpisode
+ + ", channelOption="
+ + mChannelOption
+ + ", canonicalGenreIds="
+ + Arrays.toString(mCanonicalGenreIds)
+ + ", posterUri="
+ + mPosterUri
+ + ", photoUri="
+ + mPhotoUri
+ + ", state="
+ + mState
+ + '}';
+ }
+
+ private SeriesRecording(
+ long id,
+ long priority,
+ String title,
+ String description,
+ String longDescription,
+ String inputId,
+ long channelId,
+ String seriesId,
+ int startFromSeason,
+ int startFromEpisode,
+ int channelOption,
+ int[] canonicalGenreIds,
+ String posterUri,
+ String photoUri,
+ int state) {
this.mId = id;
this.mPriority = priority;
this.mTitle = title;
@@ -690,9 +669,7 @@ public class SeriesRecording implements Parcelable {
out.writeInt(mState);
}
- /**
- * Returns an array containing all of the elements in the list.
- */
+ /** Returns an array containing all of the elements in the list. */
public static SeriesRecording[] toArray(Collection<SeriesRecording> series) {
return series.toArray(new SeriesRecording[series.size()]);
}
@@ -715,16 +692,16 @@ public class SeriesRecording implements Parcelable {
long channelId = program.getChannelId();
String seasonNumber = program.getSeasonNumber();
String episodeNumber = program.getEpisodeNumber();
- if (!mSeriesId.equals(seriesId) || (channelOption == SeriesRecording.OPTION_CHANNEL_ONE
- && mChannelId != channelId)) {
+ if (!mSeriesId.equals(seriesId)
+ || (channelOption == SeriesRecording.OPTION_CHANNEL_ONE
+ && mChannelId != channelId)) {
return false;
}
// Season number and episode number matches if
// start_season_number < program_season_number
// || (start_season_number == program_season_number
// && start_episode_number <= program_episode_number).
- if (mStartFromSeason == SeriesRecordings.THE_BEGINNING
- || TextUtils.isEmpty(seasonNumber)) {
+ if (mStartFromSeason == SeriesRecordings.THE_BEGINNING || TextUtils.isEmpty(seasonNumber)) {
return true;
} else {
int intSeasonNumber;
diff --git a/src/com/android/tv/dvr/provider/AsyncDvrDbTask.java b/src/com/android/tv/dvr/provider/AsyncDvrDbTask.java
index c5383d02..7d2af9c3 100644
--- a/src/com/android/tv/dvr/provider/AsyncDvrDbTask.java
+++ b/src/com/android/tv/dvr/provider/AsyncDvrDbTask.java
@@ -20,27 +20,23 @@ import android.content.Context;
import android.database.Cursor;
import android.os.AsyncTask;
import android.support.annotation.Nullable;
-
+import com.android.tv.common.concurrent.NamedThreadFactory;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.data.SeriesRecording;
import com.android.tv.dvr.provider.DvrContract.Schedules;
import com.android.tv.dvr.provider.DvrContract.SeriesRecordings;
-import com.android.tv.util.NamedThreadFactory;
-
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
-/**
- * {@link AsyncTask} that defaults to executing on its own single threaded Executor Service.
- */
+/** {@link AsyncTask} that defaults to executing on its own single threaded Executor Service. */
public abstract class AsyncDvrDbTask<Params, Progress, Result>
extends AsyncTask<Params, Progress, Result> {
- private static final NamedThreadFactory THREAD_FACTORY = new NamedThreadFactory(
- AsyncDvrDbTask.class.getSimpleName());
- private static final ExecutorService DB_EXECUTOR = Executors
- .newSingleThreadExecutor(THREAD_FACTORY);
+ private static final NamedThreadFactory THREAD_FACTORY =
+ new NamedThreadFactory(AsyncDvrDbTask.class.getSimpleName());
+ private static final ExecutorService DB_EXECUTOR =
+ Executors.newSingleThreadExecutor(THREAD_FACTORY);
private static DvrDatabaseHelper sDbHelper;
@@ -57,9 +53,7 @@ public abstract class AsyncDvrDbTask<Params, Progress, Result>
mContext = context;
}
- /**
- * Execute the task on the {@link #DB_EXECUTOR} thread.
- */
+ /** Execute the task on the {@link #DB_EXECUTOR} thread. */
@SafeVarargs
public final void executeOnDbThread(Params... params) {
executeOnExecutor(DB_EXECUTOR, params);
@@ -71,15 +65,11 @@ public abstract class AsyncDvrDbTask<Params, Progress, Result>
return doInDvrBackground(params);
}
- /**
- * Executes in the background after {@link #initializeDbHelper(Context)}
- */
+ /** Executes in the background after {@link #initializeDbHelper(Context)} */
@Nullable
protected abstract Result doInDvrBackground(Params... params);
- /**
- * Inserts schedules.
- */
+ /** Inserts schedules. */
public static class AsyncAddScheduleTask
extends AsyncDvrDbTask<ScheduledRecording, Void, Void> {
public AsyncAddScheduleTask(Context context) {
@@ -93,9 +83,7 @@ public abstract class AsyncDvrDbTask<Params, Progress, Result>
}
}
- /**
- * Update schedules.
- */
+ /** Update schedules. */
public static class AsyncUpdateScheduleTask
extends AsyncDvrDbTask<ScheduledRecording, Void, Void> {
public AsyncUpdateScheduleTask(Context context) {
@@ -109,9 +97,7 @@ public abstract class AsyncDvrDbTask<Params, Progress, Result>
}
}
- /**
- * Delete schedules.
- */
+ /** Delete schedules. */
public static class AsyncDeleteScheduleTask
extends AsyncDvrDbTask<ScheduledRecording, Void, Void> {
public AsyncDeleteScheduleTask(Context context) {
@@ -125,9 +111,7 @@ public abstract class AsyncDvrDbTask<Params, Progress, Result>
}
}
- /**
- * Returns all {@link ScheduledRecording}s.
- */
+ /** Returns all {@link ScheduledRecording}s. */
public abstract static class AsyncDvrQueryScheduleTask
extends AsyncDvrDbTask<Void, Void, List<ScheduledRecording>> {
public AsyncDvrQueryScheduleTask(Context context) {
@@ -150,9 +134,7 @@ public abstract class AsyncDvrDbTask<Params, Progress, Result>
}
}
- /**
- * Inserts series recordings.
- */
+ /** Inserts series recordings. */
public static class AsyncAddSeriesRecordingTask
extends AsyncDvrDbTask<SeriesRecording, Void, Void> {
public AsyncAddSeriesRecordingTask(Context context) {
@@ -166,9 +148,7 @@ public abstract class AsyncDvrDbTask<Params, Progress, Result>
}
}
- /**
- * Update series recordings.
- */
+ /** Update series recordings. */
public static class AsyncUpdateSeriesRecordingTask
extends AsyncDvrDbTask<SeriesRecording, Void, Void> {
public AsyncUpdateSeriesRecordingTask(Context context) {
@@ -182,9 +162,7 @@ public abstract class AsyncDvrDbTask<Params, Progress, Result>
}
}
- /**
- * Delete series recordings.
- */
+ /** Delete series recordings. */
public static class AsyncDeleteSeriesRecordingTask
extends AsyncDvrDbTask<SeriesRecording, Void, Void> {
public AsyncDeleteSeriesRecordingTask(Context context) {
@@ -198,9 +176,7 @@ public abstract class AsyncDvrDbTask<Params, Progress, Result>
}
}
- /**
- * Returns all {@link SeriesRecording}s.
- */
+ /** Returns all {@link SeriesRecording}s. */
public abstract static class AsyncDvrQuerySeriesRecordingTask
extends AsyncDvrDbTask<Void, Void, List<SeriesRecording>> {
public AsyncDvrQuerySeriesRecordingTask(Context context) {
@@ -214,8 +190,8 @@ public abstract class AsyncDvrDbTask<Params, Progress, Result>
return null;
}
List<SeriesRecording> scheduledRecordings = new ArrayList<>();
- try (Cursor c = sDbHelper.query(SeriesRecordings.TABLE_NAME,
- SeriesRecording.PROJECTION)) {
+ try (Cursor c =
+ sDbHelper.query(SeriesRecordings.TABLE_NAME, SeriesRecording.PROJECTION)) {
while (c.moveToNext() && !isCancelled()) {
scheduledRecordings.add(SeriesRecording.fromCursor(c));
}
diff --git a/src/com/android/tv/dvr/provider/DvrContract.java b/src/com/android/tv/dvr/provider/DvrContract.java
index f0aca18e..a5f2e2cd 100644
--- a/src/com/android/tv/dvr/provider/DvrContract.java
+++ b/src/com/android/tv/dvr/provider/DvrContract.java
@@ -55,11 +55,57 @@ public final class DvrContract {
/** The recording marked as canceled. */
public static final String STATE_RECORDING_CANCELED = "STATE_RECORDING_CANCELED";
+ /** The recording failed reason for other reasons */
+ public static final String FAILED_REASON_OTHER = "FAILED_REASON_OTHER";
+
+ /** The recording failed because the program ended before recording started. */
+ public static final String FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED =
+ "FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED";
+
+ /** The recording failed because it was not finished successfully */
+ public static final String FAILED_REASON_NOT_FINISHED = "FAILED_REASON_NOT_FINISHED";
+
+ /** The recording failed because the channel ID was invalid */
+ public static final String FAILED_REASON_INVALID_CHANNEL = "FAILED_REASON_INVALID_CHANNEL";
+
+ /** The recording failed because the scheduler was stopped */
+ public static final String FAILED_REASON_SCHEDULER_STOPPED
+ = "FAILED_REASON_SCHEDULER_STOPPED";
+
+ /** The recording failed because some messages were not sent to the message queue */
+ public static final String FAILED_REASON_MESSAGE_NOT_SENT =
+ "FAILED_REASON_MESSAGE_NOT_SENT";
+
+ /**
+ * The recording failed because it was failed to establish a connection to the recording
+ * session for the corresponding TV input.
+ */
+ public static final String FAILED_REASON_CONNECTION_FAILED =
+ "FAILED_REASON_CONNECTION_FAILED";
+
+ /**
+ * The recording failed because a required recording resource was not able to be
+ * allocated.
+ */
+ public static final String FAILED_REASON_RESOURCE_BUSY = "FAILED_REASON_RESOURCE_BUSY";
+
+ /** The recording failed because the input was not available */
+ public static final String FAILED_REASON_INPUT_UNAVAILABLE =
+ "FAILED_REASON_INPUT_UNAVAILABLE";
+
+ /** The recording failed because the input doesn't support recording */
+ public static final String FAILED_REASON_INPUT_DVR_UNSUPPORTED =
+ "FAILED_REASON_INPUT_DVR_UNSUPPORTED";
+
+ /** The recording failed because the space was not sufficient */
+ public static final String FAILED_REASON_INSUFFICIENT_SPACE =
+ "FAILED_REASON_INSUFFICIENT_SPACE";
+
/**
* The priority of this recording.
*
- * <p> The lowest number is recorded first. If there is a tie in priority then the lower id
- * wins. Defaults to {@value Long#MAX_VALUE}
+ * <p>The lowest number is recorded first. If there is a tie in priority then the lower id
+ * wins. Defaults to {@value Long#MAX_VALUE}
*
* <p>Type: INTEGER (long)
*/
@@ -68,8 +114,8 @@ public final class DvrContract {
/**
* The type of this recording.
*
- * <p>This value should be one of the followings: {@link #TYPE_PROGRAM} and
- * {@link #TYPE_TIMED}.
+ * <p>This value should be one of the followings: {@link #TYPE_PROGRAM} and {@link
+ * #TYPE_TIMED}.
*
* <p>This is a required field.
*
@@ -184,9 +230,9 @@ public final class DvrContract {
* The state of this recording.
*
* <p>This value should be one of the followings: {@link #STATE_RECORDING_NOT_STARTED},
- * {@link #STATE_RECORDING_IN_PROGRESS}, {@link #STATE_RECORDING_FINISHED},
- * {@link #STATE_RECORDING_FAILED}, {@link #STATE_RECORDING_CLIPPED} and
- * {@link #STATE_RECORDING_DELETED}.
+ * {@link #STATE_RECORDING_IN_PROGRESS}, {@link #STATE_RECORDING_FINISHED}, {@link
+ * #STATE_RECORDING_FAILED}, {@link #STATE_RECORDING_CLIPPED} and {@link
+ * #STATE_RECORDING_DELETED}.
*
* <p>This is a required field.
*
@@ -195,13 +241,20 @@ public final class DvrContract {
public static final String COLUMN_STATE = "state";
/**
+ * The reason of failure of this recording if it's failed.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_FAILED_REASON = "failed_reason";
+
+ /**
* The ID of the parent series recording.
*
* <p>Type: INTEGER (long)
*/
public static final String COLUMN_SERIES_RECORDING_ID = "series_recording_id";
- private Schedules() { }
+ private Schedules() {}
}
/** Column definition for Recording table. */
@@ -210,8 +263,8 @@ public final class DvrContract {
public static final String TABLE_NAME = "series_recording";
/**
- * This value is used for {@link #COLUMN_START_FROM_SEASON} and
- * {@link #COLUMN_START_FROM_EPISODE} to mean record all seasons or episodes.
+ * This value is used for {@link #COLUMN_START_FROM_SEASON} and {@link
+ * #COLUMN_START_FROM_EPISODE} to mean record all seasons or episodes.
*/
public static final int THE_BEGINNING = -1;
@@ -227,21 +280,17 @@ public final class DvrContract {
*/
public static final String OPTION_CHANNEL_ALL = "OPTION_CHANNEL_ALL";
- /**
- * The state indicates that it is a normal one.
- */
+ /** The state indicates that it is a normal one. */
public static final String STATE_SERIES_NORMAL = "STATE_SERIES_NORMAL";
- /**
- * The state indicates that it is stopped.
- */
+ /** The state indicates that it is stopped. */
public static final String STATE_SERIES_STOPPED = "STATE_SERIES_STOPPED";
/**
* The priority of this recording.
*
- * <p> The lowest number is recorded first. If there is a tie in priority then the lower id
- * wins. Defaults to {@value Long#MAX_VALUE}
+ * <p>The lowest number is recorded first. If there is a tie in priority then the lower id
+ * wins. Defaults to {@value Long#MAX_VALUE}
*
* <p>Type: INTEGER (long)
*/
@@ -266,7 +315,7 @@ public final class DvrContract {
public static final String COLUMN_CHANNEL_ID = "channel_id";
/**
- * The ID of the associated series to record.
+ * The ID of the associated series to record.
*
* <p>The id is an opaque but stable string.
*
@@ -300,8 +349,8 @@ public final class DvrContract {
public static final String COLUMN_LONG_DESCRIPTION = "long_description";
/**
- * The number of the earliest season to record. The
- * value {@link #THE_BEGINNING} means record all seasons.
+ * The number of the earliest season to record. The value {@link #THE_BEGINNING} means
+ * record all seasons.
*
* <p>Default value is {@value #THE_BEGINNING} {@link #THE_BEGINNING}.
*
@@ -310,7 +359,7 @@ public final class DvrContract {
public static final String COLUMN_START_FROM_SEASON = "start_from_season";
/**
- * The number of the earliest episode to record in {@link #COLUMN_START_FROM_SEASON}. The
+ * The number of the earliest episode to record in {@link #COLUMN_START_FROM_SEASON}. The
* value {@link #THE_BEGINNING} means record all episodes.
*
* <p>Default value is {@value #THE_BEGINNING} {@link #THE_BEGINNING}.
@@ -322,8 +371,8 @@ public final class DvrContract {
/**
* The series recording option which indicates the channels to record.
*
- * <p>This value should be one of the followings: {@link #OPTION_CHANNEL_ONE} and
- * {@link #OPTION_CHANNEL_ALL}. The default value is OPTION_CHANNEL_ONE.
+ * <p>This value should be one of the followings: {@link #OPTION_CHANNEL_ONE} and {@link
+ * #OPTION_CHANNEL_ALL}. The default value is OPTION_CHANNEL_ONE.
*
* <p>Type: TEXT
*/
@@ -338,6 +387,7 @@ public final class DvrContract {
* to get the canonical genre strings from the text stored in the column.
*
* <p>Type: TEXT
+ *
* @see android.media.tv.TvContract.Programs.Genres
* @see android.media.tv.TvContract.Programs.Genres#encode
* @see android.media.tv.TvContract.Programs.Genres#decode
@@ -350,10 +400,9 @@ public final class DvrContract {
* <p>The data in the column must be a URL, or a URI in one of the following formats:
*
* <ul>
- * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
- * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
- * </li>
- * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
+ * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})
+ * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
+ * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})
* </ul>
*
* <p>Type: TEXT
@@ -366,10 +415,9 @@ public final class DvrContract {
* <p>The data in the column must be a URL, or a URI in one of the following formats:
*
* <ul>
- * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
- * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
- * </li>
- * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
+ * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})
+ * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
+ * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})
* </ul>
*
* <p>Type: TEXT
@@ -379,15 +427,15 @@ public final class DvrContract {
/**
* The state of whether the series recording be canceled or not.
*
- * <p>This value should be one of the followings: {@link #STATE_SERIES_NORMAL} and
- * {@link #STATE_SERIES_STOPPED}. The default value is STATE_SERIES_NORMAL.
+ * <p>This value should be one of the followings: {@link #STATE_SERIES_NORMAL} and {@link
+ * #STATE_SERIES_STOPPED}. The default value is STATE_SERIES_NORMAL.
*
* <p>Type: TEXT
*/
public static final String COLUMN_STATE = "state";
- private SeriesRecordings() { }
+ private SeriesRecordings() {}
}
- private DvrContract() { }
+ private DvrContract() {}
}
diff --git a/src/com/android/tv/dvr/provider/DvrDatabaseHelper.java b/src/com/android/tv/dvr/provider/DvrDatabaseHelper.java
index 8b9481a9..41e5a66a 100644
--- a/src/com/android/tv/dvr/provider/DvrDatabaseHelper.java
+++ b/src/com/android/tv/dvr/provider/DvrDatabaseHelper.java
@@ -26,98 +26,145 @@ import android.database.sqlite.SQLiteStatement;
import android.provider.BaseColumns;
import android.text.TextUtils;
import android.util.Log;
-
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.data.SeriesRecording;
import com.android.tv.dvr.provider.DvrContract.Schedules;
import com.android.tv.dvr.provider.DvrContract.SeriesRecordings;
-/**
- * A data class for one recorded contents.
- */
+/** A data class for one recorded contents. */
public class DvrDatabaseHelper extends SQLiteOpenHelper {
private static final String TAG = "DvrDatabaseHelper";
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
- private static final int DATABASE_VERSION = 17;
+ private static final int DATABASE_VERSION = 18;
private static final String DB_NAME = "dvr.db";
private static final String SQL_CREATE_SCHEDULES =
- "CREATE TABLE " + Schedules.TABLE_NAME + "("
- + Schedules._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
- + Schedules.COLUMN_PRIORITY + " INTEGER DEFAULT "
- + ScheduledRecording.DEFAULT_PRIORITY + ","
- + Schedules.COLUMN_TYPE + " TEXT NOT NULL,"
- + Schedules.COLUMN_INPUT_ID + " TEXT NOT NULL,"
- + Schedules.COLUMN_CHANNEL_ID + " INTEGER NOT NULL,"
- + Schedules.COLUMN_PROGRAM_ID + " INTEGER,"
- + Schedules.COLUMN_PROGRAM_TITLE + " TEXT,"
- + Schedules.COLUMN_START_TIME_UTC_MILLIS + " INTEGER NOT NULL,"
- + Schedules.COLUMN_END_TIME_UTC_MILLIS + " INTEGER NOT NULL,"
- + Schedules.COLUMN_SEASON_NUMBER + " TEXT,"
- + Schedules.COLUMN_EPISODE_NUMBER + " TEXT,"
- + Schedules.COLUMN_EPISODE_TITLE + " TEXT,"
- + Schedules.COLUMN_PROGRAM_DESCRIPTION + " TEXT,"
- + Schedules.COLUMN_PROGRAM_LONG_DESCRIPTION + " TEXT,"
- + Schedules.COLUMN_PROGRAM_POST_ART_URI + " TEXT,"
- + Schedules.COLUMN_PROGRAM_THUMBNAIL_URI + " TEXT,"
- + Schedules.COLUMN_STATE + " TEXT NOT NULL,"
- + Schedules.COLUMN_SERIES_RECORDING_ID + " INTEGER,"
- + "FOREIGN KEY(" + Schedules.COLUMN_SERIES_RECORDING_ID + ") "
- + "REFERENCES " + SeriesRecordings.TABLE_NAME
- + "(" + SeriesRecordings._ID + ") "
+ "CREATE TABLE "
+ + Schedules.TABLE_NAME
+ + "("
+ + Schedules._ID
+ + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + Schedules.COLUMN_PRIORITY
+ + " INTEGER DEFAULT "
+ + ScheduledRecording.DEFAULT_PRIORITY
+ + ","
+ + Schedules.COLUMN_TYPE
+ + " TEXT NOT NULL,"
+ + Schedules.COLUMN_INPUT_ID
+ + " TEXT NOT NULL,"
+ + Schedules.COLUMN_CHANNEL_ID
+ + " INTEGER NOT NULL,"
+ + Schedules.COLUMN_PROGRAM_ID
+ + " INTEGER,"
+ + Schedules.COLUMN_PROGRAM_TITLE
+ + " TEXT,"
+ + Schedules.COLUMN_START_TIME_UTC_MILLIS
+ + " INTEGER NOT NULL,"
+ + Schedules.COLUMN_END_TIME_UTC_MILLIS
+ + " INTEGER NOT NULL,"
+ + Schedules.COLUMN_SEASON_NUMBER
+ + " TEXT,"
+ + Schedules.COLUMN_EPISODE_NUMBER
+ + " TEXT,"
+ + Schedules.COLUMN_EPISODE_TITLE
+ + " TEXT,"
+ + Schedules.COLUMN_PROGRAM_DESCRIPTION
+ + " TEXT,"
+ + Schedules.COLUMN_PROGRAM_LONG_DESCRIPTION
+ + " TEXT,"
+ + Schedules.COLUMN_PROGRAM_POST_ART_URI
+ + " TEXT,"
+ + Schedules.COLUMN_PROGRAM_THUMBNAIL_URI
+ + " TEXT,"
+ + Schedules.COLUMN_STATE
+ + " TEXT NOT NULL,"
+ + Schedules.COLUMN_SERIES_RECORDING_ID
+ + " INTEGER,"
+ + "FOREIGN KEY("
+ + Schedules.COLUMN_SERIES_RECORDING_ID
+ + ") "
+ + "REFERENCES "
+ + SeriesRecordings.TABLE_NAME
+ + "("
+ + SeriesRecordings._ID
+ + ") "
+ "ON UPDATE CASCADE ON DELETE SET NULL);";
private static final String SQL_DROP_SCHEDULES = "DROP TABLE IF EXISTS " + Schedules.TABLE_NAME;
private static final String SQL_CREATE_SERIES_RECORDINGS =
- "CREATE TABLE " + SeriesRecordings.TABLE_NAME + "("
- + SeriesRecordings._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
- + SeriesRecordings.COLUMN_PRIORITY + " INTEGER DEFAULT "
- + SeriesRecording.DEFAULT_PRIORITY + ","
- + SeriesRecordings.COLUMN_TITLE + " TEXT NOT NULL,"
- + SeriesRecordings.COLUMN_SHORT_DESCRIPTION + " TEXT,"
- + SeriesRecordings.COLUMN_LONG_DESCRIPTION + " TEXT,"
- + SeriesRecordings.COLUMN_INPUT_ID + " TEXT NOT NULL,"
- + SeriesRecordings.COLUMN_CHANNEL_ID + " INTEGER NOT NULL,"
- + SeriesRecordings.COLUMN_SERIES_ID + " TEXT NOT NULL,"
- + SeriesRecordings.COLUMN_START_FROM_SEASON + " INTEGER DEFAULT "
- + SeriesRecordings.THE_BEGINNING + ","
- + SeriesRecordings.COLUMN_START_FROM_EPISODE + " INTEGER DEFAULT "
- + SeriesRecordings.THE_BEGINNING + ","
- + SeriesRecordings.COLUMN_CHANNEL_OPTION + " TEXT DEFAULT "
- + SeriesRecordings.OPTION_CHANNEL_ONE + ","
- + SeriesRecordings.COLUMN_CANONICAL_GENRE + " TEXT,"
- + SeriesRecordings.COLUMN_POSTER_URI + " TEXT,"
- + SeriesRecordings.COLUMN_PHOTO_URI + " TEXT,"
- + SeriesRecordings.COLUMN_STATE + " TEXT)";
+ "CREATE TABLE "
+ + SeriesRecordings.TABLE_NAME
+ + "("
+ + SeriesRecordings._ID
+ + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + SeriesRecordings.COLUMN_PRIORITY
+ + " INTEGER DEFAULT "
+ + SeriesRecording.DEFAULT_PRIORITY
+ + ","
+ + SeriesRecordings.COLUMN_TITLE
+ + " TEXT NOT NULL,"
+ + SeriesRecordings.COLUMN_SHORT_DESCRIPTION
+ + " TEXT,"
+ + SeriesRecordings.COLUMN_LONG_DESCRIPTION
+ + " TEXT,"
+ + SeriesRecordings.COLUMN_INPUT_ID
+ + " TEXT NOT NULL,"
+ + SeriesRecordings.COLUMN_CHANNEL_ID
+ + " INTEGER NOT NULL,"
+ + SeriesRecordings.COLUMN_SERIES_ID
+ + " TEXT NOT NULL,"
+ + SeriesRecordings.COLUMN_START_FROM_SEASON
+ + " INTEGER DEFAULT "
+ + SeriesRecordings.THE_BEGINNING
+ + ","
+ + SeriesRecordings.COLUMN_START_FROM_EPISODE
+ + " INTEGER DEFAULT "
+ + SeriesRecordings.THE_BEGINNING
+ + ","
+ + SeriesRecordings.COLUMN_CHANNEL_OPTION
+ + " TEXT DEFAULT "
+ + SeriesRecordings.OPTION_CHANNEL_ONE
+ + ","
+ + SeriesRecordings.COLUMN_CANONICAL_GENRE
+ + " TEXT,"
+ + SeriesRecordings.COLUMN_POSTER_URI
+ + " TEXT,"
+ + SeriesRecordings.COLUMN_PHOTO_URI
+ + " TEXT,"
+ + SeriesRecordings.COLUMN_STATE
+ + " TEXT)";
- private static final String SQL_DROP_SERIES_RECORDINGS = "DROP TABLE IF EXISTS " +
- SeriesRecordings.TABLE_NAME;
+ private static final String SQL_DROP_SERIES_RECORDINGS =
+ "DROP TABLE IF EXISTS " + SeriesRecordings.TABLE_NAME;
private static final int SQL_DATA_TYPE_LONG = 0;
private static final int SQL_DATA_TYPE_INT = 1;
private static final int SQL_DATA_TYPE_STRING = 2;
- private static final ColumnInfo[] COLUMNS_SCHEDULES = new ColumnInfo[] {
- new ColumnInfo(Schedules._ID, SQL_DATA_TYPE_LONG),
- new ColumnInfo(Schedules.COLUMN_PRIORITY, SQL_DATA_TYPE_LONG),
- new ColumnInfo(Schedules.COLUMN_TYPE, SQL_DATA_TYPE_STRING),
- new ColumnInfo(Schedules.COLUMN_INPUT_ID, SQL_DATA_TYPE_STRING),
- new ColumnInfo(Schedules.COLUMN_CHANNEL_ID, SQL_DATA_TYPE_LONG),
- new ColumnInfo(Schedules.COLUMN_PROGRAM_ID, SQL_DATA_TYPE_LONG),
- new ColumnInfo(Schedules.COLUMN_PROGRAM_TITLE, SQL_DATA_TYPE_STRING),
- new ColumnInfo(Schedules.COLUMN_START_TIME_UTC_MILLIS, SQL_DATA_TYPE_LONG),
- new ColumnInfo(Schedules.COLUMN_END_TIME_UTC_MILLIS, SQL_DATA_TYPE_LONG),
- new ColumnInfo(Schedules.COLUMN_SEASON_NUMBER, SQL_DATA_TYPE_STRING),
- new ColumnInfo(Schedules.COLUMN_EPISODE_NUMBER, SQL_DATA_TYPE_STRING),
- new ColumnInfo(Schedules.COLUMN_EPISODE_TITLE, SQL_DATA_TYPE_STRING),
- new ColumnInfo(Schedules.COLUMN_PROGRAM_DESCRIPTION, SQL_DATA_TYPE_STRING),
- new ColumnInfo(Schedules.COLUMN_PROGRAM_LONG_DESCRIPTION, SQL_DATA_TYPE_STRING),
- new ColumnInfo(Schedules.COLUMN_PROGRAM_POST_ART_URI, SQL_DATA_TYPE_STRING),
- new ColumnInfo(Schedules.COLUMN_PROGRAM_THUMBNAIL_URI, SQL_DATA_TYPE_STRING),
- new ColumnInfo(Schedules.COLUMN_STATE, SQL_DATA_TYPE_STRING),
- new ColumnInfo(Schedules.COLUMN_SERIES_RECORDING_ID, SQL_DATA_TYPE_LONG)};
+ private static final ColumnInfo[] COLUMNS_SCHEDULES =
+ new ColumnInfo[] {
+ new ColumnInfo(Schedules._ID, SQL_DATA_TYPE_LONG),
+ new ColumnInfo(Schedules.COLUMN_PRIORITY, SQL_DATA_TYPE_LONG),
+ new ColumnInfo(Schedules.COLUMN_TYPE, SQL_DATA_TYPE_STRING),
+ new ColumnInfo(Schedules.COLUMN_INPUT_ID, SQL_DATA_TYPE_STRING),
+ new ColumnInfo(Schedules.COLUMN_CHANNEL_ID, SQL_DATA_TYPE_LONG),
+ new ColumnInfo(Schedules.COLUMN_PROGRAM_ID, SQL_DATA_TYPE_LONG),
+ new ColumnInfo(Schedules.COLUMN_PROGRAM_TITLE, SQL_DATA_TYPE_STRING),
+ new ColumnInfo(Schedules.COLUMN_START_TIME_UTC_MILLIS, SQL_DATA_TYPE_LONG),
+ new ColumnInfo(Schedules.COLUMN_END_TIME_UTC_MILLIS, SQL_DATA_TYPE_LONG),
+ new ColumnInfo(Schedules.COLUMN_SEASON_NUMBER, SQL_DATA_TYPE_STRING),
+ new ColumnInfo(Schedules.COLUMN_EPISODE_NUMBER, SQL_DATA_TYPE_STRING),
+ new ColumnInfo(Schedules.COLUMN_EPISODE_TITLE, SQL_DATA_TYPE_STRING),
+ new ColumnInfo(Schedules.COLUMN_PROGRAM_DESCRIPTION, SQL_DATA_TYPE_STRING),
+ new ColumnInfo(Schedules.COLUMN_PROGRAM_LONG_DESCRIPTION, SQL_DATA_TYPE_STRING),
+ new ColumnInfo(Schedules.COLUMN_PROGRAM_POST_ART_URI, SQL_DATA_TYPE_STRING),
+ new ColumnInfo(Schedules.COLUMN_PROGRAM_THUMBNAIL_URI, SQL_DATA_TYPE_STRING),
+ new ColumnInfo(Schedules.COLUMN_STATE, SQL_DATA_TYPE_STRING),
+ new ColumnInfo(Schedules.COLUMN_FAILED_REASON, SQL_DATA_TYPE_STRING),
+ new ColumnInfo(Schedules.COLUMN_SERIES_RECORDING_ID, SQL_DATA_TYPE_LONG)
+ };
private static final String SQL_INSERT_SCHEDULES =
buildInsertSql(Schedules.TABLE_NAME, COLUMNS_SCHEDULES);
@@ -125,22 +172,24 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper {
buildUpdateSql(Schedules.TABLE_NAME, COLUMNS_SCHEDULES);
private static final String SQL_DELETE_SCHEDULES = buildDeleteSql(Schedules.TABLE_NAME);
- private static final ColumnInfo[] COLUMNS_SERIES_RECORDINGS = new ColumnInfo[] {
- new ColumnInfo(SeriesRecordings._ID, SQL_DATA_TYPE_LONG),
- new ColumnInfo(SeriesRecordings.COLUMN_PRIORITY, SQL_DATA_TYPE_LONG),
- new ColumnInfo(SeriesRecordings.COLUMN_INPUT_ID, SQL_DATA_TYPE_STRING),
- new ColumnInfo(SeriesRecordings.COLUMN_CHANNEL_ID, SQL_DATA_TYPE_LONG),
- new ColumnInfo(SeriesRecordings.COLUMN_SERIES_ID, SQL_DATA_TYPE_STRING),
- new ColumnInfo(SeriesRecordings.COLUMN_TITLE, SQL_DATA_TYPE_STRING),
- new ColumnInfo(SeriesRecordings.COLUMN_SHORT_DESCRIPTION, SQL_DATA_TYPE_STRING),
- new ColumnInfo(SeriesRecordings.COLUMN_LONG_DESCRIPTION, SQL_DATA_TYPE_STRING),
- new ColumnInfo(SeriesRecordings.COLUMN_START_FROM_SEASON, SQL_DATA_TYPE_INT),
- new ColumnInfo(SeriesRecordings.COLUMN_START_FROM_EPISODE, SQL_DATA_TYPE_INT),
- new ColumnInfo(SeriesRecordings.COLUMN_CHANNEL_OPTION, SQL_DATA_TYPE_STRING),
- new ColumnInfo(SeriesRecordings.COLUMN_CANONICAL_GENRE, SQL_DATA_TYPE_STRING),
- new ColumnInfo(SeriesRecordings.COLUMN_POSTER_URI, SQL_DATA_TYPE_STRING),
- new ColumnInfo(SeriesRecordings.COLUMN_PHOTO_URI, SQL_DATA_TYPE_STRING),
- new ColumnInfo(SeriesRecordings.COLUMN_STATE, SQL_DATA_TYPE_STRING)};
+ private static final ColumnInfo[] COLUMNS_SERIES_RECORDINGS =
+ new ColumnInfo[] {
+ new ColumnInfo(SeriesRecordings._ID, SQL_DATA_TYPE_LONG),
+ new ColumnInfo(SeriesRecordings.COLUMN_PRIORITY, SQL_DATA_TYPE_LONG),
+ new ColumnInfo(SeriesRecordings.COLUMN_INPUT_ID, SQL_DATA_TYPE_STRING),
+ new ColumnInfo(SeriesRecordings.COLUMN_CHANNEL_ID, SQL_DATA_TYPE_LONG),
+ new ColumnInfo(SeriesRecordings.COLUMN_SERIES_ID, SQL_DATA_TYPE_STRING),
+ new ColumnInfo(SeriesRecordings.COLUMN_TITLE, SQL_DATA_TYPE_STRING),
+ new ColumnInfo(SeriesRecordings.COLUMN_SHORT_DESCRIPTION, SQL_DATA_TYPE_STRING),
+ new ColumnInfo(SeriesRecordings.COLUMN_LONG_DESCRIPTION, SQL_DATA_TYPE_STRING),
+ new ColumnInfo(SeriesRecordings.COLUMN_START_FROM_SEASON, SQL_DATA_TYPE_INT),
+ new ColumnInfo(SeriesRecordings.COLUMN_START_FROM_EPISODE, SQL_DATA_TYPE_INT),
+ new ColumnInfo(SeriesRecordings.COLUMN_CHANNEL_OPTION, SQL_DATA_TYPE_STRING),
+ new ColumnInfo(SeriesRecordings.COLUMN_CANONICAL_GENRE, SQL_DATA_TYPE_STRING),
+ new ColumnInfo(SeriesRecordings.COLUMN_POSTER_URI, SQL_DATA_TYPE_STRING),
+ new ColumnInfo(SeriesRecordings.COLUMN_PHOTO_URI, SQL_DATA_TYPE_STRING),
+ new ColumnInfo(SeriesRecordings.COLUMN_STATE, SQL_DATA_TYPE_STRING)
+ };
private static final String SQL_INSERT_SERIES_RECORDINGS =
buildInsertSql(SeriesRecordings.TABLE_NAME, COLUMNS_SERIES_RECORDINGS);
@@ -186,6 +235,7 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper {
private static String buildDeleteSql(String tableName) {
return "DELETE FROM " + tableName + " WHERE " + BaseColumns._ID + "=?";
}
+
public DvrDatabaseHelper(Context context) {
super(context.getApplicationContext(), DB_NAME, null, DATABASE_VERSION);
}
@@ -205,16 +255,20 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper {
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- if (DEBUG) Log.d(TAG, "Executing SQL: " + SQL_DROP_SCHEDULES);
- db.execSQL(SQL_DROP_SCHEDULES);
- if (DEBUG) Log.d(TAG, "Executing SQL: " + SQL_DROP_SERIES_RECORDINGS);
- db.execSQL(SQL_DROP_SERIES_RECORDINGS);
- onCreate(db);
+ if (oldVersion < 17) {
+ if (DEBUG) Log.d(TAG, "Executing SQL: " + SQL_DROP_SCHEDULES);
+ db.execSQL(SQL_DROP_SCHEDULES);
+ if (DEBUG) Log.d(TAG, "Executing SQL: " + SQL_DROP_SERIES_RECORDINGS);
+ db.execSQL(SQL_DROP_SERIES_RECORDINGS);
+ onCreate(db);
+ }
+ if (oldVersion < 18) {
+ db.execSQL("ALTER TABLE " + Schedules.TABLE_NAME + " ADD COLUMN "
+ + Schedules.COLUMN_FAILED_REASON + " TEXT DEFAULT null;");
+ }
}
- /**
- * Handles the query request and returns a {@link Cursor}.
- */
+ /** Handles the query request and returns a {@link Cursor}. */
public Cursor query(String tableName, String[] projections) {
SQLiteDatabase db = getReadableDatabase();
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
@@ -222,9 +276,7 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper {
return builder.query(db, projections, null, null, null, null, null);
}
- /**
- * Inserts schedules.
- */
+ /** Inserts schedules. */
public void insertSchedules(ScheduledRecording... scheduledRecordings) {
SQLiteDatabase db = getWritableDatabase();
SQLiteStatement statement = db.compileStatement(SQL_INSERT_SCHEDULES);
@@ -242,9 +294,7 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper {
}
}
- /**
- * Update schedules.
- */
+ /** Update schedules. */
public void updateSchedules(ScheduledRecording... scheduledRecordings) {
SQLiteDatabase db = getWritableDatabase();
SQLiteStatement statement = db.compileStatement(SQL_UPDATE_SCHEDULES);
@@ -263,9 +313,7 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper {
}
}
- /**
- * Delete schedules.
- */
+ /** Delete schedules. */
public void deleteSchedules(ScheduledRecording... scheduledRecordings) {
SQLiteDatabase db = getWritableDatabase();
SQLiteStatement statement = db.compileStatement(SQL_DELETE_SCHEDULES);
@@ -282,9 +330,7 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper {
}
}
- /**
- * Inserts series recordings.
- */
+ /** Inserts series recordings. */
public void insertSeriesRecordings(SeriesRecording... seriesRecordings) {
SQLiteDatabase db = getWritableDatabase();
SQLiteStatement statement = db.compileStatement(SQL_INSERT_SERIES_RECORDINGS);
@@ -302,9 +348,7 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper {
}
}
- /**
- * Update series recordings.
- */
+ /** Update series recordings. */
public void updateSeriesRecordings(SeriesRecording... seriesRecordings) {
SQLiteDatabase db = getWritableDatabase();
SQLiteStatement statement = db.compileStatement(SQL_UPDATE_SERIES_RECORDINGS);
@@ -323,9 +367,7 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper {
}
}
- /**
- * Delete series recordings.
- */
+ /** Delete series recordings. */
public void deleteSeriesRecordings(SeriesRecording... seriesRecordings) {
SQLiteDatabase db = getWritableDatabase();
SQLiteStatement statement = db.compileStatement(SQL_DELETE_SERIES_RECORDINGS);
@@ -342,8 +384,8 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper {
}
}
- private void bindColumns(SQLiteStatement statement, ColumnInfo[] columns,
- ContentValues values) {
+ private void bindColumns(
+ SQLiteStatement statement, ColumnInfo[] columns, ContentValues values) {
for (int i = 0; i < columns.length; ++i) {
ColumnInfo columnInfo = columns[i];
Object value = values.get(columnInfo.name);
@@ -362,14 +404,15 @@ public class DvrDatabaseHelper extends SQLiteOpenHelper {
statement.bindLong(i + 1, (Integer) value);
}
break;
- case SQL_DATA_TYPE_STRING: {
- if (TextUtils.isEmpty((String) value)) {
- statement.bindNull(i + 1);
- } else {
- statement.bindString(i + 1, (String) value);
+ case SQL_DATA_TYPE_STRING:
+ {
+ if (TextUtils.isEmpty((String) value)) {
+ statement.bindNull(i + 1);
+ } else {
+ statement.bindString(i + 1, (String) value);
+ }
+ break;
}
- break;
- }
}
}
}
diff --git a/src/com/android/tv/dvr/provider/DvrDbSync.java b/src/com/android/tv/dvr/provider/DvrDbSync.java
index ff391959..42bc8bcc 100644
--- a/src/com/android/tv/dvr/provider/DvrDbSync.java
+++ b/src/com/android/tv/dvr/provider/DvrDbSync.java
@@ -29,8 +29,7 @@ import android.os.Looper;
import android.support.annotation.MainThread;
import android.support.annotation.VisibleForTesting;
import android.util.Log;
-
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.Program;
import com.android.tv.dvr.DvrDataManager.ScheduledRecordingListener;
@@ -41,7 +40,6 @@ import com.android.tv.dvr.data.SeriesRecording;
import com.android.tv.dvr.recorder.SeriesRecordingScheduler;
import com.android.tv.util.AsyncDbTask.AsyncQueryProgramTask;
import com.android.tv.util.TvUriMatcher;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@@ -50,6 +48,7 @@ import java.util.List;
import java.util.Objects;
import java.util.Queue;
import java.util.Set;
+import java.util.concurrent.Executor;
/**
* A class to synchronizes DVR DB with TvProvider.
@@ -70,28 +69,31 @@ public class DvrDbSync {
private final DvrManager mDvrManager;
private final DvrDataManagerImpl mDataManager;
private final ChannelDataManager mChannelDataManager;
+ private final Executor mDbExecutor;
private final Queue<Long> mProgramIdQueue = new LinkedList<>();
private QueryProgramTask mQueryProgramTask;
private final SeriesRecordingScheduler mSeriesRecordingScheduler;
- private final ContentObserver mContentObserver = new ContentObserver(new Handler(
- Looper.getMainLooper())) {
- @SuppressLint("SwitchIntDef")
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- switch (TvUriMatcher.match(uri)) {
- case TvUriMatcher.MATCH_PROGRAM:
- if (DEBUG) Log.d(TAG, "onProgramsUpdated");
- onProgramsUpdated();
- break;
- case TvUriMatcher.MATCH_PROGRAM_ID:
- if (DEBUG) {
- Log.d(TAG, "onProgramUpdated: programId=" + ContentUris.parseId(uri));
+ private final ContentObserver mContentObserver =
+ new ContentObserver(new Handler(Looper.getMainLooper())) {
+ @SuppressLint("SwitchIntDef")
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ switch (TvUriMatcher.match(uri)) {
+ case TvUriMatcher.MATCH_PROGRAM:
+ if (DEBUG) Log.d(TAG, "onProgramsUpdated");
+ onProgramsUpdated();
+ break;
+ case TvUriMatcher.MATCH_PROGRAM_ID:
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "onProgramUpdated: programId=" + ContentUris.parseId(uri));
+ }
+ onProgramUpdated(ContentUris.parseId(uri));
+ break;
}
- onProgramUpdated(ContentUris.parseId(uri));
- break;
- }
- }
- };
+ }
+ };
private final ChannelDataManager.Listener mChannelDataManagerListener =
new ChannelDataManager.Listener() {
@@ -106,70 +108,76 @@ public class DvrDbSync {
}
@Override
- public void onChannelBrowsableChanged() { }
+ public void onChannelBrowsableChanged() {}
};
- private final ScheduledRecordingListener mScheduleListener = new ScheduledRecordingListener() {
- @Override
- public void onScheduledRecordingAdded(ScheduledRecording... schedules) {
- for (ScheduledRecording schedule : schedules) {
- addProgramIdToCheckIfNeeded(schedule);
- }
- startNextUpdateIfNeeded();
- }
+ private final ScheduledRecordingListener mScheduleListener =
+ new ScheduledRecordingListener() {
+ @Override
+ public void onScheduledRecordingAdded(ScheduledRecording... schedules) {
+ for (ScheduledRecording schedule : schedules) {
+ addProgramIdToCheckIfNeeded(schedule);
+ }
+ startNextUpdateIfNeeded();
+ }
- @Override
- public void onScheduledRecordingRemoved(ScheduledRecording... schedules) {
- for (ScheduledRecording schedule : schedules) {
- mProgramIdQueue.remove(schedule.getProgramId());
- }
- }
+ @Override
+ public void onScheduledRecordingRemoved(ScheduledRecording... schedules) {
+ for (ScheduledRecording schedule : schedules) {
+ mProgramIdQueue.remove(schedule.getProgramId());
+ }
+ }
- @Override
- public void onScheduledRecordingStatusChanged(ScheduledRecording... schedules) {
- for (ScheduledRecording schedule : schedules) {
- mProgramIdQueue.remove(schedule.getProgramId());
- addProgramIdToCheckIfNeeded(schedule);
- }
- startNextUpdateIfNeeded();
- }
- };
+ @Override
+ public void onScheduledRecordingStatusChanged(ScheduledRecording... schedules) {
+ for (ScheduledRecording schedule : schedules) {
+ mProgramIdQueue.remove(schedule.getProgramId());
+ addProgramIdToCheckIfNeeded(schedule);
+ }
+ startNextUpdateIfNeeded();
+ }
+ };
public DvrDbSync(Context context, DvrDataManagerImpl dataManager) {
- this(context, dataManager, TvApplication.getSingletons(context).getChannelDataManager(),
- TvApplication.getSingletons(context).getDvrManager(),
- SeriesRecordingScheduler.getInstance(context));
+ this(
+ context,
+ dataManager,
+ TvSingletons.getSingletons(context).getChannelDataManager(),
+ TvSingletons.getSingletons(context).getDvrManager(),
+ SeriesRecordingScheduler.getInstance(context),
+ TvSingletons.getSingletons(context).getDbExecutor());
}
@VisibleForTesting
- DvrDbSync(Context context, DvrDataManagerImpl dataManager,
- ChannelDataManager channelDataManager, DvrManager dvrManager,
- SeriesRecordingScheduler seriesRecordingScheduler) {
+ DvrDbSync(
+ Context context,
+ DvrDataManagerImpl dataManager,
+ ChannelDataManager channelDataManager,
+ DvrManager dvrManager,
+ SeriesRecordingScheduler seriesRecordingScheduler,
+ Executor dbExecutor) {
mContext = context;
mDvrManager = dvrManager;
mDataManager = dataManager;
mChannelDataManager = channelDataManager;
mSeriesRecordingScheduler = seriesRecordingScheduler;
+ mDbExecutor = dbExecutor;
}
- /**
- * Starts the DB sync.
- */
+ /** Starts the DB sync. */
public void start() {
if (!mChannelDataManager.isDbLoadFinished()) {
mChannelDataManager.addListener(mChannelDataManagerListener);
return;
}
- mContext.getContentResolver().registerContentObserver(Programs.CONTENT_URI, true,
- mContentObserver);
+ mContext.getContentResolver()
+ .registerContentObserver(Programs.CONTENT_URI, true, mContentObserver);
mDataManager.addScheduledRecordingListener(mScheduleListener);
onChannelsUpdated();
onProgramsUpdated();
}
- /**
- * Stops the DB sync.
- */
+ /** Stops the DB sync. */
public void stop() {
mProgramIdQueue.clear();
if (mQueryProgramTask != null) {
@@ -185,14 +193,15 @@ public class DvrDbSync {
for (SeriesRecording r : mDataManager.getSeriesRecordings()) {
if (r.getChannelOption() == SeriesRecording.OPTION_CHANNEL_ONE
&& !mChannelDataManager.doesChannelExistInDb(r.getChannelId())) {
- seriesRecordingsToUpdate.add(SeriesRecording.buildFrom(r)
- .setChannelOption(SeriesRecording.OPTION_CHANNEL_ALL)
- .setState(SeriesRecording.STATE_SERIES_STOPPED).build());
+ seriesRecordingsToUpdate.add(
+ SeriesRecording.buildFrom(r)
+ .setChannelOption(SeriesRecording.OPTION_CHANNEL_ALL)
+ .setState(SeriesRecording.STATE_SERIES_STOPPED)
+ .build());
}
}
if (!seriesRecordingsToUpdate.isEmpty()) {
- mDataManager.updateSeriesRecording(
- SeriesRecording.toArray(seriesRecordingsToUpdate));
+ mDataManager.updateSeriesRecording(SeriesRecording.toArray(seriesRecordingsToUpdate));
}
List<ScheduledRecording> schedulesToRemove = new ArrayList<>();
for (ScheduledRecording r : mDataManager.getAvailableScheduledRecordings()) {
@@ -202,8 +211,7 @@ public class DvrDbSync {
}
}
if (!schedulesToRemove.isEmpty()) {
- mDataManager.removeScheduledRecording(
- ScheduledRecording.toArray(schedulesToRemove));
+ mDataManager.removeScheduledRecording(ScheduledRecording.toArray(schedulesToRemove));
}
}
@@ -227,7 +235,7 @@ public class DvrDbSync {
if (programId != ScheduledRecording.ID_NOT_SET
&& !mProgramIdQueue.contains(programId)
&& (schedule.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED
- || schedule.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS)) {
+ || schedule.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS)) {
if (DEBUG) Log.d(TAG, "Program ID enqueued: " + programId);
mProgramIdQueue.offer(programId);
// There are schedules to be updated. Pause the SeriesRecordingScheduler until all the
@@ -258,7 +266,7 @@ public class DvrDbSync {
ScheduledRecording schedule = mDataManager.getScheduledRecordingForProgramId(programId);
if (schedule != null
&& (schedule.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED
- || schedule.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS)) {
+ || schedule.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS)) {
if (program == null) {
mDataManager.removeScheduledRecording(schedule);
if (schedule.getSeriesRecordingId() != SeriesRecording.ID_NOT_SET) {
@@ -270,15 +278,16 @@ public class DvrDbSync {
}
} else {
long currentTimeMs = System.currentTimeMillis();
- ScheduledRecording.Builder builder = ScheduledRecording.buildFrom(schedule)
- .setEndTimeMs(program.getEndTimeUtcMillis())
- .setSeasonNumber(program.getSeasonNumber())
- .setEpisodeNumber(program.getEpisodeNumber())
- .setEpisodeTitle(program.getEpisodeTitle())
- .setProgramDescription(program.getDescription())
- .setProgramLongDescription(program.getLongDescription())
- .setProgramPosterArtUri(program.getPosterArtUri())
- .setProgramThumbnailUri(program.getThumbnailUri());
+ ScheduledRecording.Builder builder =
+ ScheduledRecording.buildFrom(schedule)
+ .setEndTimeMs(program.getEndTimeUtcMillis())
+ .setSeasonNumber(program.getSeasonNumber())
+ .setEpisodeNumber(program.getEpisodeNumber())
+ .setEpisodeTitle(program.getEpisodeTitle())
+ .setProgramDescription(program.getDescription())
+ .setProgramLongDescription(program.getLongDescription())
+ .setProgramPosterArtUri(program.getPosterArtUri())
+ .setProgramThumbnailUri(program.getThumbnailUri());
boolean needUpdate = false;
// Check the series recording.
SeriesRecording seriesRecordingForOldSchedule =
@@ -289,9 +298,11 @@ public class DvrDbSync {
mDataManager.getSeriesRecording(program.getSeriesId());
if (seriesRecording == null) {
// The new program is episodic while the previous one isn't.
- SeriesRecording newSeriesRecording = mDvrManager.addSeriesRecording(
- program, Collections.singletonList(program),
- SeriesRecording.STATE_SERIES_STOPPED);
+ SeriesRecording newSeriesRecording =
+ mDvrManager.addSeriesRecording(
+ program,
+ Collections.singletonList(program),
+ SeriesRecording.STATE_SERIES_STOPPED);
builder.setSeriesRecordingId(newSeriesRecording.getId());
needUpdate = true;
} else if (seriesRecording.getId() != schedule.getSeriesRecordingId()) {
@@ -302,10 +313,10 @@ public class DvrDbSync {
if (seriesRecordingForOldSchedule != null) {
seriesRecordingsToUpdate.add(seriesRecordingForOldSchedule);
}
- } else if (!Objects.equals(schedule.getSeasonNumber(),
- program.getSeasonNumber())
- || !Objects.equals(schedule.getEpisodeNumber(),
- program.getEpisodeNumber())) {
+ } else if (!Objects.equals(
+ schedule.getSeasonNumber(), program.getSeasonNumber())
+ || !Objects.equals(
+ schedule.getEpisodeNumber(), program.getEpisodeNumber())) {
// The episode number has been changed.
if (seriesRecordingForOldSchedule != null) {
seriesRecordingsToUpdate.add(seriesRecordingForOldSchedule);
@@ -318,23 +329,24 @@ public class DvrDbSync {
// Change start time only when the recording is not started yet.
boolean needToChangeStartTime =
schedule.getState() != ScheduledRecording.STATE_RECORDING_IN_PROGRESS
- && program.getStartTimeUtcMillis() != schedule.getStartTimeMs();
+ && program.getStartTimeUtcMillis() != schedule.getStartTimeMs();
if (needToChangeStartTime) {
builder.setStartTimeMs(program.getStartTimeUtcMillis());
needUpdate = true;
}
- if (needUpdate || schedule.getEndTimeMs() != program.getEndTimeUtcMillis()
+ if (needUpdate
+ || schedule.getEndTimeMs() != program.getEndTimeUtcMillis()
|| !Objects.equals(schedule.getSeasonNumber(), program.getSeasonNumber())
|| !Objects.equals(schedule.getEpisodeNumber(), program.getEpisodeNumber())
|| !Objects.equals(schedule.getEpisodeTitle(), program.getEpisodeTitle())
- || !Objects.equals(schedule.getProgramDescription(),
- program.getDescription())
- || !Objects.equals(schedule.getProgramLongDescription(),
- program.getLongDescription())
- || !Objects.equals(schedule.getProgramPosterArtUri(),
- program.getPosterArtUri())
- || !Objects.equals(schedule.getProgramThumbnailUri(),
- program.getThumbnailUri())) {
+ || !Objects.equals(
+ schedule.getProgramDescription(), program.getDescription())
+ || !Objects.equals(
+ schedule.getProgramLongDescription(), program.getLongDescription())
+ || !Objects.equals(
+ schedule.getProgramPosterArtUri(), program.getPosterArtUri())
+ || !Objects.equals(
+ schedule.getProgramThumbnailUri(), program.getThumbnailUri())) {
mDataManager.updateScheduledRecording(builder.build());
}
if (!seriesRecordingsToUpdate.isEmpty()) {
@@ -349,7 +361,7 @@ public class DvrDbSync {
private final long mProgramId;
QueryProgramTask(long programId) {
- super(mContext.getContentResolver(), programId);
+ super(mDbExecutor, mContext.getContentResolver(), programId);
mProgramId = programId;
}
diff --git a/src/com/android/tv/dvr/provider/EpisodicProgramLoadTask.java b/src/com/android/tv/dvr/provider/EpisodicProgramLoadTask.java
index ba0aca51..b7d9f3b3 100644
--- a/src/com/android/tv/dvr/provider/EpisodicProgramLoadTask.java
+++ b/src/com/android/tv/dvr/provider/EpisodicProgramLoadTask.java
@@ -25,18 +25,16 @@ import android.net.Uri;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
-
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
+import com.android.tv.common.util.PermissionUtils;
import com.android.tv.data.Program;
import com.android.tv.dvr.DvrDataManager;
-import com.android.tv.dvr.data.SeasonEpisodeNumber;
import com.android.tv.dvr.data.ScheduledRecording;
+import com.android.tv.dvr.data.SeasonEpisodeNumber;
import com.android.tv.dvr.data.SeriesRecording;
import com.android.tv.util.AsyncDbTask.AsyncProgramQueryTask;
import com.android.tv.util.AsyncDbTask.CursorFilter;
-import com.android.tv.util.PermissionUtils;
-
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -44,11 +42,9 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
-/**
- * A wrapper of AsyncProgramQueryTask to load the episodic programs for the series recordings.
- */
+/** A wrapper of AsyncProgramQueryTask to load the episodic programs for the series recordings. */
@TargetApi(Build.VERSION_CODES.N)
-abstract public class EpisodicProgramLoadTask {
+public abstract class EpisodicProgramLoadTask {
private static final String TAG = "EpisodicProgramLoadTask";
private static final int PROGRAM_ID_INDEX = Program.getColumnIndex(Programs._ID);
@@ -61,11 +57,15 @@ abstract public class EpisodicProgramLoadTask {
private static final String PARAM_END_TIME = "end_time";
private static final String PROGRAM_PREDICATE =
- Programs.COLUMN_START_TIME_UTC_MILLIS + ">? AND "
- + Programs.COLUMN_RECORDING_PROHIBITED + "=0";
+ Programs.COLUMN_START_TIME_UTC_MILLIS
+ + ">? AND "
+ + Programs.COLUMN_RECORDING_PROHIBITED
+ + "=0";
private static final String PROGRAM_PREDICATE_WITH_CURRENT_PROGRAM =
- Programs.COLUMN_END_TIME_UTC_MILLIS + ">? AND "
- + Programs.COLUMN_RECORDING_PROHIBITED + "=0";
+ Programs.COLUMN_END_TIME_UTC_MILLIS
+ + ">? AND "
+ + Programs.COLUMN_RECORDING_PROHIBITED
+ + "=0";
private static final String CHANNEL_ID_PREDICATE = Programs.COLUMN_CHANNEL_ID + "=?";
private static final String PROGRAM_TITLE_PREDICATE = Programs.COLUMN_TITLE + "=?";
@@ -80,10 +80,7 @@ abstract public class EpisodicProgramLoadTask {
private final ArrayList<SeriesRecording> mSeriesRecordings = new ArrayList<>();
private AsyncProgramQueryTask mProgramQueryTask;
- /**
- *
- * Constructor used to load programs for one series recording with the given channel option.
- */
+ /** Constructor used to load programs for one series recording with the given channel option. */
public EpisodicProgramLoadTask(Context context, SeriesRecording seriesRecording) {
this(context, Collections.singletonList(seriesRecording));
}
@@ -94,64 +91,56 @@ abstract public class EpisodicProgramLoadTask {
*/
public EpisodicProgramLoadTask(Context context, Collection<SeriesRecording> seriesRecordings) {
mContext = context.getApplicationContext();
- mDataManager = TvApplication.getSingletons(context).getDvrDataManager();
+ mDataManager = TvSingletons.getSingletons(context).getDvrDataManager();
mSeriesRecordings.addAll(seriesRecordings);
}
- /**
- * Returns the series recordings.
- */
+ /** Returns the series recordings. */
public List<SeriesRecording> getSeriesRecordings() {
return mSeriesRecordings;
}
- /**
- * Returns the program query task. It is {@code null} until it is executed.
- */
+ /** Returns the program query task. It is {@code null} until it is executed. */
@Nullable
public AsyncProgramQueryTask getTask() {
return mProgramQueryTask;
}
- /**
- * Enables loading current programs. The default value is {@code false}.
- */
+ /** Enables loading current programs. The default value is {@code false}. */
public EpisodicProgramLoadTask setLoadCurrentProgram(boolean loadCurrentProgram) {
- SoftPreconditions.checkState(mProgramQueryTask == null, TAG,
- "Can't change setting after execution.");
+ SoftPreconditions.checkState(
+ mProgramQueryTask == null, TAG, "Can't change setting after execution.");
mLoadCurrentProgram = loadCurrentProgram;
return this;
}
- /**
- * Enables already schedules episodes. The default value is {@code false}.
- */
+ /** Enables already schedules episodes. The default value is {@code false}. */
public EpisodicProgramLoadTask setLoadScheduledEpisode(boolean loadScheduledEpisode) {
- SoftPreconditions.checkState(mProgramQueryTask == null, TAG,
- "Can't change setting after execution.");
+ SoftPreconditions.checkState(
+ mProgramQueryTask == null, TAG, "Can't change setting after execution.");
mLoadScheduledEpisode = loadScheduledEpisode;
return this;
}
/**
- * Enables loading disallowed programs whose schedules were removed manually by the user.
- * The default value is {@code false}.
+ * Enables loading disallowed programs whose schedules were removed manually by the user. The
+ * default value is {@code false}.
*/
public EpisodicProgramLoadTask setLoadDisallowedProgram(boolean loadDisallowedProgram) {
- SoftPreconditions.checkState(mProgramQueryTask == null, TAG,
- "Can't change setting after execution.");
+ SoftPreconditions.checkState(
+ mProgramQueryTask == null, TAG, "Can't change setting after execution.");
mLoadDisallowedProgram = loadDisallowedProgram;
return this;
}
/**
- * Gives the option whether to ignore the channel option when matching programs.
- * If {@code ignoreChannelOption} is {@code true}, the program will be matched with
- * {@link SeriesRecording#OPTION_CHANNEL_ALL} option.
+ * Gives the option whether to ignore the channel option when matching programs. If {@code
+ * ignoreChannelOption} is {@code true}, the program will be matched with {@link
+ * SeriesRecording#OPTION_CHANNEL_ALL} option.
*/
public EpisodicProgramLoadTask setIgnoreChannelOption(boolean ignoreChannelOption) {
- SoftPreconditions.checkState(mProgramQueryTask == null, TAG,
- "Can't change setting after execution.");
+ SoftPreconditions.checkState(
+ mProgramQueryTask == null, TAG, "Can't change setting after execution.");
mIgnoreChannelOption = ignoreChannelOption;
return this;
}
@@ -162,12 +151,15 @@ abstract public class EpisodicProgramLoadTask {
* @see com.android.tv.util.AsyncDbTask#executeOnDbThread
*/
public void execute() {
- if (SoftPreconditions.checkState(mProgramQueryTask == null, TAG,
+ if (SoftPreconditions.checkState(
+ mProgramQueryTask == null,
+ TAG,
"Can't execute task: the task is already running.")) {
- mQueryAllChannels = mSeriesRecordings.size() > 1
- || mSeriesRecordings.get(0).getChannelOption()
- == SeriesRecording.OPTION_CHANNEL_ALL
- || mIgnoreChannelOption;
+ mQueryAllChannels =
+ mSeriesRecordings.size() > 1
+ || mSeriesRecordings.get(0).getChannelOption()
+ == SeriesRecording.OPTION_CHANNEL_ALL
+ || mIgnoreChannelOption;
mProgramQueryTask = createTask();
mProgramQueryTask.executeOnDbThread();
}
@@ -184,22 +176,22 @@ abstract public class EpisodicProgramLoadTask {
}
}
- /**
- * Runs on the UI thread after the program loading finishes successfully.
- */
- protected void onPostExecute(List<Program> programs) {
- }
+ /** Runs on the UI thread after the program loading finishes successfully. */
+ protected void onPostExecute(List<Program> programs) {}
- /**
- * Runs on the UI thread after the program loading was canceled.
- */
- protected void onCancelled(List<Program> programs) {
- }
+ /** Runs on the UI thread after the program loading was canceled. */
+ protected void onCancelled(List<Program> programs) {}
private AsyncProgramQueryTask createTask() {
SqlParams sqlParams = createSqlParams();
- return new AsyncProgramQueryTask(mContext.getContentResolver(), sqlParams.uri,
- sqlParams.selection, sqlParams.selectionArgs, null, sqlParams.filter) {
+ return new AsyncProgramQueryTask(
+ TvSingletons.getSingletons(mContext).getDbExecutor(),
+ mContext.getContentResolver(),
+ sqlParams.uri,
+ sqlParams.selection,
+ sqlParams.selectionArgs,
+ null,
+ sqlParams.filter) {
@Override
protected void onPostExecute(List<Program> programs) {
EpisodicProgramLoadTask.this.onPostExecute(programs);
@@ -217,8 +209,11 @@ abstract public class EpisodicProgramLoadTask {
if (PermissionUtils.hasAccessAllEpg(mContext)) {
sqlParams.uri = Programs.CONTENT_URI;
// Base
- StringBuilder selection = new StringBuilder(mLoadCurrentProgram
- ? PROGRAM_PREDICATE_WITH_CURRENT_PROGRAM : PROGRAM_PREDICATE);
+ StringBuilder selection =
+ new StringBuilder(
+ mLoadCurrentProgram
+ ? PROGRAM_PREDICATE_WITH_CURRENT_PROGRAM
+ : PROGRAM_PREDICATE);
List<String> args = new ArrayList<>();
args.add(Long.toString(System.currentTimeMillis()));
// Channel option
@@ -237,15 +232,21 @@ abstract public class EpisodicProgramLoadTask {
} else {
// The query includes the current program. Will be filtered if needed.
if (mQueryAllChannels) {
- sqlParams.uri = Programs.CONTENT_URI.buildUpon()
- .appendQueryParameter(PARAM_START_TIME,
- String.valueOf(System.currentTimeMillis()))
- .appendQueryParameter(PARAM_END_TIME, String.valueOf(Long.MAX_VALUE))
- .build();
+ sqlParams.uri =
+ Programs.CONTENT_URI
+ .buildUpon()
+ .appendQueryParameter(
+ PARAM_START_TIME,
+ String.valueOf(System.currentTimeMillis()))
+ .appendQueryParameter(
+ PARAM_END_TIME, String.valueOf(Long.MAX_VALUE))
+ .build();
} else {
- sqlParams.uri = TvContract.buildProgramsUriForChannel(
- mSeriesRecordings.get(0).getChannelId(),
- System.currentTimeMillis(), Long.MAX_VALUE);
+ sqlParams.uri =
+ TvContract.buildProgramsUriForChannel(
+ mSeriesRecordings.get(0).getChannelId(),
+ System.currentTimeMillis(),
+ Long.MAX_VALUE);
}
sqlParams.selection = null;
sqlParams.selectionArgs = null;
@@ -292,16 +293,19 @@ abstract public class EpisodicProgramLoadTask {
for (SeriesRecording seriesRecording : mSeriesRecordings) {
boolean programMatches;
if (mIgnoreChannelOption) {
- programMatches = seriesRecording.matchProgram(program,
- SeriesRecording.OPTION_CHANNEL_ALL);
+ programMatches =
+ seriesRecording.matchProgram(
+ program, SeriesRecording.OPTION_CHANNEL_ALL);
} else {
programMatches = seriesRecording.matchProgram(program);
}
if (programMatches) {
return mLoadScheduledEpisode
- || !mSeasonEpisodeNumbers.contains(new SeasonEpisodeNumber(
- seriesRecording.getId(), program.getSeasonNumber(),
- program.getEpisodeNumber()));
+ || !mSeasonEpisodeNumbers.contains(
+ new SeasonEpisodeNumber(
+ seriesRecording.getId(),
+ program.getSeasonNumber(),
+ program.getEpisodeNumber()));
}
}
return false;
@@ -316,7 +320,8 @@ abstract public class EpisodicProgramLoadTask {
@Override
public boolean filter(Cursor c) {
return (mLoadCurrentProgram || c.getLong(START_TIME_INDEX) > System.currentTimeMillis())
- && c.getInt(RECORDING_PROHIBITED_INDEX) != 0 && super.filter(c);
+ && c.getInt(RECORDING_PROHIBITED_INDEX) != 0
+ && super.filter(c);
}
}
diff --git a/src/com/android/tv/dvr/recorder/ConflictChecker.java b/src/com/android/tv/dvr/recorder/ConflictChecker.java
index 8aa90116..bfd315e9 100644
--- a/src/com/android/tv/dvr/recorder/ConflictChecker.java
+++ b/src/com/android/tv/dvr/recorder/ConflictChecker.java
@@ -27,21 +27,19 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.ArraySet;
import android.util.Log;
-
-import com.android.tv.ApplicationSingletons;
import com.android.tv.InputSessionManager;
import com.android.tv.InputSessionManager.OnTvViewChannelChangeListener;
import com.android.tv.MainActivity;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.common.WeakHandler;
-import com.android.tv.data.Channel;
import com.android.tv.data.ChannelDataManager;
+import com.android.tv.data.api.Channel;
import com.android.tv.dvr.DvrDataManager.ScheduledRecordingListener;
import com.android.tv.dvr.DvrScheduleManager;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.ui.DvrUiHelper;
-
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -50,8 +48,9 @@ import java.util.concurrent.TimeUnit;
/**
* Checking the runtime conflict of DVR recording.
- * <p>
- * This class runs only while the {@link MainActivity} is resumed and holds the upcoming conflicts.
+ *
+ * <p>This class runs only while the {@link MainActivity} is resumed and holds the upcoming
+ * conflicts.
*/
@TargetApi(Build.VERSION_CODES.N)
@MainThread
@@ -87,24 +86,40 @@ public class ConflictChecker {
private final ScheduledRecordingListener mScheduledRecordingListener =
new ScheduledRecordingListener() {
- @Override
- public void onScheduledRecordingAdded(ScheduledRecording... scheduledRecordings) {
- if (DEBUG) Log.d(TAG, "onScheduledRecordingAdded: " + scheduledRecordings);
- mHandler.sendEmptyMessage(MSG_CHECK_CONFLICT);
- }
+ @Override
+ public void onScheduledRecordingAdded(ScheduledRecording... scheduledRecordings) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "onScheduledRecordingAdded: "
+ + Arrays.toString(scheduledRecordings));
+ }
+ mHandler.sendEmptyMessage(MSG_CHECK_CONFLICT);
+ }
- @Override
- public void onScheduledRecordingRemoved(ScheduledRecording... scheduledRecordings) {
- if (DEBUG) Log.d(TAG, "onScheduledRecordingRemoved: " + scheduledRecordings);
- mHandler.sendEmptyMessage(MSG_CHECK_CONFLICT);
- }
+ @Override
+ public void onScheduledRecordingRemoved(ScheduledRecording... scheduledRecordings) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "onScheduledRecordingRemoved: "
+ + Arrays.toString(scheduledRecordings));
+ }
+ mHandler.sendEmptyMessage(MSG_CHECK_CONFLICT);
+ }
- @Override
- public void onScheduledRecordingStatusChanged(ScheduledRecording... scheduledRecordings) {
- if (DEBUG) Log.d(TAG, "onScheduledRecordingStatusChanged: " + scheduledRecordings);
- mHandler.sendEmptyMessage(MSG_CHECK_CONFLICT);
- }
- };
+ @Override
+ public void onScheduledRecordingStatusChanged(
+ ScheduledRecording... scheduledRecordings) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "onScheduledRecordingStatusChanged: "
+ + Arrays.toString(scheduledRecordings));
+ }
+ mHandler.sendEmptyMessage(MSG_CHECK_CONFLICT);
+ }
+ };
private final OnTvViewChannelChangeListener mOnTvViewChannelChangeListener =
new OnTvViewChannelChangeListener() {
@@ -118,15 +133,13 @@ public class ConflictChecker {
public ConflictChecker(MainActivity mainActivity) {
mMainActivity = mainActivity;
- ApplicationSingletons appSingletons = TvApplication.getSingletons(mainActivity);
- mChannelDataManager = appSingletons.getChannelDataManager();
- mScheduleManager = appSingletons.getDvrScheduleManager();
- mSessionManager = appSingletons.getInputSessionManager();
+ TvSingletons tvSingletons = TvSingletons.getSingletons(mainActivity);
+ mChannelDataManager = tvSingletons.getChannelDataManager();
+ mScheduleManager = tvSingletons.getDvrScheduleManager();
+ mSessionManager = tvSingletons.getInputSessionManager();
}
- /**
- * Starts checking the conflict.
- */
+ /** Starts checking the conflict. */
public void start() {
if (mStarted) {
return;
@@ -137,9 +150,7 @@ public class ConflictChecker {
mSessionManager.addOnTvViewChannelChangeListener(mOnTvViewChannelChangeListener);
}
- /**
- * Stops checking the conflict.
- */
+ /** Stops checking the conflict. */
public void stop() {
if (!mStarted) {
return;
@@ -150,23 +161,17 @@ public class ConflictChecker {
mHandler.removeCallbacksAndMessages(null);
}
- /**
- * Returns the upcoming conflicts.
- */
+ /** Returns the upcoming conflicts. */
public List<ScheduledRecording> getUpcomingConflicts() {
return new ArrayList<>(mUpcomingConflicts);
}
- /**
- * Adds a {@link OnUpcomingConflictChangeListener}.
- */
+ /** Adds a {@link OnUpcomingConflictChangeListener}. */
public void addOnUpcomingConflictChangeListener(OnUpcomingConflictChangeListener listener) {
mOnUpcomingConflictChangeListeners.add(listener);
}
- /**
- * Removes the {@link OnUpcomingConflictChangeListener}.
- */
+ /** Removes the {@link OnUpcomingConflictChangeListener}. */
public void removeOnUpcomingConflictChangeListener(OnUpcomingConflictChangeListener listener) {
mOnUpcomingConflictChangeListeners.remove(listener);
}
@@ -177,9 +182,7 @@ public class ConflictChecker {
}
}
- /**
- * Remembers the user's decision to record while watching the channel.
- */
+ /** Remembers the user's decision to record while watching the channel. */
public void setCheckedConflictsForChannel(long mChannelId, List<ScheduledRecording> conflicts) {
mCheckedConflictsMap.put(mChannelId, new ArrayList<>(conflicts));
}
@@ -190,8 +193,7 @@ public class ConflictChecker {
if (DEBUG) Log.d(TAG, "Handling MSG_CHECK_CONFLICT");
mHandler.removeMessages(MSG_CHECK_CONFLICT);
mUpcomingConflicts.clear();
- if (!mScheduleManager.isInitialized()
- || !mChannelDataManager.isDbLoadFinished()) {
+ if (!mScheduleManager.isInitialized() || !mChannelDataManager.isDbLoadFinished()) {
mHandler.sendEmptyMessageDelayed(MSG_CHECK_CONFLICT, CHECK_RETRY_PERIOD_MS);
notifyUpcomingConflictChanged();
return;
@@ -209,8 +211,8 @@ public class ConflictChecker {
long channelId = ContentUris.parseId(channelUri);
Channel channel = mChannelDataManager.getChannel(channelId);
// The conflicts caused by watching the channel.
- List<ScheduledRecording> conflicts = mScheduleManager
- .getConflictingSchedulesForWatching(channel.getId());
+ List<ScheduledRecording> conflicts =
+ mScheduleManager.getConflictingSchedulesForWatching(channel.getId());
long earliestToCheck = Long.MAX_VALUE;
long currentTimeMs = System.currentTimeMillis();
for (ScheduledRecording schedule : conflicts) {
@@ -239,18 +241,15 @@ public class ConflictChecker {
}
}
if (earliestToCheck != Long.MAX_VALUE) {
- mHandler.sendEmptyMessageDelayed(MSG_CHECK_CONFLICT,
- earliestToCheck - currentTimeMs);
+ mHandler.sendEmptyMessageDelayed(MSG_CHECK_CONFLICT, earliestToCheck - currentTimeMs);
}
if (DEBUG) Log.d(TAG, "upcoming conflicts: " + mUpcomingConflicts);
notifyUpcomingConflictChanged();
if (!mUpcomingConflicts.isEmpty()
&& !DvrUiHelper.isChannelWatchConflictDialogShown(mMainActivity)) {
// Don't show the conflict dialog if the user already knows.
- List<ScheduledRecording> checkedConflicts = mCheckedConflictsMap.get(
- channel.getId());
- if (checkedConflicts == null
- || !checkedConflicts.containsAll(mUpcomingConflicts)) {
+ List<ScheduledRecording> checkedConflicts = mCheckedConflictsMap.get(channel.getId());
+ if (checkedConflicts == null || !checkedConflicts.containsAll(mUpcomingConflicts)) {
DvrUiHelper.showChannelWatchConflictDialog(mMainActivity, channel);
}
}
@@ -271,9 +270,7 @@ public class ConflictChecker {
}
}
- /**
- * A listener for the change of upcoming conflicts.
- */
+ /** A listener for the change of upcoming conflicts. */
public interface OnUpcomingConflictChangeListener {
void onUpcomingConflictChange();
}
diff --git a/src/com/android/tv/dvr/recorder/DvrRecordingService.java b/src/com/android/tv/dvr/recorder/DvrRecordingService.java
index 5d324ca5..9fdbf062 100644
--- a/src/com/android/tv/dvr/recorder/DvrRecordingService.java
+++ b/src/com/android/tv/dvr/recorder/DvrRecordingService.java
@@ -29,21 +29,20 @@ import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.annotation.VisibleForTesting;
import android.util.Log;
-
-import com.android.tv.ApplicationSingletons;
import com.android.tv.InputSessionManager;
import com.android.tv.InputSessionManager.OnRecordingSessionChangeListener;
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.Starter;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
import com.android.tv.common.feature.CommonFeatures;
+import com.android.tv.common.util.Clock;
import com.android.tv.dvr.WritableDvrDataManager;
-import com.android.tv.util.Clock;
import com.android.tv.util.RecurringRunner;
/**
- * DVR recording service. This service should be a foreground service and send a notification
- * to users to do long-running recording task.
+ * DVR recording service. This service should be a foreground service and send a notification to
+ * users to do long-running recording task.
*
* <p>This service is waken up when there's a scheduled recording coming soon and at boot completed
* since schedules have to be loaded from databases in order to set new recording alarms, which
@@ -67,9 +66,9 @@ public class DvrRecordingService extends Service {
/**
* Starts the service in foreground.
*
- * @param startForRecording {@code true} if there are upcoming recordings in
- * {@link RecordingScheduler#SOON_DURATION_IN_MS} and the service is
- * started in foreground for those recordings.
+ * @param startForRecording {@code true} if there are upcoming recordings in {@link
+ * RecordingScheduler#SOON_DURATION_IN_MS} and the service is started in foreground for
+ * those recordings.
*/
@MainThread
static void startForegroundService(Context context, boolean startForRecording) {
@@ -99,7 +98,8 @@ public class DvrRecordingService extends Service {
@VisibleForTesting boolean mIsRecording;
private boolean mForeground;
- @VisibleForTesting final OnRecordingSessionChangeListener mOnRecordingSessionChangeListener =
+ @VisibleForTesting
+ final OnRecordingSessionChangeListener mOnRecordingSessionChangeListener =
new OnRecordingSessionChangeListener() {
@Override
public void onRecordingSessionChange(final boolean create, final int count) {
@@ -114,18 +114,22 @@ public class DvrRecordingService extends Service {
@Override
public void onCreate() {
- TvApplication.setCurrentRunningProcess(this, true);
+ Starter.start(this);
if (DEBUG) Log.d(TAG, "onCreate");
super.onCreate();
SoftPreconditions.checkFeatureEnabled(this, CommonFeatures.DVR, TAG);
sInstance = this;
- ApplicationSingletons singletons = TvApplication.getSingletons(this);
+ TvSingletons singletons = TvSingletons.getSingletons(this);
WritableDvrDataManager dataManager =
(WritableDvrDataManager) singletons.getDvrDataManager();
mSessionManager = singletons.getInputSessionManager();
mSessionManager.addOnRecordingSessionChangeListener(mOnRecordingSessionChangeListener);
- mReaperRunner = new RecurringRunner(this, java.util.concurrent.TimeUnit.DAYS.toMillis(1),
- new ScheduledProgramReaper(dataManager, Clock.SYSTEM), null);
+ mReaperRunner =
+ new RecurringRunner(
+ this,
+ java.util.concurrent.TimeUnit.DAYS.toMillis(1),
+ new ScheduledProgramReaper(dataManager, Clock.SYSTEM),
+ null);
mReaperRunner.start();
mContentTitle = getString(R.string.dvr_notification_content_title);
mContentTextRecording = getString(R.string.dvr_notification_content_text_recording);
@@ -179,13 +183,16 @@ public class DvrRecordingService extends Service {
@VisibleForTesting
protected void startForegroundInternal(boolean hasUpcomingRecording) {
- // STOPSHIP: Replace the content title with real UX strings
- Notification.Builder builder = new Notification.Builder(this)
- .setContentTitle(mContentTitle)
- .setContentText(hasUpcomingRecording ? mContentTextRecording : mContentTextLoading)
- .setSmallIcon(R.drawable.ic_dvr);
- Notification notification = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
- builder.setChannelId(DVR_NOTIFICATION_CHANNEL_ID).build() : builder.build();
+ Notification.Builder builder =
+ new Notification.Builder(this)
+ .setContentTitle(mContentTitle)
+ .setContentText(
+ hasUpcomingRecording ? mContentTextRecording : mContentTextLoading)
+ .setSmallIcon(R.drawable.ic_dvr);
+ Notification notification =
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
+ ? builder.setChannelId(DVR_NOTIFICATION_CHANNEL_ID).build()
+ : builder.build();
startForeground(ONGOING_NOTIFICATION_ID, notification);
}
@@ -196,10 +203,11 @@ public class DvrRecordingService extends Service {
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- // STOPSHIP: Replace the channel name with real UX strings
- mNotificationChannel = new NotificationChannel(DVR_NOTIFICATION_CHANNEL_ID,
- getString(R.string.dvr_notification_channel_name),
- NotificationManager.IMPORTANCE_LOW);
+ mNotificationChannel =
+ new NotificationChannel(
+ DVR_NOTIFICATION_CHANNEL_ID,
+ getString(R.string.dvr_notification_channel_name),
+ NotificationManager.IMPORTANCE_LOW);
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE))
.createNotificationChannel(mNotificationChannel);
}
diff --git a/src/com/android/tv/dvr/recorder/DvrStartRecordingReceiver.java b/src/com/android/tv/dvr/recorder/DvrStartRecordingReceiver.java
index f1c0020b..bb5ea99d 100644
--- a/src/com/android/tv/dvr/recorder/DvrStartRecordingReceiver.java
+++ b/src/com/android/tv/dvr/recorder/DvrStartRecordingReceiver.java
@@ -21,18 +21,16 @@ import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.support.annotation.RequiresApi;
+import com.android.tv.Starter;
+import com.android.tv.TvSingletons;
-import com.android.tv.TvApplication;
-
-/**
- * Signals the DVR to start recording shows <i>soon</i>.
- */
+/** Signals the DVR to start recording shows <i>soon</i>. */
@RequiresApi(Build.VERSION_CODES.N)
public class DvrStartRecordingReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- TvApplication.setCurrentRunningProcess(context, true);
- RecordingScheduler scheduler = TvApplication.getSingletons(context).getRecordingScheduler();
+ Starter.start(context);
+ RecordingScheduler scheduler = TvSingletons.getSingletons(context).getRecordingScheduler();
if (scheduler != null) {
scheduler.updateAndStartServiceIfNeeded();
}
diff --git a/src/com/android/tv/dvr/recorder/InputTaskScheduler.java b/src/com/android/tv/dvr/recorder/InputTaskScheduler.java
index fee4568e..1021b2bc 100644
--- a/src/com/android/tv/dvr/recorder/InputTaskScheduler.java
+++ b/src/com/android/tv/dvr/recorder/InputTaskScheduler.java
@@ -25,17 +25,15 @@ import android.support.annotation.VisibleForTesting;
import android.util.ArrayMap;
import android.util.Log;
import android.util.LongSparseArray;
-
import com.android.tv.InputSessionManager;
-import com.android.tv.data.Channel;
+import com.android.tv.common.util.Clock;
import com.android.tv.data.ChannelDataManager;
+import com.android.tv.data.api.Channel;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.WritableDvrDataManager;
import com.android.tv.dvr.data.ScheduledRecording;
-import com.android.tv.util.Clock;
import com.android.tv.util.CompositeComparator;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -43,9 +41,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
-/**
- * The scheduler for a TV input.
- */
+/** The scheduler for a TV input. */
public class InputTaskScheduler {
private static final String TAG = "InputTaskScheduler";
private static final boolean DEBUG = false;
@@ -66,9 +62,7 @@ public class InputTaskScheduler {
RecordingTask.END_TIME_COMPARATOR,
RecordingTask.ID_COMPARATOR);
- /**
- * Returns the comparator which the schedules are sorted with when executed.
- */
+ /** Returns the comparator which the schedules are sorted with when executed. */
public static Comparator<ScheduledRecording> getRecordingOrderComparator() {
return ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR;
}
@@ -81,8 +75,8 @@ public class InputTaskScheduler {
private final long mId;
private final RecordingTask mTask;
- HandlerWrapper(Looper looper, ScheduledRecording scheduledRecording,
- RecordingTask recordingTask) {
+ HandlerWrapper(
+ Looper looper, ScheduledRecording scheduledRecording, RecordingTask recordingTask) {
super(looper, recordingTask);
mId = scheduledRecording.getId();
mTask = recordingTask;
@@ -94,7 +88,7 @@ public class InputTaskScheduler {
// The RecordingTask gets a chance first.
// It must return false to pass this message to here.
if (msg.what == MESSAGE_REMOVE) {
- if (DEBUG) Log.d(TAG, "done " + mId);
+ if (DEBUG) Log.d(TAG, "done " + mId);
mPendingRecordings.remove(mId);
}
removeCallbacksAndMessages(null);
@@ -120,17 +114,37 @@ public class InputTaskScheduler {
private final Object mInputLock = new Object();
private final RecordingTaskFactory mRecordingTaskFactory;
- public InputTaskScheduler(Context context, TvInputInfo input, Looper looper,
- ChannelDataManager channelDataManager, DvrManager dvrManager,
- DvrDataManager dataManager, InputSessionManager sessionManager, Clock clock) {
- this(context, input, looper, channelDataManager, dvrManager, dataManager, sessionManager,
- clock, null);
+ public InputTaskScheduler(
+ Context context,
+ TvInputInfo input,
+ Looper looper,
+ ChannelDataManager channelDataManager,
+ DvrManager dvrManager,
+ DvrDataManager dataManager,
+ InputSessionManager sessionManager,
+ Clock clock) {
+ this(
+ context,
+ input,
+ looper,
+ channelDataManager,
+ dvrManager,
+ dataManager,
+ sessionManager,
+ clock,
+ null);
}
@VisibleForTesting
- InputTaskScheduler(Context context, TvInputInfo input, Looper looper,
- ChannelDataManager channelDataManager, DvrManager dvrManager,
- DvrDataManager dataManager, InputSessionManager sessionManager, Clock clock,
+ InputTaskScheduler(
+ Context context,
+ TvInputInfo input,
+ Looper looper,
+ ChannelDataManager channelDataManager,
+ DvrManager dvrManager,
+ DvrDataManager dataManager,
+ InputSessionManager sessionManager,
+ Clock clock,
RecordingTaskFactory recordingTaskFactory) {
if (DEBUG) Log.d(TAG, "Creating scheduler for " + input);
mContext = context;
@@ -142,22 +156,32 @@ public class InputTaskScheduler {
mSessionManager = sessionManager;
mClock = clock;
mMainThreadHandler = new Handler(Looper.getMainLooper());
- mRecordingTaskFactory = recordingTaskFactory != null ? recordingTaskFactory
- : new RecordingTaskFactory() {
- @Override
- public RecordingTask createRecordingTask(ScheduledRecording schedule, Channel channel,
- DvrManager dvrManager, InputSessionManager sessionManager,
- WritableDvrDataManager dataManager, Clock clock) {
- return new RecordingTask(mContext, schedule, channel, mDvrManager, mSessionManager,
- mDataManager, mClock);
- }
- };
+ mRecordingTaskFactory =
+ recordingTaskFactory != null
+ ? recordingTaskFactory
+ : new RecordingTaskFactory() {
+ @Override
+ public RecordingTask createRecordingTask(
+ ScheduledRecording schedule,
+ Channel channel,
+ DvrManager dvrManager,
+ InputSessionManager sessionManager,
+ WritableDvrDataManager dataManager,
+ Clock clock) {
+ return new RecordingTask(
+ mContext,
+ schedule,
+ channel,
+ mDvrManager,
+ mSessionManager,
+ mDataManager,
+ mClock);
+ }
+ };
mHandler = new WorkerThreadHandler(looper);
}
- /**
- * Adds a {@link ScheduledRecording}.
- */
+ /** Adds a {@link ScheduledRecording}. */
public void addSchedule(ScheduledRecording schedule) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_ADD_SCHEDULED_RECORDING, schedule));
}
@@ -173,9 +197,7 @@ public class InputTaskScheduler {
mHandler.sendEmptyMessage(MSG_BUILD_SCHEDULE);
}
- /**
- * Removes the {@link ScheduledRecording}.
- */
+ /** Removes the {@link ScheduledRecording}. */
public void removeSchedule(ScheduledRecording schedule) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_REMOVE_SCHEDULED_RECORDING, schedule));
}
@@ -194,9 +216,7 @@ public class InputTaskScheduler {
}
}
- /**
- * Updates the {@link ScheduledRecording}.
- */
+ /** Updates the {@link ScheduledRecording}. */
public void updateSchedule(ScheduledRecording schedule) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_SCHEDULED_RECORDING, schedule));
}
@@ -224,18 +244,14 @@ public class InputTaskScheduler {
}
}
- /**
- * Updates the TV input.
- */
+ /** Updates the TV input. */
public void updateTvInputInfo(TvInputInfo input) {
synchronized (mInputLock) {
mInput = input;
}
}
- /**
- * Stops the input task scheduler.
- */
+ /** Stops the input task scheduler. */
public void stop() {
mHandler.removeCallbacksAndMessages(null);
mHandler.sendEmptyMessage(MSG_STOP_SCHEDULE);
@@ -262,7 +278,9 @@ public class InputTaskScheduler {
ScheduledRecording schedule = iter.next();
if (schedule.getEndTimeMs() - currentTimeMs
<= MIN_REMAIN_DURATION_PERCENT * schedule.getDuration()) {
- fail(schedule);
+ Log.e(TAG, "Error! Program ended before recording started:" + schedule);
+ fail(schedule,
+ ScheduledRecording.FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED);
iter.remove();
}
}
@@ -274,7 +292,8 @@ public class InputTaskScheduler {
for (ScheduledRecording schedule : mWaitingSchedules.values()) {
if (schedule.getState() != ScheduledRecording.STATE_RECORDING_CANCELED
&& schedule.getStartTimeMs() - RecordingTask.RECORDING_EARLY_START_OFFSET_MS
- <= currentTimeMs && schedule.getEndTimeMs() > currentTimeMs) {
+ <= currentTimeMs
+ && schedule.getEndTimeMs() > currentTimeMs) {
schedulesToStart.add(schedule);
}
}
@@ -321,10 +340,12 @@ public class InputTaskScheduler {
earliest = schedule.getEndTimeMs();
}
} else {
- if (earliest > schedule.getStartTimeMs()
- - RecordingTask.RECORDING_EARLY_START_OFFSET_MS) {
- earliest = schedule.getStartTimeMs()
- - RecordingTask.RECORDING_EARLY_START_OFFSET_MS;
+ if (earliest
+ > schedule.getStartTimeMs()
+ - RecordingTask.RECORDING_EARLY_START_OFFSET_MS) {
+ earliest =
+ schedule.getStartTimeMs()
+ - RecordingTask.RECORDING_EARLY_START_OFFSET_MS;
}
}
}
@@ -333,8 +354,9 @@ public class InputTaskScheduler {
private RecordingTask createRecordingTask(ScheduledRecording schedule) {
Channel channel = mChannelDataManager.getChannel(schedule.getChannelId());
- RecordingTask recordingTask = mRecordingTaskFactory.createRecordingTask(schedule, channel,
- mDvrManager, mSessionManager, mDataManager, mClock);
+ RecordingTask recordingTask =
+ mRecordingTaskFactory.createRecordingTask(
+ schedule, channel, mDvrManager, mSessionManager, mDataManager, mClock);
HandlerWrapper handlerWrapper = new HandlerWrapper(mLooper, schedule, recordingTask);
mPendingRecordings.put(schedule.getId(), handlerWrapper);
return recordingTask;
@@ -369,21 +391,24 @@ public class InputTaskScheduler {
return candidate;
}
- private void fail(ScheduledRecording schedule) {
+ private void fail(ScheduledRecording schedule, int reason) {
// It's called when the scheduling has been failed without creating RecordingTask.
- runOnMainHandler(new Runnable() {
- @Override
- public void run() {
- ScheduledRecording scheduleInManager =
- mDataManager.getScheduledRecording(schedule.getId());
- if (scheduleInManager != null) {
- // The schedule should be updated based on the object from DataManager in case
- // when it has been updated.
- mDataManager.changeState(scheduleInManager,
- ScheduledRecording.STATE_RECORDING_FAILED);
- }
- }
- });
+ runOnMainHandler(
+ new Runnable() {
+ @Override
+ public void run() {
+ ScheduledRecording scheduleInManager =
+ mDataManager.getScheduledRecording(schedule.getId());
+ if (scheduleInManager != null) {
+ // The schedule should be updated based on the object from DataManager
+ // in case when it has been updated.
+ mDataManager.changeState(
+ scheduleInManager,
+ ScheduledRecording.STATE_RECORDING_FAILED,
+ reason);
+ }
+ }
+ });
}
private void runOnMainHandler(Runnable runnable) {
@@ -396,9 +421,13 @@ public class InputTaskScheduler {
@VisibleForTesting
interface RecordingTaskFactory {
- RecordingTask createRecordingTask(ScheduledRecording scheduledRecording, Channel channel,
- DvrManager dvrManager, InputSessionManager sessionManager,
- WritableDvrDataManager dataManager, Clock clock);
+ RecordingTask createRecordingTask(
+ ScheduledRecording scheduledRecording,
+ Channel channel,
+ DvrManager dvrManager,
+ InputSessionManager sessionManager,
+ WritableDvrDataManager dataManager,
+ Clock clock);
}
private class WorkerThreadHandler extends Handler {
@@ -417,6 +446,7 @@ public class InputTaskScheduler {
break;
case MSG_UPDATE_SCHEDULED_RECORDING:
handleUpdateSchedule((ScheduledRecording) msg.obj);
+ break;
case MSG_BUILD_SCHEDULE:
handleBuildSchedule();
break;
diff --git a/src/com/android/tv/dvr/recorder/RecordingScheduler.java b/src/com/android/tv/dvr/recorder/RecordingScheduler.java
index cbaf46b5..f309537d 100644
--- a/src/com/android/tv/dvr/recorder/RecordingScheduler.java
+++ b/src/com/android/tv/dvr/recorder/RecordingScheduler.java
@@ -31,11 +31,10 @@ import android.support.annotation.VisibleForTesting;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Range;
-
-import com.android.tv.ApplicationSingletons;
import com.android.tv.InputSessionManager;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
+import com.android.tv.common.util.Clock;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.ChannelDataManager.Listener;
import com.android.tv.dvr.DvrDataManager;
@@ -44,22 +43,21 @@ import com.android.tv.dvr.DvrDataManager.ScheduledRecordingListener;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.WritableDvrDataManager;
import com.android.tv.dvr.data.ScheduledRecording;
-import com.android.tv.util.Clock;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.Utils;
-
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
- * The core class to manage DVR schedule and run recording task.
- **
- * <p> This class is responsible for:
+ * The core class to manage DVR schedule and run recording task. *
+ *
+ * <p>This class is responsible for:
+ *
* <ul>
- * <li>Sending record commands to TV inputs</li>
- * <li>Resolving conflicting schedules, handling overlapping recording time durations, etc.</li>
+ * <li>Sending record commands to TV inputs
+ * <li>Resolving conflicting schedules, handling overlapping recording time durations, etc.
* </ul>
*
* <p>This should be a singleton associated with application's main process.
@@ -71,8 +69,8 @@ public class RecordingScheduler extends TvInputCallback implements ScheduledReco
private static final boolean DEBUG = false;
private static final String HANDLER_THREAD_NAME = "RecordingScheduler";
- private final static long SOON_DURATION_IN_MS = TimeUnit.MINUTES.toMillis(1);
- @VisibleForTesting final static long MS_TO_WAKE_BEFORE_START = TimeUnit.SECONDS.toMillis(30);
+ private static final long SOON_DURATION_IN_MS = TimeUnit.MINUTES.toMillis(1);
+ @VisibleForTesting static final long MS_TO_WAKE_BEFORE_START = TimeUnit.SECONDS.toMillis(30);
private final Looper mLooper;
private final InputSessionManager mSessionManager;
@@ -98,21 +96,22 @@ public class RecordingScheduler extends TvInputCallback implements ScheduledReco
}
};
- private Listener mChannelDataLoadListener = new Listener() {
- @Override
- public void onLoadFinished() {
- mChannelDataManager.removeListener(this);
- if (isDbLoaded()) {
- updateInternal();
- }
- }
+ private Listener mChannelDataLoadListener =
+ new Listener() {
+ @Override
+ public void onLoadFinished() {
+ mChannelDataManager.removeListener(this);
+ if (isDbLoaded()) {
+ updateInternal();
+ }
+ }
- @Override
- public void onChannelListUpdated() { }
+ @Override
+ public void onChannelListUpdated() {}
- @Override
- public void onChannelBrowsableChanged() { }
- };
+ @Override
+ public void onChannelBrowsableChanged() {}
+ };
/**
* Creates a scheduler to schedule alarms for scheduled recordings and create recording tasks.
@@ -120,21 +119,32 @@ public class RecordingScheduler extends TvInputCallback implements ScheduledReco
*/
public static RecordingScheduler createScheduler(Context context) {
SoftPreconditions.checkState(
- TvApplication.getSingletons(context).getRecordingScheduler() == null);
+ TvSingletons.getSingletons(context).getRecordingScheduler() == null);
HandlerThread handlerThread = new HandlerThread(HANDLER_THREAD_NAME);
handlerThread.start();
- ApplicationSingletons singletons = TvApplication.getSingletons(context);
- return new RecordingScheduler(handlerThread.getLooper(),
- singletons.getDvrManager(), singletons.getInputSessionManager(),
+ TvSingletons singletons = TvSingletons.getSingletons(context);
+ return new RecordingScheduler(
+ handlerThread.getLooper(),
+ singletons.getDvrManager(),
+ singletons.getInputSessionManager(),
(WritableDvrDataManager) singletons.getDvrDataManager(),
- singletons.getChannelDataManager(), singletons.getTvInputManagerHelper(), context,
- Clock.SYSTEM, (AlarmManager) context.getSystemService(Context.ALARM_SERVICE));
+ singletons.getChannelDataManager(),
+ singletons.getTvInputManagerHelper(),
+ context,
+ Clock.SYSTEM,
+ (AlarmManager) context.getSystemService(Context.ALARM_SERVICE));
}
@VisibleForTesting
- RecordingScheduler(Looper looper, DvrManager dvrManager, InputSessionManager sessionManager,
- WritableDvrDataManager dataManager, ChannelDataManager channelDataManager,
- TvInputManagerHelper inputManager, Context context, Clock clock,
+ RecordingScheduler(
+ Looper looper,
+ DvrManager dvrManager,
+ InputSessionManager sessionManager,
+ WritableDvrDataManager dataManager,
+ ChannelDataManager channelDataManager,
+ TvInputManagerHelper inputManager,
+ Context context,
+ Clock clock,
AlarmManager alarmManager) {
mLooper = looper;
mDvrManager = dvrManager;
@@ -159,9 +169,7 @@ public class RecordingScheduler extends TvInputCallback implements ScheduledReco
}
}
- /**
- * Start recording that will happen soon, and set the next alarm time.
- */
+ /** Start recording that will happen soon, and set the next alarm time. */
public void updateAndStartServiceIfNeeded() {
if (DEBUG) Log.d(TAG, "update and start service if needed");
if (isDbLoaded()) {
@@ -185,8 +193,10 @@ public class RecordingScheduler extends TvInputCallback implements ScheduledReco
}
private boolean updatePendingRecordings() {
- List<ScheduledRecording> scheduledRecordings = mDataManager
- .getScheduledRecordings(new Range<>(mLastStartTimePendingMs,
+ List<ScheduledRecording> scheduledRecordings =
+ mDataManager.getScheduledRecordings(
+ new Range<>(
+ mLastStartTimePendingMs,
mClock.currentTimeMillis() + SOON_DURATION_IN_MS),
ScheduledRecording.STATE_RECORDING_NOT_STARTED);
for (ScheduledRecording r : scheduledRecordings) {
@@ -198,7 +208,8 @@ public class RecordingScheduler extends TvInputCallback implements ScheduledReco
// recording service being wrongly pushed back to background in updateInternal().
return scheduledRecordings.size() > 0
|| (mLastStartTimePendingMs > mClock.currentTimeMillis()
- && mLastStartTimePendingMs < mClock.currentTimeMillis() + SOON_DURATION_IN_MS);
+ && mLastStartTimePendingMs
+ < mClock.currentTimeMillis() + SOON_DURATION_IN_MS);
}
private boolean isDbLoaded() {
@@ -269,18 +280,32 @@ public class RecordingScheduler extends TvInputCallback implements ScheduledReco
TvInputInfo input = Utils.getTvInputInfoForInputId(mContext, schedule.getInputId());
if (input == null) {
Log.e(TAG, "Can't find input for " + schedule);
- mDataManager.changeState(schedule, ScheduledRecording.STATE_RECORDING_FAILED);
+ mDataManager.changeState(
+ schedule,
+ ScheduledRecording.STATE_RECORDING_FAILED,
+ ScheduledRecording.FAILED_REASON_INPUT_UNAVAILABLE);
return;
}
if (!input.canRecord() || input.getTunerCount() <= 0) {
Log.e(TAG, "TV input doesn't support recording: " + input);
- mDataManager.changeState(schedule, ScheduledRecording.STATE_RECORDING_FAILED);
+ mDataManager.changeState(
+ schedule,
+ ScheduledRecording.STATE_RECORDING_FAILED,
+ ScheduledRecording.FAILED_REASON_INPUT_DVR_UNSUPPORTED);
return;
}
InputTaskScheduler inputTaskScheduler = mInputSchedulerMap.get(input.getId());
if (inputTaskScheduler == null) {
- inputTaskScheduler = new InputTaskScheduler(mContext, input, mLooper,
- mChannelDataManager, mDvrManager, mDataManager, mSessionManager, mClock);
+ inputTaskScheduler =
+ new InputTaskScheduler(
+ mContext,
+ input,
+ mLooper,
+ mChannelDataManager,
+ mDvrManager,
+ mDataManager,
+ mSessionManager,
+ mClock);
mInputSchedulerMap.put(input.getId(), inputTaskScheduler);
}
inputTaskScheduler.addSchedule(schedule);
@@ -290,8 +315,9 @@ public class RecordingScheduler extends TvInputCallback implements ScheduledReco
}
private void updateNextAlarm() {
- long nextStartTime = mDataManager.getNextScheduledStartTimeAfter(
- Math.max(mLastStartTimePendingMs, mClock.currentTimeMillis()));
+ long nextStartTime =
+ mDataManager.getNextScheduledStartTimeAfter(
+ Math.max(mLastStartTimePendingMs, mClock.currentTimeMillis()));
if (nextStartTime != DvrDataManager.NEXT_START_TIME_NOT_FOUND) {
long wakeAt = nextStartTime - MS_TO_WAKE_BEFORE_START;
if (DEBUG) Log.d(TAG, "Set alarm to record at " + wakeAt);
diff --git a/src/com/android/tv/dvr/recorder/RecordingTask.java b/src/com/android/tv/dvr/recorder/RecordingTask.java
index 14888056..07a29e51 100644
--- a/src/com/android/tv/dvr/recorder/RecordingTask.java
+++ b/src/com/android/tv/dvr/recorder/RecordingTask.java
@@ -26,24 +26,24 @@ import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.support.annotation.WorkerThread;
import android.util.Log;
import android.widget.Toast;
-
import com.android.tv.InputSessionManager;
import com.android.tv.InputSessionManager.RecordingSession;
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
-import com.android.tv.data.Channel;
+import com.android.tv.common.util.Clock;
+import com.android.tv.common.util.CommonUtils;
+import com.android.tv.data.api.Channel;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.WritableDvrDataManager;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.recorder.InputTaskScheduler.HandlerWrapper;
-import com.android.tv.util.Clock;
import com.android.tv.util.Utils;
-
import java.util.Comparator;
import java.util.concurrent.TimeUnit;
@@ -55,58 +55,45 @@ import java.util.concurrent.TimeUnit;
*/
@WorkerThread
@TargetApi(Build.VERSION_CODES.N)
-public class RecordingTask extends RecordingCallback implements Handler.Callback,
- DvrManager.Listener {
+public class RecordingTask extends RecordingCallback
+ implements Handler.Callback, DvrManager.Listener {
private static final String TAG = "RecordingTask";
private static final boolean DEBUG = false;
- /**
- * Compares the end time in ascending order.
- */
- public static final Comparator<RecordingTask> END_TIME_COMPARATOR
- = new Comparator<RecordingTask>() {
- @Override
- public int compare(RecordingTask lhs, RecordingTask rhs) {
- return Long.compare(lhs.getEndTimeMs(), rhs.getEndTimeMs());
- }
- };
-
- /**
- * Compares ID in ascending order.
- */
- public static final Comparator<RecordingTask> ID_COMPARATOR
- = new Comparator<RecordingTask>() {
- @Override
- public int compare(RecordingTask lhs, RecordingTask rhs) {
- return Long.compare(lhs.getScheduleId(), rhs.getScheduleId());
- }
- };
-
- /**
- * Compares the priority in ascending order.
- */
- public static final Comparator<RecordingTask> PRIORITY_COMPARATOR
- = new Comparator<RecordingTask>() {
- @Override
- public int compare(RecordingTask lhs, RecordingTask rhs) {
- return Long.compare(lhs.getPriority(), rhs.getPriority());
- }
- };
+ /** Compares the end time in ascending order. */
+ public static final Comparator<RecordingTask> END_TIME_COMPARATOR =
+ new Comparator<RecordingTask>() {
+ @Override
+ public int compare(RecordingTask lhs, RecordingTask rhs) {
+ return Long.compare(lhs.getEndTimeMs(), rhs.getEndTimeMs());
+ }
+ };
+
+ /** Compares ID in ascending order. */
+ public static final Comparator<RecordingTask> ID_COMPARATOR =
+ new Comparator<RecordingTask>() {
+ @Override
+ public int compare(RecordingTask lhs, RecordingTask rhs) {
+ return Long.compare(lhs.getScheduleId(), rhs.getScheduleId());
+ }
+ };
+
+ /** Compares the priority in ascending order. */
+ public static final Comparator<RecordingTask> PRIORITY_COMPARATOR =
+ new Comparator<RecordingTask>() {
+ @Override
+ public int compare(RecordingTask lhs, RecordingTask rhs) {
+ return Long.compare(lhs.getPriority(), rhs.getPriority());
+ }
+ };
- @VisibleForTesting
- static final int MSG_INITIALIZE = 1;
- @VisibleForTesting
- static final int MSG_START_RECORDING = 2;
- @VisibleForTesting
- static final int MSG_STOP_RECORDING = 3;
- /**
- * Message to update schedule.
- */
+ @VisibleForTesting static final int MSG_INITIALIZE = 1;
+ @VisibleForTesting static final int MSG_START_RECORDING = 2;
+ @VisibleForTesting static final int MSG_STOP_RECORDING = 3;
+ /** Message to update schedule. */
public static final int MSG_UDPATE_SCHEDULE = 4;
- /**
- * The time when the start command will be sent before the recording starts.
- */
+ /** The time when the start command will be sent before the recording starts. */
public static final long RECORDING_EARLY_START_OFFSET_MS = TimeUnit.SECONDS.toMillis(3);
/**
* If the recording starts later than the scheduled start time or ends before the scheduled end
@@ -126,6 +113,7 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback
ERROR,
RELEASED,
}
+
private final InputSessionManager mSessionManager;
private final DvrManager mDvrManager;
private final Context mContext;
@@ -142,9 +130,14 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback
private Uri mRecordedProgramUri;
private boolean mCanceled;
- RecordingTask(Context context, ScheduledRecording scheduledRecording, Channel channel,
- DvrManager dvrManager, InputSessionManager sessionManager,
- WritableDvrDataManager dataManager, Clock clock) {
+ RecordingTask(
+ Context context,
+ ScheduledRecording scheduledRecording,
+ Channel channel,
+ DvrManager dvrManager,
+ InputSessionManager sessionManager,
+ WritableDvrDataManager dataManager,
+ Clock clock) {
mContext = context;
mScheduledRecording = scheduledRecording;
mChannel = channel;
@@ -163,8 +156,10 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback
@Override
public boolean handleMessage(Message msg) {
if (DEBUG) Log.d(TAG, "handleMessage " + msg);
- SoftPreconditions.checkState(msg.what == HandlerWrapper.MESSAGE_REMOVE || mHandler != null,
- TAG, "Null handler trying to handle " + msg);
+ SoftPreconditions.checkState(
+ msg.what == HandlerWrapper.MESSAGE_REMOVE || mHandler != null,
+ TAG,
+ "Null handler trying to handle " + msg);
try {
switch (msg.what) {
case MSG_INITIALIZE:
@@ -185,7 +180,7 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback
release();
return false;
default:
- SoftPreconditions.checkArgument(false, TAG, "unexpected message type " + msg);
+ SoftPreconditions.checkArgument(false, TAG, "unexpected message type %s", msg);
break;
}
return true;
@@ -200,7 +195,7 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback
public void onDisconnected(String inputId) {
if (DEBUG) Log.d(TAG, "onDisconnected(" + inputId + ")");
if (mRecordingSession != null && mState != State.FINISHED) {
- failAndQuit();
+ failAndQuit(ScheduledRecording.FAILED_REASON_NOT_FINISHED);
}
}
@@ -208,7 +203,7 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback
public void onConnectionFailed(String inputId) {
if (DEBUG) Log.d(TAG, "onConnectionFailed(" + inputId + ")");
if (mRecordingSession != null) {
- failAndQuit();
+ failAndQuit(ScheduledRecording.FAILED_REASON_CONNECTION_FAILED);
}
}
@@ -219,23 +214,27 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback
return;
}
mState = State.CONNECTED;
- if (mHandler == null || !sendEmptyMessageAtAbsoluteTime(MSG_START_RECORDING,
- mScheduledRecording.getStartTimeMs() - RECORDING_EARLY_START_OFFSET_MS)) {
- failAndQuit();
+ if (mHandler == null
+ || !sendEmptyMessageAtAbsoluteTime(
+ MSG_START_RECORDING,
+ mScheduledRecording.getStartTimeMs() - RECORDING_EARLY_START_OFFSET_MS)) {
+ failAndQuit(ScheduledRecording.FAILED_REASON_MESSAGE_NOT_SENT);
}
}
@Override
public void onRecordingStopped(Uri recordedProgramUri) {
- if (DEBUG) Log.d(TAG, "onRecordingStopped");
+ Log.i(TAG, "Recording Stopped: " + mScheduledRecording);
+ Log.i(TAG, "Recording Stopped: stored as " + recordedProgramUri);
if (mRecordingSession == null) {
return;
}
mRecordedProgramUri = recordedProgramUri;
mState = State.FINISHED;
int state = ScheduledRecording.STATE_RECORDING_FINISHED;
- if (mStartedWithClipping || mScheduledRecording.getEndTimeMs() - CLIPPED_THRESHOLD_MS
- > mClock.currentTimeMillis()) {
+ if (mStartedWithClipping
+ || mScheduledRecording.getEndTimeMs() - CLIPPED_THRESHOLD_MS
+ > mClock.currentTimeMillis()) {
state = ScheduledRecording.STATE_RECORDING_CLIPPED;
}
updateRecordingState(state);
@@ -247,65 +246,89 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback
@Override
public void onError(int reason) {
- if (DEBUG) Log.d(TAG, "onError reason " + reason);
+ Log.i(TAG, "Recording failed with code=" + reason + " for " + mScheduledRecording);
if (mRecordingSession == null) {
return;
}
+ int error;
switch (reason) {
case TvInputManager.RECORDING_ERROR_INSUFFICIENT_SPACE:
- mMainThreadHandler.post(new Runnable() {
- @Override
- public void run() {
- if (TvApplication.getSingletons(mContext).getMainActivityWrapper()
- .isResumed()) {
- ScheduledRecording scheduledRecording = mDataManager
- .getScheduledRecording(mScheduledRecording.getId());
- if (scheduledRecording != null) {
- Toast.makeText(mContext.getApplicationContext(),
- mContext.getString(R.string
- .dvr_error_insufficient_space_description_one_recording,
- scheduledRecording.getProgramDisplayTitle(mContext)),
- Toast.LENGTH_LONG)
- .show();
+ Log.i(TAG, "Insufficient space to record " + mScheduledRecording);
+ mMainThreadHandler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (TvSingletons.getSingletons(mContext)
+ .getMainActivityWrapper()
+ .isResumed()) {
+ ScheduledRecording scheduledRecording =
+ mDataManager.getScheduledRecording(
+ mScheduledRecording.getId());
+ if (scheduledRecording != null) {
+ Toast.makeText(
+ mContext.getApplicationContext(),
+ mContext.getString(
+ R.string
+ .dvr_error_insufficient_space_description_one_recording,
+ scheduledRecording
+ .getProgramDisplayTitle(
+ mContext)),
+ Toast.LENGTH_LONG)
+ .show();
+ }
+ } else {
+ Utils.setRecordingFailedReason(
+ mContext.getApplicationContext(),
+ TvInputManager.RECORDING_ERROR_INSUFFICIENT_SPACE);
+ Utils.addFailedScheduledRecordingInfo(
+ mContext.getApplicationContext(),
+ mScheduledRecording.getProgramDisplayTitle(mContext));
+ }
}
- } else {
- Utils.setRecordingFailedReason(mContext.getApplicationContext(),
- TvInputManager.RECORDING_ERROR_INSUFFICIENT_SPACE);
- Utils.addFailedScheduledRecordingInfo(mContext.getApplicationContext(),
- mScheduledRecording.getProgramDisplayTitle(mContext));
- }
- }
- });
- // Pass through
+ });
+ error = ScheduledRecording.FAILED_REASON_INSUFFICIENT_SPACE;
+ break;
+ case TvInputManager.RECORDING_ERROR_RESOURCE_BUSY:
+ error = ScheduledRecording.FAILED_REASON_RESOURCE_BUSY;
+ break;
default:
- failAndQuit();
+ error = ScheduledRecording.FAILED_REASON_OTHER;
break;
}
+ failAndQuit(error);
}
private void handleInit() {
if (DEBUG) Log.d(TAG, "handleInit " + mScheduledRecording);
if (mScheduledRecording.getEndTimeMs() < mClock.currentTimeMillis()) {
Log.w(TAG, "End time already past, not recording " + mScheduledRecording);
- failAndQuit();
+ failAndQuit(ScheduledRecording.FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED);
return;
}
if (mChannel == null) {
Log.w(TAG, "Null channel for " + mScheduledRecording);
- failAndQuit();
+ failAndQuit(ScheduledRecording.FAILED_REASON_INVALID_CHANNEL);
return;
}
if (mChannel.getId() != mScheduledRecording.getChannelId()) {
- Log.w(TAG, "Channel" + mChannel + " does not match scheduled recording "
- + mScheduledRecording);
- failAndQuit();
+ Log.w(
+ TAG,
+ "Channel"
+ + mChannel
+ + " does not match scheduled recording "
+ + mScheduledRecording);
+ failAndQuit(ScheduledRecording.FAILED_REASON_INVALID_CHANNEL);
return;
}
String inputId = mChannel.getInputId();
- mRecordingSession = mSessionManager.createRecordingSession(inputId,
- "recordingTask-" + mScheduledRecording.getId(), this,
- mHandler, mScheduledRecording.getEndTimeMs());
+ mRecordingSession =
+ mSessionManager.createRecordingSession(
+ inputId,
+ "recordingTask-" + mScheduledRecording.getId(),
+ this,
+ mHandler,
+ mScheduledRecording.getEndTimeMs());
mState = State.SESSION_ACQUIRED;
mDvrManager.addListener(this, mHandler);
mRecordingSession.tune(inputId, mChannel.getUri());
@@ -313,8 +336,14 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback
}
private void failAndQuit() {
+ failAndQuit(ScheduledRecording.FAILED_REASON_OTHER);
+ }
+
+ private void failAndQuit(Integer reason) {
if (DEBUG) Log.d(TAG, "failAndQuit");
- updateRecordingState(ScheduledRecording.STATE_RECORDING_FAILED);
+ updateRecordingState(
+ ScheduledRecording.STATE_RECORDING_FAILED,
+ reason);
mState = State.ERROR;
sendRemove();
}
@@ -322,16 +351,18 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback
private void sendRemove() {
if (DEBUG) Log.d(TAG, "sendRemove");
if (mHandler != null) {
- mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(
- HandlerWrapper.MESSAGE_REMOVE));
+ mHandler.sendMessageAtFrontOfQueue(
+ mHandler.obtainMessage(HandlerWrapper.MESSAGE_REMOVE));
}
}
private void handleStartRecording() {
- if (DEBUG) Log.d(TAG, "handleStartRecording " + mScheduledRecording);
+ Log.i(TAG, "Start Recording: " + mScheduledRecording);
long programId = mScheduledRecording.getProgramId();
- mRecordingSession.startRecording(programId == ScheduledRecording.ID_NOT_SET ? null
- : TvContract.buildProgramUri(programId));
+ mRecordingSession.startRecording(
+ programId == ScheduledRecording.ID_NOT_SET
+ ? null
+ : TvContract.buildProgramUri(programId));
updateRecordingState(ScheduledRecording.STATE_RECORDING_IN_PROGRESS);
// If it starts late, it's clipped.
if (mScheduledRecording.getStartTimeMs() + CLIPPED_THRESHOLD_MS
@@ -340,14 +371,14 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback
}
mState = State.RECORDING_STARTED;
- if (!sendEmptyMessageAtAbsoluteTime(MSG_STOP_RECORDING,
- mScheduledRecording.getEndTimeMs())) {
- failAndQuit();
+ if (!sendEmptyMessageAtAbsoluteTime(
+ MSG_STOP_RECORDING, mScheduledRecording.getEndTimeMs())) {
+ failAndQuit(ScheduledRecording.FAILED_REASON_MESSAGE_NOT_SENT);
}
}
private void handleStopRecording() {
- if (DEBUG) Log.d(TAG, "handleStopRecording " + mScheduledRecording);
+ Log.i(TAG, "Stop Recording: " + mScheduledRecording);
mRecordingSession.stopRecording();
mState = State.RECORDING_STOP_REQUESTED;
}
@@ -362,7 +393,7 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback
if (mState == State.RECORDING_STARTED) {
mHandler.removeMessages(MSG_STOP_RECORDING);
if (!sendEmptyMessageAtAbsoluteTime(MSG_STOP_RECORDING, schedule.getEndTimeMs())) {
- failAndQuit();
+ failAndQuit(ScheduledRecording.FAILED_REASON_MESSAGE_NOT_SENT);
}
}
}
@@ -377,23 +408,17 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback
return mScheduledRecording.getId();
}
- /**
- * Returns the priority.
- */
+ /** Returns the priority. */
public long getPriority() {
return mScheduledRecording.getPriority();
}
- /**
- * Returns the start time of the recording.
- */
+ /** Returns the start time of the recording. */
public long getStartTimeMs() {
return mScheduledRecording.getStartTimeMs();
}
- /**
- * Returns the end time of the recording.
- */
+ /** Returns the end time of the recording. */
public long getEndTimeMs() {
return mScheduledRecording.getEndTimeMs();
}
@@ -410,33 +435,53 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback
long now = mClock.currentTimeMillis();
long delay = Math.max(0L, when - now);
if (DEBUG) {
- Log.d(TAG, "Sending message " + what + " with a delay of " + delay / 1000
- + " seconds to arrive at " + Utils.toIsoDateTimeString(when));
+ Log.d(
+ TAG,
+ "Sending message "
+ + what
+ + " with a delay of "
+ + delay / 1000
+ + " seconds to arrive at "
+ + CommonUtils.toIsoDateTimeString(when));
}
return mHandler.sendEmptyMessageDelayed(what, delay);
}
private void updateRecordingState(@ScheduledRecording.RecordingState int state) {
- if (DEBUG) Log.d(TAG, "Updating the state of " + mScheduledRecording + " to " + state);
- mScheduledRecording = ScheduledRecording.buildFrom(mScheduledRecording).setState(state)
- .build();
- runOnMainThread(new Runnable() {
- @Override
- public void run() {
- ScheduledRecording schedule = mDataManager.getScheduledRecording(
- mScheduledRecording.getId());
- if (schedule == null) {
- // Schedule has been deleted. Delete the recorded program.
- removeRecordedProgram();
- } else {
- // Update the state based on the object in DataManager in case when it has been
- // updated. mScheduledRecording will be updated from
- // onScheduledRecordingStateChanged.
- mDataManager.updateScheduledRecording(ScheduledRecording.buildFrom(schedule)
- .setState(state).build());
- }
- }
- });
+ updateRecordingState(state, null);
+ }
+ private void updateRecordingState(
+ @ScheduledRecording.RecordingState int state, @Nullable Integer reason) {
+ if (DEBUG) {
+ Log.d(TAG, "Updating the state of " + mScheduledRecording + " to " + state);
+ }
+ mScheduledRecording =
+ ScheduledRecording.buildFrom(mScheduledRecording).setState(state).build();
+ runOnMainThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ ScheduledRecording schedule =
+ mDataManager.getScheduledRecording(mScheduledRecording.getId());
+ if (schedule == null) {
+ // Schedule has been deleted. Delete the recorded program.
+ removeRecordedProgram();
+ } else {
+ // Update the state based on the object in DataManager in case when it
+ // has been updated. mScheduledRecording will be updated from
+ // onScheduledRecordingStateChanged.
+ ScheduledRecording.Builder builder =
+ ScheduledRecording
+ .buildFrom(schedule)
+ .setState(state);
+ if (state == ScheduledRecording.STATE_RECORDING_FAILED
+ && reason != null) {
+ builder.setFailedReason(reason);
+ }
+ mDataManager.updateScheduledRecording(builder.build());
+ }
+ }
+ });
}
@Override
@@ -447,16 +492,12 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback
stop();
}
- /**
- * Starts the task.
- */
+ /** Starts the task. */
public void start() {
mHandler.sendEmptyMessage(MSG_INITIALIZE);
}
- /**
- * Stops the task.
- */
+ /** Stops the task. */
public void stop() {
if (DEBUG) Log.d(TAG, "stop");
switch (mState) {
@@ -480,9 +521,7 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback
}
}
- /**
- * Cancels the task
- */
+ /** Cancels the task */
public void cancel() {
if (DEBUG) Log.d(TAG, "cancel");
mCanceled = true;
@@ -490,12 +529,12 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback
removeRecordedProgram();
}
- /**
- * Clean up the task.
- */
+ /** Clean up the task. */
public void cleanUp() {
if (mState == State.RECORDING_STARTED || mState == State.RECORDING_STOP_REQUESTED) {
- updateRecordingState(ScheduledRecording.STATE_RECORDING_FAILED);
+ updateRecordingState(
+ ScheduledRecording.STATE_RECORDING_FAILED,
+ ScheduledRecording.FAILED_REASON_SCHEDULER_STOPPED);
}
release();
if (mHandler != null) {
@@ -509,14 +548,15 @@ public class RecordingTask extends RecordingCallback implements Handler.Callback
}
private void removeRecordedProgram() {
- runOnMainThread(new Runnable() {
- @Override
- public void run() {
- if (mRecordedProgramUri != null) {
- mDvrManager.removeRecordedProgram(mRecordedProgramUri);
- }
- }
- });
+ runOnMainThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mRecordedProgramUri != null) {
+ mDvrManager.removeRecordedProgram(mRecordedProgramUri);
+ }
+ }
+ });
}
private void runOnMainThread(Runnable runnable) {
diff --git a/src/com/android/tv/dvr/recorder/ScheduledProgramReaper.java b/src/com/android/tv/dvr/recorder/ScheduledProgramReaper.java
index d958c4a1..dd106e1c 100644
--- a/src/com/android/tv/dvr/recorder/ScheduledProgramReaper.java
+++ b/src/com/android/tv/dvr/recorder/ScheduledProgramReaper.java
@@ -17,24 +17,18 @@
package com.android.tv.dvr.recorder;
import android.support.annotation.MainThread;
-import android.support.annotation.VisibleForTesting;
-
+import com.android.tv.common.util.Clock;
import com.android.tv.dvr.WritableDvrDataManager;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.data.SeriesRecording;
-import com.android.tv.util.Clock;
-
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
-/**
- * Deletes {@link ScheduledRecording} older than {@value @DAYS} days.
- */
-class ScheduledProgramReaper implements Runnable {
+/** Deletes {@link ScheduledRecording} older than {@value @DAYS} days. */
+public class ScheduledProgramReaper implements Runnable {
- @VisibleForTesting
- static final int DAYS = 2;
+ public static final int DAYS = 7;
private final WritableDvrDataManager mDvrDataManager;
private final Clock mClock;
@@ -54,7 +48,7 @@ class ScheduledProgramReaper implements Runnable {
// series recording.
if (r.getEndTimeMs() < cutoff
&& (r.getSeriesRecordingId() == SeriesRecording.ID_NOT_SET
- || r.getState() != ScheduledRecording.STATE_RECORDING_FINISHED)) {
+ || r.getState() != ScheduledRecording.STATE_RECORDING_FINISHED)) {
toRemove.add(r);
}
}
diff --git a/src/com/android/tv/dvr/recorder/SeriesRecordingScheduler.java b/src/com/android/tv/dvr/recorder/SeriesRecordingScheduler.java
index 15508c24..4f7a789b 100644
--- a/src/com/android/tv/dvr/recorder/SeriesRecordingScheduler.java
+++ b/src/com/android/tv/dvr/recorder/SeriesRecordingScheduler.java
@@ -27,27 +27,23 @@ import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import android.util.LongSparseArray;
-
-import com.android.tv.ApplicationSingletons;
-import com.android.tv.TvApplication;
-import com.android.tv.common.CollectionUtils;
-import com.android.tv.common.SharedPreferencesUtils;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
+import com.android.tv.common.experiments.Experiments;
+import com.android.tv.common.util.CollectionUtils;
+import com.android.tv.common.util.SharedPreferencesUtils;
import com.android.tv.data.Program;
-import com.android.tv.data.epg.EpgFetcher;
+import com.android.tv.data.epg.EpgReader;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrDataManager.ScheduledRecordingListener;
import com.android.tv.dvr.DvrDataManager.SeriesRecordingListener;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.WritableDvrDataManager;
-import com.android.tv.dvr.data.SeasonEpisodeNumber;
import com.android.tv.dvr.data.ScheduledRecording;
+import com.android.tv.dvr.data.SeasonEpisodeNumber;
import com.android.tv.dvr.data.SeriesInfo;
import com.android.tv.dvr.data.SeriesRecording;
import com.android.tv.dvr.provider.EpisodicProgramLoadTask;
-import com.android.tv.experiments.Experiments;
-
-import com.android.tv.util.LocationUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -60,12 +56,14 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+import javax.inject.Provider;
/**
- * Creates the {@link com.android.tv.dvr.data.ScheduledRecording}s for
- * the {@link com.android.tv.dvr.data.SeriesRecording}.
- * <p>
- * The current implementation assumes that the series recordings are scheduled only for one channel.
+ * Creates the {@link com.android.tv.dvr.data.ScheduledRecording}s for the {@link
+ * com.android.tv.dvr.data.SeriesRecording}.
+ *
+ * <p>The current implementation assumes that the series recordings are scheduled only for one
+ * channel.
*/
@TargetApi(Build.VERSION_CODES.N)
public class SeriesRecordingScheduler {
@@ -78,9 +76,7 @@ public class SeriesRecordingScheduler {
@SuppressLint("StaticFieldLeak")
private static SeriesRecordingScheduler sInstance;
- /**
- * Creates and returns the {@link SeriesRecordingScheduler}.
- */
+ /** Creates and returns the {@link SeriesRecordingScheduler}. */
public static synchronized SeriesRecordingScheduler getInstance(Context context) {
if (sInstance == null) {
sInstance = new SeriesRecordingScheduler(context);
@@ -100,54 +96,59 @@ public class SeriesRecordingScheduler {
private boolean mPaused;
private final Set<Long> mPendingSeriesRecordings = new ArraySet<>();
- private final SeriesRecordingListener mSeriesRecordingListener = new SeriesRecordingListener() {
- @Override
- public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) {
- for (SeriesRecording seriesRecording : seriesRecordings) {
- executeFetchSeriesInfoTask(seriesRecording);
- }
- }
-
- @Override
- public void onSeriesRecordingRemoved(SeriesRecording... seriesRecordings) {
- // Cancel the update.
- for (Iterator<SeriesRecordingUpdateTask> iter = mScheduleTasks.iterator();
- iter.hasNext(); ) {
- SeriesRecordingUpdateTask task = iter.next();
- if (CollectionUtils.subtract(task.getSeriesRecordings(), seriesRecordings,
- SeriesRecording.ID_COMPARATOR).isEmpty()) {
- task.cancel(true);
- iter.remove();
+ private final SeriesRecordingListener mSeriesRecordingListener =
+ new SeriesRecordingListener() {
+ @Override
+ public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) {
+ for (SeriesRecording seriesRecording : seriesRecordings) {
+ executeFetchSeriesInfoTask(seriesRecording);
+ }
}
- }
- for (SeriesRecording seriesRecording : seriesRecordings) {
- FetchSeriesInfoTask task = mFetchSeriesInfoTasks.get(seriesRecording.getId());
- if (task != null) {
- task.cancel(true);
- mFetchSeriesInfoTasks.remove(seriesRecording.getId());
+
+ @Override
+ public void onSeriesRecordingRemoved(SeriesRecording... seriesRecordings) {
+ // Cancel the update.
+ for (Iterator<SeriesRecordingUpdateTask> iter = mScheduleTasks.iterator();
+ iter.hasNext(); ) {
+ SeriesRecordingUpdateTask task = iter.next();
+ if (CollectionUtils.subtract(
+ task.getSeriesRecordings(),
+ seriesRecordings,
+ SeriesRecording.ID_COMPARATOR)
+ .isEmpty()) {
+ task.cancel(true);
+ iter.remove();
+ }
+ }
+ for (SeriesRecording seriesRecording : seriesRecordings) {
+ FetchSeriesInfoTask task =
+ mFetchSeriesInfoTasks.get(seriesRecording.getId());
+ if (task != null) {
+ task.cancel(true);
+ mFetchSeriesInfoTasks.remove(seriesRecording.getId());
+ }
+ }
}
- }
- }
- @Override
- public void onSeriesRecordingChanged(SeriesRecording... seriesRecordings) {
- List<SeriesRecording> stopped = new ArrayList<>();
- List<SeriesRecording> normal = new ArrayList<>();
- for (SeriesRecording r : seriesRecordings) {
- if (r.isStopped()) {
- stopped.add(r);
- } else {
- normal.add(r);
+ @Override
+ public void onSeriesRecordingChanged(SeriesRecording... seriesRecordings) {
+ List<SeriesRecording> stopped = new ArrayList<>();
+ List<SeriesRecording> normal = new ArrayList<>();
+ for (SeriesRecording r : seriesRecordings) {
+ if (r.isStopped()) {
+ stopped.add(r);
+ } else {
+ normal.add(r);
+ }
+ }
+ if (!stopped.isEmpty()) {
+ onSeriesRecordingRemoved(SeriesRecording.toArray(stopped));
+ }
+ if (!normal.isEmpty()) {
+ updateSchedules(normal);
+ }
}
- }
- if (!stopped.isEmpty()) {
- onSeriesRecordingRemoved(SeriesRecording.toArray(stopped));
- }
- if (!normal.isEmpty()) {
- updateSchedules(normal);
- }
- }
- };
+ };
private final ScheduledRecordingListener mScheduledRecordingListener =
new ScheduledRecordingListener() {
@@ -166,7 +167,8 @@ public class SeriesRecordingScheduler {
List<ScheduledRecording> schedulesForUpdate = new ArrayList<>();
for (ScheduledRecording r : schedules) {
if ((r.getState() == ScheduledRecording.STATE_RECORDING_FAILED
- || r.getState() == ScheduledRecording.STATE_RECORDING_CLIPPED)
+ || r.getState()
+ == ScheduledRecording.STATE_RECORDING_CLIPPED)
&& r.getSeriesRecordingId() != SeriesRecording.ID_NOT_SET
&& !TextUtils.isEmpty(r.getSeasonNumber())
&& !TextUtils.isEmpty(r.getEpisodeNumber())) {
@@ -205,18 +207,17 @@ public class SeriesRecordingScheduler {
private SeriesRecordingScheduler(Context context) {
mContext = context.getApplicationContext();
- ApplicationSingletons appSingletons = TvApplication.getSingletons(context);
- mDvrManager = appSingletons.getDvrManager();
- mDataManager = (WritableDvrDataManager) appSingletons.getDvrDataManager();
- mSharedPreferences = context.getSharedPreferences(
- SharedPreferencesUtils.SHARED_PREF_SERIES_RECORDINGS, Context.MODE_PRIVATE);
- mFetchedSeriesIds.addAll(mSharedPreferences.getStringSet(KEY_FETCHED_SERIES_IDS,
- Collections.emptySet()));
+ TvSingletons tvSingletons = TvSingletons.getSingletons(context);
+ mDvrManager = tvSingletons.getDvrManager();
+ mDataManager = (WritableDvrDataManager) tvSingletons.getDvrDataManager();
+ mSharedPreferences =
+ context.getSharedPreferences(
+ SharedPreferencesUtils.SHARED_PREF_SERIES_RECORDINGS, Context.MODE_PRIVATE);
+ mFetchedSeriesIds.addAll(
+ mSharedPreferences.getStringSet(KEY_FETCHED_SERIES_IDS, Collections.emptySet()));
}
- /**
- * Starts the scheduler.
- */
+ /** Starts the scheduler. */
@MainThread
public void start() {
SoftPreconditions.checkState(mDataManager.isInitialized());
@@ -261,15 +262,16 @@ public class SeriesRecordingScheduler {
private void executeFetchSeriesInfoTask(SeriesRecording seriesRecording) {
if (Experiments.CLOUD_EPG.get()) {
- FetchSeriesInfoTask task = new FetchSeriesInfoTask(seriesRecording);
+ FetchSeriesInfoTask task =
+ new FetchSeriesInfoTask(
+ seriesRecording,
+ TvSingletons.getSingletons(mContext).providesEpgReader());
task.execute();
mFetchSeriesInfoTasks.put(seriesRecording.getId(), task);
}
}
- /**
- * Pauses the updates of the series recordings.
- */
+ /** Pauses the updates of the series recordings. */
public void pauseUpdate() {
if (DEBUG) Log.d(TAG, "Schedule paused");
if (mPaused) {
@@ -287,9 +289,7 @@ public class SeriesRecordingScheduler {
}
}
- /**
- * Resumes the updates of the series recordings.
- */
+ /** Resumes the updates of the series recordings. */
public void resumeUpdate() {
if (DEBUG) Log.d(TAG, "Schedule resumed");
if (!mPaused) {
@@ -329,25 +329,28 @@ public class SeriesRecordingScheduler {
mPendingSeriesRecordings.add(r.getId());
}
if (DEBUG) {
- Log.d(TAG, "The scheduler has been paused. Adding to the pending list. size="
- + mPendingSeriesRecordings.size());
+ Log.d(
+ TAG,
+ "The scheduler has been paused. Adding to the pending list. size="
+ + mPendingSeriesRecordings.size());
}
return;
}
Set<SeriesRecording> previousSeriesRecordings = new HashSet<>();
for (Iterator<SeriesRecordingUpdateTask> iter = mScheduleTasks.iterator();
- iter.hasNext(); ) {
+ iter.hasNext(); ) {
SeriesRecordingUpdateTask task = iter.next();
- if (CollectionUtils.containsAny(task.getSeriesRecordings(), seriesRecordings,
- SeriesRecording.ID_COMPARATOR)) {
+ if (CollectionUtils.containsAny(
+ task.getSeriesRecordings(), seriesRecordings, SeriesRecording.ID_COMPARATOR)) {
// The task is affected by the seriesRecordings
task.cancel(true);
previousSeriesRecordings.addAll(task.getSeriesRecordings());
iter.remove();
}
}
- List<SeriesRecording> seriesRecordingsToUpdate = CollectionUtils.union(seriesRecordings,
- previousSeriesRecordings, SeriesRecording.ID_COMPARATOR);
+ List<SeriesRecording> seriesRecordingsToUpdate =
+ CollectionUtils.union(
+ seriesRecordings, previousSeriesRecordings, SeriesRecording.ID_COMPARATOR);
for (Iterator<SeriesRecording> iter = seriesRecordingsToUpdate.iterator();
iter.hasNext(); ) {
SeriesRecording seriesRecording = mDataManager.getSeriesRecording(iter.next().getId());
@@ -367,8 +370,8 @@ public class SeriesRecordingScheduler {
task.execute();
} else {
for (SeriesRecording seriesRecording : seriesRecordingsToUpdate) {
- SeriesRecordingUpdateTask task = new SeriesRecordingUpdateTask(
- Collections.singletonList(seriesRecording));
+ SeriesRecordingUpdateTask task =
+ new SeriesRecordingUpdateTask(Collections.singletonList(seriesRecording));
mScheduleTasks.add(task);
if (DEBUG) Log.d(TAG, "Added schedule task: " + task);
task.execute();
@@ -389,8 +392,9 @@ public class SeriesRecordingScheduler {
* Pick one program per an episode.
*
* <p>Note that the programs which has been already scheduled have the highest priority, and all
- * of them are added even though they are the same episodes. That's because the schedules
- * should be added to the series recording.
+ * of them are added even though they are the same episodes. That's because the schedules should
+ * be added to the series recording.
+ *
* <p>If there are no existing schedules for an episode, one program which starts earlier is
* picked.
*/
@@ -399,11 +403,10 @@ public class SeriesRecordingScheduler {
return pickOneProgramPerEpisode(mDataManager, seriesRecordings, programs);
}
- /**
- * @see #pickOneProgramPerEpisode(List, List)
- */
+ /** @see #pickOneProgramPerEpisode(List, List) */
public static LongSparseArray<List<Program>> pickOneProgramPerEpisode(
- DvrDataManager dataManager, List<SeriesRecording> seriesRecordings,
+ DvrDataManager dataManager,
+ List<SeriesRecording> seriesRecordings,
List<Program> programs) {
// Initialize.
LongSparseArray<List<Program>> result = new LongSparseArray<>();
@@ -422,8 +425,11 @@ public class SeriesRecordingScheduler {
result.get(seriesRecordingId).add(program);
continue;
}
- SeasonEpisodeNumber seasonEpisodeNumber = new SeasonEpisodeNumber(seriesRecordingId,
- program.getSeasonNumber(), program.getEpisodeNumber());
+ SeasonEpisodeNumber seasonEpisodeNumber =
+ new SeasonEpisodeNumber(
+ seriesRecordingId,
+ program.getSeasonNumber(),
+ program.getEpisodeNumber());
List<Program> programsForEpisode = programsForEpisodeMap.get(seasonEpisodeNumber);
if (programsForEpisode == null) {
programsForEpisode = new ArrayList<>();
@@ -434,22 +440,24 @@ public class SeriesRecordingScheduler {
// Pick one program.
for (Entry<SeasonEpisodeNumber, List<Program>> entry : programsForEpisodeMap.entrySet()) {
List<Program> programsForEpisode = entry.getValue();
- Collections.sort(programsForEpisode, new Comparator<Program>() {
- @Override
- public int compare(Program lhs, Program rhs) {
- // Place the existing schedule first.
- boolean lhsScheduled = isProgramScheduled(dataManager, lhs);
- boolean rhsScheduled = isProgramScheduled(dataManager, rhs);
- if (lhsScheduled && !rhsScheduled) {
- return -1;
- }
- if (!lhsScheduled && rhsScheduled) {
- return 1;
- }
- // Sort by the start time in ascending order.
- return lhs.compareTo(rhs);
- }
- });
+ Collections.sort(
+ programsForEpisode,
+ new Comparator<Program>() {
+ @Override
+ public int compare(Program lhs, Program rhs) {
+ // Place the existing schedule first.
+ boolean lhsScheduled = isProgramScheduled(dataManager, lhs);
+ boolean rhsScheduled = isProgramScheduled(dataManager, rhs);
+ if (lhsScheduled && !rhsScheduled) {
+ return -1;
+ }
+ if (!lhsScheduled && rhsScheduled) {
+ return 1;
+ }
+ // Sort by the start time in ascending order.
+ return lhs.compareTo(rhs);
+ }
+ });
boolean added = false;
// Add all the scheduled programs
List<Program> programsForSeries = result.get(entry.getKey().seriesRecordingId);
@@ -469,8 +477,8 @@ public class SeriesRecordingScheduler {
private static boolean isProgramScheduled(DvrDataManager dataManager, Program program) {
ScheduledRecording schedule =
dataManager.getScheduledRecordingForProgramId(program.getId());
- return schedule != null && schedule.getState()
- == ScheduledRecording.STATE_RECORDING_NOT_STARTED;
+ return schedule != null
+ && schedule.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED;
}
private void updateFetchedSeries() {
@@ -478,8 +486,8 @@ public class SeriesRecordingScheduler {
}
/**
- * This works only for the existing series recordings. Do not use this task for the
- * "adding series recording" UI.
+ * This works only for the existing series recordings. Do not use this task for the "adding
+ * series recording" UI.
*/
private class SeriesRecordingUpdateTask extends EpisodicProgramLoadTask {
SeriesRecordingUpdateTask(List<SeriesRecording> seriesRecordings) {
@@ -491,16 +499,17 @@ public class SeriesRecordingScheduler {
if (DEBUG) Log.d(TAG, "onPostExecute: updating schedules with programs:" + programs);
mScheduleTasks.remove(this);
if (programs == null) {
- Log.e(TAG, "Creating schedules for series recording failed: "
- + getSeriesRecordings());
+ Log.e(
+ TAG,
+ "Creating schedules for series recording failed: " + getSeriesRecordings());
return;
}
- LongSparseArray<List<Program>> seriesProgramMap = pickOneProgramPerEpisode(
- getSeriesRecordings(), programs);
+ LongSparseArray<List<Program>> seriesProgramMap =
+ pickOneProgramPerEpisode(getSeriesRecordings(), programs);
for (SeriesRecording seriesRecording : getSeriesRecordings()) {
// Check the series recording is still valid.
- SeriesRecording actualSeriesRecording = mDataManager.getSeriesRecording(
- seriesRecording.getId());
+ SeriesRecording actualSeriesRecording =
+ mDataManager.getSeriesRecording(seriesRecording.getId());
if (actualSeriesRecording == null || actualSeriesRecording.isStopped()) {
continue;
}
@@ -520,35 +529,39 @@ public class SeriesRecordingScheduler {
@Override
public String toString() {
return "SeriesRecordingUpdateTask:{"
- + "series_recordings=" + getSeriesRecordings()
+ + "series_recordings="
+ + getSeriesRecordings()
+ "}";
}
}
private class FetchSeriesInfoTask extends AsyncTask<Void, Void, SeriesInfo> {
- private SeriesRecording mSeriesRecording;
+ private final SeriesRecording mSeriesRecording;
+ private final Provider<EpgReader> mEpgReaderProvider;
- FetchSeriesInfoTask(SeriesRecording seriesRecording) {
+ FetchSeriesInfoTask(
+ SeriesRecording seriesRecording, Provider<EpgReader> epgReaderProvider) {
mSeriesRecording = seriesRecording;
+ mEpgReaderProvider = epgReaderProvider;
}
@Override
protected SeriesInfo doInBackground(Void... voids) {
- return EpgFetcher.createEpgReader(mContext, LocationUtils.getCurrentCountry(mContext))
- .getSeriesInfo(mSeriesRecording.getSeriesId());
+ return mEpgReaderProvider.get().getSeriesInfo(mSeriesRecording.getSeriesId());
}
@Override
protected void onPostExecute(SeriesInfo seriesInfo) {
if (seriesInfo != null) {
- mDataManager.updateSeriesRecording(SeriesRecording.buildFrom(mSeriesRecording)
- .setTitle(seriesInfo.getTitle())
- .setDescription(seriesInfo.getDescription())
- .setLongDescription(seriesInfo.getLongDescription())
- .setCanonicalGenreIds(seriesInfo.getCanonicalGenreIds())
- .setPosterUri(seriesInfo.getPosterUri())
- .setPhotoUri(seriesInfo.getPhotoUri())
- .build());
+ mDataManager.updateSeriesRecording(
+ SeriesRecording.buildFrom(mSeriesRecording)
+ .setTitle(seriesInfo.getTitle())
+ .setDescription(seriesInfo.getDescription())
+ .setLongDescription(seriesInfo.getLongDescription())
+ .setCanonicalGenreIds(seriesInfo.getCanonicalGenreIds())
+ .setPosterUri(seriesInfo.getPosterUri())
+ .setPhotoUri(seriesInfo.getPhotoUri())
+ .build());
mFetchedSeriesIds.add(seriesInfo.getId());
updateFetchedSeries();
}
diff --git a/src/com/android/tv/dvr/ui/BigArguments.java b/src/com/android/tv/dvr/ui/BigArguments.java
index ec3b5065..0d6ff8b1 100644
--- a/src/com/android/tv/dvr/ui/BigArguments.java
+++ b/src/com/android/tv/dvr/ui/BigArguments.java
@@ -17,37 +17,27 @@
package com.android.tv.dvr.ui;
import android.support.annotation.NonNull;
-
import com.android.tv.common.SoftPreconditions;
-
import java.util.HashMap;
import java.util.Map;
-/**
- * Stores the object to pass through activities/fragments.
- */
+/** Stores the object to pass through activities/fragments. */
public class BigArguments {
- private final static String TAG = "BigArguments";
+ private static final String TAG = "BigArguments";
private static Map<String, Object> sBigArgumentMap = new HashMap<>();
- /**
- * Sets the argument.
- */
+ /** Sets the argument. */
public static void setArgument(String name, @NonNull Object value) {
SoftPreconditions.checkState(value != null, TAG, "Set argument, but value is null");
sBigArgumentMap.put(name, value);
}
- /**
- * Returns the argument which is associated to the name.
- */
+ /** Returns the argument which is associated to the name. */
public static Object getArgument(String name) {
return sBigArgumentMap.get(name);
}
- /**
- * Resets the arguments.
- */
+ /** Resets the arguments. */
public static void reset() {
sBigArgumentMap.clear();
}
diff --git a/src/com/android/tv/dvr/ui/ChangeImageTransformWithScaledParent.java b/src/com/android/tv/dvr/ui/ChangeImageTransformWithScaledParent.java
index cddece73..32679421 100644
--- a/src/com/android/tv/dvr/ui/ChangeImageTransformWithScaledParent.java
+++ b/src/com/android/tv/dvr/ui/ChangeImageTransformWithScaledParent.java
@@ -26,16 +26,14 @@ import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
-
import com.android.tv.R;
-
import java.util.Map;
/**
- * TODO: Remove this class once b/32405620 is fixed.
- * This class is for the workaround of b/32405620 and only for the shared element transition between
- * {@link com.android.tv.dvr.ui.browse.RecordingCardView} and
- * {@link com.android.tv.dvr.ui.browse.DvrDetailsActivity}.
+ * TODO: Remove this class once b/32405620 is fixed. This class is for the workaround of b/32405620
+ * and only for the shared element transition between {@link
+ * com.android.tv.dvr.ui.browse.RecordingCardView} and {@link
+ * com.android.tv.dvr.ui.browse.DvrDetailsActivity}.
*/
public class ChangeImageTransformWithScaledParent extends ChangeImageTransform {
private static final String PROPNAME_MATRIX = "android:changeImageTransform:matrix";
@@ -60,7 +58,8 @@ public class ChangeImageTransformWithScaledParent extends ChangeImageTransform {
View view = transitionValues.view;
Map<String, Object> values = transitionValues.values;
Matrix matrix = (Matrix) values.get(PROPNAME_MATRIX);
- if (matrix != null && view.getId() == R.id.details_overview_image
+ if (matrix != null
+ && view.getId() == R.id.details_overview_image
&& view instanceof ImageView) {
ImageView imageView = (ImageView) view;
if (imageView.getScaleType() == ScaleType.CENTER_INSIDE
@@ -68,10 +67,13 @@ public class ChangeImageTransformWithScaledParent extends ChangeImageTransform {
Bitmap bitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
if (bitmap.getWidth() < imageView.getWidth()
&& bitmap.getHeight() < imageView.getHeight()) {
- float scale = imageView.getContext().getResources().getFraction(
- R.fraction.lb_focus_zoom_factor_medium, 1, 1);
- matrix.postScale(scale, scale, imageView.getWidth() / 2,
- imageView.getHeight() / 2);
+ float scale =
+ imageView
+ .getContext()
+ .getResources()
+ .getFraction(R.fraction.lb_focus_zoom_factor_medium, 1, 1);
+ matrix.postScale(
+ scale, scale, imageView.getWidth() / 2, imageView.getHeight() / 2);
}
}
}
diff --git a/src/com/android/tv/dvr/ui/DvrAlreadyRecordedFragment.java b/src/com/android/tv/dvr/ui/DvrAlreadyRecordedFragment.java
index 62327870..fce94230 100644
--- a/src/com/android/tv/dvr/ui/DvrAlreadyRecordedFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrAlreadyRecordedFragment.java
@@ -24,13 +24,11 @@ import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
import android.support.v17.leanback.widget.GuidedAction;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.data.Program;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.data.RecordedProgram;
-
import java.util.List;
/**
@@ -51,13 +49,19 @@ public class DvrAlreadyRecordedFragment extends DvrGuidedStepFragment {
public void onAttach(Context context) {
super.onAttach(context);
mProgram = getArguments().getParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM);
- DvrManager dvrManager = TvApplication.getSingletons(context).getDvrManager();
- mDuplicate = dvrManager.getRecordedProgram(mProgram.getTitle(),
- mProgram.getSeasonNumber(), mProgram.getEpisodeNumber());
+ DvrManager dvrManager = TvSingletons.getSingletons(context).getDvrManager();
+ mDuplicate =
+ dvrManager.getRecordedProgram(
+ mProgram.getTitle(),
+ mProgram.getSeasonNumber(),
+ mProgram.getEpisodeNumber());
if (mDuplicate == null) {
dvrManager.addSchedule(mProgram);
- DvrUiHelper.showAddScheduleToast(context, mProgram.getTitle(),
- mProgram.getStartTimeUtcMillis(), mProgram.getEndTimeUtcMillis());
+ DvrUiHelper.showAddScheduleToast(
+ context,
+ mProgram.getTitle(),
+ mProgram.getStartTimeUtcMillis(),
+ mProgram.getEndTimeUtcMillis());
dismissDialog();
}
}
@@ -74,18 +78,21 @@ public class DvrAlreadyRecordedFragment extends DvrGuidedStepFragment {
@Override
public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
Context context = getContext();
- actions.add(new GuidedAction.Builder(context)
- .id(ACTION_RECORD_ANYWAY)
- .title(R.string.dvr_action_record_anyway)
- .build());
- actions.add(new GuidedAction.Builder(context)
- .id(ACTION_WATCH)
- .title(R.string.dvr_action_watch_now)
- .build());
- actions.add(new GuidedAction.Builder(context)
- .id(ACTION_CANCEL)
- .title(R.string.dvr_action_record_cancel)
- .build());
+ actions.add(
+ new GuidedAction.Builder(context)
+ .id(ACTION_RECORD_ANYWAY)
+ .title(R.string.dvr_action_record_anyway)
+ .build());
+ actions.add(
+ new GuidedAction.Builder(context)
+ .id(ACTION_WATCH)
+ .title(R.string.dvr_action_watch_now)
+ .build());
+ actions.add(
+ new GuidedAction.Builder(context)
+ .id(ACTION_CANCEL)
+ .title(R.string.dvr_action_record_cancel)
+ .build());
}
@Override
diff --git a/src/com/android/tv/dvr/ui/DvrAlreadyScheduledFragment.java b/src/com/android/tv/dvr/ui/DvrAlreadyScheduledFragment.java
index 6da75e55..456ad830 100644
--- a/src/com/android/tv/dvr/ui/DvrAlreadyScheduledFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrAlreadyScheduledFragment.java
@@ -25,13 +25,11 @@ import android.support.annotation.NonNull;
import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
import android.support.v17.leanback.widget.GuidedAction;
import android.text.format.DateUtils;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.data.Program;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.data.ScheduledRecording;
-
import java.util.List;
/**
@@ -52,13 +50,19 @@ public class DvrAlreadyScheduledFragment extends DvrGuidedStepFragment {
public void onAttach(Context context) {
super.onAttach(context);
mProgram = getArguments().getParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM);
- DvrManager dvrManager = TvApplication.getSingletons(context).getDvrManager();
- mDuplicate = dvrManager.getScheduledRecording(mProgram.getTitle(),
- mProgram.getSeasonNumber(), mProgram.getEpisodeNumber());
+ DvrManager dvrManager = TvSingletons.getSingletons(context).getDvrManager();
+ mDuplicate =
+ dvrManager.getScheduledRecording(
+ mProgram.getTitle(),
+ mProgram.getSeasonNumber(),
+ mProgram.getEpisodeNumber());
if (mDuplicate == null) {
dvrManager.addSchedule(mProgram);
- DvrUiHelper.showAddScheduleToast(context, mProgram.getTitle(),
- mProgram.getStartTimeUtcMillis(), mProgram.getEndTimeUtcMillis());
+ DvrUiHelper.showAddScheduleToast(
+ context,
+ mProgram.getTitle(),
+ mProgram.getStartTimeUtcMillis(),
+ mProgram.getEndTimeUtcMillis());
dismissDialog();
}
}
@@ -67,9 +71,13 @@ public class DvrAlreadyScheduledFragment extends DvrGuidedStepFragment {
@Override
public Guidance onCreateGuidance(Bundle savedInstanceState) {
String title = getString(R.string.dvr_already_scheduled_dialog_title);
- String description = getString(R.string.dvr_already_scheduled_dialog_description,
- DateUtils.formatDateTime(getContext(), mDuplicate.getStartTimeMs(),
- DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE));
+ String description =
+ getString(
+ R.string.dvr_already_scheduled_dialog_description,
+ DateUtils.formatDateTime(
+ getContext(),
+ mDuplicate.getStartTimeMs(),
+ DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE));
Drawable image = getResources().getDrawable(R.drawable.ic_warning_white_96dp, null);
return new Guidance(title, description, null, image);
}
@@ -77,18 +85,21 @@ public class DvrAlreadyScheduledFragment extends DvrGuidedStepFragment {
@Override
public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
Context context = getContext();
- actions.add(new GuidedAction.Builder(context)
- .id(ACTION_RECORD_ANYWAY)
- .title(R.string.dvr_action_record_anyway)
- .build());
- actions.add(new GuidedAction.Builder(context)
- .id(ACTION_RECORD_INSTEAD)
- .title(R.string.dvr_action_record_instead)
- .build());
- actions.add(new GuidedAction.Builder(context)
- .id(ACTION_CANCEL)
- .title(R.string.dvr_action_record_cancel)
- .build());
+ actions.add(
+ new GuidedAction.Builder(context)
+ .id(ACTION_RECORD_ANYWAY)
+ .title(R.string.dvr_action_record_anyway)
+ .build());
+ actions.add(
+ new GuidedAction.Builder(context)
+ .id(ACTION_RECORD_INSTEAD)
+ .title(R.string.dvr_action_record_instead)
+ .build());
+ actions.add(
+ new GuidedAction.Builder(context)
+ .id(ACTION_CANCEL)
+ .title(R.string.dvr_action_record_cancel)
+ .build());
}
@Override
diff --git a/src/com/android/tv/dvr/ui/DvrChannelRecordDurationOptionFragment.java b/src/com/android/tv/dvr/ui/DvrChannelRecordDurationOptionFragment.java
index 36659412..6be35cb2 100644
--- a/src/com/android/tv/dvr/ui/DvrChannelRecordDurationOptionFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrChannelRecordDurationOptionFragment.java
@@ -21,15 +21,13 @@ 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.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
-import com.android.tv.data.Channel;
+import com.android.tv.data.api.Channel;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.ui.DvrConflictFragment.DvrChannelRecordConflictFragment;
-
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -43,8 +41,10 @@ public class DvrChannelRecordDurationOptionFragment extends DvrGuidedStepFragmen
Bundle args = getArguments();
if (args != null) {
long channelId = args.getLong(DvrHalfSizedDialogFragment.KEY_CHANNEL_ID);
- mChannel = TvApplication.getSingletons(getContext()).getChannelDataManager()
- .getChannel(channelId);
+ mChannel =
+ TvSingletons.getSingletons(getContext())
+ .getChannelDataManager()
+ .getChannel(channelId);
}
SoftPreconditions.checkArgument(mChannel != null);
super.onCreate(savedInstanceState);
@@ -66,32 +66,36 @@ public class DvrChannelRecordDurationOptionFragment extends DvrGuidedStepFragmen
mDurations.add(TimeUnit.HOURS.toMillis(1));
mDurations.add(TimeUnit.HOURS.toMillis(3));
- actions.add(new GuidedAction.Builder(getContext())
- .id(++actionId)
- .title(R.string.recording_start_dialog_10_min_duration)
- .build());
- actions.add(new GuidedAction.Builder(getContext())
- .id(++actionId)
- .title(R.string.recording_start_dialog_30_min_duration)
- .build());
- actions.add(new GuidedAction.Builder(getContext())
- .id(++actionId)
- .title(R.string.recording_start_dialog_1_hour_duration)
- .build());
- actions.add(new GuidedAction.Builder(getContext())
- .id(++actionId)
- .title(R.string.recording_start_dialog_3_hours_duration)
- .build());
+ actions.add(
+ new GuidedAction.Builder(getContext())
+ .id(++actionId)
+ .title(R.string.recording_start_dialog_10_min_duration)
+ .build());
+ actions.add(
+ new GuidedAction.Builder(getContext())
+ .id(++actionId)
+ .title(R.string.recording_start_dialog_30_min_duration)
+ .build());
+ actions.add(
+ new GuidedAction.Builder(getContext())
+ .id(++actionId)
+ .title(R.string.recording_start_dialog_1_hour_duration)
+ .build());
+ actions.add(
+ new GuidedAction.Builder(getContext())
+ .id(++actionId)
+ .title(R.string.recording_start_dialog_3_hours_duration)
+ .build());
}
@Override
public void onTrackedGuidedActionClicked(GuidedAction action) {
- DvrManager dvrManager = TvApplication.getSingletons(getContext()).getDvrManager();
+ DvrManager dvrManager = TvSingletons.getSingletons(getContext()).getDvrManager();
long duration = mDurations.get((int) action.getId());
long startTimeMs = System.currentTimeMillis();
long endTimeMs = System.currentTimeMillis() + duration;
- List<ScheduledRecording> conflicts = dvrManager.getConflictingSchedules(
- mChannel.getId(), startTimeMs, endTimeMs);
+ List<ScheduledRecording> conflicts =
+ dvrManager.getConflictingSchedules(mChannel.getId(), startTimeMs, endTimeMs);
dvrManager.addSchedule(mChannel, startTimeMs, endTimeMs);
if (conflicts.isEmpty()) {
dismissDialog();
@@ -102,8 +106,7 @@ public class DvrChannelRecordDurationOptionFragment extends DvrGuidedStepFragmen
args.putLong(DvrHalfSizedDialogFragment.KEY_START_TIME_MS, startTimeMs);
args.putLong(DvrHalfSizedDialogFragment.KEY_END_TIME_MS, endTimeMs);
fragment.setArguments(args);
- GuidedStepFragment.add(getFragmentManager(), fragment,
- R.id.halfsized_dialog_host);
+ GuidedStepFragment.add(getFragmentManager(), fragment, R.id.halfsized_dialog_host);
}
}
diff --git a/src/com/android/tv/dvr/ui/DvrConflictFragment.java b/src/com/android/tv/dvr/ui/DvrConflictFragment.java
index 6f362e68..65759555 100644
--- a/src/com/android/tv/dvr/ui/DvrConflictFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrConflictFragment.java
@@ -27,18 +27,16 @@ import android.util.Log;
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.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
-import com.android.tv.data.Channel;
import com.android.tv.data.Program;
+import com.android.tv.data.api.Channel;
+import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.recorder.ConflictChecker;
import com.android.tv.dvr.recorder.ConflictChecker.OnUpcomingConflictChangeListener;
-import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.util.Utils;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@@ -72,15 +70,16 @@ public abstract class DvrConflictFragment extends DvrGuidedStepFragment {
}
@Override
- public void onCreateActions(@NonNull List<GuidedAction> actions,
- Bundle savedInstanceState) {
- actions.add(new GuidedAction.Builder(getContext())
- .clickAction(GuidedAction.ACTION_ID_OK)
- .build());
- actions.add(new GuidedAction.Builder(getContext())
- .id(ACTION_VIEW_SCHEDULES)
- .title(R.string.dvr_action_view_schedules)
- .build());
+ public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
+ actions.add(
+ new GuidedAction.Builder(getContext())
+ .clickAction(GuidedAction.ACTION_ID_OK)
+ .build());
+ actions.add(
+ new GuidedAction.Builder(getContext())
+ .id(ACTION_VIEW_SCHEDULES)
+ .title(R.string.dvr_action_view_schedules)
+ .build());
}
@Override
@@ -114,33 +113,45 @@ public abstract class DvrConflictFragment extends DvrGuidedStepFragment {
}
switch (titles.size()) {
case 0:
- Log.i(TAG, "Conflict has been resolved by any reason. Maybe input might have"
- + " been deleted.");
+ Log.i(
+ TAG,
+ "Conflict has been resolved by any reason. Maybe input might have"
+ + " been deleted.");
return null;
case 1:
- return getResources().getString(
- R.string.dvr_program_conflict_dialog_description_1, titles.get(0));
+ return getResources()
+ .getString(
+ R.string.dvr_program_conflict_dialog_description_1, titles.get(0));
case 2:
- return getResources().getString(
- R.string.dvr_program_conflict_dialog_description_2, titles.get(0),
- titles.get(1));
+ return getResources()
+ .getString(
+ R.string.dvr_program_conflict_dialog_description_2,
+ titles.get(0),
+ titles.get(1));
case 3:
- return getResources().getString(
- R.string.dvr_program_conflict_dialog_description_3, titles.get(0),
- titles.get(1));
+ return getResources()
+ .getString(
+ R.string.dvr_program_conflict_dialog_description_3,
+ titles.get(0),
+ titles.get(1));
default:
- return getResources().getQuantityString(
- R.plurals.dvr_program_conflict_dialog_description_many,
- titles.size() - LISTED_PROGRAM_COUNT, titles.get(0), titles.get(1),
- titles.size() - LISTED_PROGRAM_COUNT);
+ return getResources()
+ .getQuantityString(
+ R.plurals.dvr_program_conflict_dialog_description_many,
+ titles.size() - LISTED_PROGRAM_COUNT,
+ titles.get(0),
+ titles.get(1),
+ titles.size() - LISTED_PROGRAM_COUNT);
}
}
@Nullable
private String getScheduleTitle(ScheduledRecording schedule) {
if (schedule.getType() == ScheduledRecording.TYPE_TIMED) {
- Channel channel = TvApplication.getSingletons(getContext()).getChannelDataManager()
- .getChannel(schedule.getChannelId());
+ Channel channel =
+ TvSingletons.getSingletons(getContext())
+ .getChannelDataManager()
+ .getChannel(schedule.getChannelId());
if (channel != null) {
return channel.getDisplayName();
} else {
@@ -151,14 +162,13 @@ public abstract class DvrConflictFragment extends DvrGuidedStepFragment {
}
}
- /**
- * A fragment to show the program conflict.
- */
+ /** A fragment to show the program conflict. */
public static class DvrProgramConflictFragment extends DvrConflictFragment {
private Program mProgram;
+
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Bundle args = getArguments();
if (args != null) {
mProgram = args.getParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM);
@@ -168,8 +178,10 @@ public abstract class DvrConflictFragment extends DvrGuidedStepFragment {
SoftPreconditions.checkNotNull(input);
List<ScheduledRecording> conflicts = null;
if (input != null) {
- conflicts = TvApplication.getSingletons(getContext()).getDvrManager()
- .getConflictingSchedules(mProgram);
+ conflicts =
+ TvSingletons.getSingletons(getContext())
+ .getDvrManager()
+ .getConflictingSchedules(mProgram);
}
if (conflicts == null) {
conflicts = Collections.emptyList();
@@ -185,8 +197,10 @@ public abstract class DvrConflictFragment extends DvrGuidedStepFragment {
@Override
public Guidance onCreateGuidance(Bundle savedInstanceState) {
String title = getResources().getString(R.string.dvr_program_conflict_dialog_title);
- String descriptionPrefix = getString(
- R.string.dvr_program_conflict_dialog_description_prefix, mProgram.getTitle());
+ String descriptionPrefix =
+ getString(
+ R.string.dvr_program_conflict_dialog_description_prefix,
+ mProgram.getTitle());
String description = getConflictDescription();
if (description == null) {
dismissDialog();
@@ -201,21 +215,21 @@ public abstract class DvrConflictFragment extends DvrGuidedStepFragment {
}
}
- /**
- * A fragment to show the channel recording conflict.
- */
+ /** A fragment to show the channel recording conflict. */
public static class DvrChannelRecordConflictFragment extends DvrConflictFragment {
private Channel mChannel;
private long mStartTimeMs;
private long mEndTimeMs;
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Bundle args = getArguments();
long channelId = args.getLong(DvrHalfSizedDialogFragment.KEY_CHANNEL_ID);
- mChannel = TvApplication.getSingletons(getContext()).getChannelDataManager()
- .getChannel(channelId);
+ mChannel =
+ TvSingletons.getSingletons(getContext())
+ .getChannelDataManager()
+ .getChannel(channelId);
SoftPreconditions.checkArgument(mChannel != null);
TvInputInfo input = Utils.getTvInputInfoForChannelId(getContext(), mChannel.getId());
SoftPreconditions.checkNotNull(input);
@@ -223,8 +237,11 @@ public abstract class DvrConflictFragment extends DvrGuidedStepFragment {
if (input != null) {
mStartTimeMs = args.getLong(DvrHalfSizedDialogFragment.KEY_START_TIME_MS);
mEndTimeMs = args.getLong(DvrHalfSizedDialogFragment.KEY_END_TIME_MS);
- conflicts = TvApplication.getSingletons(getContext()).getDvrManager()
- .getConflictingSchedules(mChannel.getId(), mStartTimeMs, mEndTimeMs);
+ conflicts =
+ TvSingletons.getSingletons(getContext())
+ .getDvrManager()
+ .getConflictingSchedules(
+ mChannel.getId(), mStartTimeMs, mEndTimeMs);
}
if (conflicts == null) {
conflicts = Collections.emptyList();
@@ -240,9 +257,10 @@ public abstract class DvrConflictFragment extends DvrGuidedStepFragment {
@Override
public Guidance onCreateGuidance(Bundle savedInstanceState) {
String title = getResources().getString(R.string.dvr_channel_conflict_dialog_title);
- String descriptionPrefix = getString(
- R.string.dvr_channel_conflict_dialog_description_prefix,
- mChannel.getDisplayName());
+ String descriptionPrefix =
+ getString(
+ R.string.dvr_channel_conflict_dialog_description_prefix,
+ mChannel.getDisplayName());
String description = getConflictDescription();
if (description == null) {
dismissDialog();
@@ -259,16 +277,16 @@ public abstract class DvrConflictFragment extends DvrGuidedStepFragment {
/**
* A fragment to show the channel watching conflict.
- * <p>
- * This fragment is automatically closed when there are no upcoming conflicts.
+ *
+ * <p>This fragment is automatically closed when there are no upcoming conflicts.
*/
public static class DvrChannelWatchConflictFragment extends DvrConflictFragment
implements OnUpcomingConflictChangeListener {
private long mChannelId;
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Bundle args = getArguments();
if (args != null) {
mChannelId = args.getLong(DvrHalfSizedDialogFragment.KEY_CHANNEL_ID);
@@ -298,24 +316,27 @@ public abstract class DvrConflictFragment extends DvrGuidedStepFragment {
@NonNull
@Override
public Guidance onCreateGuidance(Bundle savedInstanceState) {
- String title = getResources().getString(
- R.string.dvr_epg_channel_watch_conflict_dialog_title);
- String description = getResources().getString(
- R.string.dvr_epg_channel_watch_conflict_dialog_description);
+ String title =
+ getResources().getString(R.string.dvr_epg_channel_watch_conflict_dialog_title);
+ String description =
+ getResources()
+ .getString(R.string.dvr_epg_channel_watch_conflict_dialog_description);
return new Guidance(title, description, null, null);
}
@Override
- public void onCreateActions(@NonNull List<GuidedAction> actions,
- Bundle savedInstanceState) {
- actions.add(new GuidedAction.Builder(getContext())
- .id(ACTION_DELETE_CONFLICT)
- .title(R.string.dvr_action_delete_schedule)
- .build());
- actions.add(new GuidedAction.Builder(getContext())
- .id(ACTION_CANCEL)
- .title(R.string.dvr_action_record_program)
- .build());
+ public void onCreateActions(
+ @NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
+ actions.add(
+ new GuidedAction.Builder(getContext())
+ .id(ACTION_DELETE_CONFLICT)
+ .title(R.string.dvr_action_delete_schedule)
+ .build());
+ actions.add(
+ new GuidedAction.Builder(getContext())
+ .id(ACTION_CANCEL)
+ .title(R.string.dvr_action_record_program)
+ .build());
}
@Override
diff --git a/src/com/android/tv/dvr/ui/DvrFutureProgramInfoFragment.java b/src/com/android/tv/dvr/ui/DvrFutureProgramInfoFragment.java
new file mode 100644
index 00000000..677a6cbb
--- /dev/null
+++ b/src/com/android/tv/dvr/ui/DvrFutureProgramInfoFragment.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2018 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.os.Bundle;
+import android.support.v17.leanback.widget.GuidanceStylist;
+import android.support.v17.leanback.widget.GuidedAction;
+import com.android.tv.TvSingletons;
+import com.android.tv.data.Program;
+import com.android.tv.dvr.data.ScheduledRecording;
+import com.android.tv.util.Utils;
+import java.util.List;
+
+/**
+ * A fragment which shows the formation of a program.
+ */
+public class DvrFutureProgramInfoFragment extends DvrGuidedStepFragment {
+ private static final long ACTION_ID_VIEW_SCHEDULE = 1;
+ private ScheduledRecording mScheduledRecording;
+ private Program mProgram;
+
+ @Override
+ public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
+ long startTime = mProgram.getStartTimeUtcMillis();
+ // TODO(b/71717923): use R.string when the strings are finalized
+ StringBuilder description = new StringBuilder()
+ .append("This program will start at ")
+ .append(Utils.getDurationString(getContext(), startTime, startTime, false));
+ if (mScheduledRecording != null) {
+ description.append("\nThis program has been scheduled for recording.");
+ }
+ return new GuidanceStylist.Guidance(
+ mProgram.getTitle(), description.toString(), null, null);
+ }
+
+ @Override
+ public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
+ Activity activity = getActivity();
+ mProgram = getArguments().getParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM);
+ mScheduledRecording =
+ TvSingletons.getSingletons(getContext())
+ .getDvrDataManager()
+ .getScheduledRecordingForProgramId(mProgram.getId());
+ actions.add(
+ new GuidedAction.Builder(activity)
+ .id(GuidedAction.ACTION_ID_OK)
+ .title(android.R.string.ok)
+ .build());
+ if (mScheduledRecording != null) {
+ actions.add(
+ new GuidedAction.Builder(activity)
+ .id(ACTION_ID_VIEW_SCHEDULE)
+ .title("View schedules")
+ .build());
+ }
+
+ }
+
+ @Override
+ public void onTrackedGuidedActionClicked(GuidedAction action) {
+ if (action.getId() == ACTION_ID_VIEW_SCHEDULE) {
+ DvrUiHelper.startSchedulesActivity(getContext(), mScheduledRecording);
+ return;
+ }
+ dismissDialog();
+ }
+
+ @Override
+ public String getTrackerPrefix() {
+ return "DvrFutureProgramInfoFragment";
+ }
+}
diff --git a/src/com/android/tv/dvr/ui/DvrGuidedActionsStylist.java b/src/com/android/tv/dvr/ui/DvrGuidedActionsStylist.java
index 6b0c22ff..611962d0 100644
--- a/src/com/android/tv/dvr/ui/DvrGuidedActionsStylist.java
+++ b/src/com/android/tv/dvr/ui/DvrGuidedActionsStylist.java
@@ -24,12 +24,9 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
-
import com.android.tv.R;
-/**
- * Stylist class used for DVR settings {@link GuidedStepFragment}.
- */
+/** Stylist class used for DVR settings {@link GuidedStepFragment}. */
public class DvrGuidedActionsStylist extends GuidedActionsStylist {
private static boolean sInitialized;
private static float sWidthWeight;
@@ -68,11 +65,13 @@ public class DvrGuidedActionsStylist extends GuidedActionsStylist {
return;
}
sInitialized = true;
- sItemHeight = context.getResources().getDimensionPixelSize(
- R.dimen.dvr_settings_one_line_action_container_height);
+ sItemHeight =
+ context.getResources()
+ .getDimensionPixelSize(
+ R.dimen.dvr_settings_one_line_action_container_height);
TypedValue outValue = new TypedValue();
- context.getResources().getValue(R.dimen.dvr_settings_button_actions_list_width_weight,
- outValue, true);
+ context.getResources()
+ .getValue(R.dimen.dvr_settings_button_actions_list_width_weight, outValue, true);
sWidthWeight = outValue.getFloat();
}
}
diff --git a/src/com/android/tv/dvr/ui/DvrGuidedStepFragment.java b/src/com/android/tv/dvr/ui/DvrGuidedStepFragment.java
index ab852e10..a900cc70 100644
--- a/src/com/android/tv/dvr/ui/DvrGuidedStepFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrGuidedStepFragment.java
@@ -26,31 +26,23 @@ import android.support.v17.leanback.widget.VerticalGridView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-
-import com.android.tv.ApplicationSingletons;
import com.android.tv.MainActivity;
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
+import com.android.tv.common.recording.RecordingStorageStatusManager;
import com.android.tv.dialog.HalfSizedDialogFragment.OnActionClickListener;
import com.android.tv.dialog.SafeDismissDialogFragment;
import com.android.tv.dvr.DvrManager;
-import com.android.tv.dvr.DvrStorageStatusManager;
-
import java.util.List;
public abstract class DvrGuidedStepFragment extends TrackedGuidedStepFragment {
- /**
- * Action ID for "recording/scheduling the program anyway".
- */
+ /** Action ID for "recording/scheduling the program anyway". */
public static final int ACTION_RECORD_ANYWAY = 1;
- /**
- * Action ID for "deleting existed recordings".
- */
+ /** Action ID for "deleting existed recordings". */
public static final int ACTION_DELETE_RECORDINGS = 2;
- /**
- * Action ID for "cancelling current recording request".
- */
+ /** Action ID for "cancelling current recording request". */
public static final int ACTION_CANCEL_RECORDING = 3;
+
public static final String UNKNOWN_DVR_ACTION = "Unknown DVR Action";
private DvrManager mDvrManager;
@@ -63,13 +55,13 @@ public abstract class DvrGuidedStepFragment extends TrackedGuidedStepFragment {
@Override
public void onAttach(Context context) {
super.onAttach(context);
- ApplicationSingletons singletons = TvApplication.getSingletons(context);
+ TvSingletons singletons = TvSingletons.getSingletons(context);
mDvrManager = singletons.getDvrManager();
}
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
VerticalGridView actionsList = getGuidedActionsStylist().getActionsGridView();
actionsList.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE);
@@ -122,32 +114,37 @@ public abstract class DvrGuidedStepFragment extends TrackedGuidedStepFragment {
}
/**
- * The inner guided step fragment for
- * {@link com.android.tv.dvr.ui.DvrHalfSizedDialogFragment
+ * The inner guided step fragment for {@link com.android.tv.dvr.ui.DvrHalfSizedDialogFragment
* .DvrNoFreeSpaceErrorDialogFragment}.
*/
public static class DvrNoFreeSpaceErrorFragment extends DvrGuidedStepFragment {
@Override
public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
- return new GuidanceStylist.Guidance(getString(R.string.dvr_error_no_free_space_title),
- getString(R.string.dvr_error_no_free_space_description), null, null);
+ return new GuidanceStylist.Guidance(
+ getString(R.string.dvr_error_no_free_space_title),
+ getString(R.string.dvr_error_no_free_space_description),
+ null,
+ null);
}
@Override
public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
Activity activity = getActivity();
- actions.add(new GuidedAction.Builder(activity)
- .id(ACTION_RECORD_ANYWAY)
- .title(R.string.dvr_action_record_anyway)
- .build());
- actions.add(new GuidedAction.Builder(activity)
- .id(ACTION_DELETE_RECORDINGS)
- .title(R.string.dvr_action_delete_recordings)
- .build());
- actions.add(new GuidedAction.Builder(activity)
- .id(ACTION_CANCEL_RECORDING)
- .title(R.string.dvr_action_record_cancel)
- .build());
+ actions.add(
+ new GuidedAction.Builder(activity)
+ .id(ACTION_RECORD_ANYWAY)
+ .title(R.string.dvr_action_record_anyway)
+ .build());
+ actions.add(
+ new GuidedAction.Builder(activity)
+ .id(ACTION_DELETE_RECORDINGS)
+ .title(R.string.dvr_action_delete_recordings)
+ .build());
+ actions.add(
+ new GuidedAction.Builder(activity)
+ .id(ACTION_CANCEL_RECORDING)
+ .title(R.string.dvr_action_record_cancel)
+ .build());
}
@Override
@@ -157,29 +154,32 @@ public abstract class DvrGuidedStepFragment extends TrackedGuidedStepFragment {
}
/**
- * The inner guided step fragment for
- * {@link com.android.tv.dvr.ui.DvrHalfSizedDialogFragment
+ * The inner guided step fragment for {@link com.android.tv.dvr.ui.DvrHalfSizedDialogFragment
* .DvrSmallSizedStorageErrorDialogFragment}.
*/
public static class DvrSmallSizedStorageErrorFragment extends DvrGuidedStepFragment {
@Override
public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
- String title = getResources().getString(
- R.string.dvr_error_small_sized_storage_title);
- String description = getResources().getString(
- R.string.dvr_error_small_sized_storage_description,
- DvrStorageStatusManager.MIN_STORAGE_SIZE_FOR_DVR_IN_BYTES / 1024
- / 1024 / 1024);
+ String title = getResources().getString(R.string.dvr_error_small_sized_storage_title);
+ String description =
+ getResources()
+ .getString(
+ R.string.dvr_error_small_sized_storage_description,
+ RecordingStorageStatusManager.MIN_STORAGE_SIZE_FOR_DVR_IN_BYTES
+ / 1024
+ / 1024
+ / 1024);
return new GuidanceStylist.Guidance(title, description, null, null);
}
@Override
public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
Activity activity = getActivity();
- actions.add(new GuidedAction.Builder(activity)
- .id(GuidedAction.ACTION_ID_OK)
- .title(android.R.string.ok)
- .build());
+ actions.add(
+ new GuidedAction.Builder(activity)
+ .id(GuidedAction.ACTION_ID_OK)
+ .title(android.R.string.ok)
+ .build());
}
@Override
@@ -192,4 +192,4 @@ public abstract class DvrGuidedStepFragment extends TrackedGuidedStepFragment {
return "DvrSmallSizedStorageErrorFragment";
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/DvrHalfSizedDialogFragment.java b/src/com/android/tv/dvr/ui/DvrHalfSizedDialogFragment.java
index f8ef3850..4a713703 100644
--- a/src/com/android/tv/dvr/ui/DvrHalfSizedDialogFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrHalfSizedDialogFragment.java
@@ -20,47 +20,26 @@ import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.v17.leanback.app.GuidedStepFragment;
-import android.support.v17.leanback.widget.GuidedAction;
-import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
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.dvr.DvrStorageStatusManager;
import com.android.tv.dialog.HalfSizedDialogFragment;
import com.android.tv.dvr.ui.DvrConflictFragment.DvrChannelWatchConflictFragment;
import com.android.tv.dvr.ui.DvrConflictFragment.DvrProgramConflictFragment;
import com.android.tv.guide.ProgramGuide;
-import java.util.List;
-
public class DvrHalfSizedDialogFragment extends HalfSizedDialogFragment {
- /**
- * Key for input ID.
- * Type: String.
- */
+ /** Key for input ID. Type: String. */
public static final String KEY_INPUT_ID = "DvrHalfSizedDialogFragment.input_id";
- /**
- * Key for the program.
- * Type: {@link com.android.tv.data.Program}.
- */
+ /** Key for the program. Type: {@link com.android.tv.data.Program}. */
public static final String KEY_PROGRAM = "DvrHalfSizedDialogFragment.program";
- /**
- * Key for the channel ID.
- * Type: long.
- */
+ /** Key for the channel ID. Type: long. */
public static final String KEY_CHANNEL_ID = "DvrHalfSizedDialogFragment.channel_id";
- /**
- * Key for the recording start time in millisecond.
- * Type: long.
- */
+ /** Key for the recording start time in millisecond. Type: long. */
public static final String KEY_START_TIME_MS = "DvrHalfSizedDialogFragment.start_time_ms";
- /**
- * Key for the recording end time in millisecond.
- * Type: long.
- */
+ /** Key for the recording end time in millisecond. Type: long. */
public static final String KEY_END_TIME_MS = "DvrHalfSizedDialogFragment.end_time_ms";
@Override
@@ -93,14 +72,14 @@ public class DvrHalfSizedDialogFragment extends HalfSizedDialogFragment {
private DvrGuidedStepFragment mFragment;
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
mFragment = onCreateGuidedStepFragment();
mFragment.setArguments(getArguments());
mFragment.setOnActionClickListener(getOnActionClickListener());
- GuidedStepFragment.add(getChildFragmentManager(),
- mFragment, R.id.halfsized_dialog_host);
+ GuidedStepFragment.add(
+ getChildFragmentManager(), mFragment, R.id.halfsized_dialog_host);
return view;
}
@@ -158,19 +137,15 @@ public class DvrHalfSizedDialogFragment extends HalfSizedDialogFragment {
}
/** A dialog fragment for {@link DvrMissingStorageErrorFragment}. */
- public static class DvrMissingStorageErrorDialogFragment
- extends DvrGuidedStepDialogFragment {
+ public static class DvrMissingStorageErrorDialogFragment extends DvrGuidedStepDialogFragment {
@Override
protected DvrGuidedStepFragment onCreateGuidedStepFragment() {
return new DvrMissingStorageErrorFragment();
}
}
- /**
- * A dialog fragment to show error message when there is no enough free space to record.
- */
- public static class DvrNoFreeSpaceErrorDialogFragment
- extends DvrGuidedStepDialogFragment {
+ /** A dialog fragment to show error message when there is no enough free space to record. */
+ public static class DvrNoFreeSpaceErrorDialogFragment extends DvrGuidedStepDialogFragment {
@Override
protected DvrGuidedStepFragment onCreateGuidedStepFragment() {
return new DvrGuidedStepFragment.DvrNoFreeSpaceErrorFragment();
@@ -178,8 +153,7 @@ public class DvrHalfSizedDialogFragment extends HalfSizedDialogFragment {
}
/**
- * A dialog fragment to show error message when the current storage is too small to
- * support DVR
+ * A dialog fragment to show error message when the current storage is too small to support DVR
*/
public static class DvrSmallSizedStorageErrorDialogFragment
extends DvrGuidedStepDialogFragment {
@@ -212,4 +186,12 @@ public class DvrHalfSizedDialogFragment extends HalfSizedDialogFragment {
return new DvrAlreadyRecordedFragment();
}
}
-} \ No newline at end of file
+
+ /** A dialog fragment for {@link DvrFutureProgramInfoFragment}. */
+ public static class DvrFutureProgramInfoDialogFragment extends DvrGuidedStepDialogFragment {
+ @Override
+ protected DvrGuidedStepFragment onCreateGuidedStepFragment() {
+ return new DvrFutureProgramInfoFragment();
+ }
+ }
+}
diff --git a/src/com/android/tv/dvr/ui/DvrInsufficientSpaceErrorFragment.java b/src/com/android/tv/dvr/ui/DvrInsufficientSpaceErrorFragment.java
index 182416b6..6fba4d98 100644
--- a/src/com/android/tv/dvr/ui/DvrInsufficientSpaceErrorFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrInsufficientSpaceErrorFragment.java
@@ -22,19 +22,15 @@ import android.content.Intent;
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.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
import com.android.tv.dvr.ui.browse.DvrBrowseActivity;
-
import java.util.ArrayList;
import java.util.List;
public class DvrInsufficientSpaceErrorFragment extends DvrGuidedStepFragment {
- /**
- * Key for the failed scheduled recordings information.
- */
+ /** Key for the failed scheduled recordings information. */
public static final String FAILED_SCHEDULED_RECORDING_INFOS =
"failed_scheduled_recording_infos";
@@ -54,7 +50,8 @@ public class DvrInsufficientSpaceErrorFragment extends DvrGuidedStepFragment {
}
SoftPreconditions.checkState(
mFailedScheduledRecordingInfos != null && !mFailedScheduledRecordingInfos.isEmpty(),
- TAG, "failed scheduled recording is null");
+ TAG,
+ "failed scheduled recording is null");
}
@Override
@@ -63,28 +60,39 @@ public class DvrInsufficientSpaceErrorFragment extends DvrGuidedStepFragment {
String description;
int failedScheduledRecordingSize = mFailedScheduledRecordingInfos.size();
if (failedScheduledRecordingSize == 1) {
- title = getString(
- R.string.dvr_error_insufficient_space_title_one_recording,
- mFailedScheduledRecordingInfos.get(0));
- description = getString(
- R.string.dvr_error_insufficient_space_description_one_recording,
- mFailedScheduledRecordingInfos.get(0));
+ title =
+ getString(
+ R.string.dvr_error_insufficient_space_title_one_recording,
+ mFailedScheduledRecordingInfos.get(0));
+ description =
+ getString(
+ R.string.dvr_error_insufficient_space_description_one_recording,
+ mFailedScheduledRecordingInfos.get(0));
} else if (failedScheduledRecordingSize == 2) {
- title = getString(
- R.string.dvr_error_insufficient_space_title_two_recordings,
- mFailedScheduledRecordingInfos.get(0), mFailedScheduledRecordingInfos.get(1));
- description = getString(
- R.string.dvr_error_insufficient_space_description_two_recordings,
- mFailedScheduledRecordingInfos.get(0), mFailedScheduledRecordingInfos.get(1));
+ title =
+ getString(
+ R.string.dvr_error_insufficient_space_title_two_recordings,
+ mFailedScheduledRecordingInfos.get(0),
+ mFailedScheduledRecordingInfos.get(1));
+ description =
+ getString(
+ R.string.dvr_error_insufficient_space_description_two_recordings,
+ mFailedScheduledRecordingInfos.get(0),
+ mFailedScheduledRecordingInfos.get(1));
} else {
- title = getString(
- R.string.dvr_error_insufficient_space_title_three_or_more_recordings,
- mFailedScheduledRecordingInfos.get(0), mFailedScheduledRecordingInfos.get(1),
- mFailedScheduledRecordingInfos.get(2));
- description = getString(
- R.string.dvr_error_insufficient_space_description_three_or_more_recordings,
- mFailedScheduledRecordingInfos.get(0), mFailedScheduledRecordingInfos.get(1),
- mFailedScheduledRecordingInfos.get(2));
+ title =
+ getString(
+ R.string.dvr_error_insufficient_space_title_three_or_more_recordings,
+ mFailedScheduledRecordingInfos.get(0),
+ mFailedScheduledRecordingInfos.get(1),
+ mFailedScheduledRecordingInfos.get(2));
+ description =
+ getString(
+ R.string
+ .dvr_error_insufficient_space_description_three_or_more_recordings,
+ mFailedScheduledRecordingInfos.get(0),
+ mFailedScheduledRecordingInfos.get(1),
+ mFailedScheduledRecordingInfos.get(2));
}
return new Guidance(title, description, null, null);
}
@@ -92,15 +100,18 @@ public class DvrInsufficientSpaceErrorFragment extends DvrGuidedStepFragment {
@Override
public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
Activity activity = getActivity();
- actions.add(new GuidedAction.Builder(activity)
- .clickAction(GuidedAction.ACTION_ID_OK)
- .build());
- if (TvApplication.getSingletons(getContext()).getDvrManager().hasValidItems()) {
- actions.add(new GuidedAction.Builder(activity)
- .id(ACTION_VIEW_RECENT_RECORDINGS)
- .title(getResources().getString(
- R.string.dvr_error_insufficient_space_action_view_recent_recordings))
- .build());
+ actions.add(
+ new GuidedAction.Builder(activity).clickAction(GuidedAction.ACTION_ID_OK).build());
+ if (TvSingletons.getSingletons(getContext()).getDvrManager().hasValidItems()) {
+ actions.add(
+ new GuidedAction.Builder(activity)
+ .id(ACTION_VIEW_RECENT_RECORDINGS)
+ .title(
+ getResources()
+ .getString(
+ R.string
+ .dvr_error_insufficient_space_action_view_recent_recordings))
+ .build());
}
}
diff --git a/src/com/android/tv/dvr/ui/DvrMissingStorageErrorFragment.java b/src/com/android/tv/dvr/ui/DvrMissingStorageErrorFragment.java
index e726995f..e5f40260 100644
--- a/src/com/android/tv/dvr/ui/DvrMissingStorageErrorFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrMissingStorageErrorFragment.java
@@ -24,10 +24,8 @@ import android.provider.Settings;
import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
import android.support.v17.leanback.widget.GuidedAction;
import android.util.Log;
-
import com.android.tv.R;
import com.android.tv.dvr.ui.browse.DvrDetailsActivity;
-
import java.util.List;
public class DvrMissingStorageErrorFragment extends DvrGuidedStepFragment {
@@ -44,22 +42,24 @@ public class DvrMissingStorageErrorFragment extends DvrGuidedStepFragment {
@Override
public Guidance onCreateGuidance(Bundle savedInstanceState) {
String title = getResources().getString(R.string.dvr_error_missing_storage_title);
- String description = getResources().getString(
- R.string.dvr_error_missing_storage_description);
+ String description =
+ getResources().getString(R.string.dvr_error_missing_storage_description);
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(ACTION_OK)
- .title(android.R.string.ok)
- .build());
- actions.add(new GuidedAction.Builder(activity)
- .id(ACTION_OPEN_STORAGE_SETTINGS)
- .title(getResources().getString(R.string.dvr_action_error_storage_settings))
- .build());
+ actions.add(
+ new GuidedAction.Builder(activity)
+ .id(ACTION_OK)
+ .title(android.R.string.ok)
+ .build());
+ actions.add(
+ new GuidedAction.Builder(activity)
+ .id(ACTION_OPEN_STORAGE_SETTINGS)
+ .title(getResources().getString(R.string.dvr_action_error_storage_settings))
+ .build());
}
@Override
diff --git a/src/com/android/tv/dvr/ui/DvrPrioritySettingsFragment.java b/src/com/android/tv/dvr/ui/DvrPrioritySettingsFragment.java
index e4cb7243..5bb97e90 100644
--- a/src/com/android/tv/dvr/ui/DvrPrioritySettingsFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrPrioritySettingsFragment.java
@@ -16,9 +16,11 @@
package com.android.tv.dvr.ui;
+import android.annotation.TargetApi;
import android.app.FragmentManager;
import android.content.Context;
import android.graphics.Typeface;
+import android.os.Build;
import android.os.Bundle;
import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
import android.support.v17.leanback.widget.GuidedAction;
@@ -26,23 +28,20 @@ import android.support.v17.leanback.widget.GuidedActionsStylist;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.DvrScheduleManager;
import com.android.tv.dvr.data.SeriesRecording;
-
import java.util.ArrayList;
import java.util.List;
/** Fragment for DVR series recording settings. */
+@TargetApi(Build.VERSION_CODES.N)
+@SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated
public class DvrPrioritySettingsFragment extends TrackedGuidedStepFragment {
- /**
- * Name of series recording id starting the fragment.
- * Type: Long
- */
+ /** Name of series recording id starting the fragment. Type: Long */
public static final String COME_FROM_SERIES_RECORDING_ID = "series_recording_id";
private static final int ONE_TIME_RECORDING_ID = 0;
@@ -61,14 +60,14 @@ public class DvrPrioritySettingsFragment extends TrackedGuidedStepFragment {
public void onAttach(Context context) {
super.onAttach(context);
mSeriesRecordings.clear();
- mSeriesRecordings.add(new SeriesRecording.Builder()
- .setTitle(getString(R.string.dvr_priority_action_one_time_recording))
- .setPriority(Long.MAX_VALUE)
- .setId(ONE_TIME_RECORDING_ID)
- .build());
- DvrDataManager dvrDataManager = TvApplication.getSingletons(context).getDvrDataManager();
- long comeFromSeriesRecordingId =
- getArguments().getLong(COME_FROM_SERIES_RECORDING_ID, -1);
+ mSeriesRecordings.add(
+ new SeriesRecording.Builder()
+ .setTitle(getString(R.string.dvr_priority_action_one_time_recording))
+ .setPriority(Long.MAX_VALUE)
+ .setId(ONE_TIME_RECORDING_ID)
+ .build());
+ DvrDataManager dvrDataManager = TvSingletons.getSingletons(context).getDvrDataManager();
+ long comeFromSeriesRecordingId = getArguments().getLong(COME_FROM_SERIES_RECORDING_ID, -1);
for (SeriesRecording series : dvrDataManager.getSeriesRecordings()) {
if (series.getState() == SeriesRecording.STATE_SERIES_NORMAL
|| series.getId() == comeFromSeriesRecordingId) {
@@ -86,52 +85,62 @@ public class DvrPrioritySettingsFragment extends TrackedGuidedStepFragment {
@Override
public void onResume() {
super.onResume();
- setSelectedActionPosition(mComeFromSeriesRecording == null ? 1
- : mSeriesRecordings.indexOf(mComeFromSeriesRecording));
+ setSelectedActionPosition(
+ mComeFromSeriesRecording == null
+ ? 1
+ : mSeriesRecordings.indexOf(mComeFromSeriesRecording));
}
@Override
public Guidance onCreateGuidance(Bundle savedInstanceState) {
- String breadcrumb = mComeFromSeriesRecording == null ? null
- : mComeFromSeriesRecording.getTitle();
- return new Guidance(getString(R.string.dvr_priority_title),
- getString(R.string.dvr_priority_description), breadcrumb, null);
+ String breadcrumb =
+ mComeFromSeriesRecording == null ? null : mComeFromSeriesRecording.getTitle();
+ return new Guidance(
+ getString(R.string.dvr_priority_title),
+ getString(R.string.dvr_priority_description),
+ breadcrumb,
+ null);
}
@Override
public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
int position = 0;
for (SeriesRecording seriesRecording : mSeriesRecordings) {
- actions.add(new GuidedAction.Builder(getActivity())
- .id(position++)
- .title(seriesRecording.getTitle())
- .build());
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(position++)
+ .title(seriesRecording.getTitle())
+ .build());
}
}
@Override
public void onCreateButtonActions(List<GuidedAction> actions, Bundle savedInstanceState) {
- actions.add(new GuidedAction.Builder(getActivity())
- .id(ACTION_ID_SAVE)
- .title(getString(R.string.dvr_priority_button_action_save))
- .build());
- actions.add(new GuidedAction.Builder(getActivity())
- .clickAction(GuidedAction.ACTION_ID_CANCEL)
- .build());
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(ACTION_ID_SAVE)
+ .title(getString(R.string.dvr_priority_button_action_save))
+ .build());
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .clickAction(GuidedAction.ACTION_ID_CANCEL)
+ .build());
}
@Override
public void onTrackedGuidedActionClicked(GuidedAction action) {
long actionId = action.getId();
if (actionId == ACTION_ID_SAVE) {
- DvrManager dvrManager = TvApplication.getSingletons(getContext()).getDvrManager();
+ DvrManager dvrManager = TvSingletons.getSingletons(getContext()).getDvrManager();
int size = mSeriesRecordings.size();
for (int i = 1; i < size; ++i) {
long priority = DvrScheduleManager.suggestSeriesPriority(size - i);
SeriesRecording seriesRecording = mSeriesRecordings.get(i);
if (seriesRecording.getPriority() != priority) {
- dvrManager.updateSeriesRecording(SeriesRecording.buildFrom(seriesRecording)
- .setPriority(priority).build());
+ dvrManager.updateSeriesRecording(
+ SeriesRecording.buildFrom(seriesRecording)
+ .setPriority(priority)
+ .build());
}
}
FragmentManager fragmentManager = getFragmentManager();
@@ -222,8 +231,9 @@ public class DvrPrioritySettingsFragment extends TrackedGuidedStepFragment {
private void updateItem(View itemView, int position) {
GuidedAction action = getActions().get(position);
action.setTitle(mSeriesRecordings.get(position).getTitle());
- boolean selected = mSelectedRecording != null
- && mSeriesRecordings.indexOf(mSelectedRecording) == position;
+ boolean selected =
+ mSelectedRecording != null
+ && mSeriesRecordings.indexOf(mSelectedRecording) == position;
TextView titleView = (TextView) itemView.findViewById(R.id.guidedactions_item_title);
ImageView imageView = (ImageView) itemView.findViewById(R.id.guidedactions_item_tail_image);
if (position == 0) {
@@ -259,4 +269,4 @@ public class DvrPrioritySettingsFragment extends TrackedGuidedStepFragment {
titleView.setTypeface(titleView.getTypeface(), Typeface.NORMAL);
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/DvrScheduleFragment.java b/src/com/android/tv/dvr/ui/DvrScheduleFragment.java
index 390e0928..5251e140 100644
--- a/src/com/android/tv/dvr/ui/DvrScheduleFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrScheduleFragment.java
@@ -26,9 +26,8 @@ import android.support.v17.leanback.app.GuidedStepFragment;
import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
import android.support.v17.leanback.widget.GuidedAction;
import android.text.format.DateUtils;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
import com.android.tv.data.Program;
import com.android.tv.dvr.DvrManager;
@@ -36,21 +35,17 @@ import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.data.SeriesRecording;
import com.android.tv.dvr.ui.DvrConflictFragment.DvrProgramConflictFragment;
import com.android.tv.util.Utils;
-
import java.util.Collections;
import java.util.List;
/**
* A fragment which asks the user the type of the recording.
- * <p>
- * The program should be episodic and the series recording should not had been created yet.
+ *
+ * <p>The program should be episodic and the series recording should not had been created yet.
*/
@TargetApi(Build.VERSION_CODES.N)
public class DvrScheduleFragment extends DvrGuidedStepFragment {
- /**
- * Key for the whether to add the current program to series.
- * Type: boolean
- */
+ /** Key for the whether to add the current program to series. Type: boolean */
public static final String KEY_ADD_CURRENT_PROGRAM_TO_SERIES = "add_current_program_to_series";
private static final String TAG = "DvrScheduleFragment";
@@ -68,13 +63,18 @@ public class DvrScheduleFragment extends DvrGuidedStepFragment {
mProgram = args.getParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM);
mAddCurrentProgramToSeries = args.getBoolean(KEY_ADD_CURRENT_PROGRAM_TO_SERIES, false);
}
- DvrManager dvrManager = TvApplication.getSingletons(getContext()).getDvrManager();
- SoftPreconditions.checkArgument(mProgram != null && mProgram.isEpisodic(), TAG,
- "The program should be episodic: " + mProgram);
+ DvrManager dvrManager = TvSingletons.getSingletons(getContext()).getDvrManager();
+ SoftPreconditions.checkArgument(
+ mProgram != null && mProgram.isEpisodic(),
+ TAG,
+ "The program should be episodic: %s ",
+ mProgram);
SeriesRecording seriesRecording = dvrManager.getSeriesRecording(mProgram);
- SoftPreconditions.checkArgument(seriesRecording == null
- || seriesRecording.isStopped(), TAG,
- "The series recording should be stopped or null: " + seriesRecording);
+ SoftPreconditions.checkArgument(
+ seriesRecording == null || seriesRecording.isStopped(),
+ TAG,
+ "The series recording should be stopped or null: %s",
+ seriesRecording);
super.onCreate(savedInstanceState);
}
@@ -96,23 +96,33 @@ public class DvrScheduleFragment extends DvrGuidedStepFragment {
Context context = getContext();
String description;
if (mProgram.getStartTimeUtcMillis() <= System.currentTimeMillis()) {
- description = getString(R.string.dvr_action_record_episode_from_now_description,
- DateUtils.formatDateTime(context, mProgram.getEndTimeUtcMillis(),
- DateUtils.FORMAT_SHOW_TIME));
+ description =
+ getString(
+ R.string.dvr_action_record_episode_from_now_description,
+ DateUtils.formatDateTime(
+ context,
+ mProgram.getEndTimeUtcMillis(),
+ DateUtils.FORMAT_SHOW_TIME));
} else {
- description = Utils.getDurationString(context, mProgram.getStartTimeUtcMillis(),
- mProgram.getEndTimeUtcMillis(), true);
+ description =
+ Utils.getDurationString(
+ context,
+ mProgram.getStartTimeUtcMillis(),
+ mProgram.getEndTimeUtcMillis(),
+ true);
}
- actions.add(new GuidedAction.Builder(context)
- .id(ACTION_RECORD_EPISODE)
- .title(R.string.dvr_action_record_episode)
- .description(description)
- .build());
- actions.add(new GuidedAction.Builder(context)
- .id(ACTION_RECORD_SERIES)
- .title(R.string.dvr_action_record_series)
- .description(mProgram.getTitle())
- .build());
+ actions.add(
+ new GuidedAction.Builder(context)
+ .id(ACTION_RECORD_EPISODE)
+ .title(R.string.dvr_action_record_episode)
+ .description(description)
+ .build());
+ actions.add(
+ new GuidedAction.Builder(context)
+ .id(ACTION_RECORD_SERIES)
+ .title(R.string.dvr_action_record_series)
+ .description(mProgram.getTitle())
+ .build());
}
@Override
@@ -121,34 +131,50 @@ public class DvrScheduleFragment extends DvrGuidedStepFragment {
getDvrManager().addSchedule(mProgram);
List<ScheduledRecording> conflicts = getDvrManager().getConflictingSchedules(mProgram);
if (conflicts.isEmpty()) {
- DvrUiHelper.showAddScheduleToast(getContext(), mProgram.getTitle(),
- mProgram.getStartTimeUtcMillis(), mProgram.getEndTimeUtcMillis());
+ DvrUiHelper.showAddScheduleToast(
+ getContext(),
+ mProgram.getTitle(),
+ mProgram.getStartTimeUtcMillis(),
+ mProgram.getEndTimeUtcMillis());
dismissDialog();
} else {
GuidedStepFragment fragment = new DvrProgramConflictFragment();
Bundle args = new Bundle();
args.putParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM, mProgram);
fragment.setArguments(args);
- GuidedStepFragment.add(getFragmentManager(), fragment,
- R.id.halfsized_dialog_host);
+ GuidedStepFragment.add(getFragmentManager(), fragment, R.id.halfsized_dialog_host);
}
} else if (action.getId() == ACTION_RECORD_SERIES) {
- SeriesRecording seriesRecording = TvApplication.getSingletons(getContext())
- .getDvrDataManager().getSeriesRecording(mProgram.getSeriesId());
+ SeriesRecording seriesRecording =
+ TvSingletons.getSingletons(getContext())
+ .getDvrDataManager()
+ .getSeriesRecording(mProgram.getSeriesId());
if (seriesRecording == null) {
- seriesRecording = getDvrManager().addSeriesRecording(mProgram,
- Collections.emptyList(), SeriesRecording.STATE_SERIES_STOPPED);
+ seriesRecording =
+ getDvrManager()
+ .addSeriesRecording(
+ mProgram,
+ Collections.emptyList(),
+ SeriesRecording.STATE_SERIES_STOPPED);
} else {
// Reset priority to the highest.
- seriesRecording = SeriesRecording.buildFrom(seriesRecording)
- .setPriority(TvApplication.getSingletons(getContext())
- .getDvrScheduleManager().suggestNewSeriesPriority())
- .build();
+ seriesRecording =
+ SeriesRecording.buildFrom(seriesRecording)
+ .setPriority(
+ TvSingletons.getSingletons(getContext())
+ .getDvrScheduleManager()
+ .suggestNewSeriesPriority())
+ .build();
getDvrManager().updateSeriesRecording(seriesRecording);
}
- DvrUiHelper.startSeriesSettingsActivity(getContext(),
- seriesRecording.getId(), null, true, true, true,
+ DvrUiHelper.startSeriesSettingsActivity(
+ getContext(),
+ seriesRecording.getId(),
+ null,
+ true,
+ true,
+ true,
mAddCurrentProgramToSeries ? mProgram : null);
dismissDialog();
}
diff --git a/src/com/android/tv/dvr/ui/DvrSeriesDeletionActivity.java b/src/com/android/tv/dvr/ui/DvrSeriesDeletionActivity.java
index 667af34a..a2ae1f97 100644
--- a/src/com/android/tv/dvr/ui/DvrSeriesDeletionActivity.java
+++ b/src/com/android/tv/dvr/ui/DvrSeriesDeletionActivity.java
@@ -19,22 +19,17 @@ package com.android.tv.dvr.ui;
import android.app.Activity;
import android.os.Bundle;
import android.support.v17.leanback.app.GuidedStepFragment;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.Starter;
-/**
- * Activity to show details view in DVR.
- */
+/** Activity to show details view in DVR. */
public class DvrSeriesDeletionActivity extends Activity {
- /**
- * Name of series id added to the Intent.
- */
+ /** Name of series id added to the Intent. */
public static final String SERIES_RECORDING_ID = "series_recording_id";
@Override
public void onCreate(Bundle savedInstanceState) {
- TvApplication.setCurrentRunningProcess(this, true);
+ Starter.start(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dvr_series_settings);
// Check savedInstanceState to prevent that activity is being showed with animation.
diff --git a/src/com/android/tv/dvr/ui/DvrSeriesDeletionFragment.java b/src/com/android/tv/dvr/ui/DvrSeriesDeletionFragment.java
index 8bf8560f..685f0a58 100644
--- a/src/com/android/tv/dvr/ui/DvrSeriesDeletionFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrSeriesDeletionFragment.java
@@ -26,9 +26,8 @@ import android.support.v17.leanback.widget.GuidedActionsStylist;
import android.text.TextUtils;
import android.view.ViewGroup.LayoutParams;
import android.widget.Toast;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrManager;
@@ -37,7 +36,6 @@ import com.android.tv.dvr.data.RecordedProgram;
import com.android.tv.dvr.data.SeriesRecording;
import com.android.tv.ui.GuidedActionsStylistWithDivider;
import com.android.tv.util.Utils;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@@ -45,9 +43,7 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
-/**
- * Fragment for DVR series recording settings.
- */
+/** Fragment for DVR series recording settings. */
public class DvrSeriesDeletionFragment extends GuidedStepFragment {
private static final long WATCHED_TIME_UNIT_THRESHOLD = TimeUnit.MINUTES.toMillis(2);
@@ -68,18 +64,23 @@ public class DvrSeriesDeletionFragment extends GuidedStepFragment {
@Override
public void onAttach(Context context) {
super.onAttach(context);
- mSeriesRecordingId = getArguments()
- .getLong(DvrSeriesDeletionActivity.SERIES_RECORDING_ID, -1);
+ mSeriesRecordingId =
+ getArguments().getLong(DvrSeriesDeletionActivity.SERIES_RECORDING_ID, -1);
SoftPreconditions.checkArgument(mSeriesRecordingId != -1);
- mDvrDataManager = TvApplication.getSingletons(context).getDvrDataManager();
+ mDvrDataManager = TvSingletons.getSingletons(context).getDvrDataManager();
mDvrWatchedPositionManager =
- TvApplication.getSingletons(context).getDvrWatchedPositionManager();
+ TvSingletons.getSingletons(context).getDvrWatchedPositionManager();
mRecordings = mDvrDataManager.getRecordedPrograms(mSeriesRecordingId);
- mOneLineActionHeight = getResources().getDimensionPixelSize(
- R.dimen.dvr_settings_one_line_action_container_height);
+ mOneLineActionHeight =
+ getResources()
+ .getDimensionPixelSize(
+ R.dimen.dvr_settings_one_line_action_container_height);
if (mRecordings.isEmpty()) {
- Toast.makeText(getActivity(), getString(R.string.dvr_series_deletion_no_recordings),
- Toast.LENGTH_LONG).show();
+ Toast.makeText(
+ getActivity(),
+ getString(R.string.dvr_series_deletion_no_recordings),
+ Toast.LENGTH_LONG)
+ .show();
finishGuidedStepFragments();
return;
}
@@ -93,28 +94,35 @@ public class DvrSeriesDeletionFragment extends GuidedStepFragment {
if (series != null) {
breadcrumb = series.getTitle();
}
- return new Guidance(getString(R.string.dvr_series_deletion_title),
- getString(R.string.dvr_series_deletion_description), breadcrumb, null);
+ return new Guidance(
+ getString(R.string.dvr_series_deletion_title),
+ getString(R.string.dvr_series_deletion_description),
+ breadcrumb,
+ null);
}
@Override
public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
- actions.add(new GuidedAction.Builder(getActivity())
- .id(ACTION_ID_SELECT_WATCHED)
- .title(getString(R.string.dvr_series_select_watched))
- .build());
- actions.add(new GuidedAction.Builder(getActivity())
- .id(ACTION_ID_SELECT_ALL)
- .title(getString(R.string.dvr_series_select_all))
- .build());
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(ACTION_ID_SELECT_WATCHED)
+ .title(getString(R.string.dvr_series_select_watched))
+ .build());
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(ACTION_ID_SELECT_ALL)
+ .title(getString(R.string.dvr_series_select_all))
+ .build());
actions.add(GuidedActionsStylistWithDivider.createDividerAction(getContext()));
for (RecordedProgram recording : mRecordings) {
long watchedPositionMs =
mDvrWatchedPositionManager.getWatchedPosition(recording.getId());
String title = recording.getEpisodeDisplayTitle(getContext());
if (TextUtils.isEmpty(title)) {
- title = TextUtils.isEmpty(recording.getTitle()) ?
- getString(R.string.channel_banner_no_title) : recording.getTitle();
+ title =
+ TextUtils.isEmpty(recording.getTitle())
+ ? getString(R.string.channel_banner_no_title)
+ : recording.getTitle();
}
String description;
if (watchedPositionMs != TvInputManager.TIME_SHIFT_INVALID_TIME) {
@@ -123,24 +131,27 @@ public class DvrSeriesDeletionFragment extends GuidedStepFragment {
} else {
description = getString(R.string.dvr_series_never_watched);
}
- actions.add(new GuidedAction.Builder(getActivity())
- .id(recording.getId())
- .title(title)
- .description(description)
- .checkSetId(GuidedAction.CHECKBOX_CHECK_SET_ID)
- .build());
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(recording.getId())
+ .title(title)
+ .description(description)
+ .checkSetId(GuidedAction.CHECKBOX_CHECK_SET_ID)
+ .build());
}
}
@Override
public void onCreateButtonActions(List<GuidedAction> actions, Bundle savedInstanceState) {
- actions.add(new GuidedAction.Builder(getActivity())
- .id(ACTION_ID_DELETE)
- .title(getString(R.string.dvr_detail_delete))
- .build());
- actions.add(new GuidedAction.Builder(getActivity())
- .clickAction(GuidedAction.ACTION_ID_CANCEL)
- .build());
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(ACTION_ID_DELETE)
+ .title(getString(R.string.dvr_detail_delete))
+ .build());
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .clickAction(GuidedAction.ACTION_ID_CANCEL)
+ .build());
}
@Override
@@ -155,12 +166,19 @@ public class DvrSeriesDeletionFragment extends GuidedStepFragment {
}
}
if (!idsToDelete.isEmpty()) {
- DvrManager dvrManager = TvApplication.getSingletons(getActivity()).getDvrManager();
+ DvrManager dvrManager = TvSingletons.getSingletons(getActivity()).getDvrManager();
dvrManager.removeRecordedPrograms(idsToDelete);
}
- Toast.makeText(getContext(), getResources().getQuantityString(
- R.plurals.dvr_msg_episodes_deleted, idsToDelete.size(), idsToDelete.size(),
- mRecordings.size()), Toast.LENGTH_LONG).show();
+ Toast.makeText(
+ getContext(),
+ getResources()
+ .getQuantityString(
+ R.plurals.dvr_msg_episodes_deleted,
+ idsToDelete.size(),
+ idsToDelete.size(),
+ mRecordings.size()),
+ Toast.LENGTH_LONG)
+ .show();
finishGuidedStepFragments();
} else if (actionId == GuidedAction.ACTION_ID_CANCEL) {
finishGuidedStepFragments();
@@ -218,13 +236,17 @@ public class DvrSeriesDeletionFragment extends GuidedStepFragment {
private String getWatchedString(long watchedPositionMs, long durationMs) {
if (durationMs > WATCHED_TIME_UNIT_THRESHOLD) {
- return getResources().getString(R.string.dvr_series_watched_info_minutes,
- Math.max(1, Utils.getRoundOffMinsFromMs(watchedPositionMs)),
- Utils.getRoundOffMinsFromMs(durationMs));
+ return getResources()
+ .getString(
+ R.string.dvr_series_watched_info_minutes,
+ Math.max(1, Utils.getRoundOffMinsFromMs(watchedPositionMs)),
+ Utils.getRoundOffMinsFromMs(durationMs));
} else {
- return getResources().getString(R.string.dvr_series_watched_info_seconds,
- Math.max(1, TimeUnit.MILLISECONDS.toSeconds(watchedPositionMs)),
- TimeUnit.MILLISECONDS.toSeconds(durationMs));
+ return getResources()
+ .getString(
+ R.string.dvr_series_watched_info_seconds,
+ Math.max(1, TimeUnit.MILLISECONDS.toSeconds(watchedPositionMs)),
+ TimeUnit.MILLISECONDS.toSeconds(durationMs));
}
}
@@ -246,8 +268,10 @@ public class DvrSeriesDeletionFragment extends GuidedStepFragment {
}
private void updateSelectAllState(GuidedAction selectAll, boolean select) {
- selectAll.setTitle(select ? getString(R.string.dvr_series_deselect_all)
- : getString(R.string.dvr_series_select_all));
+ selectAll.setTitle(
+ select
+ ? getString(R.string.dvr_series_deselect_all)
+ : getString(R.string.dvr_series_select_all));
notifyActionChanged(findActionPositionById(ACTION_ID_SELECT_ALL));
}
}
diff --git a/src/com/android/tv/dvr/ui/DvrSeriesScheduledDialogActivity.java b/src/com/android/tv/dvr/ui/DvrSeriesScheduledDialogActivity.java
index 1a0d13d3..9acb5b5e 100644
--- a/src/com/android/tv/dvr/ui/DvrSeriesScheduledDialogActivity.java
+++ b/src/com/android/tv/dvr/ui/DvrSeriesScheduledDialogActivity.java
@@ -19,18 +19,13 @@ package com.android.tv.dvr.ui;
import android.app.Activity;
import android.os.Bundle;
import android.support.v17.leanback.app.GuidedStepFragment;
-
import com.android.tv.R;
public class DvrSeriesScheduledDialogActivity extends Activity {
- /**
- * Name of series recording id added to the Intent.
- */
+ /** Name of series recording id added to the Intent. */
public static final String SERIES_RECORDING_ID = "series_recording_id";
- /**
- * Name of flag to check if the dialog should show view schedule option.
- */
+ /** Name of flag to check if the dialog should show view schedule option. */
public static final String SHOW_VIEW_SCHEDULE_OPTION = "show_view_schedule_option";
@Override
@@ -41,8 +36,8 @@ public class DvrSeriesScheduledDialogActivity extends Activity {
DvrSeriesScheduledFragment dvrSeriesScheduledFragment =
new DvrSeriesScheduledFragment();
dvrSeriesScheduledFragment.setArguments(getIntent().getExtras());
- GuidedStepFragment.addAsRoot(this, dvrSeriesScheduledFragment,
- R.id.halfsized_dialog_host);
+ GuidedStepFragment.addAsRoot(
+ this, dvrSeriesScheduledFragment, R.id.halfsized_dialog_host);
}
}
}
diff --git a/src/com/android/tv/dvr/ui/DvrSeriesScheduledFragment.java b/src/com/android/tv/dvr/ui/DvrSeriesScheduledFragment.java
index 2c4bb3ea..edb62c96 100644
--- a/src/com/android/tv/dvr/ui/DvrSeriesScheduledFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrSeriesScheduledFragment.java
@@ -22,28 +22,26 @@ import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v17.leanback.widget.GuidanceStylist;
import android.support.v17.leanback.widget.GuidedAction;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.data.Program;
import com.android.tv.dvr.DvrScheduleManager;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.data.SeriesRecording;
import com.android.tv.dvr.ui.list.DvrSchedulesActivity;
import com.android.tv.dvr.ui.list.DvrSeriesSchedulesFragment;
-
import java.util.List;
public class DvrSeriesScheduledFragment extends DvrGuidedStepFragment {
/**
- * The key for program list which will be passed to {@link DvrSeriesSchedulesFragment}.
- * Type: List<{@link Program}>
+ * The key for program list which will be passed to {@link DvrSeriesSchedulesFragment}. Type:
+ * List<{@link Program}>
*/
public static final String SERIES_SCHEDULED_KEY_PROGRAMS = "series_scheduled_key_programs";
- private final static long SERIES_RECORDING_ID_NOT_SET = -1;
+ private static final long SERIES_RECORDING_ID_NOT_SET = -1;
- private final static int ACTION_VIEW_SCHEDULES = 1;
+ private static final int ACTION_VIEW_SCHEDULES = 1;
private SeriesRecording mSeriesRecording;
private boolean mShowViewScheduleOption;
@@ -57,26 +55,35 @@ public class DvrSeriesScheduledFragment extends DvrGuidedStepFragment {
@Override
public void onAttach(Context context) {
super.onAttach(context);
- long seriesRecordingId = getArguments().getLong(
- DvrSeriesScheduledDialogActivity.SERIES_RECORDING_ID, SERIES_RECORDING_ID_NOT_SET);
+ long seriesRecordingId =
+ getArguments()
+ .getLong(
+ DvrSeriesScheduledDialogActivity.SERIES_RECORDING_ID,
+ SERIES_RECORDING_ID_NOT_SET);
if (seriesRecordingId == SERIES_RECORDING_ID_NOT_SET) {
getActivity().finish();
return;
}
- mShowViewScheduleOption = getArguments().getBoolean(
- DvrSeriesScheduledDialogActivity.SHOW_VIEW_SCHEDULE_OPTION);
- mSeriesRecording = TvApplication.getSingletons(context).getDvrDataManager()
- .getSeriesRecording(seriesRecordingId);
+ mShowViewScheduleOption =
+ getArguments()
+ .getBoolean(DvrSeriesScheduledDialogActivity.SHOW_VIEW_SCHEDULE_OPTION);
+ mSeriesRecording =
+ TvSingletons.getSingletons(context)
+ .getDvrDataManager()
+ .getSeriesRecording(seriesRecordingId);
if (mSeriesRecording == null) {
getActivity().finish();
return;
}
mPrograms = (List<Program>) BigArguments.getArgument(SERIES_SCHEDULED_KEY_PROGRAMS);
BigArguments.reset();
- mSchedulesAddedCount = TvApplication.getSingletons(getContext()).getDvrManager()
- .getAvailableScheduledRecording(mSeriesRecording.getId()).size();
+ mSchedulesAddedCount =
+ TvSingletons.getSingletons(getContext())
+ .getDvrManager()
+ .getAvailableScheduledRecording(mSeriesRecording.getId())
+ .size();
DvrScheduleManager dvrScheduleManager =
- TvApplication.getSingletons(context).getDvrScheduleManager();
+ TvSingletons.getSingletons(context).getDvrScheduleManager();
List<ScheduledRecording> conflictingRecordings =
dvrScheduleManager.getConflictingSchedules(mSeriesRecording);
mHasConflict = !conflictingRecordings.isEmpty();
@@ -87,7 +94,7 @@ public class DvrSeriesScheduledFragment extends DvrGuidedStepFragment {
++mOutThisSeriesConflictCount;
}
}
- }
+ }
@Override
public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
@@ -104,14 +111,14 @@ public class DvrSeriesScheduledFragment extends DvrGuidedStepFragment {
@Override
public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
Context context = getContext();
- actions.add(new GuidedAction.Builder(context)
- .clickAction(GuidedAction.ACTION_ID_OK)
- .build());
+ actions.add(
+ new GuidedAction.Builder(context).clickAction(GuidedAction.ACTION_ID_OK).build());
if (mShowViewScheduleOption) {
- actions.add(new GuidedAction.Builder(context)
- .id(ACTION_VIEW_SCHEDULES)
- .title(R.string.dvr_action_view_schedules)
- .build());
+ actions.add(
+ new GuidedAction.Builder(context)
+ .id(ACTION_VIEW_SCHEDULES)
+ .title(R.string.dvr_action_view_schedules)
+ .build());
}
}
@@ -119,13 +126,15 @@ public class DvrSeriesScheduledFragment extends DvrGuidedStepFragment {
public void onTrackedGuidedActionClicked(GuidedAction action) {
if (action.getId() == ACTION_VIEW_SCHEDULES) {
Intent intent = new Intent(getActivity(), DvrSchedulesActivity.class);
- intent.putExtra(DvrSchedulesActivity.KEY_SCHEDULES_TYPE, DvrSchedulesActivity
- .TYPE_SERIES_SCHEDULE);
- intent.putExtra(DvrSeriesSchedulesFragment.SERIES_SCHEDULES_KEY_SERIES_RECORDING,
+ intent.putExtra(
+ DvrSchedulesActivity.KEY_SCHEDULES_TYPE,
+ DvrSchedulesActivity.TYPE_SERIES_SCHEDULE);
+ intent.putExtra(
+ DvrSeriesSchedulesFragment.SERIES_SCHEDULES_KEY_SERIES_RECORDING,
mSeriesRecording);
BigArguments.reset();
- BigArguments.setArgument(DvrSeriesSchedulesFragment
- .SERIES_SCHEDULES_KEY_SERIES_PROGRAMS, mPrograms);
+ BigArguments.setArgument(
+ DvrSeriesSchedulesFragment.SERIES_SCHEDULES_KEY_SERIES_PROGRAMS, mPrograms);
startActivity(intent);
}
getActivity().finish();
@@ -148,33 +157,47 @@ public class DvrSeriesScheduledFragment extends DvrGuidedStepFragment {
private String getDescription() {
if (!mHasConflict) {
- return getResources().getQuantityString(
- R.plurals.dvr_series_scheduled_no_conflict, mSchedulesAddedCount,
- mSchedulesAddedCount, mSeriesRecording.getTitle());
+ return getResources()
+ .getQuantityString(
+ R.plurals.dvr_series_scheduled_no_conflict,
+ mSchedulesAddedCount,
+ mSchedulesAddedCount,
+ mSeriesRecording.getTitle());
} else {
// mInThisSeriesConflictCount equals 0 and mOutThisSeriesConflictCount equals 0 means
// mHasConflict is false. So we don't need to check that case.
if (mInThisSeriesConflictCount != 0 && mOutThisSeriesConflictCount != 0) {
- return getResources().getQuantityString(
- R.plurals.dvr_series_scheduled_this_and_other_series_conflict,
- mSchedulesAddedCount, mSchedulesAddedCount, mSeriesRecording.getTitle(),
- mInThisSeriesConflictCount + mOutThisSeriesConflictCount);
+ return getResources()
+ .getQuantityString(
+ R.plurals.dvr_series_scheduled_this_and_other_series_conflict,
+ mSchedulesAddedCount,
+ mSchedulesAddedCount,
+ mSeriesRecording.getTitle(),
+ mInThisSeriesConflictCount + mOutThisSeriesConflictCount);
} else if (mInThisSeriesConflictCount != 0) {
- return getResources().getQuantityString(
- R.plurals.dvr_series_recording_scheduled_only_this_series_conflict,
- mSchedulesAddedCount, mSchedulesAddedCount, mSeriesRecording.getTitle(),
- mInThisSeriesConflictCount);
+ return getResources()
+ .getQuantityString(
+ R.plurals.dvr_series_recording_scheduled_only_this_series_conflict,
+ mSchedulesAddedCount,
+ mSchedulesAddedCount,
+ mSeriesRecording.getTitle(),
+ mInThisSeriesConflictCount);
} else {
if (mOutThisSeriesConflictCount == 1) {
- return getResources().getQuantityString(
- R.plurals.dvr_series_scheduled_only_other_series_one_conflict,
- mSchedulesAddedCount, mSchedulesAddedCount,
- mSeriesRecording.getTitle());
+ return getResources()
+ .getQuantityString(
+ R.plurals.dvr_series_scheduled_only_other_series_one_conflict,
+ mSchedulesAddedCount,
+ mSchedulesAddedCount,
+ mSeriesRecording.getTitle());
} else {
- return getResources().getQuantityString(
- R.plurals.dvr_series_scheduled_only_other_series_many_conflicts,
- mSchedulesAddedCount, mSchedulesAddedCount, mSeriesRecording.getTitle(),
- mOutThisSeriesConflictCount);
+ return getResources()
+ .getQuantityString(
+ R.plurals.dvr_series_scheduled_only_other_series_many_conflicts,
+ mSchedulesAddedCount,
+ mSchedulesAddedCount,
+ mSeriesRecording.getTitle(),
+ mOutThisSeriesConflictCount);
}
}
}
diff --git a/src/com/android/tv/dvr/ui/DvrSeriesSettingsActivity.java b/src/com/android/tv/dvr/ui/DvrSeriesSettingsActivity.java
index 6dd20b3a..1a51cf46 100644
--- a/src/com/android/tv/dvr/ui/DvrSeriesSettingsActivity.java
+++ b/src/com/android/tv/dvr/ui/DvrSeriesSettingsActivity.java
@@ -20,34 +20,27 @@ import android.app.Activity;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.support.v17.leanback.app.GuidedStepFragment;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.Starter;
import com.android.tv.common.SoftPreconditions;
-/**
- * Activity to show details view in DVR.
- */
+/** Activity to show details view in DVR. */
public class DvrSeriesSettingsActivity extends Activity {
- /**
- * Name of series id added to the Intent.
- * Type: Long
- */
+ /** Name of series id added to the Intent. Type: Long */
public static final String SERIES_RECORDING_ID = "series_recording_id";
/**
* Name of the boolean flag to decide if the series recording with empty schedule and recording
- * will be removed.
- * Type: boolean
+ * will be removed. Type: boolean
*/
public static final String REMOVE_EMPTY_SERIES_RECORDING = "remove_empty_series_recording";
/**
- * Name of the boolean flag to decide if the setting fragment should be translucent.
- * Type: boolean
+ * Name of the boolean flag to decide if the setting fragment should be translucent. Type:
+ * boolean
*/
public static final String IS_WINDOW_TRANSLUCENT = "windows_translucent";
/**
- * Name of the program list. The list contains the programs which belong to the series.
- * Type: List<{@link com.android.tv.data.Program}>
+ * Name of the program list. The list contains the programs which belong to the series. Type:
+ * List<{@link com.android.tv.data.Program}>
*/
public static final String PROGRAM_LIST = "program_list";
@@ -67,7 +60,7 @@ public class DvrSeriesSettingsActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
- TvApplication.setCurrentRunningProcess(this, true);
+ Starter.start(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dvr_series_settings);
long seriesRecordingId = getIntent().getLongExtra(SERIES_RECORDING_ID, -1);
@@ -83,8 +76,9 @@ public class DvrSeriesSettingsActivity extends Activity {
@Override
public void onAttachedToWindow() {
if (!getIntent().getExtras().getBoolean(IS_WINDOW_TRANSLUCENT, true)) {
- getWindow().setBackgroundDrawable(
- new ColorDrawable(getColor(R.color.common_tv_background)));
+ getWindow()
+ .setBackgroundDrawable(
+ new ColorDrawable(getColor(R.color.common_tv_background)));
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/DvrSeriesSettingsFragment.java b/src/com/android/tv/dvr/ui/DvrSeriesSettingsFragment.java
index f28382da..eadb3b9e 100644
--- a/src/com/android/tv/dvr/ui/DvrSeriesSettingsFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrSeriesSettingsFragment.java
@@ -16,20 +16,22 @@
package com.android.tv.dvr.ui;
+import android.annotation.TargetApi;
import android.app.FragmentManager;
import android.content.Context;
+import android.os.Build;
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 android.support.v17.leanback.widget.GuidedActionsStylist;
import android.util.LongSparseArray;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
-import com.android.tv.data.Channel;
+import com.android.tv.TvSingletons;
import com.android.tv.data.ChannelDataManager;
+import com.android.tv.data.ChannelImpl;
import com.android.tv.data.Program;
+import com.android.tv.data.api.Channel;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.data.ScheduledRecording;
@@ -37,20 +39,18 @@ import com.android.tv.dvr.data.SeasonEpisodeNumber;
import com.android.tv.dvr.data.SeriesRecording;
import com.android.tv.dvr.data.SeriesRecording.ChannelOption;
import com.android.tv.dvr.recorder.SeriesRecordingScheduler;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-/**
- * Fragment for DVR series recording settings.
- */
+/** Fragment for DVR series recording settings. */
+@TargetApi(Build.VERSION_CODES.N)
+@SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated
public class DvrSeriesSettingsFragment extends GuidedStepFragment
implements DvrDataManager.SeriesRecordingListener {
private static final String TAG = "SeriesSettingsFragment";
- private static final boolean DEBUG = false;
private static final long ACTION_ID_PRIORITY = 10;
private static final long ACTION_ID_CHANNEL = 11;
@@ -85,19 +85,20 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment
public void onAttach(Context context) {
super.onAttach(context);
mBackStackCount = getFragmentManager().getBackStackEntryCount();
- mDvrDataManager = TvApplication.getSingletons(context).getDvrDataManager();
+ mDvrDataManager = TvSingletons.getSingletons(context).getDvrDataManager();
mSeriesRecordingId = getArguments().getLong(DvrSeriesSettingsActivity.SERIES_RECORDING_ID);
mSeriesRecording = mDvrDataManager.getSeriesRecording(mSeriesRecordingId);
if (mSeriesRecording == null) {
getActivity().finish();
return;
}
- mShowViewScheduleOptionInDialog = getArguments().getBoolean(
- DvrSeriesSettingsActivity.SHOW_VIEW_SCHEDULE_OPTION_IN_DIALOG);
+ mShowViewScheduleOptionInDialog =
+ getArguments()
+ .getBoolean(DvrSeriesSettingsActivity.SHOW_VIEW_SCHEDULE_OPTION_IN_DIALOG);
mCurrentProgram = getArguments().getParcelable(DvrSeriesSettingsActivity.CURRENT_PROGRAM);
mDvrDataManager.addSeriesRecordingListener(this);
- mPrograms = (List<Program>) BigArguments.getArgument(
- DvrSeriesSettingsActivity.PROGRAM_LIST);
+ mPrograms =
+ (List<Program>) BigArguments.getArgument(DvrSeriesSettingsActivity.PROGRAM_LIST);
BigArguments.reset();
if (mPrograms == null) {
getActivity().finish();
@@ -105,7 +106,7 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment
}
Set<Long> channelIds = new HashSet<>();
ChannelDataManager channelDataManager =
- TvApplication.getSingletons(context).getChannelDataManager();
+ TvSingletons.getSingletons(context).getChannelDataManager();
for (Program program : mPrograms) {
long channelId = program.getChannelId();
if (channelIds.add(channelId)) {
@@ -126,7 +127,7 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment
mChannelOption = SeriesRecording.OPTION_CHANNEL_ALL;
}
}
- mChannels.sort(Channel.CHANNEL_NUMBER_COMPARATOR);
+ mChannels.sort(ChannelImpl.CHANNEL_NUMBER_COMPARATOR);
mFragmentTitle = getString(R.string.dvr_series_settings_title);
mProrityActionTitle = getString(R.string.dvr_series_settings_priority);
mProrityActionHighestText = getString(R.string.dvr_series_settings_priority_highest);
@@ -150,8 +151,9 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment
@Override
public void onDestroy() {
- if (getFragmentManager().getBackStackEntryCount() == mBackStackCount && getArguments()
- .getBoolean(DvrSeriesSettingsActivity.REMOVE_EMPTY_SERIES_RECORDING)) {
+ if (getFragmentManager().getBackStackEntryCount() == mBackStackCount
+ && getArguments()
+ .getBoolean(DvrSeriesSettingsActivity.REMOVE_EMPTY_SERIES_RECORDING)) {
mDvrDataManager.checkAndRemoveEmptySeriesRecording(mSeriesRecordingId);
}
super.onDestroy();
@@ -166,29 +168,33 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment
@Override
public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
- mPriorityGuidedAction = new GuidedAction.Builder(getActivity())
- .id(ACTION_ID_PRIORITY)
- .title(mProrityActionTitle)
- .build();
+ mPriorityGuidedAction =
+ new GuidedAction.Builder(getActivity())
+ .id(ACTION_ID_PRIORITY)
+ .title(mProrityActionTitle)
+ .build();
actions.add(mPriorityGuidedAction);
- mChannelsGuidedAction = new GuidedAction.Builder(getActivity())
- .id(ACTION_ID_CHANNEL)
- .title(mChannelsActionTitle)
- .subActions(buildChannelSubAction())
- .build();
+ mChannelsGuidedAction =
+ new GuidedAction.Builder(getActivity())
+ .id(ACTION_ID_CHANNEL)
+ .title(mChannelsActionTitle)
+ .subActions(buildChannelSubAction())
+ .build();
actions.add(mChannelsGuidedAction);
updateChannelsGuidedAction(false);
}
@Override
public void onCreateButtonActions(List<GuidedAction> actions, Bundle savedInstanceState) {
- actions.add(new GuidedAction.Builder(getActivity())
- .clickAction(GuidedAction.ACTION_ID_OK)
- .build());
- actions.add(new GuidedAction.Builder(getActivity())
- .clickAction(GuidedAction.ACTION_ID_CANCEL)
- .build());
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .clickAction(GuidedAction.ACTION_ID_OK)
+ .build());
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .clickAction(GuidedAction.ACTION_ID_CANCEL)
+ .build());
}
@Override
@@ -199,16 +205,18 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment
|| mSeriesRecording.isStopped()
|| (mChannelOption == SeriesRecording.OPTION_CHANNEL_ONE
&& mSeriesRecording.getChannelId() != mSelectedChannelId)) {
- SeriesRecording.Builder builder = SeriesRecording.buildFrom(mSeriesRecording)
- .setChannelOption(mChannelOption)
- .setState(SeriesRecording.STATE_SERIES_NORMAL);
+ SeriesRecording.Builder builder =
+ SeriesRecording.buildFrom(mSeriesRecording)
+ .setChannelOption(mChannelOption)
+ .setState(SeriesRecording.STATE_SERIES_NORMAL);
if (mSelectedChannelId != Channel.INVALID_ID) {
builder.setChannelId(mSelectedChannelId);
}
- DvrManager dvrManager = TvApplication.getSingletons(getContext()).getDvrManager();
- dvrManager.updateSeriesRecording(builder.build());
- if (mCurrentProgram != null && (mChannelOption == SeriesRecording.OPTION_CHANNEL_ALL
- || mSelectedChannelId == mCurrentProgram.getChannelId())) {
+ DvrManager dvrManager = TvSingletons.getSingletons(getContext()).getDvrManager();
+ dvrManager.updateSeriesRecording(builder.build());
+ if (mCurrentProgram != null
+ && (mChannelOption == SeriesRecording.OPTION_CHANNEL_ALL
+ || mSelectedChannelId == mCurrentProgram.getChannelId())) {
dvrManager.addSchedule(mCurrentProgram);
}
updateSchedulesToSeries();
@@ -222,7 +230,8 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment
FragmentManager fragmentManager = getFragmentManager();
DvrPrioritySettingsFragment fragment = new DvrPrioritySettingsFragment();
Bundle args = new Bundle();
- args.putLong(DvrPrioritySettingsFragment.COME_FROM_SERIES_RECORDING_ID,
+ args.putLong(
+ DvrPrioritySettingsFragment.COME_FROM_SERIES_RECORDING_ID,
mSeriesRecording.getId());
fragment.setArguments(args);
GuidedStepFragment.add(fragmentManager, fragment, R.id.dvr_settings_view_frame);
@@ -254,9 +263,9 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment
private void updateChannelsGuidedAction(boolean notifyActionChanged) {
if (mChannelOption == SeriesRecording.OPTION_CHANNEL_ALL) {
mChannelsGuidedAction.setDescription(mChannelsActionAllText);
- } else if (mId2Channel.get(mSelectedChannelId) != null){
- mChannelsGuidedAction.setDescription(mId2Channel.get(mSelectedChannelId)
- .getDisplayText());
+ } else if (mId2Channel.get(mSelectedChannelId) != null) {
+ mChannelsGuidedAction.setDescription(
+ mId2Channel.get(mSelectedChannelId).getDisplayText());
}
if (notifyActionChanged) {
notifyActionChanged(findActionPositionById(ACTION_ID_CHANNEL));
@@ -282,8 +291,8 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment
} else if (priorityOrder >= totalSeriesCount - 1) {
mPriorityGuidedAction.setDescription(mProrityActionLowestText);
} else {
- mPriorityGuidedAction.setDescription(getString(
- R.string.dvr_series_settings_priority_rank, priorityOrder + 1));
+ mPriorityGuidedAction.setDescription(
+ getString(R.string.dvr_series_settings_priority_rank, priorityOrder + 1));
}
notifyActionChanged(findActionPositionById(ACTION_ID_PRIORITY));
}
@@ -294,54 +303,66 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment
for (ScheduledRecording r : mDvrDataManager.getScheduledRecordings(mSeriesRecordingId)) {
if (r.getState() != ScheduledRecording.STATE_RECORDING_FAILED
&& r.getState() != ScheduledRecording.STATE_RECORDING_CLIPPED) {
- scheduledEpisodes.add(new SeasonEpisodeNumber(
- r.getSeriesRecordingId(), r.getSeasonNumber(), r.getEpisodeNumber()));
+ scheduledEpisodes.add(
+ new SeasonEpisodeNumber(
+ r.getSeriesRecordingId(),
+ r.getSeasonNumber(),
+ r.getEpisodeNumber()));
}
}
for (Program program : mPrograms) {
// Removes current programs and scheduled episodes out, matches the channel option.
if (program.getStartTimeUtcMillis() >= System.currentTimeMillis()
&& mSeriesRecording.matchProgram(program)
- && !scheduledEpisodes.contains(new SeasonEpisodeNumber(
- mSeriesRecordingId, program.getSeasonNumber(), program.getEpisodeNumber()))) {
+ && !scheduledEpisodes.contains(
+ new SeasonEpisodeNumber(
+ mSeriesRecordingId,
+ program.getSeasonNumber(),
+ program.getEpisodeNumber()))) {
recordingCandidates.add(program);
}
}
if (recordingCandidates.isEmpty()) {
return;
}
- List<Program> programsToSchedule = SeriesRecordingScheduler.pickOneProgramPerEpisode(
- mDvrDataManager, Collections.singletonList(mSeriesRecording), recordingCandidates)
- .get(mSeriesRecordingId);
+ List<Program> programsToSchedule =
+ SeriesRecordingScheduler.pickOneProgramPerEpisode(
+ mDvrDataManager,
+ Collections.singletonList(mSeriesRecording),
+ recordingCandidates)
+ .get(mSeriesRecordingId);
if (!programsToSchedule.isEmpty()) {
- TvApplication.getSingletons(getContext()).getDvrManager()
+ TvSingletons.getSingletons(getContext())
+ .getDvrManager()
.addScheduleToSeriesRecording(mSeriesRecording, programsToSchedule);
}
}
private List<GuidedAction> buildChannelSubAction() {
List<GuidedAction> channelSubActions = new ArrayList<>();
- channelSubActions.add(new GuidedAction.Builder(getActivity())
- .id(SUB_ACTION_ID_CHANNEL_ALL)
- .title(mChannelsActionAllText)
- .build());
+ channelSubActions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(SUB_ACTION_ID_CHANNEL_ALL)
+ .title(mChannelsActionAllText)
+ .build());
for (Channel channel : mChannels) {
- channelSubActions.add(new GuidedAction.Builder(getActivity())
- .id(SUB_ACTION_ID_CHANNEL_ONE_BASE + channel.getId())
- .title(channel.getDisplayText())
- .build());
+ channelSubActions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(SUB_ACTION_ID_CHANNEL_ONE_BASE + channel.getId())
+ .title(channel.getDisplayText())
+ .build());
}
return channelSubActions;
}
private void showConfirmDialog() {
- DvrUiHelper.StartSeriesScheduledDialogActivity(getContext(), mSeriesRecording,
- mShowViewScheduleOptionInDialog, mPrograms);
+ DvrUiHelper.startSeriesScheduledDialogActivity(
+ getContext(), mSeriesRecording, mShowViewScheduleOptionInDialog, mPrograms);
finishGuidedStepFragments();
}
@Override
- public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) { }
+ public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) {}
@Override
public void onSeriesRecordingRemoved(SeriesRecording... seriesRecordings) {
@@ -363,4 +384,4 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/DvrStopRecordingFragment.java b/src/com/android/tv/dvr/ui/DvrStopRecordingFragment.java
index baa45793..e93387ab 100644
--- a/src/com/android/tv/dvr/ui/DvrStopRecordingFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrStopRecordingFragment.java
@@ -25,45 +25,36 @@ import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
import android.support.v17.leanback.widget.GuidedAction;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrDataManager.ScheduledRecordingListener;
import com.android.tv.dvr.data.ScheduledRecording;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
/**
* A fragment which asks the user to make a recording schedule for the program.
- * <p>
- * If the program belongs to a series and the series recording is not created yet, we will show the
- * option to record all the episodes of the series.
+ *
+ * <p>If the program belongs to a series and the series recording is not created yet, we will show
+ * the option to record all the episodes of the series.
*/
@TargetApi(Build.VERSION_CODES.N)
public class DvrStopRecordingFragment extends DvrGuidedStepFragment {
- /**
- * The action ID for the stop action.
- */
+ /** The action ID for the stop action. */
public static final int ACTION_STOP = 1;
- /**
- * Key for the program.
- * Type: {@link com.android.tv.data.Program}.
- */
+ /** Key for the program. Type: {@link com.android.tv.data.Program}. */
public static final String KEY_REASON = "DvrStopRecordingFragment.type";
@Retention(RetentionPolicy.SOURCE)
@IntDef({REASON_USER_STOP, REASON_ON_CONFLICT})
public @interface ReasonType {}
- /**
- * The dialog is shown because users want to stop some currently recording program.
- */
+ /** The dialog is shown because users want to stop some currently recording program. */
public static final int REASON_USER_STOP = 1;
/**
- * The dialog is shown because users want to record some program that is conflict to the
- * current recording program.
+ * The dialog is shown because users want to record some program that is conflict to the current
+ * recording program.
*/
public static final int REASON_ON_CONFLICT = 2;
@@ -74,7 +65,7 @@ public class DvrStopRecordingFragment extends DvrGuidedStepFragment {
private final ScheduledRecordingListener mScheduledRecordingListener =
new ScheduledRecordingListener() {
@Override
- public void onScheduledRecordingAdded(ScheduledRecording... schedules) { }
+ public void onScheduledRecordingAdded(ScheduledRecording... schedules) {}
@Override
public void onScheduledRecordingRemoved(ScheduledRecording... schedules) {
@@ -91,7 +82,7 @@ public class DvrStopRecordingFragment extends DvrGuidedStepFragment {
for (ScheduledRecording schedule : schedules) {
if (schedule.getId() == mSchedule.getId()
&& schedule.getState()
- != ScheduledRecording.STATE_RECORDING_IN_PROGRESS) {
+ != ScheduledRecording.STATE_RECORDING_IN_PROGRESS) {
dismissDialog();
return;
}
@@ -109,7 +100,7 @@ public class DvrStopRecordingFragment extends DvrGuidedStepFragment {
dismissDialog();
return;
}
- mDvrDataManager = TvApplication.getSingletons(context).getDvrDataManager();
+ mDvrDataManager = TvSingletons.getSingletons(context).getDvrDataManager();
mDvrDataManager.addScheduledRecordingListener(mScheduledRecordingListener);
mStopReason = args.getInt(KEY_REASON);
}
@@ -128,8 +119,10 @@ public class DvrStopRecordingFragment extends DvrGuidedStepFragment {
String title = getString(R.string.dvr_stop_recording_dialog_title);
String description;
if (mStopReason == REASON_ON_CONFLICT) {
- description = getString(R.string.dvr_stop_recording_dialog_description_on_conflict,
- mSchedule.getProgramDisplayTitle(getContext()));
+ description =
+ getString(
+ R.string.dvr_stop_recording_dialog_description_on_conflict,
+ mSchedule.getProgramDisplayTitle(getContext()));
} else {
description = getString(R.string.dvr_stop_recording_dialog_description);
}
@@ -140,13 +133,15 @@ public class DvrStopRecordingFragment extends DvrGuidedStepFragment {
@Override
public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
Context context = getContext();
- actions.add(new GuidedAction.Builder(context)
- .id(ACTION_STOP)
- .title(R.string.dvr_action_stop)
- .build());
- actions.add(new GuidedAction.Builder(context)
- .clickAction(GuidedAction.ACTION_ID_CANCEL)
- .build());
+ actions.add(
+ new GuidedAction.Builder(context)
+ .id(ACTION_STOP)
+ .title(R.string.dvr_action_stop)
+ .build());
+ actions.add(
+ new GuidedAction.Builder(context)
+ .clickAction(GuidedAction.ACTION_ID_CANCEL)
+ .build());
}
@Override
@@ -163,4 +158,4 @@ public class DvrStopRecordingFragment extends DvrGuidedStepFragment {
return super.getTrackerLabelForGuidedAction(action);
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/DvrStopSeriesRecordingDialogFragment.java b/src/com/android/tv/dvr/ui/DvrStopSeriesRecordingDialogFragment.java
index 5b880bd6..15abf902 100644
--- a/src/com/android/tv/dvr/ui/DvrStopSeriesRecordingDialogFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrStopSeriesRecordingDialogFragment.java
@@ -22,18 +22,15 @@ import android.support.v17.leanback.app.GuidedStepFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-
import com.android.tv.R;
-/**
- * A dialog fragment which contains {@link DvrStopSeriesRecordingFragment}.
- */
+/** A dialog fragment which contains {@link DvrStopSeriesRecordingFragment}. */
public class DvrStopSeriesRecordingDialogFragment extends DialogFragment {
public static final String DIALOG_TAG = "dialog_tag";
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.halfsized_dialog, container, false);
GuidedStepFragment fragment = new DvrStopSeriesRecordingFragment();
fragment.setArguments(getArguments());
diff --git a/src/com/android/tv/dvr/ui/DvrStopSeriesRecordingFragment.java b/src/com/android/tv/dvr/ui/DvrStopSeriesRecordingFragment.java
index 7b56cfc1..99211fdb 100644
--- a/src/com/android/tv/dvr/ui/DvrStopSeriesRecordingFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrStopSeriesRecordingFragment.java
@@ -25,25 +25,18 @@ import android.support.v17.leanback.widget.GuidedAction;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-
-import com.android.tv.ApplicationSingletons;
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.data.SeriesRecording;
-
import java.util.ArrayList;
import java.util.List;
-/**
- * A fragment which asks the user to stop series recording.
- */
+/** A fragment which asks the user to stop series recording. */
public class DvrStopSeriesRecordingFragment extends DvrGuidedStepFragment {
- /**
- * Key for the series recording to be stopped.
- */
+ /** Key for the series recording to be stopped. */
public static final String KEY_SERIES_RECORDING = "key_series_recoridng";
private static final int ACTION_STOP_SERIES_RECORDING = 1;
@@ -51,7 +44,8 @@ public class DvrStopSeriesRecordingFragment extends DvrGuidedStepFragment {
private SeriesRecording mSeriesRecording;
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mSeriesRecording = getArguments().getParcelable(KEY_SERIES_RECORDING);
return super.onCreateView(inflater, container, savedInstanceState);
}
@@ -68,19 +62,21 @@ public class DvrStopSeriesRecordingFragment extends DvrGuidedStepFragment {
@Override
public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
Activity activity = getActivity();
- actions.add(new GuidedAction.Builder(activity)
- .id(ACTION_STOP_SERIES_RECORDING)
- .title(R.string.dvr_series_schedules_stop_dialog_action_stop)
- .build());
- actions.add(new GuidedAction.Builder(activity)
- .clickAction(GuidedAction.ACTION_ID_CANCEL)
- .build());
+ actions.add(
+ new GuidedAction.Builder(activity)
+ .id(ACTION_STOP_SERIES_RECORDING)
+ .title(R.string.dvr_series_schedules_stop_dialog_action_stop)
+ .build());
+ actions.add(
+ new GuidedAction.Builder(activity)
+ .clickAction(GuidedAction.ACTION_ID_CANCEL)
+ .build());
}
@Override
public void onTrackedGuidedActionClicked(GuidedAction action) {
if (action.getId() == ACTION_STOP_SERIES_RECORDING) {
- ApplicationSingletons singletons = TvApplication.getSingletons(getContext());
+ TvSingletons singletons = TvSingletons.getSingletons(getContext());
DvrManager dvrManager = singletons.getDvrManager();
DvrDataManager dataManager = singletons.getDvrDataManager();
List<ScheduledRecording> toDelete = new ArrayList<>();
@@ -96,8 +92,10 @@ public class DvrStopSeriesRecordingFragment extends DvrGuidedStepFragment {
if (!toDelete.isEmpty()) {
dvrManager.forceRemoveScheduledRecording(ScheduledRecording.toArray(toDelete));
}
- dvrManager.updateSeriesRecording(SeriesRecording.buildFrom(mSeriesRecording)
- .setState(SeriesRecording.STATE_SERIES_STOPPED).build());
+ dvrManager.updateSeriesRecording(
+ SeriesRecording.buildFrom(mSeriesRecording)
+ .setState(SeriesRecording.STATE_SERIES_STOPPED)
+ .build());
}
dismissDialog();
}
diff --git a/src/com/android/tv/dvr/ui/DvrUiHelper.java b/src/com/android/tv/dvr/ui/DvrUiHelper.java
index 302fd6cd..16afbdef 100644
--- a/src/com/android/tv/dvr/ui/DvrUiHelper.java
+++ b/src/com/android/tv/dvr/ui/DvrUiHelper.java
@@ -37,17 +37,18 @@ import android.text.TextUtils;
import android.text.style.TextAppearanceSpan;
import android.widget.ImageView;
import android.widget.Toast;
-
import com.android.tv.MainActivity;
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
+import com.android.tv.common.BuildConfig;
import com.android.tv.common.SoftPreconditions;
+import com.android.tv.common.recording.RecordingStorageStatusManager;
+import com.android.tv.common.util.CommonUtils;
import com.android.tv.data.BaseProgram;
-import com.android.tv.data.Channel;
import com.android.tv.data.Program;
+import com.android.tv.data.api.Channel;
import com.android.tv.dialog.HalfSizedDialogFragment;
import com.android.tv.dvr.DvrManager;
-import com.android.tv.dvr.DvrStorageStatusManager;
import com.android.tv.dvr.data.RecordedProgram;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.data.SeriesRecording;
@@ -56,6 +57,7 @@ import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrAlreadyRecordedDialog
import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrAlreadyScheduledDialogFragment;
import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrChannelRecordDurationOptionDialogFragment;
import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrChannelWatchConflictDialogFragment;
+import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrFutureProgramInfoDialogFragment;
import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrInsufficientSpaceErrorDialogFragment;
import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrMissingStorageErrorDialogFragment;
import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrNoFreeSpaceErrorDialogFragment;
@@ -65,21 +67,19 @@ import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrSmallSizedStorageErro
import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrStopRecordingDialogFragment;
import com.android.tv.dvr.ui.browse.DvrBrowseActivity;
import com.android.tv.dvr.ui.browse.DvrDetailsActivity;
+import com.android.tv.dvr.ui.list.DvrHistoryActivity;
import com.android.tv.dvr.ui.list.DvrSchedulesActivity;
import com.android.tv.dvr.ui.list.DvrSchedulesFragment;
import com.android.tv.dvr.ui.list.DvrSeriesSchedulesFragment;
import com.android.tv.dvr.ui.playback.DvrPlaybackActivity;
import com.android.tv.util.ToastUtils;
import com.android.tv.util.Utils;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
-/**
- * A helper class for DVR UI.
- */
+/** A helper class for DVR UI. */
@MainThread
@TargetApi(Build.VERSION_CODES.N)
public class DvrUiHelper {
@@ -91,45 +91,43 @@ public class DvrUiHelper {
* Checks if the storage status is good for recording and shows error messages if needed.
*
* @param recordingRequestRunnable if the storage status is OK to record or users choose to
- * perform the operation anyway, this Runnable will run.
+ * perform the operation anyway, this Runnable will run.
*/
- public static void checkStorageStatusAndShowErrorMessage(Activity activity, String inputId,
- Runnable recordingRequestRunnable) {
- if (Utils.isBundledInput(inputId)) {
- switch (TvApplication.getSingletons(activity).getDvrStorageStatusManager()
+ public static void checkStorageStatusAndShowErrorMessage(
+ Activity activity, String inputId, Runnable recordingRequestRunnable) {
+ if (CommonUtils.isBundledInput(inputId)) {
+ switch (TvSingletons.getSingletons(activity)
+ .getRecordingStorageStatusManager()
.getDvrStorageStatus()) {
- case DvrStorageStatusManager.STORAGE_STATUS_TOTAL_CAPACITY_TOO_SMALL:
+ case RecordingStorageStatusManager.STORAGE_STATUS_TOTAL_CAPACITY_TOO_SMALL:
showDvrSmallSizedStorageErrorDialog(activity);
return;
- case DvrStorageStatusManager.STORAGE_STATUS_MISSING:
+ case RecordingStorageStatusManager.STORAGE_STATUS_MISSING:
showDvrMissingStorageErrorDialog(activity);
return;
- case DvrStorageStatusManager.STORAGE_STATUS_FREE_SPACE_INSUFFICIENT:
+ case RecordingStorageStatusManager.STORAGE_STATUS_FREE_SPACE_INSUFFICIENT:
showDvrNoFreeSpaceErrorDialog(activity, recordingRequestRunnable);
return;
+ default: // fall out
}
}
recordingRequestRunnable.run();
}
- /**
- * Shows the schedule dialog.
- */
- public static void showScheduleDialog(Activity activity, Program program,
- boolean addCurrentProgramToSeries) {
+ /** Shows the schedule dialog. */
+ public static void showScheduleDialog(
+ Activity activity, Program program, boolean addCurrentProgramToSeries) {
if (SoftPreconditions.checkNotNull(program) == null) {
return;
}
Bundle args = new Bundle();
args.putParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM, program);
- args.putBoolean(DvrScheduleFragment.KEY_ADD_CURRENT_PROGRAM_TO_SERIES,
- addCurrentProgramToSeries);
+ args.putBoolean(
+ DvrScheduleFragment.KEY_ADD_CURRENT_PROGRAM_TO_SERIES, addCurrentProgramToSeries);
showDialogFragment(activity, new DvrScheduleDialogFragment(), args, true, true);
}
- /**
- * Shows the recording duration options dialog.
- */
+ /** Shows the recording duration options dialog. */
public static void showChannelRecordDurationOptions(Activity activity, Channel channel) {
if (SoftPreconditions.checkNotNull(channel) == null) {
return;
@@ -139,9 +137,7 @@ public class DvrUiHelper {
showDialogFragment(activity, new DvrChannelRecordDurationOptionDialogFragment(), args);
}
- /**
- * Shows the dialog which says that the new schedule conflicts with others.
- */
+ /** Shows the dialog which says that the new schedule conflicts with others. */
public static void showScheduleConflictDialog(Activity activity, Program program) {
if (program == null) {
return;
@@ -151,9 +147,7 @@ public class DvrUiHelper {
showDialogFragment(activity, new DvrProgramConflictDialogFragment(), args, false, true);
}
- /**
- * Shows the conflict dialog for the channel watching.
- */
+ /** Shows the conflict dialog for the channel watching. */
public static void showChannelWatchConflictDialog(MainActivity activity, Channel channel) {
if (channel == null) {
return;
@@ -163,63 +157,60 @@ public class DvrUiHelper {
showDialogFragment(activity, new DvrChannelWatchConflictDialogFragment(), args);
}
- /**
- * Shows DVR insufficient space error dialog.
- */
- public static void showDvrInsufficientSpaceErrorDialog(MainActivity activity,
- Set<String> failedScheduledRecordingInfoSet) {
+ /** Shows DVR insufficient space error dialog. */
+ public static void showDvrInsufficientSpaceErrorDialog(
+ MainActivity activity, Set<String> failedScheduledRecordingInfoSet) {
Bundle args = new Bundle();
ArrayList<String> failedScheduledRecordingInfoArray =
new ArrayList<>(failedScheduledRecordingInfoSet);
- args.putStringArrayList(DvrInsufficientSpaceErrorFragment.FAILED_SCHEDULED_RECORDING_INFOS,
+ args.putStringArrayList(
+ DvrInsufficientSpaceErrorFragment.FAILED_SCHEDULED_RECORDING_INFOS,
failedScheduledRecordingInfoArray);
showDialogFragment(activity, new DvrInsufficientSpaceErrorDialogFragment(), args);
- Utils.clearRecordingFailedReason(activity,
- TvInputManager.RECORDING_ERROR_INSUFFICIENT_SPACE);
+ Utils.clearRecordingFailedReason(
+ activity, TvInputManager.RECORDING_ERROR_INSUFFICIENT_SPACE);
Utils.clearFailedScheduledRecordingInfoSet(activity);
}
/**
* Shows DVR no free space error dialog.
*
- * @param recordingRequestRunnable the recording request to be executed when users choose
- * {@link DvrGuidedStepFragment#ACTION_RECORD_ANYWAY}.
+ * @param recordingRequestRunnable the recording request to be executed when users choose {@link
+ * DvrGuidedStepFragment#ACTION_RECORD_ANYWAY}.
*/
- public static void showDvrNoFreeSpaceErrorDialog(Activity activity,
- Runnable recordingRequestRunnable) {
+ public static void showDvrNoFreeSpaceErrorDialog(
+ Activity activity, Runnable recordingRequestRunnable) {
DvrHalfSizedDialogFragment fragment = new DvrNoFreeSpaceErrorDialogFragment();
- fragment.setOnActionClickListener(new HalfSizedDialogFragment.OnActionClickListener() {
- @Override
- public void onActionClick(long actionId) {
- if (actionId == DvrGuidedStepFragment.ACTION_RECORD_ANYWAY) {
- recordingRequestRunnable.run();
- } else if (actionId == DvrGuidedStepFragment.ACTION_DELETE_RECORDINGS) {
- Intent intent = new Intent(activity, DvrBrowseActivity.class);
- activity.startActivity(intent);
- }
- }
- });
+ fragment.setOnActionClickListener(
+ new HalfSizedDialogFragment.OnActionClickListener() {
+ @Override
+ public void onActionClick(long actionId) {
+ if (actionId == DvrGuidedStepFragment.ACTION_RECORD_ANYWAY) {
+ recordingRequestRunnable.run();
+ } else if (actionId == DvrGuidedStepFragment.ACTION_DELETE_RECORDINGS) {
+ Intent intent = new Intent(activity, DvrBrowseActivity.class);
+ activity.startActivity(intent);
+ }
+ }
+ });
showDialogFragment(activity, fragment, null);
}
- /**
- * Shows DVR missing storage error dialog.
- */
+ /** Shows DVR missing storage error dialog. */
private static void showDvrMissingStorageErrorDialog(Activity activity) {
showDialogFragment(activity, new DvrMissingStorageErrorDialogFragment(), null);
}
- /**
- * Shows DVR small sized storage error dialog.
- */
+ /** Shows DVR small sized storage error dialog. */
public static void showDvrSmallSizedStorageErrorDialog(Activity activity) {
showDialogFragment(activity, new DvrSmallSizedStorageErrorDialogFragment(), null);
}
- /**
- * Shows stop recording dialog.
- */
- public static void showStopRecordingDialog(Activity activity, long channelId, int reason,
+ /** Shows stop recording dialog. */
+ public static void showStopRecordingDialog(
+ Activity activity,
+ long channelId,
+ int reason,
HalfSizedDialogFragment.OnActionClickListener listener) {
Bundle args = new Bundle();
args.putLong(DvrHalfSizedDialogFragment.KEY_CHANNEL_ID, channelId);
@@ -229,9 +220,7 @@ public class DvrUiHelper {
showDialogFragment(activity, fragment, args);
}
- /**
- * Shows "already scheduled" dialog.
- */
+ /** Shows "already scheduled" dialog. */
public static void showAlreadyScheduleDialog(Activity activity, Program program) {
if (program == null) {
return;
@@ -241,9 +230,7 @@ public class DvrUiHelper {
showDialogFragment(activity, new DvrAlreadyScheduledDialogFragment(), args, false, true);
}
- /**
- * Shows "already recorded" dialog.
- */
+ /** Shows "already recorded" dialog. */
public static void showAlreadyRecordedDialog(Activity activity, Program program) {
if (program == null) {
return;
@@ -253,37 +240,49 @@ public class DvrUiHelper {
showDialogFragment(activity, new DvrAlreadyRecordedDialogFragment(), args, false, true);
}
+ /** Shows program information dialog. */
+ public static void showProgramInfoDialog(Activity activity, Program program) {
+ if (program == null || !BuildConfig.ENG) {
+ return;
+ }
+ Bundle args = new Bundle();
+ args.putParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM, program);
+ showDialogFragment(activity, new DvrFutureProgramInfoDialogFragment(), args, false, true);
+ }
+
/**
* Handle the request of recording a current program. It will handle creating schedules and
* shows the proper dialog and toast message respectively for timed-recording and program
* recording cases.
*
- * @param addProgramToSeries denotes whether the program to be recorded should be added into
- * the series recording when users choose to record the entire series.
+ * @param addProgramToSeries denotes whether the program to be recorded should be added into the
+ * series recording when users choose to record the entire series.
*/
- public static void requestRecordingCurrentProgram(Activity activity,
- Channel channel, Program program, boolean addProgramToSeries) {
+ public static void requestRecordingCurrentProgram(
+ Activity activity, Channel channel, Program program, boolean addProgramToSeries) {
if (program == null) {
DvrUiHelper.showChannelRecordDurationOptions(activity, channel);
} else if (DvrUiHelper.handleCreateSchedule(activity, program, addProgramToSeries)) {
- String msg = activity.getString(R.string.dvr_msg_current_program_scheduled,
- program.getTitle(), Utils.toTimeString(program.getEndTimeUtcMillis(), false));
+ String msg =
+ activity.getString(
+ R.string.dvr_msg_current_program_scheduled,
+ program.getTitle(),
+ Utils.toTimeString(program.getEndTimeUtcMillis(), false));
Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show();
}
}
/**
- * Handle the request of recording a future program. It will handle creating schedules and
- * shows the proper toast message.
+ * Handle the request of recording a future program. It will handle creating schedules and shows
+ * the proper toast message.
*
- * @param addProgramToSeries denotes whether the program to be recorded should be added into
- * the series recording when users choose to record the entire series.
+ * @param addProgramToSeries denotes whether the program to be recorded should be added into the
+ * series recording when users choose to record the entire series.
*/
- public static void requestRecordingFutureProgram(Activity activity,
- Program program, boolean addProgramToSeries) {
+ public static void requestRecordingFutureProgram(
+ Activity activity, Program program, boolean addProgramToSeries) {
if (DvrUiHelper.handleCreateSchedule(activity, program, addProgramToSeries)) {
- String msg = activity.getString(
- R.string.dvr_msg_program_scheduled, program.getTitle());
+ String msg = activity.getString(R.string.dvr_msg_program_scheduled, program.getTitle());
ToastUtils.show(activity, msg, Toast.LENGTH_SHORT);
}
}
@@ -292,12 +291,12 @@ public class DvrUiHelper {
* Handles the action to create the new schedule. It returns {@code true} if the schedule is
* added and there's no additional UI, otherwise {@code false}.
*/
- private static boolean handleCreateSchedule(Activity activity, Program program,
- boolean addProgramToSeries) {
+ private static boolean handleCreateSchedule(
+ Activity activity, Program program, boolean addProgramToSeries) {
if (program == null) {
return false;
}
- DvrManager dvrManager = TvApplication.getSingletons(activity).getDvrManager();
+ DvrManager dvrManager = TvSingletons.getSingletons(activity).getDvrManager();
if (!program.isEpisodic()) {
// One time recording.
dvrManager.addSchedule(program);
@@ -307,18 +306,24 @@ public class DvrUiHelper {
}
} else {
// Show recorded program rather than the schedule.
- RecordedProgram recordedProgram = dvrManager.getRecordedProgram(program.getTitle(),
- program.getSeasonNumber(), program.getEpisodeNumber());
+ RecordedProgram recordedProgram =
+ dvrManager.getRecordedProgram(
+ program.getTitle(),
+ program.getSeasonNumber(),
+ program.getEpisodeNumber());
if (recordedProgram != null) {
DvrUiHelper.showAlreadyRecordedDialog(activity, program);
return false;
}
- ScheduledRecording duplicate = dvrManager.getScheduledRecording(program.getTitle(),
- program.getSeasonNumber(), program.getEpisodeNumber());
+ ScheduledRecording duplicate =
+ dvrManager.getScheduledRecording(
+ program.getTitle(),
+ program.getSeasonNumber(),
+ program.getEpisodeNumber());
if (duplicate != null
&& (duplicate.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED
- || duplicate.getState()
- == ScheduledRecording.STATE_RECORDING_IN_PROGRESS)) {
+ || duplicate.getState()
+ == ScheduledRecording.STATE_RECORDING_IN_PROGRESS)) {
DvrUiHelper.showAlreadyScheduleDialog(activity, program);
return false;
}
@@ -334,39 +339,44 @@ public class DvrUiHelper {
return true;
}
- private static void showDialogFragment(Activity activity,
- DvrHalfSizedDialogFragment dialogFragment, Bundle args) {
+ private static void showDialogFragment(
+ Activity activity, DvrHalfSizedDialogFragment dialogFragment, Bundle args) {
showDialogFragment(activity, dialogFragment, args, false, false);
}
- private static void showDialogFragment(Activity activity,
- DvrHalfSizedDialogFragment dialogFragment, Bundle args, boolean keepSidePanelHistory,
+ private static void showDialogFragment(
+ Activity activity,
+ DvrHalfSizedDialogFragment dialogFragment,
+ Bundle args,
+ boolean keepSidePanelHistory,
boolean keepProgramGuide) {
dialogFragment.setArguments(args);
if (activity instanceof MainActivity) {
- ((MainActivity) activity).getOverlayManager()
- .showDialogFragment(DvrHalfSizedDialogFragment.DIALOG_TAG, dialogFragment,
- keepSidePanelHistory, keepProgramGuide);
+ ((MainActivity) activity)
+ .getOverlayManager()
+ .showDialogFragment(
+ DvrHalfSizedDialogFragment.DIALOG_TAG,
+ dialogFragment,
+ keepSidePanelHistory,
+ keepProgramGuide);
} else {
- dialogFragment.show(activity.getFragmentManager(),
- DvrHalfSizedDialogFragment.DIALOG_TAG);
+ dialogFragment.show(
+ activity.getFragmentManager(), DvrHalfSizedDialogFragment.DIALOG_TAG);
}
}
- /**
- * Checks whether channel watch conflict dialog is open or not.
- */
+ /** Checks whether channel watch conflict dialog is open or not. */
public static boolean isChannelWatchConflictDialogShown(MainActivity activity) {
- return activity.getOverlayManager().getCurrentDialog() instanceof
- DvrChannelWatchConflictDialogFragment;
+ return activity.getOverlayManager().getCurrentDialog()
+ instanceof DvrChannelWatchConflictDialogFragment;
}
- private static ScheduledRecording getEarliestScheduledRecording(List<ScheduledRecording>
- recordings) {
+ private static ScheduledRecording getEarliestScheduledRecording(
+ List<ScheduledRecording> recordings) {
ScheduledRecording earlistScheduledRecording = null;
if (!recordings.isEmpty()) {
- Collections.sort(recordings,
- ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR);
+ Collections.sort(
+ recordings, ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR);
earlistScheduledRecording = recordings.get(0);
}
return earlistScheduledRecording;
@@ -378,10 +388,10 @@ public class DvrUiHelper {
* @param programId the ID of the recorded program going to be played.
* @param seekTimeMs the seek position to initial playback.
* @param pinChecked {@code true} if the pin code for parental controls has already been
- * verified, otherwise {@code false}.
+ * verified, otherwise {@code false}.
*/
- public static void startPlaybackActivity(Context context, long programId,
- long seekTimeMs, boolean pinChecked) {
+ public static void startPlaybackActivity(
+ Context context, long programId, long seekTimeMs, boolean pinChecked) {
Intent intent = new Intent(context, DvrPlaybackActivity.class);
intent.putExtra(Utils.EXTRA_KEY_RECORDED_PROGRAM_ID, programId);
if (seekTimeMs != TvInputManager.TIME_SHIFT_INVALID_TIME) {
@@ -391,51 +401,52 @@ public class DvrUiHelper {
context.startActivity(intent);
}
- /**
- * Shows the schedules activity to resolve the tune conflict.
- */
+ /** Shows the schedules activity to resolve the tune conflict. */
public static void startSchedulesActivityForTuneConflict(Context context, Channel channel) {
if (channel == null) {
return;
}
- List<ScheduledRecording> conflicts = TvApplication.getSingletons(context).getDvrManager()
- .getConflictingSchedulesForTune(channel.getId());
+ List<ScheduledRecording> conflicts =
+ TvSingletons.getSingletons(context)
+ .getDvrManager()
+ .getConflictingSchedulesForTune(channel.getId());
startSchedulesActivity(context, getEarliestScheduledRecording(conflicts));
}
- /**
- * Shows the schedules activity to resolve the one time recording conflict.
- */
- public static void startSchedulesActivityForOneTimeRecordingConflict(Context context,
- List<ScheduledRecording> conflicts) {
+ /** Shows the schedules activity to resolve the one time recording conflict. */
+ public static void startSchedulesActivityForOneTimeRecordingConflict(
+ Context context, List<ScheduledRecording> conflicts) {
startSchedulesActivity(context, getEarliestScheduledRecording(conflicts));
}
- /**
- * Shows the schedules activity with full schedule.
- */
- public static void startSchedulesActivity(Context context, ScheduledRecording
- focusedScheduledRecording) {
+ /** Shows the schedules activity with full schedule. */
+ public static void startDvrHistoryActivity(Context context) {
+ Intent intent = new Intent(context, DvrHistoryActivity.class);
+ context.startActivity(intent);
+ }
+
+ /** Shows the schedules activity with full schedule. */
+ public static void startSchedulesActivity(
+ Context context, ScheduledRecording focusedScheduledRecording) {
Intent intent = new Intent(context, DvrSchedulesActivity.class);
- intent.putExtra(DvrSchedulesActivity.KEY_SCHEDULES_TYPE,
- DvrSchedulesActivity.TYPE_FULL_SCHEDULE);
+ intent.putExtra(
+ DvrSchedulesActivity.KEY_SCHEDULES_TYPE, DvrSchedulesActivity.TYPE_FULL_SCHEDULE);
if (focusedScheduledRecording != null) {
- intent.putExtra(DvrSchedulesFragment.SCHEDULES_KEY_SCHEDULED_RECORDING,
+ intent.putExtra(
+ DvrSchedulesFragment.SCHEDULES_KEY_SCHEDULED_RECORDING,
focusedScheduledRecording);
}
context.startActivity(intent);
}
- /**
- * Shows the schedules activity for series recording.
- */
- public static void startSchedulesActivityForSeries(Context context,
- SeriesRecording seriesRecording) {
+ /** Shows the schedules activity for series recording. */
+ public static void startSchedulesActivityForSeries(
+ Context context, SeriesRecording seriesRecording) {
Intent intent = new Intent(context, DvrSchedulesActivity.class);
- intent.putExtra(DvrSchedulesActivity.KEY_SCHEDULES_TYPE,
- DvrSchedulesActivity.TYPE_SERIES_SCHEDULE);
- intent.putExtra(DvrSeriesSchedulesFragment.SERIES_SCHEDULES_KEY_SERIES_RECORDING,
- seriesRecording);
+ intent.putExtra(
+ DvrSchedulesActivity.KEY_SCHEDULES_TYPE, DvrSchedulesActivity.TYPE_SERIES_SCHEDULE);
+ intent.putExtra(
+ DvrSeriesSchedulesFragment.SERIES_SCHEDULES_KEY_SERIES_RECORDING, seriesRecording);
context.startActivity(intent);
}
@@ -444,93 +455,125 @@ public class DvrUiHelper {
*
* @param programs list of programs which belong to the series.
*/
- public static void startSeriesSettingsActivity(Context context, long seriesRecordingId,
- @Nullable List<Program> programs, boolean removeEmptySeriesSchedule,
- boolean isWindowTranslucent, boolean showViewScheduleOptionInDialog,
+ public static void startSeriesSettingsActivity(
+ Context context,
+ long seriesRecordingId,
+ @Nullable List<Program> programs,
+ boolean removeEmptySeriesSchedule,
+ boolean isWindowTranslucent,
+ boolean showViewScheduleOptionInDialog,
Program currentProgram) {
- SeriesRecording series = TvApplication.getSingletons(context).getDvrDataManager()
- .getSeriesRecording(seriesRecordingId);
+ SeriesRecording series =
+ TvSingletons.getSingletons(context)
+ .getDvrDataManager()
+ .getSeriesRecording(seriesRecordingId);
if (series == null) {
return;
}
if (programs != null) {
- startSeriesSettingsActivityInternal(context, seriesRecordingId, programs,
- removeEmptySeriesSchedule, isWindowTranslucent,
- showViewScheduleOptionInDialog, currentProgram);
+ startSeriesSettingsActivityInternal(
+ context,
+ seriesRecordingId,
+ programs,
+ removeEmptySeriesSchedule,
+ isWindowTranslucent,
+ showViewScheduleOptionInDialog,
+ currentProgram);
} else {
EpisodicProgramLoadTask episodicProgramLoadTask =
new EpisodicProgramLoadTask(context, series) {
- @Override
- protected void onPostExecute(List<Program> loadedPrograms) {
- sProgressDialog.dismiss();
- sProgressDialog = null;
- startSeriesSettingsActivityInternal(context, seriesRecordingId,
- loadedPrograms == null ? Collections.EMPTY_LIST : loadedPrograms,
- removeEmptySeriesSchedule, isWindowTranslucent,
- showViewScheduleOptionInDialog, currentProgram);
- }
- }.setLoadCurrentProgram(true)
- .setLoadDisallowedProgram(true)
- .setLoadScheduledEpisode(true)
- .setIgnoreChannelOption(true);
- sProgressDialog = ProgressDialog.show(context, null, context.getString(
- R.string.dvr_series_progress_message_reading_programs), true, true,
- new DialogInterface.OnCancelListener() {
@Override
- public void onCancel(DialogInterface dialogInterface) {
- episodicProgramLoadTask.cancel(true);
+ protected void onPostExecute(List<Program> loadedPrograms) {
+ sProgressDialog.dismiss();
sProgressDialog = null;
+ startSeriesSettingsActivityInternal(
+ context,
+ seriesRecordingId,
+ loadedPrograms == null
+ ? Collections.EMPTY_LIST
+ : loadedPrograms,
+ removeEmptySeriesSchedule,
+ isWindowTranslucent,
+ showViewScheduleOptionInDialog,
+ currentProgram);
}
- });
+ }.setLoadCurrentProgram(true)
+ .setLoadDisallowedProgram(true)
+ .setLoadScheduledEpisode(true)
+ .setIgnoreChannelOption(true);
+ sProgressDialog =
+ ProgressDialog.show(
+ context,
+ null,
+ context.getString(
+ R.string.dvr_series_progress_message_reading_programs),
+ true,
+ true,
+ new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialogInterface) {
+ episodicProgramLoadTask.cancel(true);
+ sProgressDialog = null;
+ }
+ });
episodicProgramLoadTask.execute();
}
}
- private static void startSeriesSettingsActivityInternal(Context context, long seriesRecordingId,
- @NonNull List<Program> programs, boolean removeEmptySeriesSchedule,
- boolean isWindowTranslucent, boolean showViewScheduleOptionInDialog,
+ private static void startSeriesSettingsActivityInternal(
+ Context context,
+ long seriesRecordingId,
+ @NonNull List<Program> programs,
+ boolean removeEmptySeriesSchedule,
+ boolean isWindowTranslucent,
+ boolean showViewScheduleOptionInDialog,
Program currentProgram) {
- SoftPreconditions.checkState(programs != null,
- TAG, "Start series settings activity but programs is null");
+ SoftPreconditions.checkState(
+ programs != null, TAG, "Start series settings activity but programs is null");
Intent intent = new Intent(context, DvrSeriesSettingsActivity.class);
intent.putExtra(DvrSeriesSettingsActivity.SERIES_RECORDING_ID, seriesRecordingId);
BigArguments.reset();
BigArguments.setArgument(DvrSeriesSettingsActivity.PROGRAM_LIST, programs);
- intent.putExtra(DvrSeriesSettingsActivity.REMOVE_EMPTY_SERIES_RECORDING,
- removeEmptySeriesSchedule);
+ intent.putExtra(
+ DvrSeriesSettingsActivity.REMOVE_EMPTY_SERIES_RECORDING, removeEmptySeriesSchedule);
intent.putExtra(DvrSeriesSettingsActivity.IS_WINDOW_TRANSLUCENT, isWindowTranslucent);
- intent.putExtra(DvrSeriesSettingsActivity.SHOW_VIEW_SCHEDULE_OPTION_IN_DIALOG,
+ intent.putExtra(
+ DvrSeriesSettingsActivity.SHOW_VIEW_SCHEDULE_OPTION_IN_DIALOG,
showViewScheduleOptionInDialog);
intent.putExtra(DvrSeriesSettingsActivity.CURRENT_PROGRAM, currentProgram);
context.startActivity(intent);
}
- /**
- * Shows "series recording scheduled" dialog activity.
- */
- public static void StartSeriesScheduledDialogActivity(Context context,
- SeriesRecording seriesRecording, boolean showViewScheduleOptionInDialog,
+ /** Shows "series recording scheduled" dialog activity. */
+ public static void startSeriesScheduledDialogActivity(
+ Context context,
+ SeriesRecording seriesRecording,
+ boolean showViewScheduleOptionInDialog,
List<Program> programs) {
if (seriesRecording == null) {
return;
}
Intent intent = new Intent(context, DvrSeriesScheduledDialogActivity.class);
- intent.putExtra(DvrSeriesScheduledDialogActivity.SERIES_RECORDING_ID,
- seriesRecording.getId());
- intent.putExtra(DvrSeriesScheduledDialogActivity.SHOW_VIEW_SCHEDULE_OPTION,
+ intent.putExtra(
+ DvrSeriesScheduledDialogActivity.SERIES_RECORDING_ID, seriesRecording.getId());
+ intent.putExtra(
+ DvrSeriesScheduledDialogActivity.SHOW_VIEW_SCHEDULE_OPTION,
showViewScheduleOptionInDialog);
BigArguments.reset();
- BigArguments.setArgument(DvrSeriesScheduledFragment.SERIES_SCHEDULED_KEY_PROGRAMS,
- programs);
+ BigArguments.setArgument(
+ DvrSeriesScheduledFragment.SERIES_SCHEDULED_KEY_PROGRAMS, programs);
context.startActivity(intent);
}
/**
- * Shows the details activity for the DVR items. The type of DVR items may be
- * {@link ScheduledRecording}, {@link RecordedProgram}, or {@link SeriesRecording}.
+ * Shows the details activity for the DVR items. The type of DVR items may be {@link
+ * ScheduledRecording}, {@link RecordedProgram}, or {@link SeriesRecording}.
*/
- public static void startDetailsActivity(Activity activity, Object dvrItem,
- @Nullable ImageView imageView, boolean hideViewSchedule) {
+ public static void startDetailsActivity(
+ Activity activity,
+ Object dvrItem,
+ @Nullable ImageView imageView,
+ boolean hideViewSchedule) {
if (dvrItem == null) {
return;
}
@@ -544,6 +587,17 @@ public class DvrUiHelper {
viewType = DvrDetailsActivity.SCHEDULED_RECORDING_VIEW;
} else if (schedule.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS) {
viewType = DvrDetailsActivity.CURRENT_RECORDING_VIEW;
+ } else if (schedule.getState() == ScheduledRecording.STATE_RECORDING_FINISHED
+ && schedule.getRecordedProgramId() != null) {
+ recordingId = schedule.getRecordedProgramId();
+ viewType = DvrDetailsActivity.RECORDED_PROGRAM_VIEW;
+ } else if (schedule.getState() == ScheduledRecording.STATE_RECORDING_FAILED) {
+ viewType = DvrDetailsActivity.SCHEDULED_RECORDING_VIEW;
+ hideViewSchedule = true;
+ // TODO(b/72638385): pass detailed error message
+ intent.putExtra(
+ DvrDetailsActivity.EXTRA_FAILED_MESSAGE,
+ activity.getString(R.string.dvr_recording_failed));
} else {
return;
}
@@ -561,89 +615,108 @@ public class DvrUiHelper {
intent.putExtra(DvrDetailsActivity.HIDE_VIEW_SCHEDULE, hideViewSchedule);
Bundle bundle = null;
if (imageView != null) {
- bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, imageView,
- DvrDetailsActivity.SHARED_ELEMENT_NAME).toBundle();
+ bundle =
+ ActivityOptionsCompat.makeSceneTransitionAnimation(
+ activity, imageView, DvrDetailsActivity.SHARED_ELEMENT_NAME)
+ .toBundle();
}
activity.startActivity(intent, bundle);
}
- /**
- * Shows the cancel all dialog for series schedules list.
- */
- public static void showCancelAllSeriesRecordingDialog(DvrSchedulesActivity activity,
- SeriesRecording seriesRecording) {
+ /** Shows the cancel all dialog for series schedules list. */
+ public static void showCancelAllSeriesRecordingDialog(
+ DvrSchedulesActivity activity, SeriesRecording seriesRecording) {
DvrStopSeriesRecordingDialogFragment dvrStopSeriesRecordingDialogFragment =
new DvrStopSeriesRecordingDialogFragment();
Bundle arguments = new Bundle();
- arguments.putParcelable(DvrStopSeriesRecordingFragment.KEY_SERIES_RECORDING,
- seriesRecording);
+ arguments.putParcelable(
+ DvrStopSeriesRecordingFragment.KEY_SERIES_RECORDING, seriesRecording);
dvrStopSeriesRecordingDialogFragment.setArguments(arguments);
- dvrStopSeriesRecordingDialogFragment.show(activity.getFragmentManager(),
- DvrStopSeriesRecordingDialogFragment.DIALOG_TAG);
+ dvrStopSeriesRecordingDialogFragment.show(
+ activity.getFragmentManager(), DvrStopSeriesRecordingDialogFragment.DIALOG_TAG);
}
- /**
- * Shows the series deletion activity.
- */
+ /** Shows the series deletion activity. */
public static void startSeriesDeletionActivity(Context context, long seriesRecordingId) {
Intent intent = new Intent(context, DvrSeriesDeletionActivity.class);
intent.putExtra(DvrSeriesDeletionActivity.SERIES_RECORDING_ID, seriesRecordingId);
context.startActivity(intent);
}
- public static void showAddScheduleToast(Context context,
- String title, long startTimeMs, long endTimeMs) {
- String msg = (startTimeMs > System.currentTimeMillis()) ?
- context.getString(R.string.dvr_msg_program_scheduled, title)
- : context.getString(R.string.dvr_msg_current_program_scheduled, title,
- Utils.toTimeString(endTimeMs, false));
+ public static void showAddScheduleToast(
+ Context context, String title, long startTimeMs, long endTimeMs) {
+ String msg =
+ (startTimeMs > System.currentTimeMillis())
+ ? context.getString(R.string.dvr_msg_program_scheduled, title)
+ : context.getString(
+ R.string.dvr_msg_current_program_scheduled,
+ title,
+ Utils.toTimeString(endTimeMs, false));
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}
- /**
- * Returns the styled schedule's title with its season and episode number.
- */
- public static CharSequence getStyledTitleWithEpisodeNumber(Context context,
- ScheduledRecording schedule, int episodeNumberStyleResId) {
- return getStyledTitleWithEpisodeNumber(context, schedule.getProgramTitle(),
- schedule.getSeasonNumber(), schedule.getEpisodeNumber(), episodeNumberStyleResId);
+ /** Returns the styled schedule's title with its season and episode number. */
+ public static CharSequence getStyledTitleWithEpisodeNumber(
+ Context context, ScheduledRecording schedule, int episodeNumberStyleResId) {
+ return getStyledTitleWithEpisodeNumber(
+ context,
+ schedule.getProgramTitle(),
+ schedule.getSeasonNumber(),
+ schedule.getEpisodeNumber(),
+ episodeNumberStyleResId);
}
- /**
- * Returns the styled program's title with its season and episode number.
- */
- public static CharSequence getStyledTitleWithEpisodeNumber(Context context,
- BaseProgram program, int episodeNumberStyleResId) {
- return getStyledTitleWithEpisodeNumber(context, program.getTitle(),
- program.getSeasonNumber(), program.getEpisodeNumber(), episodeNumberStyleResId);
+ /** Returns the styled program's title with its season and episode number. */
+ public static CharSequence getStyledTitleWithEpisodeNumber(
+ Context context, BaseProgram program, int episodeNumberStyleResId) {
+ return getStyledTitleWithEpisodeNumber(
+ context,
+ program.getTitle(),
+ program.getSeasonNumber(),
+ program.getEpisodeNumber(),
+ episodeNumberStyleResId);
}
@NonNull
- public static CharSequence getStyledTitleWithEpisodeNumber(Context context, String title,
- String seasonNumber, String episodeNumber, int episodeNumberStyleResId) {
+ public static CharSequence getStyledTitleWithEpisodeNumber(
+ Context context,
+ String title,
+ String seasonNumber,
+ String episodeNumber,
+ int episodeNumberStyleResId) {
if (TextUtils.isEmpty(title)) {
return "";
}
SpannableStringBuilder builder;
if (TextUtils.isEmpty(seasonNumber) || seasonNumber.equals("0")) {
- builder = TextUtils.isEmpty(episodeNumber) ? new SpannableStringBuilder(title) :
- new SpannableStringBuilder(Html.fromHtml(
- context.getString(R.string.program_title_with_episode_number_no_season,
- title, episodeNumber)));
+ builder =
+ TextUtils.isEmpty(episodeNumber)
+ ? new SpannableStringBuilder(title)
+ : new SpannableStringBuilder(Html.fromHtml(context.getString(
+ R.string.program_title_with_episode_number_no_season,
+ title,
+ episodeNumber)));
} else {
- builder = new SpannableStringBuilder(Html.fromHtml(
- context.getString(R.string.program_title_with_episode_number,
- title, seasonNumber, episodeNumber)));
+ builder =
+ new SpannableStringBuilder(
+ Html.fromHtml(
+ context.getString(
+ R.string.program_title_with_episode_number,
+ title,
+ seasonNumber,
+ episodeNumber)));
}
Object[] spans = builder.getSpans(0, builder.length(), Object.class);
if (spans.length > 0) {
if (episodeNumberStyleResId != 0) {
- builder.setSpan(new TextAppearanceSpan(context, episodeNumberStyleResId),
- builder.getSpanStart(spans[0]), builder.getSpanEnd(spans[0]),
+ builder.setSpan(
+ new TextAppearanceSpan(context, episodeNumberStyleResId),
+ builder.getSpanStart(spans[0]),
+ builder.getSpanEnd(spans[0]),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
builder.removeSpan(spans[0]);
}
return new SpannableString(builder);
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/FadeBackground.java b/src/com/android/tv/dvr/ui/FadeBackground.java
index 4f06ebcf..373daaaf 100644
--- a/src/com/android/tv/dvr/ui/FadeBackground.java
+++ b/src/com/android/tv/dvr/ui/FadeBackground.java
@@ -28,12 +28,9 @@ import android.transition.TransitionValues;
import android.transition.Visibility;
import android.util.AttributeSet;
import android.view.ViewGroup;
-
import com.android.tv.R;
-/**
- * This transition fades in/out of the background of the view by changing the background color.
- */
+/** This transition fades in/out of the background of the view by changing the background color. */
public class FadeBackground extends Transition {
private final int mMode;
@@ -45,22 +42,22 @@ public class FadeBackground extends Transition {
}
@Override
- public void captureStartValues(TransitionValues transitionValues) { }
+ public void captureStartValues(TransitionValues transitionValues) {}
@Override
- public void captureEndValues(TransitionValues transitionValues) { }
+ public void captureEndValues(TransitionValues transitionValues) {}
@Override
- public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
- TransitionValues endValues) {
+ public Animator createAnimator(
+ ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
if (startValues == null || endValues == null) {
return null;
}
Drawable background = endValues.view.getBackground();
if (background instanceof ColorDrawable) {
int color = ((ColorDrawable) background).getColor();
- int transparentColor = Color.argb(0, Color.red(color), Color.green(color),
- Color.blue(color));
+ int transparentColor =
+ Color.argb(0, Color.red(color), Color.green(color), Color.blue(color));
return mMode == Visibility.MODE_OUT
? ObjectAnimator.ofArgb(background, "color", transparentColor)
: ObjectAnimator.ofArgb(background, "color", transparentColor, color);
diff --git a/src/com/android/tv/dvr/ui/SortedArrayAdapter.java b/src/com/android/tv/dvr/ui/SortedArrayAdapter.java
index 8c0af9ed..1eb8080a 100644
--- a/src/com/android/tv/dvr/ui/SortedArrayAdapter.java
+++ b/src/com/android/tv/dvr/ui/SortedArrayAdapter.java
@@ -19,9 +19,7 @@ package com.android.tv.dvr.ui;
import android.support.annotation.VisibleForTesting;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.PresenterSelector;
-
import com.android.tv.common.SoftPreconditions;
-
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -45,8 +43,8 @@ public abstract class SortedArrayAdapter<T> extends ArrayObjectAdapter {
this(presenterSelector, comparator, Integer.MAX_VALUE);
}
- public SortedArrayAdapter(PresenterSelector presenterSelector, Comparator<T> comparator,
- int maxItemCount) {
+ public SortedArrayAdapter(
+ PresenterSelector presenterSelector, Comparator<T> comparator, int maxItemCount) {
super(presenterSelector);
mComparator = comparator;
mMaxItemCount = maxItemCount;
@@ -88,9 +86,8 @@ public abstract class SortedArrayAdapter<T> extends ArrayObjectAdapter {
* Adds an item in sorted order to the adapter.
*
* @param item The item to add in sorted order to the adapter.
- * @param insertToEnd If items are inserted in a more or less sorted fashion,
- * sets this parameter to {@code true} to search insertion position from
- * the end to save search time.
+ * @param insertToEnd If items are inserted in a more or less sorted fashion, sets this
+ * parameter to {@code true} to search insertion position from the end to save search time.
*/
public final void add(T item, boolean insertToEnd) {
long newItemId = getId(item);
@@ -127,9 +124,7 @@ public abstract class SortedArrayAdapter<T> extends ArrayObjectAdapter {
return removeWithId((T) item);
}
- /**
- * Removes an item which has the same ID as {@code item}.
- */
+ /** Removes an item which has the same ID as {@code item}. */
public boolean removeWithId(T item) {
int index = indexWithId(item);
return index >= 0 && index < size() && removeItems(index, 1) == 1;
@@ -166,6 +161,7 @@ public abstract class SortedArrayAdapter<T> extends ArrayObjectAdapter {
/**
* Changes an item in the list.
+ *
* @param item The item to change.
*/
public final void change(T item) {
@@ -181,9 +177,7 @@ public abstract class SortedArrayAdapter<T> extends ArrayObjectAdapter {
add(item);
}
- /**
- * Checks whether the item is in the list.
- */
+ /** Checks whether the item is in the list. */
public final boolean contains(T item) {
return indexWithId(item) != -1;
}
@@ -194,10 +188,10 @@ public abstract class SortedArrayAdapter<T> extends ArrayObjectAdapter {
}
/**
- * Returns the id of the the given {@code item}, which will be used in {@link #change} to
- * decide if the given item is already existed in the adapter.
+ * Returns the id of the the given {@code item}, which will be used in {@link #change} to decide
+ * if the given item is already existed in the adapter.
*
- * The id must be stable.
+ * <p>The id must be stable.
*/
protected abstract long getId(T item);
@@ -212,11 +206,9 @@ public abstract class SortedArrayAdapter<T> extends ArrayObjectAdapter {
return -1;
}
- /**
- * Finds the position that the given item should be inserted to keep the sorted order.
- */
+ /** Finds the position that the given item should be inserted to keep the sorted order. */
public int findInsertPosition(T item) {
- for (int i = size() - mExtraItemCount - 1; i >=0; i--) {
+ for (int i = size() - mExtraItemCount - 1; i >= 0; i--) {
T r = (T) get(i);
if (mComparator.compare(r, item) <= 0) {
return i + 1;
@@ -242,4 +234,4 @@ public abstract class SortedArrayAdapter<T> extends ArrayObjectAdapter {
}
return lb;
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/TrackedGuidedStepFragment.java b/src/com/android/tv/dvr/ui/TrackedGuidedStepFragment.java
index 5fe9c478..0172f76f 100644
--- a/src/com/android/tv/dvr/ui/TrackedGuidedStepFragment.java
+++ b/src/com/android/tv/dvr/ui/TrackedGuidedStepFragment.java
@@ -19,8 +19,7 @@ package com.android.tv.dvr.ui;
import android.content.Context;
import android.support.v17.leanback.app.GuidedStepFragment;
import android.support.v17.leanback.widget.GuidedAction;
-
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.analytics.Tracker;
/** A {@link GuidedStepFragment} with {@link Tracker} for analytics. */
@@ -30,7 +29,7 @@ public abstract class TrackedGuidedStepFragment extends GuidedStepFragment {
@Override
public void onAttach(Context context) {
super.onAttach(context);
- mTracker = TvApplication.getSingletons(context).getAnalytics().getDefaultTracker();
+ mTracker = TvSingletons.getSingletons(context).getAnalytics().getDefaultTracker();
}
@Override
diff --git a/src/com/android/tv/dvr/ui/browse/ActionPresenterSelector.java b/src/com/android/tv/dvr/ui/browse/ActionPresenterSelector.java
index 38a78f5d..f3a6fea4 100644
--- a/src/com/android/tv/dvr/ui/browse/ActionPresenterSelector.java
+++ b/src/com/android/tv/dvr/ui/browse/ActionPresenterSelector.java
@@ -32,8 +32,8 @@ import android.widget.Button;
class ActionPresenterSelector extends PresenterSelector {
private final Presenter mOneLineActionPresenter = new OneLineActionPresenter();
private final Presenter mTwoLineActionPresenter = new TwoLineActionPresenter();
- private final Presenter[] mPresenters = new Presenter[] {
- mOneLineActionPresenter, mTwoLineActionPresenter};
+ private final Presenter[] mPresenters =
+ new Presenter[] {mOneLineActionPresenter, mTwoLineActionPresenter};
@Override
public Presenter getPresenter(Object item) {
@@ -65,8 +65,9 @@ class ActionPresenterSelector extends PresenterSelector {
class OneLineActionPresenter extends Presenter {
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent) {
- View v = LayoutInflater.from(parent.getContext())
- .inflate(R.layout.lb_action_1_line, parent, false);
+ View v =
+ LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.lb_action_1_line, parent, false);
return new ActionViewHolder(v, parent.getLayoutDirection());
}
@@ -87,8 +88,9 @@ class ActionPresenterSelector extends PresenterSelector {
class TwoLineActionPresenter extends Presenter {
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent) {
- View v = LayoutInflater.from(parent.getContext())
- .inflate(R.layout.lb_action_2_lines, parent, false);
+ View v =
+ LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.lb_action_2_lines, parent, false);
return new ActionViewHolder(v, parent.getLayoutDirection());
}
@@ -100,14 +102,20 @@ class ActionPresenterSelector extends PresenterSelector {
vh.mAction = action;
if (icon != null) {
- final int startPadding = vh.view.getResources()
- .getDimensionPixelSize(R.dimen.lb_action_with_icon_padding_start);
- final int endPadding = vh.view.getResources()
- .getDimensionPixelSize(R.dimen.lb_action_with_icon_padding_end);
+ final int startPadding =
+ vh.view
+ .getResources()
+ .getDimensionPixelSize(R.dimen.lb_action_with_icon_padding_start);
+ final int endPadding =
+ vh.view
+ .getResources()
+ .getDimensionPixelSize(R.dimen.lb_action_with_icon_padding_end);
vh.view.setPaddingRelative(startPadding, 0, endPadding, 0);
} else {
- final int padding = vh.view.getResources()
- .getDimensionPixelSize(R.dimen.lb_action_padding_horizontal);
+ final int padding =
+ vh.view
+ .getResources()
+ .getDimensionPixelSize(R.dimen.lb_action_padding_horizontal);
vh.view.setPaddingRelative(padding, 0, padding, 0);
}
vh.mButton.setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null);
@@ -131,4 +139,4 @@ class ActionPresenterSelector extends PresenterSelector {
vh.mAction = null;
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/browse/CurrentRecordingDetailsFragment.java b/src/com/android/tv/dvr/ui/browse/CurrentRecordingDetailsFragment.java
index bf18ddc0..7e7e1f75 100644
--- a/src/com/android/tv/dvr/ui/browse/CurrentRecordingDetailsFragment.java
+++ b/src/com/android/tv/dvr/ui/browse/CurrentRecordingDetailsFragment.java
@@ -21,9 +21,8 @@ import android.content.res.Resources;
import android.support.v17.leanback.widget.Action;
import android.support.v17.leanback.widget.OnActionClickedListener;
import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.dialog.HalfSizedDialogFragment;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrManager;
@@ -31,9 +30,7 @@ import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.ui.DvrStopRecordingFragment;
import com.android.tv.dvr.ui.DvrUiHelper;
-/**
- * {@link RecordingDetailsFragment} for current recording in DVR.
- */
+/** {@link RecordingDetailsFragment} for current recording in DVR. */
public class CurrentRecordingDetailsFragment extends RecordingDetailsFragment {
private static final int ACTION_STOP_RECORDING = 1;
@@ -41,7 +38,7 @@ public class CurrentRecordingDetailsFragment extends RecordingDetailsFragment {
private final DvrDataManager.ScheduledRecordingListener mScheduledRecordingListener =
new DvrDataManager.ScheduledRecordingListener() {
@Override
- public void onScheduledRecordingAdded(ScheduledRecording... schedules) { }
+ public void onScheduledRecordingAdded(ScheduledRecording... schedules) {}
@Override
public void onScheduledRecordingRemoved(ScheduledRecording... schedules) {
@@ -58,7 +55,7 @@ public class CurrentRecordingDetailsFragment extends RecordingDetailsFragment {
for (ScheduledRecording schedule : schedules) {
if (schedule.getId() == getRecording().getId()
&& schedule.getState()
- != ScheduledRecording.STATE_RECORDING_IN_PROGRESS) {
+ != ScheduledRecording.STATE_RECORDING_IN_PROGRESS) {
getActivity().finish();
return;
}
@@ -69,7 +66,7 @@ public class CurrentRecordingDetailsFragment extends RecordingDetailsFragment {
@Override
public void onAttach(Context context) {
super.onAttach(context);
- mDvrDataManger = TvApplication.getSingletons(context).getDvrDataManager();
+ mDvrDataManger = TvSingletons.getSingletons(context).getDvrDataManager();
mDvrDataManger.addScheduledRecordingListener(mScheduledRecordingListener);
}
@@ -78,9 +75,13 @@ public class CurrentRecordingDetailsFragment extends RecordingDetailsFragment {
SparseArrayObjectAdapter adapter =
new SparseArrayObjectAdapter(new ActionPresenterSelector());
Resources res = getResources();
- adapter.set(ACTION_STOP_RECORDING, new Action(ACTION_STOP_RECORDING,
- res.getString(R.string.dvr_detail_stop_recording), null,
- res.getDrawable(R.drawable.lb_ic_stop)));
+ adapter.set(
+ ACTION_STOP_RECORDING,
+ new Action(
+ ACTION_STOP_RECORDING,
+ res.getString(R.string.dvr_detail_stop_recording),
+ null,
+ res.getDrawable(R.drawable.lb_ic_stop)));
return adapter;
}
@@ -90,7 +91,8 @@ public class CurrentRecordingDetailsFragment extends RecordingDetailsFragment {
@Override
public void onActionClicked(Action action) {
if (action.getId() == ACTION_STOP_RECORDING) {
- DvrUiHelper.showStopRecordingDialog(getActivity(),
+ DvrUiHelper.showStopRecordingDialog(
+ getActivity(),
getRecording().getChannelId(),
DvrStopRecordingFragment.REASON_USER_STOP,
new HalfSizedDialogFragment.OnActionClickListener() {
@@ -98,7 +100,7 @@ public class CurrentRecordingDetailsFragment extends RecordingDetailsFragment {
public void onActionClick(long actionId) {
if (actionId == DvrStopRecordingFragment.ACTION_STOP) {
DvrManager dvrManager =
- TvApplication.getSingletons(getContext())
+ TvSingletons.getSingletons(getContext())
.getDvrManager();
dvrManager.stopRecording(getRecording());
getActivity().finish();
diff --git a/src/com/android/tv/dvr/ui/browse/DetailsContent.java b/src/com/android/tv/dvr/ui/browse/DetailsContent.java
index c1fa05d7..cba6293b 100644
--- a/src/com/android/tv/dvr/ui/browse/DetailsContent.java
+++ b/src/com/android/tv/dvr/ui/browse/DetailsContent.java
@@ -20,18 +20,15 @@ import android.content.Context;
import android.media.tv.TvContract;
import android.support.annotation.Nullable;
import android.text.TextUtils;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
-import com.android.tv.data.Channel;
+import com.android.tv.TvSingletons;
+import com.android.tv.data.api.Channel;
import com.android.tv.dvr.data.RecordedProgram;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.data.SeriesRecording;
import com.android.tv.dvr.ui.DvrUiHelper;
-/**
- * A class for details content.
- */
+/** A class for details content. */
class DetailsContent {
/** Constant for invalid time. */
public static final long INVALID_TIME = -1;
@@ -44,8 +41,8 @@ class DetailsContent {
private String mBackgroundImageUri;
private boolean mUsingChannelLogo;
- static DetailsContent createFromRecordedProgram(Context context,
- RecordedProgram recordedProgram) {
+ static DetailsContent createFromRecordedProgram(
+ Context context, RecordedProgram recordedProgram) {
return new DetailsContent.Builder()
.setChannelId(recordedProgram.getChannelId())
.setProgramTitle(recordedProgram.getTitle())
@@ -53,32 +50,72 @@ class DetailsContent {
.setEpisodeNumber(recordedProgram.getEpisodeNumber())
.setStartTimeUtcMillis(recordedProgram.getStartTimeUtcMillis())
.setEndTimeUtcMillis(recordedProgram.getEndTimeUtcMillis())
- .setDescription(TextUtils.isEmpty(recordedProgram.getLongDescription())
- ? recordedProgram.getDescription() : recordedProgram.getLongDescription())
+ .setDescription(
+ TextUtils.isEmpty(recordedProgram.getLongDescription())
+ ? recordedProgram.getDescription()
+ : recordedProgram.getLongDescription())
.setPosterArtUri(recordedProgram.getPosterArtUri())
.setThumbnailUri(recordedProgram.getThumbnailUri())
.build(context);
}
- static DetailsContent createFromSeriesRecording(Context context,
- SeriesRecording seriesRecording) {
+ static DetailsContent createFromSeriesRecording(
+ Context context, SeriesRecording seriesRecording) {
return new DetailsContent.Builder()
.setChannelId(seriesRecording.getChannelId())
.setTitle(seriesRecording.getTitle())
- .setDescription(TextUtils.isEmpty(seriesRecording.getLongDescription())
- ? seriesRecording.getDescription() : seriesRecording.getLongDescription())
+ .setDescription(
+ TextUtils.isEmpty(seriesRecording.getLongDescription())
+ ? seriesRecording.getDescription()
+ : seriesRecording.getLongDescription())
.setPosterArtUri(seriesRecording.getPosterUri())
.setThumbnailUri(seriesRecording.getPhotoUri())
.build(context);
}
- static DetailsContent createFromScheduledRecording(Context context,
- ScheduledRecording scheduledRecording) {
- Channel channel = TvApplication.getSingletons(context).getChannelDataManager()
- .getChannel(scheduledRecording.getChannelId());
- String description = !TextUtils.isEmpty(scheduledRecording.getProgramDescription()) ?
- scheduledRecording.getProgramDescription()
- : scheduledRecording.getProgramLongDescription();
+ static DetailsContent createFromScheduledRecording(
+ Context context, ScheduledRecording scheduledRecording) {
+ Channel channel =
+ TvSingletons.getSingletons(context)
+ .getChannelDataManager()
+ .getChannel(scheduledRecording.getChannelId());
+ String description =
+ !TextUtils.isEmpty(scheduledRecording.getProgramDescription())
+ ? scheduledRecording.getProgramDescription()
+ : scheduledRecording.getProgramLongDescription();
+ if (TextUtils.isEmpty(description)) {
+ description = channel != null ? channel.getDescription() : null;
+ }
+ return new DetailsContent.Builder()
+ .setChannelId(scheduledRecording.getChannelId())
+ .setProgramTitle(scheduledRecording.getProgramTitle())
+ .setSeasonNumber(scheduledRecording.getSeasonNumber())
+ .setEpisodeNumber(scheduledRecording.getEpisodeNumber())
+ .setStartTimeUtcMillis(scheduledRecording.getStartTimeMs())
+ .setEndTimeUtcMillis(scheduledRecording.getEndTimeMs())
+ .setDescription(description)
+ .setPosterArtUri(scheduledRecording.getProgramPosterArtUri())
+ .setThumbnailUri(scheduledRecording.getProgramThumbnailUri())
+ .build(context);
+ }
+
+ static DetailsContent createFromFailedScheduledRecording(
+ Context context, ScheduledRecording scheduledRecording, String errMsg) {
+ Channel channel =
+ TvSingletons.getSingletons(context)
+ .getChannelDataManager()
+ .getChannel(scheduledRecording.getChannelId());
+ String description;
+ if (scheduledRecording.getState() == ScheduledRecording.STATE_RECORDING_FAILED
+ && errMsg != null) {
+ description = errMsg
+ + " (Error code: " + scheduledRecording.getFailedReason() + ")";
+ } else {
+ description =
+ !TextUtils.isEmpty(scheduledRecording.getProgramDescription())
+ ? scheduledRecording.getProgramDescription()
+ : scheduledRecording.getProgramLongDescription();
+ }
if (TextUtils.isEmpty(description)) {
description = channel != null ? channel.getDescription() : null;
}
@@ -95,60 +132,44 @@ class DetailsContent {
.build(context);
}
- private DetailsContent() { }
+ private DetailsContent() {}
- /**
- * Returns title.
- */
+ /** Returns title. */
public CharSequence getTitle() {
return mTitle;
}
- /**
- * Returns start time.
- */
+ /** Returns start time. */
public long getStartTimeUtcMillis() {
return mStartTimeUtcMillis;
}
- /**
- * Returns end time.
- */
+ /** Returns end time. */
public long getEndTimeUtcMillis() {
return mEndTimeUtcMillis;
}
- /**
- * Returns description.
- */
+ /** Returns description. */
public String getDescription() {
return mDescription;
}
- /**
- * Returns Logo image URI as a String.
- */
+ /** Returns Logo image URI as a String. */
public String getLogoImageUri() {
return mLogoImageUri;
}
- /**
- * Returns background image URI as a String.
- */
+ /** Returns background image URI as a String. */
public String getBackgroundImageUri() {
return mBackgroundImageUri;
}
- /**
- * Returns if image URIs are from its channels' logo.
- */
+ /** Returns if image URIs are from its channels' logo. */
public boolean isUsingChannelLogo() {
return mUsingChannelLogo;
}
- /**
- * Copies other details content.
- */
+ /** Copies other details content. */
public void copyFrom(DetailsContent other) {
if (this == other) {
return;
@@ -162,9 +183,7 @@ class DetailsContent {
mUsingChannelLogo = other.mUsingChannelLogo;
}
- /**
- * A class for building details content.
- */
+ /** A class for building details content. */
public static final class Builder {
private final DetailsContent mDetailsContent;
@@ -181,49 +200,37 @@ class DetailsContent {
mDetailsContent.mEndTimeUtcMillis = INVALID_TIME;
}
- /**
- * Sets title.
- */
+ /** Sets title. */
public Builder setTitle(CharSequence title) {
mDetailsContent.mTitle = title;
return this;
}
- /**
- * Sets start time.
- */
+ /** Sets start time. */
public Builder setStartTimeUtcMillis(long startTimeUtcMillis) {
mDetailsContent.mStartTimeUtcMillis = startTimeUtcMillis;
return this;
}
- /**
- * Sets end time.
- */
+ /** Sets end time. */
public Builder setEndTimeUtcMillis(long endTimeUtcMillis) {
mDetailsContent.mEndTimeUtcMillis = endTimeUtcMillis;
return this;
}
- /**
- * Sets description.
- */
+ /** Sets description. */
public Builder setDescription(String description) {
mDetailsContent.mDescription = description;
return this;
}
- /**
- * Sets logo image URI as a String.
- */
+ /** Sets logo image URI as a String. */
public Builder setLogoImageUri(String logoImageUri) {
mDetailsContent.mLogoImageUri = logoImageUri;
return this;
}
- /**
- * Sets background image URI as a String.
- */
+ /** Sets background image URI as a String. */
public Builder setBackgroundImageUri(String backgroundImageUri) {
mDetailsContent.mBackgroundImageUri = backgroundImageUri;
return this;
@@ -260,12 +267,18 @@ class DetailsContent {
}
private void createStyledTitle(Context context, Channel channel) {
- CharSequence title = DvrUiHelper.getStyledTitleWithEpisodeNumber(context,
- mProgramTitle, mSeasonNumber, mEpisodeNumber,
- R.style.text_appearance_card_view_episode_number);
+ CharSequence title =
+ DvrUiHelper.getStyledTitleWithEpisodeNumber(
+ context,
+ mProgramTitle,
+ mSeasonNumber,
+ mEpisodeNumber,
+ R.style.text_appearance_card_view_episode_number);
if (TextUtils.isEmpty(title)) {
- mDetailsContent.mTitle = channel != null ? channel.getDisplayName()
- : context.getResources().getString(R.string.no_program_information);
+ mDetailsContent.mTitle =
+ channel != null
+ ? channel.getDisplayName()
+ : context.getResources().getString(R.string.no_program_information);
} else {
mDetailsContent.mTitle = title;
}
@@ -288,20 +301,19 @@ class DetailsContent {
mDetailsContent.mBackgroundImageUri = mThumbnailUri;
}
if (TextUtils.isEmpty(mDetailsContent.mLogoImageUri) && channel != null) {
- String channelLogoUri = TvContract.buildChannelLogoUri(channel.getId())
- .toString();
+ String channelLogoUri = TvContract.buildChannelLogoUri(channel.getId()).toString();
mDetailsContent.mLogoImageUri = channelLogoUri;
mDetailsContent.mBackgroundImageUri = channelLogoUri;
mDetailsContent.mUsingChannelLogo = true;
}
}
- /**
- * Builds details content.
- */
+ /** Builds details content. */
public DetailsContent build(Context context) {
- Channel channel = TvApplication.getSingletons(context).getChannelDataManager()
- .getChannel(mChannelId);
+ Channel channel =
+ TvSingletons.getSingletons(context)
+ .getChannelDataManager()
+ .getChannel(mChannelId);
if (mDetailsContent.mTitle == null) {
createStyledTitle(context, channel);
}
@@ -314,4 +326,4 @@ class DetailsContent {
return detailsContent;
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/browse/DetailsContentPresenter.java b/src/com/android/tv/dvr/ui/browse/DetailsContentPresenter.java
index 09b57887..aec8c411 100644
--- a/src/com/android/tv/dvr/ui/browse/DetailsContentPresenter.java
+++ b/src/com/android/tv/dvr/ui/browse/DetailsContentPresenter.java
@@ -16,11 +16,11 @@
package com.android.tv.dvr.ui.browse;
-import android.app.Activity;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
+import android.app.Activity;
import android.content.Context;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
@@ -33,24 +33,20 @@ import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityManager;
import android.widget.LinearLayout;
import android.widget.TextView;
-
import com.android.tv.R;
import com.android.tv.ui.ViewUtils;
import com.android.tv.util.Utils;
/**
- * An {@link Presenter} for rendering a detailed description of an DVR item.
- * Typically this Presenter will be used in a
- * {@link android.support.v17.leanback.widget.DetailsOverviewRowPresenter}.
- * Most codes of this class is originated from
- * {@link android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter}.
- * The latter class are re-used to provide a customized version of
- * {@link android.support.v17.leanback.widget.DetailsOverviewRow}.
+ * An {@link Presenter} for rendering a detailed description of an DVR item. Typically this
+ * Presenter will be used in a {@link
+ * android.support.v17.leanback.widget.DetailsOverviewRowPresenter}. Most codes of this class is
+ * originated from {@link android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter}.
+ * The latter class are re-used to provide a customized version of {@link
+ * android.support.v17.leanback.widget.DetailsOverviewRow}.
*/
class DetailsContentPresenter extends Presenter {
- /**
- * The ViewHolder for the {@link DetailsContentPresenter}.
- */
+ /** The ViewHolder for the {@link DetailsContentPresenter}. */
public static class ViewHolder extends Presenter.ViewHolder {
final TextView mTitle;
final TextView mSubtitle;
@@ -85,31 +81,40 @@ class DetailsContentPresenter extends Presenter {
return false;
}
final int bodyLines = mBody.getLineCount();
- int maxLines = mFullTextMode ? bodyLines :
- (mTitle.getLineCount() > 1 ? mBodyMinLines : mBodyMaxLines);
+ int maxLines =
+ mFullTextMode
+ ? bodyLines
+ : (mTitle.getLineCount() > 1
+ ? mBodyMinLines
+ : mBodyMaxLines);
if (bodyLines > maxLines) {
mReadMoreView.setVisibility(View.VISIBLE);
mDescriptionContainer.setFocusable(true);
mDescriptionContainer.setClickable(true);
- mDescriptionContainer.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mFullTextMode = true;
- mReadMoreView.setVisibility(View.GONE);
- mDescriptionContainer.setFocusable((
- (AccessibilityManager) view.getContext()
- .getSystemService(
- Context.ACCESSIBILITY_SERVICE))
- .isEnabled());
- mDescriptionContainer.setClickable(false);
- mDescriptionContainer.setOnClickListener(null);
- int oldMaxLines = mBody.getMaxLines();
- mBody.setMaxLines(bodyLines);
- // Minus 1 from line difference to eliminate the space
- // originally occupied by "READ MORE"
- showFullText((bodyLines - oldMaxLines - 1) * mBodyLineSpacing);
- }
- });
+ mDescriptionContainer.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mFullTextMode = true;
+ mReadMoreView.setVisibility(View.GONE);
+ mDescriptionContainer.setFocusable(
+ ((AccessibilityManager)
+ view.getContext()
+ .getSystemService(
+ Context
+ .ACCESSIBILITY_SERVICE))
+ .isEnabled());
+ mDescriptionContainer.setClickable(false);
+ mDescriptionContainer.setOnClickListener(null);
+ int oldMaxLines = mBody.getMaxLines();
+ mBody.setMaxLines(bodyLines);
+ // Minus 1 from line difference to eliminate the space
+ // originally occupied by "READ MORE"
+ showFullText(
+ (bodyLines - oldMaxLines - 1)
+ * mBodyLineSpacing);
+ }
+ });
}
if (mReadMoreView.getVisibility() == View.VISIBLE
&& mSubtitle.getVisibility() == View.VISIBLE) {
@@ -151,30 +156,42 @@ class DetailsContentPresenter extends Presenter {
// We have to explicitly set focusable to true here for accessibility, since we might
// set the view's focusable state when we need to show "READ MORE", which would remove
// the default focusable state for accessibility.
- mDescriptionContainer.setFocusable(((AccessibilityManager) view.getContext()
- .getSystemService(Context.ACCESSIBILITY_SERVICE)).isEnabled());
+ mDescriptionContainer.setFocusable(
+ ((AccessibilityManager)
+ view.getContext()
+ .getSystemService(Context.ACCESSIBILITY_SERVICE))
+ .isEnabled());
mReadMoreView = (TextView) view.findViewById(R.id.dvr_details_description_read_more);
FontMetricsInt titleFontMetricsInt = getFontMetricsInt(mTitle);
- final int titleAscent = view.getResources().getDimensionPixelSize(
- R.dimen.lb_details_description_title_baseline);
+ final int titleAscent =
+ view.getResources()
+ .getDimensionPixelSize(R.dimen.lb_details_description_title_baseline);
// Ascent is negative
mTitleMargin = titleAscent + titleFontMetricsInt.ascent;
- mUnderTitleBaselineMargin = view.getResources().getDimensionPixelSize(
- R.dimen.lb_details_description_under_title_baseline_margin);
- mUnderSubtitleBaselineMargin = view.getResources().getDimensionPixelSize(
- R.dimen.dvr_details_description_under_subtitle_baseline_margin);
+ mUnderTitleBaselineMargin =
+ view.getResources()
+ .getDimensionPixelSize(
+ R.dimen.lb_details_description_under_title_baseline_margin);
+ mUnderSubtitleBaselineMargin =
+ view.getResources()
+ .getDimensionPixelSize(
+ R.dimen.dvr_details_description_under_subtitle_baseline_margin);
- mTitleLineSpacing = view.getResources().getDimensionPixelSize(
- R.dimen.lb_details_description_title_line_spacing);
- mBodyLineSpacing = view.getResources().getDimensionPixelSize(
- R.dimen.lb_details_description_body_line_spacing);
+ mTitleLineSpacing =
+ view.getResources()
+ .getDimensionPixelSize(
+ R.dimen.lb_details_description_title_line_spacing);
+ mBodyLineSpacing =
+ view.getResources()
+ .getDimensionPixelSize(
+ R.dimen.lb_details_description_body_line_spacing);
- mBodyMaxLines = view.getResources().getInteger(
- R.integer.lb_details_description_body_max_lines);
- mBodyMinLines = view.getResources().getInteger(
- R.integer.lb_details_description_body_min_lines);
+ mBodyMaxLines =
+ view.getResources().getInteger(R.integer.lb_details_description_body_max_lines);
+ mBodyMinLines =
+ view.getResources().getInteger(R.integer.lb_details_description_body_min_lines);
mTitleMaxLines = mTitle.getMaxLines();
mTitleFontMetricsInt = getFontMetricsInt(mTitle);
@@ -218,12 +235,14 @@ class DetailsContentPresenter extends Presenter {
private void showFullText(int heightDiff) {
final ViewGroup detailsFrame = (ViewGroup) mActivity.findViewById(R.id.details_frame);
int nowHeight = ViewUtils.getLayoutHeight(detailsFrame);
- Animator expandAnimator = ViewUtils.createHeightAnimator(
- detailsFrame, nowHeight, nowHeight + heightDiff);
+ Animator expandAnimator =
+ ViewUtils.createHeightAnimator(detailsFrame, nowHeight, nowHeight + heightDiff);
expandAnimator.setDuration(mFullTextAnimationDuration);
- Animator shiftAnimator = ObjectAnimator.ofPropertyValuesHolder(detailsFrame,
- PropertyValuesHolder.ofFloat(View.TRANSLATION_Y,
- 0f, -(heightDiff / 2)));
+ Animator shiftAnimator =
+ ObjectAnimator.ofPropertyValuesHolder(
+ detailsFrame,
+ PropertyValuesHolder.ofFloat(
+ View.TRANSLATION_Y, 0f, -(heightDiff / 2)));
shiftAnimator.setDuration(mFullTextAnimationDuration);
AnimatorSet fullTextAnimator = new AnimatorSet();
fullTextAnimator.playTogether(expandAnimator, shiftAnimator);
@@ -237,14 +256,17 @@ class DetailsContentPresenter extends Presenter {
public DetailsContentPresenter(Activity activity) {
super();
mActivity = activity;
- mFullTextAnimationDuration = mActivity.getResources()
- .getInteger(R.integer.dvr_details_full_text_animation_duration);
+ mFullTextAnimationDuration =
+ mActivity
+ .getResources()
+ .getInteger(R.integer.dvr_details_full_text_animation_duration);
}
@Override
public final ViewHolder onCreateViewHolder(ViewGroup parent) {
- View v = LayoutInflater.from(parent.getContext())
- .inflate(R.layout.dvr_details_description, parent, false);
+ View v =
+ LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.dvr_details_description, parent, false);
return new ViewHolder(v);
}
@@ -263,8 +285,11 @@ class DetailsContentPresenter extends Presenter {
} else {
vh.mTitle.setText(detailsContent.getTitle());
vh.mTitle.setVisibility(View.VISIBLE);
- vh.mTitle.setLineSpacing(vh.mTitleLineSpacing - vh.mTitle.getLineHeight()
- + vh.mTitle.getLineSpacingExtra(), vh.mTitle.getLineSpacingMultiplier());
+ vh.mTitle.setLineSpacing(
+ vh.mTitleLineSpacing
+ - vh.mTitle.getLineHeight()
+ + vh.mTitle.getLineSpacingExtra(),
+ vh.mTitle.getLineSpacingMultiplier());
vh.mTitle.setMaxLines(vh.mTitleMaxLines);
}
setTopMargin(vh.mTitle, vh.mTitleMargin);
@@ -272,13 +297,19 @@ class DetailsContentPresenter extends Presenter {
boolean hasSubtitle = true;
if (detailsContent.getStartTimeUtcMillis() != DetailsContent.INVALID_TIME
&& detailsContent.getEndTimeUtcMillis() != DetailsContent.INVALID_TIME) {
- vh.mSubtitle.setText(Utils.getDurationString(viewHolder.view.getContext(),
- detailsContent.getStartTimeUtcMillis(),
- detailsContent.getEndTimeUtcMillis(), false));
+ vh.mSubtitle.setText(
+ Utils.getDurationString(
+ viewHolder.view.getContext(),
+ detailsContent.getStartTimeUtcMillis(),
+ detailsContent.getEndTimeUtcMillis(),
+ false));
vh.mSubtitle.setVisibility(View.VISIBLE);
if (hasTitle) {
- setTopMargin(vh.mSubtitle, vh.mUnderTitleBaselineMargin
- + vh.mSubtitleFontMetricsInt.ascent - vh.mTitleFontMetricsInt.descent);
+ setTopMargin(
+ vh.mSubtitle,
+ vh.mUnderTitleBaselineMargin
+ + vh.mSubtitleFontMetricsInt.ascent
+ - vh.mTitleFontMetricsInt.descent);
} else {
setTopMargin(vh.mSubtitle, 0);
}
@@ -292,16 +323,23 @@ class DetailsContentPresenter extends Presenter {
} else {
vh.mBody.setText(detailsContent.getDescription());
vh.mBody.setVisibility(View.VISIBLE);
- vh.mBody.setLineSpacing(vh.mBodyLineSpacing - vh.mBody.getLineHeight()
- + vh.mBody.getLineSpacingExtra(), vh.mBody.getLineSpacingMultiplier());
+ vh.mBody.setLineSpacing(
+ vh.mBodyLineSpacing - vh.mBody.getLineHeight() + vh.mBody.getLineSpacingExtra(),
+ vh.mBody.getLineSpacingMultiplier());
if (hasSubtitle) {
- setTopMargin(vh.mDescriptionContainer, vh.mUnderSubtitleBaselineMargin
- + vh.mBodyFontMetricsInt.ascent - vh.mSubtitleFontMetricsInt.descent
- - vh.mBody.getPaddingTop());
+ setTopMargin(
+ vh.mDescriptionContainer,
+ vh.mUnderSubtitleBaselineMargin
+ + vh.mBodyFontMetricsInt.ascent
+ - vh.mSubtitleFontMetricsInt.descent
+ - vh.mBody.getPaddingTop());
} else if (hasTitle) {
- setTopMargin(vh.mDescriptionContainer, vh.mUnderTitleBaselineMargin
- + vh.mBodyFontMetricsInt.ascent - vh.mTitleFontMetricsInt.descent
- - vh.mBody.getPaddingTop());
+ setTopMargin(
+ vh.mDescriptionContainer,
+ vh.mUnderTitleBaselineMargin
+ + vh.mBodyFontMetricsInt.ascent
+ - vh.mTitleFontMetricsInt.descent
+ - vh.mBody.getPaddingTop());
} else {
setTopMargin(vh.mDescriptionContainer, 0);
}
@@ -309,11 +347,11 @@ class DetailsContentPresenter extends Presenter {
}
@Override
- public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) { }
+ public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {}
private void setTopMargin(View view, int topMargin) {
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
lp.topMargin = topMargin;
view.setLayoutParams(lp);
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/browse/DetailsViewBackgroundHelper.java b/src/com/android/tv/dvr/ui/browse/DetailsViewBackgroundHelper.java
index 82fe9ce3..849360b8 100644
--- a/src/com/android/tv/dvr/ui/browse/DetailsViewBackgroundHelper.java
+++ b/src/com/android/tv/dvr/ui/browse/DetailsViewBackgroundHelper.java
@@ -23,9 +23,7 @@ import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.support.v17.leanback.app.BackgroundManager;
-/**
- * The Background Helper.
- */
+/** The Background Helper. */
class DetailsViewBackgroundHelper {
// Background delay serves to avoid kicking off expensive bitmap loading
// in case multiple backgrounds are set in quick succession.
@@ -59,11 +57,10 @@ class DetailsViewBackgroundHelper {
public DetailsViewBackgroundHelper(Activity activity) {
mBackgroundManager = BackgroundManager.getInstance(activity);
mBackgroundManager.attach(activity.getWindow());
+ mBackgroundManager.setAutoReleaseOnStop(false);
}
- /**
- * Sets the given image to background.
- */
+ /** Sets the given image to background. */
public void setBackground(Drawable background) {
if (mRunnable != null) {
mHandler.removeCallbacks(mRunnable);
@@ -72,18 +69,14 @@ class DetailsViewBackgroundHelper {
mHandler.postDelayed(mRunnable, SET_BACKGROUND_DELAY_MS);
}
- /**
- * Sets the background color.
- */
+ /** Sets the background color. */
public void setBackgroundColor(int color) {
if (mBackgroundManager.isAttached()) {
mBackgroundManager.setColor(color);
}
}
- /**
- * Sets the background scrim.
- */
+ /** Sets the background scrim. */
public void setScrim(int color) {
if (mBackgroundManager.isAttached()) {
mBackgroundManager.setDimLayer(new ColorDrawable(color));
diff --git a/src/com/android/tv/dvr/ui/browse/DvrBrowseActivity.java b/src/com/android/tv/dvr/ui/browse/DvrBrowseActivity.java
index 07eec107..6cc1c7a1 100644
--- a/src/com/android/tv/dvr/ui/browse/DvrBrowseActivity.java
+++ b/src/com/android/tv/dvr/ui/browse/DvrBrowseActivity.java
@@ -20,19 +20,16 @@ import android.app.Activity;
import android.content.Intent;
import android.media.tv.TvInputManager;
import android.os.Bundle;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.Starter;
-/**
- * {@link android.app.Activity} for DVR UI.
- */
+/** {@link android.app.Activity} for DVR UI. */
public class DvrBrowseActivity extends Activity {
private DvrBrowseFragment mFragment;
@Override
public void onCreate(Bundle savedInstanceState) {
- TvApplication.setCurrentRunningProcess(this, true);
+ Starter.start(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.dvr_main);
mFragment = (DvrBrowseFragment) getFragmentManager().findFragmentById(R.id.dvr_frame);
@@ -49,4 +46,4 @@ public class DvrBrowseActivity extends Activity {
mFragment.showScheduledRow();
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/browse/DvrBrowseFragment.java b/src/com/android/tv/dvr/ui/browse/DvrBrowseFragment.java
index cb3a5745..40b3a1f0 100644
--- a/src/com/android/tv/dvr/ui/browse/DvrBrowseFragment.java
+++ b/src/com/android/tv/dvr/ui/browse/DvrBrowseFragment.java
@@ -16,7 +16,9 @@
package com.android.tv.dvr.ui.browse;
+import android.annotation.TargetApi;
import android.content.Context;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v17.leanback.app.BrowseFragment;
@@ -30,9 +32,9 @@ import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
-import com.android.tv.ApplicationSingletons;
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvFeatures;
+import com.android.tv.TvSingletons;
import com.android.tv.data.GenreItems;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrDataManager.OnDvrScheduleLoadFinishedListener;
@@ -52,12 +54,15 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
-/**
- * {@link BrowseFragment} for DVR functions.
- */
-public class DvrBrowseFragment extends BrowseFragment implements
- RecordedProgramListener, ScheduledRecordingListener, SeriesRecordingListener,
- OnDvrScheduleLoadFinishedListener, OnRecordedProgramLoadFinishedListener {
+/** {@link BrowseFragment} for DVR functions. */
+@TargetApi(Build.VERSION_CODES.N)
+@SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated
+public class DvrBrowseFragment extends BrowseFragment
+ implements RecordedProgramListener,
+ ScheduledRecordingListener,
+ SeriesRecordingListener,
+ OnDvrScheduleLoadFinishedListener,
+ OnRecordedProgramLoadFinishedListener {
private static final String TAG = "DvrBrowseFragment";
private static final boolean DEBUG = false;
@@ -67,7 +72,7 @@ public class DvrBrowseFragment extends BrowseFragment implements
private boolean mShouldShowScheduleRow;
private boolean mEntranceTransitionEnded;
- private RecordedProgramAdapter mRecentAdapter;
+ private RecentRowAdapter mRecentAdapter;
private ScheduleAdapter mScheduleAdapter;
private SeriesAdapter mSeriesAdapter;
private RecordedProgramAdapter[] mGenreAdapters =
@@ -98,82 +103,143 @@ public class DvrBrowseFragment extends BrowseFragment implements
}
};
- private final Comparator<Object> RECORDED_PROGRAM_COMPARATOR = new Comparator<Object>() {
- @Override
- public int compare(Object lhs, Object rhs) {
- if (lhs instanceof SeriesRecording) {
- lhs = mSeriesId2LatestProgram.get(((SeriesRecording) lhs).getSeriesId());
- }
- if (rhs instanceof SeriesRecording) {
- rhs = mSeriesId2LatestProgram.get(((SeriesRecording) rhs).getSeriesId());
- }
- if (lhs instanceof RecordedProgram) {
- if (rhs instanceof RecordedProgram) {
- return RecordedProgram.START_TIME_THEN_ID_COMPARATOR.reversed()
- .compare((RecordedProgram) lhs, (RecordedProgram) rhs);
- } else {
- return -1;
+ private final Comparator<Object> RECORDED_PROGRAM_COMPARATOR =
+ new Comparator<Object>() {
+ @Override
+ public int compare(Object lhs, Object rhs) {
+ if (lhs instanceof SeriesRecording) {
+ lhs = mSeriesId2LatestProgram.get(((SeriesRecording) lhs).getSeriesId());
+ }
+ if (rhs instanceof SeriesRecording) {
+ rhs = mSeriesId2LatestProgram.get(((SeriesRecording) rhs).getSeriesId());
+ }
+ if (lhs instanceof RecordedProgram) {
+ if (rhs instanceof RecordedProgram) {
+ return RecordedProgram.START_TIME_THEN_ID_COMPARATOR
+ .reversed()
+ .compare((RecordedProgram) lhs, (RecordedProgram) rhs);
+ } else {
+ return -1;
+ }
+ } else if (rhs instanceof RecordedProgram) {
+ return 1;
+ } else {
+ return 0;
+ }
}
- } else if (rhs instanceof RecordedProgram) {
- return 1;
- } else {
- return 0;
- }
- }
- };
+ };
- private static final Comparator<Object> SCHEDULE_COMPARATOR = new Comparator<Object>() {
- @Override
- public int compare(Object lhs, Object rhs) {
- if (lhs instanceof ScheduledRecording) {
- if (rhs instanceof ScheduledRecording) {
- return ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR
- .compare((ScheduledRecording) lhs, (ScheduledRecording) rhs);
- } else {
- return -1;
+ private static final Comparator<Object> SCHEDULE_COMPARATOR =
+ new Comparator<Object>() {
+ @Override
+ public int compare(Object lhs, Object rhs) {
+ if (lhs instanceof ScheduledRecording) {
+ if (rhs instanceof ScheduledRecording) {
+ return ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR
+ .compare((ScheduledRecording) lhs, (ScheduledRecording) rhs);
+ } else {
+ return -1;
+ }
+ } else if (rhs instanceof ScheduledRecording) {
+ return 1;
+ } else {
+ return 0;
+ }
}
- } else if (rhs instanceof ScheduledRecording) {
- return 1;
- } else {
- return 0;
- }
- }
- };
+ };
+
+ static final Comparator<Object> RECENT_ROW_COMPARATOR =
+ new Comparator<Object>() {
+ @Override
+ public int compare(Object lhs, Object rhs) {
+ if (lhs instanceof ScheduledRecording) {
+ if (rhs instanceof ScheduledRecording) {
+ return ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR
+ .reversed()
+ .compare((ScheduledRecording) lhs, (ScheduledRecording) rhs);
+ } else if (rhs instanceof RecordedProgram) {
+ ScheduledRecording scheduled = (ScheduledRecording) lhs;
+ RecordedProgram recorded = (RecordedProgram) rhs;
+ int compare =
+ Long.compare(
+ recorded.getStartTimeUtcMillis(),
+ scheduled.getStartTimeMs());
+ // recorded program first when the start times are the same
+ return compare == 0 ? 1 : compare;
+ } else {
+ return -1;
+ }
+ } else if (lhs instanceof RecordedProgram) {
+ if (rhs instanceof RecordedProgram) {
+ return RecordedProgram.START_TIME_THEN_ID_COMPARATOR
+ .reversed()
+ .compare((RecordedProgram) lhs, (RecordedProgram) rhs);
+ } else if (rhs instanceof ScheduledRecording) {
+ RecordedProgram recorded = (RecordedProgram) lhs;
+ ScheduledRecording scheduled = (ScheduledRecording) rhs;
+ int compare =
+ Long.compare(
+ scheduled.getStartTimeMs(),
+ recorded.getStartTimeUtcMillis());
+ // recorded program first when the start times are the same
+ return compare == 0 ? -1 : compare;
+ } else {
+ return -1;
+ }
+ } else {
+ return !(rhs instanceof RecordedProgram)
+ && !(rhs instanceof ScheduledRecording)
+ ? 0 : 1;
+ }
+ }
+ };
private final DvrScheduleManager.OnConflictStateChangeListener mOnConflictStateChangeListener =
new DvrScheduleManager.OnConflictStateChangeListener() {
- @Override
- public void onConflictStateChange(boolean conflict, ScheduledRecording... schedules) {
- if (mScheduleAdapter != null) {
- for (ScheduledRecording schedule : schedules) {
- onScheduledRecordingConflictStatusChanged(schedule);
+ @Override
+ public void onConflictStateChange(
+ boolean conflict, ScheduledRecording... schedules) {
+ if (mScheduleAdapter != null) {
+ for (ScheduledRecording schedule : schedules) {
+ onScheduledRecordingConflictStatusChanged(schedule);
+ }
+ }
}
- }
- }
- };
+ };
- private final Runnable mUpdateRowsRunnable = new Runnable() {
- @Override
- public void run() {
- updateRows();
- }
- };
+ private final Runnable mUpdateRowsRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ updateRows();
+ }
+ };
@Override
public void onCreate(Bundle savedInstanceState) {
if (DEBUG) Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
Context context = getContext();
- ApplicationSingletons singletons = TvApplication.getSingletons(context);
+ TvSingletons singletons = TvSingletons.getSingletons(context);
mDvrDataManager = singletons.getDvrDataManager();
mDvrScheudleManager = singletons.getDvrScheduleManager();
- mPresenterSelector = new ClassPresenterSelector()
- .addClassPresenter(ScheduledRecording.class,
- new ScheduledRecordingPresenter(context))
- .addClassPresenter(RecordedProgram.class, new RecordedProgramPresenter(context))
- .addClassPresenter(SeriesRecording.class, new SeriesRecordingPresenter(context))
- .addClassPresenter(FullScheduleCardHolder.class,
- new FullSchedulesCardPresenter(context));
+ mPresenterSelector =
+ new ClassPresenterSelector()
+ .addClassPresenter(
+ ScheduledRecording.class, new ScheduledRecordingPresenter(context))
+ .addClassPresenter(
+ RecordedProgram.class, new RecordedProgramPresenter(context))
+ .addClassPresenter(
+ SeriesRecording.class, new SeriesRecordingPresenter(context))
+ .addClassPresenter(
+ FullScheduleCardHolder.class,
+ new FullSchedulesCardPresenter(context));
+
+ if (TvFeatures.DVR_FAILED_LIST.isEnabled(context)) {
+ mPresenterSelector.addClassPresenter(
+ DvrHistoryCardHolder.class,
+ new DvrHistoryCardPresenter(context));
+ }
mGenreLabels = new ArrayList<>(Arrays.asList(GenreItems.getLabels(context)));
mGenreLabels.add(getString(R.string.dvr_main_others));
prepareUiElements();
@@ -195,7 +261,8 @@ public class DvrBrowseFragment extends BrowseFragment implements
@Override
public void onDestroyView() {
- getView().getViewTreeObserver()
+ getView()
+ .getViewTreeObserver()
.removeOnGlobalFocusChangeListener(mOnGlobalFocusChangeListener);
super.onDestroyView();
}
@@ -263,6 +330,8 @@ public class DvrBrowseFragment extends BrowseFragment implements
for (ScheduledRecording scheduleRecording : scheduledRecordings) {
if (needToShowScheduledRecording(scheduleRecording)) {
mScheduleAdapter.add(scheduleRecording);
+ } else if (scheduleRecording.getState() == ScheduledRecording.STATE_RECORDING_FAILED) {
+ mRecentAdapter.add(scheduleRecording);
}
}
}
@@ -361,30 +430,44 @@ public class DvrBrowseFragment extends BrowseFragment implements
private boolean startBrowseIfDvrInitialized() {
if (mDvrDataManager.isInitialized()) {
// Setup rows
- mRecentAdapter = new RecordedProgramAdapter(MAX_RECENT_ITEM_COUNT);
+ mRecentAdapter = new RecentRowAdapter(MAX_RECENT_ITEM_COUNT);
mScheduleAdapter = new ScheduleAdapter(MAX_SCHEDULED_ITEM_COUNT);
mSeriesAdapter = new SeriesAdapter();
for (int i = 0; i < mGenreAdapters.length; i++) {
mGenreAdapters[i] = new RecordedProgramAdapter();
}
// Schedule Recordings.
- List<ScheduledRecording> schedules = mDvrDataManager.getAllScheduledRecordings();
+ // only get not started or in progress recordings
+ List<ScheduledRecording> schedules = mDvrDataManager.getAvailableScheduledRecordings();
onScheduledRecordingAdded(ScheduledRecording.toArray(schedules));
mScheduleAdapter.addExtraItem(FullScheduleCardHolder.FULL_SCHEDULE_CARD_HOLDER);
// Recorded Programs.
for (RecordedProgram recordedProgram : mDvrDataManager.getRecordedPrograms()) {
handleRecordedProgramAdded(recordedProgram, false);
}
+ if (TvFeatures.DVR_FAILED_LIST.isEnabled(getContext())) {
+ // only get failed recordings
+ for (ScheduledRecording scheduledRecording
+ : mDvrDataManager.getFailedScheduledRecordings()) {
+ onScheduledRecordingAdded(scheduledRecording);
+ }
+ mRecentAdapter.addExtraItem(DvrHistoryCardHolder.DVR_HISTORY_CARD_HOLDER);
+ }
// Series Recordings. Series recordings should be added after recorded programs, because
- // we build series recordings' latest program information while adding recorded programs.
+ // we build series recordings' latest program information while adding recorded
+ // programs.
List<SeriesRecording> recordings = mDvrDataManager.getSeriesRecordings();
handleSeriesRecordingsAdded(recordings);
- mRecentRow = new ListRow(new HeaderItem(
- getString(R.string.dvr_main_recent)), mRecentAdapter);
- mScheduledRow = new ListRow(new HeaderItem(
- getString(R.string.dvr_main_scheduled)), mScheduleAdapter);
- mSeriesRow = new ListRow(new HeaderItem(
- getString(R.string.dvr_main_series)), mSeriesAdapter);
+ mRecentRow =
+ new ListRow(
+ new HeaderItem(getString(R.string.dvr_main_recent)), mRecentAdapter);
+ mScheduledRow =
+ new ListRow(
+ new HeaderItem(getString(R.string.dvr_main_scheduled)),
+ mScheduleAdapter);
+ mSeriesRow =
+ new ListRow(
+ new HeaderItem(getString(R.string.dvr_main_series)), mSeriesAdapter);
mRowsAdapter.add(mScheduledRow);
updateRows();
// Initialize listeners
@@ -398,16 +481,18 @@ public class DvrBrowseFragment extends BrowseFragment implements
return false;
}
- private void handleRecordedProgramAdded(RecordedProgram recordedProgram,
- boolean updateSeriesRecording) {
+ private void handleRecordedProgramAdded(
+ RecordedProgram recordedProgram, boolean updateSeriesRecording) {
mRecentAdapter.add(recordedProgram);
String seriesId = recordedProgram.getSeriesId();
SeriesRecording seriesRecording = null;
if (seriesId != null) {
seriesRecording = mDvrDataManager.getSeriesRecording(seriesId);
RecordedProgram latestProgram = mSeriesId2LatestProgram.get(seriesId);
- if (latestProgram == null || RecordedProgram.START_TIME_THEN_ID_COMPARATOR
- .compare(latestProgram, recordedProgram) < 0) {
+ if (latestProgram == null
+ || RecordedProgram.START_TIME_THEN_ID_COMPARATOR.compare(
+ latestProgram, recordedProgram)
+ < 0) {
mSeriesId2LatestProgram.put(seriesId, recordedProgram);
if (updateSeriesRecording && seriesRecording != null) {
onSeriesRecordingChanged(seriesRecording);
@@ -415,8 +500,8 @@ public class DvrBrowseFragment extends BrowseFragment implements
}
}
if (seriesRecording == null) {
- for (RecordedProgramAdapter adapter
- : getGenreAdapters(recordedProgram.getCanonicalGenres())) {
+ for (RecordedProgramAdapter adapter :
+ getGenreAdapters(recordedProgram.getCanonicalGenres())) {
adapter.add(recordedProgram);
}
}
@@ -436,8 +521,8 @@ public class DvrBrowseFragment extends BrowseFragment implements
}
}
}
- for (RecordedProgramAdapter adapter
- : getGenreAdapters(recordedProgram.getCanonicalGenres())) {
+ for (RecordedProgramAdapter adapter :
+ getGenreAdapters(recordedProgram.getCanonicalGenres())) {
adapter.remove(recordedProgram);
}
}
@@ -449,8 +534,10 @@ public class DvrBrowseFragment extends BrowseFragment implements
if (seriesId != null) {
seriesRecording = mDvrDataManager.getSeriesRecording(seriesId);
RecordedProgram latestProgram = mSeriesId2LatestProgram.get(seriesId);
- if (latestProgram == null || RecordedProgram.START_TIME_THEN_ID_COMPARATOR
- .compare(latestProgram, recordedProgram) <= 0) {
+ if (latestProgram == null
+ || RecordedProgram.START_TIME_THEN_ID_COMPARATOR.compare(
+ latestProgram, recordedProgram)
+ <= 0) {
mSeriesId2LatestProgram.put(seriesId, recordedProgram);
if (seriesRecording != null) {
onSeriesRecordingChanged(seriesRecording);
@@ -463,8 +550,8 @@ public class DvrBrowseFragment extends BrowseFragment implements
}
}
if (seriesRecording == null) {
- updateGenreAdapters(getGenreAdapters(
- recordedProgram.getCanonicalGenres()), recordedProgram);
+ updateGenreAdapters(
+ getGenreAdapters(recordedProgram.getCanonicalGenres()), recordedProgram);
} else {
updateGenreAdapters(new ArrayList<>(), recordedProgram);
}
@@ -474,8 +561,8 @@ public class DvrBrowseFragment extends BrowseFragment implements
for (SeriesRecording seriesRecording : seriesRecordings) {
mSeriesAdapter.add(seriesRecording);
if (mSeriesId2LatestProgram.get(seriesRecording.getSeriesId()) != null) {
- for (RecordedProgramAdapter adapter
- : getGenreAdapters(seriesRecording.getCanonicalGenreIds())) {
+ for (RecordedProgramAdapter adapter :
+ getGenreAdapters(seriesRecording.getCanonicalGenreIds())) {
adapter.add(seriesRecording);
}
}
@@ -485,8 +572,8 @@ public class DvrBrowseFragment extends BrowseFragment implements
private void handleSeriesRecordingsRemoved(List<SeriesRecording> seriesRecordings) {
for (SeriesRecording seriesRecording : seriesRecordings) {
mSeriesAdapter.remove(seriesRecording);
- for (RecordedProgramAdapter adapter
- : getGenreAdapters(seriesRecording.getCanonicalGenreIds())) {
+ for (RecordedProgramAdapter adapter :
+ getGenreAdapters(seriesRecording.getCanonicalGenreIds())) {
adapter.remove(seriesRecording);
}
}
@@ -496,8 +583,8 @@ public class DvrBrowseFragment extends BrowseFragment implements
for (SeriesRecording seriesRecording : seriesRecordings) {
mSeriesAdapter.change(seriesRecording);
if (mSeriesId2LatestProgram.get(seriesRecording.getSeriesId()) != null) {
- updateGenreAdapters(getGenreAdapters(
- seriesRecording.getCanonicalGenreIds()), seriesRecording);
+ updateGenreAdapters(
+ getGenreAdapters(seriesRecording.getCanonicalGenreIds()), seriesRecording);
} else {
// Remove series recording from all genre rows if it has no recorded program
updateGenreAdapters(new ArrayList<>(), seriesRecording);
@@ -512,7 +599,7 @@ public class DvrBrowseFragment extends BrowseFragment implements
} else {
for (String genre : genres) {
int genreId = GenreItems.getId(genre);
- if(genreId >= mGenreAdapters.length) {
+ if (genreId >= mGenreAdapters.length) {
Log.d(TAG, "Wrong Genre ID: " + genreId);
} else {
result.add(mGenreAdapters[genreId]);
@@ -528,7 +615,7 @@ public class DvrBrowseFragment extends BrowseFragment implements
result.add(mGenreAdapters[mGenreAdapters.length - 1]);
} else {
for (int genreId : genreIds) {
- if(genreId >= mGenreAdapters.length) {
+ if (genreId >= mGenreAdapters.length) {
Log.d(TAG, "Wrong Genre ID: " + genreId);
} else {
result.add(mGenreAdapters[genreId]);
@@ -554,8 +641,9 @@ public class DvrBrowseFragment extends BrowseFragment implements
}
private void updateRows() {
- int visibleRowsCount = 1; // Schedule's Row will never be empty
- if (mRecentAdapter.isEmpty()) {
+ int visibleRowsCount = 1; // Schedule's Row will never be empty
+ int recentRowMinSize = TvFeatures.DVR_FAILED_LIST.isEnabled(getContext()) ? 1 : 0;
+ if (mRecentAdapter.size() <= recentRowMinSize) {
mRowsAdapter.remove(mRecentRow);
} else {
if (mRowsAdapter.indexOf(mRecentRow) < 0) {
@@ -597,8 +685,9 @@ public class DvrBrowseFragment extends BrowseFragment implements
RecordedProgram latestProgram = null;
for (RecordedProgram program :
mDvrDataManager.getRecordedPrograms(seriesRecording.getId())) {
- if (latestProgram == null || RecordedProgram
- .START_TIME_THEN_ID_COMPARATOR.compare(latestProgram, program) < 0) {
+ if (latestProgram == null
+ || RecordedProgram.START_TIME_THEN_ID_COMPARATOR.compare(latestProgram, program)
+ < 0) {
latestProgram = program;
}
}
@@ -622,17 +711,19 @@ public class DvrBrowseFragment extends BrowseFragment implements
private class SeriesAdapter extends SortedArrayAdapter<SeriesRecording> {
SeriesAdapter() {
- super(mPresenterSelector, new Comparator<SeriesRecording>() {
- @Override
- public int compare(SeriesRecording lhs, SeriesRecording rhs) {
- if (lhs.isStopped() && !rhs.isStopped()) {
- return 1;
- } else if (!lhs.isStopped() && rhs.isStopped()) {
- return -1;
- }
- return SeriesRecording.PRIORITY_COMPARATOR.compare(lhs, rhs);
- }
- });
+ super(
+ mPresenterSelector,
+ new Comparator<SeriesRecording>() {
+ @Override
+ public int compare(SeriesRecording lhs, SeriesRecording rhs) {
+ if (lhs.isStopped() && !rhs.isStopped()) {
+ return 1;
+ } else if (!lhs.isStopped() && rhs.isStopped()) {
+ return -1;
+ }
+ return SeriesRecording.PRIORITY_COMPARATOR.compare(lhs, rhs);
+ }
+ });
}
@Override
@@ -662,4 +753,22 @@ public class DvrBrowseFragment extends BrowseFragment implements
}
}
}
-} \ No newline at end of file
+
+ private class RecentRowAdapter extends SortedArrayAdapter<Object> {
+ RecentRowAdapter(int maxItemCount) {
+ super(mPresenterSelector, RECENT_ROW_COMPARATOR, maxItemCount);
+ }
+
+ @Override
+ public long getId(Object item) {
+ // We takes the inverse number for the ID of scheduled recordings to make the ID stable.
+ if (item instanceof ScheduledRecording) {
+ return -((ScheduledRecording) item).getId() - 1;
+ } else if (item instanceof RecordedProgram) {
+ return ((RecordedProgram) item).getId();
+ } else {
+ return -1;
+ }
+ }
+ }
+}
diff --git a/src/com/android/tv/dvr/ui/browse/DvrDetailsActivity.java b/src/com/android/tv/dvr/ui/browse/DvrDetailsActivity.java
index 35d21db8..0336b319 100644
--- a/src/com/android/tv/dvr/ui/browse/DvrDetailsActivity.java
+++ b/src/com/android/tv/dvr/ui/browse/DvrDetailsActivity.java
@@ -19,21 +19,16 @@ package com.android.tv.dvr.ui.browse;
import android.app.Activity;
import android.os.Bundle;
import android.support.v17.leanback.app.DetailsFragment;
-
import android.transition.Transition;
import android.transition.Transition.TransitionListener;
import android.view.View;
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.Starter;
import com.android.tv.dialog.PinDialogFragment;
-/**
- * Activity to show details view in DVR.
- */
+/** Activity to show details view in DVR. */
public class DvrDetailsActivity extends Activity implements PinDialogFragment.OnPinCheckedListener {
- /**
- * Name of record id added to the Intent.
- */
+ /** Name of record id added to the Intent. */
public static final String RECORDING_ID = "record_id";
/**
@@ -42,46 +37,38 @@ public class DvrDetailsActivity extends Activity implements PinDialogFragment.On
*/
public static final String HIDE_VIEW_SCHEDULE = "hide_view_schedule";
- /**
- * Name of details view's type added to the intent.
- */
+ /** Name of details view's type added to the intent. */
public static final String DETAILS_VIEW_TYPE = "details_view_type";
- /**
- * Name of shared element between activities.
- */
+ /** Name of shared element between activities. */
public static final String SHARED_ELEMENT_NAME = "shared_element";
- /**
- * CURRENT_RECORDING_VIEW refers to Current Recordings in DVR.
- */
+ /** Name of error message of a failed recording */
+ public static final String EXTRA_FAILED_MESSAGE = "failed_message";
+
+ /** CURRENT_RECORDING_VIEW refers to Current Recordings in DVR. */
public static final int CURRENT_RECORDING_VIEW = 1;
- /**
- * SCHEDULED_RECORDING_VIEW refers to Scheduled Recordings in DVR.
- */
+ /** SCHEDULED_RECORDING_VIEW refers to Scheduled Recordings in DVR. */
public static final int SCHEDULED_RECORDING_VIEW = 2;
- /**
- * RECORDED_PROGRAM_VIEW refers to Recorded programs in DVR.
- */
+ /** RECORDED_PROGRAM_VIEW refers to Recorded programs in DVR. */
public static final int RECORDED_PROGRAM_VIEW = 3;
- /**
- * SERIES_RECORDING_VIEW refers to series recording in DVR.
- */
+ /** SERIES_RECORDING_VIEW refers to series recording in DVR. */
public static final int SERIES_RECORDING_VIEW = 4;
private PinDialogFragment.OnPinCheckedListener mOnPinCheckedListener;
@Override
public void onCreate(Bundle savedInstanceState) {
- TvApplication.setCurrentRunningProcess(this, true);
+ Starter.start(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dvr_details);
long recordId = getIntent().getLongExtra(RECORDING_ID, -1);
int detailsViewType = getIntent().getIntExtra(DETAILS_VIEW_TYPE, -1);
boolean hideViewSchedule = getIntent().getBooleanExtra(HIDE_VIEW_SCHEDULE, false);
+ String failedMsg = getIntent().getStringExtra(EXTRA_FAILED_MESSAGE);
if (recordId != -1 && detailsViewType != -1 && savedInstanceState == null) {
Bundle args = new Bundle();
args.putLong(RECORDING_ID, recordId);
@@ -90,6 +77,7 @@ public class DvrDetailsActivity extends Activity implements PinDialogFragment.On
detailsFragment = new CurrentRecordingDetailsFragment();
} else if (detailsViewType == SCHEDULED_RECORDING_VIEW) {
args.putBoolean(HIDE_VIEW_SCHEDULE, hideViewSchedule);
+ args.putString(EXTRA_FAILED_MESSAGE, failedMsg);
detailsFragment = new ScheduledRecordingDetailsFragment();
} else if (detailsViewType == RECORDED_PROGRAM_VIEW) {
detailsFragment = new RecordedProgramDetailsFragment();
@@ -97,8 +85,10 @@ public class DvrDetailsActivity extends Activity implements PinDialogFragment.On
detailsFragment = new SeriesRecordingDetailsFragment();
}
detailsFragment.setArguments(args);
- getFragmentManager().beginTransaction()
- .replace(R.id.dvr_details_view_frame, detailsFragment).commit();
+ getFragmentManager()
+ .beginTransaction()
+ .replace(R.id.dvr_details_view_frame, detailsFragment)
+ .commit();
}
// This is a workaround for the focus on O device
diff --git a/src/com/android/tv/dvr/ui/browse/DvrDetailsFragment.java b/src/com/android/tv/dvr/ui/browse/DvrDetailsFragment.java
index 19fb7117..8f4e4dab 100644
--- a/src/com/android/tv/dvr/ui/browse/DvrDetailsFragment.java
+++ b/src/com/android/tv/dvr/ui/browse/DvrDetailsFragment.java
@@ -36,21 +36,19 @@ import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
import android.support.v17.leanback.widget.VerticalGridView;
import android.text.TextUtils;
import android.widget.Toast;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
-import com.android.tv.data.Channel;
+import com.android.tv.common.util.CommonUtils;
import com.android.tv.data.ChannelDataManager;
+import com.android.tv.data.api.Channel;
import com.android.tv.dialog.PinDialogFragment;
import com.android.tv.dialog.PinDialogFragment.OnPinCheckedListener;
import com.android.tv.dvr.data.RecordedProgram;
import com.android.tv.dvr.ui.DvrUiHelper;
import com.android.tv.parental.ParentalControlSettings;
-import com.android.tv.util.ImageLoader;
import com.android.tv.util.ToastUtils;
-import com.android.tv.util.Utils;
-
+import com.android.tv.util.images.ImageLoader;
import java.io.File;
abstract class DvrDetailsFragment extends DetailsFragment {
@@ -77,8 +75,8 @@ abstract class DvrDetailsFragment extends DetailsFragment {
public void onStart() {
super.onStart();
// TODO: remove the workaround of b/30401180.
- VerticalGridView container = (VerticalGridView) getActivity()
- .findViewById(R.id.container_list);
+ VerticalGridView container =
+ (VerticalGridView) getActivity().findViewById(R.id.container_list);
// Need to manually modify offset. Please refer DetailsFragment.setVerticalGridViewLayout.
container.setItemAlignmentOffset(0);
container.setWindowAlignmentOffset(
@@ -86,27 +84,23 @@ abstract class DvrDetailsFragment extends DetailsFragment {
}
private void setupAdapter() {
- DetailsOverviewRowPresenter rowPresenter = new DetailsOverviewRowPresenter(
- new DetailsContentPresenter(getActivity()));
- rowPresenter.setBackgroundColor(getResources().getColor(R.color.common_tv_background,
- null));
- rowPresenter.setSharedElementEnterTransition(getActivity(),
- DvrDetailsActivity.SHARED_ELEMENT_NAME);
+ DetailsOverviewRowPresenter rowPresenter =
+ new DetailsOverviewRowPresenter(new DetailsContentPresenter(getActivity()));
+ rowPresenter.setBackgroundColor(
+ getResources().getColor(R.color.common_tv_background, null));
+ rowPresenter.setSharedElementEnterTransition(
+ getActivity(), DvrDetailsActivity.SHARED_ELEMENT_NAME);
rowPresenter.setOnActionClickedListener(onCreateOnActionClickedListener());
mRowsAdapter = new ArrayObjectAdapter(onCreatePresenterSelector(rowPresenter));
setAdapter(mRowsAdapter);
}
- /**
- * Returns details views' rows adapter.
- */
+ /** Returns details views' rows adapter. */
protected ArrayObjectAdapter getRowsAdapter() {
- return mRowsAdapter;
+ return mRowsAdapter;
}
- /**
- * Sets details overview.
- */
+ /** Sets details overview. */
protected void setDetailsOverviewRow(DetailsContent detailsContent) {
mDetailsOverview = new DetailsOverviewRow(detailsContent);
mDetailsOverview.setActionsAdapter(onCreateActionsAdapter());
@@ -114,9 +108,7 @@ abstract class DvrDetailsFragment extends DetailsFragment {
onLoadLogoAndBackgroundImages(detailsContent);
}
- /**
- * Creates and returns presenter selector will be used by rows adaptor.
- */
+ /** Creates and returns presenter selector will be used by rows adaptor. */
protected PresenterSelector onCreatePresenterSelector(
DetailsOverviewRowPresenter rowPresenter) {
ClassPresenterSelector presenterSelector = new ClassPresenterSelector();
@@ -130,11 +122,9 @@ abstract class DvrDetailsFragment extends DetailsFragment {
* do anything after calling {@link #onCreate(Bundle)}. If there's something subclasses have to
* do after the super class did onCreate, it should override this method and put the codes here.
*/
- protected void onCreateInternal() { }
+ protected void onCreateInternal() {}
- /**
- * Updates actions of details overview.
- */
+ /** Updates actions of details overview. */
protected void updateActions() {
mDetailsOverview.setActionsAdapter(onCreateActionsAdapter());
}
@@ -142,14 +132,12 @@ abstract class DvrDetailsFragment extends DetailsFragment {
/**
* Loads recording details according to the arguments the fragment got.
*
- * @return false if cannot find valid recordings, else return true. If the return value
- * is false, the detail activity and fragment will be ended.
+ * @return false if cannot find valid recordings, else return true. If the return value is
+ * false, the detail activity and fragment will be ended.
*/
abstract boolean onLoadRecordingDetails(Bundle args);
- /**
- * Creates actions users can interact with and their adaptor for this fragment.
- */
+ /** Creates actions users can interact with and their adaptor for this fragment. */
abstract SparseArrayObjectAdapter onCreateActionsAdapter();
/**
@@ -158,66 +146,76 @@ abstract class DvrDetailsFragment extends DetailsFragment {
*/
abstract OnActionClickedListener onCreateOnActionClickedListener();
- /**
- * Loads logo and background images for detail fragments.
- */
+ /** Loads logo and background images for detail fragments. */
protected void onLoadLogoAndBackgroundImages(DetailsContent detailsContent) {
Drawable logoDrawable = null;
Drawable backgroundDrawable = null;
if (TextUtils.isEmpty(detailsContent.getLogoImageUri())) {
- logoDrawable = getContext().getResources()
- .getDrawable(R.drawable.dvr_default_poster, null);
+ logoDrawable =
+ getContext().getResources().getDrawable(R.drawable.dvr_default_poster, null);
mDetailsOverview.setImageDrawable(logoDrawable);
}
if (TextUtils.isEmpty(detailsContent.getBackgroundImageUri())) {
- backgroundDrawable = getContext().getResources()
- .getDrawable(R.drawable.dvr_default_poster, null);
+ backgroundDrawable =
+ getContext().getResources().getDrawable(R.drawable.dvr_default_poster, null);
mBackgroundHelper.setBackground(backgroundDrawable);
}
if (logoDrawable != null && backgroundDrawable != null) {
return;
}
- if (logoDrawable == null && backgroundDrawable == null
- && detailsContent.getLogoImageUri().equals(
- detailsContent.getBackgroundImageUri())) {
- ImageLoader.loadBitmap(getContext(), detailsContent.getLogoImageUri(),
- new MyImageLoaderCallback(this, LOAD_LOGO_IMAGE | LOAD_BACKGROUND_IMAGE,
- getContext()));
+ if (logoDrawable == null
+ && backgroundDrawable == null
+ && detailsContent
+ .getLogoImageUri()
+ .equals(detailsContent.getBackgroundImageUri())) {
+ ImageLoader.loadBitmap(
+ getContext(),
+ detailsContent.getLogoImageUri(),
+ new MyImageLoaderCallback(
+ this, LOAD_LOGO_IMAGE | LOAD_BACKGROUND_IMAGE, getContext()));
return;
}
if (logoDrawable == null) {
int imageWidth = getResources().getDimensionPixelSize(R.dimen.dvr_details_poster_width);
- int imageHeight = getResources()
- .getDimensionPixelSize(R.dimen.dvr_details_poster_height);
- ImageLoader.loadBitmap(getContext(), detailsContent.getLogoImageUri(),
- imageWidth, imageHeight,
+ int imageHeight =
+ getResources().getDimensionPixelSize(R.dimen.dvr_details_poster_height);
+ ImageLoader.loadBitmap(
+ getContext(),
+ detailsContent.getLogoImageUri(),
+ imageWidth,
+ imageHeight,
new MyImageLoaderCallback(this, LOAD_LOGO_IMAGE, getContext()));
}
if (backgroundDrawable == null) {
- ImageLoader.loadBitmap(getContext(), detailsContent.getBackgroundImageUri(),
+ ImageLoader.loadBitmap(
+ getContext(),
+ detailsContent.getBackgroundImageUri(),
new MyImageLoaderCallback(this, LOAD_BACKGROUND_IMAGE, getContext()));
}
}
protected void startPlayback(RecordedProgram recordedProgram, long seekTimeMs) {
- if (Utils.isInBundledPackageSet(recordedProgram.getPackageName()) &&
- !isDataUriAccessible(recordedProgram.getDataUri())) {
+ if (CommonUtils.isInBundledPackageSet(recordedProgram.getPackageName())
+ && !isDataUriAccessible(recordedProgram.getDataUri())) {
// Since cleaning RecordedProgram from forgotten storage will take some time,
// ignore playback until cleaning is finished.
- ToastUtils.show(getContext(),
+ ToastUtils.show(
+ getContext(),
getContext().getResources().getString(R.string.dvr_toast_recording_deleted),
Toast.LENGTH_SHORT);
return;
}
long programId = recordedProgram.getId();
- ParentalControlSettings parental = TvApplication.getSingletons(getActivity())
- .getTvInputManagerHelper().getParentalControlSettings();
+ ParentalControlSettings parental =
+ TvSingletons.getSingletons(getActivity())
+ .getTvInputManagerHelper()
+ .getParentalControlSettings();
if (!parental.isParentalControlsEnabled()) {
DvrUiHelper.startPlaybackActivity(getContext(), programId, seekTimeMs, false);
return;
}
ChannelDataManager channelDataManager =
- TvApplication.getSingletons(getActivity()).getChannelDataManager();
+ TvSingletons.getSingletons(getActivity()).getChannelDataManager();
Channel channel = channelDataManager.getChannel(recordedProgram.getChannelId());
if (channel != null && channel.isLocked()) {
checkPinToPlay(recordedProgram, seekTimeMs);
@@ -249,36 +247,43 @@ abstract class DvrDetailsFragment extends DetailsFragment {
private void checkPinToPlay(RecordedProgram recordedProgram, long seekTimeMs) {
SoftPreconditions.checkState(getActivity() instanceof DvrDetailsActivity);
if (getActivity() instanceof DvrDetailsActivity) {
- ((DvrDetailsActivity) getActivity()).setOnPinCheckListener(new OnPinCheckedListener() {
- @Override
- public void onPinChecked(boolean checked, int type, String rating) {
- ((DvrDetailsActivity) getActivity()).setOnPinCheckListener(null);
- if (checked && type == PinDialogFragment.PIN_DIALOG_TYPE_UNLOCK_PROGRAM) {
- DvrUiHelper.startPlaybackActivity(getContext(), recordedProgram.getId(),
- seekTimeMs, true);
- }
- }
- });
+ ((DvrDetailsActivity) getActivity())
+ .setOnPinCheckListener(
+ new OnPinCheckedListener() {
+ @Override
+ public void onPinChecked(boolean checked, int type, String rating) {
+ ((DvrDetailsActivity) getActivity())
+ .setOnPinCheckListener(null);
+ if (checked
+ && type
+ == PinDialogFragment
+ .PIN_DIALOG_TYPE_UNLOCK_PROGRAM) {
+ DvrUiHelper.startPlaybackActivity(
+ getContext(),
+ recordedProgram.getId(),
+ seekTimeMs,
+ true);
+ }
+ }
+ });
PinDialogFragment.create(PinDialogFragment.PIN_DIALOG_TYPE_UNLOCK_PROGRAM)
.show(getActivity().getFragmentManager(), PinDialogFragment.DIALOG_TAG);
}
}
- private static class MyImageLoaderCallback extends
- ImageLoader.ImageLoaderCallback<DvrDetailsFragment> {
+ private static class MyImageLoaderCallback
+ extends ImageLoader.ImageLoaderCallback<DvrDetailsFragment> {
private final Context mContext;
private final int mLoadType;
- public MyImageLoaderCallback(DvrDetailsFragment fragment,
- int loadType, Context context) {
+ public MyImageLoaderCallback(DvrDetailsFragment fragment, int loadType, Context context) {
super(fragment);
mLoadType = loadType;
mContext = context;
}
@Override
- public void onBitmapLoaded(DvrDetailsFragment fragment,
- @Nullable Bitmap bitmap) {
+ public void onBitmapLoaded(DvrDetailsFragment fragment, @Nullable Bitmap bitmap) {
Drawable drawable;
int loadType = mLoadType;
if (bitmap == null) {
diff --git a/common/src/com/android/tv/common/annotation/UsedByReflection.java b/src/com/android/tv/dvr/ui/browse/DvrHistoryCardHolder.java
index 5a4517f7..c6288ef0 100644
--- a/common/src/com/android/tv/common/annotation/UsedByReflection.java
+++ b/src/com/android/tv/dvr/ui/browse/DvrHistoryCardHolder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2018 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,16 +14,12 @@
* limitations under the License.
*/
-package com.android.tv.common.annotation;
+package com.android.tv.dvr.ui.browse;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
+/** Special object for schedule preview; */
+final class DvrHistoryCardHolder {
+ /** Full schedule card holder. */
+ static final DvrHistoryCardHolder DVR_HISTORY_CARD_HOLDER = new DvrHistoryCardHolder();
-import java.lang.annotation.Retention;
-
-/**
- * Denotes that the method or field is used by reflection even though it is not ever called
- * directly.
- */
-@Retention(SOURCE)
-public @interface UsedByReflection {
+ private DvrHistoryCardHolder() {}
}
diff --git a/src/com/android/tv/dvr/ui/browse/DvrHistoryCardPresenter.java b/src/com/android/tv/dvr/ui/browse/DvrHistoryCardPresenter.java
new file mode 100644
index 00000000..62c050c9
--- /dev/null
+++ b/src/com/android/tv/dvr/ui/browse/DvrHistoryCardPresenter.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 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.browse;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import com.android.tv.R;
+import com.android.tv.dvr.ui.DvrUiHelper;
+
+/** Presents a DVR history card view in the {@link DvrBrowseFragment}. */
+class DvrHistoryCardPresenter extends DvrItemPresenter<Object> {
+ private final Drawable mIconDrawable;
+ private final String mCardTitleText;
+
+ DvrHistoryCardPresenter(Context context) {
+ super(context);
+ mIconDrawable = mContext.getDrawable(R.drawable.dvr_full_schedule);
+ mCardTitleText = mContext.getString(R.string.dvr_history_card_view_title);
+ }
+
+ @Override
+ public DvrItemViewHolder onCreateDvrItemViewHolder() {
+ return new DvrItemViewHolder(new RecordingCardView(mContext));
+ }
+
+ @Override
+ public void onBindDvrItemViewHolder(DvrItemViewHolder vh, Object o) {
+ final RecordingCardView cardView = (RecordingCardView) vh.view;
+
+ cardView.setTitle(mCardTitleText);
+ cardView.setImage(mIconDrawable);
+ }
+
+ @Override
+ public void onUnbindViewHolder(ViewHolder vh) {
+ ((RecordingCardView) vh.view).reset();
+ super.onUnbindViewHolder(vh);
+ }
+
+ @Override
+ protected View.OnClickListener onCreateOnClickListener() {
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ DvrUiHelper.startDvrHistoryActivity(mContext);
+ }
+ };
+ }
+}
diff --git a/src/com/android/tv/dvr/ui/browse/DvrItemPresenter.java b/src/com/android/tv/dvr/ui/browse/DvrItemPresenter.java
index df0e61c1..4298d86a 100644
--- a/src/com/android/tv/dvr/ui/browse/DvrItemPresenter.java
+++ b/src/com/android/tv/dvr/ui/browse/DvrItemPresenter.java
@@ -23,18 +23,15 @@ import android.support.v17.leanback.widget.Presenter;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-
import com.android.tv.common.SoftPreconditions;
import com.android.tv.dvr.ui.DvrUiHelper;
-
import java.util.HashSet;
import java.util.Set;
/**
* An abstract class to present DVR items in {@link RecordingCardView}, which is mainly used in
- * {@link DvrBrowseFragment}. DVR items might include:
- * {@link com.android.tv.dvr.data.ScheduledRecording},
- * {@link com.android.tv.dvr.data.RecordedProgram}, and
+ * {@link DvrBrowseFragment}. DVR items might include: {@link
+ * com.android.tv.dvr.data.ScheduledRecording}, {@link com.android.tv.dvr.data.RecordedProgram}, and
* {@link com.android.tv.dvr.data.SeriesRecording}.
*/
public abstract class DvrItemPresenter<T> extends Presenter {
@@ -51,9 +48,9 @@ public abstract class DvrItemPresenter<T> extends Presenter {
return (RecordingCardView) view;
}
- protected void onBound(T item) { }
+ protected void onBound(T item) {}
- protected void onUnbound() { }
+ protected void onUnbound() {}
}
DvrItemPresenter(Context context) {
@@ -94,9 +91,7 @@ public abstract class DvrItemPresenter<T> extends Presenter {
viewHolder.view.setOnClickListener(null);
}
- /**
- * Unbinds all bound view holders.
- */
+ /** Unbinds all bound view holders. */
public void unbindAllViewHolders() {
// When browse fragments are destroyed, RecyclerView would not call presenters'
// onUnbindViewHolder(). We should handle it by ourselves to prevent resources leaks.
@@ -105,36 +100,28 @@ public abstract class DvrItemPresenter<T> extends Presenter {
}
}
- /**
- * This method will be called when a {@link DvrItemViewHolder} is needed to be created.
- */
- abstract protected DvrItemViewHolder onCreateDvrItemViewHolder();
+ /** This method will be called when a {@link DvrItemViewHolder} is needed to be created. */
+ protected abstract DvrItemViewHolder onCreateDvrItemViewHolder();
- /**
- * This method will be called when a {@link DvrItemViewHolder} is bound to a DVR item.
- */
- abstract protected void onBindDvrItemViewHolder(DvrItemViewHolder viewHolder, T item);
+ /** This method will be called when a {@link DvrItemViewHolder} is bound to a DVR item. */
+ protected abstract void onBindDvrItemViewHolder(DvrItemViewHolder viewHolder, T item);
- /**
- * Returns context.
- */
+ /** Returns context. */
protected Context getContext() {
return mContext;
}
- /**
- * Creates {@link OnClickListener} for DVR library's card views.
- */
+ /** Creates {@link OnClickListener} for DVR library's card views. */
protected OnClickListener onCreateOnClickListener() {
return new OnClickListener() {
@Override
public void onClick(View view) {
if (view instanceof RecordingCardView) {
RecordingCardView v = (RecordingCardView) view;
- DvrUiHelper.startDetailsActivity((Activity) v.getContext(),
- v.getTag(), v.getImageView(), false);
+ DvrUiHelper.startDetailsActivity(
+ (Activity) v.getContext(), v.getTag(), v.getImageView(), false);
}
}
};
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/browse/DvrListRowPresenter.java b/src/com/android/tv/dvr/ui/browse/DvrListRowPresenter.java
index 37a72eaf..a2d1cb28 100644
--- a/src/com/android/tv/dvr/ui/browse/DvrListRowPresenter.java
+++ b/src/com/android/tv/dvr/ui/browse/DvrListRowPresenter.java
@@ -19,7 +19,6 @@ package com.android.tv.dvr.ui.browse;
import android.content.Context;
import android.support.v17.leanback.widget.ListRowPresenter;
import android.view.ViewGroup;
-
import com.android.tv.R;
/** A list row presenter to display expand/fold card views list. */
diff --git a/src/com/android/tv/dvr/ui/browse/FullScheduleCardHolder.java b/src/com/android/tv/dvr/ui/browse/FullScheduleCardHolder.java
index 311137a9..6def818f 100644
--- a/src/com/android/tv/dvr/ui/browse/FullScheduleCardHolder.java
+++ b/src/com/android/tv/dvr/ui/browse/FullScheduleCardHolder.java
@@ -16,14 +16,10 @@
package com.android.tv.dvr.ui.browse;
-/**
- * Special object for schedule preview;
- */
+/** Special object for schedule preview; */
final class FullScheduleCardHolder {
- /**
- * Full schedule card holder.
- */
+ /** Full schedule card holder. */
static final FullScheduleCardHolder FULL_SCHEDULE_CARD_HOLDER = new FullScheduleCardHolder();
- private FullScheduleCardHolder() { }
+ private FullScheduleCardHolder() {}
}
diff --git a/src/com/android/tv/dvr/ui/browse/FullSchedulesCardPresenter.java b/src/com/android/tv/dvr/ui/browse/FullSchedulesCardPresenter.java
index 94c67eec..af0f24c0 100644
--- a/src/com/android/tv/dvr/ui/browse/FullSchedulesCardPresenter.java
+++ b/src/com/android/tv/dvr/ui/browse/FullSchedulesCardPresenter.java
@@ -19,20 +19,15 @@ package com.android.tv.dvr.ui.browse;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.View;
-import android.view.ViewGroup;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.ui.DvrUiHelper;
import com.android.tv.util.Utils;
-
import java.util.Collections;
import java.util.List;
-/**
- * Presents a {@link ScheduledRecording} in the {@link DvrBrowseFragment}.
- */
+/** Presents a {@link ScheduledRecording} in the {@link DvrBrowseFragment}. */
class FullSchedulesCardPresenter extends DvrItemPresenter<Object> {
private final Drawable mIconDrawable;
private final String mCardTitleText;
@@ -54,16 +49,26 @@ class FullSchedulesCardPresenter extends DvrItemPresenter<Object> {
cardView.setTitle(mCardTitleText);
cardView.setImage(mIconDrawable);
- List<ScheduledRecording> scheduledRecordings = TvApplication.getSingletons(mContext)
- .getDvrDataManager().getAvailableScheduledRecordings();
+ List<ScheduledRecording> scheduledRecordings =
+ TvSingletons.getSingletons(mContext)
+ .getDvrDataManager()
+ .getAvailableScheduledRecordings();
int fullDays = 0;
if (!scheduledRecordings.isEmpty()) {
- fullDays = Utils.computeDateDifference(System.currentTimeMillis(),
- Collections.max(scheduledRecordings, ScheduledRecording.START_TIME_COMPARATOR)
- .getStartTimeMs()) + 1;
+ fullDays =
+ Utils.computeDateDifference(
+ System.currentTimeMillis(),
+ Collections.max(
+ scheduledRecordings,
+ ScheduledRecording.START_TIME_COMPARATOR)
+ .getStartTimeMs())
+ + 1;
}
- cardView.setContent(mContext.getResources().getQuantityString(
- R.plurals.dvr_full_schedule_card_view_content, fullDays, fullDays), null);
+ cardView.setContent(
+ mContext.getResources()
+ .getQuantityString(
+ R.plurals.dvr_full_schedule_card_view_content, fullDays, fullDays),
+ null);
}
@Override
@@ -81,4 +86,4 @@ class FullSchedulesCardPresenter extends DvrItemPresenter<Object> {
}
};
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/browse/RecordedProgramDetailsFragment.java b/src/com/android/tv/dvr/ui/browse/RecordedProgramDetailsFragment.java
index eb9cb26c..47b1a198 100644
--- a/src/com/android/tv/dvr/ui/browse/RecordedProgramDetailsFragment.java
+++ b/src/com/android/tv/dvr/ui/browse/RecordedProgramDetailsFragment.java
@@ -22,17 +22,14 @@ import android.os.Bundle;
import android.support.v17.leanback.widget.Action;
import android.support.v17.leanback.widget.OnActionClickedListener;
import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.DvrWatchedPositionManager;
import com.android.tv.dvr.data.RecordedProgram;
-/**
- * {@link android.support.v17.leanback.app.DetailsFragment} for recorded program in DVR.
- */
+/** {@link android.support.v17.leanback.app.DetailsFragment} for recorded program in DVR. */
public class RecordedProgramDetailsFragment extends DvrDetailsFragment
implements DvrDataManager.RecordedProgramListener {
private static final int ACTION_RESUME_PLAYING = 1;
@@ -47,17 +44,17 @@ public class RecordedProgramDetailsFragment extends DvrDetailsFragment
@Override
public void onCreate(Bundle savedInstanceState) {
- mDvrDataManager = TvApplication.getSingletons(getContext()).getDvrDataManager();
+ mDvrDataManager = TvSingletons.getSingletons(getContext()).getDvrDataManager();
mDvrDataManager.addRecordedProgramListener(this);
super.onCreate(savedInstanceState);
}
@Override
public void onCreateInternal() {
- mDvrWatchedPositionManager = TvApplication.getSingletons(getActivity())
- .getDvrWatchedPositionManager();
- setDetailsOverviewRow(DetailsContent
- .createFromRecordedProgram(getContext(), mRecordedProgram));
+ mDvrWatchedPositionManager =
+ TvSingletons.getSingletons(getActivity()).getDvrWatchedPositionManager();
+ setDetailsOverviewRow(
+ DetailsContent.createFromRecordedProgram(getContext(), mRecordedProgram));
}
@Override
@@ -95,20 +92,36 @@ public class RecordedProgramDetailsFragment extends DvrDetailsFragment
Resources res = getResources();
if (mDvrWatchedPositionManager.getWatchedStatus(mRecordedProgram)
== DvrWatchedPositionManager.DVR_WATCHED_STATUS_WATCHING) {
- adapter.set(ACTION_RESUME_PLAYING, new Action(ACTION_RESUME_PLAYING,
- res.getString(R.string.dvr_detail_resume_play), null,
- res.getDrawable(R.drawable.lb_ic_play)));
- adapter.set(ACTION_PLAY_FROM_BEGINNING, new Action(ACTION_PLAY_FROM_BEGINNING,
- res.getString(R.string.dvr_detail_play_from_beginning), null,
- res.getDrawable(R.drawable.lb_ic_replay)));
+ adapter.set(
+ ACTION_RESUME_PLAYING,
+ new Action(
+ ACTION_RESUME_PLAYING,
+ res.getString(R.string.dvr_detail_resume_play),
+ null,
+ res.getDrawable(R.drawable.lb_ic_play)));
+ adapter.set(
+ ACTION_PLAY_FROM_BEGINNING,
+ new Action(
+ ACTION_PLAY_FROM_BEGINNING,
+ res.getString(R.string.dvr_detail_play_from_beginning),
+ null,
+ res.getDrawable(R.drawable.lb_ic_replay)));
} else {
- adapter.set(ACTION_PLAY_FROM_BEGINNING, new Action(ACTION_PLAY_FROM_BEGINNING,
- res.getString(R.string.dvr_detail_watch), null,
- res.getDrawable(R.drawable.lb_ic_play)));
+ adapter.set(
+ ACTION_PLAY_FROM_BEGINNING,
+ new Action(
+ ACTION_PLAY_FROM_BEGINNING,
+ res.getString(R.string.dvr_detail_watch),
+ null,
+ res.getDrawable(R.drawable.lb_ic_play)));
}
- adapter.set(ACTION_DELETE_RECORDING, new Action(ACTION_DELETE_RECORDING,
- res.getString(R.string.dvr_detail_delete), null,
- res.getDrawable(R.drawable.ic_delete_32dp)));
+ adapter.set(
+ ACTION_DELETE_RECORDING,
+ new Action(
+ ACTION_DELETE_RECORDING,
+ res.getString(R.string.dvr_detail_delete),
+ null,
+ res.getDrawable(R.drawable.ic_delete_32dp)));
return adapter;
}
@@ -120,11 +133,13 @@ public class RecordedProgramDetailsFragment extends DvrDetailsFragment
if (action.getId() == ACTION_PLAY_FROM_BEGINNING) {
startPlayback(mRecordedProgram, TvInputManager.TIME_SHIFT_INVALID_TIME);
} else if (action.getId() == ACTION_RESUME_PLAYING) {
- startPlayback(mRecordedProgram, mDvrWatchedPositionManager
- .getWatchedPosition(mRecordedProgram.getId()));
+ startPlayback(
+ mRecordedProgram,
+ mDvrWatchedPositionManager.getWatchedPosition(
+ mRecordedProgram.getId()));
} else if (action.getId() == ACTION_DELETE_RECORDING) {
- DvrManager dvrManager = TvApplication
- .getSingletons(getActivity()).getDvrManager();
+ DvrManager dvrManager =
+ TvSingletons.getSingletons(getActivity()).getDvrManager();
dvrManager.removeRecordedProgram(mRecordedProgram);
getActivity().finish();
}
@@ -133,10 +148,10 @@ public class RecordedProgramDetailsFragment extends DvrDetailsFragment
}
@Override
- public void onRecordedProgramsAdded(RecordedProgram... recordedPrograms) { }
+ public void onRecordedProgramsAdded(RecordedProgram... recordedPrograms) {}
@Override
- public void onRecordedProgramsChanged(RecordedProgram... recordedPrograms) { }
+ public void onRecordedProgramsChanged(RecordedProgram... recordedPrograms) {}
@Override
public void onRecordedProgramsRemoved(RecordedProgram... recordedPrograms) {
diff --git a/src/com/android/tv/dvr/ui/browse/RecordedProgramPresenter.java b/src/com/android/tv/dvr/ui/browse/RecordedProgramPresenter.java
index 5fe162b6..e2db3ac4 100644
--- a/src/com/android/tv/dvr/ui/browse/RecordedProgramPresenter.java
+++ b/src/com/android/tv/dvr/ui/browse/RecordedProgramPresenter.java
@@ -18,17 +18,14 @@ package com.android.tv.dvr.ui.browse;
import android.content.Context;
import android.media.tv.TvInputManager;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.dvr.DvrWatchedPositionManager;
import com.android.tv.dvr.DvrWatchedPositionManager.WatchedPositionChangedListener;
import com.android.tv.dvr.data.RecordedProgram;
import com.android.tv.util.Utils;
-/**
- * Presents a {@link RecordedProgram} in the {@link DvrBrowseFragment}.
- */
+/** Presents a {@link RecordedProgram} in the {@link DvrBrowseFragment}. */
public class RecordedProgramPresenter extends DvrItemPresenter<RecordedProgram> {
private final DvrWatchedPositionManager mDvrWatchedPositionManager;
private String mTodayString;
@@ -53,10 +50,16 @@ public class RecordedProgramPresenter extends DvrItemPresenter<RecordedProgram>
}
private void setProgressBar(long watchedPositionMs) {
- ((RecordingCardView) view).setProgressBar(
- (watchedPositionMs == TvInputManager.TIME_SHIFT_INVALID_TIME) ? null
- : Math.min(100, (int) (100.0f * watchedPositionMs
- / mProgram.getDurationMillis())));
+ ((RecordingCardView) view)
+ .setProgressBar(
+ (watchedPositionMs == TvInputManager.TIME_SHIFT_INVALID_TIME)
+ ? null
+ : Math.min(
+ 100,
+ (int)
+ (100.0f
+ * watchedPositionMs
+ / mProgram.getDurationMillis())));
}
@Override
@@ -86,15 +89,15 @@ public class RecordedProgramPresenter extends DvrItemPresenter<RecordedProgram>
}
}
- RecordedProgramPresenter(Context context, boolean showEpisodeTitle,
- boolean expandTitleWhenFocused) {
+ RecordedProgramPresenter(
+ Context context, boolean showEpisodeTitle, boolean expandTitleWhenFocused) {
super(context);
mTodayString = mContext.getString(R.string.dvr_date_today);
mYesterdayString = mContext.getString(R.string.dvr_date_yesterday);
mDvrWatchedPositionManager =
- TvApplication.getSingletons(mContext).getDvrWatchedPositionManager();
- mProgressBarColor = mContext.getResources()
- .getColor(R.color.play_controls_progress_bar_watched);
+ TvSingletons.getSingletons(mContext).getDvrWatchedPositionManager();
+ mProgressBarColor =
+ mContext.getResources().getColor(R.color.play_controls_progress_bar_watched);
mShowEpisodeTitle = showEpisodeTitle;
mExpandTitleWhenFocused = expandTitleWhenFocused;
}
@@ -114,29 +117,37 @@ public class RecordedProgramPresenter extends DvrItemPresenter<RecordedProgram>
final RecordedProgramViewHolder viewHolder = (RecordedProgramViewHolder) baseHolder;
final RecordingCardView cardView = viewHolder.getView();
DetailsContent details = DetailsContent.createFromRecordedProgram(mContext, program);
- cardView.setTitle(mShowEpisodeTitle ?
- program.getEpisodeDisplayTitle(mContext) : details.getTitle());
+ cardView.setTitle(
+ mShowEpisodeTitle ? program.getEpisodeDisplayTitle(mContext) : details.getTitle());
cardView.setImageUri(details.getLogoImageUri(), details.isUsingChannelLogo());
cardView.setContent(generateMajorContent(program), generateMinorContent(program));
cardView.setDetailBackgroundImageUri(details.getBackgroundImageUri());
}
private String generateMajorContent(RecordedProgram program) {
- int dateDifference = Utils.computeDateDifference(program.getStartTimeUtcMillis(),
- System.currentTimeMillis());
+ int dateDifference =
+ Utils.computeDateDifference(
+ program.getStartTimeUtcMillis(), System.currentTimeMillis());
if (dateDifference == 0) {
return mTodayString;
} else if (dateDifference == 1) {
return mYesterdayString;
} else {
- return Utils.getDurationString(mContext, program.getStartTimeUtcMillis(),
- program.getStartTimeUtcMillis(), false, true, false, 0);
+ return Utils.getDurationString(
+ mContext,
+ program.getStartTimeUtcMillis(),
+ program.getStartTimeUtcMillis(),
+ false,
+ true,
+ false,
+ 0);
}
}
private String generateMinorContent(RecordedProgram program) {
int durationMinutes = Math.max(1, Utils.getRoundOffMinsFromMs(program.getDurationMillis()));
- return mContext.getResources().getQuantityString(
- R.plurals.dvr_program_duration, durationMinutes, durationMinutes);
+ return mContext.getResources()
+ .getQuantityString(
+ R.plurals.dvr_program_duration, durationMinutes, durationMinutes);
}
}
diff --git a/src/com/android/tv/dvr/ui/browse/RecordingCardView.java b/src/com/android/tv/dvr/ui/browse/RecordingCardView.java
index 767addc8..fe3c52d9 100644
--- a/src/com/android/tv/dvr/ui/browse/RecordingCardView.java
+++ b/src/com/android/tv/dvr/ui/browse/RecordingCardView.java
@@ -31,20 +31,19 @@ import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
-
import com.android.tv.R;
import com.android.tv.dvr.data.RecordedProgram;
import com.android.tv.ui.ViewUtils;
-import com.android.tv.util.ImageLoader;
+import com.android.tv.util.images.ImageLoader;
/**
- * A CardView for displaying info about a {@link com.android.tv.dvr.data.ScheduledRecording}
- * or {@link RecordedProgram} or {@link com.android.tv.dvr.data.SeriesRecording}.
+ * A CardView for displaying info about a {@link com.android.tv.dvr.data.ScheduledRecording} or
+ * {@link RecordedProgram} or {@link com.android.tv.dvr.data.SeriesRecording}.
*/
public class RecordingCardView extends BaseCardView {
// This value should be the same with
// android.support.v17.leanback.widget.FocusHighlightHelper.BrowseItemFocusHighlight.DURATION_MS
- private final static int ANIMATION_DURATION = 150;
+ private static final int ANIMATION_DURATION = 150;
private final ImageView mImageView;
private final int mImageWidth;
private final int mImageHeight;
@@ -70,16 +69,19 @@ public class RecordingCardView extends BaseCardView {
}
public RecordingCardView(Context context, boolean expandTitleWhenFocused) {
- this(context, context.getResources().getDimensionPixelSize(
- R.dimen.dvr_library_card_image_layout_width), context.getResources()
- .getDimensionPixelSize(R.dimen.dvr_library_card_image_layout_height),
+ this(
+ context,
+ context.getResources()
+ .getDimensionPixelSize(R.dimen.dvr_library_card_image_layout_width),
+ context.getResources()
+ .getDimensionPixelSize(R.dimen.dvr_library_card_image_layout_height),
expandTitleWhenFocused);
}
- public RecordingCardView(Context context, int imageWidth, int imageHeight,
- boolean expandTitleWhenFocused) {
+ public RecordingCardView(
+ Context context, int imageWidth, int imageHeight, boolean expandTitleWhenFocused) {
super(context);
- //TODO(dvr): move these to the layout XML.
+ // TODO(dvr): move these to the layout XML.
setCardType(BaseCardView.CARD_TYPE_INFO_UNDER_WITH_EXTRA);
setInfoVisibility(BaseCardView.CARD_REGION_VISIBLE_ALWAYS);
setFocusable(true);
@@ -99,21 +101,27 @@ public class RecordingCardView extends BaseCardView {
mTitleArea = (FrameLayout) findViewById(R.id.title_area);
mFoldedTitleView = (TextView) findViewById(R.id.title_one_line);
mExpandedTitleView = (TextView) findViewById(R.id.title_two_lines);
- mFoldedTitleHeight = getResources()
- .getDimensionPixelSize(R.dimen.dvr_library_card_folded_title_height);
- mExpandedTitleHeight = getResources()
- .getDimensionPixelSize(R.dimen.dvr_library_card_expanded_title_height);
+ mFoldedTitleHeight =
+ getResources().getDimensionPixelSize(R.dimen.dvr_library_card_folded_title_height);
+ mExpandedTitleHeight =
+ getResources()
+ .getDimensionPixelSize(R.dimen.dvr_library_card_expanded_title_height);
mExpandTitleAnimator = ValueAnimator.ofFloat(0.0f, 1.0f).setDuration(ANIMATION_DURATION);
- mExpandTitleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- float value = (Float) valueAnimator.getAnimatedValue();
- mExpandedTitleView.setAlpha(value);
- mFoldedTitleView.setAlpha(1.0f - value);
- ViewUtils.setLayoutHeight(mTitleArea, (int) (mFoldedTitleHeight
- + (mExpandedTitleHeight - mFoldedTitleHeight) * value));
- }
- });
+ mExpandTitleAnimator.addUpdateListener(
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ float value = (Float) valueAnimator.getAnimatedValue();
+ mExpandedTitleView.setAlpha(value);
+ mFoldedTitleView.setAlpha(1.0f - value);
+ ViewUtils.setLayoutHeight(
+ mTitleArea,
+ (int)
+ (mFoldedTitleHeight
+ + (mExpandedTitleHeight - mFoldedTitleHeight)
+ * value));
+ }
+ });
mExpandTitleWhenFocused = expandTitleWhenFocused;
}
@@ -124,8 +132,12 @@ public class RecordingCardView extends BaseCardView {
// loading and drawing background images during activity transitions.
if (gainFocus) {
if (!TextUtils.isEmpty(mDetailBackgroundImageUri)) {
- ImageLoader.loadBitmap(getContext(), mDetailBackgroundImageUri,
- Integer.MAX_VALUE, Integer.MAX_VALUE, null);
+ ImageLoader.loadBitmap(
+ getContext(),
+ mDetailBackgroundImageUri,
+ Integer.MAX_VALUE,
+ Integer.MAX_VALUE,
+ null);
}
}
if (mExpandTitleWhenFocused) {
@@ -186,9 +198,7 @@ public class RecordingCardView extends BaseCardView {
}
}
- /**
- * Sets progress bar. If progress is {@code null}, hides progress bar.
- */
+ /** Sets progress bar. If progress is {@code null}, hides progress bar. */
void setProgressBar(Integer progress) {
if (progress == null) {
mProgressBar.setVisibility(View.GONE);
@@ -198,16 +208,14 @@ public class RecordingCardView extends BaseCardView {
}
}
- /**
- * Sets the color of progress bar.
- */
+ /** Sets the color of progress bar. */
void setProgressBarColor(int color) {
mProgressBar.getProgressDrawable().setTint(color);
}
/**
* Sets the image URI of the poster should be shown on the card view.
-
+ *
* @param isChannelLogo {@code true} if the image is from channels' logo.
*/
void setImageUri(String uri, boolean isChannelLogo) {
@@ -220,14 +228,16 @@ public class RecordingCardView extends BaseCardView {
if (TextUtils.isEmpty(uri)) {
mImageView.setImageDrawable(mDefaultImage);
} else {
- ImageLoader.loadBitmap(getContext(), uri, mImageWidth, mImageHeight,
+ ImageLoader.loadBitmap(
+ getContext(),
+ uri,
+ mImageWidth,
+ mImageHeight,
new RecordingCardImageLoaderCallback(this, uri));
}
}
- /**
- * Sets the {@link Drawable} of the poster should be shown on the card view.
- */
+ /** Sets the {@link Drawable} of the poster should be shown on the card view. */
public void setImage(Drawable image) {
if (image != null) {
mImageView.setImageDrawable(image);
@@ -255,9 +265,7 @@ public class RecordingCardView extends BaseCardView {
mDetailBackgroundImageUri = uri;
}
- /**
- * Returns image view.
- */
+ /** Returns image view. */
public ImageView getImageView() {
return mImageView;
}
@@ -287,4 +295,4 @@ public class RecordingCardView extends BaseCardView {
setContent(null, null);
mImageView.setImageDrawable(mDefaultImage);
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/browse/RecordingDetailsFragment.java b/src/com/android/tv/dvr/ui/browse/RecordingDetailsFragment.java
index 56ec357f..aa2ccf75 100644
--- a/src/com/android/tv/dvr/ui/browse/RecordingDetailsFragment.java
+++ b/src/com/android/tv/dvr/ui/browse/RecordingDetailsFragment.java
@@ -18,34 +18,35 @@ package com.android.tv.dvr.ui.browse;
import android.os.Bundle;
import android.support.v17.leanback.app.DetailsFragment;
-
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.dvr.data.ScheduledRecording;
-/**
- * {@link DetailsFragment} for recordings in DVR.
- */
+/** {@link DetailsFragment} for recordings in DVR. */
abstract class RecordingDetailsFragment extends DvrDetailsFragment {
private ScheduledRecording mRecording;
@Override
protected void onCreateInternal() {
- setDetailsOverviewRow(DetailsContent
- .createFromScheduledRecording(getContext(), mRecording));
+ setDetailsOverviewRow(
+ DetailsContent.createFromScheduledRecording(getContext(), mRecording));
}
@Override
protected boolean onLoadRecordingDetails(Bundle args) {
long scheduledRecordingId = args.getLong(DvrDetailsActivity.RECORDING_ID);
- mRecording = TvApplication.getSingletons(getContext()).getDvrDataManager()
- .getScheduledRecording(scheduledRecordingId);
+ mRecording =
+ TvSingletons.getSingletons(getContext())
+ .getDvrDataManager()
+ .getScheduledRecording(scheduledRecordingId);
return mRecording != null;
}
- /**
- * Returns {@link ScheduledRecording} for the current fragment.
- */
+ protected ScheduledRecording getScheduledRecording() {
+ return mRecording;
+ }
+
+ /** Returns {@link ScheduledRecording} for the current fragment. */
public ScheduledRecording getRecording() {
return mRecording;
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/browse/ScheduledRecordingDetailsFragment.java b/src/com/android/tv/dvr/ui/browse/ScheduledRecordingDetailsFragment.java
index 958f8bf8..302b8318 100644
--- a/src/com/android/tv/dvr/ui/browse/ScheduledRecordingDetailsFragment.java
+++ b/src/com/android/tv/dvr/ui/browse/ScheduledRecordingDetailsFragment.java
@@ -21,16 +21,12 @@ import android.os.Bundle;
import android.support.v17.leanback.widget.Action;
import android.support.v17.leanback.widget.OnActionClickedListener;
import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
-import android.text.TextUtils;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.ui.DvrUiHelper;
-/**
- * {@link RecordingDetailsFragment} for scheduled recording in DVR.
- */
+/** {@link RecordingDetailsFragment} for scheduled recording in DVR. */
public class ScheduledRecordingDetailsFragment extends RecordingDetailsFragment {
private static final int ACTION_VIEW_SCHEDULE = 1;
private static final int ACTION_CANCEL = 2;
@@ -38,11 +34,14 @@ public class ScheduledRecordingDetailsFragment extends RecordingDetailsFragment
private DvrManager mDvrManager;
private Action mScheduleAction;
private boolean mHideViewSchedule;
+ private String mFailedMessage;
@Override
public void onCreate(Bundle savedInstance) {
- mDvrManager = TvApplication.getSingletons(getContext()).getDvrManager();
- mHideViewSchedule = getArguments().getBoolean(DvrDetailsActivity.HIDE_VIEW_SCHEDULE);
+ Bundle args = getArguments();
+ mDvrManager = TvSingletons.getSingletons(getContext()).getDvrManager();
+ mHideViewSchedule = args.getBoolean(DvrDetailsActivity.HIDE_VIEW_SCHEDULE);
+ mFailedMessage = args.getString(DvrDetailsActivity.EXTRA_FAILED_MESSAGE);
super.onCreate(savedInstance);
}
@@ -55,19 +54,37 @@ public class ScheduledRecordingDetailsFragment extends RecordingDetailsFragment
}
@Override
+ protected void onCreateInternal() {
+ if (mFailedMessage == null) {
+ super.onCreateInternal();
+ return;
+ }
+ setDetailsOverviewRow(
+ DetailsContent.createFromFailedScheduledRecording(
+ getContext(), getScheduledRecording(), mFailedMessage));
+ }
+
+ @Override
protected SparseArrayObjectAdapter onCreateActionsAdapter() {
SparseArrayObjectAdapter adapter =
new SparseArrayObjectAdapter(new ActionPresenterSelector());
Resources res = getResources();
if (!mHideViewSchedule) {
- mScheduleAction = new Action(ACTION_VIEW_SCHEDULE,
- res.getString(R.string.dvr_detail_view_schedule), null,
- res.getDrawable(getScheduleIconId()));
+ mScheduleAction =
+ new Action(
+ ACTION_VIEW_SCHEDULE,
+ res.getString(R.string.dvr_detail_view_schedule),
+ null,
+ res.getDrawable(getScheduleIconId()));
adapter.set(ACTION_VIEW_SCHEDULE, mScheduleAction);
}
- adapter.set(ACTION_CANCEL, new Action(ACTION_CANCEL,
- res.getString(R.string.dvr_detail_cancel_recording), null,
- res.getDrawable(R.drawable.ic_dvr_cancel_32dp)));
+ adapter.set(
+ ACTION_CANCEL,
+ new Action(
+ ACTION_CANCEL,
+ res.getString(R.string.dvr_detail_cancel_recording),
+ null,
+ res.getDrawable(R.drawable.ic_dvr_cancel_32dp)));
return adapter;
}
diff --git a/src/com/android/tv/dvr/ui/browse/ScheduledRecordingPresenter.java b/src/com/android/tv/dvr/ui/browse/ScheduledRecordingPresenter.java
index 273d3d19..8e028689 100644
--- a/src/com/android/tv/dvr/ui/browse/ScheduledRecordingPresenter.java
+++ b/src/com/android/tv/dvr/ui/browse/ScheduledRecordingPresenter.java
@@ -18,18 +18,14 @@ package com.android.tv.dvr.ui.browse;
import android.content.Context;
import android.os.Handler;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.util.Utils;
-
import java.util.concurrent.TimeUnit;
-/**
- * Presents a {@link ScheduledRecording} in the {@link DvrBrowseFragment}.
- */
+/** Presents a {@link ScheduledRecording} in the {@link DvrBrowseFragment}. */
class ScheduledRecordingPresenter extends DvrItemPresenter<ScheduledRecording> {
private static final long PROGRESS_UPDATE_INTERVAL_MS = TimeUnit.SECONDS.toMillis(5);
@@ -39,13 +35,14 @@ class ScheduledRecordingPresenter extends DvrItemPresenter<ScheduledRecording> {
private final class ScheduledRecordingViewHolder extends DvrItemViewHolder {
private final Handler mHandler = new Handler();
private ScheduledRecording mScheduledRecording;
- private final Runnable mProgressBarUpdater = new Runnable() {
- @Override
- public void run() {
- updateProgressBar();
- mHandler.postDelayed(this, PROGRESS_UPDATE_INTERVAL_MS);
- }
- };
+ private final Runnable mProgressBarUpdater =
+ new Runnable() {
+ @Override
+ public void run() {
+ updateProgressBar();
+ mHandler.postDelayed(this, PROGRESS_UPDATE_INTERVAL_MS);
+ }
+ };
ScheduledRecordingViewHolder(RecordingCardView view, int progressBarColor) {
super(view);
@@ -73,9 +70,17 @@ class ScheduledRecordingPresenter extends DvrItemPresenter<ScheduledRecording> {
int recordingState = mScheduledRecording.getState();
RecordingCardView cardView = (RecordingCardView) view;
if (recordingState == ScheduledRecording.STATE_RECORDING_IN_PROGRESS) {
- cardView.setProgressBar(Math.max(0, Math.min((int) (100 *
- (System.currentTimeMillis() - mScheduledRecording.getStartTimeMs())
- / mScheduledRecording.getDuration()), 100)));
+ cardView.setProgressBar(
+ Math.max(
+ 0,
+ Math.min(
+ (int)
+ (100
+ * (System.currentTimeMillis()
+ - mScheduledRecording
+ .getStartTimeMs())
+ / mScheduledRecording.getDuration()),
+ 100)));
} else if (recordingState == ScheduledRecording.STATE_RECORDING_FINISHED) {
cardView.setProgressBar(100);
} else {
@@ -95,9 +100,10 @@ class ScheduledRecordingPresenter extends DvrItemPresenter<ScheduledRecording> {
public ScheduledRecordingPresenter(Context context) {
super(context);
- mDvrManager = TvApplication.getSingletons(mContext).getDvrManager();
- mProgressBarColor = mContext.getResources()
- .getColor(R.color.play_controls_recording_icon_color_on_focus);
+ mDvrManager = TvSingletons.getSingletons(mContext).getDvrManager();
+ mProgressBarColor =
+ mContext.getResources()
+ .getColor(R.color.play_controls_recording_icon_color_on_focus);
}
@Override
@@ -106,33 +112,61 @@ class ScheduledRecordingPresenter extends DvrItemPresenter<ScheduledRecording> {
}
@Override
- public void onBindDvrItemViewHolder(DvrItemViewHolder baseHolder,
- ScheduledRecording recording) {
+ public void onBindDvrItemViewHolder(
+ DvrItemViewHolder baseHolder, ScheduledRecording recording) {
final ScheduledRecordingViewHolder viewHolder = (ScheduledRecordingViewHolder) baseHolder;
final RecordingCardView cardView = viewHolder.getView();
DetailsContent details = DetailsContent.createFromScheduledRecording(mContext, recording);
cardView.setTitle(details.getTitle());
cardView.setImageUri(details.getLogoImageUri(), details.isUsingChannelLogo());
- cardView.setAffiliatedIcon(mDvrManager.isConflicting(recording) ?
- R.drawable.ic_warning_white_32dp : 0);
+ if (mDvrManager.isConflicting(recording)) {
+ cardView.setAffiliatedIcon(R.drawable.ic_warning_white_32dp);
+ } else if (recording.getState() == ScheduledRecording.STATE_RECORDING_FAILED) {
+ cardView.setAffiliatedIcon(R.drawable.ic_error_white_48dp);
+ } else {
+ cardView.setAffiliatedIcon(0);
+ }
cardView.setContent(generateMajorContent(recording), null);
cardView.setDetailBackgroundImageUri(details.getBackgroundImageUri());
}
private String generateMajorContent(ScheduledRecording recording) {
- int dateDifference = Utils.computeDateDifference(System.currentTimeMillis(),
- recording.getStartTimeMs());
+ if (recording.getState() == ScheduledRecording.STATE_RECORDING_FAILED) {
+ return mContext.getString(R.string.dvr_recording_failed);
+ }
+ int dateDifference =
+ Utils.computeDateDifference(System.currentTimeMillis(), recording.getStartTimeMs());
if (dateDifference <= 0) {
- return mContext.getString(R.string.dvr_date_today_time,
- Utils.getDurationString(mContext, recording.getStartTimeMs(),
- recording.getEndTimeMs(), false, false, true, 0));
+ return mContext.getString(
+ R.string.dvr_date_today_time,
+ Utils.getDurationString(
+ mContext,
+ recording.getStartTimeMs(),
+ recording.getEndTimeMs(),
+ false,
+ false,
+ true,
+ 0));
} else if (dateDifference == 1) {
- return mContext.getString(R.string.dvr_date_tomorrow_time,
- Utils.getDurationString(mContext, recording.getStartTimeMs(),
- recording.getEndTimeMs(), false, false, true, 0));
+ return mContext.getString(
+ R.string.dvr_date_tomorrow_time,
+ Utils.getDurationString(
+ mContext,
+ recording.getStartTimeMs(),
+ recording.getEndTimeMs(),
+ false,
+ false,
+ true,
+ 0));
} else {
- return Utils.getDurationString(mContext, recording.getStartTimeMs(),
- recording.getStartTimeMs(), false, true, false, 0);
+ return Utils.getDurationString(
+ mContext,
+ recording.getStartTimeMs(),
+ recording.getStartTimeMs(),
+ false,
+ true,
+ false,
+ 0);
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/browse/SeriesRecordingDetailsFragment.java b/src/com/android/tv/dvr/ui/browse/SeriesRecordingDetailsFragment.java
index c2aa8e98..2cd191a7 100644
--- a/src/com/android/tv/dvr/ui/browse/SeriesRecordingDetailsFragment.java
+++ b/src/com/android/tv/dvr/ui/browse/SeriesRecordingDetailsFragment.java
@@ -32,9 +32,8 @@ import android.support.v17.leanback.widget.OnActionClickedListener;
import android.support.v17.leanback.widget.PresenterSelector;
import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
import android.text.TextUtils;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.data.BaseProgram;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrWatchedPositionManager;
@@ -42,16 +41,13 @@ import com.android.tv.dvr.data.RecordedProgram;
import com.android.tv.dvr.data.SeriesRecording;
import com.android.tv.dvr.ui.DvrUiHelper;
import com.android.tv.dvr.ui.SortedArrayAdapter;
-
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
-/**
- * {@link DetailsFragment} for series recording in DVR.
- */
-public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implements
- DvrDataManager.SeriesRecordingListener, DvrDataManager.RecordedProgramListener {
+/** {@link DetailsFragment} for series recording in DVR. */
+public class SeriesRecordingDetailsFragment extends DvrDetailsFragment
+ implements DvrDataManager.SeriesRecordingListener, DvrDataManager.RecordedProgramListener {
private static final int ACTION_WATCH = 1;
private static final int ACTION_SERIES_SCHEDULES = 2;
private static final int ACTION_DELETE = 3;
@@ -77,7 +73,7 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement
@Override
public void onCreate(Bundle savedInstanceState) {
- mDvrDataManager = TvApplication.getSingletons(getActivity()).getDvrDataManager();
+ mDvrDataManager = TvSingletons.getSingletons(getActivity()).getDvrDataManager();
mWatchLabel = getString(R.string.dvr_detail_watch);
mResumeLabel = getString(R.string.dvr_detail_series_resume);
mWatchDrawable = getResources().getDrawable(R.drawable.lb_ic_play, null);
@@ -87,8 +83,8 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement
@Override
protected void onCreateInternal() {
- mDvrWatchedPositionManager = TvApplication.getSingletons(getActivity())
- .getDvrWatchedPositionManager();
+ mDvrWatchedPositionManager =
+ TvSingletons.getSingletons(getActivity()).getDvrWatchedPositionManager();
setDetailsOverviewRow(DetailsContent.createFromSeriesRecording(getContext(), mSeries));
setupRecordedProgramsRow();
mDvrDataManager.addSeriesRecordingListener(this);
@@ -119,27 +115,31 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement
mActionsAdapter.clear(ACTION_WATCH);
} else {
String episodeStatus;
- if(mDvrWatchedPositionManager.getWatchedStatus(mRecommendRecordedProgram)
+ if (mDvrWatchedPositionManager.getWatchedStatus(mRecommendRecordedProgram)
== DvrWatchedPositionManager.DVR_WATCHED_STATUS_WATCHING) {
episodeStatus = mResumeLabel;
- mInitialPlaybackPositionMs = mDvrWatchedPositionManager
- .getWatchedPosition(mRecommendRecordedProgram.getId());
+ mInitialPlaybackPositionMs =
+ mDvrWatchedPositionManager.getWatchedPosition(
+ mRecommendRecordedProgram.getId());
} else {
episodeStatus = mWatchLabel;
mInitialPlaybackPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME;
}
- String episodeDisplayNumber = mRecommendRecordedProgram.getEpisodeDisplayNumber(
- getContext());
- mActionsAdapter.set(ACTION_WATCH, new Action(ACTION_WATCH,
- episodeStatus, episodeDisplayNumber, mWatchDrawable));
+ String episodeDisplayNumber =
+ mRecommendRecordedProgram.getEpisodeDisplayNumber(getContext());
+ mActionsAdapter.set(
+ ACTION_WATCH,
+ new Action(ACTION_WATCH, episodeStatus, episodeDisplayNumber, mWatchDrawable));
}
}
@Override
protected boolean onLoadRecordingDetails(Bundle args) {
long recordId = args.getLong(DvrDetailsActivity.RECORDING_ID);
- mSeries = TvApplication.getSingletons(getActivity()).getDvrDataManager()
- .getSeriesRecording(recordId);
+ mSeries =
+ TvSingletons.getSingletons(getActivity())
+ .getDvrDataManager()
+ .getSeriesRecording(recordId);
if (mSeries == null) {
return false;
}
@@ -162,12 +162,19 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement
mActionsAdapter = new SparseArrayObjectAdapter(new ActionPresenterSelector());
Resources res = getResources();
updateWatchAction();
- mActionsAdapter.set(ACTION_SERIES_SCHEDULES, new Action(ACTION_SERIES_SCHEDULES,
- getString(R.string.dvr_detail_view_schedule), null,
- res.getDrawable(R.drawable.ic_schedule_32dp, null)));
- mDeleteAction = new Action(ACTION_DELETE,
- getString(R.string.dvr_detail_series_delete), null,
- res.getDrawable(R.drawable.ic_delete_32dp, null));
+ mActionsAdapter.set(
+ ACTION_SERIES_SCHEDULES,
+ new Action(
+ ACTION_SERIES_SCHEDULES,
+ getString(R.string.dvr_detail_view_schedule),
+ null,
+ res.getDrawable(R.drawable.ic_schedule_32dp, null)));
+ mDeleteAction =
+ new Action(
+ ACTION_DELETE,
+ getString(R.string.dvr_detail_series_delete),
+ null,
+ res.getDrawable(R.drawable.ic_delete_32dp, null));
if (!mRecordedPrograms.isEmpty()) {
mActionsAdapter.set(ACTION_DELETE, mDeleteAction);
}
@@ -207,11 +214,9 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement
};
}
- /**
- * The programs are sorted by season number and episode number.
- */
+ /** The programs are sorted by season number and episode number. */
private RecordedProgram getRecommendProgram(List<RecordedProgram> programs) {
- for (int i = programs.size() - 1 ; i >= 0 ; i--) {
+ for (int i = programs.size() - 1; i >= 0; i--) {
RecordedProgram program = programs.get(i);
int watchedStatus = mDvrWatchedPositionManager.getWatchedStatus(program);
if (watchedStatus == DvrWatchedPositionManager.DVR_WATCHED_STATUS_NEW) {
@@ -230,7 +235,7 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement
}
@Override
- public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) { }
+ public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) {}
@Override
public void onSeriesRecordingChanged(SeriesRecording... seriesRecordings) {
@@ -308,8 +313,10 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement
for (int i = rowsAdaptor.size() - 1; i >= 0; i--) {
Object row = rowsAdaptor.get(i);
if (row instanceof ListRow) {
- int compareResult = BaseProgram.numberCompare(seasonNumber,
- ((SeasonRowAdapter) ((ListRow) row).getAdapter()).mSeasonNumber);
+ int compareResult =
+ BaseProgram.numberCompare(
+ seasonNumber,
+ ((SeasonRowAdapter) ((ListRow) row).getAdapter()).mSeasonNumber);
if (compareResult == 0) {
return (ListRow) row;
} else if (compareResult < 0) {
@@ -321,18 +328,25 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement
}
private ListRow createNewSeasonRow(String seasonNumber, int position) {
- String seasonTitle = seasonNumber.isEmpty() ? mSeries.getTitle()
- : getString(R.string.dvr_detail_series_season_title, seasonNumber);
+ String seasonTitle =
+ seasonNumber.isEmpty()
+ ? mSeries.getTitle()
+ : getString(R.string.dvr_detail_series_season_title, seasonNumber);
HeaderItem header = new HeaderItem(mSeasonRowCount++, seasonTitle);
ClassPresenterSelector selector = new ClassPresenterSelector();
selector.addClassPresenter(RecordedProgram.class, mRecordedProgramPresenter);
- ListRow row = new ListRow(header, new SeasonRowAdapter(selector,
- new Comparator<RecordedProgram>() {
- @Override
- public int compare(RecordedProgram lhs, RecordedProgram rhs) {
- return BaseProgram.EPISODE_COMPARATOR.compare(lhs, rhs);
- }
- }, seasonNumber));
+ ListRow row =
+ new ListRow(
+ header,
+ new SeasonRowAdapter(
+ selector,
+ new Comparator<RecordedProgram>() {
+ @Override
+ public int compare(RecordedProgram lhs, RecordedProgram rhs) {
+ return BaseProgram.EPISODE_COMPARATOR.compare(lhs, rhs);
+ }
+ },
+ seasonNumber));
getRowsAdapter().add(position, row);
return row;
}
@@ -340,7 +354,9 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement
private class SeasonRowAdapter extends SortedArrayAdapter<RecordedProgram> {
private String mSeasonNumber;
- SeasonRowAdapter(PresenterSelector selector, Comparator<RecordedProgram> comparator,
+ SeasonRowAdapter(
+ PresenterSelector selector,
+ Comparator<RecordedProgram> comparator,
String seasonNumber) {
super(selector, comparator);
mSeasonNumber = seasonNumber;
@@ -351,4 +367,4 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement
return program.getId();
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/browse/SeriesRecordingPresenter.java b/src/com/android/tv/dvr/ui/browse/SeriesRecordingPresenter.java
index e508259d..14f9dceb 100644
--- a/src/com/android/tv/dvr/ui/browse/SeriesRecordingPresenter.java
+++ b/src/com/android/tv/dvr/ui/browse/SeriesRecordingPresenter.java
@@ -19,10 +19,8 @@ package com.android.tv.dvr.ui.browse;
import android.content.Context;
import android.media.tv.TvInputManager;
import android.text.TextUtils;
-
-import com.android.tv.ApplicationSingletons;
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrDataManager.RecordedProgramListener;
import com.android.tv.dvr.DvrDataManager.ScheduledRecordingListener;
@@ -32,27 +30,29 @@ import com.android.tv.dvr.DvrWatchedPositionManager.WatchedPositionChangedListen
import com.android.tv.dvr.data.RecordedProgram;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.data.SeriesRecording;
-
import java.util.List;
-/**
- * Presents a {@link SeriesRecording} in {@link DvrBrowseFragment}.
- */
+/** Presents a {@link SeriesRecording} in {@link DvrBrowseFragment}. */
class SeriesRecordingPresenter extends DvrItemPresenter<SeriesRecording> {
private final DvrDataManager mDvrDataManager;
private final DvrManager mDvrManager;
private final DvrWatchedPositionManager mWatchedPositionManager;
- private final class SeriesRecordingViewHolder extends DvrItemViewHolder implements
- WatchedPositionChangedListener, ScheduledRecordingListener, RecordedProgramListener {
+ private final class SeriesRecordingViewHolder extends DvrItemViewHolder
+ implements WatchedPositionChangedListener,
+ ScheduledRecordingListener,
+ RecordedProgramListener {
private SeriesRecording mSeriesRecording;
private RecordingCardView mCardView;
private DvrDataManager mDvrDataManager;
private DvrManager mDvrManager;
private DvrWatchedPositionManager mWatchedPositionManager;
- SeriesRecordingViewHolder(RecordingCardView view, DvrDataManager dvrDataManager,
- DvrManager dvrManager, DvrWatchedPositionManager watchedPositionManager) {
+ SeriesRecordingViewHolder(
+ RecordingCardView view,
+ DvrDataManager dvrDataManager,
+ DvrManager dvrManager,
+ DvrWatchedPositionManager watchedPositionManager) {
super(view);
mCardView = view;
mDvrDataManager = dvrDataManager;
@@ -92,8 +92,8 @@ class SeriesRecordingPresenter extends DvrItemPresenter<SeriesRecording> {
public void onRecordedProgramsAdded(RecordedProgram... recordedPrograms) {
boolean needToUpdateCardView = false;
for (RecordedProgram recordedProgram : recordedPrograms) {
- if (TextUtils.equals(recordedProgram.getSeriesId(),
- mSeriesRecording.getSeriesId())) {
+ if (TextUtils.equals(
+ recordedProgram.getSeriesId(), mSeriesRecording.getSeriesId())) {
mDvrDataManager.removeScheduledRecordingListener(this);
mWatchedPositionManager.addListener(this, recordedProgram.getId());
needToUpdateCardView = true;
@@ -108,8 +108,8 @@ class SeriesRecordingPresenter extends DvrItemPresenter<SeriesRecording> {
public void onRecordedProgramsRemoved(RecordedProgram... recordedPrograms) {
boolean needToUpdateCardView = false;
for (RecordedProgram recordedProgram : recordedPrograms) {
- if (TextUtils.equals(recordedProgram.getSeriesId(),
- mSeriesRecording.getSeriesId())) {
+ if (TextUtils.equals(
+ recordedProgram.getSeriesId(), mSeriesRecording.getSeriesId())) {
if (mWatchedPositionManager.getWatchedPosition(recordedProgram.getId())
== TvInputManager.TIME_SHIFT_INVALID_TIME) {
mWatchedPositionManager.removeListener(this, recordedProgram.getId());
@@ -177,14 +177,15 @@ class SeriesRecordingPresenter extends DvrItemPresenter<SeriesRecording> {
quantityStringID = R.plurals.dvr_count_new_recordings;
}
}
- mCardView.setContent(mCardView.getResources()
- .getQuantityString(quantityStringID, count, count), null);
+ mCardView.setContent(
+ mCardView.getResources().getQuantityString(quantityStringID, count, count),
+ null);
}
}
public SeriesRecordingPresenter(Context context) {
super(context);
- ApplicationSingletons singletons = TvApplication.getSingletons(context);
+ TvSingletons singletons = TvSingletons.getSingletons(context);
mDvrDataManager = singletons.getDvrDataManager();
mDvrManager = singletons.getDvrManager();
mWatchedPositionManager = singletons.getDvrWatchedPositionManager();
@@ -192,8 +193,11 @@ class SeriesRecordingPresenter extends DvrItemPresenter<SeriesRecording> {
@Override
public DvrItemViewHolder onCreateDvrItemViewHolder() {
- return new SeriesRecordingViewHolder(new RecordingCardView(mContext), mDvrDataManager,
- mDvrManager, mWatchedPositionManager);
+ return new SeriesRecordingViewHolder(
+ new RecordingCardView(mContext),
+ mDvrDataManager,
+ mDvrManager,
+ mWatchedPositionManager);
}
@Override
diff --git a/src/com/android/tv/dvr/ui/list/BaseDvrSchedulesFragment.java b/src/com/android/tv/dvr/ui/list/BaseDvrSchedulesFragment.java
index b9407b15..77a63508 100644
--- a/src/com/android/tv/dvr/ui/list/BaseDvrSchedulesFragment.java
+++ b/src/com/android/tv/dvr/ui/list/BaseDvrSchedulesFragment.java
@@ -1,18 +1,18 @@
/*
-* 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
-*/
+ * 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.list;
@@ -23,23 +23,17 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
-
-import com.android.tv.ApplicationSingletons;
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrScheduleManager;
import com.android.tv.dvr.data.ScheduledRecording;
-/**
- * A base fragment to show the list of schedule recordings.
- */
+/** A base fragment to show the list of schedule recordings. */
public abstract class BaseDvrSchedulesFragment extends DetailsFragment
implements DvrDataManager.ScheduledRecordingListener,
- DvrScheduleManager.OnConflictStateChangeListener {
- /**
- * The key for scheduled recording which has be selected in the list.
- */
+ DvrScheduleManager.OnConflictStateChangeListener {
+ /** The key for scheduled recording which has be selected in the list. */
public static final String SCHEDULES_KEY_SCHEDULED_RECORDING =
"schedules_key_scheduled_recording";
@@ -55,15 +49,15 @@ public abstract class BaseDvrSchedulesFragment extends DetailsFragment
mRowsAdapter = onCreateRowsAdapter(presenterSelector);
setAdapter(mRowsAdapter);
mRowsAdapter.start();
- ApplicationSingletons singletons = TvApplication.getSingletons(getContext());
+ TvSingletons singletons = TvSingletons.getSingletons(getContext());
singletons.getDvrDataManager().addScheduledRecordingListener(this);
singletons.getDvrScheduleManager().addOnConflictStateChangeListener(this);
mEmptyInfoScreenView = (TextView) getActivity().findViewById(R.id.empty_info_screen);
}
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
int firstItemPosition = getFirstItemPosition();
if (firstItemPosition != -1) {
@@ -72,16 +66,12 @@ public abstract class BaseDvrSchedulesFragment extends DetailsFragment
return view;
}
- /**
- * Returns rows adapter.
- */
+ /** Returns rows adapter. */
protected ScheduleRowAdapter getRowsAdapter() {
return mRowsAdapter;
}
- /**
- * Shows the empty message.
- */
+ /** Shows the empty message. */
void showEmptyMessage(int messageId) {
mEmptyInfoScreenView.setText(messageId);
if (mEmptyInfoScreenView.getVisibility() != View.VISIBLE) {
@@ -89,9 +79,7 @@ public abstract class BaseDvrSchedulesFragment extends DetailsFragment
}
}
- /**
- * Hides the empty message.
- */
+ /** Hides the empty message. */
void hideEmptyMessage() {
if (mEmptyInfoScreenView.getVisibility() == View.VISIBLE) {
mEmptyInfoScreenView.setVisibility(View.GONE);
@@ -99,39 +87,32 @@ public abstract class BaseDvrSchedulesFragment extends DetailsFragment
}
@Override
- public View onInflateTitleView(LayoutInflater inflater, ViewGroup parent,
- Bundle savedInstanceState) {
+ public View onInflateTitleView(
+ LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
// Workaround of b/31046014
return null;
}
@Override
public void onDestroy() {
- ApplicationSingletons singletons = TvApplication.getSingletons(getContext());
+ TvSingletons singletons = TvSingletons.getSingletons(getContext());
singletons.getDvrScheduleManager().removeOnConflictStateChangeListener(this);
singletons.getDvrDataManager().removeScheduledRecordingListener(this);
mRowsAdapter.stop();
super.onDestroy();
}
- /**
- * Creates header row presenter.
- */
+ /** Creates header row presenter. */
public abstract SchedulesHeaderRowPresenter onCreateHeaderRowPresenter();
- /**
- * Creates rows presenter.
- */
+ /** Creates rows presenter. */
public abstract ScheduleRowPresenter onCreateRowPresenter();
- /**
- * Creates rows adapter.
- */
- public abstract ScheduleRowAdapter onCreateRowsAdapter(ClassPresenterSelector presenterSelecor);
+ /** Creates rows adapter. */
+ public abstract ScheduleRowAdapter onCreateRowsAdapter(
+ ClassPresenterSelector presenterSelector);
- /**
- * Gets the first focus position in schedules list.
- */
+ /** Gets the first focus position in schedules list. */
protected int getFirstItemPosition() {
for (int i = 0; i < mRowsAdapter.size(); i++) {
if (mRowsAdapter.get(i) instanceof ScheduleRow) {
@@ -176,4 +157,4 @@ public abstract class BaseDvrSchedulesFragment extends DetailsFragment
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/list/DvrHistoryActivity.java b/src/com/android/tv/dvr/ui/list/DvrHistoryActivity.java
new file mode 100644
index 00000000..623975e1
--- /dev/null
+++ b/src/com/android/tv/dvr/ui/list/DvrHistoryActivity.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 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.list;
+
+import android.app.Activity;
+import android.os.Bundle;
+import com.android.tv.R;
+import com.android.tv.Starter;
+
+/** Activity to show the recording history. */
+public class DvrHistoryActivity extends Activity {
+
+ @Override
+ public void onCreate(final Bundle savedInstanceState) {
+ Starter.start(this);
+ // Pass null to prevent automatically re-creating fragments
+ super.onCreate(null);
+ setContentView(R.layout.activity_dvr_history);
+ DvrHistoryFragment dvrHistoryFragment = new DvrHistoryFragment();
+ getFragmentManager()
+ .beginTransaction()
+ .add(R.id.fragment_container, dvrHistoryFragment)
+ .commit();
+ }
+}
diff --git a/src/com/android/tv/dvr/ui/list/DvrHistoryFragment.java b/src/com/android/tv/dvr/ui/list/DvrHistoryFragment.java
new file mode 100644
index 00000000..0ca05fac
--- /dev/null
+++ b/src/com/android/tv/dvr/ui/list/DvrHistoryFragment.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2018 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.list;
+
+import android.os.Bundle;
+import android.support.v17.leanback.app.DetailsFragment;
+import android.support.v17.leanback.widget.ClassPresenterSelector;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import com.android.tv.R;
+import com.android.tv.TvSingletons;
+import com.android.tv.dvr.DvrDataManager;
+import com.android.tv.dvr.data.RecordedProgram;
+import com.android.tv.dvr.data.ScheduledRecording;
+import com.android.tv.dvr.ui.list.SchedulesHeaderRowPresenter.DateHeaderRowPresenter;
+
+/** A fragment to show the DVR history. */
+public class DvrHistoryFragment extends DetailsFragment
+ implements DvrDataManager.ScheduledRecordingListener,
+ DvrDataManager.RecordedProgramListener {
+
+ private DvrHistoryRowAdapter mRowsAdapter;
+ private TextView mEmptyInfoScreenView;
+ private DvrDataManager mDvrDataManager;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ClassPresenterSelector presenterSelector = new ClassPresenterSelector();
+ presenterSelector.addClassPresenter(
+ SchedulesHeaderRow.class, new DateHeaderRowPresenter(getContext()));
+ presenterSelector.addClassPresenter(
+ ScheduleRow.class, new ScheduleRowPresenter(getContext()));
+ TvSingletons singletons = TvSingletons.getSingletons(getContext());
+ mRowsAdapter = new DvrHistoryRowAdapter(
+ getContext(), presenterSelector, singletons.getClock());
+ setAdapter(mRowsAdapter);
+ mRowsAdapter.start();
+ mDvrDataManager = singletons.getDvrDataManager();
+ mDvrDataManager.addScheduledRecordingListener(this);
+ mDvrDataManager.addRecordedProgramListener(this);
+ mEmptyInfoScreenView = (TextView) getActivity().findViewById(R.id.empty_info_screen);
+ }
+
+ @Override
+ public void onDestroy() {
+ mDvrDataManager.removeScheduledRecordingListener(this);
+ mDvrDataManager.removeRecordedProgramListener(this);
+ super.onDestroy();
+ }
+
+ /** Shows the empty message. */
+ void showEmptyMessage() {
+ mEmptyInfoScreenView.setText(R.string.dvr_history_empty_state);
+ if (mEmptyInfoScreenView.getVisibility() != View.VISIBLE) {
+ mEmptyInfoScreenView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ /** Hides the empty message. */
+ void hideEmptyMessage() {
+ if (mEmptyInfoScreenView.getVisibility() == View.VISIBLE) {
+ mEmptyInfoScreenView.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public View onInflateTitleView(
+ LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
+ // Workaround of b/31046014
+ return null;
+ }
+
+ @Override
+ public void onScheduledRecordingAdded(ScheduledRecording... scheduledRecordings) {
+ if (mRowsAdapter != null) {
+ for (ScheduledRecording recording : scheduledRecordings) {
+ mRowsAdapter.onScheduledRecordingAdded(recording);
+ }
+ if (mRowsAdapter.size() > 0) {
+ hideEmptyMessage();
+ }
+ }
+ }
+
+ @Override
+ public void onScheduledRecordingRemoved(ScheduledRecording... scheduledRecordings) {
+ if (mRowsAdapter != null) {
+ for (ScheduledRecording recording : scheduledRecordings) {
+ mRowsAdapter.onScheduledRecordingRemoved(recording);
+ }
+ if (mRowsAdapter.size() == 0) {
+ showEmptyMessage();
+ }
+ }
+ }
+
+ @Override
+ public void onScheduledRecordingStatusChanged(ScheduledRecording... scheduledRecordings) {
+ if (mRowsAdapter != null) {
+ for (ScheduledRecording recording : scheduledRecordings) {
+ mRowsAdapter.onScheduledRecordingUpdated(recording);
+ }
+ if (mRowsAdapter.size() == 0) {
+ showEmptyMessage();
+ } else {
+ hideEmptyMessage();
+ }
+ }
+ }
+
+ @Override
+ public void onRecordedProgramsAdded(RecordedProgram... recordedPrograms) {
+ if (mRowsAdapter != null) {
+ for (RecordedProgram p : recordedPrograms) {
+ mRowsAdapter.onScheduledRecordingAdded(p);
+ }
+ if (mRowsAdapter.size() > 0) {
+ hideEmptyMessage();
+ }
+ }
+
+ }
+
+ @Override
+ public void onRecordedProgramsChanged(RecordedProgram... recordedPrograms) {
+ if (mRowsAdapter != null) {
+ for (RecordedProgram program : recordedPrograms) {
+ mRowsAdapter.onScheduledRecordingUpdated(program);
+ }
+ if (mRowsAdapter.size() == 0) {
+ showEmptyMessage();
+ } else {
+ hideEmptyMessage();
+ }
+ }
+ }
+
+ @Override
+ public void onRecordedProgramsRemoved(RecordedProgram... recordedPrograms) {
+ if (mRowsAdapter != null) {
+ for (RecordedProgram p : recordedPrograms) {
+ mRowsAdapter.onScheduledRecordingRemoved(p);
+ }
+ if (mRowsAdapter.size() == 0) {
+ showEmptyMessage();
+ }
+ }
+ }
+}
diff --git a/src/com/android/tv/dvr/ui/list/DvrHistoryRowAdapter.java b/src/com/android/tv/dvr/ui/list/DvrHistoryRowAdapter.java
new file mode 100644
index 00000000..156d1a7e
--- /dev/null
+++ b/src/com/android/tv/dvr/ui/list/DvrHistoryRowAdapter.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2018 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.list;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.Nullable;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.ClassPresenterSelector;
+import android.text.format.DateUtils;
+import android.util.Log;
+import com.android.tv.R;
+import com.android.tv.TvSingletons;
+import com.android.tv.common.SoftPreconditions;
+import com.android.tv.common.util.Clock;
+import com.android.tv.dvr.DvrDataManager;
+import com.android.tv.dvr.data.RecordedProgram;
+import com.android.tv.dvr.data.ScheduledRecording;
+import com.android.tv.dvr.recorder.ScheduledProgramReaper;
+import com.android.tv.dvr.ui.list.SchedulesHeaderRow.DateHeaderRow;
+import com.android.tv.util.Utils;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/** An adapter for DVR history. */
+@TargetApi(VERSION_CODES.N)
+@SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated
+class DvrHistoryRowAdapter extends ArrayObjectAdapter {
+ private static final String TAG = "DvrHistoryRowAdapter";
+ private static final boolean DEBUG = false;
+
+ private static final long ONE_DAY_MS = TimeUnit.DAYS.toMillis(1);
+ private static final int MAX_HISTORY_DAYS = ScheduledProgramReaper.DAYS;
+
+ private final Context mContext;
+ private final Clock mClock;
+ private final DvrDataManager mDvrDataManager;
+ private final List<String> mTitles = new ArrayList<>();
+ private final Map<Long, ScheduledRecording> mRecordedProgramScheduleMap = new HashMap<>();
+
+ public DvrHistoryRowAdapter(
+ Context context, ClassPresenterSelector classPresenterSelector, Clock clock) {
+ super(classPresenterSelector);
+ mContext = context;
+ mClock = clock;
+ mDvrDataManager = TvSingletons.getSingletons(mContext).getDvrDataManager();
+ mTitles.add(mContext.getString(R.string.dvr_date_today));
+ mTitles.add(mContext.getString(R.string.dvr_date_yesterday));
+ }
+
+ /** Returns context. */
+ protected Context getContext() {
+ return mContext;
+ }
+
+ /** Starts row adapter. */
+ public void start() {
+ clear();
+ List<ScheduledRecording> recordingList = mDvrDataManager.getFailedScheduledRecordings();
+ List<RecordedProgram> recordedProgramList = mDvrDataManager.getRecordedPrograms();
+
+ recordingList.addAll(
+ recordedProgramsToScheduledRecordings(recordedProgramList, MAX_HISTORY_DAYS));
+ recordingList
+ .sort(ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR.reversed());
+ long deadLine = Utils.getFirstMillisecondOfDay(mClock.currentTimeMillis());
+ for (int i = 0; i < recordingList.size(); ) {
+ ArrayList<ScheduledRecording> section = new ArrayList<>();
+ while (i < recordingList.size() && recordingList.get(i).getStartTimeMs() >= deadLine) {
+ section.add(recordingList.get(i++));
+ }
+ if (!section.isEmpty()) {
+ SchedulesHeaderRow headerRow =
+ new DateHeaderRow(
+ calculateHeaderDate(deadLine),
+ mContext.getResources()
+ .getQuantityString(
+ R.plurals.dvr_schedules_section_subtitle,
+ section.size(),
+ section.size()),
+ section.size(),
+ deadLine);
+ add(headerRow);
+ for (ScheduledRecording recording : section) {
+ add(new ScheduleRow(recording, headerRow));
+ }
+ }
+ deadLine -= ONE_DAY_MS;
+ }
+ }
+
+ private String calculateHeaderDate(long timeMs) {
+ int titleIndex =
+ (int)
+ ((Utils.getFirstMillisecondOfDay(mClock.currentTimeMillis()) - timeMs)
+ / ONE_DAY_MS);
+ String headerDate;
+ if (titleIndex < mTitles.size()) {
+ headerDate = mTitles.get(titleIndex);
+ } else {
+ headerDate =
+ DateUtils.formatDateTime(
+ getContext(),
+ timeMs,
+ DateUtils.FORMAT_SHOW_WEEKDAY
+ | DateUtils.FORMAT_SHOW_DATE
+ | DateUtils.FORMAT_ABBREV_MONTH);
+ }
+ return headerDate;
+ }
+
+ private List<ScheduledRecording> recordedProgramsToScheduledRecordings(
+ List<RecordedProgram> programs, int maxDays) {
+ List<ScheduledRecording> result = new ArrayList<>();
+ for (RecordedProgram recordedProgram : programs) {
+ ScheduledRecording scheduledRecording =
+ recordedProgramsToScheduledRecordings(recordedProgram, maxDays);
+ if (scheduledRecording != null) {
+ result.add(scheduledRecording);
+ }
+ }
+ return result;
+ }
+
+ @Nullable
+ private ScheduledRecording recordedProgramsToScheduledRecordings(
+ RecordedProgram program, int maxDays) {
+ long firstMillisecondToday = Utils.getFirstMillisecondOfDay(mClock.currentTimeMillis());
+ if (maxDays
+ < Utils.computeDateDifference(
+ program.getStartTimeUtcMillis(),
+ firstMillisecondToday)) {
+ return null;
+ }
+ ScheduledRecording scheduledRecording = ScheduledRecording.builder(program).build();
+ mRecordedProgramScheduleMap.put(program.getId(), scheduledRecording);
+ return scheduledRecording;
+ }
+
+ public void onScheduledRecordingAdded(ScheduledRecording schedule) {
+ if (DEBUG) {
+ Log.d(TAG, "onScheduledRecordingAdded: " + schedule);
+ }
+ if (findRowByScheduledRecording(schedule) == null
+ && (schedule.getState() == ScheduledRecording.STATE_RECORDING_FINISHED
+ || schedule.getState() == ScheduledRecording.STATE_RECORDING_CLIPPED
+ || schedule.getState() == ScheduledRecording.STATE_RECORDING_FAILED)) {
+ addScheduleRow(schedule);
+ }
+ }
+
+ public void onScheduledRecordingAdded(RecordedProgram program) {
+ if (DEBUG) {
+ Log.d(TAG, "onScheduledRecordingAdded: " + program);
+ }
+ if (mRecordedProgramScheduleMap.get(program.getId()) != null) {
+ return;
+ }
+ ScheduledRecording schedule =
+ recordedProgramsToScheduledRecordings(program, MAX_HISTORY_DAYS);
+ if (schedule == null) {
+ return;
+ }
+ addScheduleRow(schedule);
+ }
+
+ public void onScheduledRecordingRemoved(ScheduledRecording schedule) {
+ if (DEBUG) {
+ Log.d(TAG, "onScheduledRecordingRemoved: " + schedule);
+ }
+ ScheduleRow row = findRowByScheduledRecording(schedule);
+ if (row != null) {
+ removeScheduleRow(row);
+ notifyArrayItemRangeChanged(indexOf(row), 1);
+ }
+ }
+
+ public void onScheduledRecordingRemoved(RecordedProgram program) {
+ if (DEBUG) {
+ Log.d(TAG, "onScheduledRecordingRemoved: " + program);
+ }
+ ScheduledRecording scheduledRecording = mRecordedProgramScheduleMap.get(program.getId());
+ if (scheduledRecording != null) {
+ mRecordedProgramScheduleMap.remove(program.getId());
+ ScheduleRow row = findRowByRecordedProgram(program);
+ if (row != null) {
+ removeScheduleRow(row);
+ notifyArrayItemRangeChanged(indexOf(row), 1);
+ }
+ }
+ }
+
+ public void onScheduledRecordingUpdated(ScheduledRecording schedule) {
+ if (DEBUG) {
+ Log.d(TAG, "onScheduledRecordingUpdated: " + schedule);
+ }
+ ScheduleRow row = findRowByScheduledRecording(schedule);
+ if (row != null) {
+ row.setSchedule(schedule);
+ if (schedule.getState() != ScheduledRecording.STATE_RECORDING_FAILED) {
+ // Only handle failed schedules. Finished schedules are handled as recorded programs
+ removeScheduleRow(row);
+ }
+ notifyArrayItemRangeChanged(indexOf(row), 1);
+ }
+ }
+
+ public void onScheduledRecordingUpdated(RecordedProgram program) {
+ if (DEBUG) {
+ Log.d(TAG, "onScheduledRecordingUpdated: " + program);
+ }
+ ScheduleRow row = findRowByRecordedProgram(program);
+ if (row != null) {
+ removeScheduleRow(row);
+ notifyArrayItemRangeChanged(indexOf(row), 1);
+ ScheduledRecording schedule = mRecordedProgramScheduleMap.get(program.getId());
+ if (schedule != null) {
+ mRecordedProgramScheduleMap.remove(program.getId());
+ }
+ }
+ onScheduledRecordingAdded(program);
+ }
+
+ private void addScheduleRow(ScheduledRecording recording) {
+ // This method must not be called from inherited class.
+ SoftPreconditions.checkState(getClass().equals(DvrHistoryRowAdapter.class));
+ if (recording != null) {
+ int pre = -1;
+ int index = 0;
+ for (; index < size(); index++) {
+ if (get(index) instanceof ScheduleRow) {
+ ScheduleRow scheduleRow = (ScheduleRow) get(index);
+ if (ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR.reversed()
+ .compare(scheduleRow.getSchedule(), recording) > 0) {
+ break;
+ }
+ pre = index;
+ }
+ }
+ long deadLine = Utils.getFirstMillisecondOfDay(recording.getStartTimeMs());
+ if (pre >= 0 && getHeaderRow(pre).getDeadLineMs() == deadLine) {
+ SchedulesHeaderRow headerRow = ((ScheduleRow) get(pre)).getHeaderRow();
+ headerRow.setItemCount(headerRow.getItemCount() + 1);
+ ScheduleRow addedRow = new ScheduleRow(recording, headerRow);
+ add(++pre, addedRow);
+ updateHeaderDescription(headerRow);
+ } else if (index < size() && getHeaderRow(index).getDeadLineMs() == deadLine) {
+ SchedulesHeaderRow headerRow = ((ScheduleRow) get(index)).getHeaderRow();
+ headerRow.setItemCount(headerRow.getItemCount() + 1);
+ ScheduleRow addedRow = new ScheduleRow(recording, headerRow);
+ add(index, addedRow);
+ updateHeaderDescription(headerRow);
+ } else {
+ SchedulesHeaderRow headerRow =
+ new DateHeaderRow(
+ calculateHeaderDate(deadLine),
+ mContext.getResources()
+ .getQuantityString(
+ R.plurals.dvr_schedules_section_subtitle, 1, 1),
+ 1,
+ deadLine);
+ add(++pre, headerRow);
+ ScheduleRow addedRow = new ScheduleRow(recording, headerRow);
+ add(pre, addedRow);
+ }
+ }
+ }
+
+ private DateHeaderRow getHeaderRow(int index) {
+ return ((DateHeaderRow) ((ScheduleRow) get(index)).getHeaderRow());
+ }
+
+ /** Gets which {@link ScheduleRow} the {@link ScheduledRecording} belongs to. */
+ private ScheduleRow findRowByScheduledRecording(ScheduledRecording recording) {
+ if (recording == null) {
+ return null;
+ }
+ for (int i = 0; i < size(); i++) {
+ Object item = get(i);
+ if (item instanceof ScheduleRow && ((ScheduleRow) item).getSchedule() != null) {
+ if (((ScheduleRow) item).getSchedule().getId() == recording.getId()) {
+ return (ScheduleRow) item;
+ }
+ }
+ }
+ return null;
+ }
+
+ private ScheduleRow findRowByRecordedProgram(RecordedProgram program) {
+ if (program == null) {
+ return null;
+ }
+ for (int i = 0; i < size(); i++) {
+ Object item = get(i);
+ if (item instanceof ScheduleRow) {
+ ScheduleRow row = (ScheduleRow) item;
+ if (row.hasRecordedProgram()
+ && row.getSchedule().getRecordedProgramId() == program.getId()) {
+ return (ScheduleRow) item;
+ }
+ }
+ }
+ return null;
+ }
+
+ private void removeScheduleRow(ScheduleRow scheduleRow) {
+ // This method must not be called from inherited class.
+ SoftPreconditions.checkState(getClass().equals(DvrHistoryRowAdapter.class));
+ if (scheduleRow != null) {
+ scheduleRow.setSchedule(null);
+ SchedulesHeaderRow headerRow = scheduleRow.getHeaderRow();
+ remove(scheduleRow);
+ // Changes the count information of header which the removed row belongs to.
+ if (headerRow != null) {
+ int currentCount = headerRow.getItemCount();
+ headerRow.setItemCount(--currentCount);
+ if (headerRow.getItemCount() == 0) {
+ remove(headerRow);
+ } else {
+ replace(indexOf(headerRow), headerRow);
+ updateHeaderDescription(headerRow);
+ }
+ }
+ }
+ }
+
+ private void updateHeaderDescription(SchedulesHeaderRow headerRow) {
+ headerRow.setDescription(
+ mContext.getResources()
+ .getQuantityString(
+ R.plurals.dvr_schedules_section_subtitle,
+ headerRow.getItemCount(),
+ headerRow.getItemCount()));
+ }
+}
diff --git a/src/com/android/tv/dvr/ui/list/DvrSchedulesActivity.java b/src/com/android/tv/dvr/ui/list/DvrSchedulesActivity.java
index a0410bb3..82b85630 100644
--- a/src/com/android/tv/dvr/ui/list/DvrSchedulesActivity.java
+++ b/src/com/android/tv/dvr/ui/list/DvrSchedulesActivity.java
@@ -20,23 +20,19 @@ import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.support.annotation.IntDef;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.Starter;
import com.android.tv.data.Program;
import com.android.tv.dvr.data.SeriesRecording;
import com.android.tv.dvr.provider.EpisodicProgramLoadTask;
import com.android.tv.dvr.recorder.SeriesRecordingScheduler;
import com.android.tv.dvr.ui.BigArguments;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.List;
-/**
- * Activity to show the list of recording schedules.
- */
+/** Activity to show the list of recording schedules. */
public class DvrSchedulesActivity extends Activity {
/**
* The key for the type of the schedules which will be listed in the list. The type of the value
@@ -47,9 +43,7 @@ public class DvrSchedulesActivity extends Activity {
@Retention(RetentionPolicy.SOURCE)
@IntDef({TYPE_FULL_SCHEDULE, TYPE_SERIES_SCHEDULE})
public @interface ScheduleListType {}
- /**
- * A type which means the activity will display the full scheduled recordings.
- */
+ /** A type which means the activity will display the full scheduled recordings. */
public static final int TYPE_FULL_SCHEDULE = 0;
/**
* A type which means the activity will display a scheduled recording list of a series
@@ -59,7 +53,7 @@ public class DvrSchedulesActivity extends Activity {
@Override
public void onCreate(final Bundle savedInstanceState) {
- TvApplication.setCurrentRunningProcess(this, true);
+ Starter.start(this);
// Pass null to prevent automatically re-creating fragments
super.onCreate(null);
setContentView(R.layout.activity_dvr_schedules);
@@ -67,20 +61,29 @@ public class DvrSchedulesActivity extends Activity {
if (scheduleType == TYPE_FULL_SCHEDULE) {
DvrSchedulesFragment schedulesFragment = new DvrSchedulesFragment();
schedulesFragment.setArguments(getIntent().getExtras());
- getFragmentManager().beginTransaction().add(
- R.id.fragment_container, schedulesFragment).commit();
+ getFragmentManager()
+ .beginTransaction()
+ .add(R.id.fragment_container, schedulesFragment)
+ .commit();
} else if (scheduleType == TYPE_SERIES_SCHEDULE) {
- if (BigArguments.getArgument(DvrSeriesSchedulesFragment
- .SERIES_SCHEDULES_KEY_SERIES_PROGRAMS) != null) {
+ if (BigArguments.getArgument(
+ DvrSeriesSchedulesFragment.SERIES_SCHEDULES_KEY_SERIES_PROGRAMS)
+ != null) {
// The programs will be passed to the DvrSeriesSchedulesFragment, so don't need
// to reset the BigArguments.
showDvrSeriesSchedulesFragment(getIntent().getExtras());
} else {
- final ProgressDialog dialog = ProgressDialog.show(this, null, getString(
- R.string.dvr_series_progress_message_reading_programs));
- SeriesRecording seriesRecording = getIntent().getExtras()
- .getParcelable(DvrSeriesSchedulesFragment
- .SERIES_SCHEDULES_KEY_SERIES_RECORDING);
+ final ProgressDialog dialog =
+ ProgressDialog.show(
+ this,
+ null,
+ getString(R.string.dvr_series_progress_message_reading_programs));
+ SeriesRecording seriesRecording =
+ getIntent()
+ .getExtras()
+ .getParcelable(
+ DvrSeriesSchedulesFragment
+ .SERIES_SCHEDULES_KEY_SERIES_RECORDING);
// To get programs faster, hold the update of the series schedules.
SeriesRecordingScheduler.getInstance(this).pauseUpdate();
new EpisodicProgramLoadTask(this, Collections.singletonList(seriesRecording)) {
@@ -110,7 +113,9 @@ public class DvrSchedulesActivity extends Activity {
private void showDvrSeriesSchedulesFragment(Bundle args) {
DvrSeriesSchedulesFragment schedulesFragment = new DvrSeriesSchedulesFragment();
schedulesFragment.setArguments(args);
- getFragmentManager().beginTransaction().add(
- R.id.fragment_container, schedulesFragment).commit();
+ getFragmentManager()
+ .beginTransaction()
+ .add(R.id.fragment_container, schedulesFragment)
+ .commit();
}
}
diff --git a/src/com/android/tv/dvr/ui/list/DvrSchedulesFocusView.java b/src/com/android/tv/dvr/ui/list/DvrSchedulesFocusView.java
index c906c62a..c86721e8 100644
--- a/src/com/android/tv/dvr/ui/list/DvrSchedulesFocusView.java
+++ b/src/com/android/tv/dvr/ui/list/DvrSchedulesFocusView.java
@@ -23,12 +23,9 @@ import android.graphics.RectF;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
-
import com.android.tv.R;
-/**
- * A view used for focus in schedules list.
- */
+/** A view used for focus in schedules list. */
public class DvrSchedulesFocusView extends View {
private final Paint mPaint;
private final RectF mRoundRectF = new RectF();
@@ -76,13 +73,11 @@ public class DvrSchedulesFocusView extends View {
private int getRoundRectRadius() {
if (TextUtils.equals(mViewTag, mHeaderFocusViewTag)) {
- return getResources().getDimensionPixelSize(
- R.dimen.dvr_schedules_header_selector_radius);
+ return getResources()
+ .getDimensionPixelSize(R.dimen.dvr_schedules_header_selector_radius);
} else if (TextUtils.equals(mViewTag, mItemFocusViewTag)) {
return getResources().getDimensionPixelSize(R.dimen.dvr_schedules_selector_radius);
}
return 0;
}
}
-
-
diff --git a/src/com/android/tv/dvr/ui/list/DvrSchedulesFragment.java b/src/com/android/tv/dvr/ui/list/DvrSchedulesFragment.java
index 3cbb500a..d97b61f4 100644
--- a/src/com/android/tv/dvr/ui/list/DvrSchedulesFragment.java
+++ b/src/com/android/tv/dvr/ui/list/DvrSchedulesFragment.java
@@ -18,14 +18,11 @@ package com.android.tv.dvr.ui.list;
import android.os.Bundle;
import android.support.v17.leanback.widget.ClassPresenterSelector;
-
import com.android.tv.R;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.ui.list.SchedulesHeaderRowPresenter.DateHeaderRowPresenter;
-/**
- * A fragment to show the list of schedule recordings.
- */
+/** A fragment to show the list of schedule recordings. */
public class DvrSchedulesFragment extends BaseDvrSchedulesFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -46,8 +43,8 @@ public class DvrSchedulesFragment extends BaseDvrSchedulesFragment {
}
@Override
- public ScheduleRowAdapter onCreateRowsAdapter(ClassPresenterSelector presenterSelecor) {
- return new ScheduleRowAdapter(getContext(), presenterSelecor);
+ public ScheduleRowAdapter onCreateRowsAdapter(ClassPresenterSelector presenterSelector) {
+ return new ScheduleRowAdapter(getContext(), presenterSelector);
}
@Override
@@ -73,11 +70,11 @@ public class DvrSchedulesFragment extends BaseDvrSchedulesFragment {
if (args != null) {
recording = args.getParcelable(SCHEDULES_KEY_SCHEDULED_RECORDING);
}
- final int selectedPostion = getRowsAdapter().indexOf(
- getRowsAdapter().findRowByScheduledRecording(recording));
+ final int selectedPostion =
+ getRowsAdapter().indexOf(getRowsAdapter().findRowByScheduledRecording(recording));
if (selectedPostion != -1) {
return selectedPostion;
}
return super.getFirstItemPosition();
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/list/DvrSeriesSchedulesFragment.java b/src/com/android/tv/dvr/ui/list/DvrSeriesSchedulesFragment.java
index 57e7a88f..d376e358 100644
--- a/src/com/android/tv/dvr/ui/list/DvrSeriesSchedulesFragment.java
+++ b/src/com/android/tv/dvr/ui/list/DvrSeriesSchedulesFragment.java
@@ -1,18 +1,18 @@
/*
-* 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
-*/
+ * 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.list;
@@ -30,10 +30,8 @@ import android.transition.Fade;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-
-import com.android.tv.ApplicationSingletons;
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.Program;
import com.android.tv.dvr.DvrDataManager;
@@ -41,25 +39,21 @@ import com.android.tv.dvr.DvrDataManager.SeriesRecordingListener;
import com.android.tv.dvr.data.SeriesRecording;
import com.android.tv.dvr.provider.EpisodicProgramLoadTask;
import com.android.tv.dvr.ui.BigArguments;
-
import java.util.Collections;
import java.util.List;
-/**
- * A fragment to show the list of series schedule recordings.
- */
+/** A fragment to show the list of series schedule recordings. */
@TargetApi(Build.VERSION_CODES.N)
public class DvrSeriesSchedulesFragment extends BaseDvrSchedulesFragment {
/**
- * The key for series recording whose scheduled recording list will be displayed.
- * Type: {@link SeriesRecording}
+ * The key for series recording whose scheduled recording list will be displayed. Type: {@link
+ * SeriesRecording}
*/
public static final String SERIES_SCHEDULES_KEY_SERIES_RECORDING =
"series_schedules_key_series_recording";
/**
- * The key for programs which belong to the series recording whose scheduled recording list
- * will be displayed.
- * Type: List<{@link Program}>
+ * The key for programs which belong to the series recording whose scheduled recording list will
+ * be displayed. Type: List<{@link Program}>
*/
public static final String SERIES_SCHEDULES_KEY_SERIES_PROGRAMS =
"series_schedules_key_series_programs";
@@ -73,7 +67,7 @@ public class DvrSeriesSchedulesFragment extends BaseDvrSchedulesFragment {
private final SeriesRecordingListener mSeriesRecordingListener =
new SeriesRecordingListener() {
@Override
- public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) { }
+ public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) {}
@Override
public void onSeriesRecordingRemoved(SeriesRecording... seriesRecordings) {
@@ -101,26 +95,28 @@ public class DvrSeriesSchedulesFragment extends BaseDvrSchedulesFragment {
};
private final Handler mHandler = new Handler(Looper.getMainLooper());
- private final ContentObserver mContentObserver = new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- super.onChange(selfChange, uri);
- executeProgramLoadingTask();
- }
- };
+ private final ContentObserver mContentObserver =
+ new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+ executeProgramLoadingTask();
+ }
+ };
- private final ChannelDataManager.Listener mChannelListener = new ChannelDataManager.Listener() {
- @Override
- public void onLoadFinished() { }
+ private final ChannelDataManager.Listener mChannelListener =
+ new ChannelDataManager.Listener() {
+ @Override
+ public void onLoadFinished() {}
- @Override
- public void onChannelListUpdated() {
- executeProgramLoadingTask();
- }
+ @Override
+ public void onChannelListUpdated() {
+ executeProgramLoadingTask();
+ }
- @Override
- public void onChannelBrowsableChanged() { }
- };
+ @Override
+ public void onChannelBrowsableChanged() {}
+ };
public DvrSeriesSchedulesFragment() {
setEnterTransition(new Fade(Fade.IN));
@@ -132,8 +128,8 @@ public class DvrSeriesSchedulesFragment extends BaseDvrSchedulesFragment {
Bundle args = getArguments();
if (args != null) {
mSeriesRecording = args.getParcelable(SERIES_SCHEDULES_KEY_SERIES_RECORDING);
- mPrograms = (List<Program>) BigArguments.getArgument(
- SERIES_SCHEDULES_KEY_SERIES_PROGRAMS);
+ mPrograms =
+ (List<Program>) BigArguments.getArgument(SERIES_SCHEDULES_KEY_SERIES_PROGRAMS);
BigArguments.reset();
}
if (args == null || mPrograms == null) {
@@ -144,18 +140,19 @@ public class DvrSeriesSchedulesFragment extends BaseDvrSchedulesFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- ApplicationSingletons singletons = TvApplication.getSingletons(getContext());
+ TvSingletons singletons = TvSingletons.getSingletons(getContext());
mChannelDataManager = singletons.getChannelDataManager();
mChannelDataManager.addListener(mChannelListener);
mDvrDataManager = singletons.getDvrDataManager();
mDvrDataManager.addSeriesRecordingListener(mSeriesRecordingListener);
- getContext().getContentResolver().registerContentObserver(Programs.CONTENT_URI, true,
- mContentObserver);
+ getContext()
+ .getContentResolver()
+ .registerContentObserver(Programs.CONTENT_URI, true, mContentObserver);
}
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
onProgramsUpdated();
return super.onCreateView(inflater, container, savedInstanceState);
}
@@ -218,14 +215,16 @@ public class DvrSeriesSchedulesFragment extends BaseDvrSchedulesFragment {
if (mProgramLoadTask != null) {
mProgramLoadTask.cancel(true);
}
- mProgramLoadTask = new EpisodicProgramLoadTask(getContext(), mSeriesRecording) {
- @Override
- protected void onPostExecute(List<Program> programs) {
- mPrograms = programs == null ? Collections.EMPTY_LIST : programs;
- onProgramsUpdated();
- }
- };
- mProgramLoadTask.setLoadCurrentProgram(true)
+ mProgramLoadTask =
+ new EpisodicProgramLoadTask(getContext(), mSeriesRecording) {
+ @Override
+ protected void onPostExecute(List<Program> programs) {
+ mPrograms = programs == null ? Collections.EMPTY_LIST : programs;
+ onProgramsUpdated();
+ }
+ };
+ mProgramLoadTask
+ .setLoadCurrentProgram(true)
.setLoadDisallowedProgram(true)
.setLoadScheduledEpisode(true)
.setIgnoreChannelOption(true)
diff --git a/src/com/android/tv/dvr/ui/list/EpisodicProgramRow.java b/src/com/android/tv/dvr/ui/list/EpisodicProgramRow.java
index 2af832ec..d5808412 100644
--- a/src/com/android/tv/dvr/ui/list/EpisodicProgramRow.java
+++ b/src/com/android/tv/dvr/ui/list/EpisodicProgramRow.java
@@ -17,29 +17,27 @@
package com.android.tv.dvr.ui.list;
import android.content.Context;
-
import com.android.tv.data.Program;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.data.ScheduledRecording.Builder;
import com.android.tv.dvr.ui.DvrUiHelper;
-/**
- * A class for the episodic program.
- */
+/** A class for the episodic program. */
class EpisodicProgramRow extends ScheduleRow {
private final String mInputId;
private final Program mProgram;
- public EpisodicProgramRow(String inputId, Program program, ScheduledRecording recording,
+ public EpisodicProgramRow(
+ String inputId,
+ Program program,
+ ScheduledRecording recording,
SchedulesHeaderRow headerRow) {
super(recording, headerRow);
mInputId = inputId;
mProgram = program;
}
- /**
- * Returns the program.
- */
+ /** Returns the program. */
public Program getProgram() {
return mProgram;
}
@@ -82,9 +80,6 @@ class EpisodicProgramRow extends ScheduleRow {
@Override
public String toString() {
- return super.toString()
- + "(inputId=" + mInputId
- + ",program=" + mProgram
- + ")";
+ return super.toString() + "(inputId=" + mInputId + ",program=" + mProgram + ")";
}
}
diff --git a/src/com/android/tv/dvr/ui/list/ScheduleRow.java b/src/com/android/tv/dvr/ui/list/ScheduleRow.java
index 91ba393a..b739c18f 100644
--- a/src/com/android/tv/dvr/ui/list/ScheduleRow.java
+++ b/src/com/android/tv/dvr/ui/list/ScheduleRow.java
@@ -18,14 +18,11 @@ package com.android.tv.dvr.ui.list;
import android.content.Context;
import android.support.annotation.Nullable;
-
import com.android.tv.common.SoftPreconditions;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.ui.DvrUiHelper;
-/**
- * A class for schedule recording row.
- */
+/** A class for schedule recording row. */
class ScheduleRow {
private final SchedulesHeaderRow mHeaderRow;
@Nullable private ScheduledRecording mSchedule;
@@ -37,113 +34,89 @@ class ScheduleRow {
mHeaderRow = headerRow;
}
- /**
- * Gets which {@link SchedulesHeaderRow} this schedule row belongs to.
- */
+ /** Gets which {@link SchedulesHeaderRow} this schedule row belongs to. */
public SchedulesHeaderRow getHeaderRow() {
return mHeaderRow;
}
- /**
- * Returns the recording schedule.
- */
+ /** Returns the recording schedule. */
@Nullable
public ScheduledRecording getSchedule() {
return mSchedule;
}
- /**
- * Checks if the stop recording has been requested or not.
- */
+ /** Checks if the stop recording has been requested or not. */
public boolean isStopRecordingRequested() {
return mStopRecordingRequested;
}
- /**
- * Sets the flag of stop recording request.
- */
+ /** Sets the flag of stop recording request. */
public void setStopRecordingRequested(boolean stopRecordingRequested) {
SoftPreconditions.checkState(!mStartRecordingRequested);
mStopRecordingRequested = stopRecordingRequested;
}
- /**
- * Checks if the start recording has been requested or not.
- */
+ /** Checks if the start recording has been requested or not. */
public boolean isStartRecordingRequested() {
return mStartRecordingRequested;
}
- /**
- * Sets the flag of start recording request.
- */
+ /** Sets the flag of start recording request. */
public void setStartRecordingRequested(boolean startRecordingRequested) {
SoftPreconditions.checkState(!mStopRecordingRequested);
mStartRecordingRequested = startRecordingRequested;
}
- /**
- * Sets the recording schedule.
- */
+ /** Sets the recording schedule. */
public void setSchedule(@Nullable ScheduledRecording schedule) {
mSchedule = schedule;
}
- /**
- * Returns the channel ID.
- */
+ /** Returns the channel ID. */
public long getChannelId() {
return mSchedule != null ? mSchedule.getChannelId() : -1;
}
- /**
- * Returns the start time.
- */
+ /** Returns the start time. */
public long getStartTimeMs() {
return mSchedule != null ? mSchedule.getStartTimeMs() : -1;
}
- /**
- * Returns the end time.
- */
+ /** Returns the end time. */
public long getEndTimeMs() {
return mSchedule != null ? mSchedule.getEndTimeMs() : -1;
}
- /**
- * Returns the duration.
- */
+ /** Returns the duration. */
public final long getDuration() {
return getEndTimeMs() - getStartTimeMs();
}
- /**
- * Checks if the program is on air.
- */
+ /** Checks if the program is on air. */
public final boolean isOnAir() {
long currentTimeMs = System.currentTimeMillis();
return getStartTimeMs() <= currentTimeMs && getEndTimeMs() > currentTimeMs;
}
- /**
- * Checks if the schedule is not started.
- */
+ /** Checks if the schedule is not started. */
public final boolean isRecordingNotStarted() {
return mSchedule != null
&& mSchedule.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED;
}
- /**
- * Checks if the schedule is in progress.
- */
+ /** Checks if the schedule is in progress. */
public final boolean isRecordingInProgress() {
return mSchedule != null
&& mSchedule.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS;
}
- /**
- * Checks if the schedule has been canceled or not.
- */
+ /** Checks if the schedule is failed. */
+ public final boolean isRecordingFailed() {
+ return mSchedule != null
+ && mSchedule.getState() == ScheduledRecording.STATE_RECORDING_FAILED;
+ }
+
+ /** Checks if the schedule has been canceled or not. */
public final boolean isScheduleCanceled() {
return mSchedule != null
&& mSchedule.getState() == ScheduledRecording.STATE_RECORDING_CANCELED;
@@ -152,28 +125,29 @@ class ScheduleRow {
public boolean isRecordingFinished() {
return mSchedule != null
&& (mSchedule.getState() == ScheduledRecording.STATE_RECORDING_FAILED
- || mSchedule.getState() == ScheduledRecording.STATE_RECORDING_CLIPPED
- || mSchedule.getState() == ScheduledRecording.STATE_RECORDING_FINISHED);
+ || mSchedule.getState() == ScheduledRecording.STATE_RECORDING_CLIPPED
+ || mSchedule.getState() == ScheduledRecording.STATE_RECORDING_FINISHED);
}
- /**
- * Creates and returns the new schedule with the existing information.
- */
+ public boolean hasRecordedProgram() {
+ return mSchedule != null
+ && mSchedule.getRecordedProgramId() != null
+ && mSchedule.getState() == ScheduledRecording.STATE_RECORDING_FINISHED;
+ }
+
+ /** Creates and returns the new schedule with the existing information. */
public ScheduledRecording.Builder createNewScheduleBuilder() {
return mSchedule != null ? ScheduledRecording.buildFrom(mSchedule) : null;
}
- /**
- * Returns the program title with episode number.
- */
+ /** Returns the program title with episode number. */
public String getProgramTitleWithEpisodeNumber(Context context) {
- return mSchedule != null ? DvrUiHelper.getStyledTitleWithEpisodeNumber(context,
- mSchedule, 0).toString() : null;
+ return mSchedule != null
+ ? DvrUiHelper.getStyledTitleWithEpisodeNumber(context, mSchedule, 0).toString()
+ : null;
}
- /**
- * Returns the program title including the season/episode number.
- */
+ /** Returns the program title including the season/episode number. */
public String getEpisodeDisplayTitle(Context context) {
return mSchedule != null ? mSchedule.getEpisodeDisplayTitle(context) : null;
}
@@ -181,15 +155,16 @@ class ScheduleRow {
@Override
public String toString() {
return getClass().getSimpleName()
- + "(schedule=" + mSchedule
- + ",stopRecordingRequested=" + mStopRecordingRequested
- + ",startRecordingRequested=" + mStartRecordingRequested
+ + "(schedule="
+ + mSchedule
+ + ",stopRecordingRequested="
+ + mStopRecordingRequested
+ + ",startRecordingRequested="
+ + mStartRecordingRequested
+ ")";
}
- /**
- * Checks if the {@code schedule} is for the program or channel.
- */
+ /** Checks if the {@code schedule} is for the program or channel. */
public boolean matchSchedule(ScheduledRecording schedule) {
if (mSchedule == null) {
return false;
diff --git a/src/com/android/tv/dvr/ui/list/ScheduleRowAdapter.java b/src/com/android/tv/dvr/ui/list/ScheduleRowAdapter.java
index 97d60473..ef4a4337 100644
--- a/src/com/android/tv/dvr/ui/list/ScheduleRowAdapter.java
+++ b/src/com/android/tv/dvr/ui/list/ScheduleRowAdapter.java
@@ -16,7 +16,9 @@
package com.android.tv.dvr.ui.list;
+import android.annotation.TargetApi;
import android.content.Context;
+import android.os.Build.VERSION_CODES;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -27,11 +29,11 @@ import android.util.ArraySet;
import android.util.Log;
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
import com.android.tv.dvr.DvrManager;
-import com.android.tv.dvr.ui.list.SchedulesHeaderRow.DateHeaderRow;
import com.android.tv.dvr.data.ScheduledRecording;
+import com.android.tv.dvr.ui.list.SchedulesHeaderRow.DateHeaderRow;
import com.android.tv.util.Utils;
import java.util.ArrayList;
@@ -40,14 +42,14 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
-/**
- * An adapter for {@link ScheduleRow}.
- */
+/** An adapter for {@link ScheduleRow}. */
+@TargetApi(VERSION_CODES.N)
+@SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated
class ScheduleRowAdapter extends ArrayObjectAdapter {
private static final String TAG = "ScheduleRowAdapter";
private static final boolean DEBUG = false;
- private final static long ONE_DAY_MS = TimeUnit.DAYS.toMillis(1);
+ private static final long ONE_DAY_MS = TimeUnit.DAYS.toMillis(1);
private static final int MSG_UPDATE_ROW = 1;
@@ -55,16 +57,17 @@ class ScheduleRowAdapter extends ArrayObjectAdapter {
private final List<String> mTitles = new ArrayList<>();
private final Set<ScheduleRow> mPendingUpdate = new ArraySet<>();
- private final Handler mHandler = new Handler(Looper.getMainLooper()) {
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == MSG_UPDATE_ROW) {
- long currentTimeMs = System.currentTimeMillis();
- handleUpdateRow(currentTimeMs);
- sendNextUpdateMessage(currentTimeMs);
- }
- }
- };
+ private final Handler mHandler =
+ new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_UPDATE_ROW) {
+ long currentTimeMs = System.currentTimeMillis();
+ handleUpdateRow(currentTimeMs);
+ sendNextUpdateMessage(currentTimeMs);
+ }
+ }
+ };
public ScheduleRowAdapter(Context context, ClassPresenterSelector classPresenterSelector) {
super(classPresenterSelector);
@@ -73,37 +76,41 @@ class ScheduleRowAdapter extends ArrayObjectAdapter {
mTitles.add(mContext.getString(R.string.dvr_date_tomorrow));
}
- /**
- * Returns context.
- */
+ /** Returns context. */
protected Context getContext() {
return mContext;
}
- /**
- * Starts schedule row adapter.
- */
+ /** Starts schedule row adapter. */
public void start() {
clear();
- List<ScheduledRecording> recordingList = TvApplication.getSingletons(mContext)
- .getDvrDataManager().getNonStartedScheduledRecordings();
- recordingList.addAll(TvApplication.getSingletons(mContext).getDvrDataManager()
- .getStartedRecordings());
- Collections.sort(recordingList,
- ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR);
+ List<ScheduledRecording> recordingList =
+ TvSingletons.getSingletons(mContext)
+ .getDvrDataManager()
+ .getNonStartedScheduledRecordings();
+ recordingList.addAll(
+ TvSingletons.getSingletons(mContext).getDvrDataManager().getStartedRecordings());
+ Collections.sort(
+ recordingList, ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR);
long deadLine = Utils.getLastMillisecondOfDay(System.currentTimeMillis());
- for (int i = 0; i < recordingList.size();) {
+ for (int i = 0; i < recordingList.size(); ) {
ArrayList<ScheduledRecording> section = new ArrayList<>();
while (i < recordingList.size() && recordingList.get(i).getStartTimeMs() < deadLine) {
section.add(recordingList.get(i++));
}
if (!section.isEmpty()) {
- SchedulesHeaderRow headerRow = new DateHeaderRow(calculateHeaderDate(deadLine),
- mContext.getResources().getQuantityString(
- R.plurals.dvr_schedules_section_subtitle, section.size(), section.size()),
- section.size(), deadLine);
+ SchedulesHeaderRow headerRow =
+ new DateHeaderRow(
+ calculateHeaderDate(deadLine),
+ mContext.getResources()
+ .getQuantityString(
+ R.plurals.dvr_schedules_section_subtitle,
+ section.size(),
+ section.size()),
+ section.size(),
+ deadLine);
add(headerRow);
- for(ScheduledRecording recording : section){
+ for (ScheduledRecording recording : section) {
add(new ScheduleRow(recording, headerRow));
}
}
@@ -113,25 +120,29 @@ class ScheduleRowAdapter extends ArrayObjectAdapter {
}
private String calculateHeaderDate(long deadLine) {
- int titleIndex = (int) ((deadLine -
- Utils.getLastMillisecondOfDay(System.currentTimeMillis())) / ONE_DAY_MS);
+ int titleIndex =
+ (int)
+ ((deadLine - Utils.getLastMillisecondOfDay(System.currentTimeMillis()))
+ / ONE_DAY_MS);
String headerDate;
if (titleIndex < mTitles.size()) {
headerDate = mTitles.get(titleIndex);
} else {
- headerDate = DateUtils.formatDateTime(getContext(), deadLine,
- DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_SHOW_DATE
- | DateUtils.FORMAT_ABBREV_MONTH);
+ headerDate =
+ DateUtils.formatDateTime(
+ getContext(),
+ deadLine,
+ DateUtils.FORMAT_SHOW_WEEKDAY
+ | DateUtils.FORMAT_SHOW_DATE
+ | DateUtils.FORMAT_ABBREV_MONTH);
}
return headerDate;
}
- /**
- * Stops schedules row adapter.
- */
+ /** Stops schedules row adapter. */
public void stop() {
mHandler.removeCallbacksAndMessages(null);
- DvrManager dvrManager = TvApplication.getSingletons(getContext()).getDvrManager();
+ DvrManager dvrManager = TvSingletons.getSingletons(getContext()).getDvrManager();
for (int i = 0; i < size(); i++) {
if (get(i) instanceof ScheduleRow) {
ScheduleRow row = (ScheduleRow) get(i);
@@ -142,9 +153,7 @@ class ScheduleRowAdapter extends ArrayObjectAdapter {
}
}
- /**
- * Gets which {@link ScheduleRow} the {@link ScheduledRecording} belongs to.
- */
+ /** Gets which {@link ScheduleRow} the {@link ScheduledRecording} belongs to. */
public ScheduleRow findRowByScheduledRecording(ScheduledRecording recording) {
if (recording == null) {
return null;
@@ -167,7 +176,8 @@ class ScheduleRowAdapter extends ArrayObjectAdapter {
continue;
}
ScheduleRow row = (ScheduleRow) item;
- if (row.getSchedule() != null && row.isStartRecordingRequested()
+ if (row.getSchedule() != null
+ && row.isStartRecordingRequested()
&& row.matchSchedule(schedule)) {
return row;
}
@@ -185,7 +195,8 @@ class ScheduleRowAdapter extends ArrayObjectAdapter {
if (get(index) instanceof ScheduleRow) {
ScheduleRow scheduleRow = (ScheduleRow) get(index);
if (ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR.compare(
- scheduleRow.getSchedule(), recording) > 0) {
+ scheduleRow.getSchedule(), recording)
+ > 0) {
break;
}
pre = index;
@@ -205,9 +216,14 @@ class ScheduleRowAdapter extends ArrayObjectAdapter {
add(index, addedRow);
updateHeaderDescription(headerRow);
} else {
- SchedulesHeaderRow headerRow = new DateHeaderRow(calculateHeaderDate(deadLine),
- mContext.getResources().getQuantityString(
- R.plurals.dvr_schedules_section_subtitle, 1, 1), 1, deadLine);
+ SchedulesHeaderRow headerRow =
+ new DateHeaderRow(
+ calculateHeaderDate(deadLine),
+ mContext.getResources()
+ .getQuantityString(
+ R.plurals.dvr_schedules_section_subtitle, 1, 1),
+ 1,
+ deadLine);
add(++pre, headerRow);
ScheduleRow addedRow = new ScheduleRow(recording, headerRow);
add(pre, addedRow);
@@ -241,14 +257,15 @@ class ScheduleRowAdapter extends ArrayObjectAdapter {
}
private void updateHeaderDescription(SchedulesHeaderRow headerRow) {
- headerRow.setDescription(mContext.getResources().getQuantityString(
- R.plurals.dvr_schedules_section_subtitle,
- headerRow.getItemCount(), headerRow.getItemCount()));
+ headerRow.setDescription(
+ mContext.getResources()
+ .getQuantityString(
+ R.plurals.dvr_schedules_section_subtitle,
+ headerRow.getItemCount(),
+ headerRow.getItemCount()));
}
- /**
- * Called when a schedule recording is added to dvr date manager.
- */
+ /** Called when a schedule recording is added to dvr date manager. */
public void onScheduledRecordingAdded(ScheduledRecording schedule) {
if (DEBUG) Log.d(TAG, "onScheduledRecordingAdded: " + schedule);
ScheduleRow row = findRowWithStartRequest(schedule);
@@ -263,9 +280,7 @@ class ScheduleRowAdapter extends ArrayObjectAdapter {
}
}
- /**
- * Called when a schedule recording is removed from dvr date manager.
- */
+ /** Called when a schedule recording is removed from dvr date manager. */
public void onScheduledRecordingRemoved(ScheduledRecording schedule) {
if (DEBUG) Log.d(TAG, "onScheduledRecordingRemoved: " + schedule);
ScheduleRow row = findRowByScheduledRecording(schedule);
@@ -276,9 +291,7 @@ class ScheduleRowAdapter extends ArrayObjectAdapter {
}
}
- /**
- * Called when a schedule recording is updated in dvr date manager.
- */
+ /** Called when a schedule recording is updated in dvr date manager. */
public void onScheduledRecordingUpdated(ScheduledRecording schedule, boolean conflictChange) {
if (DEBUG) Log.d(TAG, "onScheduledRecordingUpdated: " + schedule);
ScheduleRow row = findRowByScheduledRecording(schedule);
@@ -329,9 +342,7 @@ class ScheduleRowAdapter extends ArrayObjectAdapter {
}
}
- /**
- * Checks if there is a row which requested start/stop recording.
- */
+ /** Checks if there is a row which requested start/stop recording. */
protected boolean isStartOrStopRequested() {
for (int i = 0; i < size(); i++) {
Object item = get(i);
@@ -345,16 +356,12 @@ class ScheduleRowAdapter extends ArrayObjectAdapter {
return false;
}
- /**
- * Delays update of the row.
- */
+ /** Delays update of the row. */
protected void addPendingUpdate(ScheduleRow row) {
mPendingUpdate.add(row);
}
- /**
- * Executes the pending updates.
- */
+ /** Executes the pending updates. */
protected void executePendingUpdate() {
for (ScheduleRow row : mPendingUpdate) {
int index = indexOf(row);
@@ -365,21 +372,17 @@ class ScheduleRowAdapter extends ArrayObjectAdapter {
mPendingUpdate.clear();
}
- /**
- * To check whether the recording should be kept or not.
- */
+ /** To check whether the recording should be kept or not. */
protected boolean willBeKept(ScheduledRecording schedule) {
// CANCELED state means that the schedule was removed temporarily, which should be shown
// in the list so that the user can reschedule it.
return schedule.getEndTimeMs() > System.currentTimeMillis()
&& (schedule.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS
- || schedule.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED
- || schedule.getState() == ScheduledRecording.STATE_RECORDING_CANCELED);
+ || schedule.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED
+ || schedule.getState() == ScheduledRecording.STATE_RECORDING_CANCELED);
}
- /**
- * Handle the message to update/remove rows.
- */
+ /** Handle the message to update/remove rows. */
protected void handleUpdateRow(long currentTimeMs) {
for (int i = 0; i < size(); i++) {
Object item = get(i);
@@ -392,9 +395,7 @@ class ScheduleRowAdapter extends ArrayObjectAdapter {
}
}
- /**
- * Returns the next update time. Return {@link Long#MAX_VALUE} if no timer is necessary.
- */
+ /** Returns the next update time. Return {@link Long#MAX_VALUE} if no timer is necessary. */
protected long getNextTimerMs(long currentTimeMs) {
long earliest = Long.MAX_VALUE;
for (int i = 0; i < size(); i++) {
@@ -411,15 +412,12 @@ class ScheduleRowAdapter extends ArrayObjectAdapter {
return earliest;
}
- /**
- * Send update message at the time returned by {@link #getNextTimerMs}.
- */
+ /** Send update message at the time returned by {@link #getNextTimerMs}. */
protected final void sendNextUpdateMessage(long currentTimeMs) {
mHandler.removeMessages(MSG_UPDATE_ROW);
long nextTime = getNextTimerMs(currentTimeMs);
if (nextTime != Long.MAX_VALUE) {
- mHandler.sendEmptyMessageDelayed(MSG_UPDATE_ROW,
- nextTime - System.currentTimeMillis());
+ mHandler.sendEmptyMessageDelayed(MSG_UPDATE_ROW, nextTime - System.currentTimeMillis());
}
}
}
diff --git a/src/com/android/tv/dvr/ui/list/ScheduleRowPresenter.java b/src/com/android/tv/dvr/ui/list/ScheduleRowPresenter.java
index dc4e3c41..38d3d582 100644
--- a/src/com/android/tv/dvr/ui/list/ScheduleRowPresenter.java
+++ b/src/com/android/tv/dvr/ui/list/ScheduleRowPresenter.java
@@ -18,7 +18,6 @@ package com.android.tv.dvr.ui.list;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
@@ -37,11 +36,11 @@ import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvFeatures;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
-import com.android.tv.data.Channel;
+import com.android.tv.data.api.Channel;
import com.android.tv.dialog.HalfSizedDialogFragment;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.DvrScheduleManager;
@@ -50,21 +49,22 @@ import com.android.tv.dvr.ui.DvrStopRecordingFragment;
import com.android.tv.dvr.ui.DvrUiHelper;
import com.android.tv.util.ToastUtils;
import com.android.tv.util.Utils;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
-/**
- * A RowPresenter for {@link ScheduleRow}.
- */
+/** A RowPresenter for {@link ScheduleRow}. */
@TargetApi(Build.VERSION_CODES.N)
class ScheduleRowPresenter extends RowPresenter {
private static final String TAG = "ScheduleRowPresenter";
@Retention(RetentionPolicy.SOURCE)
- @IntDef({ACTION_START_RECORDING, ACTION_STOP_RECORDING, ACTION_CREATE_SCHEDULE,
- ACTION_REMOVE_SCHEDULE})
+ @IntDef({
+ ACTION_START_RECORDING,
+ ACTION_STOP_RECORDING,
+ ACTION_CREATE_SCHEDULE,
+ ACTION_REMOVE_SCHEDULE
+ })
public @interface ScheduleRowAction {}
/** An action to start recording. */
public static final int ACTION_START_RECORDING = 1;
@@ -85,9 +85,7 @@ class ScheduleRowPresenter extends RowPresenter {
private int mLastFocusedViewId;
- /**
- * A ViewHolder for {@link ScheduleRow}
- */
+ /** A ViewHolder for {@link ScheduleRow} */
public static class ScheduleRowViewHolder extends RowPresenter.ViewHolder {
private ScheduleRowPresenter mPresenter;
@ScheduleRowAction private int[] mActions;
@@ -118,29 +116,30 @@ class ScheduleRowPresenter extends RowPresenter {
new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean focused) {
- view.post(new Runnable() {
- @Override
- public void run() {
- if (view.isFocused()) {
- mPresenter.mLastFocusedViewId = view.getId();
- }
- updateSelector();
- }
- });
+ view.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (view.isFocused()) {
+ mPresenter.mLastFocusedViewId = view.getId();
+ }
+ updateSelector();
+ }
+ });
}
};
public ScheduleRowViewHolder(View view, ScheduleRowPresenter presenter) {
super(view);
mPresenter = presenter;
- mLtr = view.getContext().getResources().getConfiguration().getLayoutDirection()
- == View.LAYOUT_DIRECTION_LTR;
+ mLtr =
+ view.getContext().getResources().getConfiguration().getLayoutDirection()
+ == View.LAYOUT_DIRECTION_LTR;
mInfoContainer = (LinearLayout) view.findViewById(R.id.info_container);
- mSecondActionContainer = (RelativeLayout) view.findViewById(
- R.id.action_second_container);
+ mSecondActionContainer =
+ (RelativeLayout) view.findViewById(R.id.action_second_container);
mSecondActionView = (ImageView) view.findViewById(R.id.action_second);
- mFirstActionContainer = (RelativeLayout) view.findViewById(
- R.id.action_first_container);
+ mFirstActionContainer = (RelativeLayout) view.findViewById(R.id.action_first_container);
mFirstActionView = (ImageView) view.findViewById(R.id.action_first);
mSelectorView = view.findViewById(R.id.selector);
mTimeView = (TextView) view.findViewById(R.id.time);
@@ -151,47 +150,48 @@ class ScheduleRowPresenter extends RowPresenter {
Resources res = view.getResources();
mSelectorTranslationDelta =
res.getDimensionPixelSize(R.dimen.dvr_schedules_item_section_margin)
- - res.getDimensionPixelSize(R.dimen.dvr_schedules_item_focus_translation_delta);
- mSelectorWidthDelta = res.getDimensionPixelSize(
- R.dimen.dvr_schedules_item_focus_width_delta);
+ - res.getDimensionPixelSize(
+ R.dimen.dvr_schedules_item_focus_translation_delta);
+ mSelectorWidthDelta =
+ res.getDimensionPixelSize(R.dimen.dvr_schedules_item_focus_width_delta);
mRoundRectRadius = res.getDimensionPixelSize(R.dimen.dvr_schedules_selector_radius);
- int fullWidth = res.getDimensionPixelSize(
- R.dimen.dvr_schedules_item_width)
- - 2 * res.getDimensionPixelSize(R.dimen.dvr_schedules_layout_padding);
+ int fullWidth =
+ res.getDimensionPixelSize(R.dimen.dvr_schedules_item_width)
+ - 2 * res.getDimensionPixelSize(R.dimen.dvr_schedules_layout_padding);
mInfoContainerTargetWidthWithNoAction = fullWidth + 2 * mRoundRectRadius;
- mInfoContainerTargetWidthWithOneAction = fullWidth
- - res.getDimensionPixelSize(R.dimen.dvr_schedules_item_section_margin)
- - res.getDimensionPixelSize(R.dimen.dvr_schedules_item_delete_width)
- + mRoundRectRadius + mSelectorWidthDelta;
- mInfoContainerTargetWidthWithTwoAction = mInfoContainerTargetWidthWithOneAction
- - res.getDimensionPixelSize(R.dimen.dvr_schedules_item_section_margin)
- - res.getDimensionPixelSize(R.dimen.dvr_schedules_item_icon_size);
+ mInfoContainerTargetWidthWithOneAction =
+ fullWidth
+ - res.getDimensionPixelSize(R.dimen.dvr_schedules_item_section_margin)
+ - res.getDimensionPixelSize(R.dimen.dvr_schedules_item_delete_width)
+ + mRoundRectRadius
+ + mSelectorWidthDelta;
+ mInfoContainerTargetWidthWithTwoAction =
+ mInfoContainerTargetWidthWithOneAction
+ - res.getDimensionPixelSize(R.dimen.dvr_schedules_item_section_margin)
+ - res.getDimensionPixelSize(R.dimen.dvr_schedules_item_icon_size);
mInfoContainer.setOnFocusChangeListener(mOnFocusChangeListener);
mFirstActionContainer.setOnFocusChangeListener(mOnFocusChangeListener);
mSecondActionContainer.setOnFocusChangeListener(mOnFocusChangeListener);
}
- /**
- * Returns time view.
- */
+ /** Returns time view. */
public TextView getTimeView() {
return mTimeView;
}
- /**
- * Returns title view.
- */
+ /** Returns title view. */
public TextView getProgramTitleView() {
return mProgramTitleView;
}
private void updateSelector() {
- int animationDuration = mSelectorView.getResources().getInteger(
- android.R.integer.config_shortAnimTime);
+ int animationDuration =
+ mSelectorView.getResources().getInteger(android.R.integer.config_shortAnimTime);
DecelerateInterpolator interpolator = new DecelerateInterpolator();
- if (mInfoContainer.isFocused() || mSecondActionContainer.isFocused()
+ if (mInfoContainer.isFocused()
+ || mSecondActionContainer.isFocused()
|| mFirstActionContainer.isFocused()) {
final ViewGroup.LayoutParams lp = mSelectorView.getLayoutParams();
final int targetWidth;
@@ -208,33 +208,50 @@ class ScheduleRowPresenter extends RowPresenter {
} else if (mSecondActionContainer.isFocused()) {
targetWidth = Math.max(mSecondActionContainer.getWidth(), 2 * mRoundRectRadius);
} else {
- targetWidth = mFirstActionContainer.getWidth() + mRoundRectRadius
- + mSelectorTranslationDelta;
+ targetWidth =
+ mFirstActionContainer.getWidth()
+ + mRoundRectRadius
+ + mSelectorTranslationDelta;
}
float targetTranslationX;
if (mInfoContainer.isFocused()) {
- targetTranslationX = mLtr ? mInfoContainer.getLeft() - mRoundRectRadius
- - mSelectorView.getLeft() :
- mInfoContainer.getRight() + mRoundRectRadius - mSelectorView.getRight();
+ targetTranslationX =
+ mLtr
+ ? mInfoContainer.getLeft()
+ - mRoundRectRadius
+ - mSelectorView.getLeft()
+ : mInfoContainer.getRight()
+ + mRoundRectRadius
+ - mSelectorView.getRight();
} else if (mSecondActionContainer.isFocused()) {
if (mSecondActionContainer.getWidth() > 2 * mRoundRectRadius) {
- targetTranslationX = mLtr ? mSecondActionContainer.getLeft() -
- mSelectorView.getLeft()
- : mSecondActionContainer.getRight() - mSelectorView.getRight();
+ targetTranslationX =
+ mLtr
+ ? mSecondActionContainer.getLeft() - mSelectorView.getLeft()
+ : mSecondActionContainer.getRight()
+ - mSelectorView.getRight();
} else {
- targetTranslationX = mLtr ? mSecondActionContainer.getLeft() -
- (mRoundRectRadius - mSecondActionContainer.getWidth() / 2) -
- mSelectorView.getLeft()
- : mSecondActionContainer.getRight() +
- (mRoundRectRadius - mSecondActionContainer.getWidth() / 2) -
- mSelectorView.getRight();
+ targetTranslationX =
+ mLtr
+ ? mSecondActionContainer.getLeft()
+ - (mRoundRectRadius
+ - mSecondActionContainer.getWidth() / 2)
+ - mSelectorView.getLeft()
+ : mSecondActionContainer.getRight()
+ + (mRoundRectRadius
+ - mSecondActionContainer.getWidth() / 2)
+ - mSelectorView.getRight();
}
} else {
- targetTranslationX = mLtr ? mFirstActionContainer.getLeft()
- - mSelectorTranslationDelta - mSelectorView.getLeft()
- : mFirstActionContainer.getRight() + mSelectorTranslationDelta
- - mSelectorView.getRight();
+ targetTranslationX =
+ mLtr
+ ? mFirstActionContainer.getLeft()
+ - mSelectorTranslationDelta
+ - mSelectorView.getLeft()
+ : mFirstActionContainer.getRight()
+ + mSelectorTranslationDelta
+ - mSelectorView.getRight();
}
if (mSelectorView.getAlpha() == 0) {
@@ -246,57 +263,72 @@ class ScheduleRowPresenter extends RowPresenter {
// animate the selector in and to the proper width and translation X.
final float deltaWidth = lp.width - targetWidth;
mSelectorView.animate().cancel();
- mSelectorView.animate().translationX(targetTranslationX).alpha(1f)
- .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- // Set width to the proper width for this animation step.
- lp.width = targetWidth + Math.round(
- deltaWidth * (1f - animation.getAnimatedFraction()));
- mSelectorView.requestLayout();
- }
- }).setDuration(animationDuration).setInterpolator(interpolator).start();
+ mSelectorView
+ .animate()
+ .translationX(targetTranslationX)
+ .alpha(1f)
+ .setUpdateListener(
+ animation -> {
+ // Set width to the proper width for this animation step.
+ float fraction = 1f - animation.getAnimatedFraction();
+ lp.width = targetWidth + Math.round(deltaWidth * fraction);
+ mSelectorView.requestLayout();
+ })
+ .setDuration(animationDuration)
+ .setInterpolator(interpolator)
+ .start();
if (mPendingAnimationRunnable != null) {
mPendingAnimationRunnable.run();
mPendingAnimationRunnable = null;
}
} else {
mSelectorView.animate().cancel();
- mSelectorView.animate().alpha(0f).setDuration(animationDuration)
- .setInterpolator(interpolator).setUpdateListener(null).start();
+ mSelectorView
+ .animate()
+ .alpha(0f)
+ .setDuration(animationDuration)
+ .setInterpolator(interpolator)
+ .setUpdateListener(null)
+ .start();
}
}
- /**
- * Grey out the information body.
- */
+ /** Grey out the information body. */
public void greyOutInfo() {
- mTimeView.setTextColor(mInfoContainer.getResources().getColor(R.color
- .dvr_schedules_item_info_grey, null));
- mProgramTitleView.setTextColor(mInfoContainer.getResources().getColor(R.color
- .dvr_schedules_item_info_grey, null));
- mInfoSeparatorView.setTextColor(mInfoContainer.getResources().getColor(R.color
- .dvr_schedules_item_info_grey, null));
- mChannelNameView.setTextColor(mInfoContainer.getResources().getColor(R.color
- .dvr_schedules_item_info_grey, null));
- mConflictInfoView.setTextColor(mInfoContainer.getResources().getColor(R.color
- .dvr_schedules_item_info_grey, null));
+ mTimeView.setTextColor(
+ mInfoContainer
+ .getResources()
+ .getColor(R.color.dvr_schedules_item_info_grey, null));
+ mProgramTitleView.setTextColor(
+ mInfoContainer
+ .getResources()
+ .getColor(R.color.dvr_schedules_item_info_grey, null));
+ mInfoSeparatorView.setTextColor(
+ mInfoContainer
+ .getResources()
+ .getColor(R.color.dvr_schedules_item_info_grey, null));
+ mChannelNameView.setTextColor(
+ mInfoContainer
+ .getResources()
+ .getColor(R.color.dvr_schedules_item_info_grey, null));
+ mConflictInfoView.setTextColor(
+ mInfoContainer
+ .getResources()
+ .getColor(R.color.dvr_schedules_item_info_grey, null));
}
- /**
- * Reverse grey out operation.
- */
+ /** Reverse grey out operation. */
public void whiteBackInfo() {
- mTimeView.setTextColor(mInfoContainer.getResources().getColor(R.color
- .dvr_schedules_item_info, null));
- mProgramTitleView.setTextColor(mInfoContainer.getResources().getColor(R.color
- .dvr_schedules_item_main, null));
- mInfoSeparatorView.setTextColor(mInfoContainer.getResources().getColor(R.color
- .dvr_schedules_item_info, null));
- mChannelNameView.setTextColor(mInfoContainer.getResources().getColor(R.color
- .dvr_schedules_item_info, null));
- mConflictInfoView.setTextColor(mInfoContainer.getResources().getColor(R.color
- .dvr_schedules_item_info, null));
+ mTimeView.setTextColor(
+ mInfoContainer.getResources().getColor(R.color.dvr_schedules_item_info, null));
+ mProgramTitleView.setTextColor(
+ mInfoContainer.getResources().getColor(R.color.dvr_schedules_item_main, null));
+ mInfoSeparatorView.setTextColor(
+ mInfoContainer.getResources().getColor(R.color.dvr_schedules_item_info, null));
+ mChannelNameView.setTextColor(
+ mInfoContainer.getResources().getColor(R.color.dvr_schedules_item_info, null));
+ mConflictInfoView.setTextColor(
+ mInfoContainer.getResources().getColor(R.color.dvr_schedules_item_info, null));
}
}
@@ -304,32 +336,29 @@ class ScheduleRowPresenter extends RowPresenter {
setHeaderPresenter(null);
setSelectEffectEnabled(false);
mContext = context;
- mDvrManager = TvApplication.getSingletons(context).getDvrManager();
- mDvrScheduleManager = TvApplication.getSingletons(context).getDvrScheduleManager();
- mTunerConflictWillNotBeRecordedInfo = mContext.getString(
- R.string.dvr_schedules_tuner_conflict_will_not_be_recorded_info);
- mTunerConflictWillBePartiallyRecordedInfo = mContext.getString(
- R.string.dvr_schedules_tuner_conflict_will_be_partially_recorded);
- mAnimationDuration = mContext.getResources().getInteger(
- android.R.integer.config_shortAnimTime);
+ mDvrManager = TvSingletons.getSingletons(context).getDvrManager();
+ mDvrScheduleManager = TvSingletons.getSingletons(context).getDvrScheduleManager();
+ mTunerConflictWillNotBeRecordedInfo =
+ mContext.getString(R.string.dvr_schedules_tuner_conflict_will_not_be_recorded_info);
+ mTunerConflictWillBePartiallyRecordedInfo =
+ mContext.getString(
+ R.string.dvr_schedules_tuner_conflict_will_be_partially_recorded);
+ mAnimationDuration =
+ mContext.getResources().getInteger(android.R.integer.config_shortAnimTime);
}
@Override
public ViewHolder createRowViewHolder(ViewGroup parent) {
- return onGetScheduleRowViewHolder(LayoutInflater.from(mContext)
- .inflate(R.layout.dvr_schedules_item, parent, false));
+ return onGetScheduleRowViewHolder(
+ LayoutInflater.from(mContext).inflate(R.layout.dvr_schedules_item, parent, false));
}
- /**
- * Returns context.
- */
+ /** Returns context. */
protected Context getContext() {
return mContext;
}
- /**
- * Returns DVR manager.
- */
+ /** Returns DVR manager. */
protected DvrManager getDvrManager() {
return mDvrManager;
}
@@ -341,54 +370,77 @@ class ScheduleRowPresenter extends RowPresenter {
ScheduleRow row = (ScheduleRow) item;
@ScheduleRowAction int[] actions = getAvailableActions(row);
viewHolder.mActions = actions;
- viewHolder.mInfoContainer.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (isInfoClickable(row)) {
- onInfoClicked(row);
- }
- }
- });
+ viewHolder.mInfoContainer.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (isInfoClickable(row)) {
+ onInfoClicked(row);
+ }
+ }
+ });
- viewHolder.mFirstActionContainer.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onActionClicked(actions[0], row);
- }
- });
+ viewHolder.mFirstActionContainer.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ onActionClicked(actions[0], row);
+ }
+ });
- viewHolder.mSecondActionContainer.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onActionClicked(actions[1], row);
- }
- });
+ viewHolder.mSecondActionContainer.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ onActionClicked(actions[1], row);
+ }
+ });
viewHolder.mTimeView.setText(onGetRecordingTimeText(row));
String programInfoText = onGetProgramInfoText(row);
if (TextUtils.isEmpty(programInfoText)) {
int durationMins = Math.max(1, Utils.getRoundOffMinsFromMs(row.getDuration()));
- programInfoText = mContext.getResources().getQuantityString(
- R.plurals.dvr_schedules_recording_duration, durationMins, durationMins);
+ programInfoText =
+ mContext.getResources()
+ .getQuantityString(
+ R.plurals.dvr_schedules_recording_duration,
+ durationMins,
+ durationMins);
}
String channelName = getChannelNameText(row);
viewHolder.mProgramTitleView.setText(programInfoText);
- viewHolder.mInfoSeparatorView.setVisibility((!TextUtils.isEmpty(programInfoText)
- && !TextUtils.isEmpty(channelName)) ? View.VISIBLE : View.GONE);
+ viewHolder.mInfoSeparatorView.setVisibility(
+ (!TextUtils.isEmpty(programInfoText) && !TextUtils.isEmpty(channelName))
+ ? View.VISIBLE
+ : View.GONE);
viewHolder.mChannelNameView.setText(channelName);
if (actions != null) {
switch (actions.length) {
case 2:
viewHolder.mSecondActionView.setImageResource(getImageForAction(actions[1]));
- // pass through
+ // fall through
case 1:
viewHolder.mFirstActionView.setImageResource(getImageForAction(actions[0]));
break;
+ default: // fall out
}
}
- if (mDvrManager.isConflicting(row.getSchedule())) {
+ ScheduledRecording schedule = row.getSchedule();
+ if (mDvrManager.isConflicting(schedule)
+ || (TvFeatures.DVR_FAILED_LIST.isEnabled(getContext())
+ && schedule != null
+ && schedule.getState() == ScheduledRecording.STATE_RECORDING_FAILED)) {
String conflictInfo;
- if (mDvrScheduleManager.isPartiallyConflicting(row.getSchedule())) {
+ if (TvFeatures.DVR_FAILED_LIST.isEnabled(getContext())
+ && schedule != null
+ && schedule.getState() == ScheduledRecording.STATE_RECORDING_FAILED) {
+ // TODO(b/72638385): show real error messages
+ // TODO(b/72638385): use a better name for ConflictInfoXXX
+ conflictInfo = "Failed";
+ if (schedule.getFailedReason() != null) {
+ conflictInfo += " (Error code: " + schedule.getFailedReason() + ")";
+ }
+ } else if (mDvrScheduleManager.isPartiallyConflicting(row.getSchedule())) {
conflictInfo = mTunerConflictWillBePartiallyRecordedInfo;
} else {
conflictInfo = mTunerConflictWillNotBeRecordedInfo;
@@ -422,51 +474,48 @@ class ScheduleRowPresenter extends RowPresenter {
}
}
- /**
- * Returns view holder for schedule row.
- */
+ /** Returns view holder for schedule row. */
protected ScheduleRowViewHolder onGetScheduleRowViewHolder(View view) {
return new ScheduleRowViewHolder(view, this);
}
- /**
- * Returns time text for time view from scheduled recording.
- */
+ /** Returns time text for time view from scheduled recording. */
protected String onGetRecordingTimeText(ScheduleRow row) {
- return Utils.getDurationString(mContext, row.getStartTimeMs(), row.getEndTimeMs(), true,
- false, true, 0);
+ return Utils.getDurationString(
+ mContext, row.getStartTimeMs(), row.getEndTimeMs(), true, false, true, 0);
}
- /**
- * Returns program info text for program title view.
- */
+ /** Returns program info text for program title view. */
protected String onGetProgramInfoText(ScheduleRow row) {
return row.getProgramTitleWithEpisodeNumber(mContext);
}
private String getChannelNameText(ScheduleRow row) {
- Channel channel = TvApplication.getSingletons(mContext).getChannelDataManager()
- .getChannel(row.getChannelId());
- return channel == null ? null :
- TextUtils.isEmpty(channel.getDisplayName()) ? channel.getDisplayNumber() :
- channel.getDisplayName().trim() + " " + channel.getDisplayNumber();
+ Channel channel =
+ TvSingletons.getSingletons(mContext)
+ .getChannelDataManager()
+ .getChannel(row.getChannelId());
+ return channel == null
+ ? null
+ : TextUtils.isEmpty(channel.getDisplayName())
+ ? channel.getDisplayNumber()
+ : channel.getDisplayName().trim() + " " + channel.getDisplayNumber();
}
- /**
- * Called when user click Info in {@link ScheduleRow}.
- */
+ /** Called when user click Info in {@link ScheduleRow}. */
protected void onInfoClicked(ScheduleRow row) {
DvrUiHelper.startDetailsActivity((Activity) mContext, row.getSchedule(), null, true);
}
private boolean isInfoClickable(ScheduleRow row) {
- return row.getSchedule() != null
- && (row.getSchedule().isNotStarted() || row.getSchedule().isInProgress());
+ ScheduledRecording schedule = row.getSchedule();
+ return schedule != null
+ && (schedule.isNotStarted()
+ || schedule.isInProgress()
+ || schedule.isFinished());
}
- /**
- * Called when the button in a row is clicked.
- */
+ /** Called when the button in a row is clicked. */
protected void onActionClicked(@ScheduleRowAction final int action, ScheduleRow row) {
switch (action) {
case ACTION_START_RECORDING:
@@ -481,12 +530,11 @@ class ScheduleRowPresenter extends RowPresenter {
case ACTION_REMOVE_SCHEDULE:
onRemoveSchedule(row);
break;
+ default: // fall out
}
}
- /**
- * Action handler for {@link #ACTION_START_RECORDING}.
- */
+ /** Action handler for {@link #ACTION_START_RECORDING}. */
protected void onStartRecording(ScheduleRow row) {
ScheduledRecording schedule = row.getSchedule();
if (schedule == null) {
@@ -495,12 +543,16 @@ class ScheduleRowPresenter extends RowPresenter {
}
// Checks if there are current recordings that will be stopped by schedule this program.
// If so, shows confirmation dialog to users.
- List<ScheduledRecording> conflictSchedules = mDvrScheduleManager.getConflictingSchedules(
- schedule.getChannelId(), System.currentTimeMillis(), schedule.getEndTimeMs());
+ List<ScheduledRecording> conflictSchedules =
+ mDvrScheduleManager.getConflictingSchedules(
+ schedule.getChannelId(),
+ System.currentTimeMillis(),
+ schedule.getEndTimeMs());
for (int i = conflictSchedules.size() - 1; i >= 0; i--) {
ScheduledRecording conflictSchedule = conflictSchedules.get(i);
if (conflictSchedule.isInProgress()) {
- DvrUiHelper.showStopRecordingDialog((Activity) mContext,
+ DvrUiHelper.showStopRecordingDialog(
+ (Activity) mContext,
conflictSchedule.getChannelId(),
DvrStopRecordingFragment.REASON_ON_CONFLICT,
new HalfSizedDialogFragment.OnActionClickListener() {
@@ -523,26 +575,27 @@ class ScheduleRowPresenter extends RowPresenter {
if (row.isRecordingNotStarted()) {
mDvrManager.setHighestPriority(row.getSchedule());
} else if (row.isRecordingFinished()) {
- mDvrManager.addSchedule(ScheduledRecording.buildFrom(row.getSchedule())
- .setId(ScheduledRecording.ID_NOT_SET)
- .setState(ScheduledRecording.STATE_RECORDING_NOT_STARTED)
- .setPriority(mDvrManager.suggestHighestPriority(row.getSchedule()))
- .build());
+ mDvrManager.addSchedule(
+ ScheduledRecording.buildFrom(row.getSchedule())
+ .setId(ScheduledRecording.ID_NOT_SET)
+ .setState(ScheduledRecording.STATE_RECORDING_NOT_STARTED)
+ .setPriority(mDvrManager.suggestHighestPriority(row.getSchedule()))
+ .build());
} else {
- SoftPreconditions.checkState(false, TAG, "Invalid row state to start recording: "
- + row);
+ SoftPreconditions.checkState(
+ false, TAG, "Invalid row state to start recording: " + row);
return;
}
- String msg = mContext.getString(R.string.dvr_msg_current_program_scheduled,
- row.getSchedule().getProgramTitle(),
- Utils.toTimeString(row.getEndTimeMs(), false));
+ String msg =
+ mContext.getString(
+ R.string.dvr_msg_current_program_scheduled,
+ row.getSchedule().getProgramTitle(),
+ Utils.toTimeString(row.getEndTimeMs(), false));
ToastUtils.show(mContext, msg, Toast.LENGTH_SHORT);
}
}
- /**
- * Action handler for {@link #ACTION_STOP_RECORDING}.
- */
+ /** Action handler for {@link #ACTION_STOP_RECORDING}. */
protected void onStopRecording(ScheduleRow row) {
if (row.getSchedule() == null) {
// This row has been deleted.
@@ -555,15 +608,15 @@ class ScheduleRowPresenter extends RowPresenter {
if (TextUtils.isEmpty(deletedInfo)) {
deletedInfo = getChannelNameText(row);
}
- ToastUtils.show(mContext, mContext.getResources()
- .getString(R.string.dvr_schedules_deletion_info, deletedInfo),
+ ToastUtils.show(
+ mContext,
+ mContext.getResources()
+ .getString(R.string.dvr_schedules_deletion_info, deletedInfo),
Toast.LENGTH_SHORT);
}
}
- /**
- * Action handler for {@link #ACTION_CREATE_SCHEDULE}.
- */
+ /** Action handler for {@link #ACTION_CREATE_SCHEDULE}. */
protected void onCreateSchedule(ScheduleRow row) {
if (row.getSchedule() == null) {
// This row has been deleted.
@@ -571,12 +624,15 @@ class ScheduleRowPresenter extends RowPresenter {
}
if (!row.isOnAir()) {
if (row.isScheduleCanceled()) {
- mDvrManager.updateScheduledRecording(ScheduledRecording.buildFrom(row.getSchedule())
- .setState(ScheduledRecording.STATE_RECORDING_NOT_STARTED)
- .setPriority(mDvrManager.suggestHighestPriority(row.getSchedule()))
- .build());
- String msg = mContext.getString(R.string.dvr_msg_program_scheduled,
- row.getSchedule().getProgramTitle());
+ mDvrManager.updateScheduledRecording(
+ ScheduledRecording.buildFrom(row.getSchedule())
+ .setState(ScheduledRecording.STATE_RECORDING_NOT_STARTED)
+ .setPriority(mDvrManager.suggestHighestPriority(row.getSchedule()))
+ .build());
+ String msg =
+ mContext.getString(
+ R.string.dvr_msg_program_scheduled,
+ row.getSchedule().getProgramTitle());
ToastUtils.show(mContext, msg, Toast.LENGTH_SHORT);
} else if (mDvrManager.isConflicting(row.getSchedule())) {
mDvrManager.setHighestPriority(row.getSchedule());
@@ -584,9 +640,7 @@ class ScheduleRowPresenter extends RowPresenter {
}
}
- /**
- * Action handler for {@link #ACTION_REMOVE_SCHEDULE}.
- */
+ /** Action handler for {@link #ACTION_REMOVE_SCHEDULE}. */
protected void onRemoveSchedule(ScheduleRow row) {
if (row.getSchedule() == null) {
// This row has been deleted.
@@ -605,13 +659,19 @@ class ScheduleRowPresenter extends RowPresenter {
mDvrManager.removeScheduledRecording(row.getSchedule());
} else if (row.isRecordingNotStarted()) {
deletedInfo = getDeletedInfo(row);
- mDvrManager.updateScheduledRecording(ScheduledRecording.buildFrom(row.getSchedule())
- .setState(ScheduledRecording.STATE_RECORDING_CANCELED)
- .build());
+ mDvrManager.updateScheduledRecording(
+ ScheduledRecording.buildFrom(row.getSchedule())
+ .setState(ScheduledRecording.STATE_RECORDING_CANCELED)
+ .build());
+ } else if (row.isRecordingFailed()) {
+ deletedInfo = getDeletedInfo(row);
+ mDvrManager.removeScheduledRecording(row.getSchedule());
}
}
if (deletedInfo != null) {
- ToastUtils.show(mContext, mContext.getResources()
+ ToastUtils.show(
+ mContext,
+ mContext.getResources()
.getString(R.string.dvr_schedules_deletion_info, deletedInfo),
Toast.LENGTH_SHORT);
}
@@ -631,9 +691,7 @@ class ScheduleRowPresenter extends RowPresenter {
updateActionContainer(vh, selected);
}
- /**
- * Internal method for onRowViewSelected, can be customized by subclass.
- */
+ /** Internal method for onRowViewSelected, can be customized by subclass. */
private void updateActionContainer(ViewHolder vh, boolean selected) {
ScheduleRowViewHolder viewHolder = (ScheduleRowViewHolder) vh;
viewHolder.mSecondActionContainer.animate().setListener(null).cancel();
@@ -643,38 +701,43 @@ class ScheduleRowPresenter extends RowPresenter {
case 2:
prepareShowActionView(viewHolder.mSecondActionContainer);
prepareShowActionView(viewHolder.mFirstActionContainer);
- viewHolder.mPendingAnimationRunnable = new Runnable() {
- @Override
- public void run() {
- showActionView(viewHolder.mSecondActionContainer);
- showActionView(viewHolder.mFirstActionContainer);
- }
- };
+ viewHolder.mPendingAnimationRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ showActionView(viewHolder.mSecondActionContainer);
+ showActionView(viewHolder.mFirstActionContainer);
+ }
+ };
break;
case 1:
prepareShowActionView(viewHolder.mFirstActionContainer);
- viewHolder.mPendingAnimationRunnable = new Runnable() {
- @Override
- public void run() {
- hideActionView(viewHolder.mSecondActionContainer, View.GONE);
- showActionView(viewHolder.mFirstActionContainer);
- }
- };
+ viewHolder.mPendingAnimationRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ hideActionView(viewHolder.mSecondActionContainer, View.GONE);
+ showActionView(viewHolder.mFirstActionContainer);
+ }
+ };
if (mLastFocusedViewId == R.id.action_second_container) {
mLastFocusedViewId = R.id.info_container;
}
break;
case 0:
default:
- viewHolder.mPendingAnimationRunnable = new Runnable() {
- @Override
- public void run() {
- hideActionView(viewHolder.mSecondActionContainer, View.GONE);
- hideActionView(viewHolder.mFirstActionContainer, View.GONE);
- }
- };
+ viewHolder.mPendingAnimationRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ hideActionView(viewHolder.mSecondActionContainer, View.GONE);
+ hideActionView(viewHolder.mFirstActionContainer, View.GONE);
+ }
+ };
mLastFocusedViewId = R.id.info_container;
- SoftPreconditions.checkState(viewHolder.mInfoContainer.isFocusable(), TAG,
+ SoftPreconditions.checkState(
+ viewHolder.mInfoContainer.isFocusable(),
+ TAG,
"No focusable view in this row: " + viewHolder);
break;
}
@@ -685,7 +748,7 @@ class ScheduleRowPresenter extends RowPresenter {
// requestFocus() explicitly.
if (view.hasFocus()) {
viewHolder.mPendingAnimationRunnable.run();
- } else if (view.isFocusable()){
+ } else if (view.isFocusable()) {
view.requestFocus();
} else {
viewHolder.view.requestFocus();
@@ -705,17 +768,16 @@ class ScheduleRowPresenter extends RowPresenter {
view.setVisibility(View.VISIBLE);
}
- /**
- * Add animation when view is visible.
- */
+ /** Add animation when view is visible. */
private void showActionView(View view) {
- view.animate().alpha(1.0f).setInterpolator(new DecelerateInterpolator())
- .setDuration(mAnimationDuration).start();
+ view.animate()
+ .alpha(1.0f)
+ .setInterpolator(new DecelerateInterpolator())
+ .setDuration(mAnimationDuration)
+ .start();
}
- /**
- * Add animation when view change to invisible.
- */
+ /** Add animation when view change to invisible. */
private void hideActionView(View view, int visibility) {
if (view.getVisibility() != View.VISIBLE) {
if (view.getVisibility() != visibility) {
@@ -723,15 +785,19 @@ class ScheduleRowPresenter extends RowPresenter {
}
return;
}
- view.animate().alpha(0.0f).setInterpolator(new DecelerateInterpolator())
+ view.animate()
+ .alpha(0.0f)
+ .setInterpolator(new DecelerateInterpolator())
.setDuration(mAnimationDuration)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- view.setVisibility(visibility);
- view.animate().setListener(null);
- }
- }).start();
+ .setListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setVisibility(visibility);
+ view.animate().setListener(null);
+ }
+ })
+ .start();
}
/**
@@ -742,8 +808,8 @@ class ScheduleRowPresenter extends RowPresenter {
protected int[] getAvailableActions(ScheduleRow row) {
if (row.getSchedule() != null) {
if (row.isRecordingInProgress()) {
- return new int[]{ACTION_STOP_RECORDING};
- } else if (row.isOnAir()) {
+ return new int[] {ACTION_STOP_RECORDING};
+ } else if (row.isOnAir() && !row.hasRecordedProgram()) {
if (row.isRecordingNotStarted()) {
if (canResolveConflict()) {
// The "START" action can change the conflict states.
@@ -754,8 +820,12 @@ class ScheduleRowPresenter extends RowPresenter {
} else if (row.isRecordingFinished()) {
return new int[] {ACTION_START_RECORDING};
} else {
- SoftPreconditions.checkState(false, TAG, "Invalid row state in checking the"
- + " available actions(on air): " + row);
+ SoftPreconditions.checkState(
+ false,
+ TAG,
+ "Invalid row state in checking the"
+ + " available actions(on air): "
+ + row);
}
} else {
if (row.isScheduleCanceled()) {
@@ -764,36 +834,39 @@ class ScheduleRowPresenter extends RowPresenter {
return new int[] {ACTION_REMOVE_SCHEDULE, ACTION_CREATE_SCHEDULE};
} else if (row.isRecordingNotStarted()) {
return new int[] {ACTION_REMOVE_SCHEDULE};
+ } else if (row.isRecordingFailed()) {
+ return new int[] {ACTION_REMOVE_SCHEDULE};
+ } else if (row.isRecordingFinished()) {
+ return new int[] {};
} else {
- SoftPreconditions.checkState(false, TAG, "Invalid row state in checking the"
- + " available actions(future schedule): " + row);
+ SoftPreconditions.checkState(
+ false,
+ TAG,
+ "Invalid row state in checking the"
+ + " available actions(future schedule): "
+ + row);
}
}
}
return null;
}
- /**
- * Check if the conflict can be resolved in this screen.
- */
+ /** Check if the conflict can be resolved in this screen. */
protected boolean canResolveConflict() {
return true;
}
- /**
- * Check if the schedule should be kept after removing it.
- */
+ /** Check if the schedule should be kept after removing it. */
protected boolean shouldKeepScheduleAfterRemoving() {
return false;
}
- /**
- * Checks if the row should be grayed out.
- */
+ /** Checks if the row should be grayed out. */
protected boolean shouldBeGrayedOut(ScheduleRow row) {
return row.getSchedule() == null
- || (row.isOnAir() && !row.isRecordingInProgress())
+ || (row.isOnAir() && !row.isRecordingInProgress() && !row.hasRecordedProgram())
|| mDvrManager.isConflicting(row.getSchedule())
- || row.isScheduleCanceled();
+ || row.isScheduleCanceled()
+ || row.isRecordingFailed();
}
}
diff --git a/src/com/android/tv/dvr/ui/list/SchedulesHeaderRow.java b/src/com/android/tv/dvr/ui/list/SchedulesHeaderRow.java
index 715ecb8c..bbddc07f 100644
--- a/src/com/android/tv/dvr/ui/list/SchedulesHeaderRow.java
+++ b/src/com/android/tv/dvr/ui/list/SchedulesHeaderRow.java
@@ -18,12 +18,9 @@ package com.android.tv.dvr.ui.list;
import com.android.tv.data.Program;
import com.android.tv.dvr.data.SeriesRecording;
-
import java.util.List;
-/**
- * A base class for the rows for schedules' header.
- */
+/** A base class for the rows for schedules' header. */
abstract class SchedulesHeaderRow {
private String mTitle;
private String mDescription;
@@ -35,51 +32,37 @@ abstract class SchedulesHeaderRow {
mDescription = description;
}
- /**
- * Sets title.
- */
+ /** Sets title. */
public void setTitle(String title) {
mTitle = title;
}
- /**
- * Sets description.
- */
+ /** Sets description. */
public void setDescription(String description) {
mDescription = description;
}
- /**
- * Sets count of items.
- */
+ /** Sets count of items. */
public void setItemCount(int itemCount) {
mItemCount = itemCount;
}
- /**
- * Returns title.
- */
+ /** Returns title. */
public String getTitle() {
return mTitle;
}
- /**
- * Returns description.
- */
+ /** Returns description. */
public String getDescription() {
return mDescription;
}
- /**
- * Returns count of items.
- */
+ /** Returns count of items. */
public int getItemCount() {
return mItemCount;
}
- /**
- * The header row which represent the date.
- */
+ /** The header row which represent the date. */
public static class DateHeaderRow extends SchedulesHeaderRow {
private long mDeadLineMs;
@@ -88,47 +71,41 @@ abstract class SchedulesHeaderRow {
mDeadLineMs = deadLineMs;
}
- /**
- * Returns the latest time of the list which belongs to the header row.
- */
+ /** Returns the latest time of the list which belongs to the header row. */
public long getDeadLineMs() {
return mDeadLineMs;
}
}
- /**
- * The header row which represent the series recording.
- */
+ /** The header row which represent the series recording. */
public static class SeriesRecordingHeaderRow extends SchedulesHeaderRow {
private SeriesRecording mSeriesRecording;
private List<Program> mPrograms;
- public SeriesRecordingHeaderRow(String title, String description, int itemCount,
- SeriesRecording series, List<Program> programs) {
+ public SeriesRecordingHeaderRow(
+ String title,
+ String description,
+ int itemCount,
+ SeriesRecording series,
+ List<Program> programs) {
super(title, description, itemCount);
mSeriesRecording = series;
mPrograms = programs;
}
- /**
- * Returns the list of programs which belong to the series.
- */
+ /** Returns the list of programs which belong to the series. */
public List<Program> getPrograms() {
return mPrograms;
}
- /**
- * Returns the series recording, it is for series schedules list.
- */
+ /** Returns the series recording, it is for series schedules list. */
public SeriesRecording getSeriesRecording() {
return mSeriesRecording;
}
- /**
- * Sets the series recording.
- */
+ /** Sets the series recording. */
public void setSeriesRecording(SeriesRecording seriesRecording) {
mSeriesRecording = seriesRecording;
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/list/SchedulesHeaderRowPresenter.java b/src/com/android/tv/dvr/ui/list/SchedulesHeaderRowPresenter.java
index fe2033ba..eb01aba2 100644
--- a/src/com/android/tv/dvr/ui/list/SchedulesHeaderRowPresenter.java
+++ b/src/com/android/tv/dvr/ui/list/SchedulesHeaderRowPresenter.java
@@ -27,16 +27,13 @@ import android.view.View.OnFocusChangeListener;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.TextView;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.dvr.data.SeriesRecording;
import com.android.tv.dvr.ui.DvrUiHelper;
import com.android.tv.dvr.ui.list.SchedulesHeaderRow.SeriesRecordingHeaderRow;
-/**
- * A base class for RowPresenter for {@link SchedulesHeaderRow}
- */
+/** A base class for RowPresenter for {@link SchedulesHeaderRow} */
abstract class SchedulesHeaderRowPresenter extends RowPresenter {
private Context mContext;
@@ -46,23 +43,20 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter {
mContext = context;
}
- /**
- * Returns the context.
- */
+ /** Returns the context. */
Context getContext() {
return mContext;
}
- /**
- * A ViewHolder for {@link SchedulesHeaderRow}.
- */
+ /** A ViewHolder for {@link SchedulesHeaderRow}. */
public static class SchedulesHeaderRowViewHolder extends RowPresenter.ViewHolder {
private TextView mTitle;
private TextView mDescription;
public SchedulesHeaderRowViewHolder(Context context, ViewGroup parent) {
- super(LayoutInflater.from(context).inflate(R.layout.dvr_schedules_header, parent,
- false));
+ super(
+ LayoutInflater.from(context)
+ .inflate(R.layout.dvr_schedules_header, parent, false));
mTitle = (TextView) view.findViewById(R.id.header_title);
mDescription = (TextView) view.findViewById(R.id.header_description);
}
@@ -77,9 +71,7 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter {
headerViewHolder.mDescription.setText(header.getDescription());
}
- /**
- * A presenter for {@link SchedulesHeaderRow.DateHeaderRow}.
- */
+ /** A presenter for {@link SchedulesHeaderRow.DateHeaderRow}. */
public static class DateHeaderRowPresenter extends SchedulesHeaderRowPresenter {
public DateHeaderRowPresenter(Context context) {
super(context);
@@ -90,10 +82,7 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter {
return new DateHeaderRowViewHolder(getContext(), parent);
}
- /**
- * A ViewHolder for
- * {@link SchedulesHeaderRow.DateHeaderRow}.
- */
+ /** A ViewHolder for {@link SchedulesHeaderRow.DateHeaderRow}. */
public static class DateHeaderRowViewHolder extends SchedulesHeaderRowViewHolder {
public DateHeaderRowViewHolder(Context context, ViewGroup parent) {
super(context, parent);
@@ -101,9 +90,7 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter {
}
}
- /**
- * A presenter for {@link SeriesRecordingHeaderRow}.
- */
+ /** A presenter for {@link SeriesRecordingHeaderRow}. */
public static class SeriesRecordingHeaderRowPresenter extends SchedulesHeaderRowPresenter {
private final boolean mLtr;
private final Drawable mSettingsDrawable;
@@ -116,8 +103,9 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter {
public SeriesRecordingHeaderRowPresenter(Context context) {
super(context);
- mLtr = context.getResources().getConfiguration().getLayoutDirection()
- == View.LAYOUT_DIRECTION_LTR;
+ mLtr =
+ context.getResources().getConfiguration().getLayoutDirection()
+ == View.LAYOUT_DIRECTION_LTR;
mSettingsDrawable = context.getDrawable(R.drawable.ic_settings);
mCancelDrawable = context.getDrawable(R.drawable.ic_dvr_cancel_large);
mResumeDrawable = context.getDrawable(R.drawable.ic_record_start);
@@ -134,8 +122,7 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter {
@Override
protected void onBindRowViewHolder(RowPresenter.ViewHolder viewHolder, Object item) {
super.onBindRowViewHolder(viewHolder, item);
- SeriesHeaderRowViewHolder headerViewHolder =
- (SeriesHeaderRowViewHolder) viewHolder;
+ SeriesHeaderRowViewHolder headerViewHolder = (SeriesHeaderRowViewHolder) viewHolder;
SeriesRecordingHeaderRow header = (SeriesRecordingHeaderRow) item;
headerViewHolder.mSeriesSettingsButton.setVisibility(
header.getSeriesRecording().isStopped() ? View.INVISIBLE : View.VISIBLE);
@@ -148,46 +135,59 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter {
headerViewHolder.mToggleStartStopButton.setText(mCancelAllInfo);
setTextDrawable(headerViewHolder.mToggleStartStopButton, mCancelDrawable);
}
- headerViewHolder.mSeriesSettingsButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View view) {
- DvrUiHelper.startSeriesSettingsActivity(getContext(),
- header.getSeriesRecording().getId(),
- header.getPrograms(), false, false, false, null);
- }
- });
- headerViewHolder.mToggleStartStopButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View view) {
- if (header.getSeriesRecording().isStopped()) {
- // Reset priority to the highest.
- SeriesRecording seriesRecording = SeriesRecording
- .buildFrom(header.getSeriesRecording())
- .setPriority(TvApplication.getSingletons(getContext())
- .getDvrScheduleManager().suggestNewSeriesPriority())
- .build();
- TvApplication.getSingletons(getContext()).getDvrManager()
- .updateSeriesRecording(seriesRecording);
- DvrUiHelper.startSeriesSettingsActivity(getContext(),
- header.getSeriesRecording().getId(),
- header.getPrograms(), false, false, false, null);
- } else {
- DvrUiHelper.showCancelAllSeriesRecordingDialog(
- (DvrSchedulesActivity) view.getContext(),
- header.getSeriesRecording());
- }
- }
- });
+ headerViewHolder.mSeriesSettingsButton.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ DvrUiHelper.startSeriesSettingsActivity(
+ getContext(),
+ header.getSeriesRecording().getId(),
+ header.getPrograms(),
+ false,
+ false,
+ false,
+ null);
+ }
+ });
+ headerViewHolder.mToggleStartStopButton.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (header.getSeriesRecording().isStopped()) {
+ // Reset priority to the highest.
+ SeriesRecording seriesRecording =
+ SeriesRecording.buildFrom(header.getSeriesRecording())
+ .setPriority(
+ TvSingletons.getSingletons(getContext())
+ .getDvrScheduleManager()
+ .suggestNewSeriesPriority())
+ .build();
+ TvSingletons.getSingletons(getContext())
+ .getDvrManager()
+ .updateSeriesRecording(seriesRecording);
+ DvrUiHelper.startSeriesSettingsActivity(
+ getContext(),
+ header.getSeriesRecording().getId(),
+ header.getPrograms(),
+ false,
+ false,
+ false,
+ null);
+ } else {
+ DvrUiHelper.showCancelAllSeriesRecordingDialog(
+ (DvrSchedulesActivity) view.getContext(),
+ header.getSeriesRecording());
+ }
+ }
+ });
}
private void setTextDrawable(TextView textView, Drawable drawableStart) {
- textView.setCompoundDrawablesRelativeWithIntrinsicBounds(drawableStart, null, null,
- null);
+ textView.setCompoundDrawablesRelativeWithIntrinsicBounds(
+ drawableStart, null, null, null);
}
- /**
- * A ViewHolder for {@link SeriesRecordingHeaderRow}.
- */
+ /** A ViewHolder for {@link SeriesRecordingHeaderRow}. */
public static class SeriesHeaderRowViewHolder extends SchedulesHeaderRowViewHolder {
private final TextView mSeriesSettingsButton;
private final TextView mToggleStartStopButton;
@@ -196,33 +196,40 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter {
private final View mSelector;
private View mLastFocusedView;
+
public SeriesHeaderRowViewHolder(Context context, ViewGroup parent) {
super(context, parent);
- mLtr = context.getResources().getConfiguration().getLayoutDirection()
- == View.LAYOUT_DIRECTION_LTR;
+ mLtr =
+ context.getResources().getConfiguration().getLayoutDirection()
+ == View.LAYOUT_DIRECTION_LTR;
view.findViewById(R.id.button_container).setVisibility(View.VISIBLE);
mSeriesSettingsButton = (TextView) view.findViewById(R.id.series_settings);
mToggleStartStopButton =
(TextView) view.findViewById(R.id.series_toggle_start_stop);
mSelector = view.findViewById(R.id.selector);
- OnFocusChangeListener onFocusChangeListener = new View.OnFocusChangeListener() {
- @Override
- public void onFocusChange(View view, boolean focused) {
- view.post(new Runnable() {
+ OnFocusChangeListener onFocusChangeListener =
+ new View.OnFocusChangeListener() {
@Override
- public void run() {
- updateSelector(view);
+ public void onFocusChange(View view, boolean focused) {
+ view.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ updateSelector(view);
+ }
+ });
}
- });
- }
- };
+ };
mSeriesSettingsButton.setOnFocusChangeListener(onFocusChangeListener);
mToggleStartStopButton.setOnFocusChangeListener(onFocusChangeListener);
}
private void updateSelector(View focusedView) {
- int animationDuration = mSelector.getContext().getResources()
- .getInteger(android.R.integer.config_shortAnimTime);
+ int animationDuration =
+ mSelector
+ .getContext()
+ .getResources()
+ .getInteger(android.R.integer.config_shortAnimTime);
DecelerateInterpolator interpolator = new DecelerateInterpolator();
if (focusedView.hasFocus()) {
@@ -246,21 +253,38 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter {
// animate the selector in and to the proper width and translation X.
final float deltaWidth = lp.width - targetWidth;
mSelector.animate().cancel();
- mSelector.animate().translationX(targetTranslationX).alpha(1f)
- .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- // Set width to the proper width for this animation step.
- lp.width = targetWidth + Math.round(
- deltaWidth * (1f - animation.getAnimatedFraction()));
- mSelector.requestLayout();
- }
- }).setDuration(animationDuration).setInterpolator(interpolator).start();
+ mSelector
+ .animate()
+ .translationX(targetTranslationX)
+ .alpha(1f)
+ .setUpdateListener(
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ // Set width to the proper width for this animation
+ // step.
+ lp.width =
+ targetWidth
+ + Math.round(
+ deltaWidth
+ * (1f
+ - animation
+ .getAnimatedFraction()));
+ mSelector.requestLayout();
+ }
+ })
+ .setDuration(animationDuration)
+ .setInterpolator(interpolator)
+ .start();
mLastFocusedView = focusedView;
} else if (mLastFocusedView == focusedView) {
mSelector.animate().setUpdateListener(null).cancel();
- mSelector.animate().alpha(0f).setDuration(animationDuration)
- .setInterpolator(interpolator).start();
+ mSelector
+ .animate()
+ .alpha(0f)
+ .setDuration(animationDuration)
+ .setInterpolator(interpolator)
+ .start();
mLastFocusedView = null;
}
}
diff --git a/src/com/android/tv/dvr/ui/list/SeriesScheduleRowAdapter.java b/src/com/android/tv/dvr/ui/list/SeriesScheduleRowAdapter.java
index 6b6de8b8..9a9c94ea 100644
--- a/src/com/android/tv/dvr/ui/list/SeriesScheduleRowAdapter.java
+++ b/src/com/android/tv/dvr/ui/list/SeriesScheduleRowAdapter.java
@@ -1,18 +1,18 @@
/*
-* 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
-*/
+ * 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.list;
@@ -23,10 +23,8 @@ import android.os.Build;
import android.support.v17.leanback.widget.ClassPresenterSelector;
import android.util.ArrayMap;
import android.util.Log;
-
-import com.android.tv.ApplicationSingletons;
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
import com.android.tv.data.Program;
import com.android.tv.dvr.DvrDataManager;
@@ -35,16 +33,13 @@ import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.data.SeriesRecording;
import com.android.tv.dvr.ui.list.SchedulesHeaderRow.SeriesRecordingHeaderRow;
import com.android.tv.util.Utils;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-/**
- * An adapter for series schedule row.
- */
+/** An adapter for series schedule row. */
@TargetApi(Build.VERSION_CODES.N)
class SeriesScheduleRowAdapter extends ScheduleRowAdapter {
private static final String TAG = "SeriesRowAdapter";
@@ -57,7 +52,9 @@ class SeriesScheduleRowAdapter extends ScheduleRowAdapter {
private final Map<Long, Program> mPrograms = new ArrayMap<>();
private SeriesRecordingHeaderRow mHeaderRow;
- public SeriesScheduleRowAdapter(Context context, ClassPresenterSelector classPresenterSelector,
+ public SeriesScheduleRowAdapter(
+ Context context,
+ ClassPresenterSelector classPresenterSelector,
SeriesRecording seriesRecording) {
super(context, classPresenterSelector);
mSeriesRecording = seriesRecording;
@@ -67,7 +64,7 @@ class SeriesScheduleRowAdapter extends ScheduleRowAdapter {
} else {
mInputId = null;
}
- ApplicationSingletons singletons = TvApplication.getSingletons(context);
+ TvSingletons singletons = TvSingletons.getSingletons(context);
mDvrManager = singletons.getDvrManager();
mDataManager = singletons.getDvrDataManager();
setHasStableIds(true);
@@ -83,9 +80,7 @@ class SeriesScheduleRowAdapter extends ScheduleRowAdapter {
super.stop();
}
- /**
- * Sets the programs to show.
- */
+ /** Sets the programs to show. */
public void setPrograms(List<Program> programs) {
if (programs == null) {
programs = Collections.emptyList();
@@ -95,8 +90,13 @@ class SeriesScheduleRowAdapter extends ScheduleRowAdapter {
List<Program> sortedPrograms = new ArrayList<>(programs);
Collections.sort(sortedPrograms);
List<EpisodicProgramRow> rows = new ArrayList<>();
- mHeaderRow = new SeriesRecordingHeaderRow(mSeriesRecording.getTitle(),
- null, sortedPrograms.size(), mSeriesRecording, programs);
+ mHeaderRow =
+ new SeriesRecordingHeaderRow(
+ mSeriesRecording.getTitle(),
+ null,
+ sortedPrograms.size(),
+ mSeriesRecording,
+ programs);
for (Program program : sortedPrograms) {
ScheduledRecording schedule =
mDataManager.getScheduledRecordingForProgramId(program.getId());
@@ -122,8 +122,14 @@ class SeriesScheduleRowAdapter extends ScheduleRowAdapter {
++conflicts;
}
}
- return conflicts == 0 ? null : getContext().getResources().getQuantityString(
- R.plurals.dvr_series_schedules_header_description, conflicts, conflicts);
+ return conflicts == 0
+ ? null
+ : getContext()
+ .getResources()
+ .getQuantityString(
+ R.plurals.dvr_series_schedules_header_description,
+ conflicts,
+ conflicts);
}
@Override
diff --git a/src/com/android/tv/dvr/ui/list/SeriesScheduleRowPresenter.java b/src/com/android/tv/dvr/ui/list/SeriesScheduleRowPresenter.java
index c8503e0d..263c579e 100644
--- a/src/com/android/tv/dvr/ui/list/SeriesScheduleRowPresenter.java
+++ b/src/com/android/tv/dvr/ui/list/SeriesScheduleRowPresenter.java
@@ -1,33 +1,30 @@
/*
-* 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
-*/
+ * 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.list;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
-
import com.android.tv.R;
import com.android.tv.common.SoftPreconditions;
import com.android.tv.dvr.ui.DvrUiHelper;
import com.android.tv.util.Utils;
-/**
- * A RowPresenter for series schedule row.
- */
+/** A RowPresenter for series schedule row. */
class SeriesScheduleRowPresenter extends ScheduleRowPresenter {
private static final String TAG = "SeriesRowPresenter";
@@ -35,16 +32,18 @@ class SeriesScheduleRowPresenter extends ScheduleRowPresenter {
public SeriesScheduleRowPresenter(Context context) {
super(context);
- mLtr = context.getResources().getConfiguration().getLayoutDirection()
- == View.LAYOUT_DIRECTION_LTR;
+ mLtr =
+ context.getResources().getConfiguration().getLayoutDirection()
+ == View.LAYOUT_DIRECTION_LTR;
}
public static class SeriesScheduleRowViewHolder extends ScheduleRowViewHolder {
public SeriesScheduleRowViewHolder(View view, ScheduleRowPresenter presenter) {
super(view, presenter);
ViewGroup.LayoutParams lp = getTimeView().getLayoutParams();
- lp.width = view.getResources().getDimensionPixelSize(
- R.dimen.dvr_series_schedules_item_time_width);
+ lp.width =
+ view.getResources()
+ .getDimensionPixelSize(R.dimen.dvr_series_schedules_item_time_width);
getTimeView().setLayoutParams(lp);
}
}
@@ -56,8 +55,8 @@ class SeriesScheduleRowPresenter extends ScheduleRowPresenter {
@Override
protected String onGetRecordingTimeText(ScheduleRow row) {
- return Utils.getDurationString(getContext(), row.getStartTimeMs(), row.getEndTimeMs(),
- false, true, true, 0);
+ return Utils.getDurationString(
+ getContext(), row.getStartTimeMs(), row.getEndTimeMs(), false, true, true, 0);
}
@Override
@@ -71,11 +70,17 @@ class SeriesScheduleRowPresenter extends ScheduleRowPresenter {
SeriesScheduleRowViewHolder viewHolder = (SeriesScheduleRowViewHolder) vh;
EpisodicProgramRow row = (EpisodicProgramRow) item;
if (getDvrManager().isConflicting(row.getSchedule())) {
- viewHolder.getProgramTitleView().setCompoundDrawablePadding(getContext()
- .getResources().getDimensionPixelOffset(
- R.dimen.dvr_schedules_warning_icon_padding));
- viewHolder.getProgramTitleView().setCompoundDrawablesRelativeWithIntrinsicBounds(
- R.drawable.ic_warning_gray600_36dp, 0, 0, 0);
+ viewHolder
+ .getProgramTitleView()
+ .setCompoundDrawablePadding(
+ getContext()
+ .getResources()
+ .getDimensionPixelOffset(
+ R.dimen.dvr_schedules_warning_icon_padding));
+ viewHolder
+ .getProgramTitleView()
+ .setCompoundDrawablesRelativeWithIntrinsicBounds(
+ R.drawable.ic_warning_gray600_36dp, 0, 0, 0);
} else {
viewHolder.getProgramTitleView().setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
}
@@ -88,16 +93,16 @@ class SeriesScheduleRowPresenter extends ScheduleRowPresenter {
@Override
protected void onStartRecording(ScheduleRow row) {
- SoftPreconditions.checkState(row.getSchedule() == null, TAG,
- "Start request with the existing schedule: " + row);
+ SoftPreconditions.checkState(
+ row.getSchedule() == null, TAG, "Start request with the existing schedule: " + row);
row.setStartRecordingRequested(true);
getDvrManager().addScheduleWithHighestPriority(((EpisodicProgramRow) row).getProgram());
}
@Override
protected void onStopRecording(ScheduleRow row) {
- SoftPreconditions.checkState(row.getSchedule() != null, TAG,
- "Stop request with the null schedule: " + row);
+ SoftPreconditions.checkState(
+ row.getSchedule() != null, TAG, "Stop request with the null schedule: " + row);
row.setStopRecordingRequested(true);
getDvrManager().stopRecording(row.getSchedule());
}
diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlaybackActivity.java b/src/com/android/tv/dvr/ui/playback/DvrPlaybackActivity.java
index 6824cfe2..b8b19adc 100644
--- a/src/com/android/tv/dvr/ui/playback/DvrPlaybackActivity.java
+++ b/src/com/android/tv/dvr/ui/playback/DvrPlaybackActivity.java
@@ -23,16 +23,13 @@ import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.Starter;
import com.android.tv.dialog.PinDialogFragment.OnPinCheckedListener;
import com.android.tv.dvr.data.RecordedProgram;
import com.android.tv.util.Utils;
-/**
- * Activity to play a {@link RecordedProgram}.
- */
+/** Activity to play a {@link RecordedProgram}. */
public class DvrPlaybackActivity extends Activity implements OnPinCheckedListener {
private static final String TAG = "DvrPlaybackActivity";
private static final boolean DEBUG = false;
@@ -42,13 +39,14 @@ public class DvrPlaybackActivity extends Activity implements OnPinCheckedListene
@Override
public void onCreate(Bundle savedInstanceState) {
- TvApplication.setCurrentRunningProcess(this, true);
+ Starter.start(this);
if (DEBUG) Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
setIntent(createProgramIntent(getIntent()));
setContentView(R.layout.activity_dvr_playback);
- mOverlayFragment = (DvrPlaybackOverlayFragment) getFragmentManager()
- .findFragmentById(R.id.dvr_playback_controls_fragment);
+ mOverlayFragment =
+ (DvrPlaybackOverlayFragment)
+ getFragmentManager().findFragmentById(R.id.dvr_playback_controls_fragment);
}
@Override
@@ -68,7 +66,8 @@ public class DvrPlaybackActivity extends Activity implements OnPinCheckedListene
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
float density = getResources().getDisplayMetrics().density;
- mOverlayFragment.onWindowSizeChanged((int) (newConfig.screenWidthDp * density),
+ mOverlayFragment.onWindowSizeChanged(
+ (int) (newConfig.screenWidthDp * density),
(int) (newConfig.screenHeightDp * density));
}
@@ -91,4 +90,4 @@ public class DvrPlaybackActivity extends Activity implements OnPinCheckedListene
void setOnPinCheckListener(OnPinCheckedListener listener) {
mOnPinCheckedListener = listener;
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlaybackCardPresenter.java b/src/com/android/tv/dvr/ui/playback/DvrPlaybackCardPresenter.java
index 8ef0041d..a6352d95 100644
--- a/src/com/android/tv/dvr/ui/playback/DvrPlaybackCardPresenter.java
+++ b/src/com/android/tv/dvr/ui/playback/DvrPlaybackCardPresenter.java
@@ -17,14 +17,11 @@
package com.android.tv.dvr.ui.playback;
import android.content.Context;
-
import com.android.tv.R;
import com.android.tv.dvr.ui.browse.RecordedProgramPresenter;
import com.android.tv.dvr.ui.browse.RecordingCardView;
-/**
- * This class is used to generate Views and bind Objects for related recordings in DVR playback.
- */
+/** This class is used to generate Views and bind Objects for related recordings in DVR playback. */
class DvrPlaybackCardPresenter extends RecordedProgramPresenter {
private final int mRelatedRecordingCardWidth;
private final int mRelatedRecordingCardHeight;
@@ -39,7 +36,12 @@ class DvrPlaybackCardPresenter extends RecordedProgramPresenter {
@Override
public DvrItemViewHolder onCreateDvrItemViewHolder() {
- return new RecordedProgramViewHolder(new RecordingCardView(
- getContext(), mRelatedRecordingCardWidth, mRelatedRecordingCardHeight, true), null);
+ return new RecordedProgramViewHolder(
+ new RecordingCardView(
+ getContext(),
+ mRelatedRecordingCardWidth,
+ mRelatedRecordingCardHeight,
+ true),
+ null);
}
}
diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlaybackControlHelper.java b/src/com/android/tv/dvr/ui/playback/DvrPlaybackControlHelper.java
index 1a6ae187..59c90d11 100644
--- a/src/com/android/tv/dvr/ui/playback/DvrPlaybackControlHelper.java
+++ b/src/com/android/tv/dvr/ui/playback/DvrPlaybackControlHelper.java
@@ -44,8 +44,8 @@ import com.android.tv.util.TimeShiftUtils;
import java.util.ArrayList;
/**
- * A helper class to assist {@link DvrPlaybackOverlayFragment} to manage its controls row and
- * send command to the media controller. It also helps to update playback states displayed in the
+ * A helper class to assist {@link DvrPlaybackOverlayFragment} to manage its controls row and send
+ * command to the media controller. It also helps to update playback states displayed in the
* fragment according to information the media session provides.
*/
class DvrPlaybackControlHelper extends PlaybackControlGlue {
@@ -74,8 +74,9 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue {
mMediaController = activity.getMediaController();
mMediaController.registerCallback(mMediaControllerCallback);
mTransportControls = mMediaController.getTransportControls();
- mExtraPaddingTopForNoDescription = activity.getResources()
- .getDimensionPixelOffset(R.dimen.dvr_playback_controls_extra_padding_top);
+ mExtraPaddingTopForNoDescription =
+ activity.getResources()
+ .getDimensionPixelOffset(R.dimen.dvr_playback_controls_extra_padding_top);
mClosedCaptioningAction = new ClosedCaptioningAction(activity);
mMultiAudioAction = new MultiAudioAction(activity);
createControlsRowPresenter();
@@ -90,42 +91,47 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue {
private void createControlsRowPresenter() {
AbstractDetailsDescriptionPresenter detailsPresenter =
new AbstractDetailsDescriptionPresenter() {
- @Override
- protected void onBindDescription(
- AbstractDetailsDescriptionPresenter.ViewHolder viewHolder, Object object) {
- PlaybackControlGlue glue = (PlaybackControlGlue) object;
- if (glue.hasValidMedia()) {
- viewHolder.getTitle().setText(glue.getMediaTitle());
- viewHolder.getSubtitle().setText(glue.getMediaSubtitle());
- } else {
- viewHolder.getTitle().setText("");
- viewHolder.getSubtitle().setText("");
- }
- if (TextUtils.isEmpty(viewHolder.getSubtitle().getText())) {
- viewHolder.view.setPadding(viewHolder.view.getPaddingLeft(),
- mExtraPaddingTopForNoDescription,
- viewHolder.view.getPaddingRight(), viewHolder.view.getPaddingBottom());
- }
- }
- };
+ @Override
+ protected void onBindDescription(
+ AbstractDetailsDescriptionPresenter.ViewHolder viewHolder,
+ Object object) {
+ PlaybackControlGlue glue = (PlaybackControlGlue) object;
+ if (glue.hasValidMedia()) {
+ viewHolder.getTitle().setText(glue.getMediaTitle());
+ viewHolder.getSubtitle().setText(glue.getMediaSubtitle());
+ } else {
+ viewHolder.getTitle().setText("");
+ viewHolder.getSubtitle().setText("");
+ }
+ if (TextUtils.isEmpty(viewHolder.getSubtitle().getText())) {
+ viewHolder.view.setPadding(
+ viewHolder.view.getPaddingLeft(),
+ mExtraPaddingTopForNoDescription,
+ viewHolder.view.getPaddingRight(),
+ viewHolder.view.getPaddingBottom());
+ }
+ }
+ };
PlaybackControlsRowPresenter presenter =
new PlaybackControlsRowPresenter(detailsPresenter) {
- @Override
- protected void onBindRowViewHolder(RowPresenter.ViewHolder vh, Object item) {
- super.onBindRowViewHolder(vh, item);
- vh.setOnKeyListener(DvrPlaybackControlHelper.this);
- }
-
- @Override
- protected void onUnbindRowViewHolder(RowPresenter.ViewHolder vh) {
- super.onUnbindRowViewHolder(vh);
- vh.setOnKeyListener(null);
- }
- };
- presenter.setProgressColor(getContext().getResources()
- .getColor(R.color.play_controls_progress_bar_watched));
- presenter.setBackgroundColor(getContext().getResources()
- .getColor(R.color.play_controls_body_background_enabled));
+ @Override
+ protected void onBindRowViewHolder(RowPresenter.ViewHolder vh, Object item) {
+ super.onBindRowViewHolder(vh, item);
+ vh.setOnKeyListener(DvrPlaybackControlHelper.this);
+ }
+
+ @Override
+ protected void onUnbindRowViewHolder(RowPresenter.ViewHolder vh) {
+ super.onUnbindRowViewHolder(vh);
+ vh.setOnKeyListener(null);
+ }
+ };
+ presenter.setProgressColor(
+ getContext().getResources().getColor(R.color.play_controls_progress_bar_watched));
+ presenter.setBackgroundColor(
+ getContext()
+ .getResources()
+ .getColor(R.color.play_controls_body_background_enabled));
setControlsRowPresenter(presenter);
}
@@ -166,37 +172,40 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue {
return false;
}
int state = playbackState.getState();
- return state != PlaybackState.STATE_NONE && state != PlaybackState.STATE_CONNECTING
+ return state != PlaybackState.STATE_NONE
+ && state != PlaybackState.STATE_CONNECTING
&& state != PlaybackState.STATE_PAUSED;
}
- /**
- * Returns the ID of the media under playback.
- */
+ /** Returns the ID of the media under playback. */
public String getMediaId() {
MediaMetadata mediaMetadata = mMediaController.getMetadata();
- return mediaMetadata == null ? null
+ return mediaMetadata == null
+ ? null
: mediaMetadata.getString(MediaMetadata.METADATA_KEY_MEDIA_ID);
}
@Override
public CharSequence getMediaTitle() {
MediaMetadata mediaMetadata = mMediaController.getMetadata();
- return mediaMetadata == null ? ""
+ return mediaMetadata == null
+ ? ""
: mediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
}
@Override
public CharSequence getMediaSubtitle() {
MediaMetadata mediaMetadata = mMediaController.getMetadata();
- return mediaMetadata == null ? ""
+ return mediaMetadata == null
+ ? ""
: mediaMetadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE);
}
@Override
public int getMediaDuration() {
MediaMetadata mediaMetadata = mMediaController.getMetadata();
- return mediaMetadata == null ? 0
+ return mediaMetadata == null
+ ? 0
: (int) mediaMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
}
@@ -225,19 +234,18 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue {
return (int) playbackState.getPosition();
}
- /**
- * Unregister media controller's callback.
- */
+ /** Unregister media controller's callback. */
void unregisterCallback() {
mMediaController.unregisterCallback(mMediaControllerCallback);
}
/**
* Update the secondary controls row.
- * @param hasClosedCaption {@code true} to show the closed caption selection button,
- * {@code false} to hide it.
- * @param hasMultiAudio {@code true} to show the audio track selection button,
- * {@code false} to hide it.
+ *
+ * @param hasClosedCaption {@code true} to show the closed caption selection button, {@code
+ * false} to hide it.
+ * @param hasMultiAudio {@code true} to show the audio track selection button, {@code false} to
+ * hide it.
*/
void updateSecondaryRow(boolean hasClosedCaption, boolean hasMultiAudio) {
if (hasClosedCaption) {
@@ -274,7 +282,7 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue {
mTransportControls.play();
} else if (speedId <= -PLAYBACK_SPEED_FAST_L0) {
mTransportControls.rewind();
- } else if (speedId >= PLAYBACK_SPEED_FAST_L0){
+ } else if (speedId >= PLAYBACK_SPEED_FAST_L0) {
mTransportControls.fastForward();
}
}
@@ -284,12 +292,10 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue {
mTransportControls.pause();
}
- /**
- * Notifies closed caption being enabled/disabled to update related UI.
- */
+ /** Notifies closed caption being enabled/disabled to update related UI. */
void onSubtitleTrackStateChanged(boolean enabled) {
- mClosedCaptioningAction.setIndex(enabled ?
- ClosedCaptioningAction.ON : ClosedCaptioningAction.OFF);
+ mClosedCaptioningAction.setIndex(
+ enabled ? ClosedCaptioningAction.ON : ClosedCaptioningAction.OFF);
}
private void onStateChanged(int state, long positionMs, int speedLevel) {
@@ -344,7 +350,9 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue {
args.putString(DvrPlaybackSideFragment.SELECTED_TRACK_ID, selectedTrackId);
DvrPlaybackSideFragment sideFragment = new DvrPlaybackSideFragment();
sideFragment.setArguments(args);
- mFragment.getFragmentManager().beginTransaction()
+ mFragment
+ .getFragmentManager()
+ .beginTransaction()
.hide(mFragment)
.replace(R.id.dvr_playback_side_fragment, sideFragment)
.addToBackStack(null)
@@ -367,7 +375,7 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue {
private static class MultiAudioAction extends MultiAction {
MultiAudioAction(Context context) {
super(AUDIO_ACTION_ID);
- setDrawables(new Drawable[]{context.getDrawable(R.drawable.ic_tvoption_multi_track)});
+ setDrawables(new Drawable[] {context.getDrawable(R.drawable.ic_tvoption_multi_track)});
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlaybackMediaSessionHelper.java b/src/com/android/tv/dvr/ui/playback/DvrPlaybackMediaSessionHelper.java
index 843d2dbe..bef036eb 100644
--- a/src/com/android/tv/dvr/ui/playback/DvrPlaybackMediaSessionHelper.java
+++ b/src/com/android/tv/dvr/ui/playback/DvrPlaybackMediaSessionHelper.java
@@ -28,17 +28,15 @@ import android.media.tv.TvContract;
import android.os.AsyncTask;
import android.support.annotation.Nullable;
import android.text.TextUtils;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
-import com.android.tv.common.SoftPreconditions;
-import com.android.tv.data.Channel;
+import com.android.tv.TvSingletons;
import com.android.tv.data.ChannelDataManager;
+import com.android.tv.data.api.Channel;
import com.android.tv.dvr.DvrWatchedPositionManager;
import com.android.tv.dvr.data.RecordedProgram;
-import com.android.tv.util.ImageLoader;
import com.android.tv.util.TimeShiftUtils;
import com.android.tv.util.Utils;
+import com.android.tv.util.images.ImageLoader;
class DvrPlaybackMediaSessionHelper {
private static final String TAG = "DvrPlaybackMediaSessionHelper";
@@ -55,49 +53,52 @@ class DvrPlaybackMediaSessionHelper {
private final DvrWatchedPositionManager mDvrWatchedPositionManager;
private final ChannelDataManager mChannelDataManager;
- public DvrPlaybackMediaSessionHelper(Activity activity, String mediaSessionTag,
- DvrPlayer dvrPlayer, DvrPlaybackOverlayFragment overlayFragment) {
+ public DvrPlaybackMediaSessionHelper(
+ Activity activity,
+ String mediaSessionTag,
+ DvrPlayer dvrPlayer,
+ DvrPlaybackOverlayFragment overlayFragment) {
mActivity = activity;
mDvrPlayer = dvrPlayer;
mDvrWatchedPositionManager =
- TvApplication.getSingletons(activity).getDvrWatchedPositionManager();
- mChannelDataManager = TvApplication.getSingletons(activity).getChannelDataManager();
- mDvrPlayer.setCallback(new DvrPlayer.DvrPlayerCallback() {
- @Override
- public void onPlaybackStateChanged(int playbackState, int playbackSpeed) {
- updateMediaSessionPlaybackState();
- }
+ TvSingletons.getSingletons(activity).getDvrWatchedPositionManager();
+ mChannelDataManager = TvSingletons.getSingletons(activity).getChannelDataManager();
+ mDvrPlayer.setCallback(
+ new DvrPlayer.DvrPlayerCallback() {
+ @Override
+ public void onPlaybackStateChanged(int playbackState, int playbackSpeed) {
+ updateMediaSessionPlaybackState();
+ }
- @Override
- public void onPlaybackPositionChanged(long positionMs) {
- updateMediaSessionPlaybackState();
- if (mDvrPlayer.isPlaybackPrepared()) {
- mDvrWatchedPositionManager
- .setWatchedPosition(mDvrPlayer.getProgram().getId(), positionMs);
- }
- }
+ @Override
+ public void onPlaybackPositionChanged(long positionMs) {
+ updateMediaSessionPlaybackState();
+ if (mDvrPlayer.isPlaybackPrepared()) {
+ mDvrWatchedPositionManager.setWatchedPosition(
+ mDvrPlayer.getProgram().getId(), positionMs);
+ }
+ }
- @Override
- public void onPlaybackEnded() {
- // TODO: Deal with watched over recordings in DVR library
- RecordedProgram nextEpisode =
- overlayFragment.getNextEpisode(mDvrPlayer.getProgram());
- if (nextEpisode == null) {
- mDvrPlayer.reset();
- mActivity.finish();
- } else {
- Intent intent = new Intent(activity, DvrPlaybackActivity.class);
- intent.putExtra(Utils.EXTRA_KEY_RECORDED_PROGRAM_ID, nextEpisode.getId());
- mActivity.startActivity(intent);
- }
- }
- });
+ @Override
+ public void onPlaybackEnded() {
+ // TODO: Deal with watched over recordings in DVR library
+ RecordedProgram nextEpisode =
+ overlayFragment.getNextEpisode(mDvrPlayer.getProgram());
+ if (nextEpisode == null) {
+ mDvrPlayer.reset();
+ mActivity.finish();
+ } else {
+ Intent intent = new Intent(activity, DvrPlaybackActivity.class);
+ intent.putExtra(
+ Utils.EXTRA_KEY_RECORDED_PROGRAM_ID, nextEpisode.getId());
+ mActivity.startActivity(intent);
+ }
+ }
+ });
initializeMediaSession(mediaSessionTag);
}
- /**
- * Stops DVR player and release media session.
- */
+ /** Stops DVR player and release media session. */
public void release() {
if (mDvrPlayer != null) {
mDvrPlayer.reset();
@@ -108,13 +109,15 @@ class DvrPlaybackMediaSessionHelper {
}
}
- /**
- * Updates media session's playback state and speed.
- */
+ /** Updates media session's playback state and speed. */
public void updateMediaSessionPlaybackState() {
- mMediaSession.setPlaybackState(new PlaybackState.Builder()
- .setState(mDvrPlayer.getPlaybackState(), mDvrPlayer.getPlaybackPosition(),
- mSpeedLevel).build());
+ mMediaSession.setPlaybackState(
+ new PlaybackState.Builder()
+ .setState(
+ mDvrPlayer.getPlaybackState(),
+ mDvrPlayer.getPlaybackPosition(),
+ mSpeedLevel)
+ .build());
}
/**
@@ -132,42 +135,35 @@ class DvrPlaybackMediaSessionHelper {
}
}
- /**
- * Returns the recorded program now playing.
- */
+ /** Returns the recorded program now playing. */
public RecordedProgram getProgram() {
return mDvrPlayer.getProgram();
}
- /**
- * Checks if the recorded program is the same as now playing one.
- */
+ /** Checks if the recorded program is the same as now playing one. */
public boolean isCurrentProgram(RecordedProgram program) {
return program != null && program.equals(getProgram());
}
- /**
- * Returns playback state.
- */
+ /** Returns playback state. */
public int getPlaybackState() {
return mDvrPlayer.getPlaybackState();
}
- /**
- * Returns the underlying DVR player.
- */
+ /** Returns the underlying DVR player. */
public DvrPlayer getDvrPlayer() {
return mDvrPlayer;
}
private void initializeMediaSession(String mediaSessionTag) {
mMediaSession = new MediaSession(mActivity, mediaSessionTag);
- mMediaSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS
- | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
- mNowPlayingCardWidth = mActivity.getResources()
- .getDimensionPixelSize(R.dimen.notif_card_img_max_width);
- mNowPlayingCardHeight = mActivity.getResources()
- .getDimensionPixelSize(R.dimen.notif_card_img_height);
+ mMediaSession.setFlags(
+ MediaSession.FLAG_HANDLES_MEDIA_BUTTONS
+ | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
+ mNowPlayingCardWidth =
+ mActivity.getResources().getDimensionPixelSize(R.dimen.notif_card_img_max_width);
+ mNowPlayingCardHeight =
+ mActivity.getResources().getDimensionPixelSize(R.dimen.notif_card_img_height);
mMediaSession.setCallback(new MediaSessionCallback());
mActivity.setMediaController(
new MediaController(mActivity, mMediaSession.getSessionToken()));
@@ -179,11 +175,17 @@ class DvrPlaybackMediaSessionHelper {
String cardTitleText = program.getTitle();
if (TextUtils.isEmpty(cardTitleText)) {
Channel channel = mChannelDataManager.getChannel(program.getChannelId());
- cardTitleText = (channel != null) ? channel.getDisplayName()
- : mActivity.getString(R.string.no_program_information);
+ cardTitleText =
+ (channel != null)
+ ? channel.getDisplayName()
+ : mActivity.getString(R.string.no_program_information);
}
- final MediaMetadata currentMetadata = updateMetadataTextInfo(program.getId(), cardTitleText,
- program.getDescription(), mProgramDurationMs);
+ final MediaMetadata currentMetadata =
+ updateMetadataTextInfo(
+ program.getId(),
+ cardTitleText,
+ program.getDescription(),
+ mProgramDurationMs);
String posterArtUri = program.getPosterArtUri();
if (posterArtUri == null) {
posterArtUri = TvContract.buildChannelLogoUri(program.getChannelId()).toString();
@@ -192,12 +194,18 @@ class DvrPlaybackMediaSessionHelper {
mMediaSession.setActive(true);
}
- private void updatePosterArt(RecordedProgram program, MediaMetadata currentMetadata,
- @Nullable Bitmap posterArt, @Nullable String posterArtUri) {
+ private void updatePosterArt(
+ RecordedProgram program,
+ MediaMetadata currentMetadata,
+ @Nullable Bitmap posterArt,
+ @Nullable String posterArtUri) {
if (posterArt != null) {
updateMetadataImageInfo(program, currentMetadata, posterArt, 0);
} else if (posterArtUri != null) {
- ImageLoader.loadBitmap(mActivity, posterArtUri, mNowPlayingCardWidth,
+ ImageLoader.loadBitmap(
+ mActivity,
+ posterArtUri,
+ mNowPlayingCardWidth,
mNowPlayingCardHeight,
new ProgramPosterArtCallback(mActivity, program, currentMetadata));
} else {
@@ -205,13 +213,12 @@ class DvrPlaybackMediaSessionHelper {
}
}
- private class ProgramPosterArtCallback extends
- ImageLoader.ImageLoaderCallback<Activity> {
+ private class ProgramPosterArtCallback extends ImageLoader.ImageLoaderCallback<Activity> {
private final RecordedProgram mRecordedProgram;
private final MediaMetadata mCurrentMetadata;
- public ProgramPosterArtCallback(Activity activity, RecordedProgram program,
- MediaMetadata metadata) {
+ public ProgramPosterArtCallback(
+ Activity activity, RecordedProgram program, MediaMetadata metadata) {
super(activity);
mRecordedProgram = program;
mCurrentMetadata = metadata;
@@ -225,8 +232,8 @@ class DvrPlaybackMediaSessionHelper {
}
}
- private MediaMetadata updateMetadataTextInfo(final long programId, final String title,
- final String subtitle, final long duration) {
+ private MediaMetadata updateMetadataTextInfo(
+ final long programId, final String title, final String subtitle, final long duration) {
MediaMetadata.Builder builder = new MediaMetadata.Builder();
builder.putString(MediaMetadata.METADATA_KEY_MEDIA_ID, Long.toString(programId))
.putString(MediaMetadata.METADATA_KEY_TITLE, title)
@@ -239,8 +246,11 @@ class DvrPlaybackMediaSessionHelper {
return metadata;
}
- private void updateMetadataImageInfo(final RecordedProgram program,
- final MediaMetadata currentMetadata, final Bitmap posterArt, final int imageResId) {
+ private void updateMetadataImageInfo(
+ final RecordedProgram program,
+ final MediaMetadata currentMetadata,
+ final Bitmap posterArt,
+ final int imageResId) {
if (mMediaSession != null && (posterArt != null || imageResId != 0)) {
MediaMetadata.Builder builder = new MediaMetadata.Builder(currentMetadata);
if (posterArt != null) {
@@ -255,7 +265,8 @@ class DvrPlaybackMediaSessionHelper {
@Override
protected void onPostExecute(Bitmap programPosterArt) {
- if (mMediaSession != null && programPosterArt != null
+ if (mMediaSession != null
+ && programPosterArt != null
&& isCurrentProgram(program)) {
builder.putBitmap(MediaMetadata.METADATA_KEY_ART, programPosterArt);
mMediaSession.setMetadata(builder.build());
diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlaybackOverlayFragment.java b/src/com/android/tv/dvr/ui/playback/DvrPlaybackOverlayFragment.java
index 783ae682..d3374cfa 100644
--- a/src/com/android/tv/dvr/ui/playback/DvrPlaybackOverlayFragment.java
+++ b/src/com/android/tv/dvr/ui/playback/DvrPlaybackOverlayFragment.java
@@ -21,12 +21,12 @@ import android.content.Context;
import android.content.Intent;
import android.graphics.Point;
import android.hardware.display.DisplayManager;
-import android.media.tv.TvContentRating;
-import android.media.tv.TvTrackInfo;
-import android.os.Bundle;
import android.media.session.PlaybackState;
+import android.media.tv.TvContentRating;
import android.media.tv.TvInputManager;
+import android.media.tv.TvTrackInfo;
import android.media.tv.TvView;
+import android.os.Bundle;
import android.support.v17.leanback.app.PlaybackFragment;
import android.support.v17.leanback.app.PlaybackFragmentGlueHost;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
@@ -37,14 +37,13 @@ import android.support.v17.leanback.widget.ListRow;
import android.support.v17.leanback.widget.Presenter;
import android.support.v17.leanback.widget.RowPresenter;
import android.support.v17.leanback.widget.SinglePresenterSelector;
+import android.util.Log;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
-import android.util.Log;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.data.BaseProgram;
import com.android.tv.dialog.PinDialogFragment;
import com.android.tv.dvr.DvrDataManager;
@@ -57,9 +56,8 @@ import com.android.tv.parental.ContentRatingsManager;
import com.android.tv.util.TvSettings;
import com.android.tv.util.TvTrackInfoUtils;
import com.android.tv.util.Utils;
-
-import java.util.List;
import java.util.ArrayList;
+import java.util.List;
public class DvrPlaybackOverlayFragment extends PlaybackFragment {
// TODO: Handles audio focus. Deals with block and ratings.
@@ -104,15 +102,25 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment {
public void onCreate(Bundle savedInstanceState) {
if (DEBUG) Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
- mVerticalPaddingBase = getActivity().getResources()
- .getDimensionPixelOffset(R.dimen.dvr_playback_overlay_padding_top_base);
- mPaddingWithoutRelatedRow = getActivity().getResources()
- .getDimensionPixelOffset(R.dimen.dvr_playback_overlay_padding_top_no_related_row);
- mPaddingWithoutSecondaryRow = getActivity().getResources()
- .getDimensionPixelOffset(R.dimen.dvr_playback_overlay_padding_top_no_secondary_row);
- mDvrDataManager = TvApplication.getSingletons(getActivity()).getDvrDataManager();
- mContentRatingsManager = TvApplication.getSingletons(getContext())
- .getTvInputManagerHelper().getContentRatingsManager();
+ mVerticalPaddingBase =
+ getActivity()
+ .getResources()
+ .getDimensionPixelOffset(R.dimen.dvr_playback_overlay_padding_top_base);
+ mPaddingWithoutRelatedRow =
+ getActivity()
+ .getResources()
+ .getDimensionPixelOffset(
+ R.dimen.dvr_playback_overlay_padding_top_no_related_row);
+ mPaddingWithoutSecondaryRow =
+ getActivity()
+ .getResources()
+ .getDimensionPixelOffset(
+ R.dimen.dvr_playback_overlay_padding_top_no_secondary_row);
+ mDvrDataManager = TvSingletons.getSingletons(getActivity()).getDvrDataManager();
+ mContentRatingsManager =
+ TvSingletons.getSingletons(getContext())
+ .getTvInputManagerHelper()
+ .getContentRatingsManager();
if (!mDvrDataManager.isRecordedProgramLoadFinished()) {
mDvrDataManager.addRecordedProgramLoadFinishedListener(
new DvrDataManager.OnRecordedProgramLoadFinishedListener() {
@@ -124,14 +132,14 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment {
preparePlayback(getActivity().getIntent());
}
}
- }
- );
+ });
} else if (!handleIntent(getActivity().getIntent(), true)) {
return;
}
Point size = new Point();
((DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE))
- .getDisplay(Display.DEFAULT_DISPLAY).getSize(size);
+ .getDisplay(Display.DEFAULT_DISPLAY)
+ .getSize(size);
mWindowWidth = size.x;
mWindowHeight = size.y;
mWindowAspectRatio = mAppliedAspectRatio = (float) mWindowWidth / mWindowHeight;
@@ -152,19 +160,20 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment {
mTvView = (TvView) getActivity().findViewById(R.id.dvr_tv_view);
mBlockScreenView = getActivity().findViewById(R.id.block_screen);
mDvrPlayer = new DvrPlayer(mTvView);
- mMediaSessionHelper = new DvrPlaybackMediaSessionHelper(
- getActivity(), MEDIA_SESSION_TAG, mDvrPlayer, this);
+ mMediaSessionHelper =
+ new DvrPlaybackMediaSessionHelper(
+ getActivity(), MEDIA_SESSION_TAG, mDvrPlayer, this);
mPlaybackControlHelper = new DvrPlaybackControlHelper(getActivity(), this);
mRelatedRecordingsRow = getRelatedRecordingsRow();
mDvrPlayer.setOnTracksAvailabilityChangedListener(
new DvrPlayer.OnTracksAvailabilityChangedListener() {
@Override
- public void onTracksAvailabilityChanged(boolean hasClosedCaption,
- boolean hasMultiAudio) {
+ public void onTracksAvailabilityChanged(
+ boolean hasClosedCaption, boolean hasMultiAudio) {
mPlaybackControlHelper.updateSecondaryRow(hasClosedCaption, hasMultiAudio);
if (hasClosedCaption) {
- mDvrPlayer.setOnTrackSelectedListener(TvTrackInfo.TYPE_SUBTITLE,
- mOnSubtitleTrackSelectedListener);
+ mDvrPlayer.setOnTrackSelectedListener(
+ TvTrackInfo.TYPE_SUBTITLE, mOnSubtitleTrackSelectedListener);
selectBestMatchedTrack(TvTrackInfo.TYPE_SUBTITLE);
} else {
mDvrPlayer.setOnTrackSelectedListener(TvTrackInfo.TYPE_SUBTITLE, null);
@@ -175,15 +184,18 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment {
updateVerticalPosition();
mPlaybackControlHelper.getHost().notifyPlaybackRowChanged();
}
- });
- mDvrPlayer.setOnAspectRatioChangedListener(new DvrPlayer.OnAspectRatioChangedListener() {
- @Override
- public void onAspectRatioChanged(float videoAspectRatio) {
- updateAspectRatio(videoAspectRatio);
- }
- });
- mPinChecked = getActivity().getIntent()
- .getBooleanExtra(Utils.EXTRA_KEY_RECORDED_PROGRAM_PIN_CHECKED, false);
+ });
+ mDvrPlayer.setOnAspectRatioChangedListener(
+ new DvrPlayer.OnAspectRatioChangedListener() {
+ @Override
+ public void onAspectRatioChanged(float videoAspectRatio) {
+ updateAspectRatio(videoAspectRatio);
+ }
+ });
+ mPinChecked =
+ getActivity()
+ .getIntent()
+ .getBooleanExtra(Utils.EXTRA_KEY_RECORDED_PROGRAM_PIN_CHECKED, false);
mDvrPlayer.setOnContentBlockedListener(
new DvrPlayer.OnContentBlockedListener() {
@Override
@@ -221,20 +233,25 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment {
PinDialogFragment.DIALOG_TAG);
}
});
- setOnItemViewClickedListener(new BaseOnItemViewClickedListener() {
- @Override
- public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
- RowPresenter.ViewHolder rowViewHolder, Object row) {
- if (itemViewHolder.view instanceof RecordingCardView) {
- setFadingEnabled(false);
- long programId = ((RecordedProgram) itemViewHolder.view.getTag()).getId();
- if (DEBUG) Log.d(TAG, "Play Related Recording:" + programId);
- Intent intent = new Intent(getContext(), DvrPlaybackActivity.class);
- intent.putExtra(Utils.EXTRA_KEY_RECORDED_PROGRAM_ID, programId);
- getContext().startActivity(intent);
- }
- }
- });
+ setOnItemViewClickedListener(
+ new BaseOnItemViewClickedListener() {
+ @Override
+ public void onItemClicked(
+ Presenter.ViewHolder itemViewHolder,
+ Object item,
+ RowPresenter.ViewHolder rowViewHolder,
+ Object row) {
+ if (itemViewHolder.view instanceof RecordingCardView) {
+ setFadingEnabled(false);
+ long programId =
+ ((RecordedProgram) itemViewHolder.view.getTag()).getId();
+ if (DEBUG) Log.d(TAG, "Play Related Recording:" + programId);
+ Intent intent = new Intent(getContext(), DvrPlaybackActivity.class);
+ intent.putExtra(Utils.EXTRA_KEY_RECORDED_PROGRAM_ID, programId);
+ getContext().startActivity(intent);
+ }
+ }
+ });
if (mProgram != null) {
setUpRows();
preparePlayback(getActivity().getIntent());
@@ -265,9 +282,7 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment {
super.onDestroy();
}
- /**
- * Passes the intent to the fragment.
- */
+ /** Passes the intent to the fragment. */
public void onNewIntent(Intent intent) {
if (mDvrDataManager.isRecordedProgramLoadFinished() && handleIntent(intent, false)) {
preparePlayback(intent);
@@ -275,8 +290,8 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment {
}
/**
- * Should be called when windows' size is changed in order to notify DVR player
- * to update it's view width/height and position.
+ * Should be called when windows' size is changed in order to notify DVR player to update it's
+ * view width/height and position.
*/
public void onWindowSizeChanged(final int windowWidth, final int windowHeight) {
mWindowWidth = windowWidth;
@@ -285,9 +300,7 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment {
updateAspectRatio(mAppliedAspectRatio);
}
- /**
- * Returns next recorded episode in the same series as now playing program.
- */
+ /** Returns next recorded episode in the same series as now playing program. */
public RecordedProgram getNextEpisode(RecordedProgram program) {
int position = mRelatedRecordingsRowAdapter.findInsertPosition(program);
if (position == mRelatedRecordingsRowAdapter.size()) {
@@ -299,9 +312,9 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment {
/**
* Returns the tracks of the give type of the current playback.
-
- * @param trackType Should be {@link TvTrackInfo#TYPE_SUBTITLE}
- * or {@link TvTrackInfo#TYPE_AUDIO}. Or returns {@code null}.
+ *
+ * @param trackType Should be {@link TvTrackInfo#TYPE_SUBTITLE} or {@link
+ * TvTrackInfo#TYPE_AUDIO}. Or returns {@code null}.
*/
public ArrayList<TvTrackInfo> getTracks(int trackType) {
if (trackType == TvTrackInfo.TYPE_AUDIO) {
@@ -312,18 +325,16 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment {
return null;
}
- /**
- * Returns the ID of the selected track of the given type.
- */
+ /** Returns the ID of the selected track of the given type. */
public String getSelectedTrackId(int trackType) {
return mDvrPlayer.getSelectedTrackId(trackType);
}
/**
* Returns the language setting of the given track type.
-
- * @param trackType Should be {@link TvTrackInfo#TYPE_SUBTITLE}
- * or {@link TvTrackInfo#TYPE_AUDIO}.
+ *
+ * @param trackType Should be {@link TvTrackInfo#TYPE_SUBTITLE} or {@link
+ * TvTrackInfo#TYPE_AUDIO}.
* @return {@code null} if no language has been set for the given track type.
*/
TvTrackInfo getTrackSetting(int trackType) {
@@ -332,10 +343,11 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment {
/**
* Selects the given audio or subtitle track for DVR playback.
- * @param trackType Should be {@link TvTrackInfo#TYPE_SUBTITLE}
- * or {@link TvTrackInfo#TYPE_AUDIO}.
+ *
+ * @param trackType Should be {@link TvTrackInfo#TYPE_SUBTITLE} or {@link
+ * TvTrackInfo#TYPE_AUDIO}.
* @param selectedTrack {@code null} to disable the audio or subtitle track according to
- * trackType.
+ * trackType.
*/
void selectTrack(int trackType, TvTrackInfo selectedTrack) {
if (mDvrPlayer.isPlaybackPrepared()) {
@@ -346,8 +358,11 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment {
private boolean handleIntent(Intent intent, boolean finishActivity) {
mProgram = getProgramFromIntent(intent);
if (mProgram == null) {
- Toast.makeText(getActivity(), getString(R.string.dvr_program_not_found),
- Toast.LENGTH_SHORT).show();
+ Toast.makeText(
+ getActivity(),
+ getString(R.string.dvr_program_not_found),
+ Toast.LENGTH_SHORT)
+ .show();
if (finishActivity) {
getActivity().finish();
}
@@ -359,12 +374,18 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment {
private void selectBestMatchedTrack(int trackType) {
TvTrackInfo selectedTrack = getTrackSetting(trackType);
if (selectedTrack != null) {
- TvTrackInfo bestMatchedTrack = TvTrackInfoUtils.getBestTrackInfo(getTracks(trackType),
- selectedTrack.getId(), selectedTrack.getLanguage(),
- trackType == TvTrackInfo.TYPE_AUDIO ? selectedTrack.getAudioChannelCount() : 0);
- if (bestMatchedTrack != null && (trackType == TvTrackInfo.TYPE_AUDIO || Utils
- .isEqualLanguage(bestMatchedTrack.getLanguage(),
- selectedTrack.getLanguage()))) {
+ TvTrackInfo bestMatchedTrack =
+ TvTrackInfoUtils.getBestTrackInfo(
+ getTracks(trackType),
+ selectedTrack.getId(),
+ selectedTrack.getLanguage(),
+ trackType == TvTrackInfo.TYPE_AUDIO
+ ? selectedTrack.getAudioChannelCount()
+ : 0);
+ if (bestMatchedTrack != null
+ && (trackType == TvTrackInfo.TYPE_AUDIO
+ || Utils.isEqualLanguage(
+ bestMatchedTrack.getLanguage(), selectedTrack.getLanguage()))) {
selectTrack(trackType, bestMatchedTrack);
return;
}
@@ -421,7 +442,7 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment {
}
if (mRelatedRecordingsRowAdapter.size() == 0) {
mRowsAdapter.remove(mRelatedRecordingsRow);
- } else if (wasEmpty){
+ } else if (wasEmpty) {
mRowsAdapter.add(mRelatedRecordingsRow);
}
updateVerticalPosition();
@@ -446,8 +467,9 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment {
private ListRow getRelatedRecordingsRow() {
mRelatedRecordingCardPresenter = new DvrPlaybackCardPresenter(getActivity());
mRelatedRecordingsRowAdapter = new RelatedRecordingsAdapter(mRelatedRecordingCardPresenter);
- HeaderItem header = new HeaderItem(0,
- getActivity().getString(R.string.dvr_playback_related_recordings));
+ HeaderItem header =
+ new HeaderItem(
+ 0, getActivity().getString(R.string.dvr_playback_related_recordings));
return new ListRow(header, mRelatedRecordingsRowAdapter);
}
@@ -457,8 +479,8 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment {
}
private long getSeekTimeFromIntent(Intent intent) {
- return intent.getLongExtra(Utils.EXTRA_KEY_RECORDED_PROGRAM_SEEK_TIME,
- TvInputManager.TIME_SHIFT_INVALID_TIME);
+ return intent.getLongExtra(
+ Utils.EXTRA_KEY_RECORDED_PROGRAM_SEEK_TIME, TvInputManager.TIME_SHIFT_INVALID_TIME);
}
private void updateVerticalPosition() {
@@ -491,4 +513,4 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment {
return item.getId();
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlaybackSideFragment.java b/src/com/android/tv/dvr/ui/playback/DvrPlaybackSideFragment.java
index e49870f1..b4481df8 100644
--- a/src/com/android/tv/dvr/ui/playback/DvrPlaybackSideFragment.java
+++ b/src/com/android/tv/dvr/ui/playback/DvrPlaybackSideFragment.java
@@ -26,24 +26,16 @@ import android.transition.Transition;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-
import com.android.tv.R;
import com.android.tv.util.TvSettings;
-
import java.util.List;
import java.util.Locale;
-/**
- * Fragment for DVR playback closed-caption/multi-audio settings.
- */
+/** Fragment for DVR playback closed-caption/multi-audio settings. */
public class DvrPlaybackSideFragment extends GuidedStepFragment {
- /**
- * The tag for passing track infos to side fragments.
- */
+ /** The tag for passing track infos to side fragments. */
public static final String TRACK_INFOS = "dvr_key_track_infos";
- /**
- * The tag for passing selected track's ID to side fragments.
- */
+ /** The tag for passing selected track's ID to side fragments. */
public static final String SELECTED_TRACK_ID = "dvr_key_selected_track_id";
private static final int ACTION_ID_NO_SUBTITLE = -1;
@@ -60,39 +52,42 @@ public class DvrPlaybackSideFragment extends GuidedStepFragment {
mTrackInfos = getArguments().getParcelableArrayList(TRACK_INFOS);
mTrackType = mTrackInfos.get(0).getType();
mSelectedTrackId = getArguments().getString(SELECTED_TRACK_ID);
- mOverlayFragment = ((DvrPlaybackOverlayFragment) getFragmentManager()
- .findFragmentById(R.id.dvr_playback_controls_fragment));
+ mOverlayFragment =
+ ((DvrPlaybackOverlayFragment)
+ getFragmentManager().findFragmentById(R.id.dvr_playback_controls_fragment));
super.onCreate(savedInstanceState);
}
@Override
- public View onCreateBackgroundView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateBackgroundView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View backgroundView = super.onCreateBackgroundView(inflater, container, savedInstanceState);
- backgroundView.setBackgroundColor(getResources()
- .getColor(R.color.lb_playback_controls_background_light));
+ backgroundView.setBackgroundColor(
+ getResources().getColor(R.color.lb_playback_controls_background_light));
return backgroundView;
}
@Override
public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
if (mTrackType == TvTrackInfo.TYPE_SUBTITLE) {
- actions.add(new GuidedAction.Builder(getActivity())
- .id(ACTION_ID_NO_SUBTITLE)
- .title(getString(R.string.closed_caption_option_item_off))
- .checkSetId(CHECK_SET_ID)
- .checked(mSelectedTrackId == null)
- .build());
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(ACTION_ID_NO_SUBTITLE)
+ .title(getString(R.string.closed_caption_option_item_off))
+ .checkSetId(CHECK_SET_ID)
+ .checked(mSelectedTrackId == null)
+ .build());
}
for (int i = 0; i < mTrackInfos.size(); i++) {
TvTrackInfo info = mTrackInfos.get(i);
boolean checked = TextUtils.equals(info.getId(), mSelectedTrackId);
- GuidedAction action = new GuidedAction.Builder(getActivity())
- .id(i)
- .title(getTrackLabel(info, i))
- .checkSetId(CHECK_SET_ID)
- .checked(checked)
- .build();
+ GuidedAction action =
+ new GuidedAction.Builder(getActivity())
+ .id(i)
+ .title(getTrackLabel(info, i))
+ .checkSetId(CHECK_SET_ID)
+ .checked(checked)
+ .build();
actions.add(action);
if (checked) {
mSelectedTrack = info;
@@ -136,8 +131,8 @@ public class DvrPlaybackSideFragment extends GuidedStepFragment {
if (track.getLanguage() != null) {
return new Locale(track.getLanguage()).getDisplayName();
}
- return track.getType() == TvTrackInfo.TYPE_SUBTITLE ?
- getString(R.string.closed_caption_unknown_language, trackIndex + 1)
+ return track.getType() == TvTrackInfo.TYPE_SUBTITLE
+ ? getString(R.string.closed_caption_unknown_language, trackIndex + 1)
: getString(R.string.multi_audio_unknown_language);
}
@@ -151,4 +146,4 @@ public class DvrPlaybackSideFragment extends GuidedStepFragment {
t.excludeTarget(R.id.guidedstep_background, true);
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlayer.java b/src/com/android/tv/dvr/ui/playback/DvrPlayer.java
index 7226c666..85bb31b2 100644
--- a/src/com/android/tv/dvr/ui/playback/DvrPlayer.java
+++ b/src/com/android/tv/dvr/ui/playback/DvrPlayer.java
@@ -24,9 +24,7 @@ import android.media.tv.TvTrackInfo;
import android.media.tv.TvView;
import android.text.TextUtils;
import android.util.Log;
-
import com.android.tv.dvr.data.RecordedProgram;
-
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -35,17 +33,13 @@ class DvrPlayer {
private static final String TAG = "DvrPlayer";
private static final boolean DEBUG = false;
- /**
- * The max rewinding speed supported by DVR player.
- */
+ /** The max rewinding speed supported by DVR player. */
public static final int MAX_REWIND_SPEED = 256;
- /**
- * The max fast-forwarding speed supported by DVR player.
- */
+ /** The max fast-forwarding speed supported by DVR player. */
public static final int MAX_FAST_FORWARD_SPEED = 256;
private static final long SEEK_POSITION_MARGIN_MS = TimeUnit.SECONDS.toMillis(2);
- private static final long REWIND_POSITION_MARGIN_MS = 32; // Workaround value. b/29994826
+ private static final long REWIND_POSITION_MARGIN_MS = 32; // Workaround value. b/29994826
private RecordedProgram mProgram;
private long mInitialSeekPositionMs;
@@ -71,49 +65,39 @@ class DvrPlayer {
public static class DvrPlayerCallback {
/**
- * Called when the playback position is changed. The normal updating frequency is
- * around 1 sec., which is restricted to the implementation of
- * {@link android.media.tv.TvInputService}.
+ * Called when the playback position is changed. The normal updating frequency is around 1
+ * sec., which is restricted to the implementation of {@link
+ * android.media.tv.TvInputService}.
*/
- public void onPlaybackPositionChanged(long positionMs) { }
- /**
- * Called when the playback state or the playback speed is changed.
- */
- public void onPlaybackStateChanged(int playbackState, int playbackSpeed) { }
- /**
- * Called when the playback toward the end.
- */
- public void onPlaybackEnded() { }
+ public void onPlaybackPositionChanged(long positionMs) {}
+ /** Called when the playback state or the playback speed is changed. */
+ public void onPlaybackStateChanged(int playbackState, int playbackSpeed) {}
+ /** Called when the playback toward the end. */
+ public void onPlaybackEnded() {}
}
public interface OnAspectRatioChangedListener {
/**
* Called when the Video's aspect ratio is changed.
*
- * @param videoAspectRatio The aspect ratio of video. 0 stands for unknown ratios.
- * Listeners should handle it carefully.
+ * @param videoAspectRatio The aspect ratio of video. 0 stands for unknown ratios. Listeners
+ * should handle it carefully.
*/
void onAspectRatioChanged(float videoAspectRatio);
}
public interface OnContentBlockedListener {
- /**
- * Called when the Video's aspect ratio is changed.
- */
+ /** Called when the Video's aspect ratio is changed. */
void onContentBlocked(TvContentRating rating);
}
public interface OnTracksAvailabilityChangedListener {
- /**
- * Called when the Video's subtitle or audio tracks are changed.
- */
+ /** Called when the Video's subtitle or audio tracks are changed. */
void onTracksAvailabilityChanged(boolean hasClosedCaption, boolean hasMultiAudio);
}
public interface OnTrackSelectedListener {
- /**
- * Called when certain subtitle or audio track is selected.
- */
+ /** Called when certain subtitle or audio track is selected. */
void onTrackSelected(String selectedTrackId);
}
@@ -143,9 +127,7 @@ class DvrPlayer {
mCallback.onPlaybackStateChanged(mPlaybackState, 1);
}
- /**
- * Resumes playback.
- */
+ /** Resumes playback. */
public void play() throws IllegalStateException {
if (DEBUG) Log.d(TAG, "play()");
if (!isPlaybackPrepared()) {
@@ -163,9 +145,7 @@ class DvrPlayer {
mCallback.onPlaybackStateChanged(mPlaybackState, 1);
}
- /**
- * Pauses playback.
- */
+ /** Pauses playback. */
public void pause() throws IllegalStateException {
if (DEBUG) Log.d(TAG, "pause()");
if (!isPlaybackPrepared()) {
@@ -187,8 +167,8 @@ class DvrPlayer {
}
/**
- * Fast-forwards playback with the given speed. If the given speed is larger than
- * {@value #MAX_FAST_FORWARD_SPEED}, uses {@value #MAX_FAST_FORWARD_SPEED}.
+ * Fast-forwards playback with the given speed. If the given speed is larger than {@value
+ * #MAX_FAST_FORWARD_SPEED}, uses {@value #MAX_FAST_FORWARD_SPEED}.
*/
public void fastForward(int speed) throws IllegalStateException {
if (DEBUG) Log.d(TAG, "fastForward()");
@@ -209,8 +189,8 @@ class DvrPlayer {
}
/**
- * Rewinds playback with the given speed. If the given speed is larger than
- * {@value #MAX_REWIND_SPEED}, uses {@value #MAX_REWIND_SPEED}.
+ * Rewinds playback with the given speed. If the given speed is larger than {@value
+ * #MAX_REWIND_SPEED}, uses {@value #MAX_REWIND_SPEED}.
*/
public void rewind(int speed) throws IllegalStateException {
if (DEBUG) Log.d(TAG, "rewind()");
@@ -230,9 +210,7 @@ class DvrPlayer {
mCallback.onPlaybackStateChanged(mPlaybackState, speed);
}
- /**
- * Seeks playback to the specified position.
- */
+ /** Seeks playback to the specified position. */
public void seekTo(long positionMs) throws IllegalStateException {
if (DEBUG) Log.d(TAG, "seekTo()");
if (!isPlaybackPrepared()) {
@@ -244,17 +222,15 @@ class DvrPlayer {
positionMs = getRealSeekPosition(positionMs, SEEK_POSITION_MARGIN_MS);
if (DEBUG) Log.d(TAG, "Now: " + getPlaybackPosition() + ", shift to: " + positionMs);
mTvView.timeShiftSeekTo(positionMs + mStartPositionMs);
- if (mPlaybackState == PlaybackState.STATE_FAST_FORWARDING ||
- mPlaybackState == PlaybackState.STATE_REWINDING) {
+ if (mPlaybackState == PlaybackState.STATE_FAST_FORWARDING
+ || mPlaybackState == PlaybackState.STATE_REWINDING) {
mPlaybackState = PlaybackState.STATE_PLAYING;
mTvView.timeShiftResume();
mCallback.onPlaybackStateChanged(mPlaybackState, 1);
}
}
- /**
- * Resets playback.
- */
+ /** Resets playback. */
public void reset() {
if (DEBUG) Log.d(TAG, "reset()");
mCallback.onPlaybackStateChanged(PlaybackState.STATE_NONE, 1);
@@ -269,9 +245,7 @@ class DvrPlayer {
mSelectedSubtitleTrackId = null;
}
- /**
- * Sets callbacks for playback.
- */
+ /** Sets callbacks for playback. */
public void setCallback(DvrPlayerCallback callback) {
if (callback != null) {
mCallback = callback;
@@ -280,23 +254,17 @@ class DvrPlayer {
}
}
- /**
- * Sets the listener to aspect ratio changing.
- */
+ /** Sets the listener to aspect ratio changing. */
public void setOnAspectRatioChangedListener(OnAspectRatioChangedListener listener) {
mOnAspectRatioChangedListener = listener;
}
- /**
- * Sets the listener to content blocking.
- */
+ /** Sets the listener to content blocking. */
public void setOnContentBlockedListener(OnContentBlockedListener listener) {
mOnContentBlockedListener = listener;
}
- /**
- * Sets the listener to tracks changing.
- */
+ /** Sets the listener to tracks changing. */
public void setOnTracksAvailabilityChangedListener(
OnTracksAvailabilityChangedListener listener) {
mOnTracksAvailabilityChangedListener = listener;
@@ -305,8 +273,8 @@ class DvrPlayer {
/**
* Sets the listener to tracks of the given type being selected.
*
- * @param trackType should be either {@link TvTrackInfo#TYPE_AUDIO}
- * or {@link TvTrackInfo#TYPE_SUBTITLE}.
+ * @param trackType should be either {@link TvTrackInfo#TYPE_AUDIO} or {@link
+ * TvTrackInfo#TYPE_SUBTITLE}.
*/
public void setOnTrackSelectedListener(int trackType, OnTrackSelectedListener listener) {
if (trackType == TvTrackInfo.TYPE_AUDIO) {
@@ -316,9 +284,7 @@ class DvrPlayer {
}
}
- /**
- * Gets the listener to tracks of the given type being selected.
- */
+ /** Gets the listener to tracks of the given type being selected. */
public OnTrackSelectedListener getOnTrackSelectedListener(int trackType) {
if (trackType == TvTrackInfo.TYPE_AUDIO) {
return mOnAudioTrackSelectedListener;
@@ -328,9 +294,7 @@ class DvrPlayer {
return null;
}
- /**
- * Sets recorded programs for playback. If the player is playing another program, stops it.
- */
+ /** Sets recorded programs for playback. If the player is playing another program, stops it. */
public void setProgram(RecordedProgram program, long initialSeekPositionMs) {
if (mProgram != null && mProgram.equals(program)) {
return;
@@ -342,51 +306,37 @@ class DvrPlayer {
mProgram = program;
}
- /**
- * Returns the recorded program now playing.
- */
+ /** Returns the recorded program now playing. */
public RecordedProgram getProgram() {
return mProgram;
}
- /**
- * Returns the currrent playback posistion in msecs.
- */
+ /** Returns the currrent playback posistion in msecs. */
public long getPlaybackPosition() {
return mTimeShiftCurrentPositionMs;
}
- /**
- * Returns the playback speed currently used.
- */
+ /** Returns the playback speed currently used. */
public int getPlaybackSpeed() {
return (int) mPlaybackParams.getSpeed();
}
- /**
- * Returns the playback state defined in {@link android.media.session.PlaybackState}.
- */
+ /** Returns the playback state defined in {@link android.media.session.PlaybackState}. */
public int getPlaybackState() {
return mPlaybackState;
}
- /**
- * Returns the subtitle tracks of the current playback.
- */
+ /** Returns the subtitle tracks of the current playback. */
public ArrayList<TvTrackInfo> getSubtitleTracks() {
return new ArrayList<>(mTvView.getTracks(TvTrackInfo.TYPE_SUBTITLE));
}
- /**
- * Returns the audio tracks of the current playback.
- */
+ /** Returns the audio tracks of the current playback. */
public ArrayList<TvTrackInfo> getAudioTracks() {
return new ArrayList<>(mTvView.getTracks(TvTrackInfo.TYPE_AUDIO));
}
- /**
- * Returns the ID of the selected track of the given type.
- */
+ /** Returns the ID of the selected track of the given type. */
public String getSelectedTrackId(int trackType) {
if (trackType == TvTrackInfo.TYPE_AUDIO) {
return mSelectedAudioTrackId;
@@ -396,9 +346,7 @@ class DvrPlayer {
return null;
}
- /**
- * Returns if playback of the recorded program is started.
- */
+ /** Returns if playback of the recorded program is started. */
public boolean isPlaybackPrepared() {
return mPlaybackState != PlaybackState.STATE_NONE
&& mPlaybackState != PlaybackState.STATE_CONNECTING;
@@ -449,125 +397,138 @@ class DvrPlayer {
}
private void setTvViewCallbacks() {
- mTvView.setTimeShiftPositionCallback(new TvView.TimeShiftPositionCallback() {
- @Override
- public void onTimeShiftStartPositionChanged(String inputId, long timeMs) {
- if (DEBUG) Log.d(TAG, "onTimeShiftStartPositionChanged:" + timeMs);
- mStartPositionMs = timeMs;
- if (mTimeShiftPlayAvailable) {
- resumeToWatchedPositionIfNeeded();
- }
- }
+ mTvView.setTimeShiftPositionCallback(
+ new TvView.TimeShiftPositionCallback() {
+ @Override
+ public void onTimeShiftStartPositionChanged(String inputId, long timeMs) {
+ if (DEBUG) Log.d(TAG, "onTimeShiftStartPositionChanged:" + timeMs);
+ mStartPositionMs = timeMs;
+ if (mTimeShiftPlayAvailable) {
+ resumeToWatchedPositionIfNeeded();
+ }
+ }
- @Override
- public void onTimeShiftCurrentPositionChanged(String inputId, long timeMs) {
- if (DEBUG) Log.d(TAG, "onTimeShiftCurrentPositionChanged: " + timeMs);
- if (!mTimeShiftPlayAvailable) {
- // Workaround of b/31436263
- return;
- }
- // Workaround of b/32211561, TIF won't report start position when TIS report
- // its start position as 0. In that case, we have to do the prework of playback
- // on the first time we get current position, and the start position should be 0
- // at that time.
- if (mStartPositionMs == TvInputManager.TIME_SHIFT_INVALID_TIME) {
- mStartPositionMs = 0;
- resumeToWatchedPositionIfNeeded();
- }
- timeMs -= mStartPositionMs;
- if (mPlaybackState == PlaybackState.STATE_REWINDING
- && timeMs <= REWIND_POSITION_MARGIN_MS) {
- play();
- } else {
- mTimeShiftCurrentPositionMs = getRealSeekPosition(timeMs, 0);
- mCallback.onPlaybackPositionChanged(mTimeShiftCurrentPositionMs);
- if (timeMs >= mProgram.getDurationMillis()) {
- pause();
- mCallback.onPlaybackEnded();
+ @Override
+ public void onTimeShiftCurrentPositionChanged(String inputId, long timeMs) {
+ if (DEBUG) Log.d(TAG, "onTimeShiftCurrentPositionChanged: " + timeMs);
+ if (!mTimeShiftPlayAvailable) {
+ // Workaround of b/31436263
+ return;
+ }
+ // Workaround of b/32211561, TIF won't report start position when TIS report
+ // its start position as 0. In that case, we have to do the prework of
+ // playback
+ // on the first time we get current position, and the start position should
+ // be 0
+ // at that time.
+ if (mStartPositionMs == TvInputManager.TIME_SHIFT_INVALID_TIME) {
+ mStartPositionMs = 0;
+ resumeToWatchedPositionIfNeeded();
+ }
+ timeMs -= mStartPositionMs;
+ if (mPlaybackState == PlaybackState.STATE_REWINDING
+ && timeMs <= REWIND_POSITION_MARGIN_MS) {
+ play();
+ } else {
+ mTimeShiftCurrentPositionMs = getRealSeekPosition(timeMs, 0);
+ mCallback.onPlaybackPositionChanged(mTimeShiftCurrentPositionMs);
+ if (timeMs >= mProgram.getDurationMillis()) {
+ pause();
+ mCallback.onPlaybackEnded();
+ }
+ }
}
- }
- }
- });
- mTvView.setCallback(new TvView.TvInputCallback() {
- @Override
- public void onTimeShiftStatusChanged(String inputId, int status) {
- if (DEBUG) Log.d(TAG, "onTimeShiftStatusChanged:" + status);
- if (status == TvInputManager.TIME_SHIFT_STATUS_AVAILABLE
- && mPlaybackState == PlaybackState.STATE_CONNECTING) {
- mTimeShiftPlayAvailable = true;
- if (mStartPositionMs != TvInputManager.TIME_SHIFT_INVALID_TIME) {
- // onTimeShiftStatusChanged is sometimes called after
- // onTimeShiftStartPositionChanged is called. In this case,
- // resumeToWatchedPositionIfNeeded needs to be called here.
- resumeToWatchedPositionIfNeeded();
+ });
+ mTvView.setCallback(
+ new TvView.TvInputCallback() {
+ @Override
+ public void onTimeShiftStatusChanged(String inputId, int status) {
+ if (DEBUG) Log.d(TAG, "onTimeShiftStatusChanged:" + status);
+ if (status == TvInputManager.TIME_SHIFT_STATUS_AVAILABLE
+ && mPlaybackState == PlaybackState.STATE_CONNECTING) {
+ mTimeShiftPlayAvailable = true;
+ if (mStartPositionMs != TvInputManager.TIME_SHIFT_INVALID_TIME) {
+ // onTimeShiftStatusChanged is sometimes called after
+ // onTimeShiftStartPositionChanged is called. In this case,
+ // resumeToWatchedPositionIfNeeded needs to be called here.
+ resumeToWatchedPositionIfNeeded();
+ }
+ }
}
- }
- }
-
- @Override
- public void onTracksChanged(String inputId, List<TvTrackInfo> tracks) {
- boolean hasClosedCaption =
- !mTvView.getTracks(TvTrackInfo.TYPE_SUBTITLE).isEmpty();
- boolean hasMultiAudio = mTvView.getTracks(TvTrackInfo.TYPE_AUDIO).size() > 1;
- if ((hasClosedCaption != mHasClosedCaption || hasMultiAudio != mHasMultiAudio)
- && mOnTracksAvailabilityChangedListener != null) {
- mOnTracksAvailabilityChangedListener
- .onTracksAvailabilityChanged(hasClosedCaption, hasMultiAudio);
- }
- mHasClosedCaption = hasClosedCaption;
- mHasMultiAudio = hasMultiAudio;
- }
- @Override
- public void onTrackSelected(String inputId, int type, String trackId) {
- if (type == TvTrackInfo.TYPE_AUDIO || type == TvTrackInfo.TYPE_SUBTITLE) {
- setSelectedTrackId(type, trackId);
- OnTrackSelectedListener listener = getOnTrackSelectedListener(type);
- if (listener != null) {
- listener.onTrackSelected(trackId);
+ @Override
+ public void onTracksChanged(String inputId, List<TvTrackInfo> tracks) {
+ boolean hasClosedCaption =
+ !mTvView.getTracks(TvTrackInfo.TYPE_SUBTITLE).isEmpty();
+ boolean hasMultiAudio =
+ mTvView.getTracks(TvTrackInfo.TYPE_AUDIO).size() > 1;
+ if ((hasClosedCaption != mHasClosedCaption
+ || hasMultiAudio != mHasMultiAudio)
+ && mOnTracksAvailabilityChangedListener != null) {
+ mOnTracksAvailabilityChangedListener.onTracksAvailabilityChanged(
+ hasClosedCaption, hasMultiAudio);
+ }
+ mHasClosedCaption = hasClosedCaption;
+ mHasMultiAudio = hasMultiAudio;
}
- } else if (type == TvTrackInfo.TYPE_VIDEO && trackId != null
- && mOnAspectRatioChangedListener != null) {
- List<TvTrackInfo> trackInfos = mTvView.getTracks(TvTrackInfo.TYPE_VIDEO);
- if (trackInfos != null) {
- for (TvTrackInfo trackInfo : trackInfos) {
- if (trackInfo.getId().equals(trackId)) {
- float videoAspectRatio;
- int videoWidth = trackInfo.getVideoWidth();
- int videoHeight = trackInfo.getVideoHeight();
- if (videoWidth > 0 && videoHeight > 0) {
- videoAspectRatio = trackInfo.getVideoPixelAspectRatio()
- * trackInfo.getVideoWidth() / trackInfo.getVideoHeight();
- } else {
- // Aspect ratio is unknown. Pass the message to listeners.
- videoAspectRatio = 0;
- }
- if (DEBUG) Log.d(TAG, "Aspect Ratio: " + videoAspectRatio);
- if (mAspectRatio != videoAspectRatio || videoAspectRatio == 0) {
- mOnAspectRatioChangedListener
- .onAspectRatioChanged(videoAspectRatio);
- mAspectRatio = videoAspectRatio;
- return;
+
+ @Override
+ public void onTrackSelected(String inputId, int type, String trackId) {
+ if (type == TvTrackInfo.TYPE_AUDIO || type == TvTrackInfo.TYPE_SUBTITLE) {
+ setSelectedTrackId(type, trackId);
+ OnTrackSelectedListener listener = getOnTrackSelectedListener(type);
+ if (listener != null) {
+ listener.onTrackSelected(trackId);
+ }
+ } else if (type == TvTrackInfo.TYPE_VIDEO
+ && trackId != null
+ && mOnAspectRatioChangedListener != null) {
+ List<TvTrackInfo> trackInfos =
+ mTvView.getTracks(TvTrackInfo.TYPE_VIDEO);
+ if (trackInfos != null) {
+ for (TvTrackInfo trackInfo : trackInfos) {
+ if (trackInfo.getId().equals(trackId)) {
+ float videoAspectRatio;
+ int videoWidth = trackInfo.getVideoWidth();
+ int videoHeight = trackInfo.getVideoHeight();
+ if (videoWidth > 0 && videoHeight > 0) {
+ videoAspectRatio =
+ trackInfo.getVideoPixelAspectRatio()
+ * trackInfo.getVideoWidth()
+ / trackInfo.getVideoHeight();
+ } else {
+ // Aspect ratio is unknown. Pass the message to
+ // listeners.
+ videoAspectRatio = 0;
+ }
+ if (DEBUG) Log.d(TAG, "Aspect Ratio: " + videoAspectRatio);
+ if (mAspectRatio != videoAspectRatio
+ || videoAspectRatio == 0) {
+ mOnAspectRatioChangedListener.onAspectRatioChanged(
+ videoAspectRatio);
+ mAspectRatio = videoAspectRatio;
+ return;
+ }
+ }
}
}
}
}
- }
- }
- @Override
- public void onContentBlocked(String inputId, TvContentRating rating) {
- if (mOnContentBlockedListener != null) {
- mOnContentBlockedListener.onContentBlocked(rating);
- }
- }
- });
+ @Override
+ public void onContentBlocked(String inputId, TvContentRating rating) {
+ if (mOnContentBlockedListener != null) {
+ mOnContentBlockedListener.onContentBlocked(rating);
+ }
+ }
+ });
}
private void resumeToWatchedPositionIfNeeded() {
if (mInitialSeekPositionMs != TvInputManager.TIME_SHIFT_INVALID_TIME) {
- mTvView.timeShiftSeekTo(getRealSeekPosition(mInitialSeekPositionMs,
- SEEK_POSITION_MARGIN_MS) + mStartPositionMs);
+ mTvView.timeShiftSeekTo(
+ getRealSeekPosition(mInitialSeekPositionMs, SEEK_POSITION_MARGIN_MS)
+ + mStartPositionMs);
mInitialSeekPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME;
}
if (mPauseOnPrepared) {
@@ -580,4 +541,4 @@ class DvrPlayer {
}
mCallback.onPlaybackStateChanged(mPlaybackState, 1);
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/guide/GenreListAdapter.java b/src/com/android/tv/guide/GenreListAdapter.java
index ce19eb2d..b4baf421 100644
--- a/src/com/android/tv/guide/GenreListAdapter.java
+++ b/src/com/android/tv/guide/GenreListAdapter.java
@@ -24,15 +24,11 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
-
import com.android.tv.R;
import com.android.tv.data.GenreItems;
-
import java.util.List;
-/**
- * Adapts the genre items obtained from {@link GenreItems} to the program guide side panel.
- */
+/** Adapts the genre items obtained from {@link GenreItems} to the program guide side panel. */
class GenreListAdapter extends RecyclerView.Adapter<GenreListAdapter.GenreRowHolder> {
private static final String TAG = "GenreListAdapter";
private static final boolean DEBUG = false;
@@ -45,13 +41,14 @@ class GenreListAdapter extends RecyclerView.Adapter<GenreListAdapter.GenreRowHol
GenreListAdapter(Context context, ProgramManager programManager, ProgramGuide guide) {
mContext = context;
mProgramManager = programManager;
- mProgramManager.addListener(new ProgramManager.ListenerAdapter() {
- @Override
- public void onGenresUpdated() {
- mGenreLabels = GenreItems.getLabels(mContext);
- notifyDataSetChanged();
- }
- });
+ mProgramManager.addListener(
+ new ProgramManager.ListenerAdapter() {
+ @Override
+ public void onGenresUpdated() {
+ mGenreLabels = GenreItems.getLabels(mContext);
+ notifyDataSetChanged();
+ }
+ });
mProgramGuide = guide;
}
@@ -80,23 +77,24 @@ class GenreListAdapter extends RecyclerView.Adapter<GenreListAdapter.GenreRowHol
@Override
public GenreRowHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
- itemView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View view) {
- // Animation is not meaningful now, skip it.
- view.getStateListAnimator().jumpToCurrentState();
- }
-
- @Override
- public void onViewDetachedFromWindow(View view) {
- // Do nothing
- }
- });
+ itemView.addOnAttachStateChangeListener(
+ new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View view) {
+ // Animation is not meaningful now, skip it.
+ view.getStateListAnimator().jumpToCurrentState();
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View view) {
+ // Do nothing
+ }
+ });
return new GenreRowHolder(itemView, mProgramGuide);
}
- static class GenreRowHolder extends RecyclerView.ViewHolder implements
- View.OnFocusChangeListener {
+ static class GenreRowHolder extends RecyclerView.ViewHolder
+ implements View.OnFocusChangeListener {
private final ProgramGuide mProgramGuide;
private int mGenreId;
@@ -119,8 +117,13 @@ class GenreListAdapter extends RecyclerView.Adapter<GenreListAdapter.GenreRowHol
public void onFocusChange(View view, boolean hasFocus) {
if (hasFocus) {
if (DEBUG) {
- Log.d(TAG, "onFocusChanged " + ((TextView) view).getText()
- + "(" + mGenreId + ") hasFocus");
+ Log.d(
+ TAG,
+ "onFocusChanged "
+ + ((TextView) view).getText()
+ + "("
+ + mGenreId
+ + ") hasFocus");
}
mProgramGuide.requestGenreChange(mGenreId);
}
diff --git a/src/com/android/tv/guide/GuideUtils.java b/src/com/android/tv/guide/GuideUtils.java
index 403d00b5..51c14fd4 100644
--- a/src/com/android/tv/guide/GuideUtils.java
+++ b/src/com/android/tv/guide/GuideUtils.java
@@ -17,11 +17,9 @@
package com.android.tv.guide;
import android.graphics.Rect;
-import android.support.annotation.NonNull;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
-
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
@@ -30,8 +28,8 @@ class GuideUtils {
private static int sWidthPerHour = 0;
/**
- * Sets the width in pixels that corresponds to an hour in program guide.
- * Assume that this is called from main thread only, so, no synchronization.
+ * Sets the width in pixels that corresponds to an hour in program guide. Assume that this is
+ * called from main thread only, so, no synchronization.
*/
static void setWidthPerHour(int widthPerHour) {
sWidthPerHour = widthPerHour;
@@ -44,30 +42,29 @@ class GuideUtils {
return (int) (millis * sWidthPerHour / TimeUnit.HOURS.toMillis(1));
}
- /**
- * Gets the number of pixels in program guide table that corresponds to the given range.
- */
+ /** Gets the number of pixels in program guide table that corresponds to the given range. */
static int convertMillisToPixel(long startMillis, long endMillis) {
// Convert to pixels first to avoid accumulation of rounding errors.
return GuideUtils.convertMillisToPixel(endMillis)
- GuideUtils.convertMillisToPixel(startMillis);
}
- /**
- * Gets the time in millis that corresponds to the given pixels in the program guide.
- */
+ /** Gets the time in millis that corresponds to the given pixels in the program guide. */
static long convertPixelToMillis(int pixel) {
return pixel * TimeUnit.HOURS.toMillis(1) / sWidthPerHour;
}
/**
* Return the view should be focused in the given program row according to the focus range.
-
+ *
* @param keepCurrentProgramFocused If {@code true}, focuses on the current program if possible,
- * else falls back the general logic.
+ * else falls back the general logic.
*/
- static View findNextFocusedProgram(View programRow, int focusRangeLeft,
- int focusRangeRight, boolean keepCurrentProgramFocused) {
+ static View findNextFocusedProgram(
+ View programRow,
+ int focusRangeLeft,
+ int focusRangeRight,
+ boolean keepCurrentProgramFocused) {
ArrayList<View> focusables = new ArrayList<>();
findFocusables(programRow, focusables);
@@ -102,9 +99,10 @@ class GuideUtils {
maxFullyOverlappedWidth = width;
}
} else if (maxFullyOverlappedWidth == Integer.MIN_VALUE) {
- int overlappedWidth = (focusRangeLeft <= focusableRect.left) ?
- focusRangeRight - focusableRect.left
- : focusableRect.right - focusRangeLeft;
+ int overlappedWidth =
+ (focusRangeLeft <= focusableRect.left)
+ ? focusRangeRight - focusableRect.left
+ : focusableRect.right - focusRangeLeft;
if (overlappedWidth > maxPartiallyOverlappedWidth) {
nextFocusIndex = i;
maxPartiallyOverlappedWidth = overlappedWidth;
@@ -118,16 +116,14 @@ class GuideUtils {
}
/**
- * Returns {@code true} if the program displayed in the give
- * {@link com.android.tv.guide.ProgramItemView} is a current program.
+ * Returns {@code true} if the program displayed in the give {@link
+ * com.android.tv.guide.ProgramItemView} is a current program.
*/
static boolean isCurrentProgram(ProgramItemView view) {
return view.getTableEntry().isCurrentProgram();
}
- /**
- * Returns {@code true} if the given view is a descendant of the give container.
- */
+ /** Returns {@code true} if the given view is a descendant of the give container. */
static boolean isDescendant(ViewGroup container, View view) {
if (view == null) {
return false;
@@ -152,5 +148,5 @@ class GuideUtils {
}
}
- private GuideUtils() { }
+ private GuideUtils() {}
}
diff --git a/src/com/android/tv/guide/ProgramGrid.java b/src/com/android/tv/guide/ProgramGrid.java
index 58436425..caafb045 100644
--- a/src/com/android/tv/guide/ProgramGrid.java
+++ b/src/com/android/tv/guide/ProgramGrid.java
@@ -25,15 +25,11 @@ import android.util.Log;
import android.util.Range;
import android.view.View;
import android.view.ViewTreeObserver;
-
import com.android.tv.R;
import com.android.tv.ui.OnRepeatedKeyInterceptListener;
-
import java.util.concurrent.TimeUnit;
-/**
- * A {@link VerticalGridView} for the program table view.
- */
+/** A {@link VerticalGridView} for the program table view. */
public class ProgramGrid extends VerticalGridView {
private static final String TAG = "ProgramGrid";
@@ -84,7 +80,7 @@ public class ProgramGrid extends VerticalGridView {
private final int mRowHeight;
private final int mDetailHeight;
- private final int mSelectionRow; // Row that is focused
+ private final int mSelectionRow; // Row that is focused
private View mLastFocusedView;
private final Rect mTempRect = new Rect();
@@ -97,8 +93,8 @@ public class ProgramGrid extends VerticalGridView {
interface ChildFocusListener {
/**
- * Is called before focus is moved. Only children to {@code ProgramGrid} will be passed.
- * See {@code ProgramGrid#setChildFocusListener(ChildFocusListener)}.
+ * Is called before focus is moved. Only children to {@code ProgramGrid} will be passed. See
+ * {@code ProgramGrid#setChildFocusListener(ChildFocusListener)}.
*/
void onRequestChildFocus(View oldFocus, View newFocus);
}
@@ -207,16 +203,13 @@ public class ProgramGrid extends VerticalGridView {
}
/**
- * Initializes ProgramGrid. It should be called before the view is actually attached to
- * Window.
+ * Initializes ProgramGrid. It should be called before the view is actually attached to Window.
*/
void initialize(ProgramManager programManager) {
mProgramManager = programManager;
}
- /**
- * Registers a listener focus events occurring on children to the {@code ProgramGrid}.
- */
+ /** Registers a listener focus events occurring on children to the {@code ProgramGrid}. */
void setChildFocusListener(ChildFocusListener childFocusListener) {
mChildFocusListener = childFocusListener;
}
@@ -226,8 +219,8 @@ public class ProgramGrid extends VerticalGridView {
}
/**
- * Resets focus states. If the logic to keep the last focus needs to be cleared, it should
- * be called.
+ * Resets focus states. If the logic to keep the last focus needs to be cleared, it should be
+ * called.
*/
void resetFocusState() {
mLastFocusedView = null;
@@ -255,8 +248,8 @@ public class ProgramGrid extends VerticalGridView {
Log.w(TAG, "No child view has focus");
return null;
}
- int nextChildIndex = direction == View.FOCUS_UP ? focusedChildIndex - 1
- : focusedChildIndex + 1;
+ int nextChildIndex =
+ direction == View.FOCUS_UP ? focusedChildIndex - 1 : focusedChildIndex + 1;
if (nextChildIndex < 0 || nextChildIndex >= getChildCount()) {
// Wraparound if reached head or end
if (getSelectedPosition() == 0) {
@@ -268,8 +261,12 @@ public class ProgramGrid extends VerticalGridView {
}
return focused;
}
- View nextFocusedProgram = GuideUtils.findNextFocusedProgram(getChildAt(nextChildIndex),
- mFocusRangeLeft, mFocusRangeRight, mKeepCurrentProgramFocused);
+ View nextFocusedProgram =
+ GuideUtils.findNextFocusedProgram(
+ getChildAt(nextChildIndex),
+ mFocusRangeLeft,
+ mFocusRangeRight,
+ mKeepCurrentProgramFocused);
if (nextFocusedProgram != null) {
nextFocusedProgram.getGlobalVisibleRect(mTempRect);
mNextFocusByUpDown = nextFocusedProgram;
@@ -320,8 +317,9 @@ public class ProgramGrid extends VerticalGridView {
mFocusRangeRight = getRightMostFocusablePosition();
mNextFocusByUpDown = null;
// If focus is not a program item, drop focus to the current program when back to the grid
- mKeepCurrentProgramFocused = !(focus instanceof ProgramItemView)
- || GuideUtils.isCurrentProgram((ProgramItemView) focus);
+ mKeepCurrentProgramFocused =
+ !(focus instanceof ProgramItemView)
+ || GuideUtils.isCurrentProgram((ProgramItemView) focus);
}
private int getRightMostFocusablePosition() {
diff --git a/src/com/android/tv/guide/ProgramGuide.java b/src/com/android/tv/guide/ProgramGuide.java
index dd5444e2..5b53f904 100644
--- a/src/com/android/tv/guide/ProgramGuide.java
+++ b/src/com/android/tv/guide/ProgramGuide.java
@@ -43,14 +43,14 @@ import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityManager;
-
+import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
import com.android.tv.ChannelTuner;
-import com.android.tv.Features;
import com.android.tv.MainActivity;
import com.android.tv.R;
-import com.android.tv.util.DurationTimer;
+import com.android.tv.TvFeatures;
import com.android.tv.analytics.Tracker;
import com.android.tv.common.WeakHandler;
+import com.android.tv.common.util.DurationTimer;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.GenreItems;
import com.android.tv.data.ProgramDataManager;
@@ -58,17 +58,16 @@ import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrScheduleManager;
import com.android.tv.ui.HardwareLayerAnimatorListenerAdapter;
import com.android.tv.ui.ViewUtils;
+import com.android.tv.ui.hideable.AutoHideScheduler;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.Utils;
-
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
-/**
- * The program guide.
- */
-public class ProgramGuide implements ProgramGrid.ChildFocusListener {
+/** The program guide. */
+public class ProgramGuide
+ implements ProgramGrid.ChildFocusListener, AccessibilityStateChangeListener {
private static final String TAG = "ProgramGuide";
private static final boolean DEBUG = false;
@@ -83,8 +82,8 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
// We clip out the first program entry in ProgramManager, if it does not have enough width.
// In order to prevent from clipping out the current program, this value need be larger than
// or equal to ProgramManager.FIRST_ENTRY_MIN_DURATION.
- private static final long MIN_DURATION_FROM_START_TIME_TO_CURRENT_TIME
- = ProgramManager.FIRST_ENTRY_MIN_DURATION;
+ private static final long MIN_DURATION_FROM_START_TIME_TO_CURRENT_TIME =
+ ProgramManager.FIRST_ENTRY_MIN_DURATION;
private static final int MSG_PROGRAM_TABLE_FADE_IN_ANIM = 1000;
@@ -103,7 +102,7 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
private final long mViewPortMillis;
private final int mRowHeight;
private final int mDetailHeight;
- private final int mSelectionRow; // Row that is focused
+ private final int mSelectionRow; // Row that is focused
private final int mTableFadeAnimDuration;
private final int mAnimationDuration;
private final int mDetailPadding;
@@ -145,34 +144,44 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
private final Handler mHandler = new ProgramGuideHandler(this);
private boolean mActive;
- private final Runnable mHideRunnable = new Runnable() {
- @Override
- public void run() {
- hide();
- }
- };
+ private final AutoHideScheduler mAutoHideScheduler;
private final long mShowDurationMillis;
private ViewTreeObserver.OnGlobalLayoutListener mOnLayoutListenerForShow;
private final ProgramManagerListener mProgramManagerListener = new ProgramManagerListener();
- private final Runnable mUpdateTimeIndicator = new Runnable() {
- @Override
- public void run() {
- positionCurrentTimeIndicator();
- mHandler.postAtTime(this,
- Utils.ceilTime(SystemClock.uptimeMillis(), TIME_INDICATOR_UPDATE_FREQUENCY));
- }
- };
-
- public ProgramGuide(MainActivity activity, ChannelTuner channelTuner,
- TvInputManagerHelper tvInputManagerHelper, ChannelDataManager channelDataManager,
- ProgramDataManager programDataManager, @Nullable DvrDataManager dvrDataManager,
- @Nullable DvrScheduleManager dvrScheduleManager, Tracker tracker,
- Runnable preShowRunnable, Runnable postHideRunnable) {
+ private final Runnable mUpdateTimeIndicator =
+ new Runnable() {
+ @Override
+ public void run() {
+ positionCurrentTimeIndicator();
+ mHandler.postAtTime(
+ this,
+ Utils.ceilTime(
+ SystemClock.uptimeMillis(), TIME_INDICATOR_UPDATE_FREQUENCY));
+ }
+ };
+
+ @SuppressWarnings("RestrictTo")
+ public ProgramGuide(
+ MainActivity activity,
+ ChannelTuner channelTuner,
+ TvInputManagerHelper tvInputManagerHelper,
+ ChannelDataManager channelDataManager,
+ ProgramDataManager programDataManager,
+ @Nullable DvrDataManager dvrDataManager,
+ @Nullable DvrScheduleManager dvrScheduleManager,
+ Tracker tracker,
+ Runnable preShowRunnable,
+ Runnable postHideRunnable) {
mActivity = activity;
- mProgramManager = new ProgramManager(tvInputManagerHelper, channelDataManager,
- programDataManager, dvrDataManager, dvrScheduleManager);
+ mProgramManager =
+ new ProgramManager(
+ tvInputManagerHelper,
+ channelDataManager,
+ programDataManager,
+ dvrDataManager,
+ dvrScheduleManager);
mChannelTuner = channelTuner;
mTracker = tracker;
mPreShowRunnable = preShowRunnable;
@@ -185,9 +194,11 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
Point displaySize = new Point();
mActivity.getWindowManager().getDefaultDisplay().getSize(displaySize);
- int gridWidth = displaySize.x
- - res.getDimensionPixelOffset(R.dimen.program_guide_table_margin_start)
- - res.getDimensionPixelSize(R.dimen.program_guide_table_header_column_width);
+ int gridWidth =
+ displaySize.x
+ - res.getDimensionPixelOffset(R.dimen.program_guide_table_margin_start)
+ - res.getDimensionPixelSize(
+ R.dimen.program_guide_table_header_column_width);
mViewPortMillis = (gridWidth * HOUR_IN_MILLIS) / mWidthPerHour;
mRowHeight = res.getDimensionPixelSize(R.dimen.program_guide_table_item_row_height);
@@ -201,43 +212,49 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
mDetailPadding = res.getDimensionPixelOffset(R.dimen.program_guide_table_detail_padding);
mContainer = mActivity.findViewById(R.id.program_guide);
- ViewTreeObserver.OnGlobalFocusChangeListener globalFocusChangeListener
- = new GlobalFocusChangeListener();
+ ViewTreeObserver.OnGlobalFocusChangeListener globalFocusChangeListener =
+ new GlobalFocusChangeListener();
mContainer.getViewTreeObserver().addOnGlobalFocusChangeListener(globalFocusChangeListener);
GenreListAdapter genreListAdapter = new GenreListAdapter(mActivity, mProgramManager, this);
mSidePanel = mContainer.findViewById(R.id.program_guide_side_panel);
- mSidePanelGridView = (VerticalGridView) mContainer.findViewById(
- R.id.program_guide_side_panel_grid_view);
- mSidePanelGridView.getRecycledViewPool().setMaxRecycledViews(
- R.layout.program_guide_side_panel_row,
- res.getInteger(R.integer.max_recycled_view_pool_epg_side_panel_row));
+ mSidePanelGridView =
+ (VerticalGridView) mContainer.findViewById(R.id.program_guide_side_panel_grid_view);
+ mSidePanelGridView
+ .getRecycledViewPool()
+ .setMaxRecycledViews(
+ R.layout.program_guide_side_panel_row,
+ res.getInteger(R.integer.max_recycled_view_pool_epg_side_panel_row));
mSidePanelGridView.setAdapter(genreListAdapter);
mSidePanelGridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
- mSidePanelGridView.setWindowAlignmentOffset(mActivity.getResources()
- .getDimensionPixelOffset(R.dimen.program_guide_side_panel_alignment_y));
+ mSidePanelGridView.setWindowAlignmentOffset(
+ mActivity
+ .getResources()
+ .getDimensionPixelOffset(R.dimen.program_guide_side_panel_alignment_y));
mSidePanelGridView.setWindowAlignmentOffsetPercent(
VerticalGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
- if (Features.EPG_SEARCH.isEnabled(mActivity)) {
- mSearchOrb = (SearchOrbView) mContainer.findViewById(
- R.id.program_guide_side_panel_search_orb);
+ if (TvFeatures.EPG_SEARCH.isEnabled(mActivity)) {
+ mSearchOrb =
+ (SearchOrbView)
+ mContainer.findViewById(R.id.program_guide_side_panel_search_orb);
mSearchOrb.setVisibility(View.VISIBLE);
- mSearchOrb.setOnOrbClickedListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- hide();
- mActivity.showProgramGuideSearchFragment();
- }
- });
+ mSearchOrb.setOnOrbClickedListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ hide();
+ mActivity.showProgramGuideSearchFragment();
+ }
+ });
mSidePanelGridView.setOnChildSelectedListener(
new android.support.v17.leanback.widget.OnChildSelectedListener() {
- @Override
- public void onChildSelected(ViewGroup viewGroup, View view, int i, long l) {
- mSearchOrb.animate().alpha(i == 0 ? 1.0f : 0.0f);
- }
- });
+ @Override
+ public void onChildSelected(ViewGroup viewGroup, View view, int i, long l) {
+ mSearchOrb.animate().alpha(i == 0 ? 1.0f : 0.0f);
+ }
+ });
} else {
mSearchOrb = null;
}
@@ -246,134 +263,156 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
mTimelineRow = (TimelineRow) mTable.findViewById(R.id.time_row);
mTimeListAdapter = new TimeListAdapter(res);
- mTimelineRow.getRecycledViewPool().setMaxRecycledViews(
- R.layout.program_guide_table_header_row_item,
- res.getInteger(R.integer.max_recycled_view_pool_epg_header_row_item));
+ mTimelineRow
+ .getRecycledViewPool()
+ .setMaxRecycledViews(
+ R.layout.program_guide_table_header_row_item,
+ res.getInteger(R.integer.max_recycled_view_pool_epg_header_row_item));
mTimelineRow.setAdapter(mTimeListAdapter);
ProgramTableAdapter programTableAdapter = new ProgramTableAdapter(mActivity, this);
- programTableAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
- @Override
- public void onChanged() {
- // It is usually called when Genre is changed.
- // Reset selection of ProgramGrid
- resetRowSelection();
- updateGuidePosition();
- }
- });
+ programTableAdapter.registerAdapterDataObserver(
+ new RecyclerView.AdapterDataObserver() {
+ @Override
+ public void onChanged() {
+ // It is usually called when Genre is changed.
+ // Reset selection of ProgramGrid
+ resetRowSelection();
+ updateGuidePosition();
+ }
+ });
mGrid = (ProgramGrid) mTable.findViewById(R.id.grid);
mGrid.initialize(mProgramManager);
- mGrid.getRecycledViewPool().setMaxRecycledViews(
- R.layout.program_guide_table_row,
- res.getInteger(R.integer.max_recycled_view_pool_epg_table_row));
+ mGrid.getRecycledViewPool()
+ .setMaxRecycledViews(
+ R.layout.program_guide_table_row,
+ res.getInteger(R.integer.max_recycled_view_pool_epg_table_row));
mGrid.setAdapter(programTableAdapter);
mGrid.setChildFocusListener(this);
- mGrid.setOnChildSelectedListener(new OnChildSelectedListener() {
- @Override
- public void onChildSelected(ViewGroup parent, View view, int position, long id) {
- if (mIsDuringResetRowSelection) {
- // Ignore if it's during the first resetRowSelection, because onChildSelected
- // will be called again when rows are bound to the program table. if selectRow
- // is called here, mSelectedRow is set and the second selectRow call doesn't
- // work as intended.
- mIsDuringResetRowSelection = false;
- return;
- }
- selectRow(view);
- }
- });
+ mGrid.setOnChildSelectedListener(
+ new OnChildSelectedListener() {
+ @Override
+ public void onChildSelected(
+ ViewGroup parent, View view, int position, long id) {
+ if (mIsDuringResetRowSelection) {
+ // Ignore if it's during the first resetRowSelection, because
+ // onChildSelected
+ // will be called again when rows are bound to the program table. if
+ // selectRow
+ // is called here, mSelectedRow is set and the second selectRow call
+ // doesn't
+ // work as intended.
+ mIsDuringResetRowSelection = false;
+ return;
+ }
+ selectRow(view);
+ }
+ });
mGrid.setFocusScrollStrategy(ProgramGrid.FOCUS_SCROLL_ALIGNED);
mGrid.setWindowAlignmentOffset(mSelectionRow * mRowHeight);
mGrid.setWindowAlignmentOffsetPercent(ProgramGrid.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
mGrid.setItemAlignmentOffset(0);
mGrid.setItemAlignmentOffsetPercent(ProgramGrid.ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
- RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {
- @Override
- public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
- onHorizontalScrolled(dx);
- }
- };
+ RecyclerView.OnScrollListener onScrollListener =
+ new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ onHorizontalScrolled(dx);
+ }
+ };
mTimelineRow.addOnScrollListener(onScrollListener);
mCurrentTimeIndicator = mTable.findViewById(R.id.current_time_indicator);
- mShowAnimatorFull = createAnimator(
- R.animator.program_guide_side_panel_enter_full,
- 0,
- R.animator.program_guide_table_enter_full);
-
- mShowAnimatorPartial = createAnimator(
- R.animator.program_guide_side_panel_enter_partial,
- 0,
- R.animator.program_guide_table_enter_partial);
- mShowAnimatorPartial.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mSidePanelGridView.setVisibility(View.VISIBLE);
- mSidePanelGridView.setAlpha(1.0f);
- }
- });
-
- mHideAnimatorFull = createAnimator(
- R.animator.program_guide_side_panel_exit,
- 0,
- R.animator.program_guide_table_exit);
- mHideAnimatorFull.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mContainer.setVisibility(View.GONE);
- }
- });
- mHideAnimatorPartial = createAnimator(
- R.animator.program_guide_side_panel_exit,
- 0,
- R.animator.program_guide_table_exit);
- mHideAnimatorPartial.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mContainer.setVisibility(View.GONE);
- }
- });
-
- mPartialToFullAnimator = createAnimator(
- R.animator.program_guide_side_panel_hide,
- R.animator.program_guide_side_panel_grid_fade_out,
- R.animator.program_guide_table_partial_to_full);
- mFullToPartialAnimator = createAnimator(
- R.animator.program_guide_side_panel_reveal,
- R.animator.program_guide_side_panel_grid_fade_in,
- R.animator.program_guide_table_full_to_partial);
-
- mProgramTableFadeOutAnimator = AnimatorInflater.loadAnimator(mActivity,
- R.animator.program_guide_table_fade_out);
+ mShowAnimatorFull =
+ createAnimator(
+ R.animator.program_guide_side_panel_enter_full,
+ 0,
+ R.animator.program_guide_table_enter_full);
+
+ mShowAnimatorPartial =
+ createAnimator(
+ R.animator.program_guide_side_panel_enter_partial,
+ 0,
+ R.animator.program_guide_table_enter_partial);
+ mShowAnimatorPartial.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mSidePanelGridView.setVisibility(View.VISIBLE);
+ mSidePanelGridView.setAlpha(1.0f);
+ }
+ });
+
+ mHideAnimatorFull =
+ createAnimator(
+ R.animator.program_guide_side_panel_exit,
+ 0,
+ R.animator.program_guide_table_exit);
+ mHideAnimatorFull.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mContainer.setVisibility(View.GONE);
+ }
+ });
+ mHideAnimatorPartial =
+ createAnimator(
+ R.animator.program_guide_side_panel_exit,
+ 0,
+ R.animator.program_guide_table_exit);
+ mHideAnimatorPartial.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mContainer.setVisibility(View.GONE);
+ }
+ });
+
+ mPartialToFullAnimator =
+ createAnimator(
+ R.animator.program_guide_side_panel_hide,
+ R.animator.program_guide_side_panel_grid_fade_out,
+ R.animator.program_guide_table_partial_to_full);
+ mFullToPartialAnimator =
+ createAnimator(
+ R.animator.program_guide_side_panel_reveal,
+ R.animator.program_guide_side_panel_grid_fade_in,
+ R.animator.program_guide_table_full_to_partial);
+
+ mProgramTableFadeOutAnimator =
+ AnimatorInflater.loadAnimator(mActivity, R.animator.program_guide_table_fade_out);
mProgramTableFadeOutAnimator.setTarget(mTable);
- mProgramTableFadeOutAnimator.addListener(new HardwareLayerAnimatorListenerAdapter(mTable) {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
-
- if (!isActive()) {
- return;
- }
- mProgramManager.resetChannelListWithGenre(mLastRequestedGenreId);
- resetTimelineScroll();
- if (!mHandler.hasMessages(MSG_PROGRAM_TABLE_FADE_IN_ANIM)) {
- mHandler.sendEmptyMessage(MSG_PROGRAM_TABLE_FADE_IN_ANIM);
- }
- }
- });
- mProgramTableFadeInAnimator = AnimatorInflater.loadAnimator(mActivity,
- R.animator.program_guide_table_fade_in);
+ mProgramTableFadeOutAnimator.addListener(
+ new HardwareLayerAnimatorListenerAdapter(mTable) {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+
+ if (!isActive()) {
+ return;
+ }
+ mProgramManager.resetChannelListWithGenre(mLastRequestedGenreId);
+ resetTimelineScroll();
+ if (!mHandler.hasMessages(MSG_PROGRAM_TABLE_FADE_IN_ANIM)) {
+ mHandler.sendEmptyMessage(MSG_PROGRAM_TABLE_FADE_IN_ANIM);
+ }
+ }
+ });
+ mProgramTableFadeInAnimator =
+ AnimatorInflater.loadAnimator(mActivity, R.animator.program_guide_table_fade_in);
mProgramTableFadeInAnimator.setTarget(mTable);
mProgramTableFadeInAnimator.addListener(new HardwareLayerAnimatorListenerAdapter(mTable));
mSharedPreference = PreferenceManager.getDefaultSharedPreferences(mActivity);
mAccessibilityManager =
(AccessibilityManager) mActivity.getSystemService(Context.ACCESSIBILITY_SERVICE);
- mShowGuidePartial = mAccessibilityManager.isEnabled()
- || mSharedPreference.getBoolean(KEY_SHOW_GUIDE_PARTIAL, true);
+ mShowGuidePartial =
+ mAccessibilityManager.isEnabled()
+ || mSharedPreference.getBoolean(KEY_SHOW_GUIDE_PARTIAL, true);
+ mAutoHideScheduler = new AutoHideScheduler(activity, this::hide);
}
@Override
@@ -397,12 +436,12 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
}
/**
- * Show the program guide. This reveals the side panel, and the program guide table is shown
+ * Show the program guide. This reveals the side panel, and the program guide table is shown
* partially.
*
- * <p>Note: the animation which starts together with ProgramGuide showing animation needs to
- * be initiated in {@code runnableAfterAnimatorReady}. If the animation starts together
- * with show(), the animation may drop some frames.
+ * <p>Note: the animation which starts together with ProgramGuide showing animation needs to be
+ * initiated in {@code runnableAfterAnimatorReady}. If the animation starts together with
+ * show(), the animation may drop some frames.
*/
public void show(final Runnable runnableAfterAnimatorReady) {
if (mContainer.getVisibility() == View.VISIBLE) {
@@ -416,9 +455,10 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
mVisibleDuration.start();
mProgramManager.programGuideVisibilityChanged(true);
- mStartUtcTime = Utils.floorTime(
- System.currentTimeMillis() - MIN_DURATION_FROM_START_TIME_TO_CURRENT_TIME,
- HALF_HOUR_IN_MILLIS);
+ mStartUtcTime =
+ Utils.floorTime(
+ System.currentTimeMillis() - MIN_DURATION_FROM_START_TIME_TO_CURRENT_TIME,
+ HALF_HOUR_IN_MILLIS);
mProgramManager.updateInitialTimeRange(mStartUtcTime, mStartUtcTime + mViewPortMillis);
mProgramManager.addListener(mProgramManagerListener);
mLastRequestedGenreId = GenreItems.ID_ALL_CHANNELS;
@@ -435,51 +475,60 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
if (DEBUG) {
Log.d(TAG, "show()");
}
- mOnLayoutListenerForShow = new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- mContainer.getViewTreeObserver().removeOnGlobalLayoutListener(this);
- mTable.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- mSidePanelGridView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- mTable.buildLayer();
- mSidePanelGridView.buildLayer();
- mOnLayoutListenerForShow = null;
- mTimelineAnimation = true;
- // Make sure that time indicator update starts after animation is finished.
- startCurrentTimeIndicator(TIME_INDICATOR_UPDATE_FREQUENCY);
- if (DEBUG) {
- mContainer.getViewTreeObserver().addOnDrawListener(
- new ViewTreeObserver.OnDrawListener() {
- long time = System.currentTimeMillis();
- int count = 0;
-
- @Override
- public void onDraw() {
- long curtime = System.currentTimeMillis();
- Log.d(TAG, "onDraw " + count++ + " " + (curtime - time) + "ms");
- time = curtime;
- if (count > 10) {
- mContainer.getViewTreeObserver().removeOnDrawListener(this);
- }
- }
- });
- }
- updateGuidePosition();
- runnableAfterAnimatorReady.run();
- if (mShowGuidePartial) {
- mShowAnimatorPartial.start();
- } else {
- mShowAnimatorFull.start();
- }
- }
- };
+ mOnLayoutListenerForShow =
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ mContainer.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ mTable.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mSidePanelGridView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mTable.buildLayer();
+ mSidePanelGridView.buildLayer();
+ mOnLayoutListenerForShow = null;
+ mTimelineAnimation = true;
+ // Make sure that time indicator update starts after animation is finished.
+ startCurrentTimeIndicator(TIME_INDICATOR_UPDATE_FREQUENCY);
+ if (DEBUG) {
+ mContainer
+ .getViewTreeObserver()
+ .addOnDrawListener(
+ new ViewTreeObserver.OnDrawListener() {
+ long time = System.currentTimeMillis();
+ int count = 0;
+
+ @Override
+ public void onDraw() {
+ long curtime = System.currentTimeMillis();
+ Log.d(
+ TAG,
+ "onDraw "
+ + count++
+ + " "
+ + (curtime - time)
+ + "ms");
+ time = curtime;
+ if (count > 10) {
+ mContainer
+ .getViewTreeObserver()
+ .removeOnDrawListener(this);
+ }
+ }
+ });
+ }
+ updateGuidePosition();
+ runnableAfterAnimatorReady.run();
+ if (mShowGuidePartial) {
+ mShowAnimatorPartial.start();
+ } else {
+ mShowAnimatorFull.start();
+ }
+ }
+ };
mContainer.getViewTreeObserver().addOnGlobalLayoutListener(mOnLayoutListenerForShow);
scheduleHide();
}
- /**
- * Hide the program guide.
- */
+ /** Hide the program guide. */
public void hide() {
if (!isActive()) {
return;
@@ -516,52 +565,43 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
}
}
- /**
- * Schedules hiding the program guide.
- */
+ /** Schedules hiding the program guide. */
public void scheduleHide() {
- cancelHide();
- mHandler.postDelayed(mHideRunnable, mShowDurationMillis);
+ mAutoHideScheduler.schedule(mShowDurationMillis);
}
- /**
- * Cancels hiding the program guide.
- */
+ /** Cancels hiding the program guide. */
public void cancelHide() {
- mHandler.removeCallbacks(mHideRunnable);
+ mAutoHideScheduler.cancel();
}
- /**
- * Process the {@code KEYCODE_BACK} key event.
- */
+ /** Process the {@code KEYCODE_BACK} key event. */
public void onBackPressed() {
hide();
}
- /**
- * Returns {@code true} if the program guide should process the input events.
- */
+ /** Returns {@code true} if the program guide should process the input events. */
public boolean isActive() {
return mActive;
}
/**
- * Returns {@code true} if the program guide is shown, i.e. showing animation is done and
- * hiding animation is not started yet.
+ * Returns {@code true} if the program guide is shown, i.e. showing animation is done and hiding
+ * animation is not started yet.
*/
public boolean isRunningAnimation() {
- return mShowAnimatorPartial.isStarted() || mShowAnimatorFull.isStarted()
- || mHideAnimatorPartial.isStarted() || mHideAnimatorFull.isStarted();
+ return mShowAnimatorPartial.isStarted()
+ || mShowAnimatorFull.isStarted()
+ || mHideAnimatorPartial.isStarted()
+ || mHideAnimatorFull.isStarted();
}
- /** Returns if program table is in full screen mode. **/
+ /** Returns if program table is in full screen mode. * */
boolean isFull() {
return !mShowGuidePartial;
}
- /**
- * Requests change genre to {@code genreId}.
- */
+ /** Requests change genre to {@code genreId}. */
void requestGenreChange(int genreId) {
if (mLastRequestedGenreId == genreId) {
// When Recycler.onLayout() removes its children to recycle,
@@ -575,15 +615,15 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
// When requestGenreChange is called repeatedly in short time, we keep the fade-out
// state for mTableFadeAnimDuration from now. Without it, we'll see blinks.
mHandler.removeMessages(MSG_PROGRAM_TABLE_FADE_IN_ANIM);
- mHandler.sendEmptyMessageDelayed(MSG_PROGRAM_TABLE_FADE_IN_ANIM,
- mTableFadeAnimDuration);
+ mHandler.sendEmptyMessageDelayed(
+ MSG_PROGRAM_TABLE_FADE_IN_ANIM, mTableFadeAnimDuration);
return;
}
if (mHandler.hasMessages(MSG_PROGRAM_TABLE_FADE_IN_ANIM)) {
mProgramManager.resetChannelListWithGenre(mLastRequestedGenreId);
mHandler.removeMessages(MSG_PROGRAM_TABLE_FADE_IN_ANIM);
- mHandler.sendEmptyMessageDelayed(MSG_PROGRAM_TABLE_FADE_IN_ANIM,
- mTableFadeAnimDuration);
+ mHandler.sendEmptyMessageDelayed(
+ MSG_PROGRAM_TABLE_FADE_IN_ANIM, mTableFadeAnimDuration);
return;
}
if (mProgramTableFadeInAnimator.isStarted()) {
@@ -593,9 +633,7 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
mProgramTableFadeOutAnimator.start();
}
- /**
- * Returns the scroll offset of the time line row in pixels.
- */
+ /** Returns the scroll offset of the time line row in pixels. */
int getTimelineRowScrollOffset() {
return mTimelineRow.getScrollOffset();
}
@@ -605,9 +643,7 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
return mGrid;
}
- /**
- * Gets {@link VerticalGridView} for "genre select" side panel.
- */
+ /** Gets {@link VerticalGridView} for "genre select" side panel. */
VerticalGridView getSidePanel() {
return mSidePanelGridView;
}
@@ -628,9 +664,12 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
int startPadding = res.getDimensionPixelOffset(R.dimen.program_guide_table_margin_start);
int topPadding = res.getDimensionPixelOffset(R.dimen.program_guide_table_margin_top);
int bottomPadding = res.getDimensionPixelOffset(R.dimen.program_guide_table_margin_bottom);
- int tableHeight = res.getDimensionPixelOffset(R.dimen.program_guide_table_header_row_height)
- + mDetailHeight + mRowHeight * mGrid.getAdapter().getItemCount() + topPadding
- + bottomPadding;
+ int tableHeight =
+ res.getDimensionPixelOffset(R.dimen.program_guide_table_header_row_height)
+ + mDetailHeight
+ + mRowHeight * mGrid.getAdapter().getItemCount()
+ + topPadding
+ + bottomPadding;
if (tableHeight > screenHeight) {
// EPG height is longer that the screen height.
mTable.setPaddingRelative(startPadding, topPadding, 0, 0);
@@ -645,8 +684,8 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
}
}
- private Animator createAnimator(int sidePanelAnimResId, int sidePanelGridAnimResId,
- int tableAnimResId) {
+ private Animator createAnimator(
+ int sidePanelAnimResId, int sidePanelGridAnimResId, int tableAnimResId) {
List<Animator> animatorList = new ArrayList<>();
Animator sidePanelAnimator = AnimatorInflater.loadAnimator(mActivity, sidePanelAnimResId);
@@ -654,8 +693,8 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
animatorList.add(sidePanelAnimator);
if (sidePanelGridAnimResId != 0) {
- Animator sidePanelGridAnimator = AnimatorInflater.loadAnimator(mActivity,
- sidePanelGridAnimResId);
+ Animator sidePanelGridAnimator =
+ AnimatorInflater.loadAnimator(mActivity, sidePanelGridAnimResId);
sidePanelGridAnimator.setTarget(mSidePanelGridView);
sidePanelGridAnimator.addListener(
new HardwareLayerAnimatorListenerAdapter(mSidePanelGridView));
@@ -700,8 +739,9 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
}
private void positionCurrentTimeIndicator() {
- int offset = GuideUtils.convertMillisToPixel(mStartUtcTime, System.currentTimeMillis())
- - mTimelineRow.getScrollOffset();
+ int offset =
+ GuideUtils.convertMillisToPixel(mStartUtcTime, System.currentTimeMillis())
+ - mTimelineRow.getScrollOffset();
if (offset < 0) {
mCurrentTimeIndicator.setVisibility(View.GONE);
} else {
@@ -743,8 +783,7 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
mSelectedRow = null;
mIsDuringResetRowSelection = true;
mGrid.setSelectedPosition(
- Math.max(mProgramManager.getChannelIndex(mChannelTuner.getCurrentChannel()),
- 0));
+ Math.max(mProgramManager.getChannelIndex(mChannelTuner.getCurrentChannel()), 0));
mGrid.resetFocusState();
mGrid.onItemSelectionReset();
mIsDuringResetRowSelection = false;
@@ -767,12 +806,13 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
detailView.setVisibility(View.VISIBLE);
final ProgramRow programRow = (ProgramRow) row.findViewById(R.id.row);
- programRow.post(new Runnable() {
- @Override
- public void run() {
- programRow.focusCurrentProgram();
- }
- });
+ programRow.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ programRow.focusCurrentProgram();
+ }
+ });
} else {
animateRowChange(mSelectedRow, row);
}
@@ -799,38 +839,45 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
if (outDetail != null && outDetail.isShown()) {
final View outDetailContent = outDetail.findViewById(R.id.detail_content_full);
- Animator fadeOutAnimator = ObjectAnimator.ofPropertyValuesHolder(outDetailContent,
- PropertyValuesHolder.ofFloat(View.ALPHA, outDetail.getAlpha(), 0f),
- PropertyValuesHolder.ofFloat(View.TRANSLATION_Y,
- outDetailContent.getTranslationY(), animationPadding));
+ Animator fadeOutAnimator =
+ ObjectAnimator.ofPropertyValuesHolder(
+ outDetailContent,
+ PropertyValuesHolder.ofFloat(View.ALPHA, outDetail.getAlpha(), 0f),
+ PropertyValuesHolder.ofFloat(
+ View.TRANSLATION_Y,
+ outDetailContent.getTranslationY(),
+ animationPadding));
fadeOutAnimator.setStartDelay(0);
fadeOutAnimator.setDuration(mAnimationDuration);
fadeOutAnimator.addListener(new HardwareLayerAnimatorListenerAdapter(outDetailContent));
- Animator collapseAnimator = ViewUtils
- .createHeightAnimator(outDetail, ViewUtils.getLayoutHeight(outDetail), 0);
+ Animator collapseAnimator =
+ ViewUtils.createHeightAnimator(
+ outDetail, ViewUtils.getLayoutHeight(outDetail), 0);
collapseAnimator.setStartDelay(mAnimationDuration);
collapseAnimator.setDuration(mTableFadeAnimDuration);
- collapseAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animator) {
- outDetailContent.setVisibility(View.GONE);
- }
-
- @Override
- public void onAnimationEnd(Animator animator) {
- outDetailContent.setVisibility(View.VISIBLE);
- }
- });
+ collapseAnimator.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animator) {
+ outDetailContent.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ outDetailContent.setVisibility(View.VISIBLE);
+ }
+ });
AnimatorSet outAnimator = new AnimatorSet();
outAnimator.playTogether(fadeOutAnimator, collapseAnimator);
- outAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animator) {
- mDetailOutAnimator = null;
- }
- });
+ outAnimator.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ mDetailOutAnimator = null;
+ }
+ });
mDetailOutAnimator = outAnimator;
outAnimator.start();
}
@@ -842,39 +889,49 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
Animator expandAnimator = ViewUtils.createHeightAnimator(inDetail, 0, mDetailHeight);
expandAnimator.setStartDelay(mAnimationDuration);
expandAnimator.setDuration(mTableFadeAnimDuration);
- expandAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animator) {
- inDetailContent.setVisibility(View.GONE);
- }
-
- @Override
- public void onAnimationEnd(Animator animator) {
- inDetailContent.setVisibility(View.VISIBLE);
- inDetailContent.setAlpha(0);
- }
- });
- Animator fadeInAnimator = ObjectAnimator.ofPropertyValuesHolder(inDetailContent,
- PropertyValuesHolder.ofFloat(View.ALPHA, 0f, 1f),
- PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, -animationPadding, 0f));
+ expandAnimator.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animator) {
+ inDetailContent.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ inDetailContent.setVisibility(View.VISIBLE);
+ inDetailContent.setAlpha(0);
+ }
+ });
+ Animator fadeInAnimator =
+ ObjectAnimator.ofPropertyValuesHolder(
+ inDetailContent,
+ PropertyValuesHolder.ofFloat(View.ALPHA, 0f, 1f),
+ PropertyValuesHolder.ofFloat(
+ View.TRANSLATION_Y, -animationPadding, 0f));
fadeInAnimator.setDuration(mAnimationDuration);
fadeInAnimator.addListener(new HardwareLayerAnimatorListenerAdapter(inDetailContent));
AnimatorSet inAnimator = new AnimatorSet();
inAnimator.playSequentially(expandAnimator, fadeInAnimator);
- inAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animator) {
- mDetailInAnimator = null;
- }
- });
+ inAnimator.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ mDetailInAnimator = null;
+ }
+ });
mDetailInAnimator = inAnimator;
inAnimator.start();
}
}
- private class GlobalFocusChangeListener implements
- ViewTreeObserver.OnGlobalFocusChangeListener {
+ @Override
+ public void onAccessibilityStateChanged(boolean enabled) {
+ mAutoHideScheduler.onAccessibilityStateChanged(enabled);
+ }
+
+ private class GlobalFocusChangeListener
+ implements ViewTreeObserver.OnGlobalFocusChangeListener {
private static final int UNKNOWN = 0;
private static final int SIDE_PANEL = 1;
private static final int PROGRAM_TABLE = 2;
@@ -912,11 +969,16 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
private class ProgramManagerListener extends ProgramManager.ListenerAdapter {
@Override
public void onTimeRangeUpdated() {
- int scrollOffset = (int) (mWidthPerHour * mProgramManager.getShiftedTime()
- / HOUR_IN_MILLIS);
+ int scrollOffset =
+ (int) (mWidthPerHour * mProgramManager.getShiftedTime() / HOUR_IN_MILLIS);
if (DEBUG) {
- Log.d(TAG, "Horizontal scroll to " + scrollOffset + " pixels ("
- + mProgramManager.getShiftedTime() + " millis)");
+ Log.d(
+ TAG,
+ "Horizontal scroll to "
+ + scrollOffset
+ + " pixels ("
+ + mProgramManager.getShiftedTime()
+ + " millis)");
}
mTimelineRow.scrollTo(scrollOffset, mTimelineAnimation);
}
diff --git a/src/com/android/tv/guide/ProgramItemView.java b/src/com/android/tv/guide/ProgramItemView.java
index b23d578c..9f379e43 100644
--- a/src/com/android/tv/guide/ProgramItemView.java
+++ b/src/com/android/tv/guide/ProgramItemView.java
@@ -24,7 +24,6 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.StateListDrawable;
import android.os.Handler;
-import android.os.SystemClock;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
@@ -35,21 +34,21 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
-
-import com.android.tv.ApplicationSingletons;
import com.android.tv.MainActivity;
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.analytics.Tracker;
import com.android.tv.common.feature.CommonFeatures;
-import com.android.tv.data.Channel;
+import com.android.tv.common.util.Clock;
+import com.android.tv.data.ChannelDataManager;
+import com.android.tv.data.Program;
+import com.android.tv.data.api.Channel;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.ui.DvrUiHelper;
import com.android.tv.guide.ProgramManager.TableEntry;
import com.android.tv.util.ToastUtils;
import com.android.tv.util.Utils;
-
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.TimeUnit;
@@ -60,10 +59,10 @@ public class ProgramItemView extends TextView {
private static final int MAX_PROGRESS = 10000; // From android.widget.ProgressBar.MAX_VALUE
// State indicating the focused program is the current program
- private static final int[] STATE_CURRENT_PROGRAM = { R.attr.state_current_program };
+ private static final int[] STATE_CURRENT_PROGRAM = {R.attr.state_current_program};
// Workaround state in order to not use too much texture memory for RippleDrawable
- private static final int[] STATE_TOO_WIDE = { R.attr.state_program_too_wide };
+ private static final int[] STATE_TOO_WIDE = {R.attr.state_program_too_wide};
private static int sVisibleThreshold;
private static int sItemPadding;
@@ -73,8 +72,10 @@ public class ProgramItemView extends TextView {
private static TextAppearanceSpan sEpisodeTitleStyle;
private static TextAppearanceSpan sGrayedOutEpisodeTitleStyle;
+ private final DvrManager mDvrManager;
+ private final Clock mClock;
+ private final ChannelDataManager mChannelDataManager;
private ProgramGuide mProgramGuide;
- private DvrManager mDvrManager;
private TableEntry mTableEntry;
private int mMaxWidthForRipple;
private int mTextWidth;
@@ -84,96 +85,119 @@ public class ProgramItemView extends TextView {
// as a result of the re-layout (see b/21378855).
private boolean mPreventParentRelayout;
- private static final View.OnClickListener ON_CLICKED = new View.OnClickListener() {
- @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();
- final MainActivity tvActivity = (MainActivity) view.getContext();
- final Channel channel = tvActivity.getChannelDataManager().getChannel(entry.channelId);
- if (entry.isCurrentProgram()) {
- view.postDelayed(new Runnable() {
- @Override
- public void run() {
- tvActivity.tuneToChannel(channel);
- tvActivity.hideOverlaysForTune();
+ private static final View.OnClickListener ON_CLICKED =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(final View view) {
+ TableEntry entry = ((ProgramItemView) view).mTableEntry;
+ Clock clock = ((ProgramItemView) view).mClock;
+ if (entry == null) {
+ // do nothing
+ return;
}
- }, entry.getWidth() > ((ProgramItemView) view).mMaxWidthForRipple ? 0
- : view.getResources()
- .getInteger(R.integer.program_guide_ripple_anim_duration));
- } else if (entry.program != null && CommonFeatures.DVR.isEnabled(view.getContext())) {
- DvrManager dvrManager = singletons.getDvrManager();
- if (entry.entryStartUtcMillis > System.currentTimeMillis()
- && dvrManager.isProgramRecordable(entry.program)) {
- if (entry.scheduledRecording == null) {
- DvrUiHelper.checkStorageStatusAndShowErrorMessage(tvActivity,
- channel.getInputId(), new Runnable() {
+ TvSingletons singletons = TvSingletons.getSingletons(view.getContext());
+ Tracker tracker = singletons.getTracker();
+ tracker.sendEpgItemClicked();
+ final MainActivity tvActivity = (MainActivity) view.getContext();
+ final Channel channel =
+ tvActivity.getChannelDataManager().getChannel(entry.channelId);
+ if (entry.isCurrentProgram()) {
+ view.postDelayed(
+ new Runnable() {
@Override
public void run() {
- DvrUiHelper.requestRecordingFutureProgram(tvActivity,
- entry.program, false);
+ tvActivity.tuneToChannel(channel);
+ tvActivity.hideOverlaysForTune();
}
- });
- } else {
- dvrManager.removeScheduledRecording(entry.scheduledRecording);
- String msg = view.getResources().getString(
- R.string.dvr_schedules_deletion_info, entry.program.getTitle());
- ToastUtils.show(view.getContext(), msg, Toast.LENGTH_SHORT);
+ },
+ entry.getWidth() > ((ProgramItemView) view).mMaxWidthForRipple
+ ? 0
+ : view.getResources()
+ .getInteger(
+ R.integer
+ .program_guide_ripple_anim_duration));
+ } else if (entry.program != null
+ && CommonFeatures.DVR.isEnabled(view.getContext())) {
+ DvrManager dvrManager = singletons.getDvrManager();
+ if (entry.entryStartUtcMillis > clock.currentTimeMillis()
+ && dvrManager.isProgramRecordable(entry.program)) {
+ if (entry.scheduledRecording == null) {
+ DvrUiHelper.checkStorageStatusAndShowErrorMessage(
+ tvActivity,
+ channel.getInputId(),
+ new Runnable() {
+ @Override
+ public void run() {
+ DvrUiHelper.requestRecordingFutureProgram(
+ tvActivity, entry.program, false);
+ }
+ });
+ } else {
+ dvrManager.removeScheduledRecording(entry.scheduledRecording);
+ String msg =
+ view.getResources()
+ .getString(
+ R.string.dvr_schedules_deletion_info,
+ entry.program.getTitle());
+ ToastUtils.show(view.getContext(), msg, Toast.LENGTH_SHORT);
+ }
+ } else {
+ ToastUtils.show(
+ view.getContext(),
+ view.getResources()
+ .getString(R.string.dvr_msg_cannot_record_program),
+ Toast.LENGTH_SHORT);
+ }
}
- } else {
- ToastUtils.show(view.getContext(), view.getResources()
- .getString(R.string.dvr_msg_cannot_record_program), Toast.LENGTH_SHORT);
}
- }
- }
- };
+ };
private static final View.OnFocusChangeListener ON_FOCUS_CHANGED =
new View.OnFocusChangeListener() {
- @Override
- public void onFocusChange(View view, boolean hasFocus) {
- if (hasFocus) {
- ((ProgramItemView) view).mUpdateFocus.run();
- } else {
- Handler handler = view.getHandler();
- if (handler != null) {
- handler.removeCallbacks(((ProgramItemView) view).mUpdateFocus);
+ @Override
+ public void onFocusChange(View view, boolean hasFocus) {
+ if (hasFocus) {
+ ((ProgramItemView) view).mUpdateFocus.run();
+ } else {
+ Handler handler = view.getHandler();
+ if (handler != null) {
+ handler.removeCallbacks(((ProgramItemView) view).mUpdateFocus);
+ }
+ }
}
- }
- }
- };
-
- private final Runnable mUpdateFocus = new Runnable() {
- @Override
- public void run() {
- refreshDrawableState();
- TableEntry entry = mTableEntry;
- if (entry == null) {
- //do nothing
- return;
- }
- if (entry.isCurrentProgram()) {
- Drawable background = getBackground();
- if (!mProgramGuide.isActive() || mProgramGuide.isRunningAnimation()) {
- // If program guide is not active or is during showing/hiding,
- // the animation is unnecessary, skip it.
- background.jumpToCurrentState();
+ };
+
+ private final Runnable mUpdateFocus =
+ new Runnable() {
+ @Override
+ public void run() {
+ refreshDrawableState();
+ TableEntry entry = mTableEntry;
+ if (entry == null) {
+ // do nothing
+ return;
+ }
+ if (entry.isCurrentProgram()) {
+ Drawable background = getBackground();
+ if (!mProgramGuide.isActive() || mProgramGuide.isRunningAnimation()) {
+ // If program guide is not active or is during showing/hiding,
+ // the animation is unnecessary, skip it.
+ background.jumpToCurrentState();
+ }
+ int progress =
+ getProgress(
+ mClock, entry.entryStartUtcMillis, entry.entryEndUtcMillis);
+ setProgress(background, R.id.reverse_progress, MAX_PROGRESS - progress);
+ }
+ if (getHandler() != null) {
+ getHandler()
+ .postAtTime(
+ this,
+ Utils.ceilTime(
+ mClock.uptimeMillis(), FOCUS_UPDATE_FREQUENCY));
+ }
}
- int progress = getProgress(entry.entryStartUtcMillis, entry.entryEndUtcMillis);
- setProgress(background, R.id.reverse_progress, MAX_PROGRESS - progress);
- }
- if (getHandler() != null) {
- getHandler().postAtTime(this,
- Utils.ceilTime(SystemClock.uptimeMillis(), FOCUS_UPDATE_FREQUENCY));
- }
- }
- };
+ };
public ProgramItemView(Context context) {
this(context, null);
@@ -187,7 +211,10 @@ public class ProgramItemView extends TextView {
super(context, attrs, defStyle);
setOnClickListener(ON_CLICKED);
setOnFocusChangeListener(ON_FOCUS_CHANGED);
- mDvrManager = TvApplication.getSingletons(getContext()).getDvrManager();
+ TvSingletons singletons = TvSingletons.getSingletons(getContext());
+ mDvrManager = singletons.getDvrManager();
+ mChannelDataManager = singletons.getChannelDataManager();
+ mClock = singletons.getClock();
}
private void initIfNeeded() {
@@ -196,35 +223,46 @@ public class ProgramItemView extends TextView {
}
Resources res = getContext().getResources();
- sVisibleThreshold = res.getDimensionPixelOffset(
- R.dimen.program_guide_table_item_visible_threshold);
+ sVisibleThreshold =
+ res.getDimensionPixelOffset(R.dimen.program_guide_table_item_visible_threshold);
sItemPadding = res.getDimensionPixelOffset(R.dimen.program_guide_table_item_padding);
- sCompoundDrawablePadding = res.getDimensionPixelOffset(
- R.dimen.program_guide_table_item_compound_drawable_padding);
-
- ColorStateList programTitleColor = ColorStateList.valueOf(res.getColor(
- R.color.program_guide_table_item_program_title_text_color, null));
- ColorStateList grayedOutProgramTitleColor = res.getColorStateList(
- R.color.program_guide_table_item_grayed_out_program_text_color, null);
- ColorStateList episodeTitleColor = ColorStateList.valueOf(res.getColor(
- R.color.program_guide_table_item_program_episode_title_text_color, null));
- ColorStateList grayedOutEpisodeTitleColor = ColorStateList.valueOf(res.getColor(
- R.color.program_guide_table_item_grayed_out_program_episode_title_text_color,
- null));
- int programTitleSize = res.getDimensionPixelSize(
- R.dimen.program_guide_table_item_program_title_font_size);
- int episodeTitleSize = res.getDimensionPixelSize(
- R.dimen.program_guide_table_item_program_episode_title_font_size);
-
- sProgramTitleStyle = new TextAppearanceSpan(null, 0, programTitleSize, programTitleColor,
- null);
- sGrayedOutProgramTitleStyle = new TextAppearanceSpan(null, 0, programTitleSize,
- grayedOutProgramTitleColor, null);
- sEpisodeTitleStyle = new TextAppearanceSpan(null, 0, episodeTitleSize, episodeTitleColor,
- null);
- sGrayedOutEpisodeTitleStyle = new TextAppearanceSpan(null, 0, episodeTitleSize,
- grayedOutEpisodeTitleColor, null);
+ sCompoundDrawablePadding =
+ res.getDimensionPixelOffset(
+ R.dimen.program_guide_table_item_compound_drawable_padding);
+
+ ColorStateList programTitleColor =
+ ColorStateList.valueOf(
+ res.getColor(
+ R.color.program_guide_table_item_program_title_text_color, null));
+ ColorStateList grayedOutProgramTitleColor =
+ res.getColorStateList(
+ R.color.program_guide_table_item_grayed_out_program_text_color, null);
+ ColorStateList episodeTitleColor =
+ ColorStateList.valueOf(
+ res.getColor(
+ R.color.program_guide_table_item_program_episode_title_text_color,
+ null));
+ ColorStateList grayedOutEpisodeTitleColor =
+ ColorStateList.valueOf(
+ res.getColor(
+ R.color
+ .program_guide_table_item_grayed_out_program_episode_title_text_color,
+ null));
+ int programTitleSize =
+ res.getDimensionPixelSize(R.dimen.program_guide_table_item_program_title_font_size);
+ int episodeTitleSize =
+ res.getDimensionPixelSize(
+ R.dimen.program_guide_table_item_program_episode_title_font_size);
+
+ sProgramTitleStyle =
+ new TextAppearanceSpan(null, 0, programTitleSize, programTitleColor, null);
+ sGrayedOutProgramTitleStyle =
+ new TextAppearanceSpan(null, 0, programTitleSize, grayedOutProgramTitleColor, null);
+ sEpisodeTitleStyle =
+ new TextAppearanceSpan(null, 0, episodeTitleSize, episodeTitleColor, null);
+ sGrayedOutEpisodeTitleStyle =
+ new TextAppearanceSpan(null, 0, episodeTitleSize, grayedOutEpisodeTitleColor, null);
}
@Override
@@ -236,8 +274,9 @@ public class ProgramItemView extends TextView {
@Override
protected int[] onCreateDrawableState(int extraSpace) {
if (mTableEntry != null) {
- int states[] = super.onCreateDrawableState(extraSpace
- + STATE_CURRENT_PROGRAM.length + STATE_TOO_WIDE.length);
+ int[] states =
+ super.onCreateDrawableState(
+ extraSpace + STATE_CURRENT_PROGRAM.length + STATE_TOO_WIDE.length);
if (mTableEntry.isCurrentProgram()) {
mergeDrawableStates(states, STATE_CURRENT_PROGRAM);
}
@@ -254,86 +293,168 @@ public class ProgramItemView extends TextView {
}
@SuppressLint("SwitchIntDef")
- public void setValues(ProgramGuide programGuide, TableEntry entry, int selectedGenreId,
- long fromUtcMillis, long toUtcMillis, String gapTitle) {
+ public void setValues(
+ ProgramGuide programGuide,
+ TableEntry entry,
+ int selectedGenreId,
+ long fromUtcMillis,
+ long toUtcMillis,
+ String gapTitle) {
mProgramGuide = programGuide;
mTableEntry = entry;
ViewGroup.LayoutParams layoutParams = getLayoutParams();
- layoutParams.width = entry.getWidth();
- setLayoutParams(layoutParams);
+ if (layoutParams != null) {
+ // There is no layoutParams in the tests so we skip this
+ layoutParams.width = entry.getWidth();
+ setLayoutParams(layoutParams);
+ }
+ String title = mTableEntry.program != null ? mTableEntry.program.getTitle() : null;
+ if (mTableEntry.isGap()) {
+ title = gapTitle;
+ }
+ if (TextUtils.isEmpty(title)) {
+ title = getResources().getString(R.string.program_title_for_no_information);
+ }
+ updateText(selectedGenreId, title);
+ updateIcons();
+ updateContentDescription(title);
+ measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ mTextWidth = getMeasuredWidth() - getPaddingStart() - getPaddingEnd();
+ // Maximum width for us to use a ripple
+ mMaxWidthForRipple = GuideUtils.convertMillisToPixel(fromUtcMillis, toUtcMillis);
+ }
- String title = entry.program != null ? entry.program.getTitle() : null;
- String episode = entry.program != null ?
- entry.program.getEpisodeDisplayTitle(getContext()) : null;
+ private boolean isEntryWideEnough() {
+ return mTableEntry != null && mTableEntry.getWidth() >= sVisibleThreshold;
+ }
+
+ private void updateText(int selectedGenreId, String title) {
+ if (!isEntryWideEnough()) {
+ setText(null);
+ return;
+ }
+
+ String episode =
+ mTableEntry.program != null
+ ? mTableEntry.program.getEpisodeDisplayTitle(getContext())
+ : null;
TextAppearanceSpan titleStyle = sGrayedOutProgramTitleStyle;
TextAppearanceSpan episodeStyle = sGrayedOutEpisodeTitleStyle;
+ if (mTableEntry.isGap()) {
- if (entry.getWidth() < sVisibleThreshold) {
- setText(null);
+ episode = null;
+ } else if (mTableEntry.hasGenre(selectedGenreId)) {
+ titleStyle = sProgramTitleStyle;
+ episodeStyle = sEpisodeTitleStyle;
+ }
+ SpannableStringBuilder description = new SpannableStringBuilder();
+ description.append(title);
+ if (!TextUtils.isEmpty(episode)) {
+ description.append('\n');
+
+ // Add a 'zero-width joiner'/ZWJ in order to ensure we have the same line height for
+ // all lines. This is a non-printing character so it will not change the horizontal
+ // spacing however it will affect the line height. As we ensure the ZWJ has the same
+ // text style as the title it will make sure the line height is consistent.
+ description.append('\u200D');
+
+ int middle = description.length();
+ description.append(episode);
+
+ description.setSpan(titleStyle, 0, middle, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ description.setSpan(
+ episodeStyle, middle, description.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} else {
- if (entry.isGap()) {
- title = gapTitle;
- episode = null;
- } else if (entry.hasGenre(selectedGenreId)) {
- titleStyle = sProgramTitleStyle;
- episodeStyle = sEpisodeTitleStyle;
+ description.setSpan(
+ titleStyle, 0, description.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ setText(description);
+ }
+
+ private void updateIcons() {
+ // Sets recording icons if needed.
+ int iconResId = 0;
+ if (isEntryWideEnough() && mTableEntry.scheduledRecording != null) {
+ if (mDvrManager.isConflicting(mTableEntry.scheduledRecording)) {
+ iconResId = R.drawable.ic_warning_white_18dp;
+ } else {
+ switch (mTableEntry.scheduledRecording.getState()) {
+ case ScheduledRecording.STATE_RECORDING_NOT_STARTED:
+ iconResId = R.drawable.ic_scheduled_recording;
+ break;
+ case ScheduledRecording.STATE_RECORDING_IN_PROGRESS:
+ iconResId = R.drawable.ic_recording_program;
+ break;
+ default:
+ // leave the iconResId=0
+ }
}
- if (TextUtils.isEmpty(title)) {
- title = getResources().getString(R.string.program_title_for_no_information);
+ }
+ setCompoundDrawablePadding(iconResId != 0 ? sCompoundDrawablePadding : 0);
+ setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, iconResId, 0);
+ }
+
+ private void updateContentDescription(String title) {
+ // The content description includes extra information that is displayed on the detail view
+ Resources resources = getResources();
+ String description = title;
+ // TODO(b/73282818): only say channel name when the row changes
+ Channel channel = mChannelDataManager.getChannel(mTableEntry.channelId);
+ if (channel != null) {
+ description = channel.getDisplayNumber() + " " + description;
+ }
+ description +=
+ " "
+ + Utils.getDurationString(
+ getContext(),
+ mClock,
+ mTableEntry.entryStartUtcMillis,
+ mTableEntry.entryEndUtcMillis,
+ true);
+ Program program = mTableEntry.program;
+ if (program != null) {
+ String episodeDescription = program.getEpisodeContentDescription(getContext());
+ if (!TextUtils.isEmpty(episodeDescription)) {
+ description += " " + episodeDescription;
}
- SpannableStringBuilder description = new SpannableStringBuilder();
- description.append(title);
- if (!TextUtils.isEmpty(episode)) {
- description.append('\n');
-
- // Add a 'zero-width joiner'/ZWJ in order to ensure we have the same line height for
- // all lines. This is a non-printing character so it will not change the horizontal
- // spacing however it will affect the line height. As we ensure the ZWJ has the same
- // text style as the title it will make sure the line height is consistent.
- description.append('\u200D');
-
- int middle = description.length();
- description.append(episode);
-
- description.setSpan(titleStyle, 0, middle, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- description.setSpan(episodeStyle, middle, description.length(),
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ if (mTableEntry.scheduledRecording != null) {
+ if (mDvrManager.isConflicting(mTableEntry.scheduledRecording)) {
+ description +=
+ " " + resources.getString(R.string.dvr_epg_program_recording_conflict);
} else {
- description.setSpan(titleStyle, 0, description.length(),
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- setText(description);
-
- // Sets recording icons if needed.
- int iconResId = 0;
- if (mTableEntry.scheduledRecording != null) {
- if (mDvrManager.isConflicting(mTableEntry.scheduledRecording)) {
- iconResId = R.drawable.ic_warning_white_18dp;
- } else {
- switch (mTableEntry.scheduledRecording.getState()) {
- case ScheduledRecording.STATE_RECORDING_NOT_STARTED:
- iconResId = R.drawable.ic_scheduled_recording;
- break;
- case ScheduledRecording.STATE_RECORDING_IN_PROGRESS:
- iconResId = R.drawable.ic_recording_program;
- break;
- }
+ switch (mTableEntry.scheduledRecording.getState()) {
+ case ScheduledRecording.STATE_RECORDING_NOT_STARTED:
+ description +=
+ " "
+ + resources.getString(
+ R.string.dvr_epg_program_recording_scheduled);
+ break;
+ case ScheduledRecording.STATE_RECORDING_IN_PROGRESS:
+ description +=
+ " "
+ + resources.getString(
+ R.string.dvr_epg_program_recording_in_progress);
+ break;
+ default:
+ // do nothing
}
}
- setCompoundDrawablePadding(iconResId != 0 ? sCompoundDrawablePadding : 0);
- setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, iconResId, 0);
}
- measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- mTextWidth = getMeasuredWidth() - getPaddingStart() - getPaddingEnd();
- // Maximum width for us to use a ripple
- mMaxWidthForRipple = GuideUtils.convertMillisToPixel(fromUtcMillis, toUtcMillis);
+ if (mTableEntry.isBlocked()) {
+ description += " " + resources.getString(R.string.program_guide_content_locked);
+ } else if (program != null) {
+ String programDescription = program.getDescription();
+ if (!TextUtils.isEmpty(programDescription)) {
+ description += " " + programDescription;
+ }
+ }
+ setContentDescription(description);
}
- /**
- * Update programItemView to handle alignments of text.
- */
+ /** Update programItemView to handle alignments of text. */
public void updateVisibleArea() {
View parentView = ((View) getParent());
if (parentView == null) {
@@ -341,7 +462,7 @@ public class ProgramItemView extends TextView {
}
if (getLayoutDirection() == LAYOUT_DIRECTION_LTR) {
layoutVisibleArea(parentView.getLeft() - getLeft(), getRight() - parentView.getRight());
- } else {
+ } else {
layoutVisibleArea(getRight() - parentView.getRight(), parentView.getLeft() - getLeft());
}
}
@@ -349,16 +470,14 @@ public class ProgramItemView extends TextView {
/**
* Layout title and episode according to visible area.
*
- * Here's the spec.
- * 1. Don't show text if it's shorter than 48dp.
- * 2. Try showing whole text in visible area by placing and wrapping text,
- * but do not wrap text less than 30min.
- * 3. Episode title is visible only if title isn't multi-line.
+ * <p>Here's the spec. 1. Don't show text if it's shorter than 48dp. 2. Try showing whole text
+ * in visible area by placing and wrapping text, but do not wrap text less than 30min. 3.
+ * Episode title is visible only if title isn't multi-line.
*
* @param startOffset Offset of the start position from the enclosing view's start position.
* @param endOffset Offset of the end position from the enclosing view's end position.
*/
- private void layoutVisibleArea(int startOffset, int endOffset) {
+ private void layoutVisibleArea(int startOffset, int endOffset) {
int width = mTableEntry.getWidth();
int startPadding = Math.max(0, startOffset);
int endPadding = Math.max(0, endOffset);
@@ -388,8 +507,8 @@ public class ProgramItemView extends TextView {
mTableEntry = null;
}
- private static int getProgress(long start, long end) {
- long currentTime = System.currentTimeMillis();
+ private static int getProgress(Clock clock, long start, long end) {
+ long currentTime = clock.currentTimeMillis();
if (currentTime <= start) {
return 0;
} else if (currentTime >= end) {
@@ -417,11 +536,15 @@ public class ProgramItemView extends TextView {
private static int getStateCount(StateListDrawable stateListDrawable) {
try {
- Object stateCount = StateListDrawable.class.getDeclaredMethod("getStateCount")
- .invoke(stateListDrawable);
+ Object stateCount =
+ StateListDrawable.class
+ .getDeclaredMethod("getStateCount")
+ .invoke(stateListDrawable);
return (int) stateCount;
- } catch (NoSuchMethodException|IllegalAccessException|IllegalArgumentException
- |InvocationTargetException e) {
+ } catch (NoSuchMethodException
+ | IllegalAccessException
+ | IllegalArgumentException
+ | InvocationTargetException e) {
Log.e(TAG, "Failed to call StateListDrawable.getStateCount()", e);
return 0;
}
@@ -429,12 +552,15 @@ public class ProgramItemView extends TextView {
private static Drawable getStateDrawable(StateListDrawable stateListDrawable, int index) {
try {
- Object drawable = StateListDrawable.class
- .getDeclaredMethod("getStateDrawable", Integer.TYPE)
- .invoke(stateListDrawable, index);
+ Object drawable =
+ StateListDrawable.class
+ .getDeclaredMethod("getStateDrawable", Integer.TYPE)
+ .invoke(stateListDrawable, index);
return (Drawable) drawable;
- } catch (NoSuchMethodException|IllegalAccessException|IllegalArgumentException
- |InvocationTargetException e) {
+ } catch (NoSuchMethodException
+ | IllegalAccessException
+ | IllegalArgumentException
+ | InvocationTargetException e) {
Log.e(TAG, "Failed to call StateListDrawable.getStateDrawable(" + index + ")", e);
return null;
}
diff --git a/src/com/android/tv/guide/ProgramListAdapter.java b/src/com/android/tv/guide/ProgramListAdapter.java
index c1fcdd40..397bacfb 100644
--- a/src/com/android/tv/guide/ProgramListAdapter.java
+++ b/src/com/android/tv/guide/ProgramListAdapter.java
@@ -22,9 +22,8 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-
import com.android.tv.R;
-import com.android.tv.data.Channel;
+import com.android.tv.data.api.Channel;
import com.android.tv.guide.ProgramManager.TableEntriesUpdatedListener;
import com.android.tv.guide.ProgramManager.TableEntry;
@@ -111,9 +110,14 @@ class ProgramListAdapter extends RecyclerView.Adapter<ProgramListAdapter.Program
Log.d(TAG, "onBind. View = " + itemView + ", Entry = " + entry);
}
ProgramManager programManager = programGuide.getProgramManager();
- ((ProgramItemView) itemView).setValues(programGuide, entry,
- programManager.getSelectedGenreId(), programManager.getFromUtcMillis(),
- programManager.getToUtcMillis(), gapTitle);
+ ((ProgramItemView) itemView)
+ .setValues(
+ programGuide,
+ entry,
+ programManager.getSelectedGenreId(),
+ programManager.getFromUtcMillis(),
+ programManager.getToUtcMillis(),
+ gapTitle);
}
void onUnbind() {
diff --git a/src/com/android/tv/guide/ProgramManager.java b/src/com/android/tv/guide/ProgramManager.java
index 4ec3f77e..3f20a837 100644
--- a/src/com/android/tv/guide/ProgramManager.java
+++ b/src/com/android/tv/guide/ProgramManager.java
@@ -18,21 +18,20 @@ package com.android.tv.guide;
import android.support.annotation.MainThread;
import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
import android.util.ArraySet;
import android.util.Log;
-
-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.data.api.Channel;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrScheduleManager;
import com.android.tv.dvr.DvrScheduleManager.OnConflictStateChangeListener;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.Utils;
-
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -40,9 +39,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
-/**
- * Manages the channels and programs for the program guide.
- */
+/** Manages the channels and programs for the program guide. */
@MainThread
public class ProgramManager {
private static final String TAG = "ProgramManager";
@@ -60,7 +57,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 final DvrDataManager mDvrDataManager; // Only set if DVR is enabled
private final DvrScheduleManager mDvrScheduleManager;
private long mStartUtcMillis;
@@ -127,51 +124,67 @@ public class ProgramManager {
private final DvrDataManager.ScheduledRecordingListener mScheduledRecordingListener =
new DvrDataManager.ScheduledRecordingListener() {
- @Override
- public void onScheduledRecordingAdded(ScheduledRecording... scheduledRecordings) {
- for (ScheduledRecording schedule : scheduledRecordings) {
- TableEntry oldEntry = getTableEntry(schedule);
- if (oldEntry != null) {
- TableEntry newEntry = new TableEntry(oldEntry.channelId, oldEntry.program,
- schedule, oldEntry.entryStartUtcMillis,
- oldEntry.entryEndUtcMillis, oldEntry.isBlocked());
- updateEntry(oldEntry, newEntry);
+ @Override
+ public void onScheduledRecordingAdded(ScheduledRecording... scheduledRecordings) {
+ for (ScheduledRecording schedule : scheduledRecordings) {
+ TableEntry oldEntry = getTableEntry(schedule);
+ if (oldEntry != null) {
+ TableEntry newEntry =
+ new TableEntry(
+ oldEntry.channelId,
+ oldEntry.program,
+ schedule,
+ oldEntry.entryStartUtcMillis,
+ oldEntry.entryEndUtcMillis,
+ oldEntry.isBlocked());
+ updateEntry(oldEntry, newEntry);
+ }
+ }
}
- }
- }
- @Override
- public void onScheduledRecordingRemoved(ScheduledRecording... scheduledRecordings) {
- for (ScheduledRecording schedule : scheduledRecordings) {
- TableEntry oldEntry = getTableEntry(schedule);
- if (oldEntry != null) {
- TableEntry newEntry = new TableEntry(oldEntry.channelId, oldEntry.program, null,
- oldEntry.entryStartUtcMillis, oldEntry.entryEndUtcMillis,
- oldEntry.isBlocked());
- updateEntry(oldEntry, newEntry);
+ @Override
+ public void onScheduledRecordingRemoved(ScheduledRecording... scheduledRecordings) {
+ for (ScheduledRecording schedule : scheduledRecordings) {
+ TableEntry oldEntry = getTableEntry(schedule);
+ 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... scheduledRecordings) {
- for (ScheduledRecording schedule : scheduledRecordings) {
- TableEntry oldEntry = getTableEntry(schedule);
- if (oldEntry != null) {
- TableEntry newEntry = new TableEntry(oldEntry.channelId, oldEntry.program,
- schedule, oldEntry.entryStartUtcMillis,
- oldEntry.entryEndUtcMillis, oldEntry.isBlocked());
- updateEntry(oldEntry, newEntry);
+ @Override
+ public void onScheduledRecordingStatusChanged(
+ ScheduledRecording... scheduledRecordings) {
+ for (ScheduledRecording schedule : scheduledRecordings) {
+ TableEntry oldEntry = getTableEntry(schedule);
+ if (oldEntry != null) {
+ TableEntry newEntry =
+ new TableEntry(
+ oldEntry.channelId,
+ oldEntry.program,
+ schedule,
+ oldEntry.entryStartUtcMillis,
+ oldEntry.entryEndUtcMillis,
+ oldEntry.isBlocked());
+ updateEntry(oldEntry, newEntry);
+ }
+ }
}
- }
- }
- };
+ };
private final OnConflictStateChangeListener mOnConflictStateChangeListener =
new OnConflictStateChangeListener() {
@Override
- public void onConflictStateChange(boolean conflict,
- ScheduledRecording... schedules) {
+ public void onConflictStateChange(
+ boolean conflict, ScheduledRecording... schedules) {
for (ScheduledRecording schedule : schedules) {
TableEntry entry = getTableEntry(schedule);
if (entry != null) {
@@ -181,8 +194,10 @@ public class ProgramManager {
}
};
- public ProgramManager(TvInputManagerHelper tvInputManagerHelper,
- ChannelDataManager channelDataManager, ProgramDataManager programDataManager,
+ public ProgramManager(
+ TvInputManagerHelper tvInputManagerHelper,
+ ChannelDataManager channelDataManager,
+ ProgramDataManager programDataManager,
@Nullable DvrDataManager dvrDataManager,
@Nullable DvrScheduleManager dvrScheduleManager) {
mTvInputManagerHelper = tvInputManagerHelper;
@@ -221,52 +236,39 @@ public class ProgramManager {
}
}
- /**
- * Adds a {@link Listener}.
- */
+ /** Adds a {@link Listener}. */
void addListener(Listener listener) {
mListeners.add(listener);
}
- /**
- * Registers a listener to be invoked when table entries are updated.
- */
+ /** Registers a listener to be invoked when table entries are updated. */
void addTableEntriesUpdatedListener(TableEntriesUpdatedListener listener) {
mTableEntriesUpdatedListeners.add(listener);
}
- /**
- * Registers a listener to be invoked when a table entry is changed.
- */
+ /** Registers a listener to be invoked when a table entry is changed. */
void addTableEntryChangedListener(TableEntryChangedListener listener) {
mTableEntryChangedListeners.add(listener);
}
- /**
- * Removes a {@link Listener}.
- */
+ /** Removes a {@link Listener}. */
void removeListener(Listener listener) {
mListeners.remove(listener);
}
- /**
- * Removes a previously installed table entries update listener.
- */
+ /** Removes a previously installed table entries update listener. */
void removeTableEntriesUpdatedListener(TableEntriesUpdatedListener listener) {
mTableEntriesUpdatedListeners.remove(listener);
}
- /**
- * Removes a previously installed table entry changed listener.
- */
+ /** Removes a previously installed table entry changed listener. */
void removeTableEntryChangedListener(TableEntryChangedListener listener) {
mTableEntryChangedListeners.remove(listener);
}
/**
- * Resets channel list with given genre.
- * Caller should call {@link #buildGenreFilters()} prior to call this API to make
- * This notifies channel updates to listeners.
+ * Resets channel list with given genre. Caller should call {@link #buildGenreFilters()} prior
+ * to call this API to make This notifies channel updates to listeners.
*/
void resetChannelListWithGenre(int genreId) {
if (genreId == mSelectedGenreId) {
@@ -275,8 +277,14 @@ public class ProgramManager {
mFilteredChannels = mGenreChannelList.get(genreId);
mSelectedGenreId = genreId;
if (DEBUG) {
- Log.d(TAG, "resetChannelListWithGenre: " + GenreItems.getCanonicalGenre(genreId)
- + " has " + mFilteredChannels.size() + " channels out of " + mChannels.size());
+ Log.d(
+ TAG,
+ "resetChannelListWithGenre: "
+ + GenreItems.getCanonicalGenre(genreId)
+ + " has "
+ + mFilteredChannels.size()
+ + " channels out of "
+ + mChannels.size());
}
if (mGenreChannelList.get(mSelectedGenreId) == null) {
throw new IllegalStateException("Genre filter isn't ready.");
@@ -284,9 +292,7 @@ public class ProgramManager {
notifyChannelsUpdated();
}
- /**
- * Update the initial time range to manage. It updates program entries and genre as well.
- */
+ /** Update the initial time range to manage. It updates program entries and genre as well. */
void updateInitialTimeRange(long startUtcMillis, long endUtcMillis) {
mStartUtcMillis = startUtcMillis;
if (endUtcMillis > mEndUtcMillis) {
@@ -298,10 +304,7 @@ public class ProgramManager {
setTimeRange(startUtcMillis, endUtcMillis);
}
-
- /**
- * Shifts the time range by the given time. Also makes ProgramGuide scroll the views.
- */
+ /** Shifts the time range by the given time. Also makes ProgramGuide scroll the views. */
void shiftTime(long timeMillisToScroll) {
long fromUtcMillis = mFromUtcMillis + timeMillisToScroll;
long toUtcMillis = mToUtcMillis + timeMillisToScroll;
@@ -316,23 +319,17 @@ public class ProgramManager {
setTimeRange(fromUtcMillis, toUtcMillis);
}
- /**
- * Returned the scrolled(shifted) time in milliseconds.
- */
+ /** Returned the scrolled(shifted) time in milliseconds. */
long getShiftedTime() {
return mFromUtcMillis - mStartUtcMillis;
}
- /**
- * Returns the start time set by {@link #updateInitialTimeRange}.
- */
+ /** Returns the start time set by {@link #updateInitialTimeRange}. */
long getStartTime() {
return mStartUtcMillis;
}
- /**
- * Returns the program index of the program with {@code entryId} or -1 if not found.
- */
+ /** Returns the program index of the program with {@code entryId} or -1 if not found. */
int getProgramIdIndex(long channelId, long entryId) {
List<TableEntry> entries = mChannelIdEntriesMap.get(channelId);
if (entries != null) {
@@ -345,38 +342,29 @@ public class ProgramManager {
return -1;
}
- /**
- * Returns the program index of the program at {@code time} or -1 if not found.
- */
+ /** Returns the program index of the program at {@code time} or -1 if not found. */
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);
- if (entry.entryStartUtcMillis <= time
- && time < entry.entryEndUtcMillis) {
+ if (entry.entryStartUtcMillis <= time && time < entry.entryEndUtcMillis) {
return i;
}
}
return -1;
}
- /**
- * Returns the start time of currently managed time range, in UTC millisecond.
- */
+ /** Returns the start time of currently managed time range, in UTC millisecond. */
long getFromUtcMillis() {
return mFromUtcMillis;
}
- /**
- * Returns the end time of currently managed time range, in UTC millisecond.
- */
+ /** Returns the end time of currently managed time range, in UTC millisecond. */
long getToUtcMillis() {
return mToUtcMillis;
}
- /**
- * Returns the number of the currently managed channels.
- */
+ /** Returns the number of the currently managed channels. */
int getChannelCount() {
return mFilteredChannels.size();
}
@@ -393,15 +381,15 @@ public class ProgramManager {
}
/**
- * Returns the index of provided {@link Channel} within the currently managed channels.
- * Returns -1 if such a channel is not found.
+ * Returns the index of provided {@link Channel} within the currently managed channels. Returns
+ * -1 if such a channel is not found.
*/
int getChannelIndex(Channel channel) {
return mFilteredChannels.indexOf(channel);
}
/**
- * Returns the index of channel with {@code channelId} within the currently managed channels.
+ * Returns the index of channel with {@code channelId} within the currently managed channels.
* Returns -1 if such a channel is not found.
*/
int getChannelIndex(long channelId) {
@@ -425,9 +413,7 @@ public class ProgramManager {
return mChannelIdEntriesMap.get(channelId).get(index);
}
- /**
- * Returns list genre ID's which has a channel.
- */
+ /** Returns list genre ID's which has a channel. */
List<Integer> getFilteredGenreIds() {
return mFilteredGenreIds;
}
@@ -457,15 +443,13 @@ public class ProgramManager {
buildGenreFilters();
}
- /**
- * Updates the table entries without notifying the change.
- */
+ /** Updates the table entries without notifying the change. */
private void updateTableEntriesWithoutNotification(boolean clear) {
if (clear) {
mChannelIdEntriesMap.clear();
}
- boolean parentalControlsEnabled = mTvInputManagerHelper.getParentalControlSettings()
- .isParentalControlsEnabled();
+ boolean parentalControlsEnabled =
+ mTvInputManagerHelper.getParentalControlSettings().isParentalControlsEnabled();
for (Channel channel : mChannels) {
long channelId = channel.getId();
// Inline the updating of the mChannelIdEntriesMap here so we can only call
@@ -475,8 +459,12 @@ public class ProgramManager {
int size = entries.size();
if (DEBUG) {
- Log.d(TAG, "Programs are loaded for channel " + channel.getId()
- + ", loaded size = " + size);
+ Log.d(
+ TAG,
+ "Programs are loaded for channel "
+ + channel.getId()
+ + ", loaded size = "
+ + size);
}
if (size == 0) {
continue;
@@ -496,14 +484,19 @@ public class ProgramManager {
} else {
TableEntry lastEntry = entries.get(entries.size() - 1);
if (mEndUtcMillis > lastEntry.entryEndUtcMillis) {
- entries.add(new TableEntry(channelId, lastEntry.entryEndUtcMillis,
- mEndUtcMillis));
+ entries.add(
+ new TableEntry(
+ channelId, lastEntry.entryEndUtcMillis, mEndUtcMillis));
} 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));
+ entries.add(
+ new TableEntry(
+ lastEntry.channelId,
+ lastEntry.program,
+ lastEntry.scheduledRecording,
+ lastEntry.entryStartUtcMillis,
+ mEndUtcMillis,
+ lastEntry.mIsBlocked));
}
}
}
@@ -511,11 +504,10 @@ public class ProgramManager {
}
/**
- * 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
- * with built channel list.
- * This is expected to be called whenever program guide is shown.
+ * 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 with built channel list. This is expected to be called whenever program
+ * guide is shown.
*/
private void buildGenreFilters() {
if (DEBUG) Log.d(TAG, "buildGenreFilters");
@@ -572,9 +564,13 @@ public class ProgramManager {
private void setTimeRange(long fromUtcMillis, long toUtcMillis) {
if (DEBUG) {
- Log.d(TAG, "setTimeRange. {FromTime="
- + Utils.toTimeString(fromUtcMillis) + ", ToTime="
- + Utils.toTimeString(toUtcMillis) + "}");
+ Log.d(
+ TAG,
+ "setTimeRange. {FromTime="
+ + Utils.toTimeString(fromUtcMillis)
+ + ", ToTime="
+ + Utils.toTimeString(toUtcMillis)
+ + "}");
}
if (mFromUtcMillis != fromUtcMillis || mToUtcMillis != toUtcMillis) {
mFromUtcMillis = fromUtcMillis;
@@ -585,8 +581,8 @@ public class ProgramManager {
private List<TableEntry> createProgramEntries(long channelId, boolean parentalControlsEnabled) {
List<TableEntry> entries = new ArrayList<>();
- boolean channelLocked = parentalControlsEnabled
- && mChannelDataManager.getChannel(channelId).isLocked();
+ boolean channelLocked =
+ parentalControlsEnabled && mChannelDataManager.getChannel(channelId).isLocked();
if (channelLocked) {
entries.add(new TableEntry(channelId, mStartUtcMillis, Long.MAX_VALUE, true));
} else {
@@ -597,20 +593,27 @@ public class ProgramManager {
// Dummy program.
continue;
}
- long programStartTime = Math.max(program.getStartTimeUtcMillis(),
- mStartUtcMillis);
+ long programStartTime = Math.max(program.getStartTimeUtcMillis(), mStartUtcMillis);
long programEndTime = program.getEndTimeUtcMillis();
if (programStartTime > lastProgramEndTime) {
// Gap since the last program.
- entries.add(new TableEntry(channelId, lastProgramEndTime,
- programStartTime));
+ entries.add(new TableEntry(channelId, lastProgramEndTime, programStartTime));
lastProgramEndTime = programStartTime;
}
if (programEndTime > lastProgramEndTime) {
- ScheduledRecording scheduledRecording = mDvrDataManager == null ? null
- : mDvrDataManager.getScheduledRecordingForProgramId(program.getId());
- entries.add(new TableEntry(channelId, program, scheduledRecording,
- lastProgramEndTime, programEndTime, false));
+ ScheduledRecording scheduledRecording =
+ mDvrDataManager == null
+ ? null
+ : mDvrDataManager.getScheduledRecordingForProgramId(
+ program.getId());
+ entries.add(
+ new TableEntry(
+ channelId,
+ program,
+ scheduledRecording,
+ lastProgramEndTime,
+ programEndTime,
+ false));
lastProgramEndTime = programEndTime;
}
}
@@ -622,9 +625,15 @@ public class ProgramManager {
// If the first entry's width doesn't have enough width, it is not good to show
// the first entry from UI perspective. So we clip it out.
entries.remove(0);
- entries.set(0, new TableEntry(secondEntry.channelId, secondEntry.program,
- secondEntry.scheduledRecording, mStartUtcMillis,
- secondEntry.entryEndUtcMillis, secondEntry.mIsBlocked));
+ entries.set(
+ 0,
+ new TableEntry(
+ secondEntry.channelId,
+ secondEntry.program,
+ secondEntry.scheduledRecording,
+ mStartUtcMillis,
+ secondEntry.entryEndUtcMillis,
+ secondEntry.mIsBlocked));
}
}
return entries;
@@ -662,8 +671,8 @@ public class ProgramManager {
/**
* Entry for program guide table. An "entry" can be either an actual program or a gap between
- * programs. This is needed for {@link ProgramListAdapter} because
- * {@link android.support.v17.leanback.widget.HorizontalGridView} ignores margins between items.
+ * programs. This is needed for {@link ProgramListAdapter} because {@link
+ * android.support.v17.leanback.widget.HorizontalGridView} ignores margins between items.
*/
static class TableEntry {
/** Channel ID which this entry is included. */
@@ -686,18 +695,27 @@ public class ProgramManager {
this(channelId, null, startUtcMillis, endUtcMillis, false);
}
- private TableEntry(long channelId, long startUtcMillis, long endUtcMillis,
- boolean blocked) {
+ private TableEntry(
+ long channelId, long startUtcMillis, long endUtcMillis, boolean blocked) {
this(channelId, null, null, startUtcMillis, endUtcMillis, blocked);
}
- private TableEntry(long channelId, Program program, long entryStartUtcMillis,
- long entryEndUtcMillis, boolean isBlocked) {
+ 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, ScheduledRecording scheduledRecording,
- long entryStartUtcMillis, long entryEndUtcMillis, boolean isBlocked) {
+ private TableEntry(
+ long channelId,
+ Program program,
+ ScheduledRecording scheduledRecording,
+ long entryStartUtcMillis,
+ long entryEndUtcMillis,
+ boolean isBlocked) {
this.channelId = channelId;
this.program = program;
this.scheduledRecording = scheduledRecording;
@@ -706,46 +724,34 @@ public class ProgramManager {
mIsBlocked = isBlocked;
}
- /**
- * A stable id useful for {@link android.support.v7.widget.RecyclerView.Adapter}.
- */
+ /** A stable id useful for {@link android.support.v7.widget.RecyclerView.Adapter}. */
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.
- */
+ /** Returns true if this is a gap. */
boolean isGap() {
- return !Program.isValid(program);
+ return !Program.isProgramValid(program);
}
- /**
- * Returns true if this channel is blocked.
- */
+ /** Returns true if this channel is blocked. */
boolean isBlocked() {
return mIsBlocked;
}
- /**
- * Returns true if this program is on the air.
- */
+ /** Returns true if this program is on the air. */
boolean isCurrentProgram() {
long current = System.currentTimeMillis();
return entryStartUtcMillis <= current && entryEndUtcMillis > current;
}
- /**
- * Returns if this program has the genre.
- */
+ /** Returns if this program has the genre. */
boolean hasGenre(int genreId) {
return !isGap() && program.hasGenre(genreId);
}
- /**
- * Returns the width of table entry, in pixels.
- */
+ /** Returns the width of table entry, in pixels. */
int getWidth() {
return GuideUtils.convertMillisToPixel(entryStartUtcMillis, entryEndUtcMillis);
}
@@ -753,17 +759,42 @@ public class ProgramManager {
@Override
public String toString() {
return "TableEntry{"
- + "hashCode=" + hashCode()
- + ", channelId=" + channelId
- + ", program=" + program
- + ", startTime=" + Utils.toTimeString(entryStartUtcMillis)
- + ", endTimeTime=" + Utils.toTimeString(entryEndUtcMillis) + "}";
- }
+ + "hashCode="
+ + hashCode()
+ + ", channelId="
+ + channelId
+ + ", program="
+ + program
+ + ", startTime="
+ + Utils.toTimeString(entryStartUtcMillis)
+ + ", endTimeTime="
+ + Utils.toTimeString(entryEndUtcMillis)
+ + "}";
+ }
+ }
+
+ @VisibleForTesting
+ public static TableEntry createTableEntryForTest(
+ long channelId,
+ Program program,
+ ScheduledRecording scheduledRecording,
+ long entryStartUtcMillis,
+ long entryEndUtcMillis,
+ boolean isBlocked) {
+ return new TableEntry(
+ channelId,
+ program,
+ scheduledRecording,
+ entryStartUtcMillis,
+ entryEndUtcMillis,
+ isBlocked);
}
interface Listener {
void onGenresUpdated();
+
void onChannelsUpdated();
+
void onTimeRangeUpdated();
}
@@ -777,12 +808,12 @@ public class ProgramManager {
static class ListenerAdapter implements Listener {
@Override
- public void onGenresUpdated() { }
+ public void onGenresUpdated() {}
@Override
- public void onChannelsUpdated() { }
+ public void onChannelsUpdated() {}
@Override
- public void onTimeRangeUpdated() { }
+ public void onTimeRangeUpdated() {}
}
}
diff --git a/src/com/android/tv/guide/ProgramRow.java b/src/com/android/tv/guide/ProgramRow.java
index fefc724c..83175bb6 100644
--- a/src/com/android/tv/guide/ProgramRow.java
+++ b/src/com/android/tv/guide/ProgramRow.java
@@ -24,12 +24,9 @@ import android.util.Log;
import android.util.Range;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
-
-import com.android.tv.MainActivity;
-import com.android.tv.data.Channel;
+import com.android.tv.data.api.Channel;
import com.android.tv.guide.ProgramManager.TableEntry;
import com.android.tv.util.Utils;
-
import java.util.concurrent.TimeUnit;
public class ProgramRow extends TimelineGridView {
@@ -47,25 +44,23 @@ public class ProgramRow extends TimelineGridView {
interface ChildFocusListener {
/**
- * Is called after focus is moved. Caller should check if old and new focuses are
- * listener's children.
- * See {@code ProgramRow#setChildFocusListener(ChildFocusListener)}.
+ * Is called after focus is moved. Caller should check if old and new focuses are listener's
+ * children. See {@code ProgramRow#setChildFocusListener(ChildFocusListener)}.
*/
void onChildFocus(View oldFocus, View newFocus);
}
- /**
- * Used only for debugging.
- */
+ /** Used only for debugging. */
private Channel mChannel;
- private final OnGlobalLayoutListener mLayoutListener = new OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- getViewTreeObserver().removeOnGlobalLayoutListener(this);
- updateChildVisibleArea();
- }
- };
+ private final OnGlobalLayoutListener mLayoutListener =
+ new OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ updateChildVisibleArea();
+ }
+ };
public ProgramRow(Context context) {
this(context, null);
@@ -79,9 +74,7 @@ public class ProgramRow extends TimelineGridView {
super(context, attrs, defStyle);
}
- /**
- * Registers a listener focus events occurring on children to the {@code ProgramRow}.
- */
+ /** Registers a listener focus events occurring on children to the {@code ProgramRow}. */
public void setChildFocusListener(ChildFocusListener childFocusListener) {
mChildFocusListener = childFocusListener;
}
@@ -108,9 +101,7 @@ public class ProgramRow extends TimelineGridView {
updateChildVisibleArea();
}
- /**
- * Moves focus to the current program.
- */
+ /** Moves focus to the current program. */
public void focusCurrentProgram() {
View currentProgram = getCurrentProgramView();
if (currentProgram == null) {
@@ -124,13 +115,15 @@ public class ProgramRow extends TimelineGridView {
// Call this API after RTL is resolved. (i.e. View is measured.)
private boolean isDirectionStart(int direction) {
return getLayoutDirection() == LAYOUT_DIRECTION_LTR
- ? direction == View.FOCUS_LEFT : direction == View.FOCUS_RIGHT;
+ ? direction == View.FOCUS_LEFT
+ : direction == View.FOCUS_RIGHT;
}
// Call this API after RTL is resolved. (i.e. View is measured.)
private boolean isDirectionEnd(int direction) {
return getLayoutDirection() == LAYOUT_DIRECTION_LTR
- ? direction == View.FOCUS_RIGHT : direction == View.FOCUS_LEFT;
+ ? direction == View.FOCUS_RIGHT
+ : direction == View.FOCUS_LEFT;
}
@Override
@@ -142,8 +135,8 @@ public class ProgramRow extends TimelineGridView {
if (isDirectionStart(direction) || direction == View.FOCUS_BACKWARD) {
if (focusedEntry.entryStartUtcMillis < fromMillis) {
// The current entry starts outside of the view; Align or scroll to the left.
- scrollByTime(Math.max(-ONE_HOUR_MILLIS,
- focusedEntry.entryStartUtcMillis - fromMillis));
+ scrollByTime(
+ Math.max(-ONE_HOUR_MILLIS, focusedEntry.entryStartUtcMillis - fromMillis));
return focused;
}
} else if (isDirectionEnd(direction) || direction == View.FOCUS_FORWARD) {
@@ -169,17 +162,19 @@ public class ProgramRow extends TimelineGridView {
TableEntry targetEntry = ((ProgramItemView) target).getTableEntry();
if (isDirectionStart(direction) || direction == View.FOCUS_BACKWARD) {
- if (targetEntry.entryStartUtcMillis < fromMillis &&
- targetEntry.entryEndUtcMillis < fromMillis + HALF_HOUR_MILLIS) {
+ if (targetEntry.entryStartUtcMillis < fromMillis
+ && targetEntry.entryEndUtcMillis < fromMillis + HALF_HOUR_MILLIS) {
// The target entry starts outside the view; Align or scroll to the left.
- scrollByTime(Math.max(-ONE_HOUR_MILLIS,
- targetEntry.entryStartUtcMillis - fromMillis));
+ scrollByTime(
+ Math.max(-ONE_HOUR_MILLIS, targetEntry.entryStartUtcMillis - fromMillis));
}
} else if (isDirectionEnd(direction) || direction == View.FOCUS_FORWARD) {
if (targetEntry.entryStartUtcMillis > fromMillis + ONE_HOUR_MILLIS + HALF_HOUR_MILLIS) {
// The target entry starts outside the view; Align or scroll to the right.
- scrollByTime(Math.min(ONE_HOUR_MILLIS,
- targetEntry.entryStartUtcMillis - fromMillis - ONE_HOUR_MILLIS));
+ scrollByTime(
+ Math.min(
+ ONE_HOUR_MILLIS,
+ targetEntry.entryStartUtcMillis - fromMillis - ONE_HOUR_MILLIS));
}
}
@@ -188,8 +183,11 @@ public class ProgramRow extends TimelineGridView {
private void scrollByTime(long timeToScroll) {
if (DEBUG) {
- Log.d(TAG, "scrollByTime(timeToScroll="
- + TimeUnit.MILLISECONDS.toMinutes(timeToScroll) + "min)");
+ Log.d(
+ TAG,
+ "scrollByTime(timeToScroll="
+ + TimeUnit.MILLISECONDS.toMinutes(timeToScroll)
+ + "min)");
}
mProgramManager.shiftTime(timeToScroll);
}
@@ -203,12 +201,13 @@ public class ProgramRow extends TimelineGridView {
// The focus is lost due to information loaded. Requests focus immediately.
// (Because this entry is detached after real entries attached, we can't take
// the below approach to resume focus on entry being attached.)
- post(new Runnable() {
- @Override
- public void run() {
- requestFocus();
- }
- });
+ post(
+ new Runnable() {
+ @Override
+ public void run() {
+ requestFocus();
+ }
+ });
} else if (entry.isCurrentProgram()) {
if (DEBUG) Log.d(TAG, "Keep focus to the current program");
// Current program is visible in the guide.
@@ -227,12 +226,13 @@ public class ProgramRow extends TimelineGridView {
TableEntry entry = ((ProgramItemView) child).getTableEntry();
if (entry.isCurrentProgram()) {
mKeepFocusToCurrentProgram = false;
- post(new Runnable() {
- @Override
- public void run() {
- requestFocus();
- }
- });
+ post(
+ new Runnable() {
+ @Override
+ public void run() {
+ requestFocus();
+ }
+ });
}
}
}
@@ -243,8 +243,12 @@ public class ProgramRow extends TimelineGridView {
// Give focus according to the previous focused range
Range<Integer> focusRange = programGrid.getFocusRange();
- View nextFocus = GuideUtils.findNextFocusedProgram(this, focusRange.getLower(),
- focusRange.getUpper(), programGrid.isKeepCurrentProgramFocused());
+ View nextFocus =
+ GuideUtils.findNextFocusedProgram(
+ this,
+ focusRange.getLower(),
+ focusRange.getUpper(),
+ programGrid.isKeepCurrentProgramFocused());
if (nextFocus != null) {
return nextFocus.requestFocus();
@@ -279,30 +283,29 @@ public class ProgramRow extends TimelineGridView {
mChannel = channel;
}
- /**
- * Sets the instance of {@link ProgramGuide}
- */
+ /** Sets the instance of {@link ProgramGuide} */
public void setProgramGuide(ProgramGuide programGuide) {
mProgramGuide = programGuide;
mProgramManager = programGuide.getProgramManager();
}
- /**
- * Resets the scroll with the initial offset {@code scrollOffset}.
- */
+ /** Resets the scroll with the initial offset {@code scrollOffset}. */
public void resetScroll(int scrollOffset) {
- long startTime = GuideUtils.convertPixelToMillis(scrollOffset)
- + mProgramManager.getStartTime();
- int position = mChannel == null ? -1 : mProgramManager.getProgramIndexAtTime(
- mChannel.getId(), startTime);
+ long startTime =
+ GuideUtils.convertPixelToMillis(scrollOffset) + mProgramManager.getStartTime();
+ int position =
+ mChannel == null
+ ? -1
+ : mProgramManager.getProgramIndexAtTime(mChannel.getId(), startTime);
if (position < 0) {
getLayoutManager().scrollToPosition(0);
} else {
TableEntry entry = mProgramManager.getTableEntry(mChannel.getId(), position);
- int offset = GuideUtils.convertMillisToPixel(
- mProgramManager.getStartTime(), entry.entryStartUtcMillis) - scrollOffset;
- ((LinearLayoutManager) getLayoutManager())
- .scrollToPositionWithOffset(position, offset);
+ int offset =
+ GuideUtils.convertMillisToPixel(
+ mProgramManager.getStartTime(), entry.entryStartUtcMillis)
+ - scrollOffset;
+ ((LinearLayoutManager) getLayoutManager()).scrollToPositionWithOffset(position, offset);
// Workaround to b/31598505. When a program's duration is too long,
// RecyclerView.onScrolled() will not be called after scrollToPositionWithOffset().
// Therefore we have to update children's visible areas by ourselves in this case.
diff --git a/src/com/android/tv/guide/ProgramTableAdapter.java b/src/com/android/tv/guide/ProgramTableAdapter.java
index 99f853b1..6e7485ac 100644
--- a/src/com/android/tv/guide/ProgramTableAdapter.java
+++ b/src/com/android/tv/guide/ProgramTableAdapter.java
@@ -16,8 +16,6 @@
package com.android.tv.guide;
-import static com.android.tv.util.ImageLoader.ImageLoaderCallback;
-
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
@@ -49,32 +47,30 @@ import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeL
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
-import com.android.tv.common.TvCommonUtils;
+import com.android.tv.TvSingletons;
import com.android.tv.common.feature.CommonFeatures;
-import com.android.tv.data.Channel;
+import com.android.tv.common.util.CommonUtils;
import com.android.tv.data.Program;
import com.android.tv.data.Program.CriticScore;
+import com.android.tv.data.api.Channel;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.data.ScheduledRecording;
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;
+import com.android.tv.util.images.ImageCache;
+import com.android.tv.util.images.ImageLoader;
+import com.android.tv.util.images.ImageLoader.ImageLoaderCallback;
+import com.android.tv.util.images.ImageLoader.LoadTvInputLogoTask;
import java.util.ArrayList;
import java.util.List;
-/**
- * Adapts the {@link ProgramListAdapter} list to the body of the program guide table.
- */
+/** Adapts the {@link ProgramListAdapter} list to the body of the program guide table. */
class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.ProgramRowViewHolder>
implements ProgramManager.TableEntryChangedListener {
private static final String TAG = "ProgramTableAdapter";
@@ -118,10 +114,10 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
mContext = context;
mAccessibilityManager =
(AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
- mTvInputManagerHelper = TvApplication.getSingletons(context).getTvInputManagerHelper();
+ mTvInputManagerHelper = TvSingletons.getSingletons(context).getTvInputManagerHelper();
if (CommonFeatures.DVR.isEnabled(context)) {
- mDvrManager = TvApplication.getSingletons(context).getDvrManager();
- mDvrDataManager = TvApplication.getSingletons(context).getDvrDataManager();
+ mDvrManager = TvSingletons.getSingletons(context).getDvrManager();
+ mDvrDataManager = TvSingletons.getSingletons(context).getDvrDataManager();
} else {
mDvrManager = null;
mDvrDataManager = null;
@@ -130,58 +126,62 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
mProgramManager = programGuide.getProgramManager();
Resources res = context.getResources();
- mChannelLogoWidth = res.getDimensionPixelSize(
- R.dimen.program_guide_table_header_column_channel_logo_width);
- mChannelLogoHeight = res.getDimensionPixelSize(
- R.dimen.program_guide_table_header_column_channel_logo_height);
- mImageWidth = res.getDimensionPixelSize(
- R.dimen.program_guide_table_detail_image_width);
- mImageHeight = res.getDimensionPixelSize(
- R.dimen.program_guide_table_detail_image_height);
- mProgramTitleForNoInformation = res.getString(
- R.string.program_title_for_no_information);
- mProgramTitleForBlockedChannel = res.getString(
- R.string.program_title_for_blocked_channel);
- mChannelTextColor = res.getColor(
- R.color.program_guide_table_header_column_channel_number_text_color, null);
- mChannelBlockedTextColor = res.getColor(
- R.color.program_guide_table_header_column_channel_number_blocked_text_color, null);
- mDetailTextColor = res.getColor(
- R.color.program_guide_table_detail_title_text_color, null);
- mDetailGrayedTextColor = res.getColor(
- R.color.program_guide_table_detail_title_grayed_text_color, null);
+ mChannelLogoWidth =
+ res.getDimensionPixelSize(
+ R.dimen.program_guide_table_header_column_channel_logo_width);
+ mChannelLogoHeight =
+ res.getDimensionPixelSize(
+ R.dimen.program_guide_table_header_column_channel_logo_height);
+ mImageWidth = res.getDimensionPixelSize(R.dimen.program_guide_table_detail_image_width);
+ mImageHeight = res.getDimensionPixelSize(R.dimen.program_guide_table_detail_image_height);
+ mProgramTitleForNoInformation = res.getString(R.string.program_title_for_no_information);
+ mProgramTitleForBlockedChannel = res.getString(R.string.program_title_for_blocked_channel);
+ mChannelTextColor =
+ res.getColor(
+ R.color.program_guide_table_header_column_channel_number_text_color, null);
+ mChannelBlockedTextColor =
+ res.getColor(
+ R.color.program_guide_table_header_column_channel_number_blocked_text_color,
+ null);
+ mDetailTextColor = res.getColor(R.color.program_guide_table_detail_title_text_color, null);
+ mDetailGrayedTextColor =
+ res.getColor(R.color.program_guide_table_detail_title_grayed_text_color, null);
mAnimationDuration =
res.getInteger(R.integer.program_guide_table_detail_fade_anim_duration);
- mDetailPadding = res.getDimensionPixelOffset(
- R.dimen.program_guide_table_detail_padding);
+ mDetailPadding = res.getDimensionPixelOffset(R.dimen.program_guide_table_detail_padding);
mProgramRecordableText = res.getString(R.string.dvr_epg_program_recordable);
mRecordingScheduledText = res.getString(R.string.dvr_epg_program_recording_scheduled);
mRecordingConflictText = res.getString(R.string.dvr_epg_program_recording_conflict);
mRecordingFailedText = res.getString(R.string.dvr_epg_program_recording_failed);
mRecordingInProgressText = res.getString(R.string.dvr_epg_program_recording_in_progress);
- mDvrPaddingStartWithTrack = res.getDimensionPixelOffset(
- R.dimen.program_guide_table_detail_dvr_margin_start);
- mDvrPaddingStartWithOutTrack = res.getDimensionPixelOffset(
- R.dimen.program_guide_table_detail_dvr_margin_start_without_track);
-
- int episodeTitleSize = res.getDimensionPixelSize(
- R.dimen.program_guide_table_detail_episode_title_text_size);
- ColorStateList episodeTitleColor = ColorStateList.valueOf(
- res.getColor(R.color.program_guide_table_detail_episode_title_text_color, null));
- mEpisodeTitleStyle = new TextAppearanceSpan(null, 0, episodeTitleSize,
- episodeTitleColor, null);
+ mDvrPaddingStartWithTrack =
+ res.getDimensionPixelOffset(R.dimen.program_guide_table_detail_dvr_margin_start);
+ mDvrPaddingStartWithOutTrack =
+ res.getDimensionPixelOffset(
+ R.dimen.program_guide_table_detail_dvr_margin_start_without_track);
+
+ int episodeTitleSize =
+ res.getDimensionPixelSize(
+ R.dimen.program_guide_table_detail_episode_title_text_size);
+ ColorStateList episodeTitleColor =
+ ColorStateList.valueOf(
+ res.getColor(
+ R.color.program_guide_table_detail_episode_title_text_color, null));
+ mEpisodeTitleStyle =
+ new TextAppearanceSpan(null, 0, episodeTitleSize, episodeTitleColor, null);
mCriticScoreViews = new ArrayList<>();
mRecycledViewPool = new RecycledViewPool();
- mRecycledViewPool.setMaxRecycledViews(R.layout.program_guide_table_item,
- context.getResources().getInteger(
- R.integer.max_recycled_view_pool_epg_table_item));
- mProgramManager.addListener(new ProgramManager.ListenerAdapter() {
- @Override
- public void onChannelsUpdated() {
- update();
- }
- });
+ mRecycledViewPool.setMaxRecycledViews(
+ R.layout.program_guide_table_item,
+ context.getResources().getInteger(R.integer.max_recycled_view_pool_epg_table_item));
+ mProgramManager.addListener(
+ new ProgramManager.ListenerAdapter() {
+ @Override
+ public void onChannelsUpdated() {
+ update();
+ }
+ });
update();
mProgramManager.addTableEntryChangedListener(this);
}
@@ -193,8 +193,8 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
}
mProgramListAdapters.clear();
for (int i = 0; i < mProgramManager.getChannelCount(); i++) {
- ProgramListAdapter listAdapter = new ProgramListAdapter(mContext.getResources(),
- mProgramGuide, i);
+ ProgramListAdapter listAdapter =
+ new ProgramListAdapter(mContext.getResources(), mProgramGuide, i);
mProgramManager.addTableEntriesUpdatedListener(listAdapter);
mProgramListAdapters.add(listAdapter);
}
@@ -250,29 +250,31 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
private ProgramManager.TableEntry mSelectedEntry;
private Animator mDetailOutAnimator;
private Animator mDetailInAnimator;
- private final Runnable mDetailInStarter = new Runnable() {
- @Override
- public void run() {
- mProgramRow.removeOnScrollListener(mOnScrollListener);
- if (mDetailInAnimator != null) {
- mDetailInAnimator.start();
- }
- }
- };
- private final Runnable mUpdateDetailViewRunnable = new Runnable() {
- @Override
- public void run() {
- updateDetailView();
- }
- };
+ private final Runnable mDetailInStarter =
+ new Runnable() {
+ @Override
+ public void run() {
+ mProgramRow.removeOnScrollListener(mOnScrollListener);
+ if (mDetailInAnimator != null) {
+ mDetailInAnimator.start();
+ }
+ }
+ };
+ private final Runnable mUpdateDetailViewRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ updateDetailView();
+ }
+ };
private final RecyclerView.OnScrollListener mOnScrollListener =
new RecyclerView.OnScrollListener() {
- @Override
- public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
- onHorizontalScrolled();
- }
- };
+ @Override
+ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ onHorizontalScrolled();
+ }
+ };
private final ViewTreeObserver.OnGlobalFocusChangeListener mGlobalFocusChangeListener =
new ViewTreeObserver.OnGlobalFocusChangeListener() {
@@ -313,8 +315,7 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
new AccessibilityManager.AccessibilityStateChangeListener() {
@Override
public void onAccessibilityStateChanged(boolean enable) {
- enable &= !TvCommonUtils.isRunningInTest();
- mDetailView.setFocusable(enable);
+ enable &= !CommonUtils.isRunningInTest();
mChannelHeaderView.setFocusable(enable);
}
};
@@ -327,7 +328,8 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
- mContainer.getViewTreeObserver()
+ mContainer
+ .getViewTreeObserver()
.addOnGlobalFocusChangeListener(mGlobalFocusChangeListener);
mAccessibilityManager.addAccessibilityStateChangeListener(
mAccessibilityStateChangeListener);
@@ -335,7 +337,8 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
@Override
public void onViewDetachedFromWindow(View v) {
- mContainer.getViewTreeObserver()
+ mContainer
+ .getViewTreeObserver()
.removeOnGlobalFocusChangeListener(mGlobalFocusChangeListener);
mAccessibilityManager.removeAccessibilityStateChangeListener(
mAccessibilityStateChangeListener);
@@ -364,9 +367,8 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
mChannelBlockView = (ImageView) mContainer.findViewById(R.id.channel_block);
mInputLogoView = (ImageView) mContainer.findViewById(R.id.input_logo);
- boolean accessibilityEnabled = mAccessibilityManager.isEnabled()
- && !TvCommonUtils.isRunningInTest();
- mDetailView.setFocusable(accessibilityEnabled);
+ boolean accessibilityEnabled =
+ mAccessibilityManager.isEnabled() && !CommonUtils.isRunningInTest();
mChannelHeaderView.setFocusable(accessibilityEnabled);
}
@@ -382,9 +384,10 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
mDetailView.setVisibility(View.GONE);
// The bottom-left of the last channel header view will have a rounded corner.
- mChannelHeaderView.setBackgroundResource((position < mProgramListAdapters.size() - 1)
- ? R.drawable.program_guide_table_header_column_item_background
- : R.drawable.program_guide_table_header_column_last_item_background);
+ mChannelHeaderView.setBackgroundResource(
+ (position < mProgramListAdapters.size() - 1)
+ ? R.drawable.program_guide_table_header_column_item_background
+ : R.drawable.program_guide_table_header_column_last_item_background);
}
private void onBindChannel(Channel channel) {
@@ -411,7 +414,8 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
} else {
size = R.dimen.program_guide_table_header_column_channel_number_small_font_size;
}
- mChannelNumberView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+ mChannelNumberView.setTextSize(
+ TypedValue.COMPLEX_UNIT_PX,
mChannelNumberView.getContext().getResources().getDimension(size));
mChannelNumberView.setText(displayNumber);
mChannelNumberView.setVisibility(View.VISIBLE);
@@ -429,8 +433,11 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
mChannelNameView.setVisibility(View.VISIBLE);
mChannelBlockView.setVisibility(View.GONE);
- mChannel.loadBitmap(itemView.getContext(), Channel.LOAD_IMAGE_TYPE_CHANNEL_LOGO,
- mChannelLogoWidth, mChannelLogoHeight,
+ mChannel.loadBitmap(
+ itemView.getContext(),
+ Channel.LOAD_IMAGE_TYPE_CHANNEL_LOGO,
+ mChannelLogoWidth,
+ mChannelLogoHeight,
createChannelLogoLoadedCallback(this, channel.getId()));
}
}
@@ -439,7 +446,8 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
public void onChildFocus(View oldFocus, View newFocus) {
if (newFocus == null) {
return;
- } // When the accessibility service is enabled, focus might be put on channel's header or
+ } // When the accessibility service is enabled, focus might be put on channel's header
+ // or
// detail view, besides program items.
if (newFocus == mChannelHeaderView) {
mSelectedEntry = ((ProgramItemView) mProgramRow.getChildAt(0)).getTableEntry();
@@ -461,7 +469,7 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
return;
}
- if (Program.isValid(mSelectedEntry.program)) {
+ if (Program.isProgramValid(mSelectedEntry.program)) {
Program program = mSelectedEntry.program;
if (getProgramBlock(program) == null) {
program.prefetchPosterArt(itemView.getContext(), mImageWidth, mImageHeight);
@@ -473,10 +481,12 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
View detailContentView = mDetailView.findViewById(R.id.detail_content);
if (mDetailInAnimator == null) {
- mDetailOutAnimator = ObjectAnimator.ofPropertyValuesHolder(detailContentView,
- PropertyValuesHolder.ofFloat(View.ALPHA, 1f, 0f),
- PropertyValuesHolder.ofFloat(View.TRANSLATION_X,
- 0f, direction * mDetailPadding));
+ mDetailOutAnimator =
+ ObjectAnimator.ofPropertyValuesHolder(
+ detailContentView,
+ PropertyValuesHolder.ofFloat(View.ALPHA, 1f, 0f),
+ PropertyValuesHolder.ofFloat(
+ View.TRANSLATION_X, 0f, direction * mDetailPadding));
mDetailOutAnimator.setDuration(mAnimationDuration);
mDetailOutAnimator.addListener(
new HardwareLayerAnimatorListenerAdapter(detailContentView) {
@@ -501,10 +511,12 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
mHandler.postDelayed(mDetailInStarter, mAnimationDuration);
}
- mDetailInAnimator = ObjectAnimator.ofPropertyValuesHolder(detailContentView,
- PropertyValuesHolder.ofFloat(View.ALPHA, 0f, 1f),
- PropertyValuesHolder.ofFloat(View.TRANSLATION_X,
- direction * -mDetailPadding, 0f));
+ mDetailInAnimator =
+ ObjectAnimator.ofPropertyValuesHolder(
+ detailContentView,
+ PropertyValuesHolder.ofFloat(View.ALPHA, 0f, 1f),
+ PropertyValuesHolder.ofFloat(
+ View.TRANSLATION_X, direction * -mDetailPadding, 0f));
mDetailInAnimator.setDuration(mAnimationDuration);
mDetailInAnimator.addListener(
new HardwareLayerAnimatorListenerAdapter(detailContentView) {
@@ -529,7 +541,7 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
}
if (DEBUG) Log.d(TAG, "updateDetailView");
mCriticScoresLayout.removeAllViews();
- if (Program.isValid(mSelectedEntry.program)) {
+ if (Program.isProgramValid(mSelectedEntry.program)) {
mTitleView.setTextColor(mDetailTextColor);
Context context = itemView.getContext();
Program program = mSelectedEntry.program;
@@ -538,7 +550,10 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
updatePosterArt(null);
if (blockedRating == null) {
- program.loadPosterArt(context, mImageWidth, mImageHeight,
+ program.loadPosterArt(
+ context,
+ mImageWidth,
+ mImageHeight,
createProgramPosterArtCallback(this, program));
}
@@ -550,24 +565,35 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
String fullTitle = title + " " + episodeTitle;
SpannableString text = new SpannableString(fullTitle);
- text.setSpan(mEpisodeTitleStyle,
- fullTitle.length() - episodeTitle.length(), fullTitle.length(),
+ text.setSpan(
+ mEpisodeTitleStyle,
+ fullTitle.length() - episodeTitle.length(),
+ fullTitle.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mTitleView.setText(text);
}
- updateTextView(mTimeView, Utils.getDurationString(context,
- program.getStartTimeUtcMillis(),
- program.getEndTimeUtcMillis(), false));
-
- boolean trackMetaDataVisible = updateTextView(mAspectRatioView, Utils
- .getAspectRatioString(program.getVideoWidth(), program.getVideoHeight()));
-
- int videoDefinitionLevel = Utils.getVideoDefinitionLevelFromSize(
- program.getVideoWidth(), program.getVideoHeight());
+ updateTextView(
+ mTimeView,
+ Utils.getDurationString(
+ context,
+ program.getStartTimeUtcMillis(),
+ program.getEndTimeUtcMillis(),
+ false));
+
+ boolean trackMetaDataVisible =
+ updateTextView(
+ mAspectRatioView,
+ Utils.getAspectRatioString(
+ program.getVideoWidth(), program.getVideoHeight()));
+
+ int videoDefinitionLevel =
+ Utils.getVideoDefinitionLevelFromSize(
+ program.getVideoWidth(), program.getVideoHeight());
trackMetaDataVisible |=
- updateTextView(mResolutionView, Utils.getVideoDefinitionLevelString(
- context, videoDefinitionLevel));
+ updateTextView(
+ mResolutionView,
+ Utils.getVideoDefinitionLevelString(context, videoDefinitionLevel));
if (mDvrManager != null && mDvrManager.isProgramRecordable(program)) {
ScheduledRecording scheduledRecording =
@@ -616,7 +642,6 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
mDvrIndicator.setVisibility(View.GONE);
}
-
if (blockedRating == null) {
mBlockView.setVisibility(View.GONE);
updateTextView(mDescriptionView, program.getDescription());
@@ -655,8 +680,10 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
}
private String getBlockedDescription(TvContentRating blockedRating) {
- String name = mTvInputManagerHelper.getContentRatingsManager()
- .getDisplayNameForRating(blockedRating);
+ String name =
+ mTvInputManagerHelper
+ .getContentRatingsManager()
+ .getDisplayNameForRating(blockedRating);
if (TextUtils.isEmpty(name)) {
return mContext.getString(R.string.program_guide_content_locked);
} else {
@@ -691,8 +718,9 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
mIsInputLogoVisible = true;
TvInputInfo info = mTvInputManagerHelper.getTvInputInfo(mChannel.getInputId());
if (info != null) {
- LoadTvInputLogoTask task = new LoadTvInputLogoTask(
- itemView.getContext(), ImageCache.getInstance(), info);
+ LoadTvInputLogoTask task =
+ new LoadTvInputLogoTask(
+ itemView.getContext(), ImageCache.getInstance(), info);
ImageLoader.loadBitmap(createTvInputLogoLoadedCallback(info, this), task);
}
}
@@ -735,23 +763,28 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
mInputLogoView.setVisibility(View.VISIBLE);
}
- private void updateCriticScoreView(ProgramRowViewHolder holder, final long programId,
- CriticScore criticScore, View view) {
+ private void updateCriticScoreView(
+ ProgramRowViewHolder holder,
+ final long programId,
+ CriticScore criticScore,
+ View view) {
TextView criticScoreSource = (TextView) view.findViewById(R.id.critic_score_source);
TextView criticScoreText = (TextView) view.findViewById(R.id.critic_score_score);
ImageView criticScoreLogo = (ImageView) view.findViewById(R.id.critic_score_logo);
- //set the appropriate information in the views
+ // set the appropriate information in the views
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
- criticScoreSource.setText(Html.fromHtml(criticScore.source,
- Html.FROM_HTML_MODE_LEGACY));
+ criticScoreSource.setText(
+ Html.fromHtml(criticScore.source, Html.FROM_HTML_MODE_LEGACY));
} else {
criticScoreSource.setText(Html.fromHtml(criticScore.source));
}
criticScoreText.setText(criticScore.score);
criticScoreSource.setVisibility(View.VISIBLE);
criticScoreText.setVisibility(View.VISIBLE);
- ImageLoader.loadBitmap(mContext, criticScore.logoUrl,
+ ImageLoader.loadBitmap(
+ mContext,
+ criticScore.logoUrl,
createCriticScoreLogoCallback(holder, programId, criticScoreLogo));
}
@@ -768,7 +801,8 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
return new ImageLoaderCallback<ProgramRowViewHolder>(holder) {
@Override
public void onBitmapLoaded(ProgramRowViewHolder holder, @Nullable Bitmap logoImage) {
- if (logoImage == null || holder.mSelectedEntry == null
+ if (logoImage == null
+ || holder.mSelectedEntry == null
|| holder.mSelectedEntry.program == null
|| holder.mSelectedEntry.program.getId() != programId) {
logoView.setVisibility(View.GONE);
@@ -785,7 +819,8 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
return new ImageLoaderCallback<ProgramRowViewHolder>(holder) {
@Override
public void onBitmapLoaded(ProgramRowViewHolder holder, @Nullable Bitmap posterArt) {
- if (posterArt == null || holder.mSelectedEntry == null
+ if (posterArt == null
+ || holder.mSelectedEntry == null
|| holder.mSelectedEntry.program == null) {
return;
}
@@ -803,7 +838,8 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
return new ImageLoaderCallback<ProgramRowViewHolder>(holder) {
@Override
public void onBitmapLoaded(ProgramRowViewHolder holder, @Nullable Bitmap logo) {
- if (logo == null || holder.mChannel == null
+ if (logo == null
+ || holder.mChannel == null
|| holder.mChannel.getId() != channelId) {
return;
}
@@ -817,8 +853,9 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
return new ImageLoaderCallback<ProgramRowViewHolder>(holder) {
@Override
public void onBitmapLoaded(ProgramRowViewHolder holder, @Nullable Bitmap logo) {
- if (logo != null && holder.mChannel != null && info.getId()
- .equals(holder.mChannel.getInputId())) {
+ if (logo != null
+ && holder.mChannel != null
+ && info.getId().equals(holder.mChannel.getInputId())) {
holder.updateInputLogoInternal(logo);
}
}
diff --git a/src/com/android/tv/guide/TimeListAdapter.java b/src/com/android/tv/guide/TimeListAdapter.java
index d9e96a40..9c10c952 100644
--- a/src/com/android/tv/guide/TimeListAdapter.java
+++ b/src/com/android/tv/guide/TimeListAdapter.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.text.format.DateFormat;
@@ -24,17 +23,15 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
-
import com.android.tv.R;
import com.android.tv.util.Utils;
-
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
/**
- * Adapts the time range from {@link ProgramManager} to the timeline header row of the program
- * guide table.
+ * Adapts the time range from {@link ProgramManager} to the timeline header row of the program guide
+ * table.
*/
class TimeListAdapter extends RecyclerView.Adapter<TimeListAdapter.TimeViewHolder> {
private static final long TIME_UNIT_MS = TimeUnit.MINUTES.toMillis(30);
@@ -53,8 +50,10 @@ class TimeListAdapter extends RecyclerView.Adapter<TimeListAdapter.TimeViewHolde
TimeListAdapter(Resources res) {
if (sRowHeaderOverlapping == 0) {
- sRowHeaderOverlapping = Math.abs(res.getDimensionPixelOffset(
- R.dimen.program_guide_table_header_row_overlap));
+ sRowHeaderOverlapping =
+ Math.abs(
+ res.getDimensionPixelOffset(
+ R.dimen.program_guide_table_header_row_overlap));
}
Locale locale = res.getConfiguration().locale;
mTimePatternSameDay = DateFormat.getBestDateTimePattern(locale, TIME_PATTERN_SAME_DAY);
diff --git a/src/com/android/tv/guide/TimelineGridView.java b/src/com/android/tv/guide/TimelineGridView.java
index 8af1c8a3..c4922b75 100644
--- a/src/com/android/tv/guide/TimelineGridView.java
+++ b/src/com/android/tv/guide/TimelineGridView.java
@@ -34,14 +34,15 @@ public class TimelineGridView extends RecyclerView {
public TimelineGridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) {
- @Override
- public boolean onRequestChildFocus(RecyclerView parent, State state, View child,
- View focused) {
- // This disables the default scroll behavior for focus movement.
- return true;
- }
- });
+ setLayoutManager(
+ new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) {
+ @Override
+ public boolean onRequestChildFocus(
+ RecyclerView parent, State state, View child, View focused) {
+ // This disables the default scroll behavior for focus movement.
+ return true;
+ }
+ });
// RecyclerView is always focusable, however this is not desirable for us, so disable.
// See b/18863217 (ag/634046) for reasons to why RecyclerView is focusable.
diff --git a/src/com/android/tv/guide/TimelineRow.java b/src/com/android/tv/guide/TimelineRow.java
index 3f0c8678..b6a10ab1 100644
--- a/src/com/android/tv/guide/TimelineRow.java
+++ b/src/com/android/tv/guide/TimelineRow.java
@@ -40,19 +40,16 @@ public class TimelineRow extends TimelineGridView {
getLayoutManager().scrollToPosition(0);
}
- /**
- * Returns the current scroll position
- */
+ /** Returns the current scroll position */
public int getScrollOffset() {
return Math.abs(mScrollPosition);
}
- /**
- * Scrolls horizontally to the given position.
- */
+ /** Scrolls horizontally to the given position. */
public void scrollTo(int scrollOffset, boolean smoothScroll) {
- int dx = (scrollOffset - getScrollOffset())
- * (getLayoutDirection() == LAYOUT_DIRECTION_LTR ? 1 : -1);
+ int dx =
+ (scrollOffset - getScrollOffset())
+ * (getLayoutDirection() == LAYOUT_DIRECTION_LTR ? 1 : -1);
if (smoothScroll) {
smoothScrollBy(dx, 0);
} else {
diff --git a/src/com/android/tv/license/LicenseDialogFragment.java b/src/com/android/tv/license/LicenseDialogFragment.java
index b0e09776..74b99dcd 100644
--- a/src/com/android/tv/license/LicenseDialogFragment.java
+++ b/src/com/android/tv/license/LicenseDialogFragment.java
@@ -26,7 +26,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.TextView;
-
import com.android.tv.R;
import com.android.tv.dialog.SafeDismissDialogFragment;
diff --git a/src/com/android/tv/license/LicenseSideFragment.java b/src/com/android/tv/license/LicenseSideFragment.java
index fd92467c..ff99df2a 100644
--- a/src/com/android/tv/license/LicenseSideFragment.java
+++ b/src/com/android/tv/license/LicenseSideFragment.java
@@ -17,11 +17,9 @@
package com.android.tv.license;
import android.content.Context;
-
import com.android.tv.R;
import com.android.tv.ui.sidepanel.ActionItem;
import com.android.tv.ui.sidepanel.SideFragment;
-
import java.util.ArrayList;
import java.util.List;
diff --git a/src/com/android/tv/license/LicenseUtils.java b/src/com/android/tv/license/LicenseUtils.java
index cf3fe751..1bae0c6a 100644
--- a/src/com/android/tv/license/LicenseUtils.java
+++ b/src/com/android/tv/license/LicenseUtils.java
@@ -17,22 +17,14 @@
package com.android.tv.license;
import android.content.res.AssetManager;
-
-import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-/**
- * Utilities for showing open source licenses.
- */
+/** Utilities for showing open source licenses. */
public final class LicenseUtils {
- public final static String RATING_SOURCE_FILE =
- "file:///android_asset/rating_sources.html";
+ public static final String RATING_SOURCE_FILE = "file:///android_asset/rating_sources.html";
-
- /**
- * Checks if the rating_attribution.html asset is include in the apk.
- */
+ /** Checks if the rating_attribution.html asset is include in the apk. */
public static boolean hasRatingAttribution(AssetManager am) {
try (InputStream is = am.open("rating_sources.html")) {
return true;
@@ -41,6 +33,5 @@ public final class LicenseUtils {
}
}
- private LicenseUtils() {
- }
+ private LicenseUtils() {}
}
diff --git a/src/com/android/tv/license/Licenses.java b/src/com/android/tv/license/Licenses.java
index 4b8a7ffc..1da91d7a 100644
--- a/src/com/android/tv/license/Licenses.java
+++ b/src/com/android/tv/license/Licenses.java
@@ -18,10 +18,8 @@ package com.android.tv.license;
import android.content.Context;
import android.support.annotation.RawRes;
-
import com.android.tv.R;
import com.android.tv.common.SoftPreconditions;
-
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -36,6 +34,7 @@ import java.util.Collections;
public final class Licenses {
public static final String TAG = "Licenses";
+
public static boolean hasLicenses(Context context) {
return !getTextFromResource(
context.getApplicationContext(), R.raw.third_party_license_metadata, 0, -1)
diff --git a/src/com/android/tv/livetv/receiver/GlobalKeyReceiver.java b/src/com/android/tv/livetv/receiver/GlobalKeyReceiver.java
new file mode 100644
index 00000000..80ea72f4
--- /dev/null
+++ b/src/com/android/tv/livetv/receiver/GlobalKeyReceiver.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 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.livetv.receiver;
+
+import com.android.tv.receiver.AbstractGlobalKeyReceiver;
+
+/**
+ * Global Key Receiver for LiveTv.
+ *
+ * <p>This is an exported receiver and the name can not change.
+ * Partners that modify LiveTv source should rename this class.
+ */
+public final class GlobalKeyReceiver extends AbstractGlobalKeyReceiver {}
diff --git a/src/com/android/tv/menu/ActionCardView.java b/src/com/android/tv/menu/ActionCardView.java
index 2fd70bfb..3ecd5f5c 100644
--- a/src/com/android/tv/menu/ActionCardView.java
+++ b/src/com/android/tv/menu/ActionCardView.java
@@ -22,12 +22,9 @@ import android.util.Log;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
-
import com.android.tv.R;
-/**
- * A view to render an item of TV options.
- */
+/** A view to render an item of TV options. */
public class ActionCardView extends RelativeLayout implements ItemListRowView.CardView<MenuAction> {
private static final String TAG = MenuView.TAG;
private static final boolean DEBUG = MenuView.DEBUG;
@@ -97,5 +94,5 @@ public class ActionCardView extends RelativeLayout implements ItemListRowView.Ca
}
@Override
- public void onRecycled() { }
+ public void onRecycled() {}
}
diff --git a/src/com/android/tv/menu/AppLinkCardView.java b/src/com/android/tv/menu/AppLinkCardView.java
index 94ccd37f..fd93c314 100644
--- a/src/com/android/tv/menu/AppLinkCardView.java
+++ b/src/com/android/tv/menu/AppLinkCardView.java
@@ -33,19 +33,15 @@ import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
-
import com.android.tv.MainActivity;
import com.android.tv.R;
-import com.android.tv.data.Channel;
-import com.android.tv.util.BitmapUtils;
-import com.android.tv.util.ImageLoader;
+import com.android.tv.data.api.Channel;
import com.android.tv.util.TvInputManagerHelper;
-
+import com.android.tv.util.images.BitmapUtils;
+import com.android.tv.util.images.ImageLoader;
import java.util.Objects;
-/**
- * A view to render an app link card.
- */
+/** A view to render an app link card. */
public class AppLinkCardView extends BaseCardView<ChannelsRowItem> {
private static final String TAG = MenuView.TAG;
private static final boolean DEBUG = MenuView.DEBUG;
@@ -88,9 +84,7 @@ public class AppLinkCardView extends BaseCardView<ChannelsRowItem> {
mDefaultDrawable = getResources().getDrawable(R.drawable.ic_recent_thumbnail_default, null);
}
- /**
- * Returns the intent which will be started once this card is clicked.
- */
+ /** Returns the intent which will be started once this card is clicked. */
public Intent getIntent() {
return mIntent;
}
@@ -100,13 +94,20 @@ public class AppLinkCardView extends BaseCardView<ChannelsRowItem> {
Channel newChannel = item.getChannel();
boolean channelChanged = !Objects.equals(mChannel, newChannel);
String previousPosterArtUri = mChannel == null ? null : mChannel.getAppLinkPosterArtUri();
- boolean posterArtChanged = previousPosterArtUri == null
- || newChannel.getAppLinkPosterArtUri() == null
- || !TextUtils.equals(previousPosterArtUri, newChannel.getAppLinkPosterArtUri());
+ boolean posterArtChanged =
+ previousPosterArtUri == null
+ || newChannel.getAppLinkPosterArtUri() == null
+ || !TextUtils.equals(
+ previousPosterArtUri, newChannel.getAppLinkPosterArtUri());
mChannel = newChannel;
if (DEBUG) {
- Log.d(TAG, "onBind(channelName=" + mChannel.getDisplayName() + ", selected=" + selected
- + ")");
+ Log.d(
+ TAG,
+ "onBind(channelName="
+ + mChannel.getDisplayName()
+ + ", selected="
+ + selected
+ + ")");
}
ApplicationInfo appInfo = mTvInputManagerHelper.getTvInputAppInfo(mChannel.getInputId());
if (channelChanged) {
@@ -120,8 +121,8 @@ public class AppLinkCardView extends BaseCardView<ChannelsRowItem> {
mAppInfoView.setVisibility(VISIBLE);
mAppInfoView.setCompoundDrawablePadding(mIconPadding);
mAppInfoView.setCompoundDrawablesRelative(null, null, null, null);
- appLabel = mTvInputManagerHelper
- .getTvInputApplicationLabel(mChannel.getInputId());
+ appLabel =
+ mTvInputManagerHelper.getTvInputApplicationLabel(mChannel.getInputId());
if (appLabel != null) {
mAppInfoView.setText(appLabel);
} else {
@@ -140,7 +141,7 @@ public class AppLinkCardView extends BaseCardView<ChannelsRowItem> {
protected void onPostExecute(CharSequence appLabel) {
mTvInputManagerHelper.setTvInputApplicationLabel(
mLoadTvInputId, appLabel);
- if (mLoadTvInputId != mChannel.getInputId()
+ if (mLoadTvInputId.equals(mChannel.getInputId())
|| !isAttachedToWindow()) {
return;
}
@@ -149,12 +150,17 @@ public class AppLinkCardView extends BaseCardView<ChannelsRowItem> {
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
if (!TextUtils.isEmpty(mChannel.getAppLinkIconUri())) {
- mChannel.loadBitmap(getContext(), Channel.LOAD_IMAGE_TYPE_APP_LINK_ICON,
- mIconWidth, mIconHeight, createChannelLogoCallback(
+ mChannel.loadBitmap(
+ getContext(),
+ Channel.LOAD_IMAGE_TYPE_APP_LINK_ICON,
+ mIconWidth,
+ mIconHeight,
+ createChannelLogoCallback(
this, mChannel, Channel.LOAD_IMAGE_TYPE_APP_LINK_ICON));
} else if (appInfo.icon != 0) {
- Drawable appIcon = mTvInputManagerHelper
- .getTvInputApplicationIcon(mChannel.getInputId());
+ Drawable appIcon =
+ mTvInputManagerHelper.getTvInputApplicationIcon(
+ mChannel.getInputId());
if (appIcon != null) {
BitmapUtils.setColorFilterToDrawable(mIconColorFilter, appIcon);
appIcon.setBounds(0, 0, mIconWidth, mIconHeight);
@@ -186,11 +192,14 @@ public class AppLinkCardView extends BaseCardView<ChannelsRowItem> {
}
break;
case Channel.APP_LINK_TYPE_APP:
- appLabel = mTvInputManagerHelper
- .getTvInputApplicationLabel(mChannel.getInputId());
+ appLabel =
+ mTvInputManagerHelper.getTvInputApplicationLabel(mChannel.getInputId());
if (appLabel != null) {
- setText(getContext()
- .getString(R.string.channels_item_app_link_app_launcher, appLabel));
+ setText(
+ getContext()
+ .getString(
+ R.string.channels_item_app_link_app_launcher,
+ appLabel));
} else {
new AsyncTask<Void, Void, CharSequence>() {
private final String mLoadTvInputId = mChannel.getInputId();
@@ -206,15 +215,17 @@ public class AppLinkCardView extends BaseCardView<ChannelsRowItem> {
@Override
protected void onPostExecute(CharSequence appLabel) {
mTvInputManagerHelper.setTvInputApplicationLabel(
- mLoadTvInputId, appLabel);
+ mLoadTvInputId, appLabel);
if (!mLoadTvInputId.equals(mChannel.getInputId())
- || !isAttachedToWindow()) {
+ || !isAttachedToWindow()) {
return;
}
- setText(getContext()
- .getString(
- R.string.channels_item_app_link_app_launcher,
- appLabel));
+ setText(
+ getContext()
+ .getString(
+ R.string
+ .channels_item_app_link_app_launcher,
+ appLabel));
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@@ -235,9 +246,13 @@ public class AppLinkCardView extends BaseCardView<ChannelsRowItem> {
mImageView.setImageDrawable(mDefaultDrawable);
mImageView.setForeground(null);
if (!TextUtils.isEmpty(mChannel.getAppLinkPosterArtUri())) {
- mChannel.loadBitmap(getContext(), Channel.LOAD_IMAGE_TYPE_APP_LINK_POSTER_ART,
- mCardImageWidth, mCardImageHeight, createChannelLogoCallback(this, mChannel,
- Channel.LOAD_IMAGE_TYPE_APP_LINK_POSTER_ART));
+ mChannel.loadBitmap(
+ getContext(),
+ Channel.LOAD_IMAGE_TYPE_APP_LINK_POSTER_ART,
+ mCardImageWidth,
+ mCardImageHeight,
+ createChannelLogoCallback(
+ this, mChannel, Channel.LOAD_IMAGE_TYPE_APP_LINK_POSTER_ART));
} else {
setCardImageWithBanner(appInfo);
}
@@ -265,10 +280,12 @@ public class AppLinkCardView extends BaseCardView<ChannelsRowItem> {
if (bitmap != null) {
drawable = new BitmapDrawable(getResources(), bitmap);
if (bitmap.getWidth() > bitmap.getHeight()) {
- drawable.setBounds(0, 0, mIconWidth,
- mIconWidth * bitmap.getHeight() / bitmap.getWidth());
+ drawable.setBounds(
+ 0, 0, mIconWidth, mIconWidth * bitmap.getHeight() / bitmap.getWidth());
} else {
- drawable.setBounds(0, 0,
+ drawable.setBounds(
+ 0,
+ 0,
mIconHeight * bitmap.getWidth() / bitmap.getHeight(),
mIconHeight);
}
@@ -303,6 +320,7 @@ public class AppLinkCardView extends BaseCardView<ChannelsRowItem> {
private void setCardImageWithBanner(ApplicationInfo appInfo) {
new AsyncTask<Void, Void, Drawable>() {
private String mLoadTvInputId = mChannel.getInputId();
+
@Override
protected Drawable doInBackground(Void... params) {
Drawable banner = null;
@@ -321,7 +339,7 @@ public class AppLinkCardView extends BaseCardView<ChannelsRowItem> {
@Override
protected void onPostExecute(Drawable banner) {
- if (mLoadTvInputId != mChannel.getInputId() || !isAttachedToWindow()) {
+ if (mLoadTvInputId.equals(mChannel.getInputId()) || !isAttachedToWindow()) {
return;
}
if (banner != null) {
@@ -341,6 +359,7 @@ public class AppLinkCardView extends BaseCardView<ChannelsRowItem> {
} else {
new AsyncTask<Void, Void, Drawable>() {
private final String mLoadTvInputId = mChannel.getInputId();
+
@Override
protected Drawable doInBackground(Void... params) {
Drawable banner = null;
@@ -373,8 +392,8 @@ public class AppLinkCardView extends BaseCardView<ChannelsRowItem> {
mImageView.setImageDrawable(mDefaultDrawable);
mImageView.setBackgroundResource(R.color.channel_card);
} else {
- Bitmap bitmap = Bitmap.createBitmap(
- mCardImageWidth, mCardImageHeight, Bitmap.Config.ARGB_8888);
+ Bitmap bitmap =
+ Bitmap.createBitmap(mCardImageWidth, mCardImageHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
banner.setBounds(0, 0, mCardImageWidth, mCardImageHeight);
banner.draw(canvas);
@@ -387,12 +406,19 @@ public class AppLinkCardView extends BaseCardView<ChannelsRowItem> {
}
private void extractAndSetMetaViewBackgroundColor(Bitmap bitmap) {
- new Palette.Builder(bitmap).generate(new Palette.PaletteAsyncListener() {
- @Override
- public void onGenerated(Palette palette) {
- mMetaViewHolder.setBackgroundColor(palette.getDarkVibrantColor(
- getResources().getColor(R.color.channel_card_meta_background, null)));
- }
- });
+ new Palette.Builder(bitmap)
+ .generate(
+ new Palette.PaletteAsyncListener() {
+ @Override
+ public void onGenerated(Palette palette) {
+ mMetaViewHolder.setBackgroundColor(
+ palette.getDarkVibrantColor(
+ getResources()
+ .getColor(
+ R.color
+ .channel_card_meta_background,
+ null)));
+ }
+ });
}
}
diff --git a/src/com/android/tv/menu/BaseCardView.java b/src/com/android/tv/menu/BaseCardView.java
index 4c5e6c78..3a94ebbf 100644
--- a/src/com/android/tv/menu/BaseCardView.java
+++ b/src/com/android/tv/menu/BaseCardView.java
@@ -29,12 +29,9 @@ import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.widget.LinearLayout;
import android.widget.TextView;
-
import com.android.tv.R;
-/**
- * A base class to render a card.
- */
+/** A base class to render a card. */
public abstract class BaseCardView<T> extends LinearLayout implements ItemListRowView.CardView<T> {
private static final float SCALE_FACTOR_0F = 0f;
private static final float SCALE_FACTOR_1F = 1f;
@@ -49,10 +46,8 @@ public abstract class BaseCardView<T> extends LinearLayout implements ItemListRo
private final float mExtendedCardHeight;
private final float mTextViewHeight;
private final float mExtendedTextViewHeight;
- @Nullable
- private TextView mTextView;
- @Nullable
- private TextView mTextViewFocused;
+ @Nullable private TextView mTextView;
+ @Nullable private TextView mTextViewFocused;
private final int mCardImageWidth;
private final float mCardHeight;
private boolean mSelected;
@@ -74,27 +69,32 @@ public abstract class BaseCardView<T> extends LinearLayout implements ItemListRo
setClipToOutline(true);
mFocusAnimDuration = getResources().getInteger(R.integer.menu_focus_anim_duration);
- mFocusTranslationZ = getResources().getDimension(R.dimen.channel_card_elevation_focused)
- - getResources().getDimension(R.dimen.card_elevation_normal);
- mVerticalCardMargin = 2 * (
- getResources().getDimensionPixelOffset(R.dimen.menu_list_padding_top)
- + getResources().getDimensionPixelOffset(R.dimen.menu_list_margin_top));
+ mFocusTranslationZ =
+ getResources().getDimension(R.dimen.channel_card_elevation_focused)
+ - getResources().getDimension(R.dimen.card_elevation_normal);
+ mVerticalCardMargin =
+ 2
+ * (getResources().getDimensionPixelOffset(R.dimen.menu_list_padding_top)
+ + getResources()
+ .getDimensionPixelOffset(R.dimen.menu_list_margin_top));
// Ensure the same elevation and focus animation for all subclasses.
setElevation(getResources().getDimension(R.dimen.card_elevation_normal));
mCardCornerRadius = getResources().getDimensionPixelSize(R.dimen.channel_card_round_radius);
- setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCardCornerRadius);
- }
- });
+ setOutlineProvider(
+ new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(
+ 0, 0, view.getWidth(), view.getHeight(), mCardCornerRadius);
+ }
+ });
mCardImageWidth = getResources().getDimensionPixelSize(R.dimen.card_image_layout_width);
mCardHeight = getResources().getDimensionPixelSize(R.dimen.card_layout_height);
- mExtendedCardHeight = getResources().getDimensionPixelSize(
- R.dimen.card_layout_height_extended);
+ mExtendedCardHeight =
+ getResources().getDimensionPixelSize(R.dimen.card_layout_height_extended);
mTextViewHeight = getResources().getDimensionPixelSize(R.dimen.card_meta_layout_height);
- mExtendedTextViewHeight = getResources().getDimensionPixelOffset(
- R.dimen.card_meta_layout_height_extended);
+ mExtendedTextViewHeight =
+ getResources().getDimensionPixelOffset(R.dimen.card_meta_layout_height_extended);
}
@Override
@@ -104,16 +104,14 @@ public abstract class BaseCardView<T> extends LinearLayout implements ItemListRo
mTextViewFocused = (TextView) findViewById(R.id.card_text_focused);
}
- /**
- * Called when the view is displayed.
- */
+ /** Called when the view is displayed. */
@Override
public void onBind(T item, boolean selected) {
setFocusAnimatedValue(selected ? SCALE_FACTOR_1F : SCALE_FACTOR_0F);
}
@Override
- public void onRecycled() { }
+ public void onRecycled() {}
@Override
public void onSelected() {
@@ -137,9 +135,7 @@ public abstract class BaseCardView<T> extends LinearLayout implements ItemListRo
}
}
- /**
- * Sets text of this card view.
- */
+ /** Sets text of this card view. */
public void setText(int resId) {
if (mTextResId != resId) {
mTextResId = resId;
@@ -155,9 +151,7 @@ public abstract class BaseCardView<T> extends LinearLayout implements ItemListRo
}
}
- /**
- * Sets text of this card view.
- */
+ /** Sets text of this card view. */
public void setText(String text) {
if (!TextUtils.equals(text, mTextString)) {
mTextString = text;
@@ -176,8 +170,8 @@ public abstract class BaseCardView<T> extends LinearLayout implements ItemListRo
private void onTextViewUpdated() {
if (mTextView != null && mTextViewFocused != null) {
mTextViewFocused.measure(
- MeasureSpec.makeMeasureSpec(mCardImageWidth, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ MeasureSpec.makeMeasureSpec(mCardImageWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
mExtendViewOnFocus = mTextViewFocused.getLineCount() > 1;
if (mExtendViewOnFocus) {
setTextViewFocusedAlpha(mSelected ? 1f : 0f);
@@ -188,9 +182,7 @@ public abstract class BaseCardView<T> extends LinearLayout implements ItemListRo
setFocusAnimatedValue(mSelected ? SCALE_FACTOR_1F : SCALE_FACTOR_0F);
}
- /**
- * Enables or disables text view of this card view.
- */
+ /** Enables or disables text view of this card view. */
public void setTextViewEnabled(boolean enabled) {
if (mTextViewFocused != null) {
mTextViewFocused.setEnabled(enabled);
@@ -200,38 +192,38 @@ public abstract class BaseCardView<T> extends LinearLayout implements ItemListRo
}
}
- /**
- * Called when the focus animation started.
- */
+ /** Called when the focus animation started. */
protected void onFocusAnimationStart(boolean selected) {
if (mExtendViewOnFocus) {
setTextViewFocusedAlpha(selected ? 1f : 0f);
}
}
- /**
- * Called when the focus animation ended.
- */
+ /** Called when the focus animation ended. */
protected void onFocusAnimationEnd(boolean selected) {
// do nothing.
}
/**
- * Called when the view is bound, or while focus animation is running with a value
- * between {@code SCALE_FACTOR_0F} and {@code SCALE_FACTOR_1F}.
+ * Called when the view is bound, or while focus animation is running with a value between
+ * {@code SCALE_FACTOR_0F} and {@code SCALE_FACTOR_1F}.
*/
protected void onSetFocusAnimatedValue(float animatedValue) {
- float cardViewHeight = (mExtendViewOnFocus && isFocused())
- ? mExtendedCardHeight : mCardHeight;
+ float cardViewHeight =
+ (mExtendViewOnFocus && isFocused()) ? mExtendedCardHeight : mCardHeight;
float scale = 1f + (mVerticalCardMargin / cardViewHeight) * animatedValue;
setScaleX(scale);
setScaleY(scale);
setTranslationZ(mFocusTranslationZ * animatedValue);
if (mTextView != null && mTextViewFocused != null) {
ViewGroup.LayoutParams params = mTextView.getLayoutParams();
- int height = mExtendViewOnFocus ? Math.round(mTextViewHeight
- + (mExtendedTextViewHeight - mTextViewHeight) * animatedValue)
- : (int) mTextViewHeight;
+ int height =
+ mExtendViewOnFocus
+ ? Math.round(
+ mTextViewHeight
+ + (mExtendedTextViewHeight - mTextViewHeight)
+ * animatedValue)
+ : (int) mTextViewHeight;
if (height != params.height) {
params.height = height;
setTextViewLayoutParams(params);
@@ -252,25 +244,27 @@ public abstract class BaseCardView<T> extends LinearLayout implements ItemListRo
final boolean selected = targetAnimatedValue == SCALE_FACTOR_1F;
mFocusAnimator = ValueAnimator.ofFloat(mFocusAnimatedValue, targetAnimatedValue);
mFocusAnimator.setDuration(mFocusAnimDuration);
- mFocusAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- setHasTransientState(true);
- onFocusAnimationStart(selected);
- }
+ mFocusAnimator.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ setHasTransientState(true);
+ onFocusAnimationStart(selected);
+ }
- @Override
- public void onAnimationEnd(Animator animation) {
- setHasTransientState(false);
- onFocusAnimationEnd(selected);
- }
- });
- mFocusAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- setFocusAnimatedValue((Float) animation.getAnimatedValue());
- }
- });
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ setHasTransientState(false);
+ onFocusAnimationEnd(selected);
+ }
+ });
+ mFocusAnimator.addUpdateListener(
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ setFocusAnimatedValue((Float) animation.getAnimatedValue());
+ }
+ });
mFocusAnimator.start();
}
diff --git a/src/com/android/tv/menu/ChannelCardView.java b/src/com/android/tv/menu/ChannelCardView.java
index 2ecb6af7..76056ee4 100644
--- a/src/com/android/tv/menu/ChannelCardView.java
+++ b/src/com/android/tv/menu/ChannelCardView.java
@@ -26,19 +26,15 @@ import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
-
import com.android.tv.MainActivity;
import com.android.tv.R;
-import com.android.tv.data.Channel;
import com.android.tv.data.Program;
+import com.android.tv.data.api.Channel;
import com.android.tv.parental.ParentalControlSettings;
-import com.android.tv.util.ImageLoader;
-
+import com.android.tv.util.images.ImageLoader;
import java.util.Objects;
-/**
- * A view to render channel card.
- */
+/** A view to render channel card. */
public class ChannelCardView extends BaseCardView<ChannelsRowItem> {
private static final String TAG = MenuView.TAG;
private static final boolean DEBUG = MenuView.DEBUG;
@@ -81,8 +77,13 @@ public class ChannelCardView extends BaseCardView<ChannelsRowItem> {
@Override
public void onBind(ChannelsRowItem item, boolean selected) {
if (DEBUG) {
- Log.d(TAG, "onBind(channelName=" + item.getChannel().getDisplayName() + ", selected="
- + selected + ")");
+ Log.d(
+ TAG,
+ "onBind(channelName="
+ + item.getChannel().getDisplayName()
+ + ", selected="
+ + selected
+ + ")");
}
updateChannel(item);
updateProgram();
@@ -146,7 +147,8 @@ public class ChannelCardView extends BaseCardView<ChannelsRowItem> {
return new ImageLoader.ImageLoaderCallback<ChannelCardView>(cardView) {
@Override
public void onBitmapLoaded(ChannelCardView cardView, @Nullable Bitmap posterArt) {
- if (posterArt == null || cardView.mProgram == null
+ if (posterArt == null
+ || cardView.mProgram == null
|| program.getChannelId() != cardView.mProgram.getChannelId()
|| program.getChannelId() != cardView.mChannel.getId()) {
return;
@@ -160,7 +162,10 @@ public class ChannelCardView extends BaseCardView<ChannelsRowItem> {
if (!TextUtils.equals(mPosterArtUri, posterArtUri)) {
mPosterArtUri = posterArtUri;
if (posterArtUri == null
- || !mProgram.loadPosterArt(getContext(), mCardImageWidth, mCardImageHeight,
+ || !mProgram.loadPosterArt(
+ getContext(),
+ mCardImageWidth,
+ mCardImageHeight,
createProgramPosterArtCallback(this, mProgram))) {
mImageView.setImageResource(R.drawable.ic_recent_thumbnail_default);
mImageView.setForeground(null);
@@ -172,4 +177,4 @@ public class ChannelCardView extends BaseCardView<ChannelsRowItem> {
mImageView.setImageBitmap(posterArt);
mImageView.setForeground(getContext().getDrawable(R.drawable.card_image_gradient));
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/menu/ChannelsPosterPrefetcher.java b/src/com/android/tv/menu/ChannelsPosterPrefetcher.java
index bc5d6cfb..9cecb9c0 100644
--- a/src/com/android/tv/menu/ChannelsPosterPrefetcher.java
+++ b/src/com/android/tv/menu/ChannelsPosterPrefetcher.java
@@ -23,24 +23,21 @@ import android.os.Message;
import android.support.annotation.MainThread;
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.ChannelImpl;
import com.android.tv.data.Program;
import com.android.tv.data.ProgramDataManager;
-
+import com.android.tv.data.api.Channel;
import java.util.List;
-/**
- * A poster image prefetcher to show the program poster art in the Channels row faster.
- */
+/** A poster image prefetcher to show the program poster art in the Channels row faster. */
public class ChannelsPosterPrefetcher {
private static final String TAG = "PosterPrefetcher";
private static final boolean DEBUG = false;
private static final int MSG_PREFETCH_IMAGE = 1000;
- private static final int ONDEMAND_POSTER_PREFETCH_DELAY_MILLIS = 500; // 500 milliseconds
+ private static final int ONDEMAND_POSTER_PREFETCH_DELAY_MILLIS = 500; // 500 milliseconds
private final ProgramDataManager mProgramDataManager;
private final ChannelsRowAdapter mChannelsAdapter;
@@ -51,23 +48,19 @@ public class ChannelsPosterPrefetcher {
private boolean isCanceled;
- /**
- * Create {@link ChannelsPosterPrefetcher} object with given parameters.
- */
- public ChannelsPosterPrefetcher(Context context, ProgramDataManager programDataManager,
- ChannelsRowAdapter adapter) {
+ /** Create {@link ChannelsPosterPrefetcher} object with given parameters. */
+ public ChannelsPosterPrefetcher(
+ Context context, ProgramDataManager programDataManager, ChannelsRowAdapter adapter) {
mProgramDataManager = programDataManager;
mChannelsAdapter = adapter;
- mPosterArtWidth = context.getResources().getDimensionPixelSize(
- R.dimen.card_image_layout_width);
- mPosterArtHeight = context.getResources().getDimensionPixelSize(
- R.dimen.card_image_layout_height);
+ mPosterArtWidth =
+ context.getResources().getDimensionPixelSize(R.dimen.card_image_layout_width);
+ mPosterArtHeight =
+ context.getResources().getDimensionPixelSize(R.dimen.card_image_layout_height);
mContext = context.getApplicationContext();
}
- /**
- * Start prefetching of program poster art of recommendation.
- */
+ /** Start prefetching of program poster art of recommendation. */
public void prefetch() {
SoftPreconditions.checkState(!isCanceled, TAG, "Prefetch called after cancel was called.");
if (isCanceled) {
@@ -79,13 +72,11 @@ public class ChannelsPosterPrefetcher {
* prefetch the intermediate channels. So ignore previous schedule.
*/
mHandler.removeMessages(MSG_PREFETCH_IMAGE);
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREFETCH_IMAGE),
- ONDEMAND_POSTER_PREFETCH_DELAY_MILLIS);
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(MSG_PREFETCH_IMAGE), ONDEMAND_POSTER_PREFETCH_DELAY_MILLIS);
}
- /**
- * Cancels pending and current prefetch requests.
- */
+ /** Cancels pending and current prefetch requests. */
public void cancel() {
isCanceled = true;
mHandler.removeCallbacksAndMessages(null);
@@ -104,11 +95,14 @@ public class ChannelsPosterPrefetcher {
return;
}
Channel channel = item.getChannel();
- if (!Channel.isValid(channel)) {
+ if (!ChannelImpl.isValid(channel)) {
continue;
}
- channel.prefetchImage(mContext, Channel.LOAD_IMAGE_TYPE_CHANNEL_LOGO,
- mPosterArtWidth, mPosterArtHeight);
+ channel.prefetchImage(
+ mContext,
+ Channel.LOAD_IMAGE_TYPE_CHANNEL_LOGO,
+ mPosterArtWidth,
+ mPosterArtHeight);
Program program = mProgramDataManager.getCurrentProgram(channel.getId());
if (program != null) {
program.prefetchPosterArt(mContext, mPosterArtWidth, mPosterArtHeight);
@@ -116,8 +110,11 @@ public class ChannelsPosterPrefetcher {
}
}
if (DEBUG) {
- Log.d(TAG, "doPrefetchImages() finished. ImageLoader may still have async tasks for "
- + "channels " + items);
+ Log.d(
+ TAG,
+ "doPrefetchImages() finished. ImageLoader may still have async tasks for "
+ + "channels "
+ + items);
}
}
diff --git a/src/com/android/tv/menu/ChannelsRow.java b/src/com/android/tv/menu/ChannelsRow.java
index 490d73de..7d03bf2b 100644
--- a/src/com/android/tv/menu/ChannelsRow.java
+++ b/src/com/android/tv/menu/ChannelsRow.java
@@ -17,7 +17,6 @@
package com.android.tv.menu;
import android.content.Context;
-
import com.android.tv.R;
import com.android.tv.data.ProgramDataManager;
import com.android.tv.recommendation.RecentChannelEvaluator;
@@ -26,13 +25,9 @@ import com.android.tv.recommendation.Recommender;
public class ChannelsRow extends ItemListRow {
public static final String ID = ChannelsRow.class.getName();
- /**
- * Minimum count for recent channels.
- */
+ /** Minimum count for recent channels. */
public static final int MIN_COUNT_FOR_RECENT_CHANNELS = 5;
- /**
- * Maximum count for recent channels.
- */
+ /** Maximum count for recent channels. */
public static final int MAX_COUNT_FOR_RECENT_CHANNELS = 10;
private Recommender mTvRecommendation;
@@ -41,25 +36,33 @@ public class ChannelsRow extends ItemListRow {
public ChannelsRow(Context context, Menu menu, ProgramDataManager programDataManager) {
super(context, menu, R.string.menu_title_channels, R.dimen.card_layout_height, null);
- mTvRecommendation = new Recommender(getContext(), new Recommender.Listener() {
- @Override
- public void onRecommenderReady() {
- mChannelsAdapter.update();
- mChannelsPosterPrefetcher.prefetch();
- }
+ mTvRecommendation =
+ new Recommender(
+ getContext(),
+ new Recommender.Listener() {
+ @Override
+ public void onRecommenderReady() {
+ mChannelsAdapter.update();
+ mChannelsPosterPrefetcher.prefetch();
+ }
- @Override
- public void onRecommendationChanged() {
- mChannelsAdapter.update();
- mChannelsPosterPrefetcher.prefetch();
- }
- }, true);
+ @Override
+ public void onRecommendationChanged() {
+ mChannelsAdapter.update();
+ mChannelsPosterPrefetcher.prefetch();
+ }
+ },
+ true);
mTvRecommendation.registerEvaluator(new RecentChannelEvaluator());
- mChannelsAdapter = new ChannelsRowAdapter(context, mTvRecommendation,
- MIN_COUNT_FOR_RECENT_CHANNELS, MAX_COUNT_FOR_RECENT_CHANNELS);
+ mChannelsAdapter =
+ new ChannelsRowAdapter(
+ context,
+ mTvRecommendation,
+ MIN_COUNT_FOR_RECENT_CHANNELS,
+ MAX_COUNT_FOR_RECENT_CHANNELS);
setAdapter(mChannelsAdapter);
- mChannelsPosterPrefetcher = new ChannelsPosterPrefetcher(context, programDataManager,
- mChannelsAdapter);
+ mChannelsPosterPrefetcher =
+ new ChannelsPosterPrefetcher(context, programDataManager, mChannelsAdapter);
}
@Override
@@ -72,9 +75,7 @@ public class ChannelsRow extends ItemListRow {
mChannelsPosterPrefetcher.cancel();
}
- /**
- * Handle the update event of the recent channel.
- */
+ /** Handle the update event of the recent channel. */
@Override
public void onRecentChannelsChanged() {
mChannelsPosterPrefetcher.prefetch();
diff --git a/src/com/android/tv/menu/ChannelsRowAdapter.java b/src/com/android/tv/menu/ChannelsRowAdapter.java
index 7ff44ea6..8536ef1f 100644
--- a/src/com/android/tv/menu/ChannelsRowAdapter.java
+++ b/src/com/android/tv/menu/ChannelsRowAdapter.java
@@ -20,25 +20,20 @@ import android.content.Context;
import android.content.Intent;
import android.media.tv.TvInputInfo;
import android.view.View;
-
-import com.android.tv.ApplicationSingletons;
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.analytics.Tracker;
import com.android.tv.common.feature.CommonFeatures;
-import com.android.tv.data.Channel;
+import com.android.tv.data.ChannelImpl;
+import com.android.tv.data.api.Channel;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.recommendation.Recommender;
-import com.android.tv.util.SetupUtils;
import com.android.tv.util.TvInputManagerHelper;
-
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
-/**
- * An adapter of the Channels row.
- */
+/** An adapter of the Channels row. */
public class ChannelsRowAdapter extends ItemListRowView.ItemListAdapter<ChannelsRowItem> {
// There are four special cards: guide, setup, dvr, applink.
private static final int SIZE_OF_VIEW_TYPE = 5;
@@ -50,60 +45,66 @@ public class ChannelsRowAdapter extends ItemListRowView.ItemListAdapter<Channels
private final int mMaxCount;
private final int mMinCount;
- private final View.OnClickListener mGuideOnClickListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mTracker.sendMenuClicked(R.string.channels_item_program_guide);
- getMainActivity().getOverlayManager().showProgramGuide();
- }
- };
+ private final View.OnClickListener mGuideOnClickListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mTracker.sendMenuClicked(R.string.channels_item_program_guide);
+ getMainActivity().getOverlayManager().showProgramGuide();
+ }
+ };
- private final View.OnClickListener mSetupOnClickListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mTracker.sendMenuClicked(R.string.channels_item_setup);
- getMainActivity().getOverlayManager().showSetupFragment();
- }
- };
+ private final View.OnClickListener mSetupOnClickListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mTracker.sendMenuClicked(R.string.channels_item_setup);
+ getMainActivity().getOverlayManager().showSetupFragment();
+ }
+ };
- private final View.OnClickListener mDvrOnClickListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mTracker.sendMenuClicked(R.string.channels_item_dvr);
- getMainActivity().getOverlayManager().showDvrManager();
- }
- };
+ private final View.OnClickListener mDvrOnClickListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mTracker.sendMenuClicked(R.string.channels_item_dvr);
+ getMainActivity().getOverlayManager().showDvrManager();
+ }
+ };
- private final View.OnClickListener mAppLinkOnClickListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mTracker.sendMenuClicked(R.string.channels_item_app_link);
- Intent intent = ((AppLinkCardView) view).getIntent();
- if (intent != null) {
- getMainActivity().startActivitySafe(intent);
- }
- }
- };
+ private final View.OnClickListener mAppLinkOnClickListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mTracker.sendMenuClicked(R.string.channels_item_app_link);
+ Intent intent = ((AppLinkCardView) view).getIntent();
+ if (intent != null) {
+ getMainActivity().startActivitySafe(intent);
+ }
+ }
+ };
- private final View.OnClickListener mChannelOnClickListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- // Always send the label "Channels" because the channel ID or name or number might be
- // sensitive.
- mTracker.sendMenuClicked(R.string.menu_title_channels);
- getMainActivity().tuneToChannel((Channel) view.getTag());
- getMainActivity().hideOverlaysForTune();
- }
- };
+ private final View.OnClickListener mChannelOnClickListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ // Always send the label "Channels" because the channel ID or name or number
+ // might be
+ // sensitive.
+ mTracker.sendMenuClicked(R.string.menu_title_channels);
+ getMainActivity().tuneToChannel((Channel) view.getTag());
+ getMainActivity().hideOverlaysForTune();
+ }
+ };
- public ChannelsRowAdapter(Context context, Recommender recommender,
- int minCount, int maxCount) {
+ public ChannelsRowAdapter(
+ Context context, Recommender recommender, int minCount, int maxCount) {
super(context);
mContext = context;
- ApplicationSingletons appSingletons = TvApplication.getSingletons(context);
- mTracker = appSingletons.getTracker();
+ TvSingletons tvSingletons = TvSingletons.getSingletons(context);
+ mTracker = tvSingletons.getTracker();
if (CommonFeatures.DVR.isEnabled(context)) {
- mDvrDataManager = appSingletons.getDvrDataManager();
+ mDvrDataManager = tvSingletons.getDvrDataManager();
} else {
mDvrDataManager = null;
}
@@ -168,7 +169,7 @@ public class ChannelsRowAdapter extends ItemListRowView.ItemListAdapter<Channels
}
if (needToShowAppLinkItem()) {
ChannelsRowItem.APP_LINK_ITEM.setChannel(
- new Channel.Builder(getMainActivity().getCurrentChannel()).build());
+ new ChannelImpl.Builder(getMainActivity().getCurrentChannel()).build());
items.add(ChannelsRowItem.APP_LINK_ITEM);
}
for (Channel channel : getRecentChannels()) {
@@ -189,10 +190,11 @@ public class ChannelsRowAdapter extends ItemListRowView.ItemListAdapter<Channels
++currentIndex;
}
if (updateItem(needToShowAppLinkItem(), ChannelsRowItem.APP_LINK_ITEM, currentIndex)) {
- if (!getMainActivity().getCurrentChannel()
+ if (!getMainActivity()
+ .getCurrentChannel()
.hasSameReadOnlyInfo(ChannelsRowItem.APP_LINK_ITEM.getChannel())) {
ChannelsRowItem.APP_LINK_ITEM.setChannel(
- new Channel.Builder(getMainActivity().getCurrentChannel()).build());
+ new ChannelImpl.Builder(getMainActivity().getCurrentChannel()).build());
notifyItemChanged(currentIndex);
}
++currentIndex;
@@ -213,9 +215,7 @@ public class ChannelsRowAdapter extends ItemListRowView.ItemListAdapter<Channels
}
}
- /**
- * Returns {@code true} if the item should be shown.
- */
+ /** Returns {@code true} if the item should be shown. */
private boolean updateItem(boolean needToShow, ChannelsRowItem item, int index) {
List<ChannelsRowItem> items = getItemList();
boolean isItemInList = index < items.size() && item.equals(items.get(index));
@@ -230,14 +230,14 @@ public class ChannelsRowAdapter extends ItemListRowView.ItemListAdapter<Channels
}
private boolean needToShowSetupItem() {
- TvInputManagerHelper inputManager =
- TvApplication.getSingletons(mContext).getTvInputManagerHelper();
- return SetupUtils.getInstance(mContext).hasNewInput(inputManager);
+ TvSingletons singletons = TvSingletons.getSingletons(mContext);
+ TvInputManagerHelper inputManager = singletons.getTvInputManagerHelper();
+ return singletons.getSetupUtils().hasNewInput(inputManager);
}
private boolean needToShowDvrItem() {
TvInputManagerHelper inputManager =
- TvApplication.getSingletons(mContext).getTvInputManagerHelper();
+ TvSingletons.getSingletons(mContext).getTvInputManagerHelper();
if (mDvrDataManager != null) {
for (TvInputInfo info : inputManager.getTvInputInfos(true, true)) {
if (info.canRecord()) {
@@ -250,7 +250,7 @@ public class ChannelsRowAdapter extends ItemListRowView.ItemListAdapter<Channels
private boolean needToShowAppLinkItem() {
TvInputManagerHelper inputManager =
- TvApplication.getSingletons(mContext).getTvInputManagerHelper();
+ TvSingletons.getSingletons(mContext).getTvInputManagerHelper();
Channel currentChannel = getMainActivity().getCurrentChannel();
return currentChannel != null
&& currentChannel.getAppLinkType(mContext) != Channel.APP_LINK_TYPE_NONE
@@ -289,8 +289,10 @@ public class ChannelsRowAdapter extends ItemListRowView.ItemListAdapter<Channels
private static boolean addChannelToList(
List<Channel> channelList, Channel channel, long currentChannelId) {
- if (channel == null || channel.getId() == currentChannelId
- || channelList.contains(channel) || !channel.isBrowsable()) {
+ if (channel == null
+ || channel.getId() == currentChannelId
+ || channelList.contains(channel)
+ || !channel.isBrowsable()) {
return false;
}
channelList.add(channel);
diff --git a/src/com/android/tv/menu/ChannelsRowItem.java b/src/com/android/tv/menu/ChannelsRowItem.java
index c35189ec..608bb36e 100644
--- a/src/com/android/tv/menu/ChannelsRowItem.java
+++ b/src/com/android/tv/menu/ChannelsRowItem.java
@@ -17,14 +17,10 @@
package com.android.tv.menu;
import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
import com.android.tv.R;
-import com.android.tv.data.Channel;
+import com.android.tv.data.api.Channel;
-/**
- * A class for the items in channels row.
- */
+/** A class for the items in channels row. */
public class ChannelsRowItem {
/** The item ID for guide item */
public static final int GUIDE_ITEM_ID = -1;
@@ -62,31 +58,23 @@ public class ChannelsRowItem {
mLayoutId = layoutId;
}
- /**
- * Returns the channel for this item.
- */
+ /** Returns the channel for this item. */
@NonNull
public Channel getChannel() {
return mChannel;
}
- /**
- * Sets the channel.
- */
+ /** Sets the channel. */
public void setChannel(@NonNull Channel channel) {
mChannel = channel;
}
- /**
- * Returns the layout resource ID to represent this item.
- */
+ /** Returns the layout resource ID to represent this item. */
public int getLayoutId() {
return mLayoutId;
}
- /**
- * Returns the unique ID for this item.
- */
+ /** Returns the unique ID for this item. */
public long getItemId() {
return mItemId;
}
@@ -94,8 +82,12 @@ public class ChannelsRowItem {
@Override
public String toString() {
return "ChannelsRowItem{"
- + "itemId=" + mItemId
- + ", layoutId=" + mLayoutId
- + ", channel=" + mChannel + "}";
+ + "itemId="
+ + mItemId
+ + ", layoutId="
+ + mLayoutId
+ + ", channel="
+ + mChannel
+ + "}";
}
}
diff --git a/src/com/android/tv/menu/CustomizableOptionsRowAdapter.java b/src/com/android/tv/menu/CustomizableOptionsRowAdapter.java
index f69d5e86..7da26916 100644
--- a/src/com/android/tv/menu/CustomizableOptionsRowAdapter.java
+++ b/src/com/android/tv/menu/CustomizableOptionsRowAdapter.java
@@ -17,15 +17,11 @@
package com.android.tv.menu;
import android.content.Context;
-
-import com.android.tv.customization.CustomAction;
-
+import com.android.tv.common.customization.CustomAction;
import java.util.ArrayList;
import java.util.List;
-/**
- * An adapter of options that can accepts customization data.
- */
+/** An adapter of options that can accepts customization data. */
public abstract class CustomizableOptionsRowAdapter extends OptionsRowAdapter {
private final List<CustomAction> mCustomActions;
@@ -54,8 +50,9 @@ public abstract class CustomizableOptionsRowAdapter extends OptionsRowAdapter {
// Type of MenuAction should be unique in the Adapter.
int type = -(i + 1);
CustomAction customAction = mCustomActions.get(i);
- MenuAction action = new MenuAction(
- customAction.getTitle(), type, customAction.getIconDrawable());
+ MenuAction action =
+ new MenuAction(
+ customAction.getTitle(), type, customAction.getIconDrawable());
if (customAction.isFront()) {
actions.add(position++, action);
diff --git a/src/com/android/tv/menu/IMenuView.java b/src/com/android/tv/menu/IMenuView.java
index 87c5d9f6..19ebc739 100644
--- a/src/com/android/tv/menu/IMenuView.java
+++ b/src/com/android/tv/menu/IMenuView.java
@@ -17,33 +17,26 @@
package com.android.tv.menu;
import com.android.tv.menu.Menu.MenuShowReason;
-
import java.util.List;
-/**
- * An base interface for menu view.
- */
+/** An base interface for menu view. */
public interface IMenuView {
- /**
- * Sets menu rows.
- */
+ /** Sets menu rows. */
void setMenuRows(List<MenuRow> menuRows);
/**
* Shows the main menu.
*
- * <p> The inherited class should show the menu and select the row corresponding to
- * {@code rowIdToSelect}. If the menu is already visible, change the current selection to the
- * given row.
+ * <p>The inherited class should show the menu and select the row corresponding to {@code
+ * rowIdToSelect}. If the menu is already visible, change the current selection to the given
+ * row.
*
* @param reason A reason why this is called. See {@link MenuShowReason}.
* @param rowIdToSelect An ID of the row which corresponds to the {@code reason}.
*/
void onShow(@MenuShowReason int reason, String rowIdToSelect, Runnable runnableAfterShow);
- /**
- * Hides the main menu
- */
+ /** Hides the main menu */
void onHide();
/**
@@ -60,8 +53,6 @@ public interface IMenuView {
*/
boolean update(String rowId, boolean menuActive);
- /**
- * Checks if the menu view is visible or not.
- */
+ /** Checks if the menu view is visible or not. */
boolean isVisible();
}
diff --git a/src/com/android/tv/menu/ItemListRow.java b/src/com/android/tv/menu/ItemListRow.java
index faa611fa..2993d085 100644
--- a/src/com/android/tv/menu/ItemListRow.java
+++ b/src/com/android/tv/menu/ItemListRow.java
@@ -17,33 +17,37 @@
package com.android.tv.menu;
import android.content.Context;
-
import com.android.tv.R;
import com.android.tv.menu.ItemListRowView.ItemListAdapter;
/**
- * A menu item which is used to represents the list of the items.
- * A list will be displayed by a HorizontalGridView with cards, so an adapter
- * for the GridView is necessary.
+ * A menu item which is used to represents the list of the items. A list will be displayed by a
+ * HorizontalGridView with cards, so an adapter for the GridView is necessary.
*/
@SuppressWarnings("rawtypes")
public class ItemListRow extends MenuRow {
private ItemListAdapter mAdapter;
- public ItemListRow(Context context, Menu menu, int titleResId, int itemHeightResId,
+ public ItemListRow(
+ Context context,
+ Menu menu,
+ int titleResId,
+ int itemHeightResId,
ItemListAdapter adapter) {
this(context, menu, context.getString(titleResId), itemHeightResId, adapter);
}
- public ItemListRow(Context context, Menu menu, String title, int itemHeightResId,
+ public ItemListRow(
+ Context context,
+ Menu menu,
+ String title,
+ int itemHeightResId,
ItemListAdapter adapter) {
super(context, menu, title, itemHeightResId);
mAdapter = adapter;
}
- /**
- * Returns the adapter.
- */
+ /** Returns the adapter. */
public ItemListAdapter<?> getAdapter() {
return mAdapter;
}
diff --git a/src/com/android/tv/menu/ItemListRowView.java b/src/com/android/tv/menu/ItemListRowView.java
index cbeee936..7042324d 100644
--- a/src/com/android/tv/menu/ItemListRowView.java
+++ b/src/com/android/tv/menu/ItemListRowView.java
@@ -25,25 +25,24 @@ import android.util.Log;
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.util.ViewCache;
-
import java.util.Collections;
import java.util.List;
-/**
- * A view that shows a title and list view.
- */
+/** A view that shows a title and list view. */
public class ItemListRowView extends MenuRowView implements OnChildSelectedListener {
private static final String TAG = MenuView.TAG;
private static final boolean DEBUG = MenuView.DEBUG;
public interface CardView<T> {
void onBind(T row, boolean selected);
+
void onRecycled();
+
void onSelected();
+
void onDeselected();
}
@@ -115,7 +114,7 @@ public class ItemListRowView extends MenuRowView implements OnChildSelectedListe
}
}
- public static abstract class ItemListAdapter<T>
+ public abstract static class ItemListAdapter<T>
extends RecyclerView.Adapter<ItemListAdapter.MyViewHolder> {
private final MainActivity mMainActivity;
private final LayoutInflater mLayoutInflater;
@@ -129,25 +128,20 @@ public class ItemListRowView extends MenuRowView implements OnChildSelectedListe
}
/**
- * In most cases, implementation should call {@link #setItemList(java.util.List)} with
- * newly update item list.
+ * In most cases, implementation should call {@link #setItemList(java.util.List)} with newly
+ * update item list.
*/
public abstract void update();
- /**
- * Gets layout resource ID. It'll be used in {@link #onCreateViewHolder}.
- */
+ /** Gets layout resource ID. It'll be used in {@link #onCreateViewHolder}. */
protected abstract int getLayoutResId(int viewType);
- /**
- * Releases all the resources which need to be released.
- */
- public void release() {
- }
+ /** Releases all the resources which need to be released. */
+ public void release() {}
/**
- * The initial position of list that will be selected when the main menu appears.
- * By default, the first item is initially selected.
+ * The initial position of list that will be selected when the main menu appears. By
+ * default, the first item is initially selected.
*/
public int getInitialPosition() {
return 0;
@@ -169,8 +163,8 @@ public class ItemListRowView extends MenuRowView implements OnChildSelectedListe
* <p>This sends an item change event, not a structural change event. The items of the same
* positions retain the same identity.
*
- * <p>If there's any structural change and relayout and rebind is needed, call
- * {@link #notifyDataSetChanged} explicitly.
+ * <p>If there's any structural change and relayout and rebind is needed, call {@link
+ * #notifyDataSetChanged} explicitly.
*/
protected void setItemList(List<T> itemList) {
int oldSize = mItemList.size();
@@ -197,24 +191,21 @@ public class ItemListRowView extends MenuRowView implements OnChildSelectedListe
return mItemList.size();
}
- /**
- * Returns the position of the item.
- */
+ /** Returns the position of the item. */
protected int getItemPosition(T item) {
return mItemList.indexOf(item);
}
- /**
- * Returns {@code true} if the item list contains the item, otherwise {@code false}.
- */
+ /** Returns {@code true} if the item list contains the item, otherwise {@code false}. */
protected boolean containsItem(T item) {
return mItemList.contains(item);
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- View view = ViewCache.getInstance().getOrCreateView(
- mLayoutInflater, getLayoutResId(viewType), parent);
+ View view =
+ ViewCache.getInstance()
+ .getOrCreateView(mLayoutInflater, getLayoutResId(viewType), parent);
return new MyViewHolder(view);
}
diff --git a/src/com/android/tv/menu/Menu.java b/src/com/android/tv/menu/Menu.java
index e373de61..19a93dbc 100644
--- a/src/com/android/tv/menu/Menu.java
+++ b/src/com/android/tv/menu/Menu.java
@@ -21,27 +21,23 @@ import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.res.Resources;
-import android.os.Looper;
-import android.os.Message;
import android.support.annotation.IntDef;
-import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import android.support.v17.leanback.widget.HorizontalGridView;
import android.util.Log;
-
+import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
import com.android.tv.ChannelTuner;
import com.android.tv.R;
-import com.android.tv.TvApplication;
import com.android.tv.TvOptionsManager;
+import com.android.tv.TvSingletons;
import com.android.tv.analytics.Tracker;
-import com.android.tv.common.TvCommonUtils;
-import com.android.tv.common.WeakHandler;
+import com.android.tv.common.util.CommonUtils;
+import com.android.tv.common.util.DurationTimer;
import com.android.tv.menu.MenuRowFactory.PartnerRow;
import com.android.tv.menu.MenuRowFactory.TvOptionsRow;
import com.android.tv.ui.TunableTvView;
-import com.android.tv.util.DurationTimer;
+import com.android.tv.ui.hideable.AutoHideScheduler;
import com.android.tv.util.ViewCache;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -49,19 +45,25 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
-/**
- * A class which controls the menu.
- */
-public class Menu {
+/** A class which controls the menu. */
+public class Menu implements AccessibilityStateChangeListener {
private static final String TAG = "Menu";
private static final boolean DEBUG = false;
@Retention(RetentionPolicy.SOURCE)
- @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})
+ @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
+ })
public @interface MenuShowReason {}
+
public static final int REASON_NONE = 0;
public static final int REASON_GUIDE = 1;
public static final int REASON_PLAY_CONTROLS_PLAY = 2;
@@ -73,6 +75,7 @@ public class Menu {
public static final int REASON_PLAY_CONTROLS_JUMP_TO_NEXT = 8;
private static final List<String> sRowIdListForReason = new ArrayList<>();
+
static {
sRowIdListForReason.add(null); // REASON_NONE
sRowIdListForReason.add(ChannelsRow.ID); // REASON_GUIDE
@@ -86,6 +89,7 @@ public class Menu {
}
private static final Map<Integer, Integer> PRELOAD_VIEW_IDS = new HashMap<>();
+
static {
PRELOAD_VIEW_IDS.put(R.layout.menu_card_guide, 1);
PRELOAD_VIEW_IDS.put(R.layout.menu_card_setup, 1);
@@ -97,15 +101,13 @@ public class Menu {
private static final String SCREEN_NAME = "Menu";
- private static final int MSG_HIDE_MENU = 1000;
-
private final Context mContext;
private final IMenuView mMenuView;
private final Tracker mTracker;
private final DurationTimer mVisibleTimer = new DurationTimer();
private final long mShowDurationMillis;
private final OnMenuVisibilityChangeListener mOnMenuVisibilityChangeListener;
- private final WeakHandler<Menu> mHandler = new MenuWeakHandler(this, Looper.getMainLooper());
+ private final AutoHideScheduler mAutoHideScheduler;
private final MenuUpdater mMenuUpdater;
private final List<MenuRow> mMenuRows = new ArrayList<>();
@@ -116,17 +118,24 @@ public class Menu {
private boolean mAnimationDisabledForTest;
@VisibleForTesting
- Menu(Context context, IMenuView menuView, MenuRowFactory menuRowFactory,
+ Menu(
+ Context context,
+ IMenuView menuView,
+ MenuRowFactory menuRowFactory,
OnMenuVisibilityChangeListener onMenuVisibilityChangeListener) {
this(context, null, null, menuView, menuRowFactory, onMenuVisibilityChangeListener);
}
- public Menu(Context context, TunableTvView tvView, TvOptionsManager optionsManager,
- IMenuView menuView, MenuRowFactory menuRowFactory,
+ public Menu(
+ Context context,
+ TunableTvView tvView,
+ TvOptionsManager optionsManager,
+ IMenuView menuView,
+ MenuRowFactory menuRowFactory,
OnMenuVisibilityChangeListener onMenuVisibilityChangeListener) {
mContext = context;
mMenuView = menuView;
- mTracker = TvApplication.getSingletons(context).getTracker();
+ mTracker = TvSingletons.getSingletons(context).getTracker();
mMenuUpdater = new MenuUpdater(this, tvView, optionsManager);
Resources res = context.getResources();
mShowDurationMillis = res.getInteger(R.integer.menu_show_duration);
@@ -134,12 +143,13 @@ public class Menu {
mShowAnimator = AnimatorInflater.loadAnimator(context, R.animator.menu_enter);
mShowAnimator.setTarget(mMenuView);
mHideAnimator = AnimatorInflater.loadAnimator(context, R.animator.menu_exit);
- mHideAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- hideInternal();
- }
- });
+ mHideAnimator.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ hideInternal();
+ }
+ });
mHideAnimator.setTarget(mMenuView);
// Build menu rows
addMenuRow(menuRowFactory.createMenuRow(this, PlayControlsRow.class));
@@ -147,6 +157,7 @@ public class Menu {
addMenuRow(menuRowFactory.createMenuRow(this, PartnerRow.class));
addMenuRow(menuRowFactory.createMenuRow(this, TvOptionsRow.class));
mMenuView.setMenuRows(mMenuRows);
+ mAutoHideScheduler = new AutoHideScheduler(context, () -> hide(true));
}
/**
@@ -163,20 +174,16 @@ public class Menu {
}
}
- /**
- * Call this method to end the lifetime of the menu.
- */
+ /** Call this method to end the lifetime of the menu. */
public void release() {
mMenuUpdater.release();
for (MenuRow row : mMenuRows) {
row.release();
}
- mHandler.removeCallbacksAndMessages(null);
+ mAutoHideScheduler.cancel();
}
- /**
- * Preloads the item view used for the menu.
- */
+ /** Preloads the item view used for the menu. */
public void preloadItemViews() {
HorizontalGridView fakeParent = new HorizontalGridView(mContext);
for (int id : PRELOAD_VIEW_IDS.keySet()) {
@@ -201,20 +208,23 @@ public class Menu {
mOnMenuVisibilityChangeListener.onMenuVisibilityChange(true);
}
String rowIdToSelect = sRowIdListForReason.get(reason);
- mMenuView.onShow(reason, rowIdToSelect, mAnimationDisabledForTest ? null : new Runnable() {
- @Override
- public void run() {
- if (isActive()) {
- mShowAnimator.start();
- }
- }
- });
+ mMenuView.onShow(
+ reason,
+ rowIdToSelect,
+ mAnimationDisabledForTest
+ ? null
+ : new Runnable() {
+ @Override
+ public void run() {
+ if (isActive()) {
+ mShowAnimator.start();
+ }
+ }
+ });
scheduleHide();
}
- /**
- * Closes the menu.
- */
+ /** Closes the menu. */
public void hide(boolean withAnimation) {
if (mShowAnimator.isStarted()) {
mShowAnimator.cancel();
@@ -225,7 +235,7 @@ public class Menu {
if (mAnimationDisabledForTest) {
withAnimation = false;
}
- mHandler.removeMessages(MSG_HIDE_MENU);
+ mAutoHideScheduler.cancel();
if (withAnimation) {
if (!mHideAnimator.isStarted()) {
mHideAnimator.start();
@@ -246,26 +256,21 @@ public class Menu {
}
}
- /**
- * Schedules to hide the menu in some seconds.
- */
+ /** Schedules to hide the menu in some seconds. */
public void scheduleHide() {
- mHandler.removeMessages(MSG_HIDE_MENU);
- if (!mKeepVisible) {
- mHandler.sendEmptyMessageDelayed(MSG_HIDE_MENU, mShowDurationMillis);
- }
+ mAutoHideScheduler.schedule(mShowDurationMillis);
}
/**
- * Called when the caller wants the main menu to be kept visible or not.
- * If {@code keepVisible} is set to {@code true}, the hide schedule doesn't close the main menu,
- * but calling {@link #hide} still hides it.
- * If {@code keepVisible} is set to {@code false}, the hide schedule works as usual.
+ * Called when the caller wants the main menu to be kept visible or not. If {@code keepVisible}
+ * is set to {@code true}, the hide schedule doesn't close the main menu, but calling {@link
+ * #hide} still hides it. If {@code keepVisible} is set to {@code false}, the hide schedule
+ * works as usual.
*/
public void setKeepVisible(boolean keepVisible) {
mKeepVisible = keepVisible;
if (mKeepVisible) {
- mHandler.removeMessages(MSG_HIDE_MENU);
+ mAutoHideScheduler.cancel();
} else if (isActive()) {
scheduleHide();
}
@@ -273,12 +278,10 @@ public class Menu {
@VisibleForTesting
boolean isHideScheduled() {
- return mHandler.hasMessages(MSG_HIDE_MENU);
+ return mAutoHideScheduler.isScheduled();
}
- /**
- * Returns {@code true} if the menu is open and not hiding.
- */
+ /** Returns {@code true} if the menu is open and not hiding. */
public boolean isActive() {
return mMenuView.isVisible() && !mHideAnimator.isStarted();
}
@@ -303,9 +306,7 @@ public class Menu {
return mMenuView.update(rowId, isActive());
}
- /**
- * This method is called when channels are changed.
- */
+ /** This method is called when channels are changed. */
public void onRecentChannelsChanged() {
if (DEBUG) Log.d(TAG, "onRecentChannelsChanged");
for (MenuRow row : mMenuRows) {
@@ -313,42 +314,28 @@ public class Menu {
}
}
- /**
- * This method is called when the stream information is changed.
- */
+ /** This method is called when the stream information is changed. */
public void onStreamInfoChanged() {
if (DEBUG) Log.d(TAG, "update options row in main menu");
mMenuUpdater.onStreamInfoChanged();
}
+ @Override
+ public void onAccessibilityStateChanged(boolean enabled) {
+ mAutoHideScheduler.onAccessibilityStateChanged(enabled);
+ }
+
@VisibleForTesting
void disableAnimationForTest() {
- if (!TvCommonUtils.isRunningInTest()) {
+ if (!CommonUtils.isRunningInTest()) {
throw new RuntimeException("Animation may only be enabled/disabled during tests.");
}
mAnimationDisabledForTest = true;
}
- /**
- * A listener which receives the notification when the menu is visible/invisible.
- */
- public static abstract class OnMenuVisibilityChangeListener {
- /**
- * Called when the menu becomes visible/invisible.
- */
+ /** A listener which receives the notification when the menu is visible/invisible. */
+ public abstract static class OnMenuVisibilityChangeListener {
+ /** Called when the menu becomes visible/invisible. */
public abstract void onMenuVisibilityChange(boolean visible);
}
-
- private static class MenuWeakHandler extends WeakHandler<Menu> {
- public MenuWeakHandler(Menu menu, Looper mainLooper) {
- super(mainLooper, menu);
- }
-
- @Override
- public void handleMessage(Message msg, @NonNull Menu menu) {
- if (msg.what == MSG_HIDE_MENU) {
- menu.hide(true);
- }
- }
- }
}
diff --git a/src/com/android/tv/menu/MenuAction.java b/src/com/android/tv/menu/MenuAction.java
index b4356059..52372535 100644
--- a/src/com/android/tv/menu/MenuAction.java
+++ b/src/com/android/tv/menu/MenuAction.java
@@ -19,37 +19,47 @@ package com.android.tv.menu;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
-
import com.android.tv.R;
import com.android.tv.TvOptionsManager;
import com.android.tv.TvOptionsManager.OptionType;
-/**
- * A class to define possible actions from main menu.
- */
+/** A class to define possible actions from main menu. */
public class MenuAction {
// Actions in the TV option row.
public static final MenuAction SELECT_CLOSED_CAPTION_ACTION =
- new MenuAction(R.string.options_item_closed_caption,
+ new MenuAction(
+ R.string.options_item_closed_caption,
TvOptionsManager.OPTION_CLOSED_CAPTIONS,
R.drawable.ic_tvoption_cc);
public static final MenuAction SELECT_DISPLAY_MODE_ACTION =
- new MenuAction(R.string.options_item_display_mode, TvOptionsManager.OPTION_DISPLAY_MODE,
+ new MenuAction(
+ R.string.options_item_display_mode,
+ TvOptionsManager.OPTION_DISPLAY_MODE,
R.drawable.ic_tvoption_aspect);
public static final MenuAction SYSTEMWIDE_PIP_ACTION =
- new MenuAction(R.string.options_item_pip, TvOptionsManager.OPTION_SYSTEMWIDE_PIP,
+ 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,
+ new MenuAction(
+ R.string.options_item_multi_audio,
+ TvOptionsManager.OPTION_MULTI_AUDIO,
R.drawable.ic_tvoption_multi_track);
public static final MenuAction MORE_CHANNELS_ACTION =
- new MenuAction(R.string.options_item_more_channels,
- TvOptionsManager.OPTION_MORE_CHANNELS, R.drawable.ic_store);
+ new MenuAction(
+ R.string.options_item_more_channels,
+ TvOptionsManager.OPTION_MORE_CHANNELS,
+ R.drawable.ic_store);
public static final MenuAction DEV_ACTION =
- new MenuAction(R.string.options_item_developer,
- TvOptionsManager.OPTION_DEVELOPER, R.drawable.ic_developer_mode_tv_white_48dp);
+ new MenuAction(
+ R.string.options_item_developer,
+ TvOptionsManager.OPTION_DEVELOPER,
+ R.drawable.ic_developer_mode_tv_white_48dp);
public static final MenuAction SETTINGS_ACTION =
- new MenuAction(R.string.options_item_settings, TvOptionsManager.OPTION_SETTINGS,
+ new MenuAction(
+ R.string.options_item_settings,
+ TvOptionsManager.OPTION_SETTINGS,
R.drawable.ic_settings);
private final String mActionName;
@@ -60,18 +70,14 @@ public class MenuAction {
private int mDrawableResId;
private boolean mEnabled = true;
- /**
- * Sets the action description. Returns {@code trye} if the description is changed.
- */
+ /** Sets the action description. Returns {@code trye} if the description is changed. */
public static boolean setActionDescription(MenuAction action, String actionDescription) {
String oldDescription = action.mActionDescription;
action.mActionDescription = actionDescription;
return !TextUtils.equals(action.mActionDescription, oldDescription);
}
- /**
- * Enables or disables the action. Returns {@code true} if the value is changed.
- */
+ /** Enables or disables the action. Returns {@code true} if the value is changed. */
public static boolean setEnabled(MenuAction action, boolean enabled) {
boolean changed = action.mEnabled != enabled;
action.mEnabled = enabled;
@@ -105,13 +111,12 @@ public class MenuAction {
return mActionDescription;
}
- @OptionType public int getType() {
+ @OptionType
+ public int getType() {
return mType;
}
- /**
- * Returns Drawable.
- */
+ /** Returns Drawable. */
public Drawable getDrawable(Context context) {
if (mDrawable == null) {
mDrawable = context.getDrawable(mDrawableResId);
diff --git a/src/com/android/tv/menu/MenuLayoutManager.java b/src/com/android/tv/menu/MenuLayoutManager.java
index 173d4004..a600f704 100644
--- a/src/com/android/tv/menu/MenuLayoutManager.java
+++ b/src/com/android/tv/menu/MenuLayoutManager.java
@@ -34,11 +34,9 @@ import android.util.Property;
import android.view.View;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.TextView;
-
import com.android.tv.R;
import com.android.tv.common.SoftPreconditions;
import com.android.tv.util.Utils;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -47,9 +45,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
-/**
- * A view that represents TV main menu.
- */
+/** A view that represents TV main menu. */
@UiThread
public class MenuLayoutManager {
static final String TAG = "MenuLayoutManager";
@@ -93,24 +89,22 @@ public class MenuLayoutManager {
Resources res = context.getResources();
mRowAlignFromBottom = res.getDimensionPixelOffset(R.dimen.menu_row_align_from_bottom);
mRowContentsPaddingTop = res.getDimensionPixelOffset(R.dimen.menu_row_contents_padding_top);
- mRowContentsPaddingBottomMax = res.getDimensionPixelOffset(
- R.dimen.menu_row_contents_padding_bottom_max);
- mRowTitleTextDescenderHeight = res.getDimensionPixelOffset(
- R.dimen.menu_row_title_text_descender_height);
+ mRowContentsPaddingBottomMax =
+ res.getDimensionPixelOffset(R.dimen.menu_row_contents_padding_bottom_max);
+ mRowTitleTextDescenderHeight =
+ res.getDimensionPixelOffset(R.dimen.menu_row_title_text_descender_height);
mMenuMarginBottomMin = res.getDimensionPixelOffset(R.dimen.menu_margin_bottom_min);
mRowTitleHeight = res.getDimensionPixelSize(R.dimen.menu_row_title_height);
mRowScrollUpAnimationOffset =
res.getDimensionPixelOffset(R.dimen.menu_row_scroll_up_anim_offset);
mRowAnimationDuration = res.getInteger(R.integer.menu_row_selection_anim_duration);
- mOldContentsFadeOutDuration = res.getInteger(
- R.integer.menu_previous_contents_fade_out_duration);
- mCurrentContentsFadeInDuration = res.getInteger(
- R.integer.menu_current_contents_fade_in_duration);
+ mOldContentsFadeOutDuration =
+ res.getInteger(R.integer.menu_previous_contents_fade_out_duration);
+ mCurrentContentsFadeInDuration =
+ res.getInteger(R.integer.menu_current_contents_fade_in_duration);
}
- /**
- * Sets the menu rows and views.
- */
+ /** Sets the menu rows and views. */
public void setMenuRowsAndViews(List<MenuRow> menuRows, List<MenuRowView> menuRowViews) {
mMenuRows.clear();
mMenuRows.addAll(menuRows);
@@ -181,22 +175,54 @@ public class MenuLayoutManager {
for (MenuRowView view : mMenuRowViews) {
View title = view.getChildAt(0);
View contents = view.getChildAt(1);
- Log.d(TAG, prefix + " position=" + position++
- + " rowView={visiblility=" + view.getVisibility()
- + ", alpha=" + view.getAlpha()
- + ", translationY=" + view.getTranslationY()
- + ", left=" + view.getLeft() + ", top=" + view.getTop()
- + ", right=" + view.getRight() + ", bottom=" + view.getBottom()
- + "}, title={visiblility=" + title.getVisibility()
- + ", alpha=" + title.getAlpha()
- + ", translationY=" + title.getTranslationY()
- + ", left=" + title.getLeft() + ", top=" + title.getTop()
- + ", right=" + title.getRight() + ", bottom=" + title.getBottom()
- + "}, contents={visiblility=" + contents.getVisibility()
- + ", alpha=" + contents.getAlpha()
- + ", translationY=" + contents.getTranslationY()
- + ", left=" + contents.getLeft() + ", top=" + contents.getTop()
- + ", right=" + contents.getRight() + ", bottom=" + contents.getBottom()+ "}");
+ Log.d(
+ TAG,
+ prefix
+ + " position="
+ + position++
+ + " rowView={visiblility="
+ + view.getVisibility()
+ + ", alpha="
+ + view.getAlpha()
+ + ", translationY="
+ + view.getTranslationY()
+ + ", left="
+ + view.getLeft()
+ + ", top="
+ + view.getTop()
+ + ", right="
+ + view.getRight()
+ + ", bottom="
+ + view.getBottom()
+ + "}, title={visiblility="
+ + title.getVisibility()
+ + ", alpha="
+ + title.getAlpha()
+ + ", translationY="
+ + title.getTranslationY()
+ + ", left="
+ + title.getLeft()
+ + ", top="
+ + title.getTop()
+ + ", right="
+ + title.getRight()
+ + ", bottom="
+ + title.getBottom()
+ + "}, contents={visiblility="
+ + contents.getVisibility()
+ + ", alpha="
+ + contents.getAlpha()
+ + ", translationY="
+ + contents.getTranslationY()
+ + ", left="
+ + contents.getLeft()
+ + ", top="
+ + contents.getTop()
+ + ", right="
+ + contents.getRight()
+ + ", bottom="
+ + contents.getBottom()
+ + "}");
}
}
@@ -204,14 +230,14 @@ public class MenuLayoutManager {
* Checks if the view will take up space for the layout not.
*
* @param position The index of the menu row view in the list. This is not the index of the view
- * in the screen.
+ * in the screen.
* @param view The menu row view.
* @param rowsToAdd The menu row views to be added in the next layout process.
* @param rowsToRemove The menu row views to be removed in the next layout process.
* @return {@code true} if the view will take up space for the layout, otherwise {@code false}.
*/
- private boolean isVisibleInLayout(int position, MenuRowView view, List<Integer> rowsToAdd,
- List<Integer> rowsToRemove) {
+ private boolean isVisibleInLayout(
+ int position, MenuRowView view, List<Integer> rowsToAdd, List<Integer> rowsToRemove) {
// Checks if the view will be visible or not.
return (view.getVisibility() != View.GONE && !rowsToRemove.contains(position))
|| rowsToAdd.contains(position);
@@ -226,8 +252,8 @@ public class MenuLayoutManager {
* @param bottom The bottom coordinate of the menu view.
*/
private List<Rect> getViewLayouts(int left, int top, int right, int bottom) {
- return getViewLayouts(left, top, right, bottom, Collections.emptyList(),
- Collections.emptyList());
+ return getViewLayouts(
+ left, top, right, bottom, Collections.emptyList(), Collections.emptyList());
}
/**
@@ -247,8 +273,13 @@ public class MenuLayoutManager {
* @param rowsToRemove The menu row views to be removed in the next layout process.
* @return the layout bounds of the menu row views.
*/
- private List<Rect> getViewLayouts(int left, int top, int right, int bottom,
- List<Integer> rowsToAdd, List<Integer> rowsToRemove) {
+ private List<Rect> getViewLayouts(
+ int left,
+ int top,
+ int right,
+ int bottom,
+ List<Integer> rowsToAdd,
+ List<Integer> rowsToRemove) {
// The coordinates should be relative to the parent.
int relativeLeft = 0;
int relateiveRight = right - left;
@@ -262,18 +293,29 @@ public class MenuLayoutManager {
// Calculate for the selected row first.
// The distance between the bottom of the screen and the vertical center of the contents
// should be kept fixed. For more information, please see the redlines.
- int childTop = relativeBottom - mRowAlignFromBottom - rowContentsHeight / 2
- - mRowContentsPaddingTop - rowTitleHeight;
+ int childTop =
+ relativeBottom
+ - mRowAlignFromBottom
+ - rowContentsHeight / 2
+ - mRowContentsPaddingTop
+ - rowTitleHeight;
int childBottom = relativeBottom;
int position = mSelectedPosition + 1;
for (; position < count; ++position) {
// Find and layout the next row to calculate the bottom line of the selected row.
MenuRowView nextView = mMenuRowViews.get(position);
if (isVisibleInLayout(position, nextView, rowsToAdd, rowsToRemove)) {
- int nextTitleTopMax = relativeBottom - mMenuMarginBottomMin - rowTitleHeight
- + mRowTitleTextDescenderHeight;
- int childBottomMax = relativeBottom - mRowAlignFromBottom + rowContentsHeight / 2
- + mRowContentsPaddingBottomMax - rowTitleHeight;
+ int nextTitleTopMax =
+ relativeBottom
+ - mMenuMarginBottomMin
+ - rowTitleHeight
+ + mRowTitleTextDescenderHeight;
+ int childBottomMax =
+ relativeBottom
+ - mRowAlignFromBottom
+ + rowContentsHeight / 2
+ + mRowContentsPaddingBottomMax
+ - rowTitleHeight;
childBottom = Math.min(nextTitleTopMax, childBottomMax);
layouts.add(new Rect(relativeLeft, childBottom, relateiveRight, relativeBottom));
break;
@@ -309,19 +351,22 @@ public class MenuLayoutManager {
return layouts;
}
- /**
- * Move the current selection to the given {@code position}.
- */
+ /** Move the current selection to the given {@code position}. */
public void setSelectedPosition(int position) {
if (DEBUG) {
- Log.d(TAG, "setSelectedPosition(position=" + position + ") {previousPosition="
- + mSelectedPosition + "}");
+ Log.d(
+ TAG,
+ "setSelectedPosition(position="
+ + position
+ + ") {previousPosition="
+ + mSelectedPosition
+ + "}");
}
if (mSelectedPosition == position) {
return;
}
boolean indexValid = Utils.isIndexValid(mMenuRowViews, position);
- SoftPreconditions.checkArgument(indexValid, TAG, "position " + position);
+ SoftPreconditions.checkArgument(indexValid, TAG, "position %s ", position);
if (!indexValid) {
return;
}
@@ -347,13 +392,18 @@ public class MenuLayoutManager {
}
/**
- * Move the current selection to the given {@code position} with animation.
- * The animation specification is included in http://b/21069476
+ * Move the current selection to the given {@code position} with animation. The animation
+ * specification is included in http://b/21069476
*/
public void setSelectedPositionSmooth(final int position) {
if (DEBUG) {
- Log.d(TAG, "setSelectedPositionSmooth(position=" + position + ") {previousPosition="
- + mSelectedPosition + "}");
+ Log.d(
+ TAG,
+ "setSelectedPositionSmooth(position="
+ + position
+ + ") {previousPosition="
+ + mSelectedPosition
+ + "}");
}
if (mMenuView.getVisibility() != View.VISIBLE) {
setSelectedPosition(position);
@@ -363,13 +413,13 @@ public class MenuLayoutManager {
return;
}
boolean oldIndexValid = Utils.isIndexValid(mMenuRowViews, mSelectedPosition);
- SoftPreconditions
- .checkState(oldIndexValid, TAG, "No previous selection: " + mSelectedPosition);
+ SoftPreconditions.checkState(
+ oldIndexValid, TAG, "No previous selection: " + mSelectedPosition);
if (!oldIndexValid) {
return;
}
boolean newIndexValid = Utils.isIndexValid(mMenuRowViews, position);
- SoftPreconditions.checkArgument(newIndexValid, TAG, "position " + position);
+ SoftPreconditions.checkArgument(newIndexValid, TAG, "position %s", position);
if (!newIndexValid) {
return;
}
@@ -415,8 +465,7 @@ public class MenuLayoutManager {
mMenuView.requestFocus();
if (mTempTitleViewForOld == null) {
// Initialize here because we don't know when the views are inflated.
- mTempTitleViewForOld =
- (TextView) mMenuView.findViewById(R.id.temp_title_for_old);
+ mTempTitleViewForOld = (TextView) mMenuView.findViewById(R.id.temp_title_for_old);
mTempTitleViewForCurrent =
(TextView) mMenuView.findViewById(R.id.temp_title_for_current);
}
@@ -425,16 +474,21 @@ public class MenuLayoutManager {
mPropertyValuesAfterAnimation.clear();
List<Animator> animators = new ArrayList<>();
boolean scrollDown = position > oldPosition;
- List<Rect> layouts = getViewLayouts(mMenuView.getLeft(), mMenuView.getTop(),
- mMenuView.getRight(), mMenuView.getBottom());
+ List<Rect> layouts =
+ getViewLayouts(
+ mMenuView.getLeft(),
+ mMenuView.getTop(),
+ mMenuView.getRight(),
+ mMenuView.getBottom());
// Old row.
MenuRow oldRow = mMenuRows.get(oldPosition);
final MenuRowView oldView = mMenuRowViews.get(oldPosition);
View oldContentsView = oldView.getContentsView();
// Old contents view.
- animators.add(createAlphaAnimator(oldContentsView, 1.0f, 0.0f, 1.0f, mLinearOutSlowIn)
- .setDuration(mOldContentsFadeOutDuration));
+ animators.add(
+ createAlphaAnimator(oldContentsView, 1.0f, 0.0f, 1.0f, mLinearOutSlowIn)
+ .setDuration(mOldContentsFadeOutDuration));
final TextView oldTitleView = oldView.getTitleView();
setTempTitleView(mTempTitleViewForOld, oldTitleView);
Rect oldLayoutRect = layouts.get(oldPosition);
@@ -444,20 +498,36 @@ public class MenuLayoutManager {
// This case is not included in the animation specification.
mTempTitleViewForOld.setScaleX(1.0f);
mTempTitleViewForOld.setScaleY(1.0f);
- animators.add(createAlphaAnimator(mTempTitleViewForOld, 0.0f,
- oldView.getTitleViewAlphaDeselected(), mFastOutLinearIn));
+ animators.add(
+ createAlphaAnimator(
+ mTempTitleViewForOld,
+ 0.0f,
+ oldView.getTitleViewAlphaDeselected(),
+ mFastOutLinearIn));
int offset = oldLayoutRect.top - mTempTitleViewForOld.getTop();
- animators.add(createTranslationYAnimator(mTempTitleViewForOld,
- offset + mRowScrollUpAnimationOffset, offset));
+ animators.add(
+ createTranslationYAnimator(
+ mTempTitleViewForOld,
+ offset + mRowScrollUpAnimationOffset,
+ offset));
} else {
- animators.add(createScaleXAnimator(mTempTitleViewForOld,
- oldView.getTitleViewScaleSelected(), 1.0f));
- animators.add(createScaleYAnimator(mTempTitleViewForOld,
- oldView.getTitleViewScaleSelected(), 1.0f));
- animators.add(createAlphaAnimator(mTempTitleViewForOld, oldTitleView.getAlpha(),
- oldView.getTitleViewAlphaDeselected(), mLinearOutSlowIn));
- animators.add(createTranslationYAnimator(mTempTitleViewForOld, 0,
- oldLayoutRect.top - mTempTitleViewForOld.getTop()));
+ animators.add(
+ createScaleXAnimator(
+ mTempTitleViewForOld, oldView.getTitleViewScaleSelected(), 1.0f));
+ animators.add(
+ createScaleYAnimator(
+ mTempTitleViewForOld, oldView.getTitleViewScaleSelected(), 1.0f));
+ animators.add(
+ createAlphaAnimator(
+ mTempTitleViewForOld,
+ oldTitleView.getAlpha(),
+ oldView.getTitleViewAlphaDeselected(),
+ mLinearOutSlowIn));
+ animators.add(
+ createTranslationYAnimator(
+ mTempTitleViewForOld,
+ 0,
+ oldLayoutRect.top - mTempTitleViewForOld.getTop()));
}
oldTitleView.setAlpha(oldView.getTitleViewAlphaDeselected());
oldTitleView.setVisibility(View.INVISIBLE);
@@ -471,23 +541,30 @@ public class MenuLayoutManager {
// The maximum is to the top of the start position of mTempTitleViewForOld.
int distanceCurrentTitle = currentLayoutRect.top - currentView.getTop();
int distance = Math.max(mRowScrollUpAnimationOffset, distanceCurrentTitle);
- int distanceToTopOfSecondTitle = oldLayoutRect.top - mRowScrollUpAnimationOffset
- - oldView.getTop();
- animators.add(createTranslationYAnimator(oldTitleView, 0.0f,
- Math.min(distance, distanceToTopOfSecondTitle)));
- animators.add(createAlphaAnimator(oldTitleView, 1.0f, 0.0f, 1.0f, mLinearOutSlowIn)
- .setDuration(mOldContentsFadeOutDuration));
- animators.add(createScaleXAnimator(oldTitleView,
- oldView.getTitleViewScaleSelected(), 1.0f));
- animators.add(createScaleYAnimator(oldTitleView,
- oldView.getTitleViewScaleSelected(), 1.0f));
+ int distanceToTopOfSecondTitle =
+ oldLayoutRect.top - mRowScrollUpAnimationOffset - oldView.getTop();
+ animators.add(
+ createTranslationYAnimator(
+ oldTitleView, 0.0f, Math.min(distance, distanceToTopOfSecondTitle)));
+ animators.add(
+ createAlphaAnimator(oldTitleView, 1.0f, 0.0f, 1.0f, mLinearOutSlowIn)
+ .setDuration(mOldContentsFadeOutDuration));
+ animators.add(
+ createScaleXAnimator(oldTitleView, oldView.getTitleViewScaleSelected(), 1.0f));
+ animators.add(
+ createScaleYAnimator(oldTitleView, oldView.getTitleViewScaleSelected(), 1.0f));
mTempTitleViewForOld.setScaleX(1.0f);
mTempTitleViewForOld.setScaleY(1.0f);
- animators.add(createAlphaAnimator(mTempTitleViewForOld, 0.0f,
- oldView.getTitleViewAlphaDeselected(), mFastOutLinearIn));
+ animators.add(
+ createAlphaAnimator(
+ mTempTitleViewForOld,
+ 0.0f,
+ oldView.getTitleViewAlphaDeselected(),
+ mFastOutLinearIn));
int offset = oldLayoutRect.top - mTempTitleViewForOld.getTop();
- animators.add(createTranslationYAnimator(mTempTitleViewForOld,
- offset - mRowScrollUpAnimationOffset, offset));
+ animators.add(
+ createTranslationYAnimator(
+ mTempTitleViewForOld, offset - mRowScrollUpAnimationOffset, offset));
}
// Current row.
Rect currentLayoutRect = new Rect(layouts.get(position));
@@ -502,29 +579,40 @@ public class MenuLayoutManager {
// The maximum is to the top of the end position of mTempTitleViewForCurrent.
int distanceOldTitle = oldView.getTop() - oldLayoutRect.top;
int distance = Math.max(mRowScrollUpAnimationOffset, distanceOldTitle);
- int distanceTopOfSecondTitle = currentView.getTop() - mRowScrollUpAnimationOffset
- - currentLayoutRect.top;
- animators.add(createTranslationYAnimator(currentTitleView,
- Math.min(distance, distanceTopOfSecondTitle), 0.0f));
+ int distanceTopOfSecondTitle =
+ currentView.getTop() - mRowScrollUpAnimationOffset - currentLayoutRect.top;
+ animators.add(
+ createTranslationYAnimator(
+ currentTitleView, Math.min(distance, distanceTopOfSecondTitle), 0.0f));
currentView.setTop(currentLayoutRect.top);
- ObjectAnimator animator = createAlphaAnimator(currentTitleView, 0.0f, 1.0f,
- mFastOutLinearIn).setDuration(mCurrentContentsFadeInDuration);
+ ObjectAnimator animator =
+ createAlphaAnimator(currentTitleView, 0.0f, 1.0f, mFastOutLinearIn)
+ .setDuration(mCurrentContentsFadeInDuration);
animator.setStartDelay(mOldContentsFadeOutDuration);
currentTitleView.setAlpha(0.0f);
animators.add(animator);
- animators.add(createScaleXAnimator(currentTitleView, 1.0f,
- currentView.getTitleViewScaleSelected()));
- animators.add(createScaleYAnimator(currentTitleView, 1.0f,
- currentView.getTitleViewScaleSelected()));
- animators.add(createTranslationYAnimator(mTempTitleViewForCurrent, 0.0f,
- -mRowScrollUpAnimationOffset));
- animators.add(createAlphaAnimator(mTempTitleViewForCurrent,
- currentView.getTitleViewAlphaDeselected(), 0, mLinearOutSlowIn));
+ animators.add(
+ createScaleXAnimator(
+ currentTitleView, 1.0f, currentView.getTitleViewScaleSelected()));
+ animators.add(
+ createScaleYAnimator(
+ currentTitleView, 1.0f, currentView.getTitleViewScaleSelected()));
+ animators.add(
+ createTranslationYAnimator(
+ mTempTitleViewForCurrent, 0.0f, -mRowScrollUpAnimationOffset));
+ animators.add(
+ createAlphaAnimator(
+ mTempTitleViewForCurrent,
+ currentView.getTitleViewAlphaDeselected(),
+ 0,
+ mLinearOutSlowIn));
// Current contents view.
- animators.add(createTranslationYAnimator(currentContentsView,
- mRowScrollUpAnimationOffset, 0.0f));
- animator = createAlphaAnimator(currentContentsView, 0.0f, 1.0f, mFastOutLinearIn)
- .setDuration(mCurrentContentsFadeInDuration);
+ animators.add(
+ createTranslationYAnimator(
+ currentContentsView, mRowScrollUpAnimationOffset, 0.0f));
+ animator =
+ createAlphaAnimator(currentContentsView, 0.0f, 1.0f, mFastOutLinearIn)
+ .setDuration(mCurrentContentsFadeInDuration);
animator.setStartDelay(mOldContentsFadeOutDuration);
animators.add(animator);
} else {
@@ -532,17 +620,27 @@ public class MenuLayoutManager {
// Current title view.
int currentViewOffset = currentLayoutRect.top - currentView.getTop();
animators.add(createTranslationYAnimator(currentTitleView, 0, currentViewOffset));
- animators.add(createAlphaAnimator(currentTitleView,
- currentView.getTitleViewAlphaDeselected(), 1.0f, mFastOutSlowIn));
- animators.add(createScaleXAnimator(currentTitleView, 1.0f,
- currentView.getTitleViewScaleSelected()));
- animators.add(createScaleYAnimator(currentTitleView, 1.0f,
- currentView.getTitleViewScaleSelected()));
+ animators.add(
+ createAlphaAnimator(
+ currentTitleView,
+ currentView.getTitleViewAlphaDeselected(),
+ 1.0f,
+ mFastOutSlowIn));
+ animators.add(
+ createScaleXAnimator(
+ currentTitleView, 1.0f, currentView.getTitleViewScaleSelected()));
+ animators.add(
+ createScaleYAnimator(
+ currentTitleView, 1.0f, currentView.getTitleViewScaleSelected()));
// Current contents view.
- animators.add(createTranslationYAnimator(currentContentsView,
- currentViewOffset - mRowScrollUpAnimationOffset, currentViewOffset));
- ObjectAnimator animator = createAlphaAnimator(currentContentsView, 0.0f, 1.0f,
- mFastOutLinearIn).setDuration(mCurrentContentsFadeInDuration);
+ animators.add(
+ createTranslationYAnimator(
+ currentContentsView,
+ currentViewOffset - mRowScrollUpAnimationOffset,
+ currentViewOffset));
+ ObjectAnimator animator =
+ createAlphaAnimator(currentContentsView, 0.0f, 1.0f, mFastOutLinearIn)
+ .setDuration(mCurrentContentsFadeInDuration);
animator.setStartDelay(mOldContentsFadeOutDuration);
animators.add(animator);
}
@@ -553,9 +651,13 @@ public class MenuLayoutManager {
if (nextPosition != INVALID_POSITION) {
MenuRowView nextView = mMenuRowViews.get(nextPosition);
Rect nextLayoutRect = layouts.get(nextPosition);
- animators.add(createTranslationYAnimator(nextView,
- nextLayoutRect.top + mRowScrollUpAnimationOffset - nextView.getTop(),
- nextLayoutRect.top - nextView.getTop()));
+ animators.add(
+ createTranslationYAnimator(
+ nextView,
+ nextLayoutRect.top
+ + mRowScrollUpAnimationOffset
+ - nextView.getTop(),
+ nextLayoutRect.top - nextView.getTop()));
animators.add(createAlphaAnimator(nextView, 0.0f, 1.0f, mFastOutLinearIn));
}
} else {
@@ -563,15 +665,22 @@ public class MenuLayoutManager {
if (nextPosition != INVALID_POSITION) {
MenuRowView nextView = mMenuRowViews.get(nextPosition);
animators.add(createTranslationYAnimator(nextView, 0, mRowScrollUpAnimationOffset));
- animators.add(createAlphaAnimator(nextView,
- nextView.getTitleViewAlphaDeselected(), 0.0f, 1.0f, mLinearOutSlowIn));
+ animators.add(
+ createAlphaAnimator(
+ nextView,
+ nextView.getTitleViewAlphaDeselected(),
+ 0.0f,
+ 1.0f,
+ mLinearOutSlowIn));
}
}
// Other rows.
int count = mMenuRowViews.size();
for (int i = 0; i < count; ++i) {
MenuRowView view = mMenuRowViews.get(i);
- if (view.getVisibility() == View.VISIBLE && i != oldPosition && i != position
+ if (view.getVisibility() == View.VISIBLE
+ && i != oldPosition
+ && i != position
&& i != nextPosition) {
Rect rect = layouts.get(i);
animators.add(createTranslationYAnimator(view, 0, rect.top - view.getTop()));
@@ -582,51 +691,62 @@ public class MenuLayoutManager {
propertyValuesAfterAnimation.addAll(mPropertyValuesAfterAnimation);
mAnimatorSet = new AnimatorSet();
mAnimatorSet.playTogether(animators);
- mAnimatorSet.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animator) {
- if (DEBUG) dumpChildren("onRowAnimationEndBefore");
- mAnimatorSet = null;
- // The property values which are different from the end values and need to be
- // changed after the animation are set here.
- // e.g. setting translationY to 0, alpha of the contents view to 1.
- for (ViewPropertyValueHolder holder : propertyValuesAfterAnimation) {
- holder.property.set(holder.view, holder.value);
- }
- oldView.onDeselected();
- currentView.onSelected(true);
- mTempTitleViewForOld.setVisibility(View.GONE);
- mTempTitleViewForCurrent.setVisibility(View.GONE);
- layout(mMenuView.getLeft(), mMenuView.getTop(), mMenuView.getRight(),
- mMenuView.getBottom());
- if (DEBUG) dumpChildren("onRowAnimationEndAfter");
-
- MenuRow currentRow = mMenuRows.get(position);
- if (currentRow.hideTitleWhenSelected()) {
- View titleView = mMenuRowViews.get(position).getTitleView();
- mTitleFadeOutAnimator = createAlphaAnimator(titleView, titleView.getAlpha(),
- 0.0f, mLinearOutSlowIn);
- mTitleFadeOutAnimator.setStartDelay(TITLE_SHOW_DURATION_BEFORE_HIDDEN_MS);
- mTitleFadeOutAnimator.addListener(new AnimatorListenerAdapter() {
- private boolean mCanceled;
-
- @Override
- public void onAnimationCancel(Animator animator) {
- mCanceled = true;
+ mAnimatorSet.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ if (DEBUG) dumpChildren("onRowAnimationEndBefore");
+ mAnimatorSet = null;
+ // The property values which are different from the end values and need to
+ // be
+ // changed after the animation are set here.
+ // e.g. setting translationY to 0, alpha of the contents view to 1.
+ for (ViewPropertyValueHolder holder : propertyValuesAfterAnimation) {
+ holder.property.set(holder.view, holder.value);
}
-
- @Override
- public void onAnimationEnd(Animator animator) {
- mTitleFadeOutAnimator = null;
- if (!mCanceled) {
- mMenuRowViews.get(position).onSelected(false);
- }
+ oldView.onDeselected();
+ currentView.onSelected(true);
+ mTempTitleViewForOld.setVisibility(View.GONE);
+ mTempTitleViewForCurrent.setVisibility(View.GONE);
+ layout(
+ mMenuView.getLeft(),
+ mMenuView.getTop(),
+ mMenuView.getRight(),
+ mMenuView.getBottom());
+ if (DEBUG) dumpChildren("onRowAnimationEndAfter");
+
+ MenuRow currentRow = mMenuRows.get(position);
+ if (currentRow.hideTitleWhenSelected()) {
+ View titleView = mMenuRowViews.get(position).getTitleView();
+ mTitleFadeOutAnimator =
+ createAlphaAnimator(
+ titleView,
+ titleView.getAlpha(),
+ 0.0f,
+ mLinearOutSlowIn);
+ mTitleFadeOutAnimator.setStartDelay(
+ TITLE_SHOW_DURATION_BEFORE_HIDDEN_MS);
+ mTitleFadeOutAnimator.addListener(
+ new AnimatorListenerAdapter() {
+ private boolean mCanceled;
+
+ @Override
+ public void onAnimationCancel(Animator animator) {
+ mCanceled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ mTitleFadeOutAnimator = null;
+ if (!mCanceled) {
+ mMenuRowViews.get(position).onSelected(false);
+ }
+ }
+ });
+ mTitleFadeOutAnimator.start();
}
- });
- mTitleFadeOutAnimator.start();
- }
- }
- });
+ }
+ });
mAnimatorSet.start();
if (DEBUG) dumpChildren("startedRowAnimation()");
}
@@ -661,7 +781,8 @@ public class MenuLayoutManager {
if (mMenuView.getVisibility() != View.VISIBLE) {
int count = mMenuRowViews.size();
for (int i = 0; i < count; ++i) {
- mMenuRowViews.get(i)
+ mMenuRowViews
+ .get(i)
.setVisibility(mMenuRows.get(i).isVisible() ? View.VISIBLE : View.GONE);
}
return;
@@ -674,8 +795,8 @@ public class MenuLayoutManager {
for (int i = mSelectedPosition - 1; i >= 0; --i) {
MenuRow row = mMenuRows.get(i);
MenuRowView view = mMenuRowViews.get(i);
- if (row.isVisible() && (view.getVisibility() == View.GONE
- || mRemovingRowViews.contains(i))) {
+ if (row.isVisible()
+ && (view.getVisibility() == View.GONE || mRemovingRowViews.contains(i))) {
// Removing rows are still VISIBLE.
addedRowViews.add(i);
++added;
@@ -691,8 +812,8 @@ public class MenuLayoutManager {
for (int i = mSelectedPosition + 1; i < count; ++i) {
MenuRow row = mMenuRows.get(i);
MenuRowView view = mMenuRowViews.get(i);
- if (row.isVisible() && (view.getVisibility() == View.GONE
- || mRemovingRowViews.contains(i))) {
+ if (row.isVisible()
+ && (view.getVisibility() == View.GONE || mRemovingRowViews.contains(i))) {
// Removing rows are still VISIBLE.
addedRowViews.add(i);
++added;
@@ -717,8 +838,14 @@ public class MenuLayoutManager {
}
mPropertyValuesAfterAnimation.clear();
List<Animator> animators = new ArrayList<>();
- List<Rect> layouts = getViewLayouts(mMenuView.getLeft(), mMenuView.getTop(),
- mMenuView.getRight(), mMenuView.getBottom(), addedRowViews, removedRowViews);
+ List<Rect> layouts =
+ getViewLayouts(
+ mMenuView.getLeft(),
+ mMenuView.getTop(),
+ mMenuView.getRight(),
+ mMenuView.getBottom(),
+ addedRowViews,
+ removedRowViews);
for (int position : addedRowViews) {
MenuRowView view = mMenuRowViews.get(position);
view.setVisibility(View.VISIBLE);
@@ -728,7 +855,8 @@ public class MenuLayoutManager {
view.layout(rect.left, rect.top, rect.right, rect.bottom);
View titleView = view.getTitleView();
MarginLayoutParams params = (MarginLayoutParams) titleView.getLayoutParams();
- titleView.layout(view.getPaddingLeft() + params.leftMargin,
+ titleView.layout(
+ view.getPaddingLeft() + params.leftMargin,
view.getPaddingTop() + params.topMargin,
rect.right - rect.left - view.getPaddingRight() - params.rightMargin,
rect.bottom - rect.top - view.getPaddingBottom() - params.bottomMargin);
@@ -749,23 +877,28 @@ public class MenuLayoutManager {
mRemovingRowViews.addAll(removedRowViews);
mAnimatorSet = new AnimatorSet();
mAnimatorSet.playTogether(animators);
- mAnimatorSet.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mAnimatorSet = null;
- // The property values which are different from the end values and need to be
- // changed after the animation are set here.
- // e.g. setting translationY to 0, alpha of the contents view to 1.
- for (ViewPropertyValueHolder holder : propertyValuesAfterAnimation) {
- holder.property.set(holder.view, holder.value);
- }
- for (int position : mRemovingRowViews) {
- mMenuRowViews.get(position).setVisibility(View.GONE);
- }
- layout(mMenuView.getLeft(), mMenuView.getTop(), mMenuView.getRight(),
- mMenuView.getBottom());
- }
- });
+ mAnimatorSet.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimatorSet = null;
+ // The property values which are different from the end values and need to
+ // be
+ // changed after the animation are set here.
+ // e.g. setting translationY to 0, alpha of the contents view to 1.
+ for (ViewPropertyValueHolder holder : propertyValuesAfterAnimation) {
+ holder.property.set(holder.view, holder.value);
+ }
+ for (int position : mRemovingRowViews) {
+ mMenuRowViews.get(position).setVisibility(View.GONE);
+ }
+ layout(
+ mMenuView.getLeft(),
+ mMenuView.getTop(),
+ mMenuView.getRight(),
+ mMenuView.getBottom());
+ }
+ });
mAnimatorSet.start();
if (DEBUG) dumpChildren("onMenuRowUpdated()");
}
@@ -778,16 +911,16 @@ public class MenuLayoutManager {
return animator;
}
- private ObjectAnimator createAlphaAnimator(View view, float from, float to,
- TimeInterpolator interpolator) {
+ private ObjectAnimator createAlphaAnimator(
+ View view, float from, float to, TimeInterpolator interpolator) {
ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.ALPHA, from, to);
animator.setDuration(mRowAnimationDuration);
animator.setInterpolator(interpolator);
return animator;
}
- private ObjectAnimator createAlphaAnimator(View view, float from, float to, float end,
- TimeInterpolator interpolator) {
+ private ObjectAnimator createAlphaAnimator(
+ View view, float from, float to, float end, TimeInterpolator interpolator) {
ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.ALPHA, from, to);
animator.setDuration(mRowAnimationDuration);
animator.setInterpolator(interpolator);
@@ -809,9 +942,7 @@ public class MenuLayoutManager {
return animator;
}
- /**
- * Returns the current position.
- */
+ /** Returns the current position. */
public int getSelectedPosition() {
return mSelectedPosition;
}
@@ -828,15 +959,10 @@ public class MenuLayoutManager {
}
}
- /**
- * Called when the menu becomes visible.
- */
- public void onMenuShow() {
- }
+ /** Called when the menu becomes visible. */
+ public void onMenuShow() {}
- /**
- * Called when the menu becomes hidden.
- */
+ /** Called when the menu becomes hidden. */
public void onMenuHide() {
if (mAnimatorSet != null) {
mAnimatorSet.end();
diff --git a/src/com/android/tv/menu/MenuRow.java b/src/com/android/tv/menu/MenuRow.java
index 47804f11..8dc12bad 100644
--- a/src/com/android/tv/menu/MenuRow.java
+++ b/src/com/android/tv/menu/MenuRow.java
@@ -17,13 +17,11 @@
package com.android.tv.menu;
import android.content.Context;
-import android.view.View;
/**
- * A base class of the item which will be displayed in the main menu.
- * It contains the data such as title to represent a row.
- * This is an abstract class and the sub-class could have it's own data for
- * the row.
+ * A base class of the item which will be displayed in the main menu. It contains the data such as
+ * title to represent a row. This is an abstract class and the sub-class could have it's own data
+ * for the row.
*/
public abstract class MenuRow {
private final Context mContext;
@@ -45,86 +43,60 @@ public abstract class MenuRow {
mHeight = context.getResources().getDimensionPixelSize(heightResId);
}
- /**
- * Returns the context.
- */
+ /** Returns the context. */
protected Context getContext() {
return mContext;
}
- /**
- * Returns the menu object.
- */
+ /** Returns the menu object. */
public Menu getMenu() {
return mMenu;
}
- /**
- * Returns the title of this row.
- */
+ /** Returns the title of this row. */
public String getTitle() {
return mTitle;
}
- /**
- * Returns the height of this row.
- */
+ /** Returns the height of this row. */
public int getHeight() {
return mHeight;
}
- /**
- * Sets the menu row view.
- */
+ /** Sets the menu row view. */
public void setMenuRowView(MenuRowView menuRowView) {
mMenuRowView = menuRowView;
}
- /**
- * Returns the menu row view.
- */
+ /** Returns the menu row view. */
protected MenuRowView getMenuRowView() {
return mMenuRowView;
}
- /**
- * Updates the contents in this row.
- * This method is called only by the menu when necessary.
- */
- abstract public void update();
+ /** Updates the contents in this row. This method is called only by the menu when necessary. */
+ public abstract void update();
- /**
- * Indicates whether this row is shown in the menu.
- */
+ /** Indicates whether this row is shown in the menu. */
public boolean isVisible() {
return true;
}
/**
- * Releases all the resources which need to be released.
- * This method is called when the main menu is not available any more.
+ * Releases all the resources which need to be released. This method is called when the main
+ * menu is not available any more.
*/
- public void release() {
- }
+ public void release() {}
- /**
- * Returns the ID of the layout resource for this row.
- */
- abstract public int getLayoutResId();
+ /** Returns the ID of the layout resource for this row. */
+ public abstract int getLayoutResId();
- /**
- * Returns the ID of this row. This ID is used to select the row in the main menu.
- */
- abstract public String getId();
+ /** Returns the ID of this row. This ID is used to select the row in the main menu. */
+ public abstract String getId();
- /**
- * This method is called when recent channels are changed.
- */
- public void onRecentChannelsChanged() { }
+ /** This method is called when recent channels are changed. */
+ public void onRecentChannelsChanged() {}
- /**
- * Returns whether to hide the title when the row is selected.
- */
+ /** Returns whether to hide the title when the row is selected. */
public boolean hideTitleWhenSelected() {
return false;
}
diff --git a/src/com/android/tv/menu/MenuRowFactory.java b/src/com/android/tv/menu/MenuRowFactory.java
index 570cfb8f..048d725d 100644
--- a/src/com/android/tv/menu/MenuRowFactory.java
+++ b/src/com/android/tv/menu/MenuRowFactory.java
@@ -19,80 +19,76 @@ package com.android.tv.menu;
import android.content.Context;
import android.support.annotation.Nullable;
import android.text.TextUtils;
-
import com.android.tv.MainActivity;
import com.android.tv.R;
-import com.android.tv.customization.CustomAction;
-import com.android.tv.customization.TvCustomizationManager;
+import com.android.tv.common.customization.CustomAction;
+import com.android.tv.common.customization.CustomizationManager;
import com.android.tv.ui.TunableTvView;
-
import java.util.List;
-/**
- * A factory class to create menu rows.
- */
+/** A factory class to create menu rows. */
public class MenuRowFactory {
private final MainActivity mMainActivity;
private final TunableTvView mTvView;
- private final TvCustomizationManager mTvCustomizationManager;
+ private final CustomizationManager mCustomizationManager;
- /**
- * A constructor.
- */
+ /** A constructor. */
public MenuRowFactory(MainActivity mainActivity, TunableTvView tvView) {
mMainActivity = mainActivity;
mTvView = tvView;
- mTvCustomizationManager = new TvCustomizationManager(mainActivity);
- mTvCustomizationManager.initialize();
+ mCustomizationManager = new CustomizationManager(mainActivity);
+ mCustomizationManager.initialize();
}
- /**
- * Creates an object corresponding to the given {@code key}.
- */
+ /** Creates an object corresponding to the given {@code key}. */
@Nullable
public MenuRow createMenuRow(Menu menu, Class<?> key) {
if (PlayControlsRow.class.equals(key)) {
- return new PlayControlsRow(mMainActivity, mTvView, menu,
- mMainActivity.getTimeShiftManager());
+ return new PlayControlsRow(
+ mMainActivity, mTvView, menu, mMainActivity.getTimeShiftManager());
} else if (ChannelsRow.class.equals(key)) {
return new ChannelsRow(mMainActivity, menu, mMainActivity.getProgramDataManager());
} else if (PartnerRow.class.equals(key)) {
- List<CustomAction> customActions = mTvCustomizationManager.getCustomActions(
- TvCustomizationManager.ID_PARTNER_ROW);
- String title = mTvCustomizationManager.getPartnerRowTitle();
+ List<CustomAction> customActions =
+ mCustomizationManager.getCustomActions(CustomizationManager.ID_PARTNER_ROW);
+ String title = mCustomizationManager.getPartnerRowTitle();
if (customActions != null && !TextUtils.isEmpty(title)) {
return new PartnerRow(mMainActivity, menu, title, customActions);
}
return null;
} else if (TvOptionsRow.class.equals(key)) {
- return new TvOptionsRow(mMainActivity, menu, mTvCustomizationManager
- .getCustomActions(TvCustomizationManager.ID_OPTIONS_ROW));
+ return new TvOptionsRow(
+ mMainActivity,
+ menu,
+ mCustomizationManager.getCustomActions(CustomizationManager.ID_OPTIONS_ROW));
}
return null;
}
- /**
- * A menu row which represents the TV options row.
- */
+ /** A menu row which represents the TV options row. */
public static class TvOptionsRow extends ItemListRow {
- /**
- * The ID of the row.
- */
+ /** The ID of the row. */
public static final String ID = TvOptionsRow.class.getName();
private TvOptionsRow(Context context, Menu menu, List<CustomAction> customActions) {
- super(context, menu, R.string.menu_title_options, R.dimen.action_card_height,
+ super(
+ context,
+ menu,
+ R.string.menu_title_options,
+ R.dimen.action_card_height,
new TvOptionsRowAdapter(context, customActions));
}
}
- /**
- * A menu row which represents the partner row.
- */
+ /** A menu row which represents the partner row. */
public static class PartnerRow extends ItemListRow {
- private PartnerRow(Context context, Menu menu, String title,
- List<CustomAction> customActions) {
- super(context, menu, title, R.dimen.action_card_height,
+ private PartnerRow(
+ Context context, Menu menu, String title, List<CustomAction> customActions) {
+ super(
+ context,
+ menu,
+ title,
+ R.dimen.action_card_height,
new PartnerOptionsRowAdapter(context, customActions));
}
}
diff --git a/src/com/android/tv/menu/MenuRowView.java b/src/com/android/tv/menu/MenuRowView.java
index 97dea29a..a064f352 100644
--- a/src/com/android/tv/menu/MenuRowView.java
+++ b/src/com/android/tv/menu/MenuRowView.java
@@ -27,7 +27,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
-
import com.android.tv.R;
import com.android.tv.menu.Menu.MenuShowReason;
@@ -46,25 +45,23 @@ public abstract class MenuRowView extends LinearLayout {
* reset when the menu is popped up.
*/
private View mLastFocusView;
+
private MenuRow mRow;
- private final OnFocusChangeListener mOnFocusChangeListener = new OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- onChildFocusChange(v, hasFocus);
- }
- };
+ private final OnFocusChangeListener mOnFocusChangeListener =
+ new OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ onChildFocusChange(v, hasFocus);
+ }
+ };
- /**
- * Returns the alpha value of the title view when it's deselected.
- */
+ /** Returns the alpha value of the title view when it's deselected. */
public float getTitleViewAlphaDeselected() {
return mTitleViewAlphaDeselected;
}
- /**
- * Returns the scale value of the title view when it's selected.
- */
+ /** Returns the scale value of the title view when it's selected. */
public float getTitleViewScaleSelected() {
return mTitleViewScaleSelected;
}
@@ -125,26 +122,22 @@ public abstract class MenuRowView extends LinearLayout {
}
}
- abstract protected int getContentsViewId();
+ protected abstract int getContentsViewId();
- /**
- * Returns the title view.
- */
+ /** Returns the title view. */
public final TextView getTitleView() {
return mTitleView;
}
- /**
- * Returns the contents view.
- */
+ /** Returns the contents view. */
public final View getContentsView() {
return mContentsView;
}
/**
- * Initialize this view. e.g. Set the initial selection.
- * This method is called when the main menu is visible.
- * Subclass of {@link MenuRowView} should override this to set correct mLastFocusView.
+ * Initialize this view. e.g. Set the initial selection. This method is called when the main
+ * menu is visible. Subclass of {@link MenuRowView} should override this to set correct
+ * mLastFocusView.
*
* @param reason A reason why this is initialized. See {@link MenuShowReason}
*/
@@ -177,17 +170,17 @@ public abstract class MenuRowView extends LinearLayout {
}
/**
- * Sets the view which needs to have focus when this row appears.
- * Subclasses should call this in {@link #initialize} if needed.
+ * Sets the view which needs to have focus when this row appears. Subclasses should call this in
+ * {@link #initialize} if needed.
*/
protected void setInitialFocusView(@NonNull View v) {
mLastFocusView = v;
}
/**
- * Called when the focus of a child view is changed.
- * The inherited class should override this method instead of calling
- * {@link android.view.View#setOnFocusChangeListener(android.view.View.OnFocusChangeListener)}.
+ * Called when the focus of a child view is changed. The inherited class should override this
+ * method instead of calling {@link
+ * android.view.View#setOnFocusChangeListener(android.view.View.OnFocusChangeListener)}.
*/
protected void onChildFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
@@ -195,9 +188,7 @@ public abstract class MenuRowView extends LinearLayout {
}
}
- /**
- * Returns the ID of row object bound to this view.
- */
+ /** Returns the ID of row object bound to this view. */
public String getRowId() {
return mRow == null ? null : mRow.getId();
}
@@ -206,7 +197,7 @@ public abstract class MenuRowView extends LinearLayout {
* Called when this row is selected.
*
* @param showTitle If {@code true}, the title is not hidden immediately after the row is
- * selected even though hideTitleWhenSelected() is {@code true}.
+ * selected even though hideTitleWhenSelected() is {@code true}.
*/
public void onSelected(boolean showTitle) {
if (mRow.hideTitleWhenSelected() && !showTitle) {
@@ -225,9 +216,7 @@ public abstract class MenuRowView extends LinearLayout {
mLastFocusView = lastFocusView;
}
- /**
- * Called when this row is deselected.
- */
+ /** Called when this row is deselected. */
public void onDeselected() {
mTitleView.setVisibility(VISIBLE);
mTitleView.setAlpha(mTitleViewAlphaDeselected);
@@ -236,9 +225,7 @@ public abstract class MenuRowView extends LinearLayout {
mContentsView.setVisibility(GONE);
}
- /**
- * Returns the preferred height of the contents view. The top/bottom padding is excluded.
- */
+ /** Returns the preferred height of the contents view. The top/bottom padding is excluded. */
public int getPreferredContentsHeight() {
return mRow.getHeight();
}
diff --git a/src/com/android/tv/menu/MenuUpdater.java b/src/com/android/tv/menu/MenuUpdater.java
index 18416c85..5d277824 100644
--- a/src/com/android/tv/menu/MenuUpdater.java
+++ b/src/com/android/tv/menu/MenuUpdater.java
@@ -17,12 +17,11 @@
package com.android.tv.menu;
import android.support.annotation.Nullable;
-
import com.android.tv.ChannelTuner;
import com.android.tv.TvOptionsManager;
import com.android.tv.TvOptionsManager.OptionChangedListener;
import com.android.tv.TvOptionsManager.OptionType;
-import com.android.tv.data.Channel;
+import com.android.tv.data.api.Channel;
import com.android.tv.menu.MenuRowFactory.TvOptionsRow;
import com.android.tv.ui.TunableTvView;
import com.android.tv.ui.TunableTvView.OnScreenBlockingChangedListener;
@@ -39,49 +38,52 @@ public class MenuUpdater {
@Nullable private final TvOptionsManager mOptionsManager;
private ChannelTuner mChannelTuner;
- private final ChannelTuner.Listener mChannelTunerListener = new ChannelTuner.Listener() {
- @Override
- public void onLoadFinished() {}
+ private final ChannelTuner.Listener mChannelTunerListener =
+ new ChannelTuner.Listener() {
+ @Override
+ public void onLoadFinished() {}
- @Override
- public void onBrowsableChannelListChanged() {
- mMenu.update(ChannelsRow.ID);
- }
+ @Override
+ public void onBrowsableChannelListChanged() {
+ mMenu.update(ChannelsRow.ID);
+ }
- @Override
- public void onCurrentChannelUnavailable(Channel channel) {}
+ @Override
+ public void onCurrentChannelUnavailable(Channel channel) {}
- @Override
- public void onChannelChanged(Channel previousChannel, Channel currentChannel) {
- mMenu.update(ChannelsRow.ID);
- }
- };
- private final OptionChangedListener mOptionChangeListener = new OptionChangedListener() {
- @Override
- public void onOptionChanged(@OptionType int optionType, String newString) {
- mMenu.update(TvOptionsRow.ID);
- }
- };
+ @Override
+ public void onChannelChanged(Channel previousChannel, Channel currentChannel) {
+ mMenu.update(ChannelsRow.ID);
+ }
+ };
+ private final OptionChangedListener mOptionChangeListener =
+ new OptionChangedListener() {
+ @Override
+ public void onOptionChanged(@OptionType int optionType, String newString) {
+ mMenu.update(TvOptionsRow.ID);
+ }
+ };
public MenuUpdater(Menu menu, TunableTvView tvView, TvOptionsManager optionsManager) {
mMenu = menu;
mTvView = tvView;
mOptionsManager = optionsManager;
if (mTvView != null) {
- mTvView.setOnScreenBlockedListener(new OnScreenBlockingChangedListener() {
- @Override
- public void onScreenBlockingChanged(boolean blocked) {
- mMenu.update(PlayControlsRow.ID);
- }
- });
+ mTvView.setOnScreenBlockedListener(
+ new OnScreenBlockingChangedListener() {
+ @Override
+ public void onScreenBlockingChanged(boolean blocked) {
+ mMenu.update(PlayControlsRow.ID);
+ }
+ });
}
if (mOptionsManager != null) {
- mOptionsManager.setOptionChangedListener(TvOptionsManager.OPTION_CLOSED_CAPTIONS,
- mOptionChangeListener);
- mOptionsManager.setOptionChangedListener(TvOptionsManager.OPTION_DISPLAY_MODE,
- mOptionChangeListener);
- mOptionsManager.setOptionChangedListener(TvOptionsManager.OPTION_MULTI_AUDIO,
- mOptionChangeListener);
+ mOptionsManager.setOptionChangedListener(
+ TvOptionsManager.OPTION_CLOSED_CAPTIONS, mOptionChangeListener);
+ mOptionsManager.setOptionChangedListener(
+ TvOptionsManager.OPTION_DISPLAY_MODE, mOptionChangeListener);
+ mOptionsManager.setOptionChangedListener(
+ TvOptionsManager.OPTION_MULTI_AUDIO, mOptionChangeListener);
}
}
@@ -98,16 +100,12 @@ public class MenuUpdater {
}
}
- /**
- * Called when the stream information changes.
- */
+ /** Called when the stream information changes. */
public void onStreamInfoChanged() {
mMenu.update(TvOptionsRow.ID);
}
- /**
- * Called at the end of the menu's lifetime.
- */
+ /** Called at the end of the menu's lifetime. */
public void release() {
if (mChannelTuner != null) {
mChannelTuner.removeListener(mChannelTunerListener);
diff --git a/src/com/android/tv/menu/MenuView.java b/src/com/android/tv/menu/MenuView.java
index ee0b036e..f5fec000 100644
--- a/src/com/android/tv/menu/MenuView.java
+++ b/src/com/android/tv/menu/MenuView.java
@@ -26,15 +26,11 @@ import android.view.ViewParent;
import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.FrameLayout;
-
import com.android.tv.menu.Menu.MenuShowReason;
-
import java.util.ArrayList;
import java.util.List;
-/**
- * A view that represents TV main menu.
- */
+/** A view that represents TV main menu. */
public class MenuView extends FrameLayout implements IMenuView {
static final String TAG = MenuView.class.getSimpleName();
static final boolean DEBUG = false;
@@ -60,20 +56,25 @@ public class MenuView extends FrameLayout implements IMenuView {
mLayoutInflater = LayoutInflater.from(context);
// Set hardware layer type for smooth animation of lots of views.
setLayerType(LAYER_TYPE_HARDWARE, null);
- getViewTreeObserver().addOnGlobalFocusChangeListener(new OnGlobalFocusChangeListener() {
- @Override
- public void onGlobalFocusChanged(View oldFocus, View newFocus) {
- MenuRowView newParent = getParentMenuRowView(newFocus);
- if (newParent != null) {
- if (DEBUG) Log.d(TAG, "Focus changed to " + newParent);
- // When the row is selected, the row view itself has the focus because the row
- // is collapsed. To make the child of the row have the focus, requestFocus()
- // should be called again after the row is expanded. It's done in
- // setSelectedPosition().
- setSelectedPositionSmooth(mMenuRowViews.indexOf(newParent));
- }
- }
- });
+ getViewTreeObserver()
+ .addOnGlobalFocusChangeListener(
+ new OnGlobalFocusChangeListener() {
+ @Override
+ public void onGlobalFocusChanged(View oldFocus, View newFocus) {
+ MenuRowView newParent = getParentMenuRowView(newFocus);
+ if (newParent != null) {
+ if (DEBUG) Log.d(TAG, "Focus changed to " + newParent);
+ // When the row is selected, the row view itself has the focus
+ // because the row
+ // is collapsed. To make the child of the row have the focus,
+ // requestFocus()
+ // should be called again after the row is expanded. It's done
+ // in
+ // setSelectedPosition().
+ setSelectedPositionSmooth(mMenuRowViews.indexOf(newParent));
+ }
+ }
+ });
mLayoutManager = new MenuLayoutManager(context, this);
}
@@ -102,8 +103,8 @@ public class MenuView extends FrameLayout implements IMenuView {
}
@Override
- public void onShow(@MenuShowReason int reason, String rowIdToSelect,
- final Runnable runnableAfterShow) {
+ public void onShow(
+ @MenuShowReason int reason, String rowIdToSelect, final Runnable runnableAfterShow) {
if (DEBUG) {
Log.d(TAG, "onShow(reason=" + reason + ", rowIdToSelect=" + rowIdToSelect + ")");
}
@@ -132,15 +133,18 @@ public class MenuView extends FrameLayout implements IMenuView {
// Make the selected row have the focus.
requestFocus();
if (runnableAfterShow != null) {
- getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- getViewTreeObserver().removeOnGlobalLayoutListener(this);
- // Start show animation after layout finishes for smooth animation because the
- // layout can take long time.
- runnableAfterShow.run();
- }
- });
+ getViewTreeObserver()
+ .addOnGlobalLayoutListener(
+ new OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ // Start show animation after layout finishes for smooth
+ // animation because the
+ // layout can take long time.
+ runnableAfterShow.run();
+ }
+ });
}
mLayoutManager.onMenuShow();
}
diff --git a/src/com/android/tv/menu/OptionsRowAdapter.java b/src/com/android/tv/menu/OptionsRowAdapter.java
index dd6194a1..ceffe861 100644
--- a/src/com/android/tv/menu/OptionsRowAdapter.java
+++ b/src/com/android/tv/menu/OptionsRowAdapter.java
@@ -18,11 +18,9 @@ package com.android.tv.menu;
import android.content.Context;
import android.view.View;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.analytics.Tracker;
-
import java.util.List;
/*
@@ -33,33 +31,33 @@ public abstract class OptionsRowAdapter extends ItemListRowView.ItemListAdapter<
protected final Tracker mTracker;
private List<MenuAction> mActionList;
- private final View.OnClickListener mMenuActionOnClickListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- final MenuAction action = (MenuAction) view.getTag();
- view.post(new Runnable() {
+ private final View.OnClickListener mMenuActionOnClickListener =
+ new View.OnClickListener() {
@Override
- public void run() {
- int resId = action.getActionNameResId();
- if (resId == 0) {
- mTracker.sendMenuClicked(CUSTOM_ACTION_LABEL);
- } else {
- mTracker.sendMenuClicked(resId);
- }
- executeAction(action.getType());
+ public void onClick(View view) {
+ final MenuAction action = (MenuAction) view.getTag();
+ view.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ int resId = action.getActionNameResId();
+ if (resId == 0) {
+ mTracker.sendMenuClicked(CUSTOM_ACTION_LABEL);
+ } else {
+ mTracker.sendMenuClicked(resId);
+ }
+ executeAction(action.getType());
+ }
+ });
}
- });
- }
- };
+ };
public OptionsRowAdapter(Context context) {
super(context);
- mTracker = TvApplication.getSingletons(context).getTracker();
+ mTracker = TvSingletons.getSingletons(context).getTracker();
}
- /**
- * Update action list and its content.
- */
+ /** Update action list and its content. */
@Override
public void update() {
if (mActionList == null) {
@@ -76,13 +74,14 @@ public abstract class OptionsRowAdapter extends ItemListRowView.ItemListAdapter<
}
protected abstract List<MenuAction> createActions();
+
protected abstract void updateActions();
+
protected abstract void executeAction(int type);
/**
- * Gets the action at the given position.
- * Note that action at the position may differ from returned by {@link #createActions}.
- * See {@link CustomizableOptionsRowAdapter}
+ * Gets the action at the given position. Note that action at the position may differ from
+ * returned by {@link #createActions}. See {@link CustomizableOptionsRowAdapter}
*/
protected MenuAction getAction(int position) {
return mActionList.get(position);
diff --git a/src/com/android/tv/menu/PartnerOptionsRowAdapter.java b/src/com/android/tv/menu/PartnerOptionsRowAdapter.java
index c8249a4c..9676fe4d 100644
--- a/src/com/android/tv/menu/PartnerOptionsRowAdapter.java
+++ b/src/com/android/tv/menu/PartnerOptionsRowAdapter.java
@@ -17,9 +17,7 @@
package com.android.tv.menu;
import android.content.Context;
-
-import com.android.tv.customization.CustomAction;
-
+import com.android.tv.common.customization.CustomAction;
import java.util.Collections;
import java.util.List;
@@ -34,8 +32,7 @@ public class PartnerOptionsRowAdapter extends CustomizableOptionsRowAdapter {
}
@Override
- protected void executeBaseAction(int option) {
- }
+ protected void executeBaseAction(int option) {}
@Override
protected void updateActions() {
diff --git a/src/com/android/tv/menu/PlayControlsButton.java b/src/com/android/tv/menu/PlayControlsButton.java
index 77715f28..ac3292a3 100644
--- a/src/com/android/tv/menu/PlayControlsButton.java
+++ b/src/com/android/tv/menu/PlayControlsButton.java
@@ -25,7 +25,6 @@ import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
-
import com.android.tv.R;
public class PlayControlsButton extends FrameLayout {
@@ -54,8 +53,8 @@ public class PlayControlsButton extends FrameLayout {
this(context, attrs, defStyleAttr, 0);
}
- public PlayControlsButton(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
+ public PlayControlsButton(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
inflate(context, R.layout.play_controls_button, this);
mButton = (ImageView) findViewById(R.id.button);
@@ -66,9 +65,7 @@ public class PlayControlsButton extends FrameLayout {
mIconFocusedColor = mIconColor;
}
- /**
- * Sets the resource ID of the image to be displayed in the center of this control.
- */
+ /** Sets the resource ID of the image to be displayed in the center of this control. */
public void setImageResId(int imageResId) {
int newTintColor = hasFocus() ? mIconFocusedColor : mIconColor;
if (mImageResourceId != imageResId) {
@@ -87,40 +84,39 @@ public class PlayControlsButton extends FrameLayout {
mIcon.getDrawable().setTint(tintColor);
}
- /**
- * Sets an action which is to be run when the button is clicked.
- */
+ /** Sets an action which is to be run when the button is clicked. */
public void setAction(final Runnable clickAction) {
- mButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View view) {
- clickAction.run();
- }
- });
+ mButton.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ clickAction.run();
+ }
+ });
}
- /**
- * Sets the icon's color should change to when the button is on focus.
- */
+ /** Sets the icon's color should change to when the button is on focus. */
public void setFocusedIconColor(int color) {
final ValueAnimator valueAnimator = ValueAnimator.ofArgb(mIconColor, color);
- valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(final ValueAnimator animator) {
- mIcon.getDrawable().setTint((int) animator.getAnimatedValue());
- }
- });
+ valueAnimator.addUpdateListener(
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(final ValueAnimator animator) {
+ mIcon.getDrawable().setTint((int) animator.getAnimatedValue());
+ }
+ });
valueAnimator.setDuration(mFocusAnimationTimeMs);
- mButton.setOnFocusChangeListener(new View.OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- if (hasFocus) {
- valueAnimator.start();
- } else {
- valueAnimator.reverse();
- }
- }
- });
+ mButton.setOnFocusChangeListener(
+ new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (hasFocus) {
+ valueAnimator.start();
+ } else {
+ valueAnimator.reverse();
+ }
+ }
+ });
mIconFocusedColor = color;
}
diff --git a/src/com/android/tv/menu/PlayControlsRow.java b/src/com/android/tv/menu/PlayControlsRow.java
index a60ff153..f19bd2b0 100644
--- a/src/com/android/tv/menu/PlayControlsRow.java
+++ b/src/com/android/tv/menu/PlayControlsRow.java
@@ -17,7 +17,6 @@
package com.android.tv.menu;
import android.content.Context;
-
import com.android.tv.R;
import com.android.tv.TimeShiftManager;
import com.android.tv.ui.TunableTvView;
@@ -28,8 +27,8 @@ public class PlayControlsRow extends MenuRow {
private final TunableTvView mTvView;
private final TimeShiftManager mTimeShiftManager;
- public PlayControlsRow(Context context, TunableTvView tvView, Menu menu,
- TimeShiftManager timeShiftManager) {
+ public PlayControlsRow(
+ Context context, TunableTvView tvView, Menu menu, TimeShiftManager timeShiftManager) {
super(context, menu, R.string.menu_title_play_controls, R.dimen.play_controls_height);
mTvView = tvView;
mTimeShiftManager = timeShiftManager;
@@ -45,16 +44,12 @@ public class PlayControlsRow extends MenuRow {
return R.layout.play_controls;
}
- /**
- * Returns TV view.
- */
+ /** Returns TV view. */
public TunableTvView getTvView() {
return mTvView;
}
- /**
- * Returns an instance of {@link TimeShiftManager}.
- */
+ /** Returns an instance of {@link TimeShiftManager}. */
public TimeShiftManager getTimeShiftManager() {
return mTimeShiftManager;
}
diff --git a/src/com/android/tv/menu/PlayControlsRowView.java b/src/com/android/tv/menu/PlayControlsRowView.java
index 4d766788..496d1969 100644
--- a/src/com/android/tv/menu/PlayControlsRowView.java
+++ b/src/com/android/tv/menu/PlayControlsRowView.java
@@ -24,16 +24,15 @@ import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
-
import com.android.tv.MainActivity;
import com.android.tv.R;
import com.android.tv.TimeShiftManager;
import com.android.tv.TimeShiftManager.TimeShiftActionId;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
import com.android.tv.common.feature.CommonFeatures;
-import com.android.tv.data.Channel;
import com.android.tv.data.Program;
+import com.android.tv.data.api.Channel;
import com.android.tv.dialog.HalfSizedDialogFragment;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrDataManager.OnDvrScheduleLoadFinishedListener;
@@ -79,27 +78,29 @@ public class PlayControlsRowView extends MenuRowView {
private final String mUnavailableMessage;
- private final ScheduledRecordingListener mScheduledRecordingListener
- = new ScheduledRecordingListener() {
- @Override
- public void onScheduledRecordingAdded(ScheduledRecording... scheduledRecordings) { }
-
- @Override
- public void onScheduledRecordingRemoved(ScheduledRecording... scheduledRecordings) { }
-
- @Override
- public void onScheduledRecordingStatusChanged(ScheduledRecording... scheduledRecordings) {
- Channel currentChannel = mMainActivity.getCurrentChannel();
- if (currentChannel != null && isShown()) {
- for (ScheduledRecording schedule : scheduledRecordings) {
- if (schedule.getChannelId() == currentChannel.getId()) {
- updateRecordButton();
- break;
+ private final ScheduledRecordingListener mScheduledRecordingListener =
+ new ScheduledRecordingListener() {
+ @Override
+ public void onScheduledRecordingAdded(ScheduledRecording... scheduledRecordings) {}
+
+ @Override
+ public void onScheduledRecordingRemoved(
+ ScheduledRecording... scheduledRecordings) {}
+
+ @Override
+ public void onScheduledRecordingStatusChanged(
+ ScheduledRecording... scheduledRecordings) {
+ Channel currentChannel = mMainActivity.getCurrentChannel();
+ if (currentChannel != null && isShown()) {
+ for (ScheduledRecording schedule : scheduledRecordings) {
+ if (schedule.getChannelId() == currentChannel.getId()) {
+ updateRecordButton();
+ break;
+ }
+ }
}
}
- }
- }
- };
+ };
public PlayControlsRowView(Context context) {
this(context, null);
@@ -113,22 +114,21 @@ public class PlayControlsRowView extends MenuRowView {
this(context, attrs, defStyleAttr, 0);
}
- public PlayControlsRowView(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
+ public PlayControlsRowView(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
Resources res = context.getResources();
mTimeIndicatorLeftMargin =
- - res.getDimensionPixelSize(R.dimen.play_controls_time_indicator_width) / 2;
- mTimeTextLeftMargin =
- - res.getDimensionPixelOffset(R.dimen.play_controls_time_width) / 2;
+ -res.getDimensionPixelSize(R.dimen.play_controls_time_indicator_width) / 2;
+ mTimeTextLeftMargin = -res.getDimensionPixelOffset(R.dimen.play_controls_time_width) / 2;
mTimelineWidth = res.getDimensionPixelSize(R.dimen.play_controls_width);
mTimeFormat = DateFormat.getTimeFormat(context);
mNormalButtonMargin = res.getDimensionPixelSize(R.dimen.play_controls_button_normal_margin);
mCompactButtonMargin =
res.getDimensionPixelSize(R.dimen.play_controls_button_compact_margin);
if (CommonFeatures.DVR.isEnabled(context)) {
- mDvrDataManager = TvApplication.getSingletons(context).getDvrDataManager();
- mDvrManager = TvApplication.getSingletons(context).getDvrManager();
+ mDvrDataManager = TvSingletons.getSingletons(context).getDvrDataManager();
+ mDvrManager = TvSingletons.getSingletons(context).getDvrManager();
} else {
mDvrDataManager = null;
mDvrManager = null;
@@ -154,7 +154,6 @@ public class PlayControlsRowView extends MenuRowView {
}
});
}
-
}
}
@@ -181,104 +180,141 @@ public class PlayControlsRowView extends MenuRowView {
mProgramStartTimeText = (TextView) findViewById(R.id.program_start_time);
mProgramEndTimeText = (TextView) findViewById(R.id.program_end_time);
- initializeButton(mJumpPreviousButton, R.drawable.lb_ic_skip_previous,
- R.string.play_controls_description_skip_previous, null, new Runnable() {
- @Override
- public void run() {
- if (mTimeShiftManager.isAvailable()) {
- mTimeShiftManager.jumpToPrevious();
- updateControls(true);
- }
- }
- });
- initializeButton(mRewindButton, R.drawable.lb_ic_fast_rewind,
- R.string.play_controls_description_fast_rewind, null, new Runnable() {
- @Override
- public void run() {
- if (mTimeShiftManager.isAvailable()) {
- mTimeShiftManager.rewind();
- updateButtons();
- }
- }
- });
- initializeButton(mPlayPauseButton, R.drawable.lb_ic_play,
- R.string.play_controls_description_play_pause, null, new Runnable() {
- @Override
- public void run() {
- if (mTimeShiftManager.isAvailable()) {
- mTimeShiftManager.togglePlayPause();
- updateButtons();
- }
- }
- });
- initializeButton(mFastForwardButton, R.drawable.lb_ic_fast_forward,
- R.string.play_controls_description_fast_forward, null, new Runnable() {
- @Override
- public void run() {
- if (mTimeShiftManager.isAvailable()) {
- mTimeShiftManager.fastForward();
- updateButtons();
- }
- }
- });
- initializeButton(mJumpNextButton, R.drawable.lb_ic_skip_next,
- R.string.play_controls_description_skip_next, null, new Runnable() {
- @Override
- public void run() {
- if (mTimeShiftManager.isAvailable()) {
- mTimeShiftManager.jumpToNext();
- updateControls(true);
- }
- }
- });
- int color = getResources().getColor(R.color.play_controls_recording_icon_color_on_focus,
- null);
- initializeButton(mRecordButton, R.drawable.ic_record_start, R.string
- .channels_item_record_start, color, new Runnable() {
- @Override
- public void run() {
- onRecordButtonClicked();
- }
- });
+ initializeButton(
+ mJumpPreviousButton,
+ R.drawable.lb_ic_skip_previous,
+ R.string.play_controls_description_skip_previous,
+ null,
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mTimeShiftManager.isAvailable()) {
+ mTimeShiftManager.jumpToPrevious();
+ updateControls(true);
+ }
+ }
+ });
+ initializeButton(
+ mRewindButton,
+ R.drawable.lb_ic_fast_rewind,
+ R.string.play_controls_description_fast_rewind,
+ null,
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mTimeShiftManager.isAvailable()) {
+ mTimeShiftManager.rewind();
+ updateButtons();
+ }
+ }
+ });
+ initializeButton(
+ mPlayPauseButton,
+ R.drawable.lb_ic_play,
+ R.string.play_controls_description_play_pause,
+ null,
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mTimeShiftManager.isAvailable()) {
+ mTimeShiftManager.togglePlayPause();
+ updateButtons();
+ }
+ }
+ });
+ initializeButton(
+ mFastForwardButton,
+ R.drawable.lb_ic_fast_forward,
+ R.string.play_controls_description_fast_forward,
+ null,
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mTimeShiftManager.isAvailable()) {
+ mTimeShiftManager.fastForward();
+ updateButtons();
+ }
+ }
+ });
+ initializeButton(
+ mJumpNextButton,
+ R.drawable.lb_ic_skip_next,
+ R.string.play_controls_description_skip_next,
+ null,
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mTimeShiftManager.isAvailable()) {
+ mTimeShiftManager.jumpToNext();
+ updateControls(true);
+ }
+ }
+ });
+ int color =
+ getResources().getColor(R.color.play_controls_recording_icon_color_on_focus, null);
+ initializeButton(
+ mRecordButton,
+ R.drawable.ic_record_start,
+ R.string.channels_item_record_start,
+ color,
+ new Runnable() {
+ @Override
+ public void run() {
+ onRecordButtonClicked();
+ }
+ });
}
private boolean isCurrentChannelRecording() {
Channel currentChannel = mMainActivity.getCurrentChannel();
- return currentChannel != null && mDvrManager != null
+ return currentChannel != null
+ && mDvrManager != null
&& mDvrManager.getCurrentRecording(currentChannel.getId()) != null;
}
private void onRecordButtonClicked() {
boolean isRecording = isCurrentChannelRecording();
Channel currentChannel = mMainActivity.getCurrentChannel();
- TvApplication.getSingletons(getContext()).getTracker().sendMenuClicked(isRecording ?
- R.string.channels_item_record_start : R.string.channels_item_record_stop);
+ TvSingletons.getSingletons(getContext())
+ .getTracker()
+ .sendMenuClicked(
+ isRecording
+ ? R.string.channels_item_record_start
+ : R.string.channels_item_record_stop);
if (!isRecording) {
if (!(mDvrManager != null && mDvrManager.isChannelRecordable(currentChannel))) {
- Toast.makeText(mMainActivity, R.string.dvr_msg_cannot_record_channel,
- Toast.LENGTH_SHORT).show();
+ Toast.makeText(
+ mMainActivity,
+ R.string.dvr_msg_cannot_record_channel,
+ Toast.LENGTH_SHORT)
+ .show();
} else {
- Program program = TvApplication.getSingletons(mMainActivity).getProgramDataManager()
- .getCurrentProgram(currentChannel.getId());
- DvrUiHelper.checkStorageStatusAndShowErrorMessage(mMainActivity,
- currentChannel.getInputId(), new Runnable() {
+ Program program =
+ TvSingletons.getSingletons(mMainActivity)
+ .getProgramDataManager()
+ .getCurrentProgram(currentChannel.getId());
+ DvrUiHelper.checkStorageStatusAndShowErrorMessage(
+ mMainActivity,
+ currentChannel.getInputId(),
+ new Runnable() {
@Override
public void run() {
- DvrUiHelper.requestRecordingCurrentProgram(mMainActivity,
- currentChannel, program, true);
+ DvrUiHelper.requestRecordingCurrentProgram(
+ mMainActivity, currentChannel, program, true);
}
});
}
} else if (currentChannel != null) {
- DvrUiHelper.showStopRecordingDialog(mMainActivity, currentChannel.getId(),
+ DvrUiHelper.showStopRecordingDialog(
+ mMainActivity,
+ currentChannel.getId(),
DvrStopRecordingFragment.REASON_USER_STOP,
new HalfSizedDialogFragment.OnActionClickListener() {
@Override
public void onActionClick(long actionId) {
if (actionId == DvrStopRecordingFragment.ACTION_STOP) {
ScheduledRecording currentRecording =
- mDvrManager.getCurrentRecording(
- currentChannel.getId());
+ mDvrManager.getCurrentRecording(currentChannel.getId());
if (currentRecording != null) {
mDvrManager.stopRecording(currentRecording);
}
@@ -288,8 +324,12 @@ public class PlayControlsRowView extends MenuRowView {
}
}
- private void initializeButton(PlayControlsButton button, int imageResId,
- int descriptionId, Integer focusedIconColor, Runnable clickAction) {
+ private void initializeButton(
+ PlayControlsButton button,
+ int imageResId,
+ int descriptionId,
+ Integer focusedIconColor,
+ Runnable clickAction) {
button.setImageResId(imageResId);
button.setAction(clickAction);
if (focusedIconColor != null) {
@@ -305,69 +345,77 @@ public class PlayControlsRowView extends MenuRowView {
PlayControlsRow playControlsRow = (PlayControlsRow) row;
mTvView = playControlsRow.getTvView();
mTimeShiftManager = playControlsRow.getTimeShiftManager();
- mTimeShiftManager.setListener(new TimeShiftManager.Listener() {
- @Override
- public void onAvailabilityChanged() {
- updateMenuVisibility();
- PlayControlsRowView.this.updateAll(false);
- }
+ mTimeShiftManager.setListener(
+ new TimeShiftManager.Listener() {
+ @Override
+ public void onAvailabilityChanged() {
+ updateMenuVisibility();
+ PlayControlsRowView.this.updateAll(false);
+ }
- @Override
- public void onPlayStatusChanged(int status) {
- updateMenuVisibility();
- if (mTimeShiftManager.isAvailable()) {
- updateControls(false);
- }
- }
+ @Override
+ public void onPlayStatusChanged(int status) {
+ updateMenuVisibility();
+ if (mTimeShiftManager.isAvailable()) {
+ updateControls(false);
+ }
+ }
- @Override
- public void onRecordTimeRangeChanged() {
- if (mTimeShiftManager.isAvailable()) {
- updateControls(false);
- }
- }
+ @Override
+ public void onRecordTimeRangeChanged() {
+ if (mTimeShiftManager.isAvailable()) {
+ updateControls(false);
+ }
+ }
- @Override
- public void onCurrentPositionChanged() {
- if (mTimeShiftManager.isAvailable()) {
- initializeTimeline();
- updateControls(false);
- }
- }
+ @Override
+ public void onCurrentPositionChanged() {
+ if (mTimeShiftManager.isAvailable()) {
+ initializeTimeline();
+ updateControls(false);
+ }
+ }
- @Override
- public void onProgramInfoChanged() {
- if (mTimeShiftManager.isAvailable()) {
- initializeTimeline();
- updateControls(false);
- }
- }
+ @Override
+ public void onProgramInfoChanged() {
+ if (mTimeShiftManager.isAvailable()) {
+ initializeTimeline();
+ updateControls(false);
+ }
+ }
- @Override
- public void onActionEnabledChanged(@TimeShiftActionId int actionId, boolean enabled) {
- // Move focus to the play/pause button when the PREVIOUS, NEXT, REWIND or
- // FAST_FORWARD button is clicked and the button becomes disabled.
- // No need to update the UI here because the UI will be updated by other callbacks.
- if (!enabled &&
- ((actionId == TimeShiftManager.TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS
- && mJumpPreviousButton.hasFocus())
- || (actionId == TimeShiftManager.TIME_SHIFT_ACTION_ID_REWIND
- && mRewindButton.hasFocus())
- || (actionId == TimeShiftManager.TIME_SHIFT_ACTION_ID_FAST_FORWARD
- && mFastForwardButton.hasFocus())
- || (actionId == TimeShiftManager.TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT
- && mJumpNextButton.hasFocus()))) {
- mPlayPauseButton.requestFocus();
- }
- }
- });
+ @Override
+ public void onActionEnabledChanged(
+ @TimeShiftActionId int actionId, boolean enabled) {
+ // Move focus to the play/pause button when the PREVIOUS, NEXT, REWIND or
+ // FAST_FORWARD button is clicked and the button becomes disabled.
+ // No need to update the UI here because the UI will be updated by other
+ // callbacks.
+ if (!enabled
+ && ((actionId
+ == TimeShiftManager
+ .TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS
+ && mJumpPreviousButton.hasFocus())
+ || (actionId == TimeShiftManager.TIME_SHIFT_ACTION_ID_REWIND
+ && mRewindButton.hasFocus())
+ || (actionId
+ == TimeShiftManager
+ .TIME_SHIFT_ACTION_ID_FAST_FORWARD
+ && mFastForwardButton.hasFocus())
+ || (actionId
+ == TimeShiftManager
+ .TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT
+ && mJumpNextButton.hasFocus()))) {
+ mPlayPauseButton.requestFocus();
+ }
+ }
+ });
// force update to initialize everything
updateAll(true);
}
private void initializeTimeline() {
- Program program = mTimeShiftManager.getProgramAt(
- mTimeShiftManager.getCurrentPositionMs());
+ Program program = mTimeShiftManager.getProgramAt(mTimeShiftManager.getCurrentPositionMs());
mProgramStartTimeMs = program.getStartTimeUtcMillis();
mProgramEndTimeMs = program.getEndTimeUtcMillis();
mProgress.setMax(mProgramEndTimeMs - mProgramStartTimeMs);
@@ -441,16 +489,17 @@ public class PlayControlsRowView extends MenuRowView {
// Focus may be changed in another message if requestFocus is called in this message.
// After the focus is actually changed, hideRippleAnimation should run
// to reflect the result of the focus change. To be sure, hideRippleAnimation is posted.
- post(new Runnable() {
- @Override
- public void run() {
- mJumpPreviousButton.hideRippleAnimation();
- mRewindButton.hideRippleAnimation();
- mPlayPauseButton.hideRippleAnimation();
- mFastForwardButton.hideRippleAnimation();
- mJumpNextButton.hideRippleAnimation();
- }
- });
+ post(
+ new Runnable() {
+ @Override
+ public void run() {
+ mJumpPreviousButton.hideRippleAnimation();
+ mRewindButton.hideRippleAnimation();
+ mPlayPauseButton.hideRippleAnimation();
+ mFastForwardButton.hideRippleAnimation();
+ mJumpNextButton.hideRippleAnimation();
+ }
+ });
}
@Override
@@ -465,9 +514,7 @@ public class PlayControlsRowView extends MenuRowView {
}
}
- /**
- * Updates the view contents. It is called from the PlayControlsRow.
- */
+ /** Updates the view contents. It is called from the PlayControlsRow. */
public void update() {
updateAll(false);
}
@@ -516,13 +563,22 @@ public class PlayControlsRowView extends MenuRowView {
private void updateProgress() {
if (isEnabled()) {
- long progressStartTimeMs = Math.min(mProgramEndTimeMs,
- Math.max(mProgramStartTimeMs, mTimeShiftManager.getRecordStartTimeMs()));
- long currentPlayingTimeMs = Math.min(mProgramEndTimeMs,
- Math.max(mProgramStartTimeMs, mTimeShiftManager.getCurrentPositionMs()));
- long progressEndTimeMs = Math.min(mProgramEndTimeMs,
- Math.max(mProgramStartTimeMs, mTimeShiftManager.getRecordEndTimeMs()));
- mProgress.setProgressRange(progressStartTimeMs - mProgramStartTimeMs,
+ long progressStartTimeMs =
+ Math.min(
+ mProgramEndTimeMs,
+ Math.max(
+ mProgramStartTimeMs, mTimeShiftManager.getRecordStartTimeMs()));
+ long currentPlayingTimeMs =
+ Math.min(
+ mProgramEndTimeMs,
+ Math.max(
+ mProgramStartTimeMs, mTimeShiftManager.getCurrentPositionMs()));
+ long progressEndTimeMs =
+ Math.min(
+ mProgramEndTimeMs,
+ Math.max(mProgramStartTimeMs, mTimeShiftManager.getRecordEndTimeMs()));
+ mProgress.setProgressRange(
+ progressStartTimeMs - mProgramStartTimeMs,
progressEndTimeMs - mProgramStartTimeMs);
mProgress.setProgress(currentPlayingTimeMs - mProgramStartTimeMs);
} else {
@@ -560,21 +616,24 @@ public class PlayControlsRowView extends MenuRowView {
if (mTimeShiftManager.getPlayStatus() == TimeShiftManager.PLAY_STATUS_PAUSED) {
mPlayPauseButton.setImageResId(R.drawable.lb_ic_play);
- mPlayPauseButton.setEnabled(mTimeShiftManager.isActionEnabled(
- TimeShiftManager.TIME_SHIFT_ACTION_ID_PLAY));
+ mPlayPauseButton.setEnabled(
+ mTimeShiftManager.isActionEnabled(TimeShiftManager.TIME_SHIFT_ACTION_ID_PLAY));
} else {
mPlayPauseButton.setImageResId(R.drawable.lb_ic_pause);
- mPlayPauseButton.setEnabled(mTimeShiftManager.isActionEnabled(
- TimeShiftManager.TIME_SHIFT_ACTION_ID_PAUSE));
+ mPlayPauseButton.setEnabled(
+ mTimeShiftManager.isActionEnabled(TimeShiftManager.TIME_SHIFT_ACTION_ID_PAUSE));
}
- mJumpPreviousButton.setEnabled(mTimeShiftManager.isActionEnabled(
- TimeShiftManager.TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS));
- mRewindButton.setEnabled(mTimeShiftManager.isActionEnabled(
- TimeShiftManager.TIME_SHIFT_ACTION_ID_REWIND));
- mFastForwardButton.setEnabled(mTimeShiftManager.isActionEnabled(
- TimeShiftManager.TIME_SHIFT_ACTION_ID_FAST_FORWARD));
- mJumpNextButton.setEnabled(mTimeShiftManager.isActionEnabled(
- TimeShiftManager.TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT));
+ mJumpPreviousButton.setEnabled(
+ mTimeShiftManager.isActionEnabled(
+ TimeShiftManager.TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS));
+ mRewindButton.setEnabled(
+ mTimeShiftManager.isActionEnabled(TimeShiftManager.TIME_SHIFT_ACTION_ID_REWIND));
+ mFastForwardButton.setEnabled(
+ mTimeShiftManager.isActionEnabled(
+ TimeShiftManager.TIME_SHIFT_ACTION_ID_FAST_FORWARD));
+ mJumpNextButton.setEnabled(
+ mTimeShiftManager.isActionEnabled(
+ TimeShiftManager.TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT));
mJumpPreviousButton.setVisibility(VISIBLE);
mJumpNextButton.setVisibility(VISIBLE);
updateButtonMargin();
@@ -590,8 +649,11 @@ public class PlayControlsRowView extends MenuRowView {
if (mTimeShiftManager.getDisplayedPlaySpeed() == TimeShiftManager.PLAY_SPEED_1X) {
button.setLabel(null);
} else {
- button.setLabel(getResources().getString(R.string.play_controls_speed,
- mTimeShiftManager.getDisplayedPlaySpeed()));
+ button.setLabel(
+ getResources()
+ .getString(
+ R.string.play_controls_speed,
+ mTimeShiftManager.getDisplayedPlaySpeed()));
}
}
@@ -618,12 +680,13 @@ public class PlayControlsRowView extends MenuRowView {
}
private void updateButtonMargin() {
- int numOfVisibleButtons = (mJumpPreviousButton.getVisibility() == View.VISIBLE ? 1 : 0)
- + (mRewindButton.getVisibility() == View.VISIBLE ? 1 : 0)
- + (mPlayPauseButton.getVisibility() == View.VISIBLE ? 1 : 0)
- + (mFastForwardButton.getVisibility() == View.VISIBLE ? 1 : 0)
- + (mJumpNextButton.getVisibility() == View.VISIBLE ? 1 : 0)
- + (mRecordButton.getVisibility() == View.VISIBLE ? 1 : 0);
+ int numOfVisibleButtons =
+ (mJumpPreviousButton.getVisibility() == View.VISIBLE ? 1 : 0)
+ + (mRewindButton.getVisibility() == View.VISIBLE ? 1 : 0)
+ + (mPlayPauseButton.getVisibility() == View.VISIBLE ? 1 : 0)
+ + (mFastForwardButton.getVisibility() == View.VISIBLE ? 1 : 0)
+ + (mJumpNextButton.getVisibility() == View.VISIBLE ? 1 : 0)
+ + (mRecordButton.getVisibility() == View.VISIBLE ? 1 : 0);
boolean useCompactLayout = numOfVisibleButtons > NORMAL_WIDTH_MAX_BUTTON_COUNT;
if (mUseCompactLayout == useCompactLayout) {
return;
diff --git a/src/com/android/tv/menu/PlaybackProgressBar.java b/src/com/android/tv/menu/PlaybackProgressBar.java
index e8061bc6..398afe13 100644
--- a/src/com/android/tv/menu/PlaybackProgressBar.java
+++ b/src/com/android/tv/menu/PlaybackProgressBar.java
@@ -24,12 +24,9 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.util.AttributeSet;
import android.view.View;
-
import com.android.tv.R;
-/**
- * A progress bar control which has two progresses which start in the middle of the control.
- */
+/** A progress bar control which has two progresses which start in the middle of the control. */
public class PlaybackProgressBar extends View {
private final LayerDrawable mProgressDrawable;
private final Drawable mPrimaryDrawable;
@@ -51,11 +48,12 @@ public class PlaybackProgressBar extends View {
this(context, attrs, defStyleAttr, 0);
}
- public PlaybackProgressBar(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
+ public PlaybackProgressBar(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- TypedArray a = context.obtainStyledAttributes(
- attrs, R.styleable.PlaybackProgressBar, defStyleAttr, defStyleRes);
+ TypedArray a =
+ context.obtainStyledAttributes(
+ attrs, R.styleable.PlaybackProgressBar, defStyleAttr, defStyleRes);
mProgressDrawable =
(LayerDrawable) a.getDrawable(R.styleable.PlaybackProgressBar_progressDrawable);
mPrimaryDrawable = mProgressDrawable.findDrawableByLayerId(android.R.id.progress);
@@ -98,9 +96,7 @@ public class PlaybackProgressBar extends View {
}
}
- /**
- * Sets the start and end position of the progress.
- */
+ /** Sets the start and end position of the progress. */
public void setProgressRange(long start, long end) {
start = constrain(start, 0, mMax);
end = constrain(end, start, mMax);
@@ -112,9 +108,7 @@ public class PlaybackProgressBar extends View {
}
}
- /**
- * Sets the progress position.
- */
+ /** Sets the progress position. */
public void setProgress(long progress) {
progress = constrain(progress, mProgressStart, mProgressEnd);
if (progress != mProgress) {
diff --git a/src/com/android/tv/menu/SimpleCardView.java b/src/com/android/tv/menu/SimpleCardView.java
index fc5192da..e8ecdc72 100644
--- a/src/com/android/tv/menu/SimpleCardView.java
+++ b/src/com/android/tv/menu/SimpleCardView.java
@@ -19,9 +19,7 @@ package com.android.tv.menu;
import android.content.Context;
import android.util.AttributeSet;
-/**
- * A view to render a guide card.
- */
+/** A view to render a guide card. */
public class SimpleCardView extends BaseCardView<ChannelsRowItem> {
public SimpleCardView(Context context) {
diff --git a/src/com/android/tv/menu/TvOptionsRowAdapter.java b/src/com/android/tv/menu/TvOptionsRowAdapter.java
index 6e035f22..55affb59 100644
--- a/src/com/android/tv/menu/TvOptionsRowAdapter.java
+++ b/src/com/android/tv/menu/TvOptionsRowAdapter.java
@@ -19,18 +19,16 @@ package com.android.tv.menu;
import android.content.Context;
import android.media.tv.TvTrackInfo;
import android.support.annotation.VisibleForTesting;
-
-import com.android.tv.Features;
+import com.android.tv.TvFeatures;
import com.android.tv.TvOptionsManager;
-import com.android.tv.customization.CustomAction;
+import com.android.tv.common.customization.CustomAction;
+import com.android.tv.common.util.CommonUtils;
import com.android.tv.data.DisplayMode;
import com.android.tv.ui.TvViewUiManager;
import com.android.tv.ui.sidepanel.ClosedCaptionFragment;
import com.android.tv.ui.sidepanel.DeveloperOptionFragment;
import com.android.tv.ui.sidepanel.DisplayModeFragment;
import com.android.tv.ui.sidepanel.MultiAudioFragment;
-import com.android.tv.util.Utils;
-
import java.util.ArrayList;
import java.util.List;
@@ -47,12 +45,12 @@ public class TvOptionsRowAdapter extends CustomizableOptionsRowAdapter {
List<MenuAction> actionList = new ArrayList<>();
actionList.add(MenuAction.SELECT_CLOSED_CAPTION_ACTION);
actionList.add(MenuAction.SELECT_DISPLAY_MODE_ACTION);
- if (Features.PICTURE_IN_PICTURE.isEnabled(getMainActivity())) {
+ if (TvFeatures.PICTURE_IN_PICTURE.isEnabled(getMainActivity())) {
actionList.add(MenuAction.SYSTEMWIDE_PIP_ACTION);
}
actionList.add(MenuAction.SELECT_AUDIO_LANGUAGE_ACTION);
actionList.add(MenuAction.MORE_CHANNELS_ACTION);
- if (Utils.isDeveloper()) {
+ if (CommonUtils.isDeveloper()) {
actionList.add(MenuAction.DEV_ACTION);
}
actionList.add(MenuAction.SETTINGS_ACTION);
@@ -87,7 +85,8 @@ public class TvOptionsRowAdapter extends CustomizableOptionsRowAdapter {
private boolean updatePipAction() {
if (containsItem(MenuAction.SYSTEMWIDE_PIP_ACTION)) {
- return MenuAction.setEnabled(MenuAction.SYSTEMWIDE_PIP_ACTION,
+ return MenuAction.setEnabled(
+ MenuAction.SYSTEMWIDE_PIP_ACTION,
!getMainActivity().isScreenBlockedByResourceConflictOrParentalControl());
}
return false;
@@ -103,41 +102,50 @@ public class TvOptionsRowAdapter extends CustomizableOptionsRowAdapter {
private boolean updateDisplayModeAction() {
TvViewUiManager uiManager = getMainActivity().getTvViewUiManager();
- boolean enabled = uiManager.isDisplayModeAvailable(DisplayMode.MODE_FULL)
- || uiManager.isDisplayModeAvailable(DisplayMode.MODE_ZOOM);
+ boolean enabled =
+ uiManager.isDisplayModeAvailable(DisplayMode.MODE_FULL)
+ || uiManager.isDisplayModeAvailable(DisplayMode.MODE_ZOOM);
// Use "|" operator for non-short-circuit evaluation.
return MenuAction.setEnabled(MenuAction.SELECT_DISPLAY_MODE_ACTION, enabled)
| updateActionDescription(MenuAction.SELECT_DISPLAY_MODE_ACTION);
}
private boolean updateActionDescription(MenuAction action) {
- return MenuAction.setActionDescription(action,
- getMainActivity().getTvOptionsManager().getOptionString(action.getType()));
+ return MenuAction.setActionDescription(
+ action, getMainActivity().getTvOptionsManager().getOptionString(action.getType()));
}
@Override
protected void executeBaseAction(int type) {
switch (type) {
case TvOptionsManager.OPTION_CLOSED_CAPTIONS:
- getMainActivity().getOverlayManager().getSideFragmentManager()
+ getMainActivity()
+ .getOverlayManager()
+ .getSideFragmentManager()
.show(new ClosedCaptionFragment());
break;
case TvOptionsManager.OPTION_DISPLAY_MODE:
- getMainActivity().getOverlayManager().getSideFragmentManager()
+ getMainActivity()
+ .getOverlayManager()
+ .getSideFragmentManager()
.show(new DisplayModeFragment());
break;
case TvOptionsManager.OPTION_SYSTEMWIDE_PIP:
getMainActivity().enterPictureInPictureMode();
break;
case TvOptionsManager.OPTION_MULTI_AUDIO:
- getMainActivity().getOverlayManager().getSideFragmentManager()
+ getMainActivity()
+ .getOverlayManager()
+ .getSideFragmentManager()
.show(new MultiAudioFragment());
break;
case TvOptionsManager.OPTION_MORE_CHANNELS:
getMainActivity().showMerchantCollection();
break;
case TvOptionsManager.OPTION_DEVELOPER:
- getMainActivity().getOverlayManager().getSideFragmentManager()
+ getMainActivity()
+ .getOverlayManager()
+ .getSideFragmentManager()
.show(new DeveloperOptionFragment());
break;
case TvOptionsManager.OPTION_SETTINGS:
@@ -145,4 +153,4 @@ public class TvOptionsRowAdapter extends CustomizableOptionsRowAdapter {
break;
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/onboarding/NewSourcesFragment.java b/src/com/android/tv/onboarding/NewSourcesFragment.java
index 8509b50c..f3b077a7 100644
--- a/src/com/android/tv/onboarding/NewSourcesFragment.java
+++ b/src/com/android/tv/onboarding/NewSourcesFragment.java
@@ -23,28 +23,17 @@ import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.common.ui.setup.SetupActionHelper;
-import com.android.tv.util.SetupUtils;
-/**
- * A fragment for new channel source info/setup.
- */
+/** A fragment for new channel source info/setup. */
public class NewSourcesFragment extends Fragment {
- /**
- * The action category.
- */
- public static final String ACTION_CATEOGRY =
- "com.android.tv.onboarding.NewSourcesFragment";
- /**
- * An action to show the setup screen.
- */
+ /** The action category. */
+ public static final String ACTION_CATEOGRY = "com.android.tv.onboarding.NewSourcesFragment";
+ /** An action to show the setup screen. */
public static final int ACTION_SETUP = 1;
- /**
- * An action to close this fragment.
- */
+ /** An action to close this fragment. */
public static final int ACTION_SKIP = 2;
public NewSourcesFragment() {
@@ -57,19 +46,20 @@ public class NewSourcesFragment extends Fragment {
}
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_new_sources, container, false);
initializeButton(view.findViewById(R.id.setup), ACTION_SETUP);
initializeButton(view.findViewById(R.id.skip), ACTION_SKIP);
- SetupUtils.getInstance(getActivity()).markAllInputsRecognized(TvApplication
- .getSingletons(getActivity()).getTvInputManagerHelper());
+ TvSingletons singletons = TvSingletons.getSingletons(getActivity());
+ singletons.getSetupUtils().markAllInputsRecognized(singletons.getTvInputManagerHelper());
view.requestFocus();
return view;
}
private void initializeButton(View view, int actionId) {
- view.setOnClickListener(SetupActionHelper.createOnClickListenerForAction(this,
- ACTION_CATEOGRY, actionId, null));
+ view.setOnClickListener(
+ SetupActionHelper.createOnClickListenerForAction(
+ this, ACTION_CATEOGRY, actionId, null));
}
}
diff --git a/src/com/android/tv/onboarding/OnboardingActivity.java b/src/com/android/tv/onboarding/OnboardingActivity.java
index 45205c4c..a1cf9de1 100644
--- a/src/com/android/tv/onboarding/OnboardingActivity.java
+++ b/src/com/android/tv/onboarding/OnboardingActivity.java
@@ -26,17 +26,15 @@ import android.media.tv.TvInputInfo;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.widget.Toast;
-
-import com.android.tv.ApplicationSingletons;
import com.android.tv.R;
import com.android.tv.SetupPassthroughActivity;
-import com.android.tv.TvApplication;
-import com.android.tv.common.TvCommonUtils;
+import com.android.tv.TvSingletons;
import com.android.tv.common.ui.setup.SetupActivity;
import com.android.tv.common.ui.setup.SetupMultiPaneFragment;
+import com.android.tv.common.util.CommonUtils;
+import com.android.tv.common.util.PermissionUtils;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.util.OnboardingUtils;
-import com.android.tv.util.PermissionUtils;
import com.android.tv.util.SetupUtils;
import com.android.tv.util.TvInputManagerHelper;
@@ -51,29 +49,31 @@ public class OnboardingActivity extends SetupActivity {
private ChannelDataManager mChannelDataManager;
private TvInputManagerHelper mInputManager;
- private final ChannelDataManager.Listener mChannelListener = new ChannelDataManager.Listener() {
- @Override
- public void onLoadFinished() {
- mChannelDataManager.removeListener(this);
- SetupUtils.getInstance(OnboardingActivity.this).markNewChannelsBrowsable();
- }
+ private SetupUtils mSetupUtils;
+ private final ChannelDataManager.Listener mChannelListener =
+ new ChannelDataManager.Listener() {
+ @Override
+ public void onLoadFinished() {
+ mChannelDataManager.removeListener(this);
+ mSetupUtils.markNewChannelsBrowsable();
+ }
- @Override
- public void onChannelListUpdated() { }
+ @Override
+ public void onChannelListUpdated() {}
- @Override
- public void onChannelBrowsableChanged() { }
- };
+ @Override
+ public void onChannelBrowsableChanged() {}
+ };
/**
* Returns an intent to start {@link OnboardingActivity}.
*
* @param context context to create an intent. Should not be {@code null}.
* @param intentAfterCompletion intent which will be used to start a new activity when this
- * activity finishes. Should not be {@code null}.
+ * activity finishes. Should not be {@code null}.
*/
- public static Intent buildIntent(@NonNull Context context,
- @NonNull Intent intentAfterCompletion) {
+ public static Intent buildIntent(
+ @NonNull Context context, @NonNull Intent intentAfterCompletion) {
return new Intent(context, OnboardingActivity.class)
.putExtra(OnboardingActivity.KEY_INTENT_AFTER_COMPLETION, intentAfterCompletion);
}
@@ -81,19 +81,21 @@ public class OnboardingActivity extends SetupActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- ApplicationSingletons singletons = TvApplication.getSingletons(this);
+ TvSingletons singletons = TvSingletons.getSingletons(this);
mInputManager = singletons.getTvInputManagerHelper();
+ mSetupUtils = singletons.getSetupUtils();
if (PermissionUtils.hasAccessAllEpg(this) || PermissionUtils.hasReadTvListings(this)) {
mChannelDataManager = singletons.getChannelDataManager();
// Make the channels of the new inputs which have been setup outside Live TV
// browsable.
if (mChannelDataManager.isDbLoadFinished()) {
- SetupUtils.getInstance(this).markNewChannelsBrowsable();
+ mSetupUtils.markNewChannelsBrowsable();
} else {
mChannelDataManager.addListener(mChannelListener);
}
} else {
- requestPermissions(new String[] {PermissionUtils.PERMISSION_READ_TV_LISTINGS},
+ requestPermissions(
+ new String[] {PermissionUtils.PERMISSION_READ_TV_LISTINGS},
PERMISSIONS_REQUEST_READ_TV_LISTINGS);
}
}
@@ -109,32 +111,35 @@ public class OnboardingActivity extends SetupActivity {
@Override
protected Fragment onCreateInitialFragment() {
if (PermissionUtils.hasAccessAllEpg(this) || PermissionUtils.hasReadTvListings(this)) {
- return OnboardingUtils.isFirstRunWithCurrentVersion(this) ? new WelcomeFragment()
+ return OnboardingUtils.isFirstRunWithCurrentVersion(this)
+ ? new WelcomeFragment()
: new SetupSourcesFragment();
}
return null;
}
@Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
- @NonNull int[] grantResults) {
+ public void onRequestPermissionsResult(
+ int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == PERMISSIONS_REQUEST_READ_TV_LISTINGS) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
finish();
- Intent intentForNextActivity = getIntent().getParcelableExtra(
- KEY_INTENT_AFTER_COMPLETION);
+ 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();
+ Toast.makeText(
+ this,
+ R.string.msg_read_tv_listing_permission_denied,
+ Toast.LENGTH_LONG)
+ .show();
finish();
}
}
}
private void finishActivity() {
- Intent intentForNextActivity = getIntent().getParcelableExtra(
- KEY_INTENT_AFTER_COMPLETION);
+ Intent intentForNextActivity = getIntent().getParcelableExtra(KEY_INTENT_AFTER_COMPLETION);
if (intentForNextActivity != null) {
startActivity(intentForNextActivity);
}
@@ -142,12 +147,14 @@ public class OnboardingActivity extends SetupActivity {
}
private void showMerchantCollection() {
- executeActionWithDelay(new Runnable() {
- @Override
- public void run() {
- startActivity(OnboardingUtils.ONLINE_STORE_INTENT);
- }
- }, SHOW_RIPPLE_DURATION_MS);
+ executeActionWithDelay(
+ new Runnable() {
+ @Override
+ public void run() {
+ startActivity(OnboardingUtils.ONLINE_STORE_INTENT);
+ }
+ },
+ SHOW_RIPPLE_DURATION_MS);
}
@Override
@@ -167,42 +174,55 @@ public class OnboardingActivity extends SetupActivity {
case SetupSourcesFragment.ACTION_ONLINE_STORE:
showMerchantCollection();
return true;
- case SetupSourcesFragment.ACTION_SETUP_INPUT: {
- String inputId = params.getString(
- SetupSourcesFragment.ACTION_PARAM_KEY_INPUT_ID);
- TvInputInfo input = mInputManager.getTvInputInfo(inputId);
- Intent intent = TvCommonUtils.createSetupIntent(input);
- if (intent == null) {
- Toast.makeText(this, R.string.msg_no_setup_activity, Toast.LENGTH_SHORT)
- .show();
+ case SetupSourcesFragment.ACTION_SETUP_INPUT:
+ {
+ String inputId =
+ params.getString(
+ SetupSourcesFragment.ACTION_PARAM_KEY_INPUT_ID);
+ TvInputInfo input = mInputManager.getTvInputInfo(inputId);
+ Intent intent = CommonUtils.createSetupIntent(input);
+ if (intent == null) {
+ Toast.makeText(
+ this,
+ R.string.msg_no_setup_activity,
+ Toast.LENGTH_SHORT)
+ .show();
+ return true;
+ }
+ // Even though other app can handle the intent, the setup launched by
+ // Live
+ // channels should go through Live channels SetupPassthroughActivity.
+ intent.setComponent(
+ new ComponentName(this, SetupPassthroughActivity.class));
+ try {
+ // Now we know that the user intends to set up this input. Grant
+ // permission for writing EPG data.
+ SetupUtils.grantEpgPermission(
+ this, input.getServiceInfo().packageName);
+ startActivityForResult(intent, REQUEST_CODE_START_SETUP_ACTIVITY);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(
+ this,
+ getString(
+ R.string.msg_unable_to_start_setup_activity,
+ input.loadLabel(this)),
+ Toast.LENGTH_SHORT)
+ .show();
+ }
return true;
}
- // Even though other app can handle the intent, the setup launched by Live
- // channels should go through Live channels SetupPassthroughActivity.
- intent.setComponent(new ComponentName(this,
- SetupPassthroughActivity.class));
- try {
- // Now we know that the user intends to set up this input. Grant
- // permission for writing EPG data.
- SetupUtils.grantEpgPermission(this, input.getServiceInfo().packageName);
- startActivityForResult(intent, REQUEST_CODE_START_SETUP_ACTIVITY);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(this,
- getString(R.string.msg_unable_to_start_setup_activity,
- input.loadLabel(this)), Toast.LENGTH_SHORT).show();
- }
- return true;
- }
- case SetupMultiPaneFragment.ACTION_DONE: {
- ChannelDataManager manager = TvApplication.getSingletons(
- OnboardingActivity.this).getChannelDataManager();
- if (manager.getChannelCount() == 0) {
- finish();
- } else {
- finishActivity();
+ case SetupMultiPaneFragment.ACTION_DONE:
+ {
+ ChannelDataManager manager =
+ TvSingletons.getSingletons(OnboardingActivity.this)
+ .getChannelDataManager();
+ if (manager.getChannelCount() == 0) {
+ finish();
+ } else {
+ finishActivity();
+ }
+ return true;
}
- return true;
- }
}
break;
}
diff --git a/src/com/android/tv/onboarding/SetupSourcesFragment.java b/src/com/android/tv/onboarding/SetupSourcesFragment.java
index f56daec5..f032f622 100644
--- a/src/com/android/tv/onboarding/SetupSourcesFragment.java
+++ b/src/com/android/tv/onboarding/SetupSourcesFragment.java
@@ -30,41 +30,30 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
-
-import com.android.tv.ApplicationSingletons;
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.common.ui.setup.SetupGuidedStepFragment;
import com.android.tv.common.ui.setup.SetupMultiPaneFragment;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.TvInputNewComparator;
-import com.android.tv.tuner.TunerInputController;
import com.android.tv.ui.GuidedActionsStylistWithDivider;
import com.android.tv.util.SetupUtils;
import com.android.tv.util.TvInputManagerHelper;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-/**
- * A fragment for channel source info/setup.
- */
+/** A fragment for channel source info/setup. */
public class SetupSourcesFragment extends SetupMultiPaneFragment {
- /**
- * The action category for the actions which is fired from this fragment.
- */
- public static final String ACTION_CATEGORY =
- "com.android.tv.onboarding.SetupSourcesFragment";
- /**
- * An action to open the merchant collection.
- */
+ /** The action category for the actions which is fired from this fragment. */
+ public static final String ACTION_CATEGORY = "com.android.tv.onboarding.SetupSourcesFragment";
+ /** An action to open the merchant collection. */
public static final int ACTION_ONLINE_STORE = 1;
/**
* An action to show the setup activity of TV input.
- * <p>
- * This action is not added to the action list. This is sent outside of the fragment.
- * Use {@link #ACTION_PARAM_KEY_INPUT_ID} to get the input ID from the parameter.
+ *
+ * <p>This action is not added to the action list. This is sent outside of the fragment. Use
+ * {@link #ACTION_PARAM_KEY_INPUT_ID} to get the input ID from the parameter.
*/
public static final int ACTION_SETUP_INPUT = 2;
@@ -77,10 +66,10 @@ public class SetupSourcesFragment extends SetupMultiPaneFragment {
private static final String SETUP_TRACKER_LABEL = "Setup fragment";
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
- TvApplication.getSingletons(getActivity()).getTracker().sendScreenView(SETUP_TRACKER_LABEL);
+ TvSingletons.getSingletons(getActivity()).getTracker().sendScreenView(SETUP_TRACKER_LABEL);
return view;
}
@@ -130,82 +119,87 @@ public class SetupSourcesFragment extends SetupMultiPaneFragment {
private int mPendingAction = PENDING_ACTION_NONE;
- private final TvInputCallback mInputCallback = new TvInputCallback() {
- @Override
- public void onInputAdded(String inputId) {
- handleInputChanged();
- }
+ private final TvInputCallback mInputCallback =
+ new TvInputCallback() {
+ @Override
+ public void onInputAdded(String inputId) {
+ handleInputChanged();
+ }
- @Override
- public void onInputRemoved(String inputId) {
- handleInputChanged();
- }
+ @Override
+ public void onInputRemoved(String inputId) {
+ handleInputChanged();
+ }
- @Override
- public void onInputUpdated(String inputId) {
- handleInputChanged();
- }
+ @Override
+ public void onInputUpdated(String inputId) {
+ handleInputChanged();
+ }
- @Override
- public void onTvInputInfoUpdated(TvInputInfo inputInfo) {
- handleInputChanged();
- }
+ @Override
+ public void onTvInputInfoUpdated(TvInputInfo inputInfo) {
+ handleInputChanged();
+ }
- private void handleInputChanged() {
- // The actions created while enter transition is running will not be included in the
- // fragment transition.
- if (mParentFragment.isEnterTransitionRunning()) {
- mPendingAction = PENDING_ACTION_INPUT_CHANGED;
- return;
- }
- buildInputs();
- updateActions();
- }
- };
+ private void handleInputChanged() {
+ // The actions created while enter transition is running will not be
+ // included in the
+ // fragment transition.
+ if (mParentFragment.isEnterTransitionRunning()) {
+ mPendingAction = PENDING_ACTION_INPUT_CHANGED;
+ return;
+ }
+ buildInputs();
+ updateActions();
+ }
+ };
- private final ChannelDataManager.Listener mChannelDataManagerListener
- = new ChannelDataManager.Listener() {
- @Override
- public void onLoadFinished() {
- handleChannelChanged();
- }
+ private final ChannelDataManager.Listener mChannelDataManagerListener =
+ new ChannelDataManager.Listener() {
+ @Override
+ public void onLoadFinished() {
+ handleChannelChanged();
+ }
- @Override
- public void onChannelListUpdated() {
- handleChannelChanged();
- }
+ @Override
+ public void onChannelListUpdated() {
+ handleChannelChanged();
+ }
- @Override
- public void onChannelBrowsableChanged() {
- handleChannelChanged();
- }
+ @Override
+ public void onChannelBrowsableChanged() {
+ handleChannelChanged();
+ }
- private void handleChannelChanged() {
- // The actions created while enter transition is running will not be included in the
- // fragment transition.
- if (mParentFragment.isEnterTransitionRunning()) {
- if (mPendingAction != PENDING_ACTION_INPUT_CHANGED) {
- mPendingAction = PENDING_ACTION_CHANNEL_CHANGED;
+ private void handleChannelChanged() {
+ // The actions created while enter transition is running will not be
+ // included in the
+ // fragment transition.
+ if (mParentFragment.isEnterTransitionRunning()) {
+ if (mPendingAction != PENDING_ACTION_INPUT_CHANGED) {
+ mPendingAction = PENDING_ACTION_CHANNEL_CHANGED;
+ }
+ return;
+ }
+ updateActions();
}
- return;
- }
- updateActions();
- }
- };
+ };
@Override
public void onCreate(Bundle savedInstanceState) {
Context context = getActivity();
- ApplicationSingletons app = TvApplication.getSingletons(context);
- mInputManager = app.getTvInputManagerHelper();
- mChannelDataManager = app.getChannelDataManager();
- mSetupUtils = SetupUtils.getInstance(context);
+ TvSingletons singletons = TvSingletons.getSingletons(context);
+ mInputManager = singletons.getTvInputManagerHelper();
+ mChannelDataManager = singletons.getChannelDataManager();
+ mSetupUtils = singletons.getSetupUtils();
buildInputs();
mInputManager.addCallback(mInputCallback);
mChannelDataManager.addListener(mChannelDataManagerListener);
super.onCreate(savedInstanceState);
mParentFragment = (SetupSourcesFragment) getParentFragment();
- TunerInputController.executeNetworkTunerDiscoveryAsyncTask(getContext());
+ singletons
+ .getTunerInputController()
+ .executeNetworkTunerDiscoveryAsyncTask(getContext());
}
@Override
@@ -229,8 +223,8 @@ public class SetupSourcesFragment extends SetupMultiPaneFragment {
}
@Override
- public void onCreateActions(@NonNull List<GuidedAction> actions,
- Bundle savedInstanceState) {
+ public void onCreateActions(
+ @NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
createActionsInternal(actions);
}
@@ -274,24 +268,26 @@ public class SetupSourcesFragment extends SetupMultiPaneFragment {
int position = 0;
if (mDoneInputStartIndex > 0) {
// Need a "New" category
- actions.add(new GuidedAction.Builder(getActivity())
- .id(ACTION_HEADER)
- .title(null)
- .description(getString(R.string.setup_category_new))
- .focusable(false)
- .infoOnly(true)
- .build());
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(ACTION_HEADER)
+ .title(null)
+ .description(getString(R.string.setup_category_new))
+ .focusable(false)
+ .infoOnly(true)
+ .build());
}
for (int i = 0; i < mInputs.size(); ++i) {
if (i == mDoneInputStartIndex) {
++position;
- actions.add(new GuidedAction.Builder(getActivity())
- .id(ACTION_HEADER)
- .title(null)
- .description(getString(R.string.setup_category_done))
- .focusable(false)
- .infoOnly(true)
- .build());
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(ACTION_HEADER)
+ .title(null)
+ .description(getString(R.string.setup_category_done))
+ .focusable(false)
+ .infoOnly(true)
+ .build());
}
TvInputInfo input = mInputs.get(i);
String inputId = input.getId();
@@ -301,8 +297,12 @@ public class SetupSourcesFragment extends SetupMultiPaneFragment {
if (channelCount == 0) {
description = getString(R.string.setup_input_no_channels);
} else {
- description = getResources().getQuantityString(
- R.plurals.setup_input_channels, channelCount, channelCount);
+ description =
+ getResources()
+ .getQuantityString(
+ R.plurals.setup_input_channels,
+ channelCount,
+ channelCount);
}
} else if (i >= mKnownInputStartIndex) {
description = getString(R.string.setup_input_setup_now);
@@ -313,11 +313,12 @@ public class SetupSourcesFragment extends SetupMultiPaneFragment {
if (input.getId().equals(mNewlyAddedInputId)) {
newPosition = position;
}
- actions.add(new GuidedAction.Builder(getActivity())
- .id(ACTION_INPUT_START + i)
- .title(input.loadLabel(getActivity()).toString())
- .description(description)
- .build());
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(ACTION_INPUT_START + i)
+ .title(input.loadLabel(getActivity()).toString())
+ .description(description)
+ .build());
}
if (mInputs.size() > 0) {
// Divider
@@ -326,12 +327,13 @@ public class SetupSourcesFragment extends SetupMultiPaneFragment {
}
// online store action
++position;
- actions.add(new GuidedAction.Builder(getActivity())
- .id(ACTION_ONLINE_STORE)
- .title(getString(R.string.setup_store_action_title))
- .description(getString(R.string.setup_store_action_description))
- .icon(R.drawable.ic_store)
- .build());
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(ACTION_ONLINE_STORE)
+ .title(getString(R.string.setup_store_action_title))
+ .description(getString(R.string.setup_store_action_description))
+ .icon(R.drawable.ic_store)
+ .build());
if (newPosition != -1) {
VerticalGridView gridView = getGuidedActionsStylist().getActionsGridView();
@@ -367,6 +369,7 @@ public class SetupSourcesFragment extends SetupMultiPaneFragment {
case PENDING_ACTION_CHANNEL_CHANGED:
updateActions();
break;
+ default: // fall out
}
mPendingAction = PENDING_ACTION_NONE;
}
@@ -382,17 +385,19 @@ public class SetupSourcesFragment extends SetupMultiPaneFragment {
if (descriptionView != null) {
if (action.getId() == ACTION_HEADER) {
descriptionView.setAlpha(ALPHA_CATEGORY);
- descriptionView.setTextColor(getResources().getColor(R.color.setup_category,
- null));
- descriptionView.setTypeface(Typeface.create(
- getString(R.string.condensed_font), 0));
+ descriptionView.setTextColor(
+ getResources().getColor(R.color.setup_category, null));
+ descriptionView.setTypeface(
+ Typeface.create(getString(R.string.condensed_font), 0));
} else {
descriptionView.setAlpha(ALPHA_INPUT_DESCRIPTION);
- descriptionView.setTextColor(getResources().getColor(
- R.color.common_setup_input_description, null));
+ descriptionView.setTextColor(
+ getResources()
+ .getColor(R.color.common_setup_input_description, null));
descriptionView.setTypeface(Typeface.create(getString(R.string.font), 0));
}
}
+ setAccessibilityDelegate(vh, action);
}
}
}
diff --git a/src/com/android/tv/onboarding/WelcomeFragment.java b/src/com/android/tv/onboarding/WelcomeFragment.java
index f12233e9..8c119a8a 100644
--- a/src/com/android/tv/onboarding/WelcomeFragment.java
+++ b/src/com/android/tv/onboarding/WelcomeFragment.java
@@ -16,33 +16,37 @@
package com.android.tv.onboarding;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
+
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.text.Editable;
+import android.text.TextWatcher;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.View.AccessibilityDelegate;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.Button;
import android.widget.ImageView;
-
+import android.widget.TextView;
import com.android.tv.R;
import com.android.tv.common.ui.setup.SetupActionHelper;
import com.android.tv.common.ui.setup.animation.SetupAnimationHelper;
-
import java.util.ArrayList;
import java.util.List;
-/**
- * A fragment for the onboarding welcome screen.
- */
+/** A fragment for the onboarding welcome screen. */
public class WelcomeFragment extends OnboardingFragment {
- public static final String ACTION_CATEGORY = "comgoogle.android.tv.onboarding.WelcomeFragment";
+ public static final String ACTION_CATEGORY = "com.android.tv.onboarding.WelcomeFragment";
public static final int ACTION_NEXT = 1;
private static final long START_DELAY_CLOUD_MS = 33;
@@ -56,523 +60,523 @@ public class WelcomeFragment extends OnboardingFragment {
// TODO: Use animator list xml.
private static final int[] TV_FRAMES_1_START = {
- R.drawable.tv_1a_01,
- R.drawable.tv_1a_02,
- R.drawable.tv_1a_03,
- R.drawable.tv_1a_04,
- R.drawable.tv_1a_05,
- R.drawable.tv_1a_06,
- R.drawable.tv_1a_07,
- R.drawable.tv_1a_08,
- R.drawable.tv_1a_09,
- R.drawable.tv_1a_10,
- R.drawable.tv_1a_11,
- R.drawable.tv_1a_12,
- R.drawable.tv_1a_13,
- R.drawable.tv_1a_14,
- R.drawable.tv_1a_15,
- R.drawable.tv_1a_16,
- R.drawable.tv_1a_17,
- R.drawable.tv_1a_18,
- R.drawable.tv_1a_19,
- R.drawable.tv_1a_20
+ R.drawable.tv_1a_01,
+ R.drawable.tv_1a_02,
+ R.drawable.tv_1a_03,
+ R.drawable.tv_1a_04,
+ R.drawable.tv_1a_05,
+ R.drawable.tv_1a_06,
+ R.drawable.tv_1a_07,
+ R.drawable.tv_1a_08,
+ R.drawable.tv_1a_09,
+ R.drawable.tv_1a_10,
+ R.drawable.tv_1a_11,
+ R.drawable.tv_1a_12,
+ R.drawable.tv_1a_13,
+ R.drawable.tv_1a_14,
+ R.drawable.tv_1a_15,
+ R.drawable.tv_1a_16,
+ R.drawable.tv_1a_17,
+ R.drawable.tv_1a_18,
+ R.drawable.tv_1a_19,
+ R.drawable.tv_1a_20
};
private static final int[] TV_FRAMES_1_END = {
- R.drawable.tv_1b_01,
- R.drawable.tv_1b_02,
- R.drawable.tv_1b_03,
- R.drawable.tv_1b_04,
- R.drawable.tv_1b_05,
- R.drawable.tv_1b_06,
- R.drawable.tv_1b_07,
- R.drawable.tv_1b_08,
- R.drawable.tv_1b_09,
- R.drawable.tv_1b_10,
- R.drawable.tv_1b_11
+ R.drawable.tv_1b_01,
+ R.drawable.tv_1b_02,
+ R.drawable.tv_1b_03,
+ R.drawable.tv_1b_04,
+ R.drawable.tv_1b_05,
+ R.drawable.tv_1b_06,
+ R.drawable.tv_1b_07,
+ R.drawable.tv_1b_08,
+ R.drawable.tv_1b_09,
+ R.drawable.tv_1b_10,
+ R.drawable.tv_1b_11
};
private static final int[] TV_FRAMES_2_START = {
- R.drawable.tv_5a_0,
- R.drawable.tv_5a_1,
- R.drawable.tv_5a_2,
- R.drawable.tv_5a_3,
- R.drawable.tv_5a_4,
- R.drawable.tv_5a_5,
- R.drawable.tv_5a_6,
- R.drawable.tv_5a_7,
- R.drawable.tv_5a_8,
- R.drawable.tv_5a_9,
- R.drawable.tv_5a_10,
- R.drawable.tv_5a_11,
- R.drawable.tv_5a_12,
- R.drawable.tv_5a_13,
- R.drawable.tv_5a_14,
- R.drawable.tv_5a_15,
- R.drawable.tv_5a_16,
- R.drawable.tv_5a_17,
- R.drawable.tv_5a_18,
- R.drawable.tv_5a_19,
- R.drawable.tv_5a_20,
- R.drawable.tv_5a_21,
- R.drawable.tv_5a_22,
- R.drawable.tv_5a_23,
- R.drawable.tv_5a_24,
- R.drawable.tv_5a_25,
- R.drawable.tv_5a_26,
- R.drawable.tv_5a_27,
- R.drawable.tv_5a_28,
- R.drawable.tv_5a_29,
- R.drawable.tv_5a_30,
- R.drawable.tv_5a_31,
- R.drawable.tv_5a_32,
- R.drawable.tv_5a_33,
- R.drawable.tv_5a_34,
- R.drawable.tv_5a_35,
- R.drawable.tv_5a_36,
- R.drawable.tv_5a_37,
- R.drawable.tv_5a_38,
- R.drawable.tv_5a_39,
- R.drawable.tv_5a_40,
- R.drawable.tv_5a_41,
- R.drawable.tv_5a_42,
- R.drawable.tv_5a_43,
- R.drawable.tv_5a_44,
- R.drawable.tv_5a_45,
- R.drawable.tv_5a_46,
- R.drawable.tv_5a_47,
- R.drawable.tv_5a_48,
- R.drawable.tv_5a_49,
- R.drawable.tv_5a_50,
- R.drawable.tv_5a_51,
- R.drawable.tv_5a_52,
- R.drawable.tv_5a_53,
- R.drawable.tv_5a_54,
- R.drawable.tv_5a_55,
- R.drawable.tv_5a_56,
- R.drawable.tv_5a_57,
- R.drawable.tv_5a_58,
- R.drawable.tv_5a_59,
- R.drawable.tv_5a_60,
- R.drawable.tv_5a_61,
- R.drawable.tv_5a_62,
- R.drawable.tv_5a_63,
- R.drawable.tv_5a_64,
- R.drawable.tv_5a_65,
- R.drawable.tv_5a_66,
- R.drawable.tv_5a_67,
- R.drawable.tv_5a_68,
- R.drawable.tv_5a_69,
- R.drawable.tv_5a_70,
- R.drawable.tv_5a_71,
- R.drawable.tv_5a_72,
- R.drawable.tv_5a_73,
- R.drawable.tv_5a_74,
- R.drawable.tv_5a_75,
- R.drawable.tv_5a_76,
- R.drawable.tv_5a_77,
- R.drawable.tv_5a_78,
- R.drawable.tv_5a_79,
- R.drawable.tv_5a_80,
- R.drawable.tv_5a_81,
- R.drawable.tv_5a_82,
- R.drawable.tv_5a_83,
- R.drawable.tv_5a_84,
- R.drawable.tv_5a_85,
- R.drawable.tv_5a_86,
- R.drawable.tv_5a_87,
- R.drawable.tv_5a_88,
- R.drawable.tv_5a_89,
- R.drawable.tv_5a_90,
- R.drawable.tv_5a_91,
- R.drawable.tv_5a_92,
- R.drawable.tv_5a_93,
- R.drawable.tv_5a_94,
- R.drawable.tv_5a_95,
- R.drawable.tv_5a_96,
- R.drawable.tv_5a_97,
- R.drawable.tv_5a_98,
- R.drawable.tv_5a_99,
- R.drawable.tv_5a_100,
- R.drawable.tv_5a_101,
- R.drawable.tv_5a_102,
- R.drawable.tv_5a_103,
- R.drawable.tv_5a_104,
- R.drawable.tv_5a_105,
- R.drawable.tv_5a_106,
- R.drawable.tv_5a_107,
- R.drawable.tv_5a_108,
- R.drawable.tv_5a_109,
- R.drawable.tv_5a_110,
- R.drawable.tv_5a_111,
- R.drawable.tv_5a_112,
- R.drawable.tv_5a_113,
- R.drawable.tv_5a_114,
- R.drawable.tv_5a_115,
- R.drawable.tv_5a_116,
- R.drawable.tv_5a_117,
- R.drawable.tv_5a_118,
- R.drawable.tv_5a_119,
- R.drawable.tv_5a_120,
- R.drawable.tv_5a_121,
- R.drawable.tv_5a_122,
- R.drawable.tv_5a_123,
- R.drawable.tv_5a_124,
- R.drawable.tv_5a_125,
- R.drawable.tv_5a_126,
- R.drawable.tv_5a_127,
- R.drawable.tv_5a_128,
- R.drawable.tv_5a_129,
- R.drawable.tv_5a_130,
- R.drawable.tv_5a_131,
- R.drawable.tv_5a_132,
- R.drawable.tv_5a_133,
- R.drawable.tv_5a_134,
- R.drawable.tv_5a_135,
- R.drawable.tv_5a_136,
- R.drawable.tv_5a_137,
- R.drawable.tv_5a_138,
- R.drawable.tv_5a_139,
- R.drawable.tv_5a_140,
- R.drawable.tv_5a_141,
- R.drawable.tv_5a_142,
- R.drawable.tv_5a_143,
- R.drawable.tv_5a_144,
- R.drawable.tv_5a_145,
- R.drawable.tv_5a_146,
- R.drawable.tv_5a_147,
- R.drawable.tv_5a_148,
- R.drawable.tv_5a_149,
- R.drawable.tv_5a_150,
- R.drawable.tv_5a_151,
- R.drawable.tv_5a_152,
- R.drawable.tv_5a_153,
- R.drawable.tv_5a_154,
- R.drawable.tv_5a_155,
- R.drawable.tv_5a_156,
- R.drawable.tv_5a_157,
- R.drawable.tv_5a_158,
- R.drawable.tv_5a_159,
- R.drawable.tv_5a_160,
- R.drawable.tv_5a_161,
- R.drawable.tv_5a_162,
- R.drawable.tv_5a_163,
- R.drawable.tv_5a_164,
- R.drawable.tv_5a_165,
- R.drawable.tv_5a_166,
- R.drawable.tv_5a_167,
- R.drawable.tv_5a_168,
- R.drawable.tv_5a_169,
- R.drawable.tv_5a_170,
- R.drawable.tv_5a_171,
- R.drawable.tv_5a_172,
- R.drawable.tv_5a_173,
- R.drawable.tv_5a_174,
- R.drawable.tv_5a_175,
- R.drawable.tv_5a_176,
- R.drawable.tv_5a_177,
- R.drawable.tv_5a_178,
- R.drawable.tv_5a_179,
- R.drawable.tv_5a_180,
- R.drawable.tv_5a_181,
- R.drawable.tv_5a_182,
- R.drawable.tv_5a_183,
- R.drawable.tv_5a_184,
- R.drawable.tv_5a_185,
- R.drawable.tv_5a_186,
- R.drawable.tv_5a_187,
- R.drawable.tv_5a_188,
- R.drawable.tv_5a_189,
- R.drawable.tv_5a_190,
- R.drawable.tv_5a_191,
- R.drawable.tv_5a_192,
- R.drawable.tv_5a_193,
- R.drawable.tv_5a_194,
- R.drawable.tv_5a_195,
- R.drawable.tv_5a_196,
- R.drawable.tv_5a_197,
- R.drawable.tv_5a_198,
- R.drawable.tv_5a_199,
- R.drawable.tv_5a_200,
- R.drawable.tv_5a_201,
- R.drawable.tv_5a_202,
- R.drawable.tv_5a_203,
- R.drawable.tv_5a_204,
- R.drawable.tv_5a_205,
- R.drawable.tv_5a_206,
- R.drawable.tv_5a_207,
- R.drawable.tv_5a_208,
- R.drawable.tv_5a_209,
- R.drawable.tv_5a_210,
- R.drawable.tv_5a_211,
- R.drawable.tv_5a_212,
- R.drawable.tv_5a_213,
- R.drawable.tv_5a_214,
- R.drawable.tv_5a_215,
- R.drawable.tv_5a_216,
- R.drawable.tv_5a_217,
- R.drawable.tv_5a_218,
- R.drawable.tv_5a_219,
- R.drawable.tv_5a_220,
- R.drawable.tv_5a_221,
- R.drawable.tv_5a_222,
- R.drawable.tv_5a_223,
- R.drawable.tv_5a_224
+ R.drawable.tv_5a_0,
+ R.drawable.tv_5a_1,
+ R.drawable.tv_5a_2,
+ R.drawable.tv_5a_3,
+ R.drawable.tv_5a_4,
+ R.drawable.tv_5a_5,
+ R.drawable.tv_5a_6,
+ R.drawable.tv_5a_7,
+ R.drawable.tv_5a_8,
+ R.drawable.tv_5a_9,
+ R.drawable.tv_5a_10,
+ R.drawable.tv_5a_11,
+ R.drawable.tv_5a_12,
+ R.drawable.tv_5a_13,
+ R.drawable.tv_5a_14,
+ R.drawable.tv_5a_15,
+ R.drawable.tv_5a_16,
+ R.drawable.tv_5a_17,
+ R.drawable.tv_5a_18,
+ R.drawable.tv_5a_19,
+ R.drawable.tv_5a_20,
+ R.drawable.tv_5a_21,
+ R.drawable.tv_5a_22,
+ R.drawable.tv_5a_23,
+ R.drawable.tv_5a_24,
+ R.drawable.tv_5a_25,
+ R.drawable.tv_5a_26,
+ R.drawable.tv_5a_27,
+ R.drawable.tv_5a_28,
+ R.drawable.tv_5a_29,
+ R.drawable.tv_5a_30,
+ R.drawable.tv_5a_31,
+ R.drawable.tv_5a_32,
+ R.drawable.tv_5a_33,
+ R.drawable.tv_5a_34,
+ R.drawable.tv_5a_35,
+ R.drawable.tv_5a_36,
+ R.drawable.tv_5a_37,
+ R.drawable.tv_5a_38,
+ R.drawable.tv_5a_39,
+ R.drawable.tv_5a_40,
+ R.drawable.tv_5a_41,
+ R.drawable.tv_5a_42,
+ R.drawable.tv_5a_43,
+ R.drawable.tv_5a_44,
+ R.drawable.tv_5a_45,
+ R.drawable.tv_5a_46,
+ R.drawable.tv_5a_47,
+ R.drawable.tv_5a_48,
+ R.drawable.tv_5a_49,
+ R.drawable.tv_5a_50,
+ R.drawable.tv_5a_51,
+ R.drawable.tv_5a_52,
+ R.drawable.tv_5a_53,
+ R.drawable.tv_5a_54,
+ R.drawable.tv_5a_55,
+ R.drawable.tv_5a_56,
+ R.drawable.tv_5a_57,
+ R.drawable.tv_5a_58,
+ R.drawable.tv_5a_59,
+ R.drawable.tv_5a_60,
+ R.drawable.tv_5a_61,
+ R.drawable.tv_5a_62,
+ R.drawable.tv_5a_63,
+ R.drawable.tv_5a_64,
+ R.drawable.tv_5a_65,
+ R.drawable.tv_5a_66,
+ R.drawable.tv_5a_67,
+ R.drawable.tv_5a_68,
+ R.drawable.tv_5a_69,
+ R.drawable.tv_5a_70,
+ R.drawable.tv_5a_71,
+ R.drawable.tv_5a_72,
+ R.drawable.tv_5a_73,
+ R.drawable.tv_5a_74,
+ R.drawable.tv_5a_75,
+ R.drawable.tv_5a_76,
+ R.drawable.tv_5a_77,
+ R.drawable.tv_5a_78,
+ R.drawable.tv_5a_79,
+ R.drawable.tv_5a_80,
+ R.drawable.tv_5a_81,
+ R.drawable.tv_5a_82,
+ R.drawable.tv_5a_83,
+ R.drawable.tv_5a_84,
+ R.drawable.tv_5a_85,
+ R.drawable.tv_5a_86,
+ R.drawable.tv_5a_87,
+ R.drawable.tv_5a_88,
+ R.drawable.tv_5a_89,
+ R.drawable.tv_5a_90,
+ R.drawable.tv_5a_91,
+ R.drawable.tv_5a_92,
+ R.drawable.tv_5a_93,
+ R.drawable.tv_5a_94,
+ R.drawable.tv_5a_95,
+ R.drawable.tv_5a_96,
+ R.drawable.tv_5a_97,
+ R.drawable.tv_5a_98,
+ R.drawable.tv_5a_99,
+ R.drawable.tv_5a_100,
+ R.drawable.tv_5a_101,
+ R.drawable.tv_5a_102,
+ R.drawable.tv_5a_103,
+ R.drawable.tv_5a_104,
+ R.drawable.tv_5a_105,
+ R.drawable.tv_5a_106,
+ R.drawable.tv_5a_107,
+ R.drawable.tv_5a_108,
+ R.drawable.tv_5a_109,
+ R.drawable.tv_5a_110,
+ R.drawable.tv_5a_111,
+ R.drawable.tv_5a_112,
+ R.drawable.tv_5a_113,
+ R.drawable.tv_5a_114,
+ R.drawable.tv_5a_115,
+ R.drawable.tv_5a_116,
+ R.drawable.tv_5a_117,
+ R.drawable.tv_5a_118,
+ R.drawable.tv_5a_119,
+ R.drawable.tv_5a_120,
+ R.drawable.tv_5a_121,
+ R.drawable.tv_5a_122,
+ R.drawable.tv_5a_123,
+ R.drawable.tv_5a_124,
+ R.drawable.tv_5a_125,
+ R.drawable.tv_5a_126,
+ R.drawable.tv_5a_127,
+ R.drawable.tv_5a_128,
+ R.drawable.tv_5a_129,
+ R.drawable.tv_5a_130,
+ R.drawable.tv_5a_131,
+ R.drawable.tv_5a_132,
+ R.drawable.tv_5a_133,
+ R.drawable.tv_5a_134,
+ R.drawable.tv_5a_135,
+ R.drawable.tv_5a_136,
+ R.drawable.tv_5a_137,
+ R.drawable.tv_5a_138,
+ R.drawable.tv_5a_139,
+ R.drawable.tv_5a_140,
+ R.drawable.tv_5a_141,
+ R.drawable.tv_5a_142,
+ R.drawable.tv_5a_143,
+ R.drawable.tv_5a_144,
+ R.drawable.tv_5a_145,
+ R.drawable.tv_5a_146,
+ R.drawable.tv_5a_147,
+ R.drawable.tv_5a_148,
+ R.drawable.tv_5a_149,
+ R.drawable.tv_5a_150,
+ R.drawable.tv_5a_151,
+ R.drawable.tv_5a_152,
+ R.drawable.tv_5a_153,
+ R.drawable.tv_5a_154,
+ R.drawable.tv_5a_155,
+ R.drawable.tv_5a_156,
+ R.drawable.tv_5a_157,
+ R.drawable.tv_5a_158,
+ R.drawable.tv_5a_159,
+ R.drawable.tv_5a_160,
+ R.drawable.tv_5a_161,
+ R.drawable.tv_5a_162,
+ R.drawable.tv_5a_163,
+ R.drawable.tv_5a_164,
+ R.drawable.tv_5a_165,
+ R.drawable.tv_5a_166,
+ R.drawable.tv_5a_167,
+ R.drawable.tv_5a_168,
+ R.drawable.tv_5a_169,
+ R.drawable.tv_5a_170,
+ R.drawable.tv_5a_171,
+ R.drawable.tv_5a_172,
+ R.drawable.tv_5a_173,
+ R.drawable.tv_5a_174,
+ R.drawable.tv_5a_175,
+ R.drawable.tv_5a_176,
+ R.drawable.tv_5a_177,
+ R.drawable.tv_5a_178,
+ R.drawable.tv_5a_179,
+ R.drawable.tv_5a_180,
+ R.drawable.tv_5a_181,
+ R.drawable.tv_5a_182,
+ R.drawable.tv_5a_183,
+ R.drawable.tv_5a_184,
+ R.drawable.tv_5a_185,
+ R.drawable.tv_5a_186,
+ R.drawable.tv_5a_187,
+ R.drawable.tv_5a_188,
+ R.drawable.tv_5a_189,
+ R.drawable.tv_5a_190,
+ R.drawable.tv_5a_191,
+ R.drawable.tv_5a_192,
+ R.drawable.tv_5a_193,
+ R.drawable.tv_5a_194,
+ R.drawable.tv_5a_195,
+ R.drawable.tv_5a_196,
+ R.drawable.tv_5a_197,
+ R.drawable.tv_5a_198,
+ R.drawable.tv_5a_199,
+ R.drawable.tv_5a_200,
+ R.drawable.tv_5a_201,
+ R.drawable.tv_5a_202,
+ R.drawable.tv_5a_203,
+ R.drawable.tv_5a_204,
+ R.drawable.tv_5a_205,
+ R.drawable.tv_5a_206,
+ R.drawable.tv_5a_207,
+ R.drawable.tv_5a_208,
+ R.drawable.tv_5a_209,
+ R.drawable.tv_5a_210,
+ R.drawable.tv_5a_211,
+ R.drawable.tv_5a_212,
+ R.drawable.tv_5a_213,
+ R.drawable.tv_5a_214,
+ R.drawable.tv_5a_215,
+ R.drawable.tv_5a_216,
+ R.drawable.tv_5a_217,
+ R.drawable.tv_5a_218,
+ R.drawable.tv_5a_219,
+ R.drawable.tv_5a_220,
+ R.drawable.tv_5a_221,
+ R.drawable.tv_5a_222,
+ R.drawable.tv_5a_223,
+ R.drawable.tv_5a_224
};
private static final int[] TV_FRAMES_3_BLUE_ARROW = {
- R.drawable.arrow_blue_00,
- R.drawable.arrow_blue_01,
- R.drawable.arrow_blue_02,
- R.drawable.arrow_blue_03,
- R.drawable.arrow_blue_04,
- R.drawable.arrow_blue_05,
- R.drawable.arrow_blue_06,
- R.drawable.arrow_blue_07,
- R.drawable.arrow_blue_08,
- R.drawable.arrow_blue_09,
- R.drawable.arrow_blue_10,
- R.drawable.arrow_blue_11,
- R.drawable.arrow_blue_12,
- R.drawable.arrow_blue_13,
- R.drawable.arrow_blue_14,
- R.drawable.arrow_blue_15,
- R.drawable.arrow_blue_16,
- R.drawable.arrow_blue_17,
- R.drawable.arrow_blue_18,
- R.drawable.arrow_blue_19,
- R.drawable.arrow_blue_20,
- R.drawable.arrow_blue_21,
- R.drawable.arrow_blue_22,
- R.drawable.arrow_blue_23,
- R.drawable.arrow_blue_24,
- R.drawable.arrow_blue_25,
- R.drawable.arrow_blue_26,
- R.drawable.arrow_blue_27,
- R.drawable.arrow_blue_28,
- R.drawable.arrow_blue_29,
- R.drawable.arrow_blue_30,
- R.drawable.arrow_blue_31,
- R.drawable.arrow_blue_32,
- R.drawable.arrow_blue_33,
- R.drawable.arrow_blue_34,
- R.drawable.arrow_blue_35,
- R.drawable.arrow_blue_36,
- R.drawable.arrow_blue_37,
- R.drawable.arrow_blue_38,
- R.drawable.arrow_blue_39,
- R.drawable.arrow_blue_40,
- R.drawable.arrow_blue_41,
- R.drawable.arrow_blue_42,
- R.drawable.arrow_blue_43,
- R.drawable.arrow_blue_44,
- R.drawable.arrow_blue_45,
- R.drawable.arrow_blue_46,
- R.drawable.arrow_blue_47,
- R.drawable.arrow_blue_48,
- R.drawable.arrow_blue_49,
- R.drawable.arrow_blue_50,
- R.drawable.arrow_blue_51,
- R.drawable.arrow_blue_52,
- R.drawable.arrow_blue_53,
- R.drawable.arrow_blue_54,
- R.drawable.arrow_blue_55,
- R.drawable.arrow_blue_56,
- R.drawable.arrow_blue_57,
- R.drawable.arrow_blue_58,
- R.drawable.arrow_blue_59,
- R.drawable.arrow_blue_60
+ R.drawable.arrow_blue_00,
+ R.drawable.arrow_blue_01,
+ R.drawable.arrow_blue_02,
+ R.drawable.arrow_blue_03,
+ R.drawable.arrow_blue_04,
+ R.drawable.arrow_blue_05,
+ R.drawable.arrow_blue_06,
+ R.drawable.arrow_blue_07,
+ R.drawable.arrow_blue_08,
+ R.drawable.arrow_blue_09,
+ R.drawable.arrow_blue_10,
+ R.drawable.arrow_blue_11,
+ R.drawable.arrow_blue_12,
+ R.drawable.arrow_blue_13,
+ R.drawable.arrow_blue_14,
+ R.drawable.arrow_blue_15,
+ R.drawable.arrow_blue_16,
+ R.drawable.arrow_blue_17,
+ R.drawable.arrow_blue_18,
+ R.drawable.arrow_blue_19,
+ R.drawable.arrow_blue_20,
+ R.drawable.arrow_blue_21,
+ R.drawable.arrow_blue_22,
+ R.drawable.arrow_blue_23,
+ R.drawable.arrow_blue_24,
+ R.drawable.arrow_blue_25,
+ R.drawable.arrow_blue_26,
+ R.drawable.arrow_blue_27,
+ R.drawable.arrow_blue_28,
+ R.drawable.arrow_blue_29,
+ R.drawable.arrow_blue_30,
+ R.drawable.arrow_blue_31,
+ R.drawable.arrow_blue_32,
+ R.drawable.arrow_blue_33,
+ R.drawable.arrow_blue_34,
+ R.drawable.arrow_blue_35,
+ R.drawable.arrow_blue_36,
+ R.drawable.arrow_blue_37,
+ R.drawable.arrow_blue_38,
+ R.drawable.arrow_blue_39,
+ R.drawable.arrow_blue_40,
+ R.drawable.arrow_blue_41,
+ R.drawable.arrow_blue_42,
+ R.drawable.arrow_blue_43,
+ R.drawable.arrow_blue_44,
+ R.drawable.arrow_blue_45,
+ R.drawable.arrow_blue_46,
+ R.drawable.arrow_blue_47,
+ R.drawable.arrow_blue_48,
+ R.drawable.arrow_blue_49,
+ R.drawable.arrow_blue_50,
+ R.drawable.arrow_blue_51,
+ R.drawable.arrow_blue_52,
+ R.drawable.arrow_blue_53,
+ R.drawable.arrow_blue_54,
+ R.drawable.arrow_blue_55,
+ R.drawable.arrow_blue_56,
+ R.drawable.arrow_blue_57,
+ R.drawable.arrow_blue_58,
+ R.drawable.arrow_blue_59,
+ R.drawable.arrow_blue_60
};
private static final int[] TV_FRAMES_3_BLUE_START = {
- R.drawable.tv_2a_01,
- R.drawable.tv_2a_02,
- R.drawable.tv_2a_03,
- R.drawable.tv_2a_04,
- R.drawable.tv_2a_05,
- R.drawable.tv_2a_06,
- R.drawable.tv_2a_07,
- R.drawable.tv_2a_08,
- R.drawable.tv_2a_09,
- R.drawable.tv_2a_10,
- R.drawable.tv_2a_11,
- R.drawable.tv_2a_12,
- R.drawable.tv_2a_13,
- R.drawable.tv_2a_14,
- R.drawable.tv_2a_15,
- R.drawable.tv_2a_16,
- R.drawable.tv_2a_17,
- R.drawable.tv_2a_18,
- R.drawable.tv_2a_19
+ R.drawable.tv_2a_01,
+ R.drawable.tv_2a_02,
+ R.drawable.tv_2a_03,
+ R.drawable.tv_2a_04,
+ R.drawable.tv_2a_05,
+ R.drawable.tv_2a_06,
+ R.drawable.tv_2a_07,
+ R.drawable.tv_2a_08,
+ R.drawable.tv_2a_09,
+ R.drawable.tv_2a_10,
+ R.drawable.tv_2a_11,
+ R.drawable.tv_2a_12,
+ R.drawable.tv_2a_13,
+ R.drawable.tv_2a_14,
+ R.drawable.tv_2a_15,
+ R.drawable.tv_2a_16,
+ R.drawable.tv_2a_17,
+ R.drawable.tv_2a_18,
+ R.drawable.tv_2a_19
};
private static final int[] TV_FRAMES_3_BLUE_END = {
- R.drawable.tv_2b_01,
- R.drawable.tv_2b_02,
- R.drawable.tv_2b_03,
- R.drawable.tv_2b_04,
- R.drawable.tv_2b_05,
- R.drawable.tv_2b_06,
- R.drawable.tv_2b_07,
- R.drawable.tv_2b_08,
- R.drawable.tv_2b_09,
- R.drawable.tv_2b_10,
- R.drawable.tv_2b_11,
- R.drawable.tv_2b_12,
- R.drawable.tv_2b_13,
- R.drawable.tv_2b_14,
- R.drawable.tv_2b_15,
- R.drawable.tv_2b_16,
- R.drawable.tv_2b_17,
- R.drawable.tv_2b_18,
- R.drawable.tv_2b_19
+ R.drawable.tv_2b_01,
+ R.drawable.tv_2b_02,
+ R.drawable.tv_2b_03,
+ R.drawable.tv_2b_04,
+ R.drawable.tv_2b_05,
+ R.drawable.tv_2b_06,
+ R.drawable.tv_2b_07,
+ R.drawable.tv_2b_08,
+ R.drawable.tv_2b_09,
+ R.drawable.tv_2b_10,
+ R.drawable.tv_2b_11,
+ R.drawable.tv_2b_12,
+ R.drawable.tv_2b_13,
+ R.drawable.tv_2b_14,
+ R.drawable.tv_2b_15,
+ R.drawable.tv_2b_16,
+ R.drawable.tv_2b_17,
+ R.drawable.tv_2b_18,
+ R.drawable.tv_2b_19
};
private static final int[] TV_FRAMES_3_ORANGE_ARROW = {
- R.drawable.arrow_orange_180,
- R.drawable.arrow_orange_181,
- R.drawable.arrow_orange_182,
- R.drawable.arrow_orange_183,
- R.drawable.arrow_orange_184,
- R.drawable.arrow_orange_185,
- R.drawable.arrow_orange_186,
- R.drawable.arrow_orange_187,
- R.drawable.arrow_orange_188,
- R.drawable.arrow_orange_189,
- R.drawable.arrow_orange_190,
- R.drawable.arrow_orange_191,
- R.drawable.arrow_orange_192,
- R.drawable.arrow_orange_193,
- R.drawable.arrow_orange_194,
- R.drawable.arrow_orange_195,
- R.drawable.arrow_orange_196,
- R.drawable.arrow_orange_197,
- R.drawable.arrow_orange_198,
- R.drawable.arrow_orange_199,
- R.drawable.arrow_orange_200,
- R.drawable.arrow_orange_201,
- R.drawable.arrow_orange_202,
- R.drawable.arrow_orange_203,
- R.drawable.arrow_orange_204,
- R.drawable.arrow_orange_205,
- R.drawable.arrow_orange_206,
- R.drawable.arrow_orange_207,
- R.drawable.arrow_orange_208,
- R.drawable.arrow_orange_209,
- R.drawable.arrow_orange_210,
- R.drawable.arrow_orange_211,
- R.drawable.arrow_orange_212,
- R.drawable.arrow_orange_213,
- R.drawable.arrow_orange_214,
- R.drawable.arrow_orange_215,
- R.drawable.arrow_orange_216,
- R.drawable.arrow_orange_217,
- R.drawable.arrow_orange_218,
- R.drawable.arrow_orange_219,
- R.drawable.arrow_orange_220,
- R.drawable.arrow_orange_221,
- R.drawable.arrow_orange_222,
- R.drawable.arrow_orange_223,
- R.drawable.arrow_orange_224,
- R.drawable.arrow_orange_225,
- R.drawable.arrow_orange_226,
- R.drawable.arrow_orange_227,
- R.drawable.arrow_orange_228,
- R.drawable.arrow_orange_229,
- R.drawable.arrow_orange_230,
- R.drawable.arrow_orange_231,
- R.drawable.arrow_orange_232,
- R.drawable.arrow_orange_233,
- R.drawable.arrow_orange_234,
- R.drawable.arrow_orange_235,
- R.drawable.arrow_orange_236,
- R.drawable.arrow_orange_237,
- R.drawable.arrow_orange_238,
- R.drawable.arrow_orange_239,
- R.drawable.arrow_orange_240
+ R.drawable.arrow_orange_180,
+ R.drawable.arrow_orange_181,
+ R.drawable.arrow_orange_182,
+ R.drawable.arrow_orange_183,
+ R.drawable.arrow_orange_184,
+ R.drawable.arrow_orange_185,
+ R.drawable.arrow_orange_186,
+ R.drawable.arrow_orange_187,
+ R.drawable.arrow_orange_188,
+ R.drawable.arrow_orange_189,
+ R.drawable.arrow_orange_190,
+ R.drawable.arrow_orange_191,
+ R.drawable.arrow_orange_192,
+ R.drawable.arrow_orange_193,
+ R.drawable.arrow_orange_194,
+ R.drawable.arrow_orange_195,
+ R.drawable.arrow_orange_196,
+ R.drawable.arrow_orange_197,
+ R.drawable.arrow_orange_198,
+ R.drawable.arrow_orange_199,
+ R.drawable.arrow_orange_200,
+ R.drawable.arrow_orange_201,
+ R.drawable.arrow_orange_202,
+ R.drawable.arrow_orange_203,
+ R.drawable.arrow_orange_204,
+ R.drawable.arrow_orange_205,
+ R.drawable.arrow_orange_206,
+ R.drawable.arrow_orange_207,
+ R.drawable.arrow_orange_208,
+ R.drawable.arrow_orange_209,
+ R.drawable.arrow_orange_210,
+ R.drawable.arrow_orange_211,
+ R.drawable.arrow_orange_212,
+ R.drawable.arrow_orange_213,
+ R.drawable.arrow_orange_214,
+ R.drawable.arrow_orange_215,
+ R.drawable.arrow_orange_216,
+ R.drawable.arrow_orange_217,
+ R.drawable.arrow_orange_218,
+ R.drawable.arrow_orange_219,
+ R.drawable.arrow_orange_220,
+ R.drawable.arrow_orange_221,
+ R.drawable.arrow_orange_222,
+ R.drawable.arrow_orange_223,
+ R.drawable.arrow_orange_224,
+ R.drawable.arrow_orange_225,
+ R.drawable.arrow_orange_226,
+ R.drawable.arrow_orange_227,
+ R.drawable.arrow_orange_228,
+ R.drawable.arrow_orange_229,
+ R.drawable.arrow_orange_230,
+ R.drawable.arrow_orange_231,
+ R.drawable.arrow_orange_232,
+ R.drawable.arrow_orange_233,
+ R.drawable.arrow_orange_234,
+ R.drawable.arrow_orange_235,
+ R.drawable.arrow_orange_236,
+ R.drawable.arrow_orange_237,
+ R.drawable.arrow_orange_238,
+ R.drawable.arrow_orange_239,
+ R.drawable.arrow_orange_240
};
private static final int[] TV_FRAMES_3_ORANGE_START = {
- R.drawable.tv_2c_01,
- R.drawable.tv_2c_02,
- R.drawable.tv_2c_03,
- R.drawable.tv_2c_04,
- R.drawable.tv_2c_05,
- R.drawable.tv_2c_06,
- R.drawable.tv_2c_07,
- R.drawable.tv_2c_08,
- R.drawable.tv_2c_09,
- R.drawable.tv_2c_10,
- R.drawable.tv_2c_11,
- R.drawable.tv_2c_12,
- R.drawable.tv_2c_13,
- R.drawable.tv_2c_14,
- R.drawable.tv_2c_15,
- R.drawable.tv_2c_16
+ R.drawable.tv_2c_01,
+ R.drawable.tv_2c_02,
+ R.drawable.tv_2c_03,
+ R.drawable.tv_2c_04,
+ R.drawable.tv_2c_05,
+ R.drawable.tv_2c_06,
+ R.drawable.tv_2c_07,
+ R.drawable.tv_2c_08,
+ R.drawable.tv_2c_09,
+ R.drawable.tv_2c_10,
+ R.drawable.tv_2c_11,
+ R.drawable.tv_2c_12,
+ R.drawable.tv_2c_13,
+ R.drawable.tv_2c_14,
+ R.drawable.tv_2c_15,
+ R.drawable.tv_2c_16
};
private static final int[] TV_FRAMES_4_START = {
- R.drawable.tv_3a_01,
- R.drawable.tv_3a_02,
- R.drawable.tv_3a_03,
- R.drawable.tv_3a_04,
- R.drawable.tv_3a_05,
- R.drawable.tv_3a_06,
- R.drawable.tv_3a_07,
- R.drawable.tv_3a_08,
- R.drawable.tv_3a_09,
- R.drawable.tv_3a_10,
- R.drawable.tv_3a_11,
- R.drawable.tv_3a_12,
- R.drawable.tv_3a_13,
- R.drawable.tv_3a_14,
- R.drawable.tv_3a_15,
- R.drawable.tv_3a_16,
- R.drawable.tv_3a_17,
- R.drawable.tv_3b_75,
- R.drawable.tv_3b_76,
- R.drawable.tv_3b_77,
- R.drawable.tv_3b_78,
- R.drawable.tv_3b_79,
- R.drawable.tv_3b_80,
- R.drawable.tv_3b_81,
- R.drawable.tv_3b_82,
- R.drawable.tv_3b_83,
- R.drawable.tv_3b_84,
- R.drawable.tv_3b_85,
- R.drawable.tv_3b_86,
- R.drawable.tv_3b_87,
- R.drawable.tv_3b_88,
- R.drawable.tv_3b_89,
- R.drawable.tv_3b_90,
- R.drawable.tv_3b_91,
- R.drawable.tv_3b_92,
- R.drawable.tv_3b_93,
- R.drawable.tv_3b_94,
- R.drawable.tv_3b_95,
- R.drawable.tv_3b_96,
- R.drawable.tv_3b_97,
- R.drawable.tv_3b_98,
- R.drawable.tv_3b_99,
- R.drawable.tv_3b_100,
- R.drawable.tv_3b_101,
- R.drawable.tv_3b_102,
- R.drawable.tv_3b_103,
- R.drawable.tv_3b_104,
- R.drawable.tv_3b_105,
- R.drawable.tv_3b_106,
- R.drawable.tv_3b_107,
- R.drawable.tv_3b_108,
- R.drawable.tv_3b_109,
- R.drawable.tv_3b_110,
- R.drawable.tv_3b_111,
- R.drawable.tv_3b_112,
- R.drawable.tv_3b_113,
- R.drawable.tv_3b_114,
- R.drawable.tv_3b_115,
- R.drawable.tv_3b_116,
- R.drawable.tv_3b_117,
- R.drawable.tv_3b_118
+ R.drawable.tv_3a_01,
+ R.drawable.tv_3a_02,
+ R.drawable.tv_3a_03,
+ R.drawable.tv_3a_04,
+ R.drawable.tv_3a_05,
+ R.drawable.tv_3a_06,
+ R.drawable.tv_3a_07,
+ R.drawable.tv_3a_08,
+ R.drawable.tv_3a_09,
+ R.drawable.tv_3a_10,
+ R.drawable.tv_3a_11,
+ R.drawable.tv_3a_12,
+ R.drawable.tv_3a_13,
+ R.drawable.tv_3a_14,
+ R.drawable.tv_3a_15,
+ R.drawable.tv_3a_16,
+ R.drawable.tv_3a_17,
+ R.drawable.tv_3b_75,
+ R.drawable.tv_3b_76,
+ R.drawable.tv_3b_77,
+ R.drawable.tv_3b_78,
+ R.drawable.tv_3b_79,
+ R.drawable.tv_3b_80,
+ R.drawable.tv_3b_81,
+ R.drawable.tv_3b_82,
+ R.drawable.tv_3b_83,
+ R.drawable.tv_3b_84,
+ R.drawable.tv_3b_85,
+ R.drawable.tv_3b_86,
+ R.drawable.tv_3b_87,
+ R.drawable.tv_3b_88,
+ R.drawable.tv_3b_89,
+ R.drawable.tv_3b_90,
+ R.drawable.tv_3b_91,
+ R.drawable.tv_3b_92,
+ R.drawable.tv_3b_93,
+ R.drawable.tv_3b_94,
+ R.drawable.tv_3b_95,
+ R.drawable.tv_3b_96,
+ R.drawable.tv_3b_97,
+ R.drawable.tv_3b_98,
+ R.drawable.tv_3b_99,
+ R.drawable.tv_3b_100,
+ R.drawable.tv_3b_101,
+ R.drawable.tv_3b_102,
+ R.drawable.tv_3b_103,
+ R.drawable.tv_3b_104,
+ R.drawable.tv_3b_105,
+ R.drawable.tv_3b_106,
+ R.drawable.tv_3b_107,
+ R.drawable.tv_3b_108,
+ R.drawable.tv_3b_109,
+ R.drawable.tv_3b_110,
+ R.drawable.tv_3b_111,
+ R.drawable.tv_3b_112,
+ R.drawable.tv_3b_113,
+ R.drawable.tv_3b_114,
+ R.drawable.tv_3b_115,
+ R.drawable.tv_3b_116,
+ R.drawable.tv_3b_117,
+ R.drawable.tv_3b_118
};
private String[] mPageTitles;
@@ -581,13 +585,21 @@ public class WelcomeFragment extends OnboardingFragment {
private ImageView mTvContentView;
private ImageView mArrowView;
+ private TextView mTitleView;
+ private Button mStartButton;
+ private View mPagingIndicator;
+
private Animator mAnimator;
+ private boolean mLogoAnimationFinished;
+ private boolean mTitleChanged;
+
public WelcomeFragment() {
- setExitTransition(new SetupAnimationHelper.TransitionBuilder()
- .setSlideEdge(Gravity.START)
- .setParentIdsForDelay(new int[]{R.id.onboarding_fragment_root})
- .build());
+ setExitTransition(
+ new SetupAnimationHelper.TransitionBuilder()
+ .setSlideEdge(Gravity.START)
+ .setParentIdsForDelay(new int[] {R.id.onboarding_fragment_root})
+ .build());
}
@Override
@@ -605,11 +617,97 @@ public class WelcomeFragment extends OnboardingFragment {
@Nullable
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle 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) {
+ mTitleView = view.findViewById(android.support.v17.leanback.R.id.title);
+ mPagingIndicator = view.findViewById(android.support.v17.leanback.R.id.page_indicator);
+ mStartButton = view.findViewById(android.support.v17.leanback.R.id.button_start);
+
+ mStartButton.setAccessibilityDelegate(
+ new AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityEvent(
+ View host, AccessibilityEvent event) {
+ int type = event.getEventType();
+ if (type == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED
+ || type == AccessibilityEvent.TYPE_VIEW_FOCUSED) {
+ if (!mTitleChanged || mTitleView.isAccessibilityFocused()) {
+ // Skip the event before the title is accessibility focused to avoid
+ // race
+ // conditions
+ return;
+ }
+ }
+ super.onInitializeAccessibilityEvent(host, event);
+ }
+ });
+
+ mPagingIndicator.setAccessibilityDelegate(
+ new AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityEvent(
+ View host, AccessibilityEvent event) {
+ int type = event.getEventType();
+ if (type == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED
+ || type == AccessibilityEvent.TYPE_VIEW_FOCUSED) {
+ if (!mTitleChanged || mTitleView.isAccessibilityFocused()) {
+ // Skip the event before the title is accessibility focused to avoid
+ // race
+ // conditions
+ return;
+ }
+ }
+ super.onInitializeAccessibilityEvent(host, event);
+ }
+ });
+
+ mTitleView.setAccessibilityDelegate(
+ new AccessibilityDelegate() {
+ @Override
+ public boolean performAccessibilityAction(View host, int action, Bundle args) {
+ if (action == ACTION_CLEAR_ACCESSIBILITY_FOCUS) {
+ if (!mTitleChanged || mTitleView.isAccessibilityFocused()) {
+ // Skip the event before the title is accessibility focused to avoid
+ // race
+ // conditions
+ return false;
+ }
+ }
+ return super.performAccessibilityAction(host, action, args);
+ }
+ });
+
+ mTitleView.addTextChangedListener(
+ new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ mTitleChanged = false;
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {}
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (!mTitleView.isAccessibilityFocused()) {
+ mTitleView.performAccessibilityAction(
+ AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
+ } else {
+ mTitleView.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+ }
+ mTitleChanged = true;
+ }
+ });
+ return view;
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ if (savedInstanceState != null && mLogoAnimationFinished) {
switch (getCurrentPageIndex()) {
case 0:
mTvContentView.setImageResource(
@@ -631,7 +729,6 @@ public class WelcomeFragment extends OnboardingFragment {
break;
}
}
- return view;
}
@Override
@@ -640,29 +737,38 @@ public class WelcomeFragment extends OnboardingFragment {
}
@Override
+ protected void onLogoAnimationFinished() {
+ super.onLogoAnimationFinished();
+ mLogoAnimationFinished = true;
+ }
+
+ @Override
protected Animator onCreateEnterAnimation() {
List<Animator> animators = new ArrayList<>();
// Cloud 1
View view = getActivity().findViewById(R.id.cloud1);
view.setAlpha(0);
- Animator animator = AnimatorInflater.loadAnimator(getActivity(),
- R.animator.onboarding_welcome_cloud_enter);
+ Animator animator =
+ AnimatorInflater.loadAnimator(
+ getActivity(), R.animator.onboarding_welcome_cloud_enter);
animator.setStartDelay(START_DELAY_CLOUD_MS);
animator.setTarget(view);
animators.add(animator);
// Cloud 2
view = getActivity().findViewById(R.id.cloud2);
view.setAlpha(0);
- animator = AnimatorInflater.loadAnimator(getActivity(),
- R.animator.onboarding_welcome_cloud_enter);
+ animator =
+ AnimatorInflater.loadAnimator(
+ getActivity(), R.animator.onboarding_welcome_cloud_enter);
animator.setStartDelay(START_DELAY_CLOUD_MS);
animator.setTarget(view);
animators.add(animator);
// TV container
view = getActivity().findViewById(R.id.tv_container);
view.setAlpha(0);
- animator = AnimatorInflater.loadAnimator(getActivity(),
- R.animator.onboarding_welcome_tv_enter);
+ animator =
+ AnimatorInflater.loadAnimator(
+ getActivity(), R.animator.onboarding_welcome_tv_enter);
animator.setStartDelay(START_DELAY_TV_MS);
animator.setTarget(view);
animators.add(animator);
@@ -675,8 +781,9 @@ public class WelcomeFragment extends OnboardingFragment {
// Shadow
view = getActivity().findViewById(R.id.shadow);
view.setAlpha(0);
- animator = AnimatorInflater.loadAnimator(getActivity(),
- R.animator.onboarding_welcome_shadow_enter);
+ animator =
+ AnimatorInflater.loadAnimator(
+ getActivity(), R.animator.onboarding_welcome_shadow_enter);
animator.setStartDelay(START_DELAY_SHADOW_MS);
animator.setTarget(view);
animators.add(animator);
@@ -702,8 +809,9 @@ public class WelcomeFragment extends OnboardingFragment {
@Nullable
@Override
protected View onCreateForegroundView(LayoutInflater inflater, ViewGroup container) {
- mArrowView = (ImageView) inflater.inflate(R.layout.onboarding_welcome_foreground, container,
- false);
+ mArrowView =
+ (ImageView)
+ inflater.inflate(R.layout.onboarding_welcome_foreground, container, false);
return mArrowView;
}
@@ -732,65 +840,74 @@ public class WelcomeFragment extends OnboardingFragment {
if (mAnimator != null) {
mAnimator.cancel();
}
+ mTitleChanged = false;
mArrowView.setVisibility(View.GONE);
// TV screen hiding animator.
- Animator hideAnimator = previousPage == 0
- ? SetupAnimationHelper.createFrameAnimator(mTvContentView, TV_FRAMES_1_END)
- : SetupAnimationHelper.createFadeOutAnimator(mTvContentView,
- VIDEO_FADE_OUT_DURATION_MS, true);
+ Animator hideAnimator =
+ previousPage == 0
+ ? SetupAnimationHelper.createFrameAnimator(mTvContentView, TV_FRAMES_1_END)
+ : SetupAnimationHelper.createFadeOutAnimator(
+ mTvContentView, VIDEO_FADE_OUT_DURATION_MS, true);
// TV screen showing animator.
AnimatorSet animatorSet = new AnimatorSet();
int firstFrame;
switch (newPage) {
case 0:
- animatorSet.playSequentially(hideAnimator,
- SetupAnimationHelper.createFrameAnimator(mTvContentView,
- TV_FRAMES_1_START));
+ animatorSet.playSequentially(
+ hideAnimator,
+ SetupAnimationHelper.createFrameAnimator(
+ mTvContentView, TV_FRAMES_1_START));
firstFrame = TV_FRAMES_1_START[0];
break;
case 1:
- animatorSet.playSequentially(hideAnimator,
- SetupAnimationHelper.createFrameAnimator(mTvContentView,
- TV_FRAMES_2_START));
+ animatorSet.playSequentially(
+ hideAnimator,
+ SetupAnimationHelper.createFrameAnimator(
+ mTvContentView, TV_FRAMES_2_START));
firstFrame = TV_FRAMES_2_START[0];
break;
case 2:
mArrowView.setVisibility(View.VISIBLE);
- animatorSet.playSequentially(hideAnimator,
- SetupAnimationHelper.createFrameAnimator(mArrowView,
- TV_FRAMES_3_BLUE_ARROW),
- SetupAnimationHelper.createFrameAnimator(mTvContentView,
- TV_FRAMES_3_BLUE_START),
- SetupAnimationHelper.createFrameAnimatorWithDelay(mTvContentView,
- TV_FRAMES_3_BLUE_END, BLUE_SCREEN_HOLD_DURATION_MS),
- SetupAnimationHelper.createFrameAnimator(mArrowView,
- TV_FRAMES_3_ORANGE_ARROW),
- SetupAnimationHelper.createFrameAnimator(mTvContentView,
- TV_FRAMES_3_ORANGE_START));
- animatorSet.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mArrowView.setImageResource(TV_FRAMES_3_BLUE_ARROW[0]);
- }
- });
+ animatorSet.playSequentially(
+ hideAnimator,
+ SetupAnimationHelper.createFrameAnimator(
+ mArrowView, TV_FRAMES_3_BLUE_ARROW),
+ SetupAnimationHelper.createFrameAnimator(
+ mTvContentView, TV_FRAMES_3_BLUE_START),
+ SetupAnimationHelper.createFrameAnimatorWithDelay(
+ mTvContentView, TV_FRAMES_3_BLUE_END, BLUE_SCREEN_HOLD_DURATION_MS),
+ SetupAnimationHelper.createFrameAnimator(
+ mArrowView, TV_FRAMES_3_ORANGE_ARROW),
+ SetupAnimationHelper.createFrameAnimator(
+ mTvContentView, TV_FRAMES_3_ORANGE_START));
+ animatorSet.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mArrowView.setImageResource(TV_FRAMES_3_BLUE_ARROW[0]);
+ }
+ });
firstFrame = TV_FRAMES_3_BLUE_START[0];
break;
case 3:
default:
- animatorSet.playSequentially(hideAnimator,
- SetupAnimationHelper.createFrameAnimator(mTvContentView,
- TV_FRAMES_4_START));
+ animatorSet.playSequentially(
+ hideAnimator,
+ SetupAnimationHelper.createFrameAnimator(
+ mTvContentView, TV_FRAMES_4_START));
firstFrame = TV_FRAMES_4_START[0];
break;
}
final int firstImageResource = firstFrame;
- hideAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // Shows the first frame of show animation when the hide animator is canceled.
- mTvContentView.setImageResource(firstImageResource);
- }
- });
+ hideAnimator.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Shows the first frame of show animation when the hide animator is
+ // canceled.
+ mTvContentView.setImageResource(firstImageResource);
+ }
+ });
mAnimator = SetupAnimationHelper.applyAnimationTimeScale(animatorSet);
mAnimator.start();
}
diff --git a/src/com/android/tv/parental/ContentRatingLevelPolicy.java b/src/com/android/tv/parental/ContentRatingLevelPolicy.java
index 9bd15423..5b0a6cf5 100644
--- a/src/com/android/tv/parental/ContentRatingLevelPolicy.java
+++ b/src/com/android/tv/parental/ContentRatingLevelPolicy.java
@@ -17,12 +17,10 @@
package com.android.tv.parental;
import android.media.tv.TvContentRating;
-
import com.android.tv.parental.ContentRatingSystem.Rating;
import com.android.tv.parental.ContentRatingSystem.SubRating;
import com.android.tv.util.TvSettings;
import com.android.tv.util.TvSettings.ContentRatingLevel;
-
import java.util.HashSet;
import java.util.Set;
@@ -31,10 +29,11 @@ public class ContentRatingLevelPolicy {
private static final int AGE_THRESHOLD_FOR_LEVEL_MEDIUM = 12;
private static final int AGE_THRESHOLD_FOR_LEVEL_LOW = -1; // Highest age for each rating system
- private ContentRatingLevelPolicy() { }
+ private ContentRatingLevelPolicy() {}
public static Set<TvContentRating> getRatingsForLevel(
- ParentalControlSettings settings, ContentRatingsManager manager,
+ ParentalControlSettings settings,
+ ContentRatingsManager manager,
@ContentRatingLevel int level) {
if (level == TvSettings.CONTENT_RATING_LEVEL_NONE) {
return new HashSet<>();
@@ -64,14 +63,17 @@ public class ContentRatingLevelPolicy {
if (rating.getAgeHint() < ageLimit) {
continue;
}
- TvContentRating tvContentRating = TvContentRating.createRating(
- contentRatingSystem.getDomain(), contentRatingSystem.getName(),
- rating.getName());
+ TvContentRating tvContentRating =
+ TvContentRating.createRating(
+ contentRatingSystem.getDomain(),
+ contentRatingSystem.getName(),
+ rating.getName());
ratings.add(tvContentRating);
for (SubRating subRating : rating.getSubRatings()) {
- tvContentRating = TvContentRating.createRating(
- contentRatingSystem.getDomain(), contentRatingSystem.getName(),
- rating.getName(), subRating.getName());
+ tvContentRating =
+ TvContentRating.createRating(
+ contentRatingSystem.getDomain(), contentRatingSystem.getName(),
+ rating.getName(), subRating.getName());
ratings.add(tvContentRating);
}
}
diff --git a/src/com/android/tv/parental/ContentRatingSystem.java b/src/com/android/tv/parental/ContentRatingSystem.java
index 5672b793..600aaca1 100644
--- a/src/com/android/tv/parental/ContentRatingSystem.java
+++ b/src/com/android/tv/parental/ContentRatingSystem.java
@@ -20,9 +20,7 @@ import android.content.Context;
import android.graphics.drawable.Drawable;
import android.media.tv.TvContentRating;
import android.text.TextUtils;
-
import com.android.tv.R;
-
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
@@ -86,7 +84,7 @@ public class ContentRatingSystem {
return mDomain + DELIMITER + mName;
}
- public String getName(){
+ public String getName() {
return mName;
}
@@ -94,19 +92,19 @@ public class ContentRatingSystem {
return mDomain;
}
- public String getTitle(){
+ public String getTitle() {
return mTitle;
}
- public String getDescription(){
+ public String getDescription() {
return mDescription;
}
- public List<String> getCountries(){
+ public List<String> getCountries() {
return mCountries;
}
- public List<Rating> getRatings(){
+ public List<Rating> getRatings() {
return mRatings;
}
@@ -119,11 +117,11 @@ public class ContentRatingSystem {
return null;
}
- public List<SubRating> getSubRatings(){
+ public List<SubRating> getSubRatings() {
return mSubRatings;
}
- public List<Order> getOrders(){
+ public List<Order> getOrders() {
return mOrders;
}
@@ -139,9 +137,7 @@ public class ContentRatingSystem {
return mIsCustom;
}
- /**
- * Returns true if the ratings is owned by this content rating system.
- */
+ /** Returns true if the ratings is owned by this content rating system. */
public boolean ownsRating(TvContentRating rating) {
return mDomain.equals(rating.getDomain()) && mName.equals(rating.getRatingSystem());
}
@@ -161,9 +157,16 @@ public class ContentRatingSystem {
}
private ContentRatingSystem(
- String name, String domain, String title, String description, List<String> countries,
- String displayName, List<Rating> ratings, List<SubRating> subRatings,
- List<Order> orders, boolean isCustom) {
+ String name,
+ String domain,
+ String title,
+ String description,
+ List<String> countries,
+ String displayName,
+ List<Rating> ratings,
+ List<SubRating> subRatings,
+ List<Order> orders,
+ boolean isCustom) {
mName = name;
mDomain = domain;
mTitle = title;
@@ -298,8 +301,8 @@ public class ContentRatingSystem {
}
}
if (!used) {
- throw new IllegalArgumentException("Subrating " + subRating.getName() +
- " isn't used by any rating");
+ throw new IllegalArgumentException(
+ "Subrating " + subRating.getName() + " isn't used by any rating");
}
}
@@ -310,8 +313,17 @@ public class ContentRatingSystem {
}
}
- return new ContentRatingSystem(mName, mDomain, mTitle, mDescription, mCountries,
- displayName, ratings, subRatings, orders, mIsCustom);
+ return new ContentRatingSystem(
+ mName,
+ mDomain,
+ mTitle,
+ mDescription,
+ mCountries,
+ displayName,
+ ratings,
+ subRatings,
+ orders,
+ mIsCustom);
}
}
@@ -347,8 +359,13 @@ public class ContentRatingSystem {
return mSubRatings;
}
- private Rating(String name, String title, String description, Drawable icon,
- int contentAgeHint, List<SubRating> subRatings) {
+ private Rating(
+ String name,
+ String title,
+ String description,
+ Drawable icon,
+ int contentAgeHint,
+ List<SubRating> subRatings) {
mName = name;
mTitle = title;
mDescription = description;
@@ -365,8 +382,7 @@ public class ContentRatingSystem {
private int mContentAgeHint = -1;
private final List<String> mSubRatingNames = new ArrayList<>();
- public Builder() {
- }
+ public Builder() {}
public void setName(String name) {
mName = name;
@@ -400,8 +416,8 @@ public class ContentRatingSystem {
throw new IllegalArgumentException("Invalid subrating for rating " + mName);
}
if (mContentAgeHint < 0) {
- throw new IllegalArgumentException("Rating " + mName + " should define " +
- "non-negative contentAgeHint");
+ throw new IllegalArgumentException(
+ "Rating " + mName + " should define " + "non-negative contentAgeHint");
}
List<SubRating> subRatings = new ArrayList<>();
@@ -415,12 +431,11 @@ public class ContentRatingSystem {
}
}
if (!found) {
- throw new IllegalArgumentException("Unknown subrating name " + subRatingId +
- " in rating " + mName);
+ throw new IllegalArgumentException(
+ "Unknown subrating name " + subRatingId + " in rating " + mName);
}
}
- return new Rating(
- mName, mTitle, mDescription, mIcon, mContentAgeHint, subRatings);
+ return new Rating(mName, mTitle, mDescription, mIcon, mContentAgeHint, subRatings);
}
}
}
@@ -460,8 +475,7 @@ public class ContentRatingSystem {
private String mDescription;
private Drawable mIcon;
- public Builder() {
- }
+ public Builder() {}
public void setName(String name) {
mName = name;
@@ -500,8 +514,8 @@ public class ContentRatingSystem {
}
/**
- * Returns index of the rating in this order.
- * Returns -1 if this order doesn't contain the rating.
+ * 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++) {
@@ -515,8 +529,7 @@ public class ContentRatingSystem {
public static class Builder {
private final List<String> mRatingNames = new ArrayList<>();
- public Builder() {
- }
+ public Builder() {}
private Order build(List<Rating> ratings) {
List<Rating> ratingOrder = new ArrayList<>();
@@ -531,8 +544,8 @@ public class ContentRatingSystem {
}
if (!found) {
- throw new IllegalArgumentException("Unknown rating " + ratingName +
- " in rating-order tag");
+ throw new IllegalArgumentException(
+ "Unknown rating " + ratingName + " in rating-order tag");
}
}
diff --git a/src/com/android/tv/parental/ContentRatingsManager.java b/src/com/android/tv/parental/ContentRatingsManager.java
index c3bb8c0f..32a1325b 100644
--- a/src/com/android/tv/parental/ContentRatingsManager.java
+++ b/src/com/android/tv/parental/ContentRatingsManager.java
@@ -19,14 +19,12 @@ package com.android.tv.parental;
import android.content.Context;
import android.media.tv.TvContentRating;
import android.media.tv.TvContentRatingSystemInfo;
-import android.media.tv.TvInputManager;
import android.support.annotation.Nullable;
import android.text.TextUtils;
-
import com.android.tv.R;
import com.android.tv.parental.ContentRatingSystem.Rating;
import com.android.tv.parental.ContentRatingSystem.SubRating;
-
+import com.android.tv.util.TvInputManagerHelper;
import java.util.ArrayList;
import java.util.List;
@@ -34,19 +32,19 @@ public class ContentRatingsManager {
private final List<ContentRatingSystem> mContentRatingSystems = new ArrayList<>();
private final Context mContext;
+ private final TvInputManagerHelper.TvInputManagerInterface mTvInputManager;
- public ContentRatingsManager(Context context) {
+ public ContentRatingsManager(
+ Context context, TvInputManagerHelper.TvInputManagerInterface tvInputManager) {
mContext = context;
+ this.mTvInputManager = tvInputManager;
}
public void update() {
mContentRatingSystems.clear();
-
- TvInputManager manager =
- (TvInputManager) mContext.getSystemService(Context.TV_INPUT_SERVICE);
ContentRatingsParser parser = new ContentRatingsParser(mContext);
- List<TvContentRatingSystemInfo> infos = manager.getTvContentRatingSystemList();
+ List<TvContentRatingSystemInfo> infos = mTvInputManager.getTvContentRatingSystemList();
for (TvContentRatingSystemInfo info : infos) {
List<ContentRatingSystem> list = parser.parse(info);
if (list != null) {
@@ -55,9 +53,7 @@ public class ContentRatingsManager {
}
}
- /**
- * Returns the content rating system with the give ID.
- */
+ /** Returns the content rating system with the give ID. */
@Nullable
public ContentRatingSystem getContentRatingSystem(String contentRatingSystemId) {
for (ContentRatingSystem ratingSystem : mContentRatingSystems) {
@@ -68,9 +64,7 @@ public class ContentRatingsManager {
return null;
}
- /**
- * Returns a new list of all content rating systems defined.
- */
+ /** Returns a new list of all content rating systems defined. */
public List<ContentRatingSystem> getContentRatingSystems() {
return new ArrayList<>(mContentRatingSystems);
}
@@ -118,8 +112,10 @@ public class ContentRatingsManager {
private List<SubRating> getSubRatings(Rating rating, TvContentRating canonicalRating) {
List<SubRating> subRatings = new ArrayList<>();
- if (rating == null || rating.getSubRatings() == null
- || canonicalRating == null || canonicalRating.getSubRatings() == null) {
+ if (rating == null
+ || rating.getSubRatings() == null
+ || canonicalRating == null
+ || canonicalRating.getSubRatings() == null) {
return subRatings;
}
for (String subRatingString : canonicalRating.getSubRatings()) {
diff --git a/src/com/android/tv/parental/ContentRatingsParser.java b/src/com/android/tv/parental/ContentRatingsParser.java
index d9f62473..14df88ea 100644
--- a/src/com/android/tv/parental/ContentRatingsParser.java
+++ b/src/com/android/tv/parental/ContentRatingsParser.java
@@ -24,18 +24,16 @@ import android.content.res.XmlResourceParser;
import android.media.tv.TvContentRatingSystemInfo;
import android.net.Uri;
import android.util.Log;
-
import com.android.tv.parental.ContentRatingSystem.Order;
import com.android.tv.parental.ContentRatingSystem.Rating;
import com.android.tv.parental.ContentRatingSystem.SubRating;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+/** Parses Content Ratings */
public class ContentRatingsParser {
private static final String TAG = "ContentRatingsParser";
private static final boolean DEBUG = false;
@@ -74,8 +72,8 @@ public class ContentRatingsParser {
try {
String packageName = uri.getAuthority();
int resId = (int) ContentUris.parseId(uri);
- try (XmlResourceParser parser = mContext.getPackageManager()
- .getXml(packageName, resId, null)) {
+ try (XmlResourceParser parser =
+ mContext.getPackageManager().getXml(packageName, resId, null)) {
if (parser == null) {
throw new IllegalArgumentException("Cannot get XML with URI " + uri);
}
@@ -90,8 +88,8 @@ public class ContentRatingsParser {
return ratingSystems;
}
- private List<ContentRatingSystem> parse(XmlResourceParser parser, String domain,
- boolean isCustom)
+ private List<ContentRatingSystem> parse(
+ XmlResourceParser parser, String domain, boolean isCustom)
throws XmlPullParserException, IOException {
try {
mResources = mContext.getPackageManager().getResourcesForApplication(domain);
@@ -112,7 +110,9 @@ public class ContentRatingsParser {
int eventType = parser.getEventType();
assertEquals(eventType, XmlPullParser.START_TAG, "Malformed XML: Not a valid XML file");
- assertEquals(parser.getName(), TAG_RATING_SYSTEM_DEFINITIONS,
+ assertEquals(
+ parser.getName(),
+ TAG_RATING_SYSTEM_DEFINITIONS,
"Malformed XML: Should start with tag " + TAG_RATING_SYSTEM_DEFINITIONS);
boolean hasVersionAttr = false;
@@ -124,8 +124,10 @@ public class ContentRatingsParser {
}
}
if (!hasVersionAttr) {
- throw new XmlPullParserException("Malformed XML: Should contains a version attribute"
- + " in " + TAG_RATING_SYSTEM_DEFINITIONS);
+ throw new XmlPullParserException(
+ "Malformed XML: Should contains a version attribute"
+ + " in "
+ + TAG_RATING_SYSTEM_DEFINITIONS);
}
List<ContentRatingSystem> ratingSystems = new ArrayList<>();
@@ -135,25 +137,29 @@ public class ContentRatingsParser {
if (TAG_RATING_SYSTEM_DEFINITION.equals(parser.getName())) {
ratingSystems.add(parseRatingSystemDefinition(parser, domain, isCustom));
} else {
- checkVersion("Malformed XML: Should contains " +
- TAG_RATING_SYSTEM_DEFINITION);
+ checkVersion(
+ "Malformed XML: Should contains " + TAG_RATING_SYSTEM_DEFINITION);
}
break;
case XmlPullParser.END_TAG:
if (TAG_RATING_SYSTEM_DEFINITIONS.equals(parser.getName())) {
eventType = parser.next();
- assertEquals(eventType, XmlPullParser.END_DOCUMENT,
- "Malformed XML: Should end with tag " +
- TAG_RATING_SYSTEM_DEFINITIONS);
+ assertEquals(
+ eventType,
+ XmlPullParser.END_DOCUMENT,
+ "Malformed XML: Should end with tag "
+ + TAG_RATING_SYSTEM_DEFINITIONS);
return ratingSystems;
} else {
- checkVersion("Malformed XML: Should end with tag " +
- TAG_RATING_SYSTEM_DEFINITIONS);
+ checkVersion(
+ "Malformed XML: Should end with tag "
+ + TAG_RATING_SYSTEM_DEFINITIONS);
}
}
}
- throw new XmlPullParserException(TAG_RATING_SYSTEM_DEFINITIONS +
- " section is incomplete or section ending tag is missing");
+ throw new XmlPullParserException(
+ TAG_RATING_SYSTEM_DEFINITIONS
+ + " section is incomplete or section ending tag is missing");
}
private static void assertEquals(int a, int b, String msg) throws XmlPullParserException {
@@ -174,8 +180,9 @@ public class ContentRatingsParser {
}
}
- private ContentRatingSystem parseRatingSystemDefinition(XmlResourceParser parser, String domain,
- boolean isCustom) throws XmlPullParserException, IOException {
+ private ContentRatingSystem parseRatingSystemDefinition(
+ XmlResourceParser parser, String domain, boolean isCustom)
+ throws XmlPullParserException, IOException {
ContentRatingSystem.Builder builder = new ContentRatingSystem.Builder(mContext);
builder.setDomain(domain);
@@ -198,13 +205,17 @@ public class ContentRatingsParser {
mResources.getString(parser.getAttributeResourceValue(i, 0)));
break;
default:
- checkVersion("Malformed XML: Unknown attribute " + attr + " in " +
- TAG_RATING_SYSTEM_DEFINITION);
+ checkVersion(
+ "Malformed XML: Unknown attribute "
+ + attr
+ + " in "
+ + TAG_RATING_SYSTEM_DEFINITION);
}
}
while (parser.next() != XmlPullParser.END_DOCUMENT) {
- switch (parser.getEventType()) {
+ int eventType = parser.getEventType();
+ switch (eventType) {
case XmlPullParser.START_TAG:
String tag = parser.getName();
switch (tag) {
@@ -218,8 +229,11 @@ public class ContentRatingsParser {
builder.addOrderBuilder(parseOrder(parser));
break;
default:
- checkVersion("Malformed XML: Unknown tag " + tag + " in " +
- TAG_RATING_SYSTEM_DEFINITION);
+ checkVersion(
+ "Malformed XML: Unknown tag "
+ + tag
+ + " in "
+ + TAG_RATING_SYSTEM_DEFINITION);
}
break;
case XmlPullParser.END_TAG:
@@ -227,13 +241,21 @@ public class ContentRatingsParser {
builder.setIsCustom(isCustom);
return builder.build();
} else {
- checkVersion("Malformed XML: Tag mismatch for " +
- TAG_RATING_SYSTEM_DEFINITION);
+ checkVersion(
+ "Malformed XML: Tag mismatch for " + TAG_RATING_SYSTEM_DEFINITION);
}
+ break;
+ default:
+ checkVersion(
+ "Malformed XML: Unknown event type "
+ + eventType
+ + " in "
+ + TAG_RATING_SYSTEM_DEFINITION);
}
}
- throw new XmlPullParserException(TAG_RATING_SYSTEM_DEFINITION +
- " section is incomplete or section ending tag is missing");
+ throw new XmlPullParserException(
+ TAG_RATING_SYSTEM_DEFINITION
+ + " section is incomplete or section ending tag is missing");
}
private Rating.Builder parseRatingDefinition(XmlResourceParser parser)
@@ -265,14 +287,19 @@ public class ContentRatingsParser {
}
if (contentAgeHint < 0) {
- throw new XmlPullParserException("Malformed XML: " + ATTR_CONTENT_AGE_HINT +
- " should be a non-negative number");
+ throw new XmlPullParserException(
+ "Malformed XML: "
+ + ATTR_CONTENT_AGE_HINT
+ + " should be a non-negative number");
}
builder.setContentAgeHint(contentAgeHint);
break;
default:
- checkVersion("Malformed XML: Unknown attribute " + attr + " in " +
- TAG_RATING_DEFINITION);
+ checkVersion(
+ "Malformed XML: Unknown attribute "
+ + attr
+ + " in "
+ + TAG_RATING_DEFINITION);
}
}
@@ -282,8 +309,11 @@ public class ContentRatingsParser {
if (TAG_SUB_RATING.equals(parser.getName())) {
builder = parseSubRating(parser, builder);
} else {
- checkVersion(("Malformed XML: Only " + TAG_SUB_RATING + " is allowed in " +
- TAG_RATING_DEFINITION));
+ checkVersion(
+ ("Malformed XML: Only "
+ + TAG_SUB_RATING
+ + " is allowed in "
+ + TAG_RATING_DEFINITION));
}
break;
case XmlPullParser.END_TAG:
@@ -294,8 +324,8 @@ public class ContentRatingsParser {
}
}
}
- throw new XmlPullParserException(TAG_RATING_DEFINITION +
- " section is incomplete or section ending tag is missing");
+ throw new XmlPullParserException(
+ TAG_RATING_DEFINITION + " section is incomplete or section ending tag is missing");
}
private SubRating.Builder parseSubRatingDefinition(XmlResourceParser parser)
@@ -320,8 +350,11 @@ public class ContentRatingsParser {
mResources.getDrawable(parser.getAttributeResourceValue(i, 0), null));
break;
default:
- checkVersion("Malformed XML: Unknown attribute " + attr + " in " +
- TAG_SUB_RATING_DEFINITION);
+ checkVersion(
+ "Malformed XML: Unknown attribute "
+ + attr
+ + " in "
+ + TAG_SUB_RATING_DEFINITION);
}
}
@@ -331,23 +364,26 @@ public class ContentRatingsParser {
if (TAG_SUB_RATING_DEFINITION.equals(parser.getName())) {
return builder;
} else {
- checkVersion("Malformed XML: " + TAG_SUB_RATING_DEFINITION +
- " isn't closed");
+ checkVersion(
+ "Malformed XML: " + TAG_SUB_RATING_DEFINITION + " isn't closed");
}
break;
default:
checkVersion("Malformed XML: " + TAG_SUB_RATING_DEFINITION + " has child");
}
}
- throw new XmlPullParserException(TAG_SUB_RATING_DEFINITION +
- " section is incomplete or section ending tag is missing");
+ throw new XmlPullParserException(
+ TAG_SUB_RATING_DEFINITION
+ + " section is incomplete or section ending tag is missing");
}
private Order.Builder parseOrder(XmlResourceParser parser)
throws XmlPullParserException, IOException {
Order.Builder builder = new Order.Builder();
- assertEquals(parser.getAttributeCount(), 0,
+ assertEquals(
+ parser.getAttributeCount(),
+ 0,
"Malformed XML: Attribute isn't allowed in " + TAG_RATING_ORDER);
while (parser.next() != XmlPullParser.END_DOCUMENT) {
@@ -355,19 +391,24 @@ public class ContentRatingsParser {
case XmlPullParser.START_TAG:
if (TAG_RATING.equals(parser.getName())) {
builder = parseRating(parser, builder);
- } else {
- checkVersion("Malformed XML: Only " + TAG_RATING + " is allowed in " +
- TAG_RATING_ORDER);
+ } else {
+ checkVersion(
+ "Malformed XML: Only "
+ + TAG_RATING
+ + " is allowed in "
+ + TAG_RATING_ORDER);
}
break;
case XmlPullParser.END_TAG:
- assertEquals(parser.getName(), TAG_RATING_ORDER,
+ assertEquals(
+ parser.getName(),
+ TAG_RATING_ORDER,
"Malformed XML: Tag mismatch for " + TAG_RATING_ORDER);
return builder;
}
}
- throw new XmlPullParserException(TAG_RATING_ORDER +
- " section is incomplete or section ending tag is missing");
+ throw new XmlPullParserException(
+ TAG_RATING_ORDER + " section is incomplete or section ending tag is missing");
}
private Order.Builder parseRating(XmlResourceParser parser, Order.Builder builder)
@@ -379,8 +420,11 @@ public class ContentRatingsParser {
builder.addRatingName(parser.getAttributeValue(i));
break;
default:
- checkVersion("Malformed XML: " + TAG_RATING_ORDER + " should only contain "
- + ATTR_NAME);
+ checkVersion(
+ "Malformed XML: "
+ + TAG_RATING_ORDER
+ + " should only contain "
+ + ATTR_NAME);
}
}
@@ -393,8 +437,8 @@ public class ContentRatingsParser {
}
}
}
- throw new XmlPullParserException(TAG_RATING +
- " section is incomplete or section ending tag is missing");
+ throw new XmlPullParserException(
+ TAG_RATING + " section is incomplete or section ending tag is missing");
}
private Rating.Builder parseSubRating(XmlResourceParser parser, Rating.Builder builder)
@@ -406,8 +450,11 @@ public class ContentRatingsParser {
builder.addSubRatingName(parser.getAttributeValue(i));
break;
default:
- checkVersion("Malformed XML: " + TAG_SUB_RATING + " should only contain " +
- ATTR_NAME);
+ checkVersion(
+ "Malformed XML: "
+ + TAG_SUB_RATING
+ + " should only contain "
+ + ATTR_NAME);
}
}
@@ -420,8 +467,8 @@ public class ContentRatingsParser {
}
}
}
- throw new XmlPullParserException(TAG_SUB_RATING +
- " section is incomplete or section ending tag is missing");
+ throw new XmlPullParserException(
+ TAG_SUB_RATING + " section is incomplete or section ending tag is missing");
}
// Title might be a resource id or a string value. Try loading as an id first, then use the
diff --git a/src/com/android/tv/parental/ParentalControlSettings.java b/src/com/android/tv/parental/ParentalControlSettings.java
index 2471c565..db1f0a4d 100644
--- a/src/com/android/tv/parental/ParentalControlSettings.java
+++ b/src/com/android/tv/parental/ParentalControlSettings.java
@@ -19,30 +19,22 @@ package com.android.tv.parental;
import android.content.Context;
import android.media.tv.TvContentRating;
import android.media.tv.TvInputManager;
-
-import com.android.tv.experiments.Experiments;
+import com.android.tv.common.experiments.Experiments;
import com.android.tv.parental.ContentRatingSystem.Rating;
import com.android.tv.parental.ContentRatingSystem.SubRating;
import com.android.tv.util.TvSettings;
import com.android.tv.util.TvSettings.ContentRatingLevel;
-
import java.util.HashSet;
import java.util.Set;
public class ParentalControlSettings {
- /**
- * The rating and all of its sub-ratings are blocked.
- */
+ /** The rating and all of its sub-ratings are blocked. */
public static final int RATING_BLOCKED = 0;
- /**
- * The rating is blocked but not all of its sub-ratings are blocked.
- */
+ /** The rating is blocked but not all of its sub-ratings are blocked. */
public static final int RATING_BLOCKED_PARTIAL = 1;
- /**
- * The rating is not blocked.
- */
+ /** The rating is not blocked. */
public static final int RATING_NOT_BLOCKED = 2;
private final Context mContext;
@@ -65,8 +57,10 @@ public class ParentalControlSettings {
mTvInputManager.setParentalControlsEnabled(enabled);
}
- public void setContentRatingSystemEnabled(ContentRatingsManager manager,
- ContentRatingSystem contentRatingSystem, boolean enabled) {
+ public void setContentRatingSystemEnabled(
+ ContentRatingsManager manager,
+ ContentRatingSystem contentRatingSystem,
+ boolean enabled) {
if (enabled) {
TvSettings.addContentRatingSystem(mContext, contentRatingSystem.getId());
@@ -118,8 +112,8 @@ public class ParentalControlSettings {
}
}
- public void setContentRatingLevel(ContentRatingsManager manager,
- @ContentRatingLevel int level) {
+ public void setContentRatingLevel(
+ ContentRatingsManager manager, @ContentRatingLevel int level) {
@ContentRatingLevel int currentLevel = getContentRatingLevel();
if (level == currentLevel) {
return;
@@ -167,18 +161,17 @@ public class ParentalControlSettings {
/**
* Sets the blocked status of a given content rating.
- * <p>
- * Note that a call to this method automatically changes the current rating level to
- * {@code TvSettings.CONTENT_RATING_LEVEL_CUSTOM} if needed.
- * </p>
+ *
+ * <p>Note that a call to this method automatically changes the current rating level to {@code
+ * TvSettings.CONTENT_RATING_LEVEL_CUSTOM} if needed.
*
* @param contentRatingSystem The content rating system where the given rating belongs.
* @param rating The content rating to set.
* @return {@code true} if changed, {@code false} otherwise.
* @see #setSubRatingBlocked
*/
- public boolean setRatingBlocked(ContentRatingSystem contentRatingSystem, Rating rating,
- boolean blocked) {
+ public boolean setRatingBlocked(
+ ContentRatingSystem contentRatingSystem, Rating rating, boolean blocked) {
return setRatingBlockedInternal(contentRatingSystem, rating, null, blocked);
}
@@ -225,10 +218,9 @@ public class ParentalControlSettings {
/**
* Sets the blocked status of a given content sub-rating.
- * <p>
- * Note that a call to this method automatically changes the current rating level to
- * {@code TvSettings.CONTENT_RATING_LEVEL_CUSTOM} if needed.
- * </p>
+ *
+ * <p>Note that a call to this method automatically changes the current rating level to {@code
+ * TvSettings.CONTENT_RATING_LEVEL_CUSTOM} if needed.
*
* @param contentRatingSystem The content rating system where the given rating belongs.
* @param rating The content rating associated with the given sub-rating.
@@ -236,8 +228,11 @@ public class ParentalControlSettings {
* @return {@code true} if changed, {@code false} otherwise.
* @see #setRatingBlocked
*/
- public boolean setSubRatingBlocked(ContentRatingSystem contentRatingSystem, Rating rating,
- SubRating subRating, boolean blocked) {
+ public boolean setSubRatingBlocked(
+ ContentRatingSystem contentRatingSystem,
+ Rating rating,
+ SubRating subRating,
+ boolean blocked) {
return setRatingBlockedInternal(contentRatingSystem, rating, subRating, blocked);
}
@@ -249,16 +244,20 @@ public class ParentalControlSettings {
* @param subRating The content sub-rating to check.
* @return {@code true} if blocked, {@code false} otherwise.
*/
- public boolean isSubRatingEnabled(ContentRatingSystem contentRatingSystem, Rating rating,
- SubRating subRating) {
+ public boolean isSubRatingEnabled(
+ ContentRatingSystem contentRatingSystem, Rating rating, SubRating subRating) {
return mRatings.contains(toTvContentRating(contentRatingSystem, rating, subRating));
}
- private boolean setRatingBlockedInternal(ContentRatingSystem contentRatingSystem, Rating rating,
- SubRating subRating, boolean blocked) {
- TvContentRating tvContentRating = (subRating == null)
- ? toTvContentRating(contentRatingSystem, rating)
- : toTvContentRating(contentRatingSystem, rating, subRating);
+ private boolean setRatingBlockedInternal(
+ ContentRatingSystem contentRatingSystem,
+ Rating rating,
+ SubRating subRating,
+ boolean blocked) {
+ TvContentRating tvContentRating =
+ (subRating == null)
+ ? toTvContentRating(contentRatingSystem, rating)
+ : toTvContentRating(contentRatingSystem, rating, subRating);
boolean changed;
if (blocked) {
changed = mRatings.add(tvContentRating);
@@ -280,8 +279,8 @@ public class ParentalControlSettings {
}
/**
- * Returns the blocked status of a given rating. The status can be one of the followings:
- * {@link #RATING_BLOCKED}, {@link #RATING_BLOCKED_PARTIAL} and {@link #RATING_NOT_BLOCKED}
+ * Returns the blocked status of a given rating. The status can be one of the followings: {@link
+ * #RATING_BLOCKED}, {@link #RATING_BLOCKED_PARTIAL} and {@link #RATING_NOT_BLOCKED}
*/
public int getBlockedStatus(ContentRatingSystem contentRatingSystem, Rating rating) {
if (isRatingBlocked(contentRatingSystem, rating)) {
@@ -295,15 +294,18 @@ public class ParentalControlSettings {
return RATING_NOT_BLOCKED;
}
- private TvContentRating toTvContentRating(ContentRatingSystem contentRatingSystem,
- Rating rating) {
- return TvContentRating.createRating(contentRatingSystem.getDomain(),
- contentRatingSystem.getName(), rating.getName());
+ private TvContentRating toTvContentRating(
+ ContentRatingSystem contentRatingSystem, Rating rating) {
+ return TvContentRating.createRating(
+ contentRatingSystem.getDomain(), contentRatingSystem.getName(), rating.getName());
}
- private TvContentRating toTvContentRating(ContentRatingSystem contentRatingSystem,
- Rating rating, SubRating subRating) {
- return TvContentRating.createRating(contentRatingSystem.getDomain(),
- contentRatingSystem.getName(), rating.getName(), subRating.getName());
+ private TvContentRating toTvContentRating(
+ ContentRatingSystem contentRatingSystem, Rating rating, SubRating subRating) {
+ return TvContentRating.createRating(
+ contentRatingSystem.getDomain(),
+ contentRatingSystem.getName(),
+ rating.getName(),
+ subRating.getName());
}
}
diff --git a/src/com/android/tv/perf/EventNames.java b/src/com/android/tv/perf/EventNames.java
index 6026897b..54745f3b 100644
--- a/src/com/android/tv/perf/EventNames.java
+++ b/src/com/android/tv/perf/EventNames.java
@@ -16,12 +16,11 @@
package com.android.tv.perf;
-import android.support.annotation.StringDef;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+import android.support.annotation.StringDef;
import java.lang.annotation.Retention;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
/**
* Constants for performance event names.
*
@@ -47,8 +46,8 @@ public final class EventNames {
public static final String MAIN_ACTIVITY_ONSTART = "MainActivity.onStart";
public static final String MAIN_ACTIVITY_ONRESUME = "MainActivity.onResume";
/**
- * Event name for query running time of on-device search in
- * {@link com.android.tv.search.LocalSearchProvider}.
+ * Event name for query running time of on-device search in {@link
+ * com.android.tv.search.LocalSearchProvider}.
*/
public static final String ON_DEVICE_SEARCH = "OnDeviceSearch";
diff --git a/src/com/android/tv/perf/PerformanceMonitor.java b/src/com/android/tv/perf/PerformanceMonitor.java
index 40368b41..111aa851 100644
--- a/src/com/android/tv/perf/PerformanceMonitor.java
+++ b/src/com/android/tv/perf/PerformanceMonitor.java
@@ -16,10 +16,10 @@
package com.android.tv.perf;
-import android.content.Context;
-
import static com.android.tv.perf.EventNames.EventName;
+import android.content.Context;
+
/** Measures Performance. */
public interface PerformanceMonitor {
@@ -62,7 +62,6 @@ public interface PerformanceMonitor {
*/
TimerEvent startTimer();
-
/**
* Stops timer for a specific event and records the timer duration. passing a null TimerEvent
* will cause this operation to be skipped.
diff --git a/src/com/android/tv/receiver/GlobalKeyReceiver.java b/src/com/android/tv/receiver/AbstractGlobalKeyReceiver.java
index cc8e76c4..f88bd8a8 100644
--- a/src/com/android/tv/receiver/GlobalKeyReceiver.java
+++ b/src/com/android/tv/receiver/AbstractGlobalKeyReceiver.java
@@ -23,15 +23,14 @@ import android.os.AsyncTask;
import android.provider.Settings;
import android.util.Log;
import android.view.KeyEvent;
-
+import com.android.tv.Starter;
import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
-/**
- * Handles global keys.
- */
-public class GlobalKeyReceiver extends BroadcastReceiver {
+/** Handles global keys. */
+public abstract class AbstractGlobalKeyReceiver extends BroadcastReceiver {
private static final boolean DEBUG = false;
- private static final String TAG = "GlobalKeyReceiver";
+ private static final String TAG = "AbstractGlobalKeyReceiver";
private static final String ACTION_GLOBAL_BUTTON = "android.intent.action.GLOBAL_BUTTON";
// Settings.Secure.USER_SETUP_COMPLETE is hidden.
@@ -42,11 +41,11 @@ public class GlobalKeyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- if (!TvApplication.getSingletons(context).getTvInputManagerHelper().hasTvInputManager()) {
+ if (!TvSingletons.getSingletons(context).getTvInputManagerHelper().hasTvInputManager()) {
Log.wtf(TAG, "Stopping because device does not have a TvInputManager");
return;
}
- TvApplication.setCurrentRunningProcess(context, true);
+ Starter.start(context);
Context appContext = context.getApplicationContext();
if (DEBUG) Log.d(TAG, "onReceive: " + intent);
if (sUserSetupComplete) {
@@ -55,8 +54,11 @@ public class GlobalKeyReceiver extends BroadcastReceiver {
new AsyncTask<Void, Void, Boolean>() {
@Override
protected Boolean doInBackground(Void... params) {
- return Settings.Secure.getInt(appContext.getContentResolver(),
- SETTINGS_USER_SETUP_COMPLETE, 0) != 0;
+ return Settings.Secure.getInt(
+ appContext.getContentResolver(),
+ SETTINGS_USER_SETUP_COMPLETE,
+ 0)
+ != 0;
}
@Override
@@ -82,6 +84,9 @@ public class GlobalKeyReceiver extends BroadcastReceiver {
// Workaround for b/23947504, the same key event may be sent twice, filter it.
sLastEventTime = eventTime;
switch (keyCode) {
+ case KeyEvent.KEYCODE_DVR:
+ ((TvApplication) appContext).handleDvrKey();
+ break;
case KeyEvent.KEYCODE_GUIDE:
((TvApplication) appContext).handleGuideKey();
break;
diff --git a/src/com/android/tv/receiver/AudioCapabilitiesReceiver.java b/src/com/android/tv/receiver/AudioCapabilitiesReceiver.java
index 313b2dfa..3fb66245 100644
--- a/src/com/android/tv/receiver/AudioCapabilitiesReceiver.java
+++ b/src/com/android/tv/receiver/AudioCapabilitiesReceiver.java
@@ -24,17 +24,15 @@ import android.media.AudioFormat;
import android.media.AudioManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-
-import com.android.tv.ApplicationSingletons;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.analytics.Analytics;
import com.android.tv.analytics.Tracker;
-import com.android.tv.common.SharedPreferencesUtils;
+import com.android.tv.common.util.SharedPreferencesUtils;
/**
* Creates HDMI plug broadcast receiver, and reports AC3 passthrough capabilities to Google
- * Analytics and listeners. Call {@link #register} to start receiving notifications, and
- * {@link #unregister} to stop.
+ * Analytics and listeners. Call {@link #register} to start receiving notifications, and {@link
+ * #unregister} to stop.
*/
public final class AudioCapabilitiesReceiver {
private static final String SETTINGS_KEY_AC3_PASSTHRU_REPORTED = "ac3_passthrough_reported";
@@ -50,8 +48,7 @@ public final class AudioCapabilitiesReceiver {
private final Context mContext;
private final Analytics mAnalytics;
private final Tracker mTracker;
- @Nullable
- private final OnAc3PassthroughCapabilityChangeListener mListener;
+ @Nullable private final OnAc3PassthroughCapabilityChangeListener mListener;
private final BroadcastReceiver mReceiver = new HdmiAudioPlugBroadcastReceiver();
/**
@@ -60,12 +57,12 @@ public final class AudioCapabilitiesReceiver {
* @param context context for registering to receive broadcasts
* @param listener listener which receives AC3 passthrough capability change notification
*/
- public AudioCapabilitiesReceiver(@NonNull Context context,
- @Nullable OnAc3PassthroughCapabilityChangeListener listener) {
+ public AudioCapabilitiesReceiver(
+ @NonNull Context context, @Nullable OnAc3PassthroughCapabilityChangeListener listener) {
mContext = context;
- ApplicationSingletons appSingletons = TvApplication.getSingletons(context);
- mAnalytics = appSingletons.getAnalytics();
- mTracker = appSingletons.getTracker();
+ TvSingletons tvSingletons = TvSingletons.getSingletons(context);
+ mAnalytics = tvSingletons.getAnalytics();
+ mTracker = tvSingletons.getTracker();
mListener = listener;
}
@@ -121,8 +118,8 @@ public final class AudioCapabilitiesReceiver {
}
private SharedPreferences getSharedPreferences() {
- return mContext.getSharedPreferences(SharedPreferencesUtils.SHARED_PREF_AUDIO_CAPABILITIES,
- Context.MODE_PRIVATE);
+ return mContext.getSharedPreferences(
+ SharedPreferencesUtils.SHARED_PREF_AUDIO_CAPABILITIES, Context.MODE_PRIVATE);
}
private boolean getBoolean(String key, boolean def) {
@@ -141,13 +138,9 @@ public final class AudioCapabilitiesReceiver {
getSharedPreferences().edit().putInt(key, val).apply();
}
- /**
- * Listener notified when AC3 passthrough capability changes.
- */
+ /** Listener notified when AC3 passthrough capability changes. */
public interface OnAc3PassthroughCapabilityChangeListener {
- /**
- * Called when the AC3 passthrough capability changes.
- */
+ /** Called when the AC3 passthrough capability changes. */
void onAc3PassthroughCapabilityChange(boolean capability);
}
}
diff --git a/src/com/android/tv/receiver/BootCompletedReceiver.java b/src/com/android/tv/receiver/BootCompletedReceiver.java
index 369e7d54..d8528bb5 100644
--- a/src/com/android/tv/receiver/BootCompletedReceiver.java
+++ b/src/com/android/tv/receiver/BootCompletedReceiver.java
@@ -23,10 +23,10 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;
-
-import com.android.tv.Features;
+import com.android.tv.Starter;
import com.android.tv.TvActivity;
-import com.android.tv.TvApplication;
+import com.android.tv.TvFeatures;
+import com.android.tv.TvSingletons;
import com.android.tv.dvr.recorder.DvrRecordingService;
import com.android.tv.dvr.recorder.RecordingScheduler;
import com.android.tv.recommendation.ChannelPreviewUpdater;
@@ -38,11 +38,12 @@ import com.android.tv.util.SetupUtils;
* Boot completed receiver for TV app.
*
* <p>It's used to
+ *
* <ul>
- * <li>start the {@link NotificationService} for recommendation</li>
- * <li>grant permission to the TIS's </li>
- * <li>enable {@link TvActivity} if necessary</li>
- * <li>start the {@link DvrRecordingService} </li>
+ * <li>start the {@link NotificationService} for recommendation
+ * <li>grant permission to the TIS's
+ * <li>enable {@link TvActivity} if necessary
+ * <li>start the {@link DvrRecordingService}
* </ul>
*/
public class BootCompletedReceiver extends BroadcastReceiver {
@@ -51,12 +52,12 @@ public class BootCompletedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- if (!TvApplication.getSingletons(context).getTvInputManagerHelper().hasTvInputManager()) {
+ if (!TvSingletons.getSingletons(context).getTvInputManagerHelper().hasTvInputManager()) {
Log.wtf(TAG, "Stopping because device does not have a TvInputManager");
return;
}
if (DEBUG) Log.d(TAG, "boot completed " + intent);
- TvApplication.setCurrentRunningProcess(context, true);
+ Starter.start(context);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ChannelPreviewUpdater.getInstance(context).updatePreviewDataForChannelsImmediately();
@@ -69,7 +70,7 @@ public class BootCompletedReceiver extends BroadcastReceiver {
// Grant permission to already set up packages after the system has finished booting.
SetupUtils.grantEpgPermissionToSetUpPackages(context);
- if (Features.UNHIDE.isEnabled(context)) {
+ if (TvFeatures.UNHIDE.isEnabled(context)) {
if (OnboardingUtils.isFirstBoot(context)) {
// Enable the application if this is the first "unhide" feature is enabled just in
// case when the app has been disabled before.
@@ -77,14 +78,14 @@ public class BootCompletedReceiver extends BroadcastReceiver {
ComponentName name = new ComponentName(context, TvActivity.class);
if (pm.getComponentEnabledSetting(name)
!= PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
- pm.setComponentEnabledSetting(name,
- PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+ pm.setComponentEnabledSetting(
+ name, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
}
OnboardingUtils.setFirstBootCompleted(context);
}
}
- RecordingScheduler scheduler = TvApplication.getSingletons(context).getRecordingScheduler();
+ RecordingScheduler scheduler = TvSingletons.getSingletons(context).getRecordingScheduler();
if (scheduler != null) {
scheduler.updateAndStartServiceIfNeeded();
}
diff --git a/src/com/android/tv/receiver/PackageIntentsReceiver.java b/src/com/android/tv/receiver/PackageIntentsReceiver.java
index 124172f0..07f5d6be 100644
--- a/src/com/android/tv/receiver/PackageIntentsReceiver.java
+++ b/src/com/android/tv/receiver/PackageIntentsReceiver.java
@@ -21,24 +21,24 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;
-
-import com.android.tv.TvApplication;
+import com.android.tv.Starter;
+import com.android.tv.TvFeatures;
+import com.android.tv.TvSingletons;
import com.android.tv.util.Partner;
+import com.google.android.tv.partner.support.EpgContract;
-/**
- * A class for handling the broadcast intents from PackageManager.
- */
+/** A class for handling the broadcast intents from PackageManager. */
public class PackageIntentsReceiver extends BroadcastReceiver {
private static final String TAG = "PackageIntentsReceiver";
@Override
public void onReceive(Context context, Intent intent) {
- if (!TvApplication.getSingletons(context).getTvInputManagerHelper().hasTvInputManager()) {
+ if (!TvSingletons.getSingletons(context).getTvInputManagerHelper().hasTvInputManager()) {
Log.wtf(TAG, "Stopping because device does not have a TvInputManager");
return;
}
- TvApplication.setCurrentRunningProcess(context, true);
- ((TvApplication) context.getApplicationContext()).handleInputCountChanged();
+ Starter.start(context);
+ ((TvSingletons) context.getApplicationContext()).handleInputCountChanged();
Uri uri = intent.getData();
final String packageName = (uri != null ? uri.getSchemeSpecificPart() : null);
diff --git a/src/com/android/tv/recommendation/ChannelPreviewUpdater.java b/src/com/android/tv/recommendation/ChannelPreviewUpdater.java
index 2709ebe1..410b8252 100644
--- a/src/com/android/tv/recommendation/ChannelPreviewUpdater.java
+++ b/src/com/android/tv/recommendation/ChannelPreviewUpdater.java
@@ -28,16 +28,14 @@ import android.support.annotation.RequiresApi;
import android.support.media.tv.TvContractCompat;
import android.text.TextUtils;
import android.util.Log;
-
-import com.android.tv.ApplicationSingletons;
-import com.android.tv.TvApplication;
-import com.android.tv.data.Channel;
+import com.android.tv.Starter;
+import com.android.tv.TvSingletons;
import com.android.tv.data.PreviewDataManager;
import com.android.tv.data.PreviewProgramContent;
import com.android.tv.data.Program;
+import com.android.tv.data.api.Channel;
import com.android.tv.parental.ParentalControlSettings;
import com.android.tv.util.Utils;
-
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -48,23 +46,19 @@ import java.util.concurrent.TimeUnit;
@RequiresApi(Build.VERSION_CODES.O)
public class ChannelPreviewUpdater {
private static final String TAG = "ChannelPreviewUpdater";
- // STOPSHIP: set it to false.
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
private static final int UPATE_PREVIEW_PROGRAMS_JOB_ID = 1000001;
private static final long ROUTINE_INTERVAL_MS = TimeUnit.MINUTES.toMillis(10);
// The left time of a program should meet the threshold so that it could be recommended.
- private static final long RECOMMENDATION_THRESHOLD_LEFT_TIME_MS =
- TimeUnit.MINUTES.toMillis(10);
- private static final int RECOMMENDATION_THRESHOLD_PROGRESS = 90; // 90%
+ private static final long RECOMMENDATION_THRESHOLD_LEFT_TIME_MS = TimeUnit.MINUTES.toMillis(10);
+ private static final int RECOMMENDATION_THRESHOLD_PROGRESS = 90; // 90%
private static final int RECOMMENDATION_COUNT = 6;
private static final int MIN_COUNT_TO_ADD_ROW = 4;
private static ChannelPreviewUpdater sChannelPreviewUpdater;
- /**
- * Creates and returns the {@link ChannelPreviewUpdater}.
- */
+ /** Creates and returns the {@link ChannelPreviewUpdater}. */
public static ChannelPreviewUpdater getInstance(Context context) {
if (sChannelPreviewUpdater == null) {
sChannelPreviewUpdater = new ChannelPreviewUpdater(context.getApplicationContext());
@@ -82,21 +76,22 @@ public class ChannelPreviewUpdater {
private boolean mNeedUpdateAfterRecommenderReady = false;
- private Recommender.Listener mRecommenderListener = new Recommender.Listener() {
- @Override
- public void onRecommenderReady() {
- if (mNeedUpdateAfterRecommenderReady) {
- if (DEBUG) Log.d(TAG, "Recommender is ready");
- updatePreviewDataForChannelsImmediately();
- mNeedUpdateAfterRecommenderReady = false;
- }
- }
+ private Recommender.Listener mRecommenderListener =
+ new Recommender.Listener() {
+ @Override
+ public void onRecommenderReady() {
+ if (mNeedUpdateAfterRecommenderReady) {
+ if (DEBUG) Log.d(TAG, "Recommender is ready");
+ updatePreviewDataForChannelsImmediately();
+ mNeedUpdateAfterRecommenderReady = false;
+ }
+ }
- @Override
- public void onRecommendationChanged() {
- updatePreviewDataForChannelsImmediately();
- }
- };
+ @Override
+ public void onRecommendationChanged() {
+ updatePreviewDataForChannelsImmediately();
+ }
+ };
private ChannelPreviewUpdater(Context context) {
mContext = context;
@@ -104,15 +99,13 @@ public class ChannelPreviewUpdater {
mRecommender.registerEvaluator(new RandomEvaluator(), 0.1, 0.1);
mRecommender.registerEvaluator(new FavoriteChannelEvaluator(), 0.5, 0.5);
mRecommender.registerEvaluator(new RoutineWatchEvaluator(), 1.0, 1.0);
- ApplicationSingletons appSingleton = TvApplication.getSingletons(context);
- mPreviewDataManager = appSingleton.getPreviewDataManager();
- mParentalControlSettings = appSingleton.getTvInputManagerHelper()
- .getParentalControlSettings();
+ TvSingletons tvSingleton = TvSingletons.getSingletons(context);
+ mPreviewDataManager = tvSingleton.getPreviewDataManager();
+ mParentalControlSettings =
+ tvSingleton.getTvInputManagerHelper().getParentalControlSettings();
}
- /**
- * Starts the routine service for updating the preview programs.
- */
+ /** Starts the routine service for updating the preview programs. */
public void startRoutineService() {
JobScheduler jobScheduler =
(JobScheduler) mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE);
@@ -120,11 +113,13 @@ public class ChannelPreviewUpdater {
if (DEBUG) Log.d(TAG, "UPDATE_PREVIEW_JOB already exists");
return;
}
- JobInfo job = new JobInfo.Builder(UPATE_PREVIEW_PROGRAMS_JOB_ID,
- new ComponentName(mContext, ChannelPreviewUpdateService.class))
- .setPeriodic(ROUTINE_INTERVAL_MS)
- .setPersisted(true)
- .build();
+ JobInfo job =
+ new JobInfo.Builder(
+ UPATE_PREVIEW_PROGRAMS_JOB_ID,
+ new ComponentName(mContext, ChannelPreviewUpdateService.class))
+ .setPeriodic(ROUTINE_INTERVAL_MS)
+ .setPersisted(true)
+ .build();
if (jobScheduler.schedule(job) < 0) {
Log.i(TAG, "JobScheduler failed to schedule the job");
}
@@ -138,9 +133,7 @@ public class ChannelPreviewUpdater {
updatePreviewDataForChannelsImmediately();
}
- /**
- * Updates the preview programs table.
- */
+ /** Updates the preview programs table. */
public void updatePreviewDataForChannelsImmediately() {
if (!mRecommender.isReady()) {
mNeedUpdateAfterRecommenderReady = true;
@@ -148,16 +141,17 @@ public class ChannelPreviewUpdater {
}
if (!mPreviewDataManager.isLoadFinished()) {
- mPreviewDataManager.addListener(new PreviewDataManager.PreviewDataListener() {
- @Override
- public void onPreviewDataLoadFinished() {
- mPreviewDataManager.removeListener(this);
- updatePreviewDataForChannels();
- }
+ mPreviewDataManager.addListener(
+ new PreviewDataManager.PreviewDataListener() {
+ @Override
+ public void onPreviewDataLoadFinished() {
+ mPreviewDataManager.removeListener(this);
+ updatePreviewDataForChannels();
+ }
- @Override
- public void onPreviewDataUpdateFinished() { }
- });
+ @Override
+ public void onPreviewDataUpdateFinished() {}
+ });
return;
}
updatePreviewDataForChannels();
@@ -225,8 +219,9 @@ public class ChannelPreviewUpdater {
}
private void updatePreviewDataForChannelsInternal(Set<Program> programs) {
- long defaultPreviewChannelId = mPreviewDataManager.getPreviewChannelId(
- PreviewDataManager.TYPE_DEFAULT_PREVIEW_CHANNEL);
+ long defaultPreviewChannelId =
+ mPreviewDataManager.getPreviewChannelId(
+ PreviewDataManager.TYPE_DEFAULT_PREVIEW_CHANNEL);
if (defaultPreviewChannelId == PreviewDataManager.INVALID_PREVIEW_CHANNEL_ID) {
// Only create if there is enough programs
if (programs.size() > MIN_COUNT_TO_ADD_ROW) {
@@ -248,7 +243,8 @@ public class ChannelPreviewUpdater {
});
}
} else {
- updatePreviewProgramsForPreviewChannel(defaultPreviewChannelId,
+ updatePreviewProgramsForPreviewChannel(
+ defaultPreviewChannelId,
generatePreviewProgramContentsFromPrograms(defaultPreviewChannelId, programs));
}
}
@@ -266,39 +262,38 @@ public class ChannelPreviewUpdater {
return result;
}
- private void updatePreviewProgramsForPreviewChannel(long previewChannelId,
- Set<PreviewProgramContent> previewProgramContents) {
- PreviewDataManager.PreviewDataListener previewDataListener
- = new PreviewDataManager.PreviewDataListener() {
- @Override
- public void onPreviewDataLoadFinished() { }
-
- @Override
- public void onPreviewDataUpdateFinished() {
- mPreviewDataManager.removeListener(this);
- if (mJobService != null && mJobParams != null) {
- if (DEBUG) Log.d(TAG, "UpdateAsyncTask.onPostExecute with JobService");
- mJobService.jobFinished(mJobParams, false);
- mJobService = null;
- mJobParams = null;
- } else {
- if (DEBUG) Log.d(TAG, "UpdateAsyncTask.onPostExecute without JobService");
- }
- }
- };
+ private void updatePreviewProgramsForPreviewChannel(
+ long previewChannelId, Set<PreviewProgramContent> previewProgramContents) {
+ PreviewDataManager.PreviewDataListener previewDataListener =
+ new PreviewDataManager.PreviewDataListener() {
+ @Override
+ public void onPreviewDataLoadFinished() {}
+
+ @Override
+ public void onPreviewDataUpdateFinished() {
+ mPreviewDataManager.removeListener(this);
+ if (mJobService != null && mJobParams != null) {
+ if (DEBUG) Log.d(TAG, "UpdateAsyncTask.onPostExecute with JobService");
+ mJobService.jobFinished(mJobParams, false);
+ mJobService = null;
+ mJobParams = null;
+ } else {
+ if (DEBUG)
+ Log.d(TAG, "UpdateAsyncTask.onPostExecute without JobService");
+ }
+ }
+ };
mPreviewDataManager.updatePreviewProgramsForChannel(
previewChannelId, previewProgramContents, previewDataListener);
}
- /**
- * Job to execute the update of preview programs.
- */
+ /** Job to execute the update of preview programs. */
public static class ChannelPreviewUpdateService extends JobService {
private ChannelPreviewUpdater mChannelPreviewUpdater;
@Override
public void onCreate() {
- TvApplication.setCurrentRunningProcess(this, true);
+ Starter.start(this);
if (DEBUG) Log.d(TAG, "ChannelPreviewUpdateService.onCreate");
mChannelPreviewUpdater = ChannelPreviewUpdater.getInstance(this);
}
diff --git a/src/com/android/tv/recommendation/ChannelRecord.java b/src/com/android/tv/recommendation/ChannelRecord.java
index 26f0fbf0..c7a7cb37 100644
--- a/src/com/android/tv/recommendation/ChannelRecord.java
+++ b/src/com/android/tv/recommendation/ChannelRecord.java
@@ -17,14 +17,12 @@
package com.android.tv.recommendation;
import android.content.Context;
+import android.support.annotation.GuardedBy;
import android.support.annotation.VisibleForTesting;
-
-import com.android.tv.TvApplication;
-import com.android.tv.data.Channel;
+import com.android.tv.TvSingletons;
import com.android.tv.data.Program;
import com.android.tv.data.ProgramDataManager;
-import com.android.tv.util.Utils;
-
+import com.android.tv.data.api.Channel;
import java.util.ArrayDeque;
import java.util.Deque;
@@ -32,7 +30,10 @@ public class ChannelRecord {
// TODO: decide the value for max history size.
@VisibleForTesting static final int MAX_HISTORY_SIZE = 100;
private final Context mContext;
+
+ @GuardedBy("this")
private final Deque<WatchedProgram> mWatchHistory;
+
private Program mCurrentProgram;
private Channel mChannel;
private long mTotalWatchDurationMs;
@@ -62,7 +63,7 @@ public class ChannelRecord {
mInputRemoved = removed;
}
- public long getLastWatchEndTimeMs() {
+ public synchronized long getLastWatchEndTimeMs() {
WatchedProgram p = mWatchHistory.peekLast();
return (p == null) ? 0 : p.getWatchEndTimeMs();
}
@@ -71,7 +72,7 @@ public class ChannelRecord {
long time = System.currentTimeMillis();
if (mCurrentProgram == null || mCurrentProgram.getEndTimeUtcMillis() < time) {
ProgramDataManager manager =
- TvApplication.getSingletons(mContext).getProgramDataManager();
+ TvSingletons.getSingletons(mContext).getProgramDataManager();
mCurrentProgram = manager.getCurrentProgram(mChannel.getId());
}
return mCurrentProgram;
@@ -81,11 +82,11 @@ public class ChannelRecord {
return mTotalWatchDurationMs;
}
- public final WatchedProgram[] getWatchHistory() {
+ public final synchronized WatchedProgram[] getWatchHistory() {
return mWatchHistory.toArray(new WatchedProgram[mWatchHistory.size()]);
}
- public void logWatchHistory(WatchedProgram p) {
+ public synchronized void logWatchHistory(WatchedProgram p) {
mWatchHistory.offer(p);
mTotalWatchDurationMs += p.getWatchedDurationMs();
if (mWatchHistory.size() > MAX_HISTORY_SIZE) {
diff --git a/src/com/android/tv/recommendation/FavoriteChannelEvaluator.java b/src/com/android/tv/recommendation/FavoriteChannelEvaluator.java
index 9a6de7e2..8b0a3502 100644
--- a/src/com/android/tv/recommendation/FavoriteChannelEvaluator.java
+++ b/src/com/android/tv/recommendation/FavoriteChannelEvaluator.java
@@ -19,7 +19,7 @@ package com.android.tv.recommendation;
import java.util.List;
public class FavoriteChannelEvaluator extends Recommender.Evaluator {
- private static final long MIN_WATCH_PERIOD_MS = 1000 * 60 * 60 * 24; // 1 day
+ private static final long MIN_WATCH_PERIOD_MS = 1000 * 60 * 60 * 24; // 1 day
// When there is no watch history, use the current time as a default value.
private long mEarliestWatchStartTimeMs = System.currentTimeMillis();
@@ -46,7 +46,6 @@ public class FavoriteChannelEvaluator extends Recommender.Evaluator {
}
long watchPeriodMs = System.currentTimeMillis() - mEarliestWatchStartTimeMs;
- return (double) cr.getTotalWatchDurationMs() /
- Math.max(watchPeriodMs, MIN_WATCH_PERIOD_MS);
+ return (double) cr.getTotalWatchDurationMs() / Math.max(watchPeriodMs, MIN_WATCH_PERIOD_MS);
}
}
diff --git a/src/com/android/tv/recommendation/NotificationService.java b/src/com/android/tv/recommendation/NotificationService.java
index a44eca41..f40a0862 100644
--- a/src/com/android/tv/recommendation/NotificationService.java
+++ b/src/com/android/tv/recommendation/NotificationService.java
@@ -40,42 +40,39 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.SparseLongArray;
import android.view.View;
-
-import com.android.tv.ApplicationSingletons;
import com.android.tv.MainActivityWrapper.OnCurrentChannelChangeListener;
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.Starter;
+import com.android.tv.TvSingletons;
+import com.android.tv.common.CommonConstants;
import com.android.tv.common.WeakHandler;
-import com.android.tv.data.Channel;
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.data.api.Channel;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.Utils;
-
+import com.android.tv.util.images.BitmapUtils;
+import com.android.tv.util.images.BitmapUtils.ScaledBitmapInfo;
+import com.android.tv.util.images.ImageLoader;
import java.util.ArrayList;
import java.util.List;
-/**
- * A local service for notify recommendation at home launcher.
- */
-public class NotificationService extends Service implements Recommender.Listener,
- OnCurrentChannelChangeListener {
+/** A local service for notify recommendation at home launcher. */
+public class NotificationService extends Service
+ implements Recommender.Listener, OnCurrentChannelChangeListener {
private static final String TAG = "NotificationService";
private static final boolean DEBUG = false;
public static final String ACTION_SHOW_RECOMMENDATION =
- "com.android.tv.notification.ACTION_SHOW_RECOMMENDATION";
+ CommonConstants.BASE_PACKAGE + ".notification.ACTION_SHOW_RECOMMENDATION";
public static final String ACTION_HIDE_RECOMMENDATION =
- "com.android.tv.notification.ACTION_HIDE_RECOMMENDATION";
+ CommonConstants.BASE_PACKAGE + ".notification.ACTION_HIDE_RECOMMENDATION";
/**
- * Recommendation intent has an extra data for the recommendation type. It'll be also
- * sent to a TV input as a tune parameter.
+ * Recommendation intent has an extra data for the recommendation type. It'll be also sent to a
+ * TV input as a tune parameter.
*/
public static final String TUNE_PARAMS_RECOMMENDATION_TYPE =
- "com.android.tv.recommendation_type";
+ CommonConstants.BASE_PACKAGE + ".recommendation_type";
private static final String TYPE_RANDOM_RECOMMENDATION = "random";
private static final String TYPE_ROUTINE_WATCH_RECOMMENDATION = "routine_watch";
@@ -92,9 +89,9 @@ public class NotificationService extends Service implements Recommender.Listener
private static final int MSG_UPDATE_RECOMMENDATION = 1002;
private static final int MSG_HIDE_RECOMMENDATION = 1003;
- private static final long RECOMMENDATION_RETRY_TIME_MS = 5 * 60 * 1000; // 5 min
- private static final long RECOMMENDATION_THRESHOLD_LEFT_TIME_MS = 10 * 60 * 1000; // 10 min
- private static final int RECOMMENDATION_THRESHOLD_PROGRESS = 90; // 90%
+ private static final long RECOMMENDATION_RETRY_TIME_MS = 5 * 60 * 1000; // 5 min
+ private static final long RECOMMENDATION_THRESHOLD_LEFT_TIME_MS = 10 * 60 * 1000; // 10 min
+ private static final int RECOMMENDATION_THRESHOLD_PROGRESS = 90; // 90%
private static final int MAX_PROGRAM_UPDATE_COUNT = 20;
private TvInputManagerHelper mTvInputManagerHelper;
@@ -126,17 +123,17 @@ public class NotificationService extends Service implements Recommender.Listener
@Override
public void onCreate() {
if (DEBUG) Log.d(TAG, "onCreate");
- TvApplication.setCurrentRunningProcess(this, true);
+ Starter.start(this);
super.onCreate();
mCurrentNotificationCount = 0;
mNotificationChannels = new long[NOTIFICATION_COUNT];
for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
mNotificationChannels[i] = Channel.INVALID_ID;
}
- mNotificationCardMaxWidth = getResources().getDimensionPixelSize(
- R.dimen.notif_card_img_max_width);
- mNotificationCardHeight = getResources().getDimensionPixelSize(
- R.dimen.notif_card_img_height);
+ mNotificationCardMaxWidth =
+ getResources().getDimensionPixelSize(R.dimen.notif_card_img_max_width);
+ mNotificationCardHeight =
+ getResources().getDimensionPixelSize(R.dimen.notif_card_img_height);
mCardImageHeight = getResources().getDimensionPixelSize(R.dimen.notif_card_img_height);
mCardImageMaxWidth = getResources().getDimensionPixelSize(R.dimen.notif_card_img_max_width);
mCardImageMinWidth = getResources().getDimensionPixelSize(R.dimen.notif_card_img_min_width);
@@ -150,17 +147,17 @@ public class NotificationService extends Service implements Recommender.Listener
getResources().getDimensionPixelOffset(R.dimen.notif_ch_logo_padding_bottom);
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- ApplicationSingletons appSingletons = TvApplication.getSingletons(this);
- mTvInputManagerHelper = appSingletons.getTvInputManagerHelper();
+ TvSingletons tvSingletons = TvSingletons.getSingletons(this);
+ mTvInputManagerHelper = tvSingletons.getTvInputManagerHelper();
mHandlerThread = new HandlerThread("tv notification");
mHandlerThread.start();
mHandler = new NotificationHandler(mHandlerThread.getLooper(), this);
mHandler.sendEmptyMessage(MSG_INITIALIZE_RECOMMENDER);
// Just called for early initialization.
- appSingletons.getChannelDataManager();
- appSingletons.getProgramDataManager();
- appSingletons.getMainActivityWrapper().addOnCurrentChannelChangeListener(this);
+ tvSingletons.getChannelDataManager();
+ tvSingletons.getProgramDataManager();
+ tvSingletons.getMainActivityWrapper().addOnCurrentChannelChangeListener(this);
}
@UiThread
@@ -178,8 +175,8 @@ public class NotificationService extends Service implements Recommender.Listener
mRecommender.registerEvaluator(new RandomEvaluator());
} else if (TYPE_ROUTINE_WATCH_RECOMMENDATION.equals(mRecommendationType)) {
mRecommender.registerEvaluator(new RoutineWatchEvaluator());
- } else if (TYPE_ROUTINE_WATCH_AND_FAVORITE_CHANNEL_RECOMMENDATION
- .equals(mRecommendationType)) {
+ } else if (TYPE_ROUTINE_WATCH_AND_FAVORITE_CHANNEL_RECOMMENDATION.equals(
+ mRecommendationType)) {
mRecommender.registerEvaluator(new FavoriteChannelEvaluator(), 0.5, 0.5);
mRecommender.registerEvaluator(new RoutineWatchEvaluator(), 1.0, 1.0);
} else {
@@ -189,6 +186,9 @@ public class NotificationService extends Service implements Recommender.Listener
}
private void handleShowRecommendation() {
+ if (mRecommender == null) {
+ return;
+ }
if (!mRecommender.isReady()) {
mShowRecommendationAfterRecommenderReady = true;
} else {
@@ -197,13 +197,16 @@ public class NotificationService extends Service implements Recommender.Listener
}
private void handleUpdateRecommendation(int notificationId, Channel channel) {
- if (mNotificationChannels[notificationId] == Channel.INVALID_ID || !sendNotification(
- channel.getId(), notificationId)) {
+ if (mNotificationChannels[notificationId] == Channel.INVALID_ID
+ || !sendNotification(channel.getId(), notificationId)) {
changeRecommendation(notificationId);
}
}
private void handleHideRecommendation() {
+ if (mRecommender == null) {
+ return;
+ }
if (!mRecommender.isReady()) {
mShowRecommendationAfterRecommenderReady = false;
} else {
@@ -213,7 +216,8 @@ public class NotificationService extends Service implements Recommender.Listener
@Override
public void onDestroy() {
- TvApplication.getSingletons(this).getMainActivityWrapper()
+ TvSingletons.getSingletons(this)
+ .getMainActivityWrapper()
.removeOnCurrentChannelChangeListener(this);
if (mRecommender != null) {
mRecommender.release();
@@ -316,7 +320,7 @@ public class NotificationService extends Service implements Recommender.Listener
}
for (Channel c : channels) {
if (!isNotifiedChannel(c.getId())) {
- if(sendNotification(c.getId(), notificationId)) {
+ if (sendNotification(c.getId(), notificationId)) {
return;
}
}
@@ -334,13 +338,13 @@ public class NotificationService extends Service implements Recommender.Listener
}
private void hideAllRecommendation() {
- for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
- if (mNotificationChannels[i] != Channel.INVALID_ID) {
- mNotificationChannels[i] = Channel.INVALID_ID;
- mNotificationManager.cancel(NOTIFY_TAG, i);
- }
- }
- mCurrentNotificationCount = 0;
+ for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
+ if (mNotificationChannels[i] != Channel.INVALID_ID) {
+ mNotificationChannels[i] = Channel.INVALID_ID;
+ mNotificationManager.cancel(NOTIFY_TAG, i);
+ }
+ }
+ mCurrentNotificationCount = 0;
}
private boolean sendNotification(final long channelId, final int notificationId) {
@@ -350,8 +354,13 @@ public class NotificationService extends Service implements Recommender.Listener
}
final Channel channel = cr.getChannel();
if (DEBUG) {
- Log.d(TAG, "sendNotification (channelName=" + channel.getDisplayName() + " notifyId="
- + notificationId + ")");
+ Log.d(
+ TAG,
+ "sendNotification (channelName="
+ + channel.getDisplayName()
+ + " notifyId="
+ + notificationId
+ + ")");
}
// TODO: Move some checking logic into TvRecommendation.
@@ -363,17 +372,18 @@ public class NotificationService extends Service implements Recommender.Listener
if (inputInfo == null) {
return false;
}
- final String inputDisplayName = inputInfo.loadLabel(this).toString();
final Program program = Utils.getCurrentProgram(this, channel.getId());
if (program == null) {
return false;
}
- final long programDurationMs = program.getEndTimeUtcMillis()
- - program.getStartTimeUtcMillis();
+ final long programDurationMs =
+ program.getEndTimeUtcMillis() - program.getStartTimeUtcMillis();
long programLeftTimsMs = program.getEndTimeUtcMillis() - System.currentTimeMillis();
- final int programProgress = (programDurationMs <= 0) ? -1
- : 100 - (int) (programLeftTimsMs * 100 / programDurationMs);
+ final int programProgress =
+ (programDurationMs <= 0)
+ ? -1
+ : 100 - (int) (programLeftTimsMs * 100 / programDurationMs);
// We recommend those programs that meet the condition only.
if (programProgress >= RECOMMENDATION_THRESHOLD_PROGRESS
@@ -382,19 +392,24 @@ public class NotificationService extends Service implements Recommender.Listener
}
// We don't trust TIS to provide us with proper sized image
- ScaledBitmapInfo posterArtBitmapInfo = BitmapUtils.decodeSampledBitmapFromUriString(this,
- program.getPosterArtUri(), (int) mNotificationCardMaxWidth,
- (int) mNotificationCardHeight);
+ ScaledBitmapInfo posterArtBitmapInfo =
+ BitmapUtils.decodeSampledBitmapFromUriString(
+ this,
+ program.getPosterArtUri(),
+ (int) mNotificationCardMaxWidth,
+ (int) mNotificationCardHeight);
if (posterArtBitmapInfo == null) {
Log.e(TAG, "Failed to decode poster image for " + program.getPosterArtUri());
return false;
}
final Bitmap posterArtBitmap = posterArtBitmapInfo.bitmap;
- channel.loadBitmap(this, Channel.LOAD_IMAGE_TYPE_CHANNEL_LOGO, mChannelLogoMaxWidth,
+ channel.loadBitmap(
+ this,
+ Channel.LOAD_IMAGE_TYPE_CHANNEL_LOGO,
+ mChannelLogoMaxWidth,
mChannelLogoMaxHeight,
- createChannelLogoCallback(this, notificationId, inputDisplayName, channel, program,
- posterArtBitmap));
+ createChannelLogoCallback(this, notificationId, channel, program, posterArtBitmap));
if (mNotificationChannels[notificationId] == Channel.INVALID_ID) {
++mCurrentNotificationCount;
@@ -404,62 +419,77 @@ public class NotificationService extends Service implements Recommender.Listener
return true;
}
- @NonNull
- private static ImageLoader.ImageLoaderCallback<NotificationService> createChannelLogoCallback(
- NotificationService service, final int notificationId, final String inputDisplayName,
- final Channel channel, final Program program, final Bitmap posterArtBitmap) {
- return new ImageLoader.ImageLoaderCallback<NotificationService>(service) {
- @Override
- public void onBitmapLoaded(NotificationService service, Bitmap channelLogo) {
- service.sendNotification(notificationId, channelLogo, channel, posterArtBitmap,
- program, inputDisplayName);
- }
- };
- }
-
- private void sendNotification(int notificationId, Bitmap channelLogo, Channel channel,
- Bitmap posterArtBitmap, Program program, String inputDisplayName) {
- final long programDurationMs = program.getEndTimeUtcMillis() - program
- .getStartTimeUtcMillis();
+ private void sendNotification(
+ int notificationId,
+ Bitmap channelLogo,
+ Channel channel,
+ Bitmap posterArtBitmap,
+ Program program) {
+ final long programDurationMs =
+ program.getEndTimeUtcMillis() - program.getStartTimeUtcMillis();
long programLeftTimsMs = program.getEndTimeUtcMillis() - System.currentTimeMillis();
- final int programProgress = (programDurationMs <= 0) ? -1
- : 100 - (int) (programLeftTimsMs * 100 / programDurationMs);
+ final int programProgress =
+ (programDurationMs <= 0)
+ ? -1
+ : 100 - (int) (programLeftTimsMs * 100 / programDurationMs);
Intent intent = new Intent(Intent.ACTION_VIEW, channel.getUri());
intent.putExtra(TUNE_PARAMS_RECOMMENDATION_TYPE, mRecommendationType);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
final PendingIntent notificationIntent = PendingIntent.getActivity(this, 0, intent, 0);
// This callback will run on the main thread.
- Bitmap largeIconBitmap = (channelLogo == null) ? posterArtBitmap
- : overlayChannelLogo(channelLogo, posterArtBitmap);
+ Bitmap largeIconBitmap =
+ (channelLogo == null)
+ ? posterArtBitmap
+ : overlayChannelLogo(channelLogo, posterArtBitmap);
String channelDisplayName = channel.getDisplayName();
- Notification notification = new Notification.Builder(this)
- .setContentIntent(notificationIntent)
- .setContentTitle(program.getTitle())
- .setContentText(TextUtils.isEmpty(channelDisplayName) ? channel.getDisplayNumber()
- : channelDisplayName)
- .setContentInfo(channelDisplayName)
- .setAutoCancel(true).setLargeIcon(largeIconBitmap)
- .setSmallIcon(R.drawable.ic_launcher_s)
- .setCategory(Notification.CATEGORY_RECOMMENDATION)
- .setProgress((programProgress > 0) ? 100 : 0, programProgress, false)
- .setSortKey(mRecommender.getChannelSortKey(channel.getId()))
- .build();
+ Notification notification =
+ new Notification.Builder(this)
+ .setContentIntent(notificationIntent)
+ .setContentTitle(program.getTitle())
+ .setContentText(
+ TextUtils.isEmpty(channelDisplayName)
+ ? channel.getDisplayNumber()
+ : channelDisplayName)
+ .setContentInfo(channelDisplayName)
+ .setAutoCancel(true)
+ .setLargeIcon(largeIconBitmap)
+ .setSmallIcon(R.drawable.ic_launcher_s)
+ .setCategory(Notification.CATEGORY_RECOMMENDATION)
+ .setProgress((programProgress > 0) ? 100 : 0, programProgress, false)
+ .setSortKey(mRecommender.getChannelSortKey(channel.getId()))
+ .build();
notification.color = getResources().getColor(R.color.recommendation_card_background, null);
if (!TextUtils.isEmpty(program.getThumbnailUri())) {
- notification.extras
- .putString(Notification.EXTRA_BACKGROUND_IMAGE_URI, program.getThumbnailUri());
+ notification.extras.putString(
+ Notification.EXTRA_BACKGROUND_IMAGE_URI, program.getThumbnailUri());
}
mNotificationManager.notify(NOTIFY_TAG, notificationId, notification);
Message msg = mHandler.obtainMessage(MSG_UPDATE_RECOMMENDATION, notificationId, 0, channel);
mHandler.sendMessageDelayed(msg, programDurationMs / MAX_PROGRAM_UPDATE_COUNT);
}
+ @NonNull
+ private static ImageLoader.ImageLoaderCallback<NotificationService> createChannelLogoCallback(
+ NotificationService service,
+ final int notificationId,
+ final Channel channel,
+ final Program program,
+ final Bitmap posterArtBitmap) {
+ return new ImageLoader.ImageLoaderCallback<NotificationService>(service) {
+ @Override
+ public void onBitmapLoaded(NotificationService service, Bitmap channelLogo) {
+ service.sendNotification(
+ notificationId, channelLogo, channel, posterArtBitmap, program);
+ }
+ };
+ }
+
private Bitmap overlayChannelLogo(Bitmap logo, Bitmap background) {
- Bitmap result = BitmapUtils.getScaledMutableBitmap(
- background, Integer.MAX_VALUE, mCardImageHeight);
- Bitmap scaledLogo = BitmapUtils.scaleBitmap(
- logo, mChannelLogoMaxWidth, mChannelLogoMaxHeight);
+ Bitmap result =
+ BitmapUtils.getScaledMutableBitmap(background, Integer.MAX_VALUE, mCardImageHeight);
+ Bitmap scaledLogo =
+ BitmapUtils.scaleBitmap(logo, mChannelLogoMaxWidth, mChannelLogoMaxHeight);
Canvas canvas;
try {
canvas = new Canvas(result);
@@ -524,27 +554,32 @@ public class NotificationService extends Service implements Recommender.Listener
@Override
public void handleMessage(Message msg, @NonNull NotificationService notificationService) {
switch (msg.what) {
- case MSG_INITIALIZE_RECOMMENDER: {
- notificationService.handleInitializeRecommender();
- break;
- }
- case MSG_SHOW_RECOMMENDATION: {
- notificationService.handleShowRecommendation();
- break;
- }
- case MSG_UPDATE_RECOMMENDATION: {
- int notificationId = msg.arg1;
- Channel channel = ((Channel) msg.obj);
- notificationService.handleUpdateRecommendation(notificationId, channel);
- break;
- }
- case MSG_HIDE_RECOMMENDATION: {
- notificationService.handleHideRecommendation();
- break;
- }
- default: {
- super.handleMessage(msg);
- }
+ case MSG_INITIALIZE_RECOMMENDER:
+ {
+ notificationService.handleInitializeRecommender();
+ break;
+ }
+ case MSG_SHOW_RECOMMENDATION:
+ {
+ notificationService.handleShowRecommendation();
+ break;
+ }
+ case MSG_UPDATE_RECOMMENDATION:
+ {
+ int notificationId = msg.arg1;
+ Channel channel = ((Channel) msg.obj);
+ notificationService.handleUpdateRecommendation(notificationId, channel);
+ break;
+ }
+ case MSG_HIDE_RECOMMENDATION:
+ {
+ notificationService.handleHideRecommendation();
+ break;
+ }
+ default:
+ {
+ super.handleMessage(msg);
+ }
}
}
}
diff --git a/src/com/android/tv/recommendation/RecentChannelEvaluator.java b/src/com/android/tv/recommendation/RecentChannelEvaluator.java
index e724f4ce..f4c4877d 100644
--- a/src/com/android/tv/recommendation/RecentChannelEvaluator.java
+++ b/src/com/android/tv/recommendation/RecentChannelEvaluator.java
@@ -51,9 +51,12 @@ public class RecentChannelEvaluator extends Recommender.Evaluator {
if (watchDuration < WATCH_DURATION_MS_LOWER_BOUND) {
watchDurationScore = MAX_SCORE_FOR_LOWER_BOUND;
} else if (watchDuration < WATCH_DURATION_MS_UPPER_BOUND) {
- watchDurationScore = (watchDuration - WATCH_DURATION_MS_LOWER_BOUND)
- / (WATCH_DURATION_MS_UPPER_BOUND - WATCH_DURATION_MS_LOWER_BOUND)
- * (1 - MAX_SCORE_FOR_LOWER_BOUND) + MAX_SCORE_FOR_LOWER_BOUND;
+ watchDurationScore =
+ (watchDuration - WATCH_DURATION_MS_LOWER_BOUND)
+ / (WATCH_DURATION_MS_UPPER_BOUND
+ - WATCH_DURATION_MS_LOWER_BOUND)
+ * (1 - MAX_SCORE_FOR_LOWER_BOUND)
+ + MAX_SCORE_FOR_LOWER_BOUND;
} else {
watchDurationScore = 1.0;
}
@@ -61,4 +64,4 @@ public class RecentChannelEvaluator extends Recommender.Evaluator {
}
return (maxScore > 0.0) ? maxScore : NOT_RECOMMENDED;
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/recommendation/RecommendationDataManager.java b/src/com/android/tv/recommendation/RecommendationDataManager.java
index dc148ec8..649920fb 100644
--- a/src/com/android/tv/recommendation/RecommendationDataManager.java
+++ b/src/com/android/tv/recommendation/RecommendationDataManager.java
@@ -33,16 +33,14 @@ import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
-
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.common.WeakHandler;
-import com.android.tv.data.Channel;
+import com.android.tv.common.util.PermissionUtils;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.Program;
import com.android.tv.data.WatchedHistoryManager;
-import com.android.tv.util.PermissionUtils;
+import com.android.tv.data.api.Channel;
import com.android.tv.util.TvUriMatcher;
-
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -52,6 +50,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+/** Manages teh data need to make recommendations. */
public class RecommendationDataManager implements WatchedHistoryManager.Listener {
private static final int MSG_START = 1000;
private static final int MSG_STOP = 1001;
@@ -84,40 +83,38 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
private final HandlerThread mHandlerThread;
private final Handler mHandler;
private final Handler mMainHandler;
- @Nullable
- private WatchedHistoryManager mWatchedHistoryManager;
+ @Nullable private WatchedHistoryManager mWatchedHistoryManager;
private final ChannelDataManager mChannelDataManager;
private final ChannelDataManager.Listener mChannelDataListener =
new ChannelDataManager.Listener() {
- @Override
- @MainThread
- public void onLoadFinished() {
- updateChannelData();
- }
+ @Override
+ @MainThread
+ public void onLoadFinished() {
+ updateChannelData();
+ }
- @Override
- @MainThread
- public void onChannelListUpdated() {
- updateChannelData();
- }
+ @Override
+ @MainThread
+ public void onChannelListUpdated() {
+ updateChannelData();
+ }
- @Override
- @MainThread
- public void onChannelBrowsableChanged() {
- 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}.
- * The listener methods will be called in the same thread as its caller of the method.
- * Note that {@link #release(Listener)} should be called when this manager is not needed
- * any more.
+ * Gets instance of RecommendationDataManager, and adds a {@link Listener}. The listener methods
+ * will be called in the same thread as its caller of the method. Note that {@link
+ * #release(Listener)} should be called when this manager is not needed any more.
*/
- public synchronized static RecommendationDataManager acquireManager(
+ public static synchronized RecommendationDataManager acquireManager(
Context context, @NonNull Listener listener) {
if (sManager == null) {
sManager = new RecommendationDataManager(context);
@@ -129,7 +126,7 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
private final TvInputCallback mInternalCallback =
new TvInputCallback() {
@Override
- public void onInputStateChanged(String inputId, int state) { }
+ public void onInputStateChanged(String inputId, int state) {}
@Override
public void onInputAdded(String inputId) {
@@ -144,8 +141,8 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
for (ChannelRecord channelRecord : mChannelRecordMap.values()) {
if (channelRecord.getChannel().getInputId().equals(inputId)) {
channelRecord.setInputRemoved(false);
- mAvailableChannelRecordMap.put(channelRecord.getChannel().getId(),
- channelRecord);
+ mAvailableChannelRecordMap.put(
+ channelRecord.getChannel().getId(), channelRecord);
channelRecordMapChanged = true;
}
}
@@ -179,7 +176,7 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
}
@Override
- public void onInputUpdated(String inputId) { }
+ public void onInputUpdated(String inputId) {}
};
private RecommendationDataManager(Context context) {
@@ -189,48 +186,44 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
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() {
- start();
- }
- });
+ mChannelDataManager = TvSingletons.getSingletons(mContext).getChannelDataManager();
+ runOnMainThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ start();
+ }
+ });
}
/**
- * Removes the {@link Listener}, and releases RecommendationDataManager
- * if there are no listeners remained.
+ * 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();
- }
- }
- });
+ runOnMainThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ removeListener(listener);
+ if (mListeners.size() == 0) {
+ stop();
+ }
+ }
+ });
}
- /**
- * Returns a {@link ChannelRecord} corresponds to the channel ID {@code ChannelId}.
- */
+ /** Returns a {@link ChannelRecord} corresponds to the channel ID {@code ChannelId}. */
public ChannelRecord getChannelRecord(long channelId) {
return mAvailableChannelRecordMap.get(channelId);
}
- /**
- * Returns the number of channels registered in ChannelRecord map.
- */
+ /** Returns the number of channels registered in ChannelRecord map. */
public int getChannelRecordCount() {
return mAvailableChannelRecordMap.size();
}
- /**
- * Returns a Collection of ChannelRecords.
- */
+ /** Returns a Collection of ChannelRecords. */
public Collection<ChannelRecord> getChannelRecords() {
return Collections.unmodifiableCollection(mAvailableChannelRecordMap.values());
}
@@ -264,12 +257,13 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
}
private void addListener(Listener listener) {
- runOnMainThread(new Runnable() {
- @Override
- public void run() {
- mListeners.add(listener);
- }
- });
+ runOnMainThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ mListeners.add(listener);
+ }
+ });
}
@MainThread
@@ -286,10 +280,11 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
mWatchedHistoryManager.setListener(this);
mWatchedHistoryManager.start();
} else {
- mContext.getContentResolver().registerContentObserver(
- TvContract.WatchedPrograms.CONTENT_URI, true, mContentObserver);
- mHandler.obtainMessage(MSG_UPDATE_WATCH_HISTORY,
- TvContract.WatchedPrograms.CONTENT_URI)
+ mContext.getContentResolver()
+ .registerContentObserver(
+ TvContract.WatchedPrograms.CONTENT_URI, true, mContentObserver);
+ mHandler.obtainMessage(
+ MSG_UPDATE_WATCH_HISTORY, TvContract.WatchedPrograms.CONTENT_URI)
.sendToTarget();
}
mTvInputManager = (TvInputManager) mContext.getSystemService(Context.TV_INPUT_SERVICE);
@@ -333,7 +328,8 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
}
}
}
- if (isChannelRecordMapChanged && mChannelRecordMapLoaded
+ if (isChannelRecordMapChanged
+ && mChannelRecordMapLoaded
&& !mHandler.hasMessages(MSG_NOTIFY_CHANNEL_RECORD_MAP_CHANGED)) {
mHandler.sendEmptyMessage(MSG_NOTIFY_CHANNEL_RECORD_MAP_CHANGED);
}
@@ -356,14 +352,15 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
final ChannelRecord channelRecord =
updateChannelRecordFromWatchedProgram(watchedProgram);
if (mChannelRecordMapLoaded && channelRecord != null) {
- runOnMainThread(new Runnable() {
- @Override
- public void run() {
- for (Listener l : mListeners) {
- l.onNewWatchLog(channelRecord);
- }
- }
- });
+ runOnMainThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ for (Listener l : mListeners) {
+ l.onNewWatchLog(channelRecord);
+ }
+ }
+ });
}
}
if (!mChannelRecordMapLoaded) {
@@ -374,96 +371,99 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
private WatchedProgram convertFromWatchedHistoryManagerRecords(
WatchedHistoryManager.WatchedRecord watchedRecord) {
long endTime = watchedRecord.watchedStartTime + watchedRecord.duration;
- Program program = new Program.Builder()
- .setChannelId(watchedRecord.channelId)
- .setTitle("")
- .setStartTimeUtcMillis(watchedRecord.watchedStartTime)
- .setEndTimeUtcMillis(endTime)
- .build();
+ Program program =
+ new Program.Builder()
+ .setChannelId(watchedRecord.channelId)
+ .setTitle("")
+ .setStartTimeUtcMillis(watchedRecord.watchedStartTime)
+ .setEndTimeUtcMillis(endTime)
+ .build();
return new WatchedProgram(program, watchedRecord.watchedStartTime, endTime);
}
@Override
public void onLoadFinished() {
- for (WatchedHistoryManager.WatchedRecord record
- : mWatchedHistoryManager.getWatchedHistory()) {
- updateChannelRecordFromWatchedProgram(
- convertFromWatchedHistoryManagerRecords(record));
+ for (WatchedHistoryManager.WatchedRecord record :
+ mWatchedHistoryManager.getWatchedHistory()) {
+ updateChannelRecordFromWatchedProgram(convertFromWatchedHistoryManagerRecords(record));
}
mHandler.sendEmptyMessage(MSG_NOTIFY_CHANNEL_RECORD_MAP_LOADED);
}
@Override
public void onNewRecordAdded(WatchedHistoryManager.WatchedRecord watchedRecord) {
- final ChannelRecord channelRecord = updateChannelRecordFromWatchedProgram(
- convertFromWatchedHistoryManagerRecords(watchedRecord));
+ final ChannelRecord channelRecord =
+ updateChannelRecordFromWatchedProgram(
+ convertFromWatchedHistoryManagerRecords(watchedRecord));
if (mChannelRecordMapLoaded && channelRecord != null) {
- runOnMainThread(new Runnable() {
- @Override
- public void run() {
- for (Listener l : mListeners) {
- l.onNewWatchLog(channelRecord);
- }
- }
- });
+ runOnMainThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ for (Listener l : mListeners) {
+ l.onNewWatchLog(channelRecord);
+ }
+ }
+ });
}
}
private WatchedProgram createWatchedProgramFromWatchedProgramCursor(Cursor cursor) {
// Have to initiate the indexes of WatchedProgram Columns.
if (mIndexWatchChannelId == -1) {
- mIndexWatchChannelId = cursor.getColumnIndex(
- TvContract.WatchedPrograms.COLUMN_CHANNEL_ID);
- mIndexProgramTitle = cursor.getColumnIndex(
- TvContract.WatchedPrograms.COLUMN_TITLE);
- mIndexProgramStartTime = cursor.getColumnIndex(
- TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS);
- mIndexProgramEndTime = cursor.getColumnIndex(
- TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS);
- mIndexWatchStartTime = cursor.getColumnIndex(
- TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS);
- mIndexWatchEndTime = cursor.getColumnIndex(
- TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS);
+ mIndexWatchChannelId =
+ cursor.getColumnIndex(TvContract.WatchedPrograms.COLUMN_CHANNEL_ID);
+ mIndexProgramTitle = cursor.getColumnIndex(TvContract.WatchedPrograms.COLUMN_TITLE);
+ mIndexProgramStartTime =
+ cursor.getColumnIndex(TvContract.WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS);
+ mIndexProgramEndTime =
+ cursor.getColumnIndex(TvContract.WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS);
+ mIndexWatchStartTime =
+ cursor.getColumnIndex(
+ TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS);
+ mIndexWatchEndTime =
+ cursor.getColumnIndex(
+ TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS);
}
- Program program = new Program.Builder()
- .setChannelId(cursor.getLong(mIndexWatchChannelId))
- .setTitle(cursor.getString(mIndexProgramTitle))
- .setStartTimeUtcMillis(cursor.getLong(mIndexProgramStartTime))
- .setEndTimeUtcMillis(cursor.getLong(mIndexProgramEndTime))
- .build();
+ Program program =
+ new Program.Builder()
+ .setChannelId(cursor.getLong(mIndexWatchChannelId))
+ .setTitle(cursor.getString(mIndexProgramTitle))
+ .setStartTimeUtcMillis(cursor.getLong(mIndexProgramStartTime))
+ .setEndTimeUtcMillis(cursor.getLong(mIndexProgramEndTime))
+ .build();
- return new WatchedProgram(program,
- cursor.getLong(mIndexWatchStartTime),
- cursor.getLong(mIndexWatchEndTime));
+ return new WatchedProgram(
+ program, cursor.getLong(mIndexWatchStartTime), cursor.getLong(mIndexWatchEndTime));
}
private void onNotifyChannelRecordMapLoaded() {
mChannelRecordMapLoaded = true;
- runOnMainThread(new Runnable() {
- @Override
- public void run() {
- for (Listener l : mListeners) {
- l.onChannelRecordLoaded();
- }
- }
- });
+ runOnMainThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ for (Listener l : mListeners) {
+ l.onChannelRecordLoaded();
+ }
+ }
+ });
}
private void onNotifyChannelRecordMapChanged() {
- runOnMainThread(new Runnable() {
- @Override
- public void run() {
- for (Listener l : mListeners) {
- l.onChannelRecordChanged();
- }
- }
- });
+ runOnMainThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ for (Listener l : mListeners) {
+ l.onChannelRecordChanged();
+ }
+ }
+ });
}
- /**
- * Returns true if ChannelRecords are added into mChannelRecordMap or removed from it.
- */
+ /** Returns true if ChannelRecords are added into mChannelRecordMap or removed from it. */
private boolean updateChannelRecordMapFromChannel(Channel channel) {
if (!channel.isBrowsable()) {
mChannelRecordMap.remove(channel.getId());
@@ -507,8 +507,8 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
public void onChange(final boolean selfChange, final Uri uri) {
switch (TvUriMatcher.match(uri)) {
case TvUriMatcher.MATCH_WATCHED_PROGRAM_ID:
- if (!mHandler.hasMessages(MSG_UPDATE_WATCH_HISTORY,
- TvContract.WatchedPrograms.CONTENT_URI)) {
+ if (!mHandler.hasMessages(
+ MSG_UPDATE_WATCH_HISTORY, TvContract.WatchedPrograms.CONTENT_URI)) {
mHandler.obtainMessage(MSG_UPDATE_WATCH_HISTORY, uri).sendToTarget();
}
break;
@@ -524,15 +524,11 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
}
}
- /**
- * A listener interface to receive notification about the recommendation data.
- *
- * @MainThread
- */
+ /** A listener interface to receive notification about the recommendation data. @MainThread */
public interface Listener {
/**
- * Called when loading channel record map from database is finished.
- * It will be called after RecommendationDataManager.start() is finished.
+ * Called when loading channel record map from database is finished. It will be called after
+ * RecommendationDataManager.start() is finished.
*
* <p>Note that this method is called on the main thread.
*/
@@ -601,6 +597,6 @@ public class RecommendationDataManager implements WatchedHistoryManager.Listener
}
@Override
- protected void handleMessage(Message msg, @NonNull RecommendationDataManager referent) { }
+ 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 82c2893d..f350799f 100644
--- a/src/com/android/tv/recommendation/Recommender.java
+++ b/src/com/android/tv/recommendation/Recommender.java
@@ -20,9 +20,7 @@ import android.content.Context;
import android.support.annotation.VisibleForTesting;
import android.util.Log;
import android.util.Pair;
-
-import com.android.tv.data.Channel;
-
+import com.android.tv.data.api.Channel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -35,8 +33,7 @@ import java.util.concurrent.TimeUnit;
public class Recommender implements RecommendationDataManager.Listener {
private static final String TAG = "Recommender";
- @VisibleForTesting
- static final String INVALID_CHANNEL_SORT_KEY = "INVALID";
+ @VisibleForTesting static final String INVALID_CHANNEL_SORT_KEY = "INVALID";
private static final long MINIMUM_RECOMMENDATION_UPDATE_PERIOD = TimeUnit.MINUTES.toMillis(5);
private static final Comparator<Pair<Channel, Double>> mChannelScoreComparator =
new Comparator<Pair<Channel, Double>>() {
@@ -69,7 +66,9 @@ public class Recommender implements RecommendationDataManager.Listener {
}
@VisibleForTesting
- Recommender(Listener listener, boolean includeRecommendedOnly,
+ Recommender(
+ Listener listener,
+ boolean includeRecommendedOnly,
RecommendationDataManager dataManager) {
mListener = listener;
mIncludeRecommendedOnly = includeRecommendedOnly;
@@ -85,16 +84,16 @@ public class Recommender implements RecommendationDataManager.Listener {
}
public void registerEvaluator(Evaluator evaluator) {
- registerEvaluator(evaluator,
- EvaluatorWrapper.DEFAULT_BASE_SCORE, EvaluatorWrapper.DEFAULT_WEIGHT);
+ registerEvaluator(
+ evaluator, EvaluatorWrapper.DEFAULT_BASE_SCORE, EvaluatorWrapper.DEFAULT_WEIGHT);
}
/**
* Register the evaluator used in recommendation.
*
- * The range of evaluated scores by this evaluator will be between {@code baseScore} and
+ * <p>The range of evaluated scores by this evaluator will be between {@code baseScore} and
* {@code baseScore} + {@code weight} (inclusive).
-
+ *
* @param evaluator The evaluator to register inside this recommender.
* @param baseScore Base(Minimum) score of the score evaluated by {@code evaluator}.
* @param weight Weight value to rearrange the score evaluated by {@code evaluator}.
@@ -108,13 +107,13 @@ public class Recommender implements RecommendationDataManager.Listener {
}
/**
- * Return the channel list of recommendation up to {@code n} or the number of channels.
- * During the evaluation, this method updates the channel sort key of recommended channels.
+ * Return the channel list of recommendation up to {@code n} or the number of channels. During
+ * the evaluation, this method updates the channel sort key of recommended channels.
*
* @param size The number of channels that might be recommended.
- * @return Top {@code size} channels recommended sorted by score in descending order. If
- * {@code size} is bigger than the number of channels, the number of results could
- * be less than {@code size}.
+ * @return Top {@code size} channels recommended sorted by score in descending order. If {@code
+ * size} is bigger than the number of channels, the number of results could be less than
+ * {@code size}.
*/
public List<Channel> recommendChannels(int size) {
List<Pair<Channel, Double>> records = new ArrayList<>();
@@ -154,7 +153,7 @@ public class Recommender implements RecommendationDataManager.Listener {
*
* @param channelId The channel ID to retrieve the {@link Channel} object for.
* @return the {@link Channel} object for the given channel ID, {@code null} if such a channel
- * is not found.
+ * is not found.
*/
public Channel getChannel(long channelId) {
ChannelRecord record = mDataManager.getChannelRecord(channelId);
@@ -172,10 +171,10 @@ public class Recommender implements RecommendationDataManager.Listener {
}
/**
- * Returns the sort key of a given channel Id. Sort key is determined in
- * {@link #recommendChannels()} and getChannelSortKey must be called after that.
+ * Returns the sort key of a given channel Id. Sort key is determined in {@link
+ * #recommendChannels()} and getChannelSortKey must be called after that.
*
- * If getChannelSortKey was called before evaluating the channels or trying to get sort key
+ * <p>If getChannelSortKey was called before evaluating the channels or trying to get sort key
* of non-recommended channel, it returns {@link #INVALID_CHANNEL_SORT_KEY}.
*/
public String getChannelSortKey(long channelId) {
@@ -231,27 +230,25 @@ public class Recommender implements RecommendationDataManager.Listener {
mLastRecommendationUpdatedTimeUtcMillis = newUpdatedTimeMs;
}
- public static abstract class Evaluator {
+ public abstract static class Evaluator {
public static final double NOT_RECOMMENDED = -1.0;
private Recommender mRecommender;
protected Evaluator() {}
- protected void onChannelRecordListChanged(List<ChannelRecord> channelRecords) {
- }
+ protected void onChannelRecordListChanged(List<ChannelRecord> channelRecords) {}
/**
* This will be called when a new watch log comes into WatchedPrograms table.
*
* @param channelRecord The channel record corresponds to the new watch log.
*/
- protected void onNewWatchLog(ChannelRecord channelRecord) {
- }
+ protected void onNewWatchLog(ChannelRecord channelRecord) {}
/**
- * The implementation should return the recommendation score for the given channel ID.
- * The return value should be in the range of [0.0, 1.0] or NOT_RECOMMENDED for denoting
- * that it gives up to calculate the score for the channel.
+ * The implementation should return the recommendation score for the given channel ID. The
+ * return value should be in the range of [0.0, 1.0] or NOT_RECOMMENDED for denoting that it
+ * gives up to calculate the score for the channel.
*
* @param channelId The channel ID which will be evaluated by this recommender.
* @return The recommendation score
@@ -278,8 +275,8 @@ public class Recommender implements RecommendationDataManager.Listener {
// this value.
private final double mWeight;
- public EvaluatorWrapper(Recommender recommender, Evaluator evaluator,
- double baseScore, double weight) {
+ public EvaluatorWrapper(
+ Recommender recommender, Evaluator evaluator, double baseScore, double weight) {
mEvaluator = evaluator;
evaluator.setRecommender(recommender);
mBaseScore = baseScore;
@@ -287,27 +284,27 @@ public class Recommender implements RecommendationDataManager.Listener {
}
/**
- * This returns the scaled score for the given channel ID based on the returned value
- * of evaluateChannel().
+ * This returns the scaled score for the given channel ID based on the returned value of
+ * evaluateChannel().
*
* @param channelId The channel ID which will be evaluated by the recommender.
* @return Returns the scaled score (mBaseScore + score * mWeight) when evaluateChannel() is
- * in the range of [0.0, 1.0]. If evaluateChannel() returns NOT_RECOMMENDED or any
- * negative numbers, it returns NOT_RECOMMENDED. If calculateScore() returns more
- * than 1.0, it returns (mBaseScore + mWeight).
+ * in the range of [0.0, 1.0]. If evaluateChannel() returns NOT_RECOMMENDED or any
+ * negative numbers, it returns NOT_RECOMMENDED. If calculateScore() returns more than
+ * 1.0, it returns (mBaseScore + mWeight).
*/
private double getScaledEvaluatorScore(long channelId) {
double score = mEvaluator.evaluateChannel(channelId);
if (score < 0.0) {
if (score != Evaluator.NOT_RECOMMENDED) {
- Log.w(TAG, "Unexpected score (" + score + ") from the recommender"
- + mEvaluator);
+ Log.w(
+ TAG,
+ "Unexpected score (" + score + ") from the recommender" + mEvaluator);
}
// If the recommender gives up to calculate the score, return 0.0
return Evaluator.NOT_RECOMMENDED;
} else if (score > 1.0) {
- Log.w(TAG, "Unexpected score (" + score + ") from the recommender"
- + mEvaluator);
+ Log.w(TAG, "Unexpected score (" + score + ") from the recommender" + mEvaluator);
score = 1.0;
}
return mBaseScore + score * mWeight;
@@ -323,14 +320,10 @@ public class Recommender implements RecommendationDataManager.Listener {
}
public interface Listener {
- /**
- * Called after channel record map is loaded.
- */
+ /** Called after channel record map is loaded. */
void onRecommenderReady();
- /**
- * Called when the recommendation changes.
- */
+ /** Called when the recommendation changes. */
void onRecommendationChanged();
}
}
diff --git a/src/com/android/tv/recommendation/RecordedProgramPreviewUpdater.java b/src/com/android/tv/recommendation/RecordedProgramPreviewUpdater.java
index ad55afb7..edc23c53 100644
--- a/src/com/android/tv/recommendation/RecordedProgramPreviewUpdater.java
+++ b/src/com/android/tv/recommendation/RecordedProgramPreviewUpdater.java
@@ -21,39 +21,32 @@ import android.os.Build;
import android.support.annotation.RequiresApi;
import android.text.TextUtils;
import android.util.Log;
-
-import com.android.tv.ApplicationSingletons;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.data.PreviewDataManager;
import com.android.tv.data.PreviewProgramContent;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.data.RecordedProgram;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
-/**
- * Class to update the preview data for {@link RecordedProgram}
- */
+/** Class to update the preview data for {@link RecordedProgram} */
@RequiresApi(Build.VERSION_CODES.O)
+@SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated
public class RecordedProgramPreviewUpdater {
private static final String TAG = "RecordedProgramPreviewUpdater";
- // STOPSHIP: set it to false.
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
private static final int RECOMMENDATION_COUNT = 6;
private static RecordedProgramPreviewUpdater sRecordedProgramPreviewUpdater;
- /**
- * Creates and returns the {@link RecordedProgramPreviewUpdater}.
- */
+ /** Creates and returns the {@link RecordedProgramPreviewUpdater}. */
public static RecordedProgramPreviewUpdater getInstance(Context context) {
if (sRecordedProgramPreviewUpdater == null) {
- sRecordedProgramPreviewUpdater
- = new RecordedProgramPreviewUpdater(context.getApplicationContext());
+ sRecordedProgramPreviewUpdater =
+ new RecordedProgramPreviewUpdater(context.getApplicationContext());
}
return sRecordedProgramPreviewUpdater;
}
@@ -64,56 +57,56 @@ public class RecordedProgramPreviewUpdater {
private RecordedProgramPreviewUpdater(Context context) {
mContext = context.getApplicationContext();
- ApplicationSingletons applicationSingletons = TvApplication.getSingletons(mContext);
- mPreviewDataManager = applicationSingletons.getPreviewDataManager();
- mDvrDataManager = applicationSingletons.getDvrDataManager();
- mDvrDataManager.addRecordedProgramListener(new DvrDataManager.RecordedProgramListener() {
- @Override
- public void onRecordedProgramsAdded(RecordedProgram... recordedPrograms) {
- if (DEBUG) Log.d(TAG, "Add new preview recorded programs");
- updatePreviewDataForRecordedPrograms();
- }
+ TvSingletons tvSingletons = TvSingletons.getSingletons(mContext);
+ mPreviewDataManager = tvSingletons.getPreviewDataManager();
+ mDvrDataManager = tvSingletons.getDvrDataManager();
+ mDvrDataManager.addRecordedProgramListener(
+ new DvrDataManager.RecordedProgramListener() {
+ @Override
+ public void onRecordedProgramsAdded(RecordedProgram... recordedPrograms) {
+ if (DEBUG) Log.d(TAG, "Add new preview recorded programs");
+ updatePreviewDataForRecordedPrograms();
+ }
- @Override
- public void onRecordedProgramsChanged(RecordedProgram... recordedPrograms) {
- if (DEBUG) Log.d(TAG, "Update preview recorded programs");
- updatePreviewDataForRecordedPrograms();
- }
+ @Override
+ public void onRecordedProgramsChanged(RecordedProgram... recordedPrograms) {
+ if (DEBUG) Log.d(TAG, "Update preview recorded programs");
+ updatePreviewDataForRecordedPrograms();
+ }
- @Override
- public void onRecordedProgramsRemoved(RecordedProgram... recordedPrograms) {
- if (DEBUG) Log.d(TAG, "Delete preview recorded programs");
- updatePreviewDataForRecordedPrograms();
- }
- });
+ @Override
+ public void onRecordedProgramsRemoved(RecordedProgram... recordedPrograms) {
+ if (DEBUG) Log.d(TAG, "Delete preview recorded programs");
+ updatePreviewDataForRecordedPrograms();
+ }
+ });
}
- /**
- * Updates the preview data for recorded programs.
- */
+ /** Updates the preview data for recorded programs. */
public void updatePreviewDataForRecordedPrograms() {
if (!mPreviewDataManager.isLoadFinished()) {
- mPreviewDataManager.addListener(new PreviewDataManager.PreviewDataListener() {
- @Override
- public void onPreviewDataLoadFinished() {
- mPreviewDataManager.removeListener(this);
- updatePreviewDataForRecordedPrograms();
- }
+ mPreviewDataManager.addListener(
+ new PreviewDataManager.PreviewDataListener() {
+ @Override
+ public void onPreviewDataLoadFinished() {
+ mPreviewDataManager.removeListener(this);
+ updatePreviewDataForRecordedPrograms();
+ }
- @Override
- public void onPreviewDataUpdateFinished() { }
- });
+ @Override
+ public void onPreviewDataUpdateFinished() {}
+ });
return;
}
if (!mDvrDataManager.isRecordedProgramLoadFinished()) {
mDvrDataManager.addRecordedProgramLoadFinishedListener(
new DvrDataManager.OnRecordedProgramLoadFinishedListener() {
- @Override
- public void onRecordedProgramLoadFinished() {
- mDvrDataManager.removeRecordedProgramLoadFinishedListener(this);
- updatePreviewDataForRecordedPrograms();
- }
- });
+ @Override
+ public void onRecordedProgramLoadFinished() {
+ mDvrDataManager.removeRecordedProgramLoadFinishedListener(this);
+ updatePreviewDataForRecordedPrograms();
+ }
+ });
return;
}
updatePreviewDataForRecordedProgramsInternal();
@@ -121,15 +114,18 @@ public class RecordedProgramPreviewUpdater {
private void updatePreviewDataForRecordedProgramsInternal() {
Set<RecordedProgram> recordedPrograms = generateRecommendationRecordedPrograms();
- Long recordedPreviewChannelId = mPreviewDataManager.getPreviewChannelId(
- PreviewDataManager.TYPE_RECORDED_PROGRAM_PREVIEW_CHANNEL);
+ Long recordedPreviewChannelId =
+ mPreviewDataManager.getPreviewChannelId(
+ PreviewDataManager.TYPE_RECORDED_PROGRAM_PREVIEW_CHANNEL);
if (recordedPreviewChannelId == PreviewDataManager.INVALID_PREVIEW_CHANNEL_ID
&& !recordedPrograms.isEmpty()) {
createPreviewChannelForRecordedPrograms();
} else {
- mPreviewDataManager.updatePreviewProgramsForChannel(recordedPreviewChannelId,
+ mPreviewDataManager.updatePreviewProgramsForChannel(
+ recordedPreviewChannelId,
generatePreviewProgramContentsFromRecordedPrograms(
- recordedPreviewChannelId, recordedPrograms), null);
+ recordedPreviewChannelId, recordedPrograms),
+ null);
}
}
@@ -168,8 +164,9 @@ public class RecordedProgramPreviewUpdater {
long previewChannelId, Set<RecordedProgram> recordedPrograms) {
Set<PreviewProgramContent> result = new HashSet<>();
for (RecordedProgram recordedProgram : recordedPrograms) {
- result.add(PreviewProgramContent.createFromRecordedProgram(mContext, previewChannelId,
- recordedProgram));
+ result.add(
+ PreviewProgramContent.createFromRecordedProgram(
+ mContext, previewChannelId, recordedProgram));
}
return result;
}
diff --git a/src/com/android/tv/recommendation/RoutineWatchEvaluator.java b/src/com/android/tv/recommendation/RoutineWatchEvaluator.java
index 5ff7cae9..9240682a 100644
--- a/src/com/android/tv/recommendation/RoutineWatchEvaluator.java
+++ b/src/com/android/tv/recommendation/RoutineWatchEvaluator.java
@@ -19,9 +19,7 @@ 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;
-
import java.text.BreakIterator;
import java.util.ArrayList;
import java.util.Calendar;
@@ -32,8 +30,7 @@ public class RoutineWatchEvaluator extends Recommender.Evaluator {
// TODO: test and refine constant values in WatchedProgramRecommender in order to
// improve the performance of this recommender.
private static final double REQUIRED_MIN_SCORE = 0.15;
- @VisibleForTesting
- static final double MULTIPLIER_FOR_UNMATCHED_DAY_OF_WEEK = 0.7;
+ @VisibleForTesting static final double MULTIPLIER_FOR_UNMATCHED_DAY_OF_WEEK = 0.7;
private static final double TITLE_MATCH_WEIGHT = 0.5;
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);
@@ -57,8 +54,8 @@ public class RoutineWatchEvaluator extends Recommender.Evaluator {
}
Program watchedProgram = watchHistory[watchHistory.length - 1].getProgram();
- long startTimeDiffMsWithCurrentProgram = currentProgram.getStartTimeUtcMillis()
- - watchedProgram.getStartTimeUtcMillis();
+ long startTimeDiffMsWithCurrentProgram =
+ currentProgram.getStartTimeUtcMillis() - watchedProgram.getStartTimeUtcMillis();
if (startTimeDiffMsWithCurrentProgram >= MAX_DIFF_MS_FOR_OLD_PROGRAM) {
return NOT_RECOMMENDED;
}
@@ -70,42 +67,48 @@ public class RoutineWatchEvaluator extends Recommender.Evaluator {
== watchHistory[i].getProgram().getStartTimeUtcMillis()) {
watchedDurationMs += watchHistory[i].getWatchedDurationMs();
} else {
- double score = calculateRoutineWatchScore(
- currentProgram, watchedProgram, watchedDurationMs);
+ double score =
+ calculateRoutineWatchScore(
+ currentProgram, watchedProgram, watchedDurationMs);
if (score >= REQUIRED_MIN_SCORE && score > maxScore) {
maxScore = score;
}
watchedProgram = watchHistory[i].getProgram();
watchedDurationMs = watchHistory[i].getWatchedDurationMs();
- startTimeDiffMsWithCurrentProgram = currentProgram.getStartTimeUtcMillis()
- - watchedProgram.getStartTimeUtcMillis();
+ startTimeDiffMsWithCurrentProgram =
+ currentProgram.getStartTimeUtcMillis()
+ - watchedProgram.getStartTimeUtcMillis();
if (startTimeDiffMsWithCurrentProgram >= MAX_DIFF_MS_FOR_OLD_PROGRAM) {
return maxScore;
}
}
}
- double score = calculateRoutineWatchScore(
- currentProgram, watchedProgram, watchedDurationMs);
+ double score =
+ calculateRoutineWatchScore(currentProgram, watchedProgram, watchedDurationMs);
if (score >= REQUIRED_MIN_SCORE && score > maxScore) {
maxScore = score;
}
return maxScore;
}
- private static 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());
+ double titleMatchScore =
+ calculateTitleMatchScore(currentProgram.getTitle(), watchedProgram.getTitle());
double watchDurationScore = calculateWatchDurationScore(watchedProgram, watchedDurationMs);
- long diffMs = currentProgram.getStartTimeUtcMillis()
- - watchedProgram.getStartTimeUtcMillis();
- double multiplierForOldProgram = (diffMs < MAX_DIFF_MS_FOR_OLD_PROGRAM)
- ? 1.0 - (double) Math.max(diffMs - DIFF_MS_TOLERANCE_FOR_OLD_PROGRAM, 0)
- / (MAX_DIFF_MS_FOR_OLD_PROGRAM - DIFF_MS_TOLERANCE_FOR_OLD_PROGRAM)
- : 0.0;
+ long diffMs =
+ currentProgram.getStartTimeUtcMillis() - watchedProgram.getStartTimeUtcMillis();
+ double multiplierForOldProgram =
+ (diffMs < MAX_DIFF_MS_FOR_OLD_PROGRAM)
+ ? 1.0
+ - (double) Math.max(diffMs - DIFF_MS_TOLERANCE_FOR_OLD_PROGRAM, 0)
+ / (MAX_DIFF_MS_FOR_OLD_PROGRAM
+ - DIFF_MS_TOLERANCE_FOR_OLD_PROGRAM)
+ : 0.0;
return (titleMatchScore * TITLE_MATCH_WEIGHT + timeMatchScore * TIME_MATCH_WEIGHT)
- * watchDurationScore * multiplierForOldProgram;
+ * watchDurationScore
+ * multiplierForOldProgram;
}
@VisibleForTesting
@@ -118,8 +121,7 @@ public class RoutineWatchEvaluator extends Recommender.Evaluator {
if (wordList1.isEmpty() || wordList2.isEmpty()) {
return 0;
}
- int maxMatchedWordSeqLen = calculateMaximumMatchedWordSequenceLength(
- wordList1, wordList2);
+ int maxMatchedWordSeqLen = calculateMaximumMatchedWordSequenceLength(wordList1, wordList2);
// F-measure score
double precision = (double) maxMatchedWordSeqLen / wordList1.size();
@@ -128,8 +130,8 @@ public class RoutineWatchEvaluator extends Recommender.Evaluator {
}
@VisibleForTesting
- static 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) {
@@ -170,14 +172,20 @@ public class RoutineWatchEvaluator extends Recommender.Evaluator {
boolean sameDay = false;
// Handle cases like (00:00 - 02:00) - (01:00 - 03:00) or (22:00 - 25:00) - (23:00 - 26:00).
- double score = Math.max(0, Math.min(t1.endTimeOfDayInSec, t2.endTimeOfDayInSec)
- - Math.max(t1.startTimeOfDayInSec, t2.startTimeOfDayInSec));
+ double score =
+ Math.max(
+ 0,
+ Math.min(t1.endTimeOfDayInSec, t2.endTimeOfDayInSec)
+ - Math.max(t1.startTimeOfDayInSec, t2.startTimeOfDayInSec));
if (score > 0) {
sameDay = (t1.weekDay == t2.weekDay);
} else if (t1.dayChanged != t2.dayChanged) {
// To handle cases like t1 : (00:00 - 01:00) and t2 : (23:00 - 25:00).
- score = Math.max(0, Math.min(t1.endTimeOfDayInSec, t2.endTimeOfDayInSec - 24 * 60 * 60)
- - t1.startTimeOfDayInSec);
+ score =
+ Math.max(
+ 0,
+ Math.min(t1.endTimeOfDayInSec, t2.endTimeOfDayInSec - 24 * 60 * 60)
+ - t1.startTimeOfDayInSec);
// Same day if next day of t2's start day equals to t1's start day. (1 <= weekDay <= 7)
sameDay = (t1.weekDay == ((t2.weekDay % 7) + 1));
}
@@ -206,7 +214,8 @@ public class RoutineWatchEvaluator extends Recommender.Evaluator {
BreakIterator boundary = BreakIterator.getWordInstance();
boundary.setText(text);
int start = boundary.first();
- for (int end = boundary.next(); end != BreakIterator.DONE;
+ for (int end = boundary.next();
+ end != BreakIterator.DONE;
start = end, end = boundary.next()) {
String word = text.substring(start, end);
if (Character.isLetterOrDigit(word.charAt(0))) {
@@ -233,15 +242,20 @@ public class RoutineWatchEvaluator extends Recommender.Evaluator {
time.setTimeInMillis(p.getEndTimeUtcMillis());
boolean dayChanged = (weekDay != time.get(Calendar.DAY_OF_WEEK));
// Set maximum program duration time to 12 hours.
- int endTimeOfDayInSec = startTimeOfDayInSec +
- (int) Math.min(p.getEndTimeUtcMillis() - p.getStartTimeUtcMillis(),
- TimeUnit.HOURS.toMillis(12)) / 1000;
+ int endTimeOfDayInSec =
+ startTimeOfDayInSec
+ + (int)
+ Math.min(
+ p.getEndTimeUtcMillis()
+ - p.getStartTimeUtcMillis(),
+ TimeUnit.HOURS.toMillis(12))
+ / 1000;
return new ProgramTime(startTimeOfDayInSec, endTimeOfDayInSec, weekDay, dayChanged);
}
- private ProgramTime(int startTimeOfDayInSec, int endTimeOfDayInSec, int weekDay,
- boolean dayChanged) {
+ private ProgramTime(
+ int startTimeOfDayInSec, int endTimeOfDayInSec, int weekDay, boolean dayChanged) {
this.startTimeOfDayInSec = startTimeOfDayInSec;
this.endTimeOfDayInSec = endTimeOfDayInSec;
this.weekDay = weekDay;
diff --git a/src/com/android/tv/search/AutoValue_LocalSearchProvider_SearchResult.java b/src/com/android/tv/search/AutoValue_LocalSearchProvider_SearchResult.java
new file mode 100644
index 00000000..528096dd
--- /dev/null
+++ b/src/com/android/tv/search/AutoValue_LocalSearchProvider_SearchResult.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2018 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.search;
+
+import android.support.annotation.Nullable;
+
+/**
+ * Hand copy of generated Autovalue class.
+ *
+ * TODO get autovalue working
+ */
+
+final class AutoValue_LocalSearchProvider_SearchResult extends LocalSearchProvider.SearchResult {
+
+ private final long channelId;
+ private final String channelNumber;
+ private final String title;
+ private final String description;
+ private final String imageUri;
+ private final String intentAction;
+ private final String intentData;
+ private final String intentExtraData;
+ private final String contentType;
+ private final boolean isLive;
+ private final int videoWidth;
+ private final int videoHeight;
+ private final long duration;
+ private final int progressPercentage;
+
+ private AutoValue_LocalSearchProvider_SearchResult(
+ long channelId,
+ @Nullable String channelNumber,
+ @Nullable String title,
+ @Nullable String description,
+ @Nullable String imageUri,
+ @Nullable String intentAction,
+ @Nullable String intentData,
+ @Nullable String intentExtraData,
+ @Nullable String contentType,
+ boolean isLive,
+ int videoWidth,
+ int videoHeight,
+ long duration,
+ int progressPercentage) {
+ this.channelId = channelId;
+ this.channelNumber = channelNumber;
+ this.title = title;
+ this.description = description;
+ this.imageUri = imageUri;
+ this.intentAction = intentAction;
+ this.intentData = intentData;
+ this.intentExtraData = intentExtraData;
+ this.contentType = contentType;
+ this.isLive = isLive;
+ this.videoWidth = videoWidth;
+ this.videoHeight = videoHeight;
+ this.duration = duration;
+ this.progressPercentage = progressPercentage;
+ }
+
+ @Override
+ long getChannelId() {
+ return channelId;
+ }
+
+ @Nullable
+ @Override
+ String getChannelNumber() {
+ return channelNumber;
+ }
+
+ @Nullable
+ @Override
+ String getTitle() {
+ return title;
+ }
+
+ @Nullable
+ @Override
+ String getDescription() {
+ return description;
+ }
+
+ @Nullable
+ @Override
+ String getImageUri() {
+ return imageUri;
+ }
+
+ @Nullable
+ @Override
+ String getIntentAction() {
+ return intentAction;
+ }
+
+ @Nullable
+ @Override
+ String getIntentData() {
+ return intentData;
+ }
+
+ @Nullable
+ @Override
+ String getIntentExtraData() {
+ return intentExtraData;
+ }
+
+ @Nullable
+ @Override
+ String getContentType() {
+ return contentType;
+ }
+
+ @Override
+ boolean getIsLive() {
+ return isLive;
+ }
+
+ @Override
+ int getVideoWidth() {
+ return videoWidth;
+ }
+
+ @Override
+ int getVideoHeight() {
+ return videoHeight;
+ }
+
+ @Override
+ long getDuration() {
+ return duration;
+ }
+
+ @Override
+ int getProgressPercentage() {
+ return progressPercentage;
+ }
+
+ @Override
+ public String toString() {
+ return "SearchResult{"
+ + "channelId=" + channelId + ", "
+ + "channelNumber=" + channelNumber + ", "
+ + "title=" + title + ", "
+ + "description=" + description + ", "
+ + "imageUri=" + imageUri + ", "
+ + "intentAction=" + intentAction + ", "
+ + "intentData=" + intentData + ", "
+ + "intentExtraData=" + intentExtraData + ", "
+ + "contentType=" + contentType + ", "
+ + "isLive=" + isLive + ", "
+ + "videoWidth=" + videoWidth + ", "
+ + "videoHeight=" + videoHeight + ", "
+ + "duration=" + duration + ", "
+ + "progressPercentage=" + progressPercentage
+ + "}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof LocalSearchProvider.SearchResult) {
+ LocalSearchProvider.SearchResult that = (LocalSearchProvider.SearchResult) o;
+ return (this.channelId == that.getChannelId())
+ && ((this.channelNumber == null) ? (that.getChannelNumber() == null) : this.channelNumber.equals(that.getChannelNumber()))
+ && ((this.title == null) ? (that.getTitle() == null) : this.title.equals(that.getTitle()))
+ && ((this.description == null) ? (that.getDescription() == null) : this.description.equals(that.getDescription()))
+ && ((this.imageUri == null) ? (that.getImageUri() == null) : this.imageUri.equals(that.getImageUri()))
+ && ((this.intentAction == null) ? (that.getIntentAction() == null) : this.intentAction.equals(that.getIntentAction()))
+ && ((this.intentData == null) ? (that.getIntentData() == null) : this.intentData.equals(that.getIntentData()))
+ && ((this.intentExtraData == null) ? (that.getIntentExtraData() == null) : this.intentExtraData.equals(that.getIntentExtraData()))
+ && ((this.contentType == null) ? (that.getContentType() == null) : this.contentType.equals(that.getContentType()))
+ && (this.isLive == that.getIsLive())
+ && (this.videoWidth == that.getVideoWidth())
+ && (this.videoHeight == that.getVideoHeight())
+ && (this.duration == that.getDuration())
+ && (this.progressPercentage == that.getProgressPercentage());
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int h$ = 1;
+ h$ *= 1000003;
+ h$ ^= (int) ((channelId >>> 32) ^ channelId);
+ h$ *= 1000003;
+ h$ ^= (channelNumber == null) ? 0 : channelNumber.hashCode();
+ h$ *= 1000003;
+ h$ ^= (title == null) ? 0 : title.hashCode();
+ h$ *= 1000003;
+ h$ ^= (description == null) ? 0 : description.hashCode();
+ h$ *= 1000003;
+ h$ ^= (imageUri == null) ? 0 : imageUri.hashCode();
+ h$ *= 1000003;
+ h$ ^= (intentAction == null) ? 0 : intentAction.hashCode();
+ h$ *= 1000003;
+ h$ ^= (intentData == null) ? 0 : intentData.hashCode();
+ h$ *= 1000003;
+ h$ ^= (intentExtraData == null) ? 0 : intentExtraData.hashCode();
+ h$ *= 1000003;
+ h$ ^= (contentType == null) ? 0 : contentType.hashCode();
+ h$ *= 1000003;
+ h$ ^= isLive ? 1231 : 1237;
+ h$ *= 1000003;
+ h$ ^= videoWidth;
+ h$ *= 1000003;
+ h$ ^= videoHeight;
+ h$ *= 1000003;
+ h$ ^= (int) ((duration >>> 32) ^ duration);
+ h$ *= 1000003;
+ h$ ^= progressPercentage;
+ return h$;
+ }
+
+ static final class Builder extends LocalSearchProvider.SearchResult.Builder {
+ private Long channelId;
+ private String channelNumber;
+ private String title;
+ private String description;
+ private String imageUri;
+ private String intentAction;
+ private String intentData;
+ private String intentExtraData;
+ private String contentType;
+ private Boolean isLive;
+ private Integer videoWidth;
+ private Integer videoHeight;
+ private Long duration;
+ private Integer progressPercentage;
+ Builder() {
+ }
+ @Override
+ LocalSearchProvider.SearchResult.Builder setChannelId(long channelId) {
+ this.channelId = channelId;
+ return this;
+ }
+ @Override
+ LocalSearchProvider.SearchResult.Builder setChannelNumber(@Nullable String channelNumber) {
+ this.channelNumber = channelNumber;
+ return this;
+ }
+ @Override
+ LocalSearchProvider.SearchResult.Builder setTitle(@Nullable String title) {
+ this.title = title;
+ return this;
+ }
+ @Override
+ LocalSearchProvider.SearchResult.Builder setDescription(@Nullable String description) {
+ this.description = description;
+ return this;
+ }
+ @Override
+ LocalSearchProvider.SearchResult.Builder setImageUri(@Nullable String imageUri) {
+ this.imageUri = imageUri;
+ return this;
+ }
+ @Override
+ LocalSearchProvider.SearchResult.Builder setIntentAction(@Nullable String intentAction) {
+ this.intentAction = intentAction;
+ return this;
+ }
+ @Override
+ LocalSearchProvider.SearchResult.Builder setIntentData(@Nullable String intentData) {
+ this.intentData = intentData;
+ return this;
+ }
+ @Override
+ LocalSearchProvider.SearchResult.Builder setIntentExtraData(@Nullable String intentExtraData) {
+ this.intentExtraData = intentExtraData;
+ return this;
+ }
+ @Override
+ LocalSearchProvider.SearchResult.Builder setContentType(@Nullable String contentType) {
+ this.contentType = contentType;
+ return this;
+ }
+ @Override
+ LocalSearchProvider.SearchResult.Builder setIsLive(boolean isLive) {
+ this.isLive = isLive;
+ return this;
+ }
+ @Override
+ LocalSearchProvider.SearchResult.Builder setVideoWidth(int videoWidth) {
+ this.videoWidth = videoWidth;
+ return this;
+ }
+ @Override
+ LocalSearchProvider.SearchResult.Builder setVideoHeight(int videoHeight) {
+ this.videoHeight = videoHeight;
+ return this;
+ }
+ @Override
+ LocalSearchProvider.SearchResult.Builder setDuration(long duration) {
+ this.duration = duration;
+ return this;
+ }
+ @Override
+ LocalSearchProvider.SearchResult.Builder setProgressPercentage(int progressPercentage) {
+ this.progressPercentage = progressPercentage;
+ return this;
+ }
+ @Override
+ LocalSearchProvider.SearchResult build() {
+ String missing = "";
+ if (this.channelId == null) {
+ missing += " channelId";
+ }
+ if (this.isLive == null) {
+ missing += " isLive";
+ }
+ if (this.videoWidth == null) {
+ missing += " videoWidth";
+ }
+ if (this.videoHeight == null) {
+ missing += " videoHeight";
+ }
+ if (this.duration == null) {
+ missing += " duration";
+ }
+ if (this.progressPercentage == null) {
+ missing += " progressPercentage";
+ }
+ if (!missing.isEmpty()) {
+ throw new IllegalStateException("Missing required properties:" + missing);
+ }
+ return new AutoValue_LocalSearchProvider_SearchResult(
+ this.channelId,
+ this.channelNumber,
+ this.title,
+ this.description,
+ this.imageUri,
+ this.intentAction,
+ this.intentData,
+ this.intentExtraData,
+ this.contentType,
+ this.isLive,
+ this.videoWidth,
+ this.videoHeight,
+ this.duration,
+ this.progressPercentage);
+ }
+ }
+
+}
diff --git a/src/com/android/tv/search/DataManagerSearch.java b/src/com/android/tv/search/DataManagerSearch.java
index d90908f1..82fb5016 100644
--- a/src/com/android/tv/search/DataManagerSearch.java
+++ b/src/com/android/tv/search/DataManagerSearch.java
@@ -26,17 +26,14 @@ import android.os.SystemClock;
import android.support.annotation.MainThread;
import android.text.TextUtils;
import android.util.Log;
-
-import com.android.tv.ApplicationSingletons;
-import com.android.tv.TvApplication;
-import com.android.tv.data.Channel;
+import com.android.tv.TvSingletons;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.Program;
import com.android.tv.data.ProgramDataManager;
+import com.android.tv.data.api.Channel;
import com.android.tv.search.LocalSearchProvider.SearchResult;
import com.android.tv.util.MainThreadExecutor;
import com.android.tv.util.Utils;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@@ -47,11 +44,11 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/**
- * An implementation of {@link SearchInterface} to search query from {@link ChannelDataManager}
- * and {@link ProgramDataManager}.
+ * An implementation of {@link SearchInterface} to search query from {@link ChannelDataManager} and
+ * {@link ProgramDataManager}.
*/
public class DataManagerSearch implements SearchInterface {
- private static final String TAG = "TvProviderSearch";
+ private static final String TAG = "DataManagerSearch";
private static final boolean DEBUG = false;
private final Context mContext;
@@ -62,20 +59,22 @@ public class DataManagerSearch implements SearchInterface {
DataManagerSearch(Context context) {
mContext = context;
mTvInputManager = (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE);
- ApplicationSingletons appSingletons = TvApplication.getSingletons(context);
- mChannelDataManager = appSingletons.getChannelDataManager();
- mProgramDataManager = appSingletons.getProgramDataManager();
+ TvSingletons tvSingletons = TvSingletons.getSingletons(context);
+ mChannelDataManager = tvSingletons.getChannelDataManager();
+ mProgramDataManager = tvSingletons.getProgramDataManager();
}
@Override
public List<SearchResult> search(final String query, final int limit, final int action) {
- Future<List<SearchResult>> future = MainThreadExecutor.getInstance()
- .submit(new Callable<List<SearchResult>>() {
- @Override
- public List<SearchResult> call() throws Exception {
- return searchFromDataManagers(query, limit, action);
- }
- });
+ Future<List<SearchResult>> future =
+ MainThreadExecutor.getInstance()
+ .submit(
+ new Callable<List<SearchResult>>() {
+ @Override
+ public List<SearchResult> call() throws Exception {
+ return searchFromDataManagers(query, limit, action);
+ }
+ });
try {
return future.get();
@@ -90,12 +89,12 @@ public class DataManagerSearch implements SearchInterface {
@MainThread
private List<SearchResult> searchFromDataManagers(String query, int limit, int action) {
+ // TODO(b/72499165): add a test.
List<SearchResult> results = new ArrayList<>();
if (!mChannelDataManager.isDbLoadFinished()) {
return results;
}
- if (action == ACTION_TYPE_SWITCH_CHANNEL
- || action == ACTION_TYPE_SWITCH_INPUT) {
+ if (action == ACTION_TYPE_SWITCH_CHANNEL || action == ACTION_TYPE_SWITCH_INPUT) {
// Voice search query should be handled by the a system TV app.
return results;
}
@@ -114,9 +113,14 @@ public class DataManagerSearch implements SearchInterface {
}
if (results.size() >= limit) {
if (DEBUG) {
- Log.d(TAG, "Found " + results.size() + " channels. Elapsed time for" +
- " searching channels: " + (SystemClock.elapsedRealtime() - time) +
- "(msec)");
+ Log.d(
+ TAG,
+ "Found "
+ + results.size()
+ + " channels. Elapsed time for"
+ + " searching channels: "
+ + (SystemClock.elapsedRealtime() - time)
+ + "(msec)");
}
return results;
}
@@ -133,16 +137,27 @@ public class DataManagerSearch implements SearchInterface {
}
if (results.size() >= limit) {
if (DEBUG) {
- Log.d(TAG, "Found " + results.size() + " channels. Elapsed time for" +
- " searching channels: " + (SystemClock.elapsedRealtime() - time) +
- "(msec)");
+ Log.d(
+ TAG,
+ "Found "
+ + results.size()
+ + " channels. Elapsed time for"
+ + " searching channels: "
+ + (SystemClock.elapsedRealtime() - time)
+ + "(msec)");
}
return results;
}
}
if (DEBUG) {
- Log.d(TAG, "Found " + results.size() + " channels. Elapsed time for" +
- " searching channels: " + (SystemClock.elapsedRealtime() - time) + "(msec)");
+ Log.d(
+ TAG,
+ "Found "
+ + results.size()
+ + " channels. Elapsed time for"
+ + " searching channels: "
+ + (SystemClock.elapsedRealtime() - time)
+ + "(msec)");
}
int channelResult = results.size();
if (DEBUG) Log.d(TAG, "Searching programs: '" + query + "'");
@@ -161,9 +176,14 @@ public class DataManagerSearch implements SearchInterface {
}
if (results.size() >= limit) {
if (DEBUG) {
- Log.d(TAG, "Found " + (results.size() - channelResult) + " programs. Elapsed" +
- " time for searching programs: " +
- (SystemClock.elapsedRealtime() - time) + "(msec)");
+ Log.d(
+ TAG,
+ "Found "
+ + (results.size() - channelResult)
+ + " programs. Elapsed"
+ + " time for searching programs: "
+ + (SystemClock.elapsedRealtime() - time)
+ + "(msec)");
}
return results;
}
@@ -182,16 +202,27 @@ public class DataManagerSearch implements SearchInterface {
}
if (results.size() >= limit) {
if (DEBUG) {
- Log.d(TAG, "Found " + (results.size() - channelResult) + " programs. Elapsed" +
- " time for searching programs: " +
- (SystemClock.elapsedRealtime() - time) + "(msec)");
+ Log.d(
+ TAG,
+ "Found "
+ + (results.size() - channelResult)
+ + " programs. Elapsed"
+ + " time for searching programs: "
+ + (SystemClock.elapsedRealtime() - time)
+ + "(msec)");
}
return results;
}
}
if (DEBUG) {
- Log.d(TAG, "Found " + (results.size() - channelResult) + " programs. Elapsed time for" +
- " searching programs: " + (SystemClock.elapsedRealtime() - time) + "(msec)");
+ Log.d(
+ TAG,
+ "Found "
+ + (results.size() - channelResult)
+ + " programs. Elapsed time for"
+ + " searching programs: "
+ + (SystemClock.elapsedRealtime() - time)
+ + "(msec)");
}
return results;
}
@@ -201,11 +232,9 @@ public class DataManagerSearch implements SearchInterface {
return string != null && string.toLowerCase().contains(query);
}
- /**
- * If query is matched to channel, {@code program} should be null.
- */
- private void addResult(List<SearchResult> results, Set<Long> channelsFound, Channel channel,
- Program program) {
+ /** If query is matched to channel, {@code program} should be null. */
+ private void addResult(
+ List<SearchResult> results, Set<Long> channelsFound, Channel channel, Program program) {
if (program == null) {
program = mProgramDataManager.getCurrentProgram(channel.getId());
if (program != null && isRatingBlocked(program.getContentRatings())) {
@@ -213,47 +242,58 @@ public class DataManagerSearch implements SearchInterface {
}
}
- SearchResult result = new SearchResult();
+ SearchResult.Builder result = SearchResult.builder();
long channelId = channel.getId();
- result.channelId = channelId;
- result.channelNumber = channel.getDisplayNumber();
+ result.setChannelId(channelId);
+ result.setChannelNumber(channel.getDisplayNumber());
if (program == null) {
- result.title = channel.getDisplayName();
- result.description = channel.getDescription();
- result.imageUri = TvContract.buildChannelLogoUri(channelId).toString();
- result.intentAction = Intent.ACTION_VIEW;
- result.intentData = buildIntentData(channelId);
- result.contentType = Programs.CONTENT_ITEM_TYPE;
- result.isLive = true;
- result.progressPercentage = LocalSearchProvider.PROGRESS_PERCENTAGE_HIDE;
+ result.setTitle(channel.getDisplayName());
+ result.setDescription(channel.getDescription());
+ result.setImageUri(TvContract.buildChannelLogoUri(channelId).toString());
+ result.setIntentAction(Intent.ACTION_VIEW);
+ result.setIntentData(buildIntentData(channelId));
+ result.setContentType(Programs.CONTENT_ITEM_TYPE);
+ result.setIsLive(true);
+ result.setProgressPercentage(LocalSearchProvider.PROGRESS_PERCENTAGE_HIDE);
} else {
- result.title = program.getTitle();
- result.description = buildProgramDescription(channel.getDisplayNumber(),
- channel.getDisplayName(), program.getStartTimeUtcMillis(),
- program.getEndTimeUtcMillis());
- result.imageUri = program.getPosterArtUri();
- result.intentAction = Intent.ACTION_VIEW;
- result.intentData = buildIntentData(channelId);
- result.contentType = Programs.CONTENT_ITEM_TYPE;
- result.isLive = true;
- result.videoWidth = program.getVideoWidth();
- result.videoHeight = program.getVideoHeight();
- result.duration = program.getDurationMillis();
- result.progressPercentage = getProgressPercentage(
- program.getStartTimeUtcMillis(), program.getEndTimeUtcMillis());
+ result.setTitle(program.getTitle());
+ result.setDescription(
+ buildProgramDescription(
+ channel.getDisplayNumber(),
+ channel.getDisplayName(),
+ program.getStartTimeUtcMillis(),
+ program.getEndTimeUtcMillis()));
+ result.setImageUri(program.getPosterArtUri());
+ result.setIntentAction(Intent.ACTION_VIEW);
+ result.setIntentData(buildIntentData(channelId));
+ result.setIntentExtraData(TvContract.buildProgramUri(program.getId()).toString());
+ result.setContentType(Programs.CONTENT_ITEM_TYPE);
+ result.setIsLive(true);
+ result.setVideoWidth(program.getVideoWidth());
+ result.setVideoHeight(program.getVideoHeight());
+ result.setDuration(program.getDurationMillis());
+ result.setProgressPercentage(
+ getProgressPercentage(
+ program.getStartTimeUtcMillis(), program.getEndTimeUtcMillis()));
}
if (DEBUG) {
Log.d(TAG, "Add a result : channel=" + channel + " program=" + program);
}
- results.add(result);
+ results.add(result.build());
channelsFound.add(channel.getId());
}
- private String buildProgramDescription(String channelNumber, String channelName,
- long programStartUtcMillis, long programEndUtcMillis) {
+ private String buildProgramDescription(
+ String channelNumber,
+ String channelName,
+ long programStartUtcMillis,
+ long programEndUtcMillis) {
return Utils.getDurationString(mContext, programStartUtcMillis, programEndUtcMillis, false)
- + System.lineSeparator() + channelNumber + " " + channelName;
+ + System.lineSeparator()
+ + channelNumber
+ + " "
+ + channelName;
}
private int getProgressPercentage(long startUtcMillis, long endUtcMillis) {
@@ -261,7 +301,7 @@ public class DataManagerSearch implements SearchInterface {
if (startUtcMillis > current || endUtcMillis <= current) {
return LocalSearchProvider.PROGRESS_PERCENTAGE_HIDE;
}
- return (int)(100 * (current - startUtcMillis) / (endUtcMillis - startUtcMillis));
+ return (int) (100 * (current - startUtcMillis) / (endUtcMillis - startUtcMillis));
}
private String buildIntentData(long channelId) {
@@ -269,7 +309,8 @@ public class DataManagerSearch implements SearchInterface {
}
private boolean isRatingBlocked(TvContentRating[] ratings) {
- if (ratings == null || ratings.length == 0
+ if (ratings == null
+ || ratings.length == 0
|| !mTvInputManager.isParentalControlsEnabled()) {
return false;
}
diff --git a/src/com/android/tv/search/LocalSearchProvider.java b/src/com/android/tv/search/LocalSearchProvider.java
index ef9336d7..97e7f229 100644
--- a/src/com/android/tv/search/LocalSearchProvider.java
+++ b/src/com/android/tv/search/LocalSearchProvider.java
@@ -24,19 +24,19 @@ import android.database.MatrixCursor;
import android.net.Uri;
import android.os.SystemClock;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import android.util.Log;
-
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
+import com.android.tv.common.CommonConstants;
import com.android.tv.common.SoftPreconditions;
-import com.android.tv.common.TvCommonUtils;
+import com.android.tv.common.util.CommonUtils;
+import com.android.tv.common.util.PermissionUtils;
import com.android.tv.perf.EventNames;
import com.android.tv.perf.PerformanceMonitor;
import com.android.tv.perf.TimerEvent;
-import com.android.tv.util.PermissionUtils;
import com.android.tv.util.TvUriMatcher;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -46,32 +46,34 @@ public class LocalSearchProvider extends ContentProvider {
private static final boolean DEBUG = false;
/** The authority for LocalSearchProvider. */
- public static final String AUTHORITY = "com.android.tv.search";
+ public static final String AUTHORITY = CommonConstants.BASE_PACKAGE + ".search";
public static final int PROGRESS_PERCENTAGE_HIDE = -1;
// TODO: Remove this once added to the SearchManager.
private static final String SUGGEST_COLUMN_PROGRESS_BAR_PERCENTAGE = "progress_bar_percentage";
- private static final String[] SEARCHABLE_COLUMNS = new String[] {
- SearchManager.SUGGEST_COLUMN_TEXT_1,
- SearchManager.SUGGEST_COLUMN_TEXT_2,
- SearchManager.SUGGEST_COLUMN_RESULT_CARD_IMAGE,
- SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
- SearchManager.SUGGEST_COLUMN_INTENT_DATA,
- SearchManager.SUGGEST_COLUMN_CONTENT_TYPE,
- SearchManager.SUGGEST_COLUMN_IS_LIVE,
- SearchManager.SUGGEST_COLUMN_VIDEO_WIDTH,
- SearchManager.SUGGEST_COLUMN_VIDEO_HEIGHT,
- SearchManager.SUGGEST_COLUMN_DURATION,
- SUGGEST_COLUMN_PROGRESS_BAR_PERCENTAGE
- };
+ private static final String[] SEARCHABLE_COLUMNS =
+ new String[] {
+ SearchManager.SUGGEST_COLUMN_TEXT_1,
+ SearchManager.SUGGEST_COLUMN_TEXT_2,
+ SearchManager.SUGGEST_COLUMN_RESULT_CARD_IMAGE,
+ SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
+ SearchManager.SUGGEST_COLUMN_INTENT_DATA,
+ SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA,
+ SearchManager.SUGGEST_COLUMN_CONTENT_TYPE,
+ SearchManager.SUGGEST_COLUMN_IS_LIVE,
+ SearchManager.SUGGEST_COLUMN_VIDEO_WIDTH,
+ SearchManager.SUGGEST_COLUMN_VIDEO_HEIGHT,
+ SearchManager.SUGGEST_COLUMN_DURATION,
+ SUGGEST_COLUMN_PROGRESS_BAR_PERCENTAGE
+ };
private static final String EXPECTED_PATH_PREFIX = "/" + SearchManager.SUGGEST_URI_PATH_QUERY;
static final String SUGGEST_PARAMETER_ACTION = "action";
// The launcher passes 10 as a 'limit' parameter by default.
- @VisibleForTesting
- static final int DEFAULT_SEARCH_LIMIT = 10;
+ @VisibleForTesting static final int DEFAULT_SEARCH_LIMIT = 10;
+
@VisibleForTesting
static final int DEFAULT_SEARCH_ACTION = SearchInterface.ACTION_TYPE_AMBIGUOUS;
@@ -85,26 +87,41 @@ public class LocalSearchProvider extends ContentProvider {
@Override
public boolean onCreate() {
- mPerformanceMonitor = TvApplication.getSingletons(getContext()).getPerformanceMonitor();
+ mPerformanceMonitor = TvSingletons.getSingletons(getContext()).getPerformanceMonitor();
return true;
}
@VisibleForTesting
void setSearchInterface(SearchInterface searchInterface) {
- SoftPreconditions.checkState(TvCommonUtils.isRunningInTest());
+ SoftPreconditions.checkState(CommonUtils.isRunningInTest());
mSearchInterface = searchInterface;
}
@Override
- public Cursor query(@NonNull Uri uri, String[] projection, String selection,
- String[] selectionArgs, String sortOrder) {
+ public Cursor query(
+ @NonNull Uri uri,
+ String[] projection,
+ String selection,
+ String[] selectionArgs,
+ String sortOrder) {
if (TvUriMatcher.match(uri) != TvUriMatcher.MATCH_ON_DEVICE_SEARCH) {
throw new IllegalArgumentException("Unknown URI: " + uri);
}
TimerEvent queryTimer = mPerformanceMonitor.startTimer();
if (DEBUG) {
- Log.d(TAG, "query(" + uri + ", " + Arrays.toString(projection) + ", " + selection + ", "
- + Arrays.toString(selectionArgs) + ", " + sortOrder + ")");
+ Log.d(
+ TAG,
+ "query("
+ + uri
+ + ", "
+ + Arrays.toString(projection)
+ + ", "
+ + selection
+ + ", "
+ + Arrays.toString(selectionArgs)
+ + ", "
+ + sortOrder
+ + ")");
}
long time = SystemClock.elapsedRealtime();
SearchInterface search = mSearchInterface;
@@ -118,8 +135,8 @@ public class LocalSearchProvider extends ContentProvider {
}
}
String query = uri.getLastPathSegment();
- int limit = getQueryParamater(uri, SearchManager.SUGGEST_PARAMETER_LIMIT,
- DEFAULT_SEARCH_LIMIT);
+ int limit =
+ getQueryParamater(uri, SearchManager.SUGGEST_PARAMETER_LIMIT, DEFAULT_SEARCH_LIMIT);
if (limit <= 0) {
limit = DEFAULT_SEARCH_LIMIT;
}
@@ -134,8 +151,13 @@ public class LocalSearchProvider extends ContentProvider {
}
Cursor c = createSuggestionsCursor(results);
if (DEBUG) {
- Log.d(TAG, "Elapsed time(count=" + c.getCount() + "): "
- + (SystemClock.elapsedRealtime() - time) + "(msec)");
+ Log.d(
+ TAG,
+ "Elapsed time(count="
+ + c.getCount()
+ + "): "
+ + (SystemClock.elapsedRealtime() - time)
+ + "(msec)");
}
mPerformanceMonitor.stopTimer(queryTimer, EventNames.ON_DEVICE_SEARCH);
return c;
@@ -157,17 +179,18 @@ public class LocalSearchProvider extends ContentProvider {
int index = 0;
for (SearchResult result : results) {
row.clear();
- row.add(result.title);
- row.add(result.description);
- row.add(result.imageUri);
- row.add(result.intentAction);
- row.add(result.intentData);
- row.add(result.contentType);
- row.add(result.isLive ? LIVE_CONTENTS : NO_LIVE_CONTENTS);
- row.add(result.videoWidth == 0 ? null : String.valueOf(result.videoWidth));
- row.add(result.videoHeight == 0 ? null : String.valueOf(result.videoHeight));
- row.add(result.duration == 0 ? null : String.valueOf(result.duration));
- row.add(String.valueOf(result.progressPercentage));
+ row.add(result.getTitle());
+ row.add(result.getDescription());
+ row.add(result.getImageUri());
+ row.add(result.getIntentAction());
+ row.add(result.getIntentData());
+ row.add(result.getIntentExtraData());
+ row.add(result.getContentType());
+ row.add(result.getIsLive() ? LIVE_CONTENTS : NO_LIVE_CONTENTS);
+ row.add(result.getVideoWidth() == 0 ? null : String.valueOf(result.getVideoWidth()));
+ row.add(result.getVideoHeight() == 0 ? null : String.valueOf(result.getVideoHeight()));
+ row.add(result.getDuration() == 0 ? null : String.valueOf(result.getDuration()));
+ row.add(String.valueOf(result.getProgressPercentage()));
cursor.addRow(row);
if (DEBUG) Log.d(TAG, "Result[" + (++index) + "]: " + result);
}
@@ -199,40 +222,87 @@ public class LocalSearchProvider extends ContentProvider {
throw new UnsupportedOperationException();
}
- /**
- * A placeholder to a search result.
- */
- public static class SearchResult {
- public long channelId;
- public String channelNumber;
- public String title;
- public String description;
- public String imageUri;
- public String intentAction;
- public String intentData;
- public String contentType;
- public boolean isLive;
- public int videoWidth;
- public int videoHeight;
- public long duration;
- public int progressPercentage;
-
- @Override
- public String toString() {
- return "SearchResult{channelId=" + channelId +
- ", channelNumber=" + channelNumber +
- ", title=" + title +
- ", description=" + description +
- ", imageUri=" + imageUri +
- ", intentAction=" + intentAction +
- ", intentData=" + intentData +
- ", contentType=" + contentType +
- ", isLive=" + isLive +
- ", videoWidth=" + videoWidth +
- ", videoHeight=" + videoHeight +
- ", duration=" + duration +
- ", progressPercentage=" + progressPercentage +
- "}";
+ /** A placeholder to a search result. */
+ // TODO(b/72052568): Get autovalue to work in aosp master
+ public abstract static class SearchResult {
+ public static Builder builder() {
+ // primitive fields cannot be nullable. Set to default;
+ return new AutoValue_LocalSearchProvider_SearchResult.Builder()
+ .setChannelId(0)
+ .setIsLive(false)
+ .setVideoWidth(0)
+ .setVideoHeight(0)
+ .setDuration(0)
+ .setProgressPercentage(0);
+ }
+
+ // TODO(b/72052568): Get autovalue to work in aosp master
+ abstract static class Builder {
+ abstract Builder setChannelId(long value);
+
+ abstract Builder setChannelNumber(String value);
+
+ abstract Builder setTitle(String value);
+
+ abstract Builder setDescription(String value);
+
+ abstract Builder setImageUri(String value);
+
+ abstract Builder setIntentAction(String value);
+
+ abstract Builder setIntentData(String value);
+
+ abstract Builder setIntentExtraData(String value);
+
+ abstract Builder setContentType(String value);
+
+ abstract Builder setIsLive(boolean value);
+
+ abstract Builder setVideoWidth(int value);
+
+ abstract Builder setVideoHeight(int value);
+
+ abstract Builder setDuration(long value);
+
+ abstract Builder setProgressPercentage(int value);
+
+ abstract SearchResult build();
}
+
+ abstract long getChannelId();
+
+ @Nullable
+ abstract String getChannelNumber();
+
+ @Nullable
+ abstract String getTitle();
+
+ @Nullable
+ abstract String getDescription();
+
+ @Nullable
+ abstract String getImageUri();
+
+ @Nullable
+ abstract String getIntentAction();
+
+ @Nullable
+ abstract String getIntentData();
+
+ @Nullable
+ abstract String getIntentExtraData();
+
+ @Nullable
+ abstract String getContentType();
+
+ abstract boolean getIsLive();
+
+ abstract int getVideoWidth();
+
+ abstract int getVideoHeight();
+
+ abstract long getDuration();
+
+ abstract int getProgressPercentage();
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/search/ProgramGuideSearchFragment.java b/src/com/android/tv/search/ProgramGuideSearchFragment.java
index 87eec68e..cb26252b 100644
--- a/src/com/android/tv/search/ProgramGuideSearchFragment.java
+++ b/src/com/android/tv/search/ProgramGuideSearchFragment.java
@@ -38,12 +38,10 @@ import android.util.Log;
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.util.ImageLoader;
-import com.android.tv.util.PermissionUtils;
-
+import com.android.tv.common.util.PermissionUtils;
+import com.android.tv.util.images.ImageLoader;
import java.util.List;
public class ProgramGuideSearchFragment extends SearchFragment {
@@ -51,44 +49,50 @@ public class ProgramGuideSearchFragment extends SearchFragment {
private static final boolean DEBUG = false;
private static final int SEARCH_RESULT_MAX = 10;
- private final Presenter mPresenter = new Presenter() {
- @Override
- public Presenter.ViewHolder onCreateViewHolder(ViewGroup viewGroup) {
- if (DEBUG) Log.d(TAG, "onCreateViewHolder");
-
- ImageCardView cardView = new ImageCardView(mMainActivity);
- cardView.setFocusable(true);
- cardView.setFocusableInTouchMode(true);
- cardView.setMainImageAdjustViewBounds(false);
-
- Resources res = mMainActivity.getResources();
- cardView.setMainImageDimensions(
- res.getDimensionPixelSize(R.dimen.card_image_layout_width),
- res.getDimensionPixelSize(R.dimen.card_image_layout_height));
-
- return new Presenter.ViewHolder(cardView);
- }
-
- @Override
- public void onBindViewHolder(ViewHolder viewHolder, Object o) {
- ImageCardView cardView = (ImageCardView) viewHolder.view;
- LocalSearchProvider.SearchResult result = (LocalSearchProvider.SearchResult) o;
- if (DEBUG) Log.d(TAG, "onBindViewHolder result:" + result);
-
- cardView.setTitleText(result.title);
- if (!TextUtils.isEmpty(result.imageUri)) {
- ImageLoader.loadBitmap(mMainActivity, result.imageUri, mMainCardWidth,
- mMainCardHeight, createImageLoaderCallback(cardView));
- } else {
- cardView.setMainImage(mMainActivity.getDrawable(R.drawable.ic_launcher));
- }
- }
-
- @Override
- public void onUnbindViewHolder(ViewHolder viewHolder) {
- // Do nothing here.
- }
- };
+ private final Presenter mPresenter =
+ new Presenter() {
+ @Override
+ public Presenter.ViewHolder onCreateViewHolder(ViewGroup viewGroup) {
+ if (DEBUG) Log.d(TAG, "onCreateViewHolder");
+
+ ImageCardView cardView = new ImageCardView(mMainActivity);
+ cardView.setFocusable(true);
+ cardView.setFocusableInTouchMode(true);
+ cardView.setMainImageAdjustViewBounds(false);
+
+ Resources res = mMainActivity.getResources();
+ cardView.setMainImageDimensions(
+ res.getDimensionPixelSize(R.dimen.card_image_layout_width),
+ res.getDimensionPixelSize(R.dimen.card_image_layout_height));
+
+ return new Presenter.ViewHolder(cardView);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder viewHolder, Object o) {
+ ImageCardView cardView = (ImageCardView) viewHolder.view;
+ LocalSearchProvider.SearchResult result = (LocalSearchProvider.SearchResult) o;
+ if (DEBUG) Log.d(TAG, "onBindViewHolder result:" + result);
+
+ cardView.setTitleText(result.getTitle());
+ if (!TextUtils.isEmpty(result.getImageUri())) {
+ ImageLoader.loadBitmap(
+ mMainActivity,
+ result.getImageUri(),
+ mMainCardWidth,
+ mMainCardHeight,
+ createImageLoaderCallback(cardView));
+ } else {
+ cardView.setMainImage(
+ mMainActivity.getDrawable(R.drawable.ic_live_channels_96x96));
+ }
+ }
+
+ @Override
+ public void onUnbindViewHolder(ViewHolder viewHolder) {
+ // Do nothing here.
+ }
+ };
private static ImageLoader.ImageLoaderCallback<ImageCardView> createImageLoaderCallback(
ImageCardView cardView) {
@@ -101,35 +105,42 @@ public class ProgramGuideSearchFragment extends SearchFragment {
};
}
- private final SearchResultProvider mSearchResultProvider = new SearchResultProvider() {
- @Override
- public ObjectAdapter getResultsAdapter() {
- return mResultAdapter;
- }
-
- @Override
- public boolean onQueryTextChange(String query) {
- searchAndRefresh(query);
- return true;
- }
-
- @Override
- public boolean onQueryTextSubmit(String query) {
- searchAndRefresh(query);
- return true;
- }
- };
-
- private final OnItemViewClickedListener mItemClickedListener = new OnItemViewClickedListener() {
- @Override
- public void onItemClicked(Presenter.ViewHolder viewHolder, Object o, RowPresenter
- .ViewHolder viewHolder1, Row row) {
- LocalSearchProvider.SearchResult result = (LocalSearchProvider.SearchResult) o;
- mMainActivity.getFragmentManager().popBackStack();
- mMainActivity.tuneToChannel(
- mMainActivity.getChannelDataManager().getChannel(result.channelId));
- }
- };
+ private final SearchResultProvider mSearchResultProvider =
+ new SearchResultProvider() {
+ @Override
+ public ObjectAdapter getResultsAdapter() {
+ return mResultAdapter;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String query) {
+ searchAndRefresh(query);
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ searchAndRefresh(query);
+ return true;
+ }
+ };
+
+ private final OnItemViewClickedListener mItemClickedListener =
+ new OnItemViewClickedListener() {
+ @Override
+ public void onItemClicked(
+ Presenter.ViewHolder viewHolder,
+ Object o,
+ RowPresenter.ViewHolder viewHolder1,
+ Row row) {
+ LocalSearchProvider.SearchResult result = (LocalSearchProvider.SearchResult) o;
+ mMainActivity.getFragmentManager().popBackStack();
+ mMainActivity.tuneToChannel(
+ mMainActivity
+ .getChannelDataManager()
+ .getChannel(result.getChannelId()));
+ }
+ };
private final ArrayObjectAdapter mResultAdapter =
new ArrayObjectAdapter(new ListRowPresenter());
@@ -160,7 +171,7 @@ public class ProgramGuideSearchFragment extends SearchFragment {
View v = super.onCreateView(inflater, container, savedInstanceState);
v.setBackgroundResource(R.color.program_guide_scrim);
- setBadgeDrawable(mMainActivity.getDrawable(R.drawable.ic_launcher));
+ setBadgeDrawable(mMainActivity.getDrawable(R.drawable.ic_live_channels_96x96));
setSearchResultProvider(mSearchResultProvider);
setOnItemViewClickedListener(mItemClickedListener);
return v;
@@ -185,8 +196,7 @@ public class ProgramGuideSearchFragment extends SearchFragment {
mSearchTask.execute();
}
- private class SearchTask extends
- AsyncTask<Void, Void, List<LocalSearchProvider.SearchResult>> {
+ private class SearchTask extends AsyncTask<Void, Void, List<LocalSearchProvider.SearchResult>> {
private final String mQuery;
public SearchTask(String query) {
@@ -195,8 +205,8 @@ public class ProgramGuideSearchFragment extends SearchFragment {
@Override
protected List<LocalSearchProvider.SearchResult> doInBackground(Void... params) {
- return mSearch.search(mQuery, SEARCH_RESULT_MAX,
- TvProviderSearch.ACTION_TYPE_AMBIGUOUS);
+ return mSearch.search(
+ mQuery, SEARCH_RESULT_MAX, TvProviderSearch.ACTION_TYPE_AMBIGUOUS);
}
@Override
@@ -205,20 +215,23 @@ public class ProgramGuideSearchFragment extends SearchFragment {
mResultAdapter.clear();
if (DEBUG) {
- Log.d(TAG, "searchAndRefresh query=" + mQuery
- + " results=" + ((results == null) ? 0 : results.size()));
+ Log.d(
+ TAG,
+ "searchAndRefresh query="
+ + mQuery
+ + " results="
+ + ((results == null) ? 0 : results.size()));
}
if (results == null || results.size() == 0) {
HeaderItem header =
- new HeaderItem(0, mMainActivity.getString(R.string
- .search_result_no_result));
+ new HeaderItem(
+ 0, mMainActivity.getString(R.string.search_result_no_result));
ArrayObjectAdapter resultsAdapter = new ArrayObjectAdapter(mPresenter);
mResultAdapter.add(new ListRow(header, resultsAdapter));
} else {
HeaderItem header =
- new HeaderItem(0, mMainActivity.getString(R.string
- .search_result_title));
+ new HeaderItem(0, mMainActivity.getString(R.string.search_result_title));
ArrayObjectAdapter resultsAdapter = new ArrayObjectAdapter(mPresenter);
resultsAdapter.addAll(0, results);
mResultAdapter.add(new ListRow(header, resultsAdapter));
diff --git a/src/com/android/tv/search/SearchInterface.java b/src/com/android/tv/search/SearchInterface.java
index d631972a..4866ee84 100644
--- a/src/com/android/tv/search/SearchInterface.java
+++ b/src/com/android/tv/search/SearchInterface.java
@@ -17,12 +17,9 @@
package com.android.tv.search;
import com.android.tv.search.LocalSearchProvider.SearchResult;
-
import java.util.List;
-/**
- * Interface for channel and program search.
- */
+/** Interface for channel and program search. */
public interface SearchInterface {
int ACTION_TYPE_START = 1;
int ACTION_TYPE_AMBIGUOUS = 1;
@@ -31,11 +28,11 @@ public interface SearchInterface {
int ACTION_TYPE_END = 3;
/**
- * Search channels, inputs, or programs.
- * This assumes that parental control settings will not be change while searching.
+ * Search channels, inputs, or programs. This assumes that parental control settings will not be
+ * change while searching.
*
* @param action One of {@link #ACTION_TYPE_SWITCH_CHANNEL}, {@link #ACTION_TYPE_SWITCH_INPUT},
- * or {@link #ACTION_TYPE_AMBIGUOUS},
+ * or {@link #ACTION_TYPE_AMBIGUOUS},
*/
List<SearchResult> search(String query, int limit, int action);
}
diff --git a/src/com/android/tv/search/TvProviderSearch.java b/src/com/android/tv/search/TvProviderSearch.java
index e7d8a02d..92197f2d 100644
--- a/src/com/android/tv/search/TvProviderSearch.java
+++ b/src/com/android/tv/search/TvProviderSearch.java
@@ -32,12 +32,10 @@ import android.os.SystemClock;
import android.support.annotation.WorkerThread;
import android.text.TextUtils;
import android.util.Log;
-
import com.android.tv.common.TvContentRatingCache;
+import com.android.tv.common.util.PermissionUtils;
import com.android.tv.search.LocalSearchProvider.SearchResult;
-import com.android.tv.util.PermissionUtils;
import com.android.tv.util.Utils;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -48,14 +46,15 @@ import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
-/**
- * An implementation of {@link SearchInterface} to search query from TvProvider directly.
- */
+/** An implementation of {@link SearchInterface} to search query from TvProvider directly. */
public class TvProviderSearch implements SearchInterface {
private static final String TAG = "TvProviderSearch";
private static final boolean DEBUG = false;
+ private static final long SEARCH_TIME_FRAME_MS = TimeUnit.DAYS.toMillis(14);
+
private static final int NO_LIMIT = 0;
private final Context mContext;
@@ -70,15 +69,16 @@ public class TvProviderSearch implements SearchInterface {
}
/**
- * Search channels, inputs, or programs from TvProvider.
- * This assumes that parental control settings will not be change while searching.
+ * Search channels, inputs, or programs from TvProvider. This assumes that parental control
+ * settings will not be change while searching.
*
* @param action One of {@link #ACTION_TYPE_SWITCH_CHANNEL}, {@link #ACTION_TYPE_SWITCH_INPUT},
- * or {@link #ACTION_TYPE_AMBIGUOUS},
+ * or {@link #ACTION_TYPE_AMBIGUOUS},
*/
@Override
@WorkerThread
public List<SearchResult> search(String query, int limit, int action) {
+ // TODO(b/72499463): add a test.
List<SearchResult> results = new ArrayList<>();
if (!PermissionUtils.hasAccessAllEpg(mContext)) {
// TODO: support this feature for non-system LC app. b/23939816
@@ -107,15 +107,19 @@ public class TvProviderSearch implements SearchInterface {
// Lastly, search programs.
limit -= results.size();
- results.addAll(searchPrograms(query, null, new String[] {
- Programs.COLUMN_TITLE, Programs.COLUMN_SHORT_DESCRIPTION },
- channelsFound, limit));
+ results.addAll(
+ searchPrograms(
+ query,
+ null,
+ new String[] {Programs.COLUMN_TITLE, Programs.COLUMN_SHORT_DESCRIPTION},
+ channelsFound,
+ limit));
}
return results;
}
- private void appendSelectionString(StringBuilder sb, String[] columnForExactMatching,
- String[] columnForPartialMatching) {
+ private void appendSelectionString(
+ StringBuilder sb, String[] columnForExactMatching, String[] columnForPartialMatching) {
boolean firstColumn = true;
if (columnForExactMatching != null) {
for (String column : columnForExactMatching) {
@@ -139,8 +143,12 @@ public class TvProviderSearch implements SearchInterface {
}
}
- private void insertSelectionArgumentStrings(String[] selectionArgs, int pos,
- String query, String[] columnForExactMatching, String[] columnForPartialMatching) {
+ private void insertSelectionArgumentStrings(
+ String[] selectionArgs,
+ int pos,
+ String query,
+ String[] columnForExactMatching,
+ String[] columnForPartialMatching) {
if (columnForExactMatching != null) {
int until = pos + columnForExactMatching.length;
for (; pos < until; ++pos) {
@@ -162,43 +170,66 @@ public class TvProviderSearch implements SearchInterface {
long time = SystemClock.elapsedRealtime();
List<SearchResult> results = new ArrayList<>();
if (TextUtils.isDigitsOnly(query)) {
- results.addAll(searchChannels(query, new String[] { Channels.COLUMN_DISPLAY_NUMBER },
- null, channels, NO_LIMIT));
+ results.addAll(
+ searchChannels(
+ query,
+ new String[] {Channels.COLUMN_DISPLAY_NUMBER},
+ null,
+ channels,
+ NO_LIMIT));
if (results.size() > 1) {
Collections.sort(results, new ChannelComparatorWithSameDisplayNumber());
}
}
if (results.size() < limit) {
- results.addAll(searchChannels(query, null,
- new String[] { Channels.COLUMN_DISPLAY_NAME, Channels.COLUMN_DESCRIPTION },
- channels, limit - results.size()));
+ results.addAll(
+ searchChannels(
+ query,
+ null,
+ new String[] {
+ Channels.COLUMN_DISPLAY_NAME, Channels.COLUMN_DESCRIPTION
+ },
+ channels,
+ limit - results.size()));
}
if (results.size() > limit) {
results = results.subList(0, limit);
}
- for (SearchResult result : results) {
- fillProgramInfo(result);
+ for (int i = 0; i < results.size(); i++) {
+ results.set(i, fillProgramInfo(results.get(i)));
}
if (DEBUG) {
- Log.d(TAG, "Found " + results.size() + " channels. Elapsed time for searching" +
- " channels: " + (SystemClock.elapsedRealtime() - time) + "(msec)");
+ Log.d(
+ TAG,
+ "Found "
+ + results.size()
+ + " channels. Elapsed time for searching"
+ + " channels: "
+ + (SystemClock.elapsedRealtime() - time)
+ + "(msec)");
}
return results;
}
@WorkerThread
- private List<SearchResult> searchChannels(String query, String[] columnForExactMatching,
- String[] columnForPartialMatching, Set<Long> channelsFound, int limit) {
+ private List<SearchResult> searchChannels(
+ String query,
+ String[] columnForExactMatching,
+ String[] columnForPartialMatching,
+ Set<Long> channelsFound,
+ int limit) {
String[] projection = {
- Channels._ID,
- Channels.COLUMN_DISPLAY_NUMBER,
- Channels.COLUMN_DISPLAY_NAME,
- Channels.COLUMN_DESCRIPTION
+ Channels._ID,
+ Channels.COLUMN_DISPLAY_NUMBER,
+ Channels.COLUMN_DISPLAY_NAME,
+ Channels.COLUMN_DESCRIPTION
};
StringBuilder sb = new StringBuilder();
- sb.append(Channels.COLUMN_BROWSABLE).append("=1 AND ")
- .append(Channels.COLUMN_SEARCHABLE).append("=1");
+ sb.append(Channels.COLUMN_BROWSABLE)
+ .append("=1 AND ")
+ .append(Channels.COLUMN_SEARCHABLE)
+ .append("=1");
if (mTvInputManager.isParentalControlsEnabled()) {
sb.append(" AND ").append(Channels.COLUMN_LOCKED).append("=0");
}
@@ -207,16 +238,18 @@ public class TvProviderSearch implements SearchInterface {
sb.append(")");
String selection = sb.toString();
- int len = (columnForExactMatching == null ? 0 : columnForExactMatching.length) +
- (columnForPartialMatching == null ? 0 : columnForPartialMatching.length);
+ int len =
+ (columnForExactMatching == null ? 0 : columnForExactMatching.length)
+ + (columnForPartialMatching == null ? 0 : columnForPartialMatching.length);
String[] selectionArgs = new String[len];
- insertSelectionArgumentStrings(selectionArgs, 0, query, columnForExactMatching,
- columnForPartialMatching);
+ insertSelectionArgumentStrings(
+ selectionArgs, 0, query, columnForExactMatching, columnForPartialMatching);
List<SearchResult> searchResults = new ArrayList<>();
- try (Cursor c = mContentResolver.query(Channels.CONTENT_URI, projection, selection,
- selectionArgs, null)) {
+ try (Cursor c =
+ mContentResolver.query(
+ Channels.CONTENT_URI, projection, selection, selectionArgs, null)) {
if (c != null) {
int count = 0;
while (c.moveToNext()) {
@@ -227,19 +260,19 @@ public class TvProviderSearch implements SearchInterface {
}
channelsFound.add(id);
- SearchResult result = new SearchResult();
- result.channelId = id;
- result.channelNumber = c.getString(1);
- result.title = c.getString(2);
- result.description = c.getString(3);
- result.imageUri = TvContract.buildChannelLogoUri(result.channelId).toString();
- result.intentAction = Intent.ACTION_VIEW;
- result.intentData = buildIntentData(result.channelId);
- result.contentType = Programs.CONTENT_ITEM_TYPE;
- result.isLive = true;
- result.progressPercentage = LocalSearchProvider.PROGRESS_PERCENTAGE_HIDE;
+ SearchResult.Builder result = SearchResult.builder();
+ result.setChannelId(id);
+ result.setChannelNumber(c.getString(1));
+ result.setTitle(c.getString(2));
+ result.setDescription(c.getString(3));
+ result.setImageUri(TvContract.buildChannelLogoUri(id).toString());
+ result.setIntentAction(Intent.ACTION_VIEW);
+ result.setIntentData(buildIntentData(id));
+ result.setContentType(Programs.CONTENT_ITEM_TYPE);
+ result.setIsLive(true);
+ result.setProgressPercentage(LocalSearchProvider.PROGRESS_PERCENTAGE_HIDE);
- searchResults.add(result);
+ searchResults.add(result.build());
if (limit != NO_LIMIT && ++count >= limit) {
break;
@@ -256,43 +289,55 @@ public class TvProviderSearch implements SearchInterface {
* blocked.
*/
@WorkerThread
- private void fillProgramInfo(SearchResult result) {
+ private SearchResult fillProgramInfo(SearchResult result) {
long now = System.currentTimeMillis();
- Uri uri = TvContract.buildProgramsUriForChannel(result.channelId, now, now);
- String[] projection = new String[] {
- Programs.COLUMN_TITLE,
- Programs.COLUMN_POSTER_ART_URI,
- Programs.COLUMN_CONTENT_RATING,
- Programs.COLUMN_VIDEO_WIDTH,
- Programs.COLUMN_VIDEO_HEIGHT,
- Programs.COLUMN_START_TIME_UTC_MILLIS,
- Programs.COLUMN_END_TIME_UTC_MILLIS
- };
+ Uri uri = TvContract.buildProgramsUriForChannel(result.getChannelId(), now, now);
+ String[] projection =
+ new String[] {
+ Programs.COLUMN_TITLE,
+ Programs.COLUMN_POSTER_ART_URI,
+ Programs.COLUMN_CONTENT_RATING,
+ Programs.COLUMN_VIDEO_WIDTH,
+ Programs.COLUMN_VIDEO_HEIGHT,
+ Programs.COLUMN_START_TIME_UTC_MILLIS,
+ Programs.COLUMN_END_TIME_UTC_MILLIS
+ };
try (Cursor c = mContentResolver.query(uri, projection, null, null, null)) {
if (c != null && c.moveToNext() && !isRatingBlocked(c.getString(2))) {
- String channelName = result.title;
+ String channelName = result.getTitle();
+ String channelNumber = result.getChannelNumber();
+ SearchResult.Builder builder = SearchResult.builder();
long startUtcMillis = c.getLong(5);
long endUtcMillis = c.getLong(6);
- result.title = c.getString(0);
- result.description = buildProgramDescription(result.channelNumber, channelName,
- startUtcMillis, endUtcMillis);
+ builder.setTitle(c.getString(0));
+ builder.setDescription(
+ buildProgramDescription(
+ channelNumber, channelName, startUtcMillis, endUtcMillis));
String imageUri = c.getString(1);
if (imageUri != null) {
- result.imageUri = imageUri;
+ builder.setImageUri(imageUri);
}
- result.videoWidth = c.getInt(3);
- result.videoHeight = c.getInt(4);
- result.duration = endUtcMillis - startUtcMillis;
- result.progressPercentage = getProgressPercentage(startUtcMillis, endUtcMillis);
+ builder.setVideoWidth(c.getInt(3));
+ builder.setVideoHeight(c.getInt(4));
+ builder.setDuration(endUtcMillis - startUtcMillis);
+ builder.setProgressPercentage(getProgressPercentage(startUtcMillis, endUtcMillis));
+ return builder.build();
}
}
+ return result;
}
- private String buildProgramDescription(String channelNumber, String channelName,
- long programStartUtcMillis, long programEndUtcMillis) {
+ private String buildProgramDescription(
+ String channelNumber,
+ String channelName,
+ long programStartUtcMillis,
+ long programEndUtcMillis) {
return Utils.getDurationString(mContext, programStartUtcMillis, programEndUtcMillis, false)
- + System.lineSeparator() + channelNumber + " " + channelName;
+ + System.lineSeparator()
+ + channelNumber
+ + " "
+ + channelName;
}
private int getProgressPercentage(long startUtcMillis, long endUtcMillis) {
@@ -300,23 +345,28 @@ public class TvProviderSearch implements SearchInterface {
if (startUtcMillis > current || endUtcMillis <= current) {
return LocalSearchProvider.PROGRESS_PERCENTAGE_HIDE;
}
- return (int)(100 * (current - startUtcMillis) / (endUtcMillis - startUtcMillis));
+ return (int) (100 * (current - startUtcMillis) / (endUtcMillis - startUtcMillis));
}
@WorkerThread
- private List<SearchResult> searchPrograms(String query, String[] columnForExactMatching,
- String[] columnForPartialMatching, Set<Long> channelsFound, int limit) {
+ private List<SearchResult> searchPrograms(
+ String query,
+ String[] columnForExactMatching,
+ String[] columnForPartialMatching,
+ Set<Long> channelsFound,
+ int limit) {
if (DEBUG) Log.d(TAG, "Searching programs: '" + query + "'");
long time = SystemClock.elapsedRealtime();
String[] projection = {
- Programs.COLUMN_CHANNEL_ID,
- Programs.COLUMN_TITLE,
- Programs.COLUMN_POSTER_ART_URI,
- Programs.COLUMN_CONTENT_RATING,
- Programs.COLUMN_VIDEO_WIDTH,
- Programs.COLUMN_VIDEO_HEIGHT,
- Programs.COLUMN_START_TIME_UTC_MILLIS,
- Programs.COLUMN_END_TIME_UTC_MILLIS
+ Programs.COLUMN_CHANNEL_ID,
+ Programs.COLUMN_TITLE,
+ Programs.COLUMN_POSTER_ART_URI,
+ Programs.COLUMN_CONTENT_RATING,
+ Programs.COLUMN_VIDEO_WIDTH,
+ Programs.COLUMN_VIDEO_HEIGHT,
+ Programs.COLUMN_START_TIME_UTC_MILLIS,
+ Programs.COLUMN_END_TIME_UTC_MILLIS,
+ Programs._ID
};
StringBuilder sb = new StringBuilder();
@@ -327,17 +377,21 @@ public class TvProviderSearch implements SearchInterface {
sb.append(")");
String selection = sb.toString();
- int len = (columnForExactMatching == null ? 0 : columnForExactMatching.length) +
- (columnForPartialMatching == null ? 0 : columnForPartialMatching.length);
+ int len =
+ (columnForExactMatching == null ? 0 : columnForExactMatching.length)
+ + (columnForPartialMatching == null ? 0 : columnForPartialMatching.length);
String[] selectionArgs = new String[len + 2];
- selectionArgs[0] = selectionArgs[1] = String.valueOf(System.currentTimeMillis());
- insertSelectionArgumentStrings(selectionArgs, 2, query, columnForExactMatching,
- columnForPartialMatching);
+ long now = System.currentTimeMillis();
+ selectionArgs[0] = String.valueOf(now + SEARCH_TIME_FRAME_MS);
+ selectionArgs[1] = String.valueOf(now);
+ insertSelectionArgumentStrings(
+ selectionArgs, 2, query, columnForExactMatching, columnForPartialMatching);
List<SearchResult> searchResults = new ArrayList<>();
- try (Cursor c = mContentResolver.query(Programs.CONTENT_URI, projection, selection,
- selectionArgs, null)) {
+ try (Cursor c =
+ mContentResolver.query(
+ Programs.CONTENT_URI, projection, selection, selectionArgs, null)) {
if (c != null) {
int count = 0;
while (c.moveToNext()) {
@@ -350,41 +404,53 @@ public class TvProviderSearch implements SearchInterface {
// Don't know whether the channel is searchable or not.
String[] channelProjection = {
- Channels._ID,
- Channels.COLUMN_DISPLAY_NUMBER,
- Channels.COLUMN_DISPLAY_NAME
+ Channels._ID, Channels.COLUMN_DISPLAY_NUMBER, Channels.COLUMN_DISPLAY_NAME
};
sb = new StringBuilder();
- sb.append(Channels._ID).append("=? AND ")
- .append(Channels.COLUMN_BROWSABLE).append("=1 AND ")
- .append(Channels.COLUMN_SEARCHABLE).append("=1");
+ sb.append(Channels._ID)
+ .append("=? AND ")
+ .append(Channels.COLUMN_BROWSABLE)
+ .append("=1 AND ")
+ .append(Channels.COLUMN_SEARCHABLE)
+ .append("=1");
if (mTvInputManager.isParentalControlsEnabled()) {
sb.append(" AND ").append(Channels.COLUMN_LOCKED).append("=0");
}
String selectionChannel = sb.toString();
- try (Cursor cChannel = mContentResolver.query(Channels.CONTENT_URI,
- channelProjection, selectionChannel,
- new String[] { String.valueOf(id) }, null)) {
- if (cChannel != null && cChannel.moveToNext()
+ try (Cursor cChannel =
+ mContentResolver.query(
+ Channels.CONTENT_URI,
+ channelProjection,
+ selectionChannel,
+ new String[] {String.valueOf(id)},
+ null)) {
+ if (cChannel != null
+ && cChannel.moveToNext()
&& !isRatingBlocked(c.getString(3))) {
long startUtcMillis = c.getLong(6);
long endUtcMillis = c.getLong(7);
- SearchResult result = new SearchResult();
- result.channelId = c.getLong(0);
- result.title = c.getString(1);
- result.description = buildProgramDescription(cChannel.getString(1),
- cChannel.getString(2), startUtcMillis, endUtcMillis);
- result.imageUri = c.getString(2);
- result.intentAction = Intent.ACTION_VIEW;
- result.intentData = buildIntentData(id);
- result.contentType = Programs.CONTENT_ITEM_TYPE;
- result.isLive = true;
- result.videoWidth = c.getInt(4);
- result.videoHeight = c.getInt(5);
- result.duration = endUtcMillis - startUtcMillis;
- result.progressPercentage = getProgressPercentage(startUtcMillis,
- endUtcMillis);
- searchResults.add(result);
+ SearchResult.Builder result = SearchResult.builder();
+ result.setChannelId(c.getLong(0));
+ result.setTitle(c.getString(1));
+ result.setDescription(
+ buildProgramDescription(
+ cChannel.getString(1),
+ cChannel.getString(2),
+ startUtcMillis,
+ endUtcMillis));
+ result.setImageUri(c.getString(2));
+ result.setIntentAction(Intent.ACTION_VIEW);
+ result.setIntentData(buildIntentData(id));
+ result.setIntentExtraData(
+ TvContract.buildProgramUri(c.getLong(8)).toString());
+ result.setContentType(Programs.CONTENT_ITEM_TYPE);
+ result.setIsLive(true);
+ result.setVideoWidth(c.getInt(4));
+ result.setVideoHeight(c.getInt(5));
+ result.setDuration(endUtcMillis - startUtcMillis);
+ result.setProgressPercentage(
+ getProgressPercentage(startUtcMillis, endUtcMillis));
+ searchResults.add(result.build());
if (limit != NO_LIMIT && ++count >= limit) {
break;
@@ -395,8 +461,14 @@ public class TvProviderSearch implements SearchInterface {
}
}
if (DEBUG) {
- Log.d(TAG, "Found " + searchResults.size() + " programs. Elapsed time for searching" +
- " programs: " + (SystemClock.elapsedRealtime() - time) + "(msec)");
+ Log.d(
+ TAG,
+ "Found "
+ + searchResults.size()
+ + " programs. Elapsed time for searching"
+ + " programs: "
+ + (SystemClock.elapsedRealtime() - time)
+ + "(msec)");
}
return searchResults;
}
@@ -439,9 +511,14 @@ public class TvProviderSearch implements SearchInterface {
results.add(buildSearchResultForInput(input.getId()));
if (results.size() >= limit) {
if (DEBUG) {
- Log.d(TAG, "Found " + results.size() + " inputs. Elapsed time for" +
- " searching inputs: " + (SystemClock.elapsedRealtime() - time) +
- "(msec)");
+ Log.d(
+ TAG,
+ "Found "
+ + results.size()
+ + " inputs. Elapsed time for"
+ + " searching inputs: "
+ + (SystemClock.elapsedRealtime() - time)
+ + "(msec)");
}
return results;
}
@@ -455,22 +532,33 @@ public class TvProviderSearch implements SearchInterface {
}
String label = canonicalizeLabel(input.loadLabel(mContext));
String customLabel = canonicalizeLabel(input.loadCustomLabel(mContext));
- if ((label != null && label.contains(query)) ||
- (customLabel != null && customLabel.contains(query))) {
+ if ((label != null && label.contains(query))
+ || (customLabel != null && customLabel.contains(query))) {
results.add(buildSearchResultForInput(input.getId()));
if (results.size() >= limit) {
if (DEBUG) {
- Log.d(TAG, "Found " + results.size() + " inputs. Elapsed time for" +
- " searching inputs: " + (SystemClock.elapsedRealtime() - time) +
- "(msec)");
+ Log.d(
+ TAG,
+ "Found "
+ + results.size()
+ + " inputs. Elapsed time for"
+ + " searching inputs: "
+ + (SystemClock.elapsedRealtime() - time)
+ + "(msec)");
}
return results;
}
}
}
if (DEBUG) {
- Log.d(TAG, "Found " + results.size() + " inputs. Elapsed time for searching" +
- " inputs: " + (SystemClock.elapsedRealtime() - time) + "(msec)");
+ Log.d(
+ TAG,
+ "Found "
+ + results.size()
+ + " inputs. Elapsed time for searching"
+ + " inputs: "
+ + (SystemClock.elapsedRealtime() - time)
+ + "(msec)");
}
return results;
}
@@ -481,10 +569,10 @@ public class TvProviderSearch implements SearchInterface {
}
private SearchResult buildSearchResultForInput(String inputId) {
- SearchResult result = new SearchResult();
- result.intentAction = Intent.ACTION_VIEW;
- result.intentData = TvContract.buildChannelUriForPassthroughInput(inputId).toString();
- return result;
+ SearchResult.Builder result = SearchResult.builder();
+ result.setIntentAction(Intent.ACTION_VIEW);
+ result.setIntentData(TvContract.buildChannelUriForPassthroughInput(inputId).toString());
+ return result.build();
}
@WorkerThread
@@ -494,33 +582,35 @@ public class TvProviderSearch implements SearchInterface {
@Override
public int compare(SearchResult lhs, SearchResult rhs) {
// Show recently watched channel first
- Long lhsMaxWatchStartTime = mMaxWatchStartTimeMap.get(lhs.channelId);
+ Long lhsMaxWatchStartTime = mMaxWatchStartTimeMap.get(lhs.getChannelId());
if (lhsMaxWatchStartTime == null) {
- lhsMaxWatchStartTime = getMaxWatchStartTime(lhs.channelId);
- mMaxWatchStartTimeMap.put(lhs.channelId, lhsMaxWatchStartTime);
+ lhsMaxWatchStartTime = getMaxWatchStartTime(lhs.getChannelId());
+ mMaxWatchStartTimeMap.put(lhs.getChannelId(), lhsMaxWatchStartTime);
}
- Long rhsMaxWatchStartTime = mMaxWatchStartTimeMap.get(rhs.channelId);
+ Long rhsMaxWatchStartTime = mMaxWatchStartTimeMap.get(rhs.getChannelId());
if (rhsMaxWatchStartTime == null) {
- rhsMaxWatchStartTime = getMaxWatchStartTime(rhs.channelId);
- mMaxWatchStartTimeMap.put(rhs.channelId, rhsMaxWatchStartTime);
+ rhsMaxWatchStartTime = getMaxWatchStartTime(rhs.getChannelId());
+ mMaxWatchStartTimeMap.put(rhs.getChannelId(), rhsMaxWatchStartTime);
}
if (!Objects.equals(lhsMaxWatchStartTime, rhsMaxWatchStartTime)) {
return Long.compare(rhsMaxWatchStartTime, lhsMaxWatchStartTime);
}
// Show recently added channel first if there's no watch history.
- return Long.compare(rhs.channelId, lhs.channelId);
+ return Long.compare(rhs.getChannelId(), lhs.getChannelId());
}
private long getMaxWatchStartTime(long channelId) {
Uri uri = WatchedPrograms.CONTENT_URI;
- String[] projections = new String[] {
- "MAX(" + WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS
- + ") AS max_watch_start_time"
- };
+ String[] projections =
+ new String[] {
+ "MAX("
+ + WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS
+ + ") AS max_watch_start_time"
+ };
String selection = WatchedPrograms.COLUMN_CHANNEL_ID + "=?";
- String[] selectionArgs = new String[] { Long.toString(channelId) };
- try (Cursor c = mContentResolver.query(uri, projections, selection, selectionArgs,
- null)) {
+ String[] selectionArgs = new String[] {Long.toString(channelId)};
+ try (Cursor c =
+ mContentResolver.query(uri, projections, selection, selectionArgs, null)) {
if (c != null && c.moveToNext()) {
return c.getLong(0);
}
diff --git a/src/com/android/tv/setup/SystemSetupActivity.java b/src/com/android/tv/setup/SystemSetupActivity.java
index 7e627410..c6b10e52 100644
--- a/src/com/android/tv/setup/SystemSetupActivity.java
+++ b/src/com/android/tv/setup/SystemSetupActivity.java
@@ -24,27 +24,22 @@ import android.content.Intent;
import android.media.tv.TvInputInfo;
import android.os.Bundle;
import android.widget.Toast;
-
-import com.android.tv.ApplicationSingletons;
import com.android.tv.R;
import com.android.tv.SetupPassthroughActivity;
-import com.android.tv.common.TvCommonUtils;
+import com.android.tv.TvSingletons;
+import com.android.tv.common.CommonConstants;
import com.android.tv.common.ui.setup.SetupActivity;
-import com.android.tv.common.ui.setup.SetupFragment;
import com.android.tv.common.ui.setup.SetupMultiPaneFragment;
-import com.android.tv.TvApplication;
-import com.android.tv.data.ChannelDataManager;
+import com.android.tv.common.util.CommonUtils;
import com.android.tv.onboarding.SetupSourcesFragment;
import com.android.tv.util.OnboardingUtils;
import com.android.tv.util.SetupUtils;
import com.android.tv.util.TvInputManagerHelper;
-/**
- * A activity to start input sources setup fragment for initial setup flow.
- */
+/** A activity to start input sources setup fragment for initial setup flow. */
public class SystemSetupActivity extends SetupActivity {
private static final String SYSTEM_SETUP =
- "com.android.tv.action.LAUNCH_SYSTEM_SETUP";
+ CommonConstants.BASE_PACKAGE + ".action.LAUNCH_SYSTEM_SETUP";
private static final int SHOW_RIPPLE_DURATION_MS = 266;
private static final int REQUEST_CODE_START_SETUP_ACTIVITY = 1;
@@ -58,7 +53,7 @@ public class SystemSetupActivity extends SetupActivity {
finish();
return;
}
- ApplicationSingletons singletons = TvApplication.getSingletons(this);
+ TvSingletons singletons = TvSingletons.getSingletons(this);
mInputManager = singletons.getTvInputManagerHelper();
}
@@ -68,12 +63,14 @@ public class SystemSetupActivity extends SetupActivity {
}
private void showMerchantCollection() {
- executeActionWithDelay(new Runnable() {
- @Override
- public void run() {
- startActivity(OnboardingUtils.ONLINE_STORE_INTENT);
- }
- }, SHOW_RIPPLE_DURATION_MS);
+ executeActionWithDelay(
+ new Runnable() {
+ @Override
+ public void run() {
+ startActivity(OnboardingUtils.ONLINE_STORE_INTENT);
+ }
+ },
+ SHOW_RIPPLE_DURATION_MS);
}
@Override
@@ -84,38 +81,50 @@ public class SystemSetupActivity extends SetupActivity {
case SetupSourcesFragment.ACTION_ONLINE_STORE:
showMerchantCollection();
return true;
- case SetupSourcesFragment.ACTION_SETUP_INPUT: {
- String inputId = params.getString(
- SetupSourcesFragment.ACTION_PARAM_KEY_INPUT_ID);
- TvInputInfo input = mInputManager.getTvInputInfo(inputId);
- Intent intent = TvCommonUtils.createSetupIntent(input);
- if (intent == null) {
- Toast.makeText(this, R.string.msg_no_setup_activity, Toast.LENGTH_SHORT)
- .show();
+ case SetupSourcesFragment.ACTION_SETUP_INPUT:
+ {
+ String inputId =
+ params.getString(
+ SetupSourcesFragment.ACTION_PARAM_KEY_INPUT_ID);
+ TvInputInfo input = mInputManager.getTvInputInfo(inputId);
+ Intent intent = CommonUtils.createSetupIntent(input);
+ if (intent == null) {
+ Toast.makeText(
+ this,
+ R.string.msg_no_setup_activity,
+ Toast.LENGTH_SHORT)
+ .show();
+ return true;
+ }
+ // Even though other app can handle the intent, the setup launched by
+ // Live
+ // channels should go through Live channels SetupPassthroughActivity.
+ intent.setComponent(
+ new ComponentName(this, SetupPassthroughActivity.class));
+ try {
+ // Now we know that the user intends to set up this input. Grant
+ // permission for writing EPG data.
+ SetupUtils.grantEpgPermission(
+ this, input.getServiceInfo().packageName);
+ startActivityForResult(intent, REQUEST_CODE_START_SETUP_ACTIVITY);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(
+ this,
+ getString(
+ R.string.msg_unable_to_start_setup_activity,
+ input.loadLabel(this)),
+ Toast.LENGTH_SHORT)
+ .show();
+ }
return true;
}
- // Even though other app can handle the intent, the setup launched by Live
- // channels should go through Live channels SetupPassthroughActivity.
- intent.setComponent(new ComponentName(this,
- SetupPassthroughActivity.class));
- try {
- // Now we know that the user intends to set up this input. Grant
- // permission for writing EPG data.
- SetupUtils.grantEpgPermission(this, input.getServiceInfo().packageName);
- startActivityForResult(intent, REQUEST_CODE_START_SETUP_ACTIVITY);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(this,
- getString(R.string.msg_unable_to_start_setup_activity,
- input.loadLabel(this)), Toast.LENGTH_SHORT).show();
+ case SetupMultiPaneFragment.ACTION_DONE:
+ {
+ // To make sure user can finish setup flow, set result as RESULT_OK.
+ setResult(Activity.RESULT_OK);
+ finish();
+ return true;
}
- return true;
- }
- case SetupMultiPaneFragment.ACTION_DONE: {
- // To make sure user can finish setup flow, set result as RESULT_OK.
- setResult(Activity.RESULT_OK);
- finish();
- return true;
- }
}
break;
}
diff --git a/src/com/android/tv/tuner/TunerInputController.java b/src/com/android/tv/tuner/TunerInputController.java
index e06b9b4a..02611bbf 100644
--- a/src/com/android/tv/tuner/TunerInputController.java
+++ b/src/com/android/tv/tuner/TunerInputController.java
@@ -17,6 +17,9 @@
package com.android.tv.tuner;
import android.app.AlarmManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -24,10 +27,14 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
+import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
@@ -38,118 +45,132 @@ import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
-
-import com.android.tv.Features;
import com.android.tv.R;
+import com.android.tv.Starter;
import com.android.tv.TvApplication;
-import com.android.tv.common.SoftPreconditions;
-import com.android.tv.tuner.setup.TunerSetupActivity;
-import com.android.tv.tuner.tvinput.TunerTvInputService;
-import com.android.tv.tuner.util.SystemPropertiesProxy;
-import com.android.tv.tuner.util.TunerInputInfoUtils;
+import com.android.tv.TvSingletons;
+import com.android.tv.common.BuildConfig;
+import com.android.tv.common.util.SystemPropertiesProxy;
+
+import com.android.tv.tuner.setup.BaseTunerSetupActivity;
+import com.android.tv.tuner.util.TunerInputInfoUtils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
- * Controls the package visibility of {@link TunerTvInputService}.
- * <p>
- * Listens to broadcast intent for {@link Intent#ACTION_BOOT_COMPLETED},
- * {@code UsbManager.ACTION_USB_DEVICE_ATTACHED}, and {@code UsbManager.ACTION_USB_DEVICE_ATTACHED}
- * to update the connection status of the supported USB TV tuners.
+ * Controls the package visibility of {@link BaseTunerTvInputService}.
+ *
+ * <p>Listens to broadcast intent for {@link Intent#ACTION_BOOT_COMPLETED}, {@code
+ * UsbManager.ACTION_USB_DEVICE_ATTACHED}, and {@code UsbManager.ACTION_USB_DEVICE_ATTACHED} to
+ * update the connection status of the supported USB TV tuners.
*/
public class TunerInputController {
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
private static final String TAG = "TunerInputController";
private static final String PREFERENCE_IS_NETWORK_TUNER_ATTACHED = "network_tuner";
private static final String SECURITY_PATCH_LEVEL_KEY = "ro.build.version.security_patch";
private static final String SECURITY_PATCH_LEVEL_FORMAT = "yyyy-MM-dd";
+ private static final String PLAY_STORE_LINK_TEMPLATE = "market://details?id=%s";
- /**
- * Action of {@link Intent} to check network connection repeatedly when it is necessary.
- */
- private static final String CHECKING_NETWORK_CONNECTION =
- "com.android.tv.action.CHECKING_NETWORK_CONNECTION";
+ /** Action of {@link Intent} to check network connection repeatedly when it is necessary. */
+ private static final String CHECKING_NETWORK_TUNER_STATUS =
+ "com.android.tv.action.CHECKING_NETWORK_TUNER_STATUS";
private static final String EXTRA_CHECKING_DURATION =
"com.android.tv.action.extra.CHECKING_DURATION";
+ private static final String EXTRA_DEVICE_IP = "com.android.tv.action.extra.DEVICE_IP";
private static final long INITIAL_CHECKING_DURATION_MS = TimeUnit.SECONDS.toMillis(10);
private static final long MAXIMUM_CHECKING_DURATION_MS = TimeUnit.MINUTES.toMillis(10);
+ private static final String NOTIFICATION_CHANNEL_ID = "tuner_discovery_notification";
+ // TODO: Load settings from XML file
private static final TunerDevice[] TUNER_DEVICES = {
new TunerDevice(0x2040, 0xb123, null), // WinTV-HVR-955Q
new TunerDevice(0x07ca, 0x0837, null), // AverTV Volar Hybrid Q
// WinTV-dualHD (bulk) will be supported after 2017 April security patch.
new TunerDevice(0x2040, 0x826d, "2017-04-01"), // WinTV-dualHD (bulk)
- // STOPSHIP: Add WinTV-soloHD (Isoc) temporary for test. Remove this after test complete.
new TunerDevice(0x2040, 0x0264, null),
};
private static final int MSG_ENABLE_INPUT_SERVICE = 1000;
private static final long DVB_DRIVER_CHECK_DELAY_MS = 300;
- /**
- * Checks status of USB devices to see if there are available USB tuners connected.
- */
- public static void onCheckingUsbTunerStatus(Context context, String action) {
- onCheckingUsbTunerStatus(context, action, new CheckDvbDeviceHandler());
- }
+ private final ComponentName usbTunerComponent;
+ private final ComponentName networkTunerComponent;
+ private final ComponentName builtInTunerComponent;
+ private final Map<TunerDevice, ComponentName> mTunerServiceMapping = new HashMap<>();
- private static void onCheckingUsbTunerStatus(Context context, String action,
- @NonNull CheckDvbDeviceHandler handler) {
- SharedPreferences sharedPreferences =
- PreferenceManager.getDefaultSharedPreferences(context);
- if (TunerHal.useBuiltInTuner(context)) {
- enableTunerTvInputService(context, true, false, TunerHal.TUNER_TYPE_BUILT_IN);
- return;
+ private final Map<ComponentName, String> mTunerApplicationNames = new HashMap<>();
+ private final Map<ComponentName, String> mNotificationMessages = new HashMap<>();
+ private final Map<ComponentName, Bitmap> mNotificationLargeIcons = new HashMap<>();
+
+ private final CheckDvbDeviceHandler mHandler = new CheckDvbDeviceHandler(this);
+
+ public TunerInputController(ComponentName embeddedTuner) {
+ usbTunerComponent = embeddedTuner;
+ networkTunerComponent = usbTunerComponent;
+ builtInTunerComponent = usbTunerComponent;
+ for (TunerDevice device : TUNER_DEVICES) {
+ mTunerServiceMapping.put(device, usbTunerComponent);
}
- // Falls back to the below to check USB tuner devices.
- boolean enabled = isUsbTunerConnected(context);
+ }
+
+ /** Checks status of USB devices to see if there are available USB tuners connected. */
+ public void onCheckingUsbTunerStatus(Context context, String action) {
+ onCheckingUsbTunerStatus(context, action, mHandler);
+ }
+
+ private void onCheckingUsbTunerStatus(
+ Context context, String action, @NonNull CheckDvbDeviceHandler handler) {
+ Set<TunerDevice> connectedUsbTuners = getConnectedUsbTuners(context);
handler.removeMessages(MSG_ENABLE_INPUT_SERVICE);
- if (enabled) {
+ if (!connectedUsbTuners.isEmpty()) {
// Need to check if DVB driver is accessible. Since the driver creation
// could be happen after the USB event, delay the checking by
// DVB_DRIVER_CHECK_DELAY_MS.
- handler.sendMessageDelayed(handler.obtainMessage(MSG_ENABLE_INPUT_SERVICE, context),
+ handler.sendMessageDelayed(
+ handler.obtainMessage(MSG_ENABLE_INPUT_SERVICE, context),
DVB_DRIVER_CHECK_DELAY_MS);
} else {
- if (sharedPreferences.getBoolean(PREFERENCE_IS_NETWORK_TUNER_ATTACHED, false)) {
- // Since network tuner is attached, do not disable TunerTvInput,
- // just updates the TvInputInfo.
- TunerInputInfoUtils.updateTunerInputInfo(context);
- return;
- }
- enableTunerTvInputService(context, false, false, TextUtils
- .equals(action, UsbManager.ACTION_USB_DEVICE_DETACHED) ?
- TunerHal.TUNER_TYPE_USB : null);
+ handleTunerStatusChanged(
+ context,
+ false,
+ connectedUsbTuners,
+ TextUtils.equals(action, UsbManager.ACTION_USB_DEVICE_DETACHED)
+ ? TunerHal.TUNER_TYPE_USB
+ : null);
}
}
- private static void onNetworkTunerChanged(Context context, boolean enabled) {
+ private void onNetworkTunerChanged(Context context, boolean enabled) {
SharedPreferences sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(context);
+ if (sharedPreferences.contains(PREFERENCE_IS_NETWORK_TUNER_ATTACHED)
+ && sharedPreferences.getBoolean(PREFERENCE_IS_NETWORK_TUNER_ATTACHED, false)
+ == enabled) {
+ // the status is not changed
+ return;
+ }
if (enabled) {
- // Network tuner detection is initiated by UI. So the app should not
- // be killed.
sharedPreferences.edit().putBoolean(PREFERENCE_IS_NETWORK_TUNER_ATTACHED, true).apply();
- enableTunerTvInputService(context, true, true, TunerHal.TUNER_TYPE_NETWORK);
} else {
- sharedPreferences.edit()
- .putBoolean(PREFERENCE_IS_NETWORK_TUNER_ATTACHED, false).apply();
- if(!isUsbTunerConnected(context) && !TunerHal.useBuiltInTuner(context)) {
- // Network tuner detection is initiated by UI. So the app should not
- // be killed.
- enableTunerTvInputService(context, false, true, TunerHal.TUNER_TYPE_NETWORK);
- } else {
- // Since USB tuner is attached, do not disable TunerTvInput,
- // just updates the TvInputInfo.
- TunerInputInfoUtils.updateTunerInputInfo(context);
- }
+ sharedPreferences
+ .edit()
+ .putBoolean(PREFERENCE_IS_NETWORK_TUNER_ATTACHED, false)
+ .apply();
}
+ // Network tuner detection is initiated by UI. So the app should not
+ // be killed.
+ handleTunerStatusChanged(
+ context, true, getConnectedUsbTuners(context), TunerHal.TUNER_TYPE_NETWORK);
}
/**
@@ -158,66 +179,131 @@ public class TunerInputController {
* @param context {@link Context} instance
* @return {@code true} if any tuner device we support is plugged in
*/
- private static boolean isUsbTunerConnected(Context context) {
+ private Set<TunerDevice> getConnectedUsbTuners(Context context) {
UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
Map<String, UsbDevice> deviceList = manager.getDeviceList();
String currentSecurityLevel =
SystemPropertiesProxy.getString(SECURITY_PATCH_LEVEL_KEY, null);
+ Set<TunerDevice> devices = new HashSet<>();
for (UsbDevice device : deviceList.values()) {
if (DEBUG) {
Log.d(TAG, "Device: " + device);
}
for (TunerDevice tuner : TUNER_DEVICES) {
- if (tuner.equals(device) && tuner.isSupported(currentSecurityLevel)) {
+ if (tuner.equalsTo(device) && tuner.isSupported(currentSecurityLevel)) {
Log.i(TAG, "Tuner found");
- return true;
+ devices.add(tuner);
}
}
}
- return false;
+ return devices;
+ }
+
+ private void handleTunerStatusChanged(
+ Context context,
+ boolean forceDontKillApp,
+ Set<TunerDevice> connectedUsbTuners,
+ Integer triggerType) {
+ Map<ComponentName, Integer> serviceToEnable = new HashMap<>();
+ Set<ComponentName> serviceToDisable = new HashSet<>();
+ serviceToDisable.add(builtInTunerComponent);
+ serviceToDisable.add(networkTunerComponent);
+ if (TunerFeatures.TUNER.isEnabled(context)) {
+ // TODO: support both built-in tuner and other tuners at the same time?
+ if (TunerHal.useBuiltInTuner(context)) {
+ enableTunerTvInputService(
+ context, true, false, TunerHal.TUNER_TYPE_BUILT_IN, builtInTunerComponent);
+ return;
+ }
+ SharedPreferences sharedPreferences =
+ PreferenceManager.getDefaultSharedPreferences(context);
+ if (sharedPreferences.getBoolean(PREFERENCE_IS_NETWORK_TUNER_ATTACHED, false)) {
+ serviceToEnable.put(networkTunerComponent, TunerHal.TUNER_TYPE_NETWORK);
+ }
+ }
+ for (TunerDevice device : TUNER_DEVICES) {
+ if (TunerFeatures.TUNER.isEnabled(context) && connectedUsbTuners.contains(device)) {
+ serviceToEnable.put(mTunerServiceMapping.get(device), TunerHal.TUNER_TYPE_USB);
+ } else {
+ serviceToDisable.add(mTunerServiceMapping.get(device));
+ }
+ }
+ serviceToDisable.removeAll(serviceToEnable.keySet());
+ for (ComponentName serviceComponent : serviceToEnable.keySet()) {
+ if (isTunerPackageInstalled(context, serviceComponent)) {
+ enableTunerTvInputService(
+ context,
+ true,
+ forceDontKillApp,
+ serviceToEnable.get(serviceComponent),
+ serviceComponent);
+ } else {
+ sendNotificationToInstallPackage(context, serviceComponent);
+ }
+ }
+ for (ComponentName serviceComponent : serviceToDisable) {
+ if (isTunerPackageInstalled(context, serviceComponent)) {
+ enableTunerTvInputService(
+ context, false, forceDontKillApp, triggerType, serviceComponent);
+ } else {
+ cancelNotificationToInstallPackage(context, serviceComponent);
+ }
+ }
}
/**
- * Enable/disable the component {@link TunerTvInputService}.
+ * Enable/disable the component {@link BaseTunerTvInputService}.
*
* @param context {@link Context} instance
* @param enabled {@code true} to enable the service; otherwise {@code false}
*/
- private static void enableTunerTvInputService(Context context, boolean enabled,
- boolean forceDontKillApp, Integer tunerType) {
+ private static void enableTunerTvInputService(
+ Context context,
+ boolean enabled,
+ boolean forceDontKillApp,
+ Integer tunerType,
+ ComponentName serviceComponent) {
if (DEBUG) Log.d(TAG, "enableTunerTvInputService: " + enabled);
- PackageManager pm = context.getPackageManager();
- ComponentName componentName = new ComponentName(context, TunerTvInputService.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.
- int flags = forceDontKillApp
- || TvApplication.getSingletons(context).getMainActivityWrapper().isCreated()
- ? PackageManager.DONT_KILL_APP : 0;
- int newState = enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
- : PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
- if (newState != pm.getComponentEnabledSetting(componentName)) {
- // Send/cancel the USB tuner TV input setup notification.
- TunerSetupActivity.onTvInputEnabled(context, enabled, tunerType);
- // Enable/disable the USB tuner TV input.
- pm.setComponentEnabledSetting(componentName, newState, flags);
- if (!enabled && tunerType != null) {
- if (tunerType == TunerHal.TUNER_TYPE_USB) {
- Toast.makeText(context, R.string.msg_usb_tuner_disconnected,
- Toast.LENGTH_SHORT).show();
- } else if (tunerType == TunerHal.TUNER_TYPE_NETWORK) {
- Toast.makeText(context, R.string.msg_network_tuner_disconnected,
- Toast.LENGTH_SHORT).show();
+ PackageManager pm = context.getPackageManager();
+ int newState =
+ enabled
+ ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+ : PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+ if (newState != pm.getComponentEnabledSetting(serviceComponent)) {
+ int flags = forceDontKillApp ? PackageManager.DONT_KILL_APP : 0;
+ if (serviceComponent.getPackageName().equals(context.getPackageName())) {
+ // Don't kill APP when handling input count changing. Or the following
+ // setComponentEnabledSetting() call won't work.
+ ((TvApplication) context.getApplicationContext())
+ .handleInputCountChanged(true, enabled, true);
+ // Bundled input. Don't kill app if LiveChannels app is active since we don't want
+ // to kill the running app.
+ if (TvSingletons.getSingletons(context).getMainActivityWrapper().isCreated()) {
+ flags |= PackageManager.DONT_KILL_APP;
+ }
+ // Send/cancel the USB tuner TV input setup notification.
+ BaseTunerSetupActivity.onTvInputEnabled(context, enabled, tunerType);
+ if (!enabled && tunerType != null) {
+ if (tunerType == TunerHal.TUNER_TYPE_USB) {
+ Toast.makeText(
+ context,
+ R.string.msg_usb_tuner_disconnected,
+ Toast.LENGTH_SHORT)
+ .show();
+ } else if (tunerType == TunerHal.TUNER_TYPE_NETWORK) {
+ Toast.makeText(
+ context,
+ R.string.msg_network_tuner_disconnected,
+ Toast.LENGTH_SHORT)
+ .show();
+ }
}
}
+ // Enable/disable the USB tuner TV input.
+ pm.setComponentEnabledSetting(serviceComponent, newState, flags);
if (DEBUG) Log.d(TAG, "Status updated:" + enabled);
- } else if (enabled) {
+ } else if (enabled && serviceComponent.getPackageName().equals(context.getPackageName())) {
// When # of tuners is changed or the tuner input service is switching from/to using
// network tuners or the device just boots.
TunerInputInfoUtils.updateTunerInputInfo(context);
@@ -227,96 +313,179 @@ public class TunerInputController {
/**
* Discovers a network tuner. If the network connection is down, it won't repeatedly checking.
*/
- public static void executeNetworkTunerDiscoveryAsyncTask(final Context context) {
- boolean runningInMainProcess =
- TvApplication.getSingletons(context).isRunningInMainProcess();
- SoftPreconditions.checkState(runningInMainProcess);
- if (!runningInMainProcess) {
- return;
- }
- executeNetworkTunerDiscoveryAsyncTask(context, 0);
+ public void executeNetworkTunerDiscoveryAsyncTask(final Context context) {
+ executeNetworkTunerDiscoveryAsyncTask(context, 0, 0);
}
/**
* Discovers a network tuner.
+ *
* @param context {@link Context}
- * @param repeatedDurationMs the time length to wait to repeatedly check network status to start
- * finding network tuner when the network connection is not available.
- * {@code 0} to disable repeatedly checking.
+ * @param repeatedDurationMs The time length to wait to repeatedly check network status to start
+ * finding network tuner when the network connection is not available. {@code 0} to disable
+ * repeatedly checking.
+ * @param deviceIp The previous discovered device IP, 0 if none.
*/
- private static void executeNetworkTunerDiscoveryAsyncTask(final Context context,
- final long repeatedDurationMs) {
- if (!Features.NETWORK_TUNER.isEnabled(context)) {
+ private void executeNetworkTunerDiscoveryAsyncTask(
+ final Context context, final long repeatedDurationMs, final int deviceIp) {
+ if (!TunerFeatures.NETWORK_TUNER.isEnabled(context)) {
return;
}
- new AsyncTask<Void, Void, Boolean>() {
- @Override
- protected Boolean doInBackground(Void... params) {
- if (isNetworkConnected(context)) {
+ final Intent networkCheckingIntent = new Intent(context, IntentReceiver.class);
+ networkCheckingIntent.setAction(CHECKING_NETWORK_TUNER_STATUS);
+ if (!isNetworkConnected(context) && repeatedDurationMs > 0) {
+ sendCheckingAlarm(context, networkCheckingIntent, repeatedDurationMs);
+ } else {
+ new AsyncTask<Void, Void, Boolean>() {
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ Boolean result = null;
// Implement and execute network tuner discovery AsyncTask here.
- } else if (repeatedDurationMs > 0) {
- AlarmManager alarmManager =
- (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- Intent networkCheckingIntent = new Intent(context, IntentReceiver.class);
- networkCheckingIntent.setAction(CHECKING_NETWORK_CONNECTION);
- networkCheckingIntent.putExtra(EXTRA_CHECKING_DURATION, repeatedDurationMs);
- PendingIntent alarmIntent = PendingIntent.getBroadcast(
- context, 0, networkCheckingIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- alarmManager.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime()
- + repeatedDurationMs, alarmIntent);
+ return result;
}
- return null;
- }
- @Override
- protected void onPostExecute(Boolean result) {
- if (result == null) {
- return;
+ @Override
+ protected void onPostExecute(Boolean foundNetworkTuner) {
+ if (foundNetworkTuner == null) {
+ return;
+ }
+ sendCheckingAlarm(
+ context,
+ networkCheckingIntent,
+ foundNetworkTuner ? INITIAL_CHECKING_DURATION_MS : repeatedDurationMs);
+ onNetworkTunerChanged(context, foundNetworkTuner);
}
- onNetworkTunerChanged(context, result);
- }
- }.execute();
+ }.execute();
+ }
}
private static boolean isNetworkConnected(Context context) {
- ConnectivityManager cm = (ConnectivityManager)
- context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ ConnectivityManager cm =
+ (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
return networkInfo != null && networkInfo.isConnected();
}
+ private static void sendCheckingAlarm(Context context, Intent intent, long delayMs) {
+ AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ intent.putExtra(EXTRA_CHECKING_DURATION, delayMs);
+ PendingIntent alarmIntent =
+ PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ alarmManager.set(
+ AlarmManager.ELAPSED_REALTIME,
+ SystemClock.elapsedRealtime() + delayMs,
+ alarmIntent);
+ }
+
+ private static boolean isTunerPackageInstalled(
+ Context context, ComponentName serviceComponent) {
+ try {
+ context.getPackageManager().getPackageInfo(serviceComponent.getPackageName(), 0);
+ return true;
+ } catch (NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ private void sendNotificationToInstallPackage(Context context, ComponentName serviceComponent) {
+ if (!BuildConfig.ENG) {
+ return;
+ }
+ String applicationName = mTunerApplicationNames.get(serviceComponent);
+ if (applicationName == null) {
+ applicationName = context.getString(R.string.tuner_install_default_application_name);
+ }
+ String contentTitle =
+ context.getString(
+ R.string.tuner_install_notification_content_title, applicationName);
+ String contentText = mNotificationMessages.get(serviceComponent);
+ if (contentText == null) {
+ contentText = context.getString(R.string.tuner_install_notification_content_text);
+ }
+ Bitmap largeIcon = mNotificationLargeIcons.get(serviceComponent);
+ if (largeIcon == null) {
+ // TODO: Make a better default image.
+ largeIcon = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_store);
+ }
+ NotificationManager notificationManager =
+ (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ if (notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID) == null) {
+ createNotificationChannel(context, notificationManager);
+ }
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(
+ Uri.parse(
+ String.format(
+ PLAY_STORE_LINK_TEMPLATE, serviceComponent.getPackageName())));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ Notification.Builder builder = new Notification.Builder(context, NOTIFICATION_CHANNEL_ID);
+ builder.setAutoCancel(true)
+ .setSmallIcon(R.drawable.ic_launcher_s)
+ .setLargeIcon(largeIcon)
+ .setContentTitle(contentTitle)
+ .setContentText(contentText)
+ .setCategory(Notification.CATEGORY_RECOMMENDATION)
+ .setContentIntent(PendingIntent.getActivity(context, 0, intent, 0));
+ notificationManager.notify(serviceComponent.getPackageName(), 0, builder.build());
+ }
+
+ private static void cancelNotificationToInstallPackage(
+ Context context, ComponentName serviceComponent) {
+ NotificationManager notificationManager =
+ (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ notificationManager.cancel(serviceComponent.getPackageName(), 0);
+ }
+
+ private static void createNotificationChannel(
+ Context context, NotificationManager notificationManager) {
+ notificationManager.createNotificationChannel(
+ new NotificationChannel(
+ NOTIFICATION_CHANNEL_ID,
+ context.getResources()
+ .getString(R.string.ut_setup_notification_channel_name),
+ NotificationManager.IMPORTANCE_HIGH));
+ }
+
public static class IntentReceiver extends BroadcastReceiver {
- private final CheckDvbDeviceHandler mHandler = new CheckDvbDeviceHandler();
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) Log.d(TAG, "Broadcast intent received:" + intent);
- TvApplication.setCurrentRunningProcess(context, true);
- if (!Features.TUNER.isEnabled(context)) {
- enableTunerTvInputService(context, false, false, null);
+ Starter.start(context);
+ TunerInputController tunerInputController =
+ TvSingletons.getSingletons(context).getTunerInputController();
+ if (!TunerFeatures.TUNER.isEnabled(context)) {
+ tunerInputController.handleTunerStatusChanged(
+ context, false, Collections.emptySet(), null);
return;
}
switch (intent.getAction()) {
case Intent.ACTION_BOOT_COMPLETED:
- executeNetworkTunerDiscoveryAsyncTask(context, INITIAL_CHECKING_DURATION_MS);
+ tunerInputController.executeNetworkTunerDiscoveryAsyncTask(
+ context, INITIAL_CHECKING_DURATION_MS, 0);
+ // fall through
case TvApplication.ACTION_APPLICATION_FIRST_LAUNCHED:
case UsbManager.ACTION_USB_DEVICE_ATTACHED:
case UsbManager.ACTION_USB_DEVICE_DETACHED:
- onCheckingUsbTunerStatus(context, intent.getAction(), mHandler);
+ tunerInputController.onCheckingUsbTunerStatus(context, intent.getAction());
break;
- case CHECKING_NETWORK_CONNECTION:
- long repeatedDurationMs = intent.getLongExtra(EXTRA_CHECKING_DURATION,
- INITIAL_CHECKING_DURATION_MS);
- executeNetworkTunerDiscoveryAsyncTask(context,
- Math.min(repeatedDurationMs * 2, MAXIMUM_CHECKING_DURATION_MS));
+ case CHECKING_NETWORK_TUNER_STATUS:
+ long repeatedDurationMs =
+ intent.getLongExtra(
+ EXTRA_CHECKING_DURATION, INITIAL_CHECKING_DURATION_MS);
+ tunerInputController.executeNetworkTunerDiscoveryAsyncTask(
+ context,
+ Math.min(repeatedDurationMs * 2, MAXIMUM_CHECKING_DURATION_MS),
+ intent.getIntExtra(EXTRA_DEVICE_IP, 0));
break;
+ default: // fall out
}
}
}
/**
- * Simple data holder for a USB device. Used to represent a tuner model, and compare
- * against {@link UsbDevice}.
+ * Simple data holder for a USB device. Used to represent a tuner model, and compare against
+ * {@link UsbDevice}.
*/
private static class TunerDevice {
private final int vendorId;
@@ -331,7 +500,7 @@ public class TunerInputController {
this.minSecurityLevel = minSecurityLevel;
}
- private boolean equals(UsbDevice device) {
+ private boolean equalsTo(UsbDevice device) {
return device.getVendorId() == vendorId && device.getProductId() == productId;
}
@@ -354,10 +523,13 @@ public class TunerInputController {
}
private static class CheckDvbDeviceHandler extends Handler {
+
+ private final TunerInputController mTunerInputController;
private DvbDeviceAccessor mDvbDeviceAccessor;
- CheckDvbDeviceHandler() {
+ CheckDvbDeviceHandler(TunerInputController tunerInputController) {
super(Looper.getMainLooper());
+ this.mTunerInputController = tunerInputController;
}
@Override
@@ -369,9 +541,15 @@ public class TunerInputController {
mDvbDeviceAccessor = new DvbDeviceAccessor(context);
}
boolean enabled = mDvbDeviceAccessor.isDvbDeviceAvailable();
- enableTunerTvInputService(
- context, enabled, false, enabled ? TunerHal.TUNER_TYPE_USB : null);
+ mTunerInputController.handleTunerStatusChanged(
+ context,
+ false,
+ enabled
+ ? mTunerInputController.getConnectedUsbTuners(context)
+ : Collections.emptySet(),
+ TunerHal.TUNER_TYPE_USB);
break;
+ default: // fall out
}
}
}
diff --git a/src/com/android/tv/tuner/TunerPreferences.java b/src/com/android/tv/tuner/TunerPreferences.java
deleted file mode 100644
index 11a6a969..00000000
--- a/src/com/android/tv/tuner/TunerPreferences.java
+++ /dev/null
@@ -1,428 +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.tuner;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.Handler;
-import android.support.annotation.GuardedBy;
-import android.support.annotation.IntDef;
-import android.support.annotation.MainThread;
-
-import com.android.tv.common.SoftPreconditions;
-import com.android.tv.tuner.TunerPreferenceProvider.Preferences;
-import com.android.tv.tuner.util.TisConfiguration;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * A helper class for the USB tuner preferences.
- */
-public class TunerPreferences {
- private static final String TAG = "TunerPreferences";
-
- 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_LAST_POSTAL_CODE = "last_postal_code";
- private static final String PREFS_KEY_SCAN_DONE = "scan_done";
- private static final String PREFS_KEY_LAUNCH_SETUP = "launch_setup";
- private static final String PREFS_KEY_STORE_TS_STREAM = "store_ts_stream";
- private static final String PREFS_KEY_TRICKPLAY_SETTING = "trickplay_setting";
- private static final String PREFS_KEY_TRICKPLAY_EXPIRED_MS = "trickplay_expired_ms";
-
- private static final String SHARED_PREFS_NAME = "com.android.tv.tuner.preferences";
-
- public static final int CHANNEL_DATA_VERSION_NOT_SET = -1;
-
- @IntDef({TRICKPLAY_SETTING_NOT_SET, TRICKPLAY_SETTING_DISABLED, TRICKPLAY_SETTING_ENABLED})
- @Retention(RetentionPolicy.SOURCE)
- public @interface TrickplaySetting {
- }
-
- /**
- * Trickplay setting is not changed by a user. Trickplay will be enabled in this case.
- */
- public static final int TRICKPLAY_SETTING_NOT_SET = -1;
-
- /**
- * Trickplay setting is disabled.
- */
- public static final int TRICKPLAY_SETTING_DISABLED = 0;
-
- /**
- * Trickplay setting is enabled.
- */
- public static final int TRICKPLAY_SETTING_ENABLED = 1;
-
- @GuardedBy("TunerPreferences.class")
- private static final Bundle sPreferenceValues = new Bundle();
- private static LoadPreferencesTask sLoadPreferencesTask;
- private static ContentObserver sContentObserver;
- private static TunerPreferencesChangedListener sPreferencesChangedListener = null;
-
- private static boolean sInitialized;
-
- /**
- * Listeners for TunerPreferences change.
- */
- public interface TunerPreferencesChangedListener {
- void onTunerPreferencesChanged();
- }
-
- /**
- * Initializes the USB tuner preferences.
- */
- @MainThread
- public static void initialize(final Context context) {
- if (sInitialized) {
- return;
- }
- sInitialized = true;
- if (useContentProvider(context)) {
- loadPreferences(context);
- sContentObserver = new ContentObserver(new Handler()) {
- @Override
- public void onChange(boolean selfChange) {
- loadPreferences(context);
- }
- };
- context.getContentResolver().registerContentObserver(
- TunerPreferenceProvider.Preferences.CONTENT_URI, true, sContentObserver);
- } else {
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- getSharedPreferences(context);
- return null;
- }
- }.execute();
- }
- }
-
- /**
- * Releases the resources.
- */
- public static synchronized void release(Context context) {
- if (useContentProvider(context) && sContentObserver != null) {
- context.getContentResolver().unregisterContentObserver(sContentObserver);
- }
- setTunerPreferencesChangedListener(null);
- }
-
- /**
- * Sets the listener for TunerPreferences change.
- */
- public static void setTunerPreferencesChangedListener(
- TunerPreferencesChangedListener listener) {
- sPreferencesChangedListener = listener;
- }
-
- /**
- * Loads the preferences from database.
- * <p>
- * This preferences is used across processes, so the preferences should be loaded again when the
- * databases changes.
- */
- @MainThread
- public static void loadPreferences(Context context) {
- if (sLoadPreferencesTask != null
- && sLoadPreferencesTask.getStatus() != AsyncTask.Status.FINISHED) {
- sLoadPreferencesTask.cancel(true);
- }
- sLoadPreferencesTask = new LoadPreferencesTask(context);
- sLoadPreferencesTask.execute();
- }
-
- 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);
- }
-
- public static synchronized int getChannelDataVersion(Context context) {
- SoftPreconditions.checkState(sInitialized);
- if (useContentProvider(context)) {
- return sPreferenceValues.getInt(PREFS_KEY_CHANNEL_DATA_VERSION,
- CHANNEL_DATA_VERSION_NOT_SET);
- } else {
- return getSharedPreferences(context)
- .getInt(TunerPreferences.PREFS_KEY_CHANNEL_DATA_VERSION,
- CHANNEL_DATA_VERSION_NOT_SET);
- }
- }
-
- public static synchronized void setChannelDataVersion(Context context, int version) {
- if (useContentProvider(context)) {
- setPreference(context, PREFS_KEY_CHANNEL_DATA_VERSION, version);
- } else {
- getSharedPreferences(context).edit()
- .putInt(TunerPreferences.PREFS_KEY_CHANNEL_DATA_VERSION, version)
- .apply();
- }
- }
-
- public static synchronized int getScannedChannelCount(Context context) {
- SoftPreconditions.checkState(sInitialized);
- if (useContentProvider(context)) {
- return sPreferenceValues.getInt(PREFS_KEY_SCANNED_CHANNEL_COUNT);
- } else {
- return getSharedPreferences(context)
- .getInt(TunerPreferences.PREFS_KEY_SCANNED_CHANNEL_COUNT, 0);
- }
- }
-
- public static synchronized void setScannedChannelCount(Context context, int channelCount) {
- if (useContentProvider(context)) {
- setPreference(context, PREFS_KEY_SCANNED_CHANNEL_COUNT, channelCount);
- } else {
- getSharedPreferences(context).edit()
- .putInt(TunerPreferences.PREFS_KEY_SCANNED_CHANNEL_COUNT, channelCount)
- .apply();
- }
- }
-
- public static synchronized String getLastPostalCode(Context context) {
- SoftPreconditions.checkState(sInitialized);
- if (useContentProvider(context)) {
- return sPreferenceValues.getString(PREFS_KEY_LAST_POSTAL_CODE);
- } else {
- return getSharedPreferences(context).getString(PREFS_KEY_LAST_POSTAL_CODE, null);
- }
- }
-
- public static synchronized void setLastPostalCode(Context context, String postalCode) {
- if (useContentProvider(context)) {
- setPreference(context, PREFS_KEY_LAST_POSTAL_CODE, postalCode);
- } else {
- getSharedPreferences(context).edit()
- .putString(PREFS_KEY_LAST_POSTAL_CODE, postalCode).apply();
- }
- }
-
- public static synchronized boolean isScanDone(Context context) {
- SoftPreconditions.checkState(sInitialized);
- if (useContentProvider(context)) {
- return sPreferenceValues.getBoolean(PREFS_KEY_SCAN_DONE);
- } else {
- return getSharedPreferences(context)
- .getBoolean(TunerPreferences.PREFS_KEY_SCAN_DONE, false);
- }
- }
-
- public static synchronized void setScanDone(Context context) {
- if (useContentProvider(context)) {
- setPreference(context, PREFS_KEY_SCAN_DONE, true);
- } else {
- getSharedPreferences(context).edit()
- .putBoolean(TunerPreferences.PREFS_KEY_SCAN_DONE, true)
- .apply();
- }
- }
-
- public static synchronized boolean shouldShowSetupActivity(Context context) {
- SoftPreconditions.checkState(sInitialized);
- if (useContentProvider(context)) {
- return sPreferenceValues.getBoolean(PREFS_KEY_LAUNCH_SETUP);
- } else {
- return getSharedPreferences(context)
- .getBoolean(TunerPreferences.PREFS_KEY_LAUNCH_SETUP, false);
- }
- }
-
- public static synchronized void setShouldShowSetupActivity(Context context, boolean need) {
- if (useContentProvider(context)) {
- setPreference(context, PREFS_KEY_LAUNCH_SETUP, need);
- } else {
- getSharedPreferences(context).edit()
- .putBoolean(TunerPreferences.PREFS_KEY_LAUNCH_SETUP, need)
- .apply();
- }
- }
-
- public static synchronized long getTrickplayExpiredMs(Context context) {
- SoftPreconditions.checkState(sInitialized);
- if (useContentProvider(context)) {
- return sPreferenceValues.getLong(PREFS_KEY_TRICKPLAY_EXPIRED_MS, 0);
- } else {
- return getSharedPreferences(context)
- .getLong(TunerPreferences.PREFS_KEY_TRICKPLAY_EXPIRED_MS, 0);
- }
- }
-
- public static synchronized void setTrickplayExpiredMs(Context context, long timeMs) {
- if (useContentProvider(context)) {
- setPreference(context, PREFS_KEY_TRICKPLAY_EXPIRED_MS, timeMs);
- } else {
- getSharedPreferences(context).edit()
- .putLong(TunerPreferences.PREFS_KEY_TRICKPLAY_EXPIRED_MS, timeMs)
- .apply();
- }
- }
-
- public static synchronized @TrickplaySetting int getTrickplaySetting(Context context) {
- SoftPreconditions.checkState(sInitialized);
- if (useContentProvider(context)) {
- return sPreferenceValues.getInt(PREFS_KEY_TRICKPLAY_SETTING, TRICKPLAY_SETTING_NOT_SET);
- } else {
- return getSharedPreferences(context)
- .getInt(TunerPreferences.PREFS_KEY_TRICKPLAY_SETTING, TRICKPLAY_SETTING_NOT_SET);
- }
- }
-
- public static synchronized void setTrickplaySetting(Context context,
- @TrickplaySetting int trickplaySetting) {
- SoftPreconditions.checkState(sInitialized);
- SoftPreconditions.checkArgument(trickplaySetting != TRICKPLAY_SETTING_NOT_SET);
- if (useContentProvider(context)) {
- setPreference(context, PREFS_KEY_TRICKPLAY_SETTING, trickplaySetting);
- } else {
- getSharedPreferences(context).edit()
- .putInt(TunerPreferences.PREFS_KEY_TRICKPLAY_SETTING, trickplaySetting)
- .apply();
- }
- }
-
- public static synchronized boolean getStoreTsStream(Context context) {
- SoftPreconditions.checkState(sInitialized);
- if (useContentProvider(context)) {
- return sPreferenceValues.getBoolean(PREFS_KEY_STORE_TS_STREAM, false);
- } else {
- return getSharedPreferences(context)
- .getBoolean(TunerPreferences.PREFS_KEY_STORE_TS_STREAM, false);
- }
- }
-
- public static synchronized void setStoreTsStream(Context context, boolean shouldStore) {
- if (useContentProvider(context)) {
- setPreference(context, PREFS_KEY_STORE_TS_STREAM, shouldStore);
- } else {
- getSharedPreferences(context).edit()
- .putBoolean(TunerPreferences.PREFS_KEY_STORE_TS_STREAM, shouldStore)
- .apply();
- }
- }
-
- private static SharedPreferences getSharedPreferences(Context context) {
- return context.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE);
- }
-
- private static synchronized void setPreference(Context context, String key, String value) {
- sPreferenceValues.putString(key, value);
- savePreference(context, key, value);
- }
-
- private static synchronized void setPreference(Context context, String key, int value) {
- sPreferenceValues.putInt(key, value);
- savePreference(context, key, Integer.toString(value));
- }
-
- private static synchronized void setPreference(Context context, String key, long value) {
- sPreferenceValues.putLong(key, value);
- savePreference(context, key, Long.toString(value));
- }
-
- private static synchronized void setPreference(Context context, String key, boolean value) {
- sPreferenceValues.putBoolean(key, value);
- savePreference(context, key, Boolean.toString(value));
- }
-
- private static void savePreference(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 class LoadPreferencesTask extends AsyncTask<Void, Void, Bundle> {
- private final Context mContext;
- private LoadPreferencesTask(Context context) {
- mContext = context;
- }
-
- @Override
- protected Bundle doInBackground(Void... params) {
- Bundle bundle = new Bundle();
- ContentResolver resolver = mContext.getContentResolver();
- String[] projection = new String[] { Preferences.COLUMN_KEY, Preferences.COLUMN_VALUE };
- try (Cursor cursor = resolver.query(Preferences.CONTENT_URI, projection, null, null,
- null)) {
- if (cursor != null) {
- while (!isCancelled() && cursor.moveToNext()) {
- String key = cursor.getString(0);
- String value = cursor.getString(1);
- switch (key) {
- case PREFS_KEY_TRICKPLAY_EXPIRED_MS:
- bundle.putLong(key, Long.parseLong(value));
- break;
- case PREFS_KEY_CHANNEL_DATA_VERSION:
- case PREFS_KEY_SCANNED_CHANNEL_COUNT:
- case PREFS_KEY_TRICKPLAY_SETTING:
- try {
- bundle.putInt(key, Integer.parseInt(value));
- } catch (NumberFormatException e) {
- // Does nothing.
- }
- break;
- case PREFS_KEY_SCAN_DONE:
- case PREFS_KEY_LAUNCH_SETUP:
- case PREFS_KEY_STORE_TS_STREAM:
- bundle.putBoolean(key, Boolean.parseBoolean(value));
- break;
- case PREFS_KEY_LAST_POSTAL_CODE:
- bundle.putString(key, value);
- break;
- }
- }
- }
- } catch (Exception e) {
- SoftPreconditions.warn(TAG, "getPreference", "Error querying preference values", e);
- return null;
- }
- return bundle;
- }
-
- @Override
- protected void onPostExecute(Bundle bundle) {
- synchronized (TunerPreferences.class) {
- if (bundle != null) {
- sPreferenceValues.putAll(bundle);
- }
- }
- if (sPreferencesChangedListener != null) {
- sPreferencesChangedListener.onTunerPreferencesChanged();
- }
- }
- }
-} \ No newline at end of file
diff --git a/src/com/android/tv/tuner/exoplayer/ffmpeg/FfmpegDecoderClient.java b/src/com/android/tv/tuner/exoplayer/ffmpeg/FfmpegDecoderClient.java
deleted file mode 100644
index 356636cc..00000000
--- a/src/com/android/tv/tuner/exoplayer/ffmpeg/FfmpegDecoderClient.java
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * Copyright (C) 2017 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.tuner.exoplayer.ffmpeg;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.IBinder;
-import android.os.RemoteException;
-
-import android.support.annotation.MainThread;
-import android.support.annotation.WorkerThread;
-import android.support.annotation.VisibleForTesting;
-import com.google.android.exoplayer.SampleHolder;
-import com.android.tv.Features;
-import com.android.tv.tuner.exoplayer.audio.AudioDecoder;
-
-import java.nio.ByteBuffer;
-
-/**
- * The class connects {@link FfmpegDecoderService} to decode audio samples.
- * In order to sandbox ffmpeg based decoder, {@link FfmpegDecoderService} is an isolated process
- * without any permission and connected by binder.
- */
-public class FfmpegDecoderClient extends AudioDecoder {
- private static FfmpegDecoderClient sInstance;
-
- private IFfmpegDecoder mService;
- private Boolean mIsAvailable;
-
- private static final String FFMPEG_DECODER_SERVICE_FILTER =
- "com.android.tv.tuner.exoplayer.ffmpeg.IFfmpegDecoder";
- private static final long FFMPEG_SERVICE_CONNECT_TIMEOUT_MS = 500;
-
- private final ServiceConnection mConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName className, IBinder service) {
- mService = IFfmpegDecoder.Stub.asInterface(service);
- synchronized (FfmpegDecoderClient.this) {
- try {
- mIsAvailable = mService.isAvailable();
- } catch (RemoteException e) {
- }
- FfmpegDecoderClient.this.notify();
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName className) {
- synchronized (FfmpegDecoderClient.this) {
- sInstance.releaseLocked();
- mIsAvailable = false;
- mService = null;
- }
- }
- };
-
- /**
- * Connects to the decoder service for future uses.
- * @param context
- * @return {@code true} when decoder service is connected.
- */
- @MainThread
- public synchronized static boolean connect(Context context) {
- if (Features.AC3_SOFTWARE_DECODE.isEnabled(context)) {
- if (sInstance == null) {
- sInstance = new FfmpegDecoderClient();
- Intent intent =
- new Intent(FFMPEG_DECODER_SERVICE_FILTER)
- .setComponent(
- new ComponentName(context, FfmpegDecoderService.class));
- if (context.bindService(intent, sInstance.mConnection, Context.BIND_AUTO_CREATE)) {
- return true;
- } else {
- sInstance = null;
- }
- }
- }
- return false;
- }
-
- /**
- * Disconnects from the decoder service and release resources.
- * @param context
- */
- @MainThread
- public synchronized static void disconnect(Context context) {
- if (sInstance != null) {
- synchronized (sInstance) {
- sInstance.releaseLocked();
- if (sInstance.mIsAvailable != null && sInstance.mIsAvailable) {
- context.unbindService(sInstance.mConnection);
- }
- sInstance.mIsAvailable = false;
- sInstance.mService = null;
- }
- sInstance = null;
- }
- }
-
- /**
- * Returns whether service is available or not.
- * Before using client, this should be used to check availability.
- */
- @WorkerThread
- public synchronized static boolean isAvailable() {
- if (sInstance != null) {
- return sInstance.available();
- }
- return false;
- }
-
- /**
- * Returns an client instance.
- */
- public synchronized static FfmpegDecoderClient getInstance() {
- if (sInstance != null) {
- sInstance.createDecoder();
- }
- return sInstance;
- }
-
- private FfmpegDecoderClient() {
- }
-
- private synchronized boolean available() {
- if (mIsAvailable == null) {
- try {
- this.wait(FFMPEG_SERVICE_CONNECT_TIMEOUT_MS);
- } catch (InterruptedException e) {
- }
- }
- return mIsAvailable != null && mIsAvailable == true;
- }
-
- private synchronized void createDecoder() {
- if (mIsAvailable == null || mIsAvailable == false) {
- return;
- }
- try {
- mService.create();
- } catch (RemoteException e) {
- }
- }
-
- private void releaseLocked() {
- if (mIsAvailable == null || mIsAvailable == false) {
- return;
- }
- try {
- mService.release();
- } catch (RemoteException e) {
- }
- }
-
- @Override
- public synchronized void release() {
- releaseLocked();
- }
-
- @Override
- public synchronized void decode(SampleHolder sampleHolder) {
- if (mIsAvailable == null || mIsAvailable == false) {
- return;
- }
- byte[] sampleBytes = new byte [sampleHolder.data.limit()];
- sampleHolder.data.get(sampleBytes, 0, sampleBytes.length);
- try {
- mService.decode(sampleHolder.timeUs, sampleBytes);
- } catch (RemoteException e) {
- }
- }
-
- @Override
- public synchronized void resetDecoderState(String mimeType) {
- if (mIsAvailable == null || mIsAvailable == false) {
- return;
- }
- try {
- mService.resetDecoderState(mimeType);
- } catch (RemoteException e) {
- }
- }
-
- @Override
- public synchronized ByteBuffer getDecodedSample() {
- if (mIsAvailable == null || mIsAvailable == false) {
- return null;
- }
- try {
- byte[] outputBytes = mService.getDecodedSample();
- if (outputBytes != null && outputBytes.length > 0) {
- return ByteBuffer.wrap(outputBytes);
- }
- } catch (RemoteException e) {
- }
- return null;
- }
-
- @Override
- public synchronized long getDecodedTimeUs() {
- if (mIsAvailable == null || mIsAvailable == false) {
- return 0;
- }
- try {
- return mService.getDecodedTimeUs();
- } catch (RemoteException e) {
- }
- return 0;
- }
-
- @VisibleForTesting
- public boolean testSandboxIsolatedProcess() {
- // When testing isolated process, we will check the permission in FfmpegDecoderService.
- // If the service have any permission, an exception will be thrown.
- try {
- mService.testSandboxIsolatedProcess();
- } catch (RemoteException e) {
- return false;
- }
- return true;
- }
-
- @VisibleForTesting
- public void testSandboxMinijail() {
- // When testing minijail, we will call a system call which is blocked by minijail. In that
- // case, the FfmpegDecoderService will be disconnected, we can check the connection status
- // to make sure if the minijail works or not.
- try {
- mService.testSandboxMinijail();
- } catch (RemoteException e) {
- }
- }
-}
diff --git a/src/com/android/tv/tuner/exoplayer/ffmpeg/FfmpegDecoderService.java b/src/com/android/tv/tuner/exoplayer/ffmpeg/FfmpegDecoderService.java
deleted file mode 100644
index 3ebdd381..00000000
--- a/src/com/android/tv/tuner/exoplayer/ffmpeg/FfmpegDecoderService.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2017 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.tuner.exoplayer.ffmpeg;
-
-import android.app.Service;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.res.AssetFileDescriptor;
-import android.os.AsyncTask;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-
-import com.google.android.exoplayer2.ext.ffmpeg.FfmpegAudioDecoder;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-/**
- * Ffmpeg based audio decoder service.
- * It should be isolatedProcess due to security reason.
- */
-public class FfmpegDecoderService extends Service {
- private static final String TAG = "FfmpegDecoderService";
- private static final boolean DEBUG = false;
-
- private static final String POLICY_FILE = "whitelist.policy";
-
- private static final long MINIJAIL_SETUP_WAIT_TIMEOUT_MS = 5000;
-
- private static boolean sLibraryLoaded = true;
-
- static {
- try {
- System.loadLibrary("minijail_jni");
- } catch (Exception | Error e) {
- Log.e(TAG, "Load minijail failed:", e);
- sLibraryLoaded = false;
- }
- }
-
- private FfmpegDecoder mBinder = new FfmpegDecoder();
- private volatile Object mMinijailSetupMonitor = new Object();
- //@GuardedBy("mMinijailSetupMonitor")
- private volatile Boolean mMinijailSetup;
-
- @Override
- public void onCreate() {
- if (sLibraryLoaded) {
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- synchronized (mMinijailSetupMonitor) {
- int pipeFd = getPolicyPipeFd();
- if (pipeFd <= 0) {
- Log.e(TAG, "fail to open policy file");
- mMinijailSetup = false;
- } else {
- nativeSetupMinijail(pipeFd);
- mMinijailSetup = true;
- if (DEBUG) Log.d(TAG, "Minijail setup successfully");
- }
- mMinijailSetupMonitor.notify();
- }
- return null;
- }
- }.execute();
- } else {
- synchronized (mMinijailSetupMonitor) {
- mMinijailSetup = false;
- mMinijailSetupMonitor.notify();
- }
- }
- super.onCreate();
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return mBinder;
- }
-
- private int getPolicyPipeFd() {
- try {
- ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
- final ParcelFileDescriptor.AutoCloseOutputStream outputStream =
- new ParcelFileDescriptor.AutoCloseOutputStream(pipe[1]);
- final AssetFileDescriptor policyFile = getAssets().openFd("whitelist.policy");
- final byte[] buffer = new byte[2048];
- final FileInputStream policyStream = policyFile.createInputStream();
- while (true) {
- int bytesRead = policyStream.read(buffer);
- if (bytesRead == -1) break;
- outputStream.write(buffer, 0, bytesRead);
- }
- policyStream.close();
- outputStream.close();
- return pipe[0].detachFd();
- } catch (IOException e) {
- Log.e(TAG, "Policy file not found:" + e);
- }
- return -1;
- }
-
- private final class FfmpegDecoder extends IFfmpegDecoder.Stub {
- FfmpegAudioDecoder mDecoder;
- @Override
- public boolean isAvailable() {
- return isMinijailSetupDone() && FfmpegAudioDecoder.isAvailable();
- }
-
- @Override
- public void create() {
- mDecoder = new FfmpegAudioDecoder(FfmpegDecoderService.this);
- }
-
- @Override
- public void release() {
- if (mDecoder != null) {
- mDecoder.release();
- mDecoder = null;
- }
- }
-
- @Override
- public void decode(long timeUs, byte[] sample) {
- if (!isMinijailSetupDone()) {
- // If minijail is not setup, we don't run decode for better security.
- return;
- }
- mDecoder.decode(timeUs, sample);
- }
-
- @Override
- public void resetDecoderState(String mimetype) {
- mDecoder.resetDecoderState(mimetype);
- }
-
- @Override
- public byte[] getDecodedSample() {
- ByteBuffer decodedBuffer = mDecoder.getDecodedSample();
- byte[] ret = new byte[decodedBuffer.limit()];
- decodedBuffer.get(ret, 0, ret.length);
- return ret;
- }
-
- @Override
- public long getDecodedTimeUs() {
- return mDecoder.getDecodedTimeUs();
- }
-
- private boolean isMinijailSetupDone() {
- synchronized (mMinijailSetupMonitor) {
- if (DEBUG) Log.d(TAG, "mMinijailSetup in isAvailable(): " + mMinijailSetup);
- if (mMinijailSetup == null) {
- try {
- if (DEBUG) Log.d(TAG, "Wait till Minijail setup is done");
- mMinijailSetupMonitor.wait(MINIJAIL_SETUP_WAIT_TIMEOUT_MS);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- }
- return mMinijailSetup != null && mMinijailSetup;
- }
- }
-
- @Override
- public void testSandboxIsolatedProcess() {
- if (!isMinijailSetupDone()) {
- // If minijail is not setup, we return directly to make the test fail.
- return;
- }
- if (FfmpegDecoderService.this.checkSelfPermission("android.permission.INTERNET")
- == PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Shouldn't have the permission of internet");
- }
- }
-
- @Override
- public void testSandboxMinijail() {
- if (!isMinijailSetupDone()) {
- // If minijail is not setup, we return directly to make the test fail.
- return;
- }
- nativeTestMinijail();
- }
- }
-
- private native void nativeSetupMinijail(int policyFd);
- private native void nativeTestMinijail();
-}
diff --git a/src/com/android/tv/tuner/tvinput/EventDetector.java b/src/com/android/tv/tuner/tvinput/EventDetector.java
deleted file mode 100644
index dc99118a..00000000
--- a/src/com/android/tv/tuner/tvinput/EventDetector.java
+++ /dev/null
@@ -1,334 +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.tuner.tvinput;
-
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-
-import com.android.tv.tuner.TunerHal;
-import com.android.tv.tuner.ts.TsParser;
-import com.android.tv.tuner.data.PsiData;
-import com.android.tv.tuner.data.PsipData;
-import com.android.tv.tuner.data.TunerChannel;
-import com.android.tv.tuner.data.nano.Track.AtscAudioTrack;
-import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Detects channels and programs that are emerged or changed while parsing ATSC PSIP information.
- */
-public class EventDetector {
- private static final String TAG = "EventDetector";
- private static final boolean DEBUG = false;
- public static final int ALL_PROGRAM_NUMBERS = -1;
-
- private final TunerHal mTunerHal;
-
- private TsParser mTsParser;
- private final Set<Integer> mPidSet = new HashSet<>();
-
- // To prevent channel duplication
- private final Set<Integer> mVctProgramNumberSet = new HashSet<>();
- private final Set<Integer> mSdtProgramNumberSet = new HashSet<>();
- private final SparseArray<TunerChannel> mChannelMap = new SparseArray<>();
- private final SparseBooleanArray mVctCaptionTracksFound = new SparseBooleanArray();
- private final SparseBooleanArray mEitCaptionTracksFound = new SparseBooleanArray();
- private final List<EventListener> mEventListeners = new ArrayList<>();
- private int mFrequency;
- private String mModulation;
- private int mProgramNumber = ALL_PROGRAM_NUMBERS;
-
- private final TsParser.TsOutputListener mTsOutputListener = new TsParser.TsOutputListener() {
- @Override
- public void onPatDetected(List<PsiData.PatItem> items) {
- for (PsiData.PatItem i : items) {
- if (mProgramNumber == ALL_PROGRAM_NUMBERS || mProgramNumber == i.getProgramNo()) {
- mTunerHal.addPidFilter(i.getPmtPid(), TunerHal.FILTER_TYPE_OTHER);
- }
- }
- }
-
- @Override
- public void onEitPidDetected(int pid) {
- startListening(pid);
- }
-
- @Override
- public void onEitItemParsed(PsipData.VctItem channel, List<PsipData.EitItem> items) {
- TunerChannel tunerChannel = mChannelMap.get(channel.getProgramNumber());
- if (DEBUG) {
- Log.d(TAG, "onEitItemParsed tunerChannel:" + tunerChannel + " "
- + channel.getProgramNumber());
- }
- int channelSourceId = channel.getSourceId();
-
- // Source id 0 is useful for cases where a cable operator wishes to define a channel for
- // which no EPG data is currently available.
- // We don't handle such a case.
- if (channelSourceId == 0) {
- return;
- }
-
- // If at least a one caption track have been found in EIT items for the given channel,
- // we starts to interpret the zero tracks as a clearance of the caption tracks.
- boolean captionTracksFound = mEitCaptionTracksFound.get(channelSourceId);
- for (PsipData.EitItem item : items) {
- if (captionTracksFound) {
- break;
- }
- List<AtscCaptionTrack> captionTracks = item.getCaptionTracks();
- if (captionTracks != null && !captionTracks.isEmpty()) {
- captionTracksFound = true;
- }
- }
- mEitCaptionTracksFound.put(channelSourceId, captionTracksFound);
- if (captionTracksFound) {
- for (PsipData.EitItem item : items) {
- item.setHasCaptionTrack();
- }
- }
- if (tunerChannel != null && !mEventListeners.isEmpty()) {
- for (EventListener eventListener : mEventListeners) {
- eventListener.onEventDetected(tunerChannel, items);
- }
- }
- }
-
- @Override
- public void onEttPidDetected(int pid) {
- startListening(pid);
- }
-
- @Override
- public void onAllVctItemsParsed() {
- if (!mEventListeners.isEmpty()) {
- for (EventListener eventListener : mEventListeners) {
- eventListener.onChannelScanDone();
- }
- }
- }
-
- @Override
- public void onVctItemParsed(PsipData.VctItem channel, List<PsiData.PmtItem> pmtItems) {
- if (DEBUG) {
- Log.d(TAG, "onVctItemParsed VCT " + channel);
- Log.d(TAG, " PMT " + pmtItems);
- }
-
- // Merges the audio and caption tracks located in PMT items into the tracks of the given
- // tuner channel.
- TunerChannel tunerChannel = new TunerChannel(channel, pmtItems);
- List<AtscAudioTrack> audioTracks = new ArrayList<>();
- List<AtscCaptionTrack> captionTracks = new ArrayList<>();
- for (PsiData.PmtItem pmtItem : pmtItems) {
- if (pmtItem.getAudioTracks() != null) {
- audioTracks.addAll(pmtItem.getAudioTracks());
- }
- if (pmtItem.getCaptionTracks() != null) {
- captionTracks.addAll(pmtItem.getCaptionTracks());
- }
- }
- int channelProgramNumber = channel.getProgramNumber();
-
- // If at least a one caption track have been found in VCT items for the given channel,
- // we starts to interpret the zero tracks as a clearance of the caption tracks.
- boolean captionTracksFound = mVctCaptionTracksFound.get(channelProgramNumber)
- || !captionTracks.isEmpty();
- mVctCaptionTracksFound.put(channelProgramNumber, captionTracksFound);
- if (captionTracksFound) {
- tunerChannel.setHasCaptionTrack();
- }
- tunerChannel.setAudioTracks(audioTracks);
- tunerChannel.setCaptionTracks(captionTracks);
- tunerChannel.setFrequency(mFrequency);
- tunerChannel.setModulation(mModulation);
- mChannelMap.put(tunerChannel.getProgramNumber(), tunerChannel);
- boolean found = mVctProgramNumberSet.contains(channelProgramNumber);
- if (!found) {
- mVctProgramNumberSet.add(channelProgramNumber);
- }
- if (!mEventListeners.isEmpty()) {
- for (EventListener eventListener : mEventListeners) {
- eventListener.onChannelDetected(tunerChannel, !found);
- }
- }
- }
-
- @Override
- public void onSdtItemParsed(PsipData.SdtItem channel, List<PsiData.PmtItem> pmtItems) {
- if (DEBUG) {
- Log.d(TAG, "onSdtItemParsed SDT " + channel);
- Log.d(TAG, " PMT " + pmtItems);
- }
-
- // Merges the audio and caption tracks located in PMT items into the tracks of the given
- // tuner channel.
- TunerChannel tunerChannel = new TunerChannel(channel, pmtItems);
- List<AtscAudioTrack> audioTracks = new ArrayList<>();
- List<AtscCaptionTrack> captionTracks = new ArrayList<>();
- for (PsiData.PmtItem pmtItem : pmtItems) {
- if (pmtItem.getAudioTracks() != null) {
- audioTracks.addAll(pmtItem.getAudioTracks());
- }
- if (pmtItem.getCaptionTracks() != null) {
- captionTracks.addAll(pmtItem.getCaptionTracks());
- }
- }
- int channelProgramNumber = channel.getServiceId();
- tunerChannel.setAudioTracks(audioTracks);
- tunerChannel.setCaptionTracks(captionTracks);
- tunerChannel.setFrequency(mFrequency);
- tunerChannel.setModulation(mModulation);
- mChannelMap.put(tunerChannel.getProgramNumber(), tunerChannel);
- boolean found = mSdtProgramNumberSet.contains(channelProgramNumber);
- if (!found) {
- mSdtProgramNumberSet.add(channelProgramNumber);
- }
- if (!mEventListeners.isEmpty()) {
- for (EventListener eventListener : mEventListeners) {
- eventListener.onChannelDetected(tunerChannel, !found);
- }
- }
- }
- };
-
- /**
- * Listener for detecting ATSC TV channels and receiving EPG data.
- */
- public interface EventListener {
-
- /**
- * Fired when new information of an ATSC TV channel arrived.
- *
- * @param channel an ATSC TV channel
- * @param channelArrivedAtFirstTime tells whether this channel arrived at first time
- */
- void onChannelDetected(TunerChannel channel, boolean channelArrivedAtFirstTime);
-
- /**
- * Fired when new program events of an ATSC TV channel arrived.
- *
- * @param channel an ATSC TV channel
- * @param items a list of EIT items that were received
- */
- void onEventDetected(TunerChannel channel, List<PsipData.EitItem> items);
-
- /**
- * Fired when information of all detectable ATSC TV channels in current frequency arrived.
- */
- void onChannelScanDone();
- }
-
- /**
- * Creates a detector for ATSC TV channles and program information.
- *
- * @param usbTunerInteface {@link TunerHal}
- */
- public EventDetector(TunerHal usbTunerInteface) {
- mTunerHal = usbTunerInteface;
- }
-
- private void reset() {
- // TODO: Use TsParser.reset()
- int deliverySystemType = mTunerHal.getDeliverySystemType();
- mTsParser =
- new TsParser(
- mTsOutputListener,
- TunerHal.isDvbDeliverySystem(mTunerHal.getDeliverySystemType()));
- mPidSet.clear();
- mVctProgramNumberSet.clear();
- mSdtProgramNumberSet.clear();
- mVctCaptionTracksFound.clear();
- mEitCaptionTracksFound.clear();
- mChannelMap.clear();
- }
-
- /**
- * Starts detecting channel and program information.
- *
- * @param frequency The frequency to listen to.
- * @param modulation The modulation type.
- * @param programNumber The program number if this is for handling tune request. For scanning
- * purpose, supply {@link #ALL_PROGRAM_NUMBERS}.
- */
- public void startDetecting(int frequency, String modulation, int programNumber) {
- reset();
- mFrequency = frequency;
- mModulation = modulation;
- mProgramNumber = programNumber;
- }
-
- private void startListening(int pid) {
- if (mPidSet.contains(pid)) {
- return;
- }
- mPidSet.add(pid);
- mTunerHal.addPidFilter(pid, TunerHal.FILTER_TYPE_OTHER);
- }
-
- /**
- * Feeds ATSC TS stream to detect channel and program information.
- * @param data buffer for ATSC TS stream
- * @param startOffset the offset where buffer starts
- * @param length The length of available data
- */
- public void feedTSStream(byte[] data, int startOffset, int length) {
- if (mPidSet.isEmpty()) {
- startListening(TsParser.ATSC_SI_BASE_PID);
- }
- if (mTsParser != null) {
- mTsParser.feedTSData(data, startOffset, length);
- }
- }
-
- /**
- * Retrieves the channel information regardless of being well-formed.
- * @return {@link List} of {@link TunerChannel}
- */
- public List<TunerChannel> getMalFormedChannels() {
- return mTsParser.getMalFormedChannels();
- }
-
- /**
- * Registers an EventListener.
- * @param eventListener the listener to be registered
- */
- public void registerListener(EventListener eventListener) {
- if (mTsParser != null) {
- // Resets the version numbers so that the new listener can receive the EIT items.
- // Otherwise, each EIT session is handled only once unless there is a new version.
- mTsParser.resetDataVersions();
- }
- mEventListeners.add(eventListener);
- }
-
- /**
- * Unregisters an EventListener.
- * @param eventListener the listener to be unregistered
- */
- public void unregisterListener(EventListener eventListener) {
- boolean removed = mEventListeners.remove(eventListener);
- if (!removed && DEBUG) {
- Log.d(TAG, "Cannot unregister a non-registered listener!");
- }
- }
-}
diff --git a/src/com/android/tv/tuner/tvinput/FileSourceEventDetector.java b/src/com/android/tv/tuner/tvinput/FileSourceEventDetector.java
deleted file mode 100644
index 99222bf8..00000000
--- a/src/com/android/tv/tuner/tvinput/FileSourceEventDetector.java
+++ /dev/null
@@ -1,249 +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.tuner.tvinput;
-
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-
-import com.android.tv.tuner.data.PsiData.PatItem;
-import com.android.tv.tuner.data.PsiData.PmtItem;
-import com.android.tv.tuner.data.PsipData.EitItem;
-import com.android.tv.tuner.data.PsipData.SdtItem;
-import com.android.tv.tuner.data.PsipData.VctItem;
-import com.android.tv.tuner.data.TunerChannel;
-import com.android.tv.tuner.data.nano.Track.AtscAudioTrack;
-import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
-import com.android.tv.tuner.source.FileTsStreamer;
-import com.android.tv.tuner.ts.TsParser;
-import com.android.tv.tuner.tvinput.EventDetector.EventListener;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * PSIP event detector for a file source.
- *
- * <p>Uses {@link TsParser} to analyze input MPEG-2 transport stream, detects and reports
- * various PSIP-related events via {@link TsParser.TsOutputListener}.
- */
-public class FileSourceEventDetector {
- private static final String TAG = "FileSourceEventDetector";
- private static final boolean DEBUG = true;
- public static final int ALL_PROGRAM_NUMBERS = 0;
-
- private TsParser mTsParser;
- private final Set<Integer> mVctProgramNumberSet = new HashSet<>();
- private final Set<Integer> mSdtProgramNumberSet = new HashSet<>();
- private final SparseArray<TunerChannel> mChannelMap = new SparseArray<>();
- private final SparseBooleanArray mVctCaptionTracksFound = new SparseBooleanArray();
- private final SparseBooleanArray mEitCaptionTracksFound = new SparseBooleanArray();
- private final EventListener mEventListener;
- private final boolean mEnableDvbSignal;
- private FileTsStreamer.StreamProvider mStreamProvider;
- private int mProgramNumber = ALL_PROGRAM_NUMBERS;
-
- public FileSourceEventDetector(EventDetector.EventListener listener, boolean enableDvbSignal) {
- mEventListener = listener;
- mEnableDvbSignal = enableDvbSignal;
- }
-
- /**
- * Starts detecting channel and program information.
- *
- * @param provider MPEG-2 transport stream source.
- * @param programNumber The program number if this is for handling tune request. For scanning
- * purpose, supply {@link #ALL_PROGRAM_NUMBERS}.
- */
- public void start(FileTsStreamer.StreamProvider provider, int programNumber) {
- mStreamProvider = provider;
- mProgramNumber = programNumber;
- reset();
- }
-
- private void reset() {
- mTsParser = new TsParser(mTsOutputListener, mEnableDvbSignal); // TODO: Use TsParser.reset()
- mStreamProvider.clearPidFilter();
- mVctProgramNumberSet.clear();
- mSdtProgramNumberSet.clear();
- mVctCaptionTracksFound.clear();
- mEitCaptionTracksFound.clear();
- mChannelMap.clear();
- }
-
- public void feedTSStream(byte[] data, int startOffset, int length) {
- if (mStreamProvider.isFilterEmpty()) {
- startListening(TsParser.ATSC_SI_BASE_PID);
- startListening(TsParser.PAT_PID);
- }
- if (mTsParser != null) {
- mTsParser.feedTSData(data, startOffset, length);
- }
- }
-
- private void startListening(int pid) {
- if (mStreamProvider.isInFilter(pid)) {
- return;
- }
- mStreamProvider.addPidFilter(pid);
- }
-
- private final TsParser.TsOutputListener mTsOutputListener = new TsParser.TsOutputListener() {
- @Override
- public void onPatDetected(List<PatItem> items) {
- for (PatItem i : items) {
- if (mProgramNumber == ALL_PROGRAM_NUMBERS || mProgramNumber == i.getProgramNo()) {
- mStreamProvider.addPidFilter(i.getPmtPid());
- }
- }
- }
-
- @Override
- public void onEitPidDetected(int pid) {
- startListening(pid);
- }
-
- @Override
- public void onEitItemParsed(VctItem channel, List<EitItem> items) {
- TunerChannel tunerChannel = mChannelMap.get(channel.getProgramNumber());
- if (DEBUG) {
- Log.d(TAG, "onEitItemParsed tunerChannel:" + tunerChannel + " "
- + channel.getProgramNumber());
- }
- int channelSourceId = channel.getSourceId();
-
- // Source id 0 is useful for cases where a cable operator wishes to define a channel for
- // which no EPG data is currently available.
- // We don't handle such a case.
- if (channelSourceId == 0) {
- return;
- }
-
- // If at least a one caption track have been found in EIT items for the given channel,
- // we starts to interpret the zero tracks as a clearance of the caption tracks.
- boolean captionTracksFound = mEitCaptionTracksFound.get(channelSourceId);
- for (EitItem item : items) {
- if (captionTracksFound) {
- break;
- }
- List<AtscCaptionTrack> captionTracks = item.getCaptionTracks();
- if (captionTracks != null && !captionTracks.isEmpty()) {
- captionTracksFound = true;
- }
- }
- mEitCaptionTracksFound.put(channelSourceId, captionTracksFound);
- if (captionTracksFound) {
- for (EitItem item : items) {
- item.setHasCaptionTrack();
- }
- }
- if (tunerChannel != null && mEventListener != null) {
- mEventListener.onEventDetected(tunerChannel, items);
- }
- }
-
- @Override
- public void onEttPidDetected(int pid) {
- startListening(pid);
- }
-
- @Override
- public void onAllVctItemsParsed() {
- // do nothing.
- }
-
- @Override
- public void onVctItemParsed(VctItem channel, List<PmtItem> pmtItems) {
- if (DEBUG) {
- Log.d(TAG, "onVctItemParsed VCT " + channel);
- Log.d(TAG, " PMT " + pmtItems);
- }
-
- // Merges the audio and caption tracks located in PMT items into the tracks of the given
- // tuner channel.
- TunerChannel tunerChannel = TunerChannel.forFile(channel, pmtItems);
- List<AtscAudioTrack> audioTracks = new ArrayList<>();
- List<AtscCaptionTrack> captionTracks = new ArrayList<>();
- for (PmtItem pmtItem : pmtItems) {
- if (pmtItem.getAudioTracks() != null) {
- audioTracks.addAll(pmtItem.getAudioTracks());
- }
- if (pmtItem.getCaptionTracks() != null) {
- captionTracks.addAll(pmtItem.getCaptionTracks());
- }
- }
- int channelProgramNumber = channel.getProgramNumber();
-
- // If at least a one caption track have been found in VCT items for the given channel,
- // we starts to interpret the zero tracks as a clearance of the caption tracks.
- boolean captionTracksFound = mVctCaptionTracksFound.get(channelProgramNumber)
- || !captionTracks.isEmpty();
- mVctCaptionTracksFound.put(channelProgramNumber, captionTracksFound);
- if (captionTracksFound) {
- tunerChannel.setHasCaptionTrack();
- }
- tunerChannel.setFilepath(mStreamProvider.getFilepath());
- tunerChannel.setAudioTracks(audioTracks);
- tunerChannel.setCaptionTracks(captionTracks);
-
- mChannelMap.put(tunerChannel.getProgramNumber(), tunerChannel);
- boolean found = mVctProgramNumberSet.contains(channelProgramNumber);
- if (!found) {
- mVctProgramNumberSet.add(channelProgramNumber);
- }
- if (mEventListener != null) {
- mEventListener.onChannelDetected(tunerChannel, !found);
- }
- }
-
- @Override
- public void onSdtItemParsed(SdtItem channel, List<PmtItem> pmtItems) {
- if (DEBUG) {
- Log.d(TAG, "onSdtItemParsed SDT " + channel);
- Log.d(TAG, " PMT " + pmtItems);
- }
-
- // Merges the audio and caption tracks located in PMT items into the tracks of the given
- // tuner channel.
- TunerChannel tunerChannel = TunerChannel.forDvbFile(channel, pmtItems);
- List<AtscAudioTrack> audioTracks = new ArrayList<>();
- List<AtscCaptionTrack> captionTracks = new ArrayList<>();
- for (PmtItem pmtItem : pmtItems) {
- if (pmtItem.getAudioTracks() != null) {
- audioTracks.addAll(pmtItem.getAudioTracks());
- }
- if (pmtItem.getCaptionTracks() != null) {
- captionTracks.addAll(pmtItem.getCaptionTracks());
- }
- }
- int channelProgramNumber = channel.getServiceId();
- tunerChannel.setFilepath(mStreamProvider.getFilepath());
- tunerChannel.setAudioTracks(audioTracks);
- tunerChannel.setCaptionTracks(captionTracks);
- mChannelMap.put(tunerChannel.getProgramNumber(), tunerChannel);
- boolean found = mSdtProgramNumberSet.contains(channelProgramNumber);
- if (!found) {
- mSdtProgramNumberSet.add(channelProgramNumber);
- }
- if (mEventListener != null) {
- mEventListener.onChannelDetected(tunerChannel, !found);
- }
- }
- };
-}
diff --git a/src/com/android/tv/tuner/util/Ints.java b/src/com/android/tv/tuner/util/Ints.java
deleted file mode 100644
index 0b1be426..00000000
--- a/src/com/android/tv/tuner/util/Ints.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.android.tv.tuner.util;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Static utility methods pertaining to int primitives. (Referred Guava's Ints class)
- */
-public class Ints {
- private Ints() {}
-
- public static int[] toArray(List<Integer> integerList) {
- int[] intArray = new int[integerList.size()];
- int i = 0;
- for (Integer data : integerList) {
- intArray[i++] = data;
- }
- return intArray;
- }
-
- public static List<Integer> asList(int[] intArray) {
- List<Integer> integerList = new ArrayList<>(intArray.length);
- for (int data : intArray) {
- integerList.add(data);
- }
- return integerList;
- }
-}
diff --git a/src/com/android/tv/tuner/util/TisConfiguration.java b/src/com/android/tv/tuner/util/TisConfiguration.java
deleted file mode 100644
index ca861d67..00000000
--- a/src/com/android/tv/tuner/util/TisConfiguration.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.android.tv.tuner.util;
-
-import android.content.Context;
-
-/**
- * A helper class of tuner configuration.
- */
-public class TisConfiguration {
- private static final String LC_PACKAGE_NAME = "com.android.tv";
-
- public static boolean isPackagedWithLiveChannels(Context context) {
- return (LC_PACKAGE_NAME.equals(context.getPackageName()));
- }
-
- public static boolean isInternalTunerTvInput(Context context) {
- return (!LC_PACKAGE_NAME.equals(context.getPackageName()));
- }
-
- public static int getTunerHwDeviceId(Context context) {
- return 0; // FIXME: Make it OEM configurable
- }
-}
diff --git a/src/com/android/tv/ui/AppLayerTvView.java b/src/com/android/tv/ui/AppLayerTvView.java
index 625014ea..b2be9f02 100644
--- a/src/com/android/tv/ui/AppLayerTvView.java
+++ b/src/com/android/tv/ui/AppLayerTvView.java
@@ -21,18 +21,15 @@ import android.media.tv.TvView;
import android.util.AttributeSet;
import android.view.SurfaceView;
import android.view.View;
-
-import com.android.tv.util.Debug;
-import com.android.tv.util.Utils;
+import com.android.tv.common.util.CommonUtils;
+import com.android.tv.common.util.Debug;
/**
* A TvView class for application layer when multiple windows are being used in the app.
- * <p>
- * Once an app starts using additional window like SubPanel and it gets window focus, the
- * {@link android.media.tv.TvView#setMain()} does not work because its implementation assumes that
- * the app uses only application layer.
- * TODO: remove this class once the TvView.setMain() is revisited.
- * </p>
+ *
+ * <p>Once an app starts using additional window like SubPanel and it gets window focus, the {@link
+ * android.media.tv.TvView#setMain()} does not work because its implementation assumes that the app
+ * uses only application layer. TODO: remove this class once the TvView.setMain() is revisited.
*/
public class AppLayerTvView extends TvView {
public AppLayerTvView(Context context) {
@@ -56,7 +53,7 @@ public class AppLayerTvView extends TvView {
public void onViewAdded(View child) {
if (child instanceof SurfaceView) {
// Note: See b/29118070 for detail.
- ((SurfaceView) child).setSecure(!Utils.isDeveloper());
+ ((SurfaceView) child).setSecure(!CommonUtils.isDeveloper());
}
super.onViewAdded(child);
}
@@ -66,7 +63,7 @@ public class AppLayerTvView extends TvView {
super.getLocationOnScreen(outLocation);
// The TvView.MySessionCallback.onSessionCreated() will call this method indirectly.
- Debug.getTimer(Debug.TAG_START_UP_TIMER).log(
- "AppLayerTvView.getLocationOnScreen, session created");
+ Debug.getTimer(Debug.TAG_START_UP_TIMER)
+ .log("AppLayerTvView.getLocationOnScreen, session created");
}
}
diff --git a/src/com/android/tv/ui/BlockScreenView.java b/src/com/android/tv/ui/BlockScreenView.java
index 09c167ca..6b2d9a01 100644
--- a/src/com/android/tv/ui/BlockScreenView.java
+++ b/src/com/android/tv/ui/BlockScreenView.java
@@ -29,7 +29,6 @@ import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.TextView;
-
import com.android.tv.R;
import com.android.tv.ui.TunableTvView.BlockScreenType;
@@ -62,10 +61,11 @@ public class BlockScreenView extends FrameLayout {
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);
+ mSpacingNormal =
+ getResources().getDimensionPixelOffset(R.dimen.tvview_block_vertical_spacing);
+ mSpacingShrunken =
+ getResources()
+ .getDimensionPixelOffset(R.dimen.shrunken_tvview_block_vertical_spacing);
}
@Override
@@ -78,66 +78,60 @@ public class BlockScreenView extends FrameLayout {
mSpace = findViewById(R.id.space);
mBlockingInfoTextView = (TextView) findViewById(R.id.block_screen_text);
mBackgroundImageView = (ImageView) findViewById(R.id.background_image);
- mFadeOut = AnimatorInflater.loadAnimator(getContext(),
- R.animator.tvview_block_screen_fade_out);
+ mFadeOut =
+ AnimatorInflater.loadAnimator(
+ getContext(), R.animator.tvview_block_screen_fade_out);
mFadeOut.setTarget(this);
- mFadeOut.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- setVisibility(GONE);
- setBackgroundImage(null);
- setAlpha(1.0f);
- }
- });
- mInfoFadeIn = AnimatorInflater.loadAnimator(getContext(),
- R.animator.tvview_block_screen_fade_in);
+ mFadeOut.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ setVisibility(GONE);
+ setBackgroundImage(null);
+ setAlpha(1.0f);
+ }
+ });
+ mInfoFadeIn =
+ AnimatorInflater.loadAnimator(getContext(), R.animator.tvview_block_screen_fade_in);
mInfoFadeIn.setTarget(mContainerView);
- mInfoFadeOut = AnimatorInflater.loadAnimator(getContext(),
- R.animator.tvview_block_screen_fade_out);
+ mInfoFadeOut =
+ AnimatorInflater.loadAnimator(
+ getContext(), R.animator.tvview_block_screen_fade_out);
mInfoFadeOut.setTarget(mContainerView);
- mInfoFadeOut.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mContainerView.setVisibility(GONE);
- }
- });
+ mInfoFadeOut.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mContainerView.setVisibility(GONE);
+ }
+ });
}
- /**
- * Sets the normal image.
- */
+ /** Sets the normal image. */
public void setIconImage(int resId) {
mNormalLockIconView.setImageResource(resId);
updateSpaceVisibility();
}
- /**
- * Sets the scale type of the normal image.
- */
+ /** Sets the scale type of the normal image. */
public void setIconScaleType(ScaleType scaleType) {
mNormalLockIconView.setScaleType(scaleType);
updateSpaceVisibility();
}
- /**
- * Show or hide the image of this view.
- */
+ /** Show or hide the image of this view. */
public void setIconVisibility(boolean visible) {
mImageContainer.setVisibility(visible ? VISIBLE : GONE);
updateSpaceVisibility();
}
- /**
- * Sets the text message.
- */
+ /** Sets the text message. */
public void setInfoText(int resId) {
mBlockingInfoTextView.setText(resId);
updateSpaceVisibility();
}
- /**
- * Sets the text message.
- */
+ /** Sets the text message. */
public void setInfoText(String text) {
mBlockingInfoTextView.setText(text);
updateSpaceVisibility();
@@ -175,19 +169,18 @@ public class BlockScreenView extends FrameLayout {
}
/**
- * Changes the spacing between the image view and the text view according to the
- * {@code blockScreenType}.
+ * 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;
+ ? mSpacingShrunken
+ : mSpacingNormal;
requestLayout();
}
- /**
- * Changes the view layout according to the {@code blockScreenType}.
- */
+ /** Changes the view layout according to the {@code blockScreenType}. */
public void onBlockStatusChanged(@BlockScreenType int blockScreenType, boolean withAnimation) {
if (!withAnimation) {
switch (blockScreenType) {
@@ -235,25 +228,19 @@ public class BlockScreenView extends FrameLayout {
updateSpaceVisibility();
}
- /**
- * Adds a listener to the fade-in animation of info text and icons of the block screen.
- */
+ /** Adds a listener to the fade-in animation of info text and icons of the block screen. */
public void addInfoFadeInAnimationListener(AnimatorListener listener) {
mInfoFadeIn.addListener(listener);
}
- /**
- * Fades out the block screen.
- */
+ /** Fades out the block screen. */
public void fadeOut() {
if (getVisibility() == VISIBLE && !mFadeOut.isStarted()) {
mFadeOut.start();
}
}
- /**
- * Ends the currently running animations.
- */
+ /** Ends the currently running animations. */
public void endAnimations() {
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 a5d897f2..28325197 100644
--- a/src/com/android/tv/ui/ChannelBannerView.java
+++ b/src/com/android/tv/ui/ChannelBannerView.java
@@ -26,7 +26,6 @@ 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.Nullable;
import android.text.Spannable;
import android.text.SpannableString;
@@ -38,6 +37,8 @@ import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
@@ -45,46 +46,43 @@ import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
-
import com.android.tv.MainActivity;
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
import com.android.tv.common.feature.CommonFeatures;
-import com.android.tv.data.Channel;
import com.android.tv.data.Program;
import com.android.tv.data.StreamInfo;
+import com.android.tv.data.api.Channel;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.parental.ContentRatingsManager;
-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.ui.TvTransitionManager.TransitionLayout;
+import com.android.tv.ui.hideable.AutoHideScheduler;
import com.android.tv.util.Utils;
-
-/**
- * A view to render channel banner.
- */
-public class ChannelBannerView extends FrameLayout implements TvTransitionManager.TransitionLayout {
+import com.android.tv.util.images.ImageCache;
+import com.android.tv.util.images.ImageLoader;
+import com.android.tv.util.images.ImageLoader.ImageLoaderCallback;
+import com.android.tv.util.images.ImageLoader.LoadTvInputLogoTask;
+
+/** A view to render channel banner. */
+public class ChannelBannerView extends FrameLayout
+ implements TransitionLayout, AccessibilityStateChangeListener {
private static final String TAG = "ChannelBannerView";
private static final boolean DEBUG = false;
- /**
- * Show all information at the channel banner.
- */
+ /** Show all information at the channel banner. */
public static final int LOCK_NONE = 0;
/**
- * Lock program details at the channel banner.
- * This is used when a content is locked so we don't want to show program details
- * including program description text and poster art.
+ * Lock program details at the channel banner. This is used when a content is locked so we don't
+ * want to show program details including program description text and poster art.
*/
public static final int LOCK_PROGRAM_DETAIL = 1;
/**
- * Lock channel information at the channel banner.
- * This is used when a channel is locked so we only want to show input information.
+ * Lock channel information at the channel banner. This is used when a channel is locked so we
+ * only want to show input information.
*/
public static final int LOCK_CHANNEL_INFO = 2;
@@ -119,7 +117,7 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
private Channel mCurrentChannel;
private boolean mCurrentChannelLogoExists;
private Program mLastUpdatedProgram;
- private final Handler mHandler = new Handler();
+ private final AutoHideScheduler mAutoHideScheduler;
private final DvrManager mDvrManager;
private ContentRatingsManager mContentRatingsManager;
private TvContentRating mBlockingContentRating;
@@ -134,18 +132,6 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
private final Animator mProgramDescriptionFadeInAnimator;
private final Animator mProgramDescriptionFadeOutAnimator;
- private final Runnable mHideRunnable = new Runnable() {
- @Override
- public void run() {
- mCurrentHeight = 0;
- mMainActivity.getOverlayManager().hideOverlays(
- TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_DIALOG
- | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANELS
- | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_PROGRAM_GUIDE
- | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_MENU
- | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_FRAGMENT);
- }
- };
private final long mShowDurationMillis;
private final int mChannelLogoImageViewWidth;
private final int mChannelLogoImageViewHeight;
@@ -157,22 +143,23 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
private final int mRecordingIconPadding;
private final Interpolator mResizeInterpolator;
- private final AnimatorListenerAdapter mResizeAnimatorListener = new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animator) {
- mProgramInfoUpdatePendingByResizing = false;
- }
+ private final AnimatorListenerAdapter mResizeAnimatorListener =
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animator) {
+ mProgramInfoUpdatePendingByResizing = false;
+ }
- @Override
- public void onAnimationEnd(Animator animator) {
- mProgramDescriptionTextView.setAlpha(1f);
- mResizeAnimator = null;
- if (mProgramInfoUpdatePendingByResizing) {
- mProgramInfoUpdatePendingByResizing = false;
- updateProgramInfo(mLastUpdatedProgram);
- }
- }
- };
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ mProgramDescriptionTextView.setAlpha(1f);
+ mResizeAnimator = null;
+ if (mProgramInfoUpdatePendingByResizing) {
+ mProgramInfoUpdatePendingByResizing = false;
+ updateProgramInfo(mLastUpdatedProgram);
+ }
+ }
+ };
public ChannelBannerView(Context context) {
this(context, null);
@@ -185,53 +172,60 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
public ChannelBannerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mResources = getResources();
-
mMainActivity = (MainActivity) context;
- mShowDurationMillis = mResources.getInteger(
- R.integer.channel_banner_show_duration);
- mChannelLogoImageViewWidth = mResources.getDimensionPixelSize(
- R.dimen.channel_banner_channel_logo_width);
- mChannelLogoImageViewHeight = mResources.getDimensionPixelSize(
- R.dimen.channel_banner_channel_logo_height);
- mChannelLogoImageViewMarginStart = mResources.getDimensionPixelSize(
- R.dimen.channel_banner_channel_logo_margin_start);
- mProgramDescriptionTextViewWidth = mResources.getDimensionPixelSize(
- R.dimen.channel_banner_program_description_width);
+ mShowDurationMillis = mResources.getInteger(R.integer.channel_banner_show_duration);
+ mChannelLogoImageViewWidth =
+ mResources.getDimensionPixelSize(R.dimen.channel_banner_channel_logo_width);
+ mChannelLogoImageViewHeight =
+ mResources.getDimensionPixelSize(R.dimen.channel_banner_channel_logo_height);
+ mChannelLogoImageViewMarginStart =
+ mResources.getDimensionPixelSize(R.dimen.channel_banner_channel_logo_margin_start);
+ mProgramDescriptionTextViewWidth =
+ mResources.getDimensionPixelSize(R.dimen.channel_banner_program_description_width);
mChannelBannerTextColor = mResources.getColor(R.color.channel_banner_text_color, null);
- mChannelBannerDimTextColor = mResources.getColor(R.color.channel_banner_dim_text_color,
- null);
+ mChannelBannerDimTextColor =
+ mResources.getColor(R.color.channel_banner_dim_text_color, null);
mResizeAnimDuration = mResources.getInteger(R.integer.channel_banner_fast_anim_duration);
- mRecordingIconPadding = mResources.getDimensionPixelOffset(
- R.dimen.channel_banner_recording_icon_padding);
+ mRecordingIconPadding =
+ mResources.getDimensionPixelOffset(R.dimen.channel_banner_recording_icon_padding);
- mResizeInterpolator = AnimationUtils.loadInterpolator(context,
- android.R.interpolator.linear_out_slow_in);
+ mResizeInterpolator =
+ AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in);
- mProgramDescriptionFadeInAnimator = AnimatorInflater.loadAnimator(mMainActivity,
- R.animator.channel_banner_program_description_fade_in);
- mProgramDescriptionFadeOutAnimator = AnimatorInflater.loadAnimator(mMainActivity,
- R.animator.channel_banner_program_description_fade_out);
+ mProgramDescriptionFadeInAnimator =
+ AnimatorInflater.loadAnimator(
+ mMainActivity, R.animator.channel_banner_program_description_fade_in);
+ mProgramDescriptionFadeOutAnimator =
+ AnimatorInflater.loadAnimator(
+ mMainActivity, R.animator.channel_banner_program_description_fade_out);
if (CommonFeatures.DVR.isEnabled(mMainActivity)) {
- mDvrManager = TvApplication.getSingletons(mMainActivity).getDvrManager();
+ mDvrManager = TvSingletons.getSingletons(mMainActivity).getDvrManager();
} else {
mDvrManager = null;
}
- mContentRatingsManager = TvApplication.getSingletons(getContext())
- .getTvInputManagerHelper().getContentRatingsManager();
-
- mNoProgram = new Program.Builder()
- .setTitle(context.getString(R.string.channel_banner_no_title))
- .setDescription(EMPTY_STRING)
- .build();
- mLockedChannelProgram = new Program.Builder()
- .setTitle(context.getString(R.string.channel_banner_locked_channel_title))
- .setDescription(EMPTY_STRING)
- .build();
+ mContentRatingsManager =
+ TvSingletons.getSingletons(getContext())
+ .getTvInputManagerHelper()
+ .getContentRatingsManager();
+
+ mNoProgram =
+ new Program.Builder()
+ .setTitle(context.getString(R.string.channel_banner_no_title))
+ .setDescription(EMPTY_STRING)
+ .build();
+ mLockedChannelProgram =
+ new Program.Builder()
+ .setTitle(context.getString(R.string.channel_banner_locked_channel_title))
+ .setDescription(EMPTY_STRING)
+ .build();
if (sClosedCaptionMark == null) {
sClosedCaptionMark = context.getString(R.string.closed_caption);
}
+ mAutoHideScheduler = new AutoHideScheduler(context, this::hide);
+ context.getSystemService(AccessibilityManager.class)
+ .addAccessibilityStateChangeListener(mAutoHideScheduler);
}
@Override
@@ -260,12 +254,13 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
mProgramDescriptionFadeInAnimator.setTarget(mProgramDescriptionTextView);
mProgramDescriptionFadeOutAnimator.setTarget(mProgramDescriptionTextView);
- mProgramDescriptionFadeOutAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animator) {
- mProgramDescriptionTextView.setText(mProgramDescriptionText);
- }
- });
+ mProgramDescriptionFadeOutAnimator.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ mProgramDescriptionTextView.setText(mProgramDescriptionText);
+ }
+ });
}
@Override
@@ -274,22 +269,13 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
if (fromEmptyScene) {
ViewUtils.setTransitionAlpha(mChannelView, 1f);
}
- scheduleHide();
+ mAutoHideScheduler.schedule(mShowDurationMillis);
}
@Override
public void onExitAction() {
mCurrentHeight = 0;
- cancelHide();
- }
-
- private void scheduleHide() {
- cancelHide();
- mHandler.postDelayed(mHideRunnable, mShowDurationMillis);
- }
-
- private void cancelHide() {
- mHandler.removeCallbacks(mHideRunnable);
+ mAutoHideScheduler.cancel();
}
private void resetAnimationEffects() {
@@ -308,7 +294,8 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
* @throws IllegalArgumentException if lockType is invalid.
*/
public int setLockType(int lockType) {
- if (lockType != LOCK_NONE && lockType != LOCK_CHANNEL_INFO
+ if (lockType != LOCK_NONE
+ && lockType != LOCK_CHANNEL_INFO
&& lockType != LOCK_PROGRAM_DETAIL) {
throw new IllegalArgumentException("No such lock type " + lockType);
}
@@ -330,7 +317,7 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
* Update channel banner view.
*
* @param updateOnTune {@false} denotes the channel banner is updated due to other reasons than
- * tuning. The channel info will not be updated in this case.
+ * tuning. The channel info will not be updated in this case.
*/
public void updateViews(boolean updateOnTune) {
resetAnimationEffects();
@@ -338,7 +325,7 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
mUpdateOnTune = updateOnTune;
if (mUpdateOnTune) {
if (isShown()) {
- scheduleHide();
+ mAutoHideScheduler.schedule(mShowDurationMillis);
}
mBlockingContentRating = null;
mCurrentChannel = mMainActivity.getCurrentChannel();
@@ -351,6 +338,18 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
mUpdateOnTune = false;
}
+ private void hide() {
+ mCurrentHeight = 0;
+ mMainActivity
+ .getOverlayManager()
+ .hideOverlays(
+ TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_DIALOG
+ | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANELS
+ | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_PROGRAM_GUIDE
+ | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_MENU
+ | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_FRAGMENT);
+ }
+
/**
* Update channel banner view with stream info.
*
@@ -359,14 +358,18 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
public void updateStreamInfo(StreamInfo info) {
// Update stream information in a channel.
if (mLockType != LOCK_CHANNEL_INFO && info != null) {
- updateText(mClosedCaptionTextView, info.hasClosedCaption() ? sClosedCaptionMark
- : EMPTY_STRING);
- updateText(mAspectRatioTextView,
+ updateText(
+ mClosedCaptionTextView,
+ info.hasClosedCaption() ? sClosedCaptionMark : EMPTY_STRING);
+ updateText(
+ mAspectRatioTextView,
Utils.getAspectRatioString(info.getVideoDisplayAspectRatio()));
- updateText(mResolutionTextView,
+ updateText(
+ mResolutionTextView,
Utils.getVideoDefinitionLevelString(
mMainActivity, info.getVideoDefinitionLevel()));
- updateText(mAudioChannelTextView,
+ updateText(
+ mAudioChannelTextView,
Utils.getAudioChannelString(mMainActivity, info.getAudioChannelCount()));
} else {
// Channel change has been requested. But, StreamInfo hasn't been updated yet.
@@ -415,9 +418,11 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
}
mChannelNumberTextView.setText(displayNumber);
mChannelNameTextView.setText(displayName);
- TvInputInfo info = mMainActivity.getTvInputManagerHelper().getTvInputInfo(
- getCurrentInputId());
- if (info == null || !ImageLoader.loadBitmap(createTvInputLogoLoaderCallback(info, this),
+ TvInputInfo info =
+ mMainActivity.getTvInputManagerHelper().getTvInputInfo(getCurrentInputId());
+ if (info == null
+ || !ImageLoader.loadBitmap(
+ createTvInputLogoLoaderCallback(info, this),
new LoadTvInputLogoTask(getContext(), ImageCache.getInstance(), info))) {
mTvInputLogoImageView.setVisibility(View.GONE);
mTvInputLogoImageView.setImageDrawable(null);
@@ -425,8 +430,11 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
mChannelLogoImageView.setImageBitmap(null);
mChannelLogoImageView.setVisibility(View.GONE);
if (mCurrentChannel != null && mCurrentChannelLogoExists) {
- mCurrentChannel.loadBitmap(getContext(), Channel.LOAD_IMAGE_TYPE_CHANNEL_LOGO,
- mChannelLogoImageViewWidth, mChannelLogoImageViewHeight,
+ mCurrentChannel.loadBitmap(
+ getContext(),
+ Channel.LOAD_IMAGE_TYPE_CHANNEL_LOGO,
+ mChannelLogoImageViewWidth,
+ mChannelLogoImageViewHeight,
createChannelLogoCallback(this, mCurrentChannel));
}
}
@@ -446,7 +454,8 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
return new ImageLoaderCallback<ChannelBannerView>(channelBannerView) {
@Override
public void onBitmapLoaded(ChannelBannerView channelBannerView, Bitmap bitmap) {
- if (bitmap != null && channelBannerView.mCurrentChannel != null
+ if (bitmap != null
+ && channelBannerView.mCurrentChannel != null
&& info.getId().equals(channelBannerView.mCurrentChannel.getInputId())) {
channelBannerView.updateTvInputLogo(bitmap);
}
@@ -485,7 +494,7 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
return new ImageLoaderCallback<ChannelBannerView>(channelBannerView) {
@Override
public void onBitmapLoaded(ChannelBannerView view, @Nullable Bitmap logo) {
- if (channel != view.mCurrentChannel) {
+ if (channel.equals(view.mCurrentChannel)) {
// The logo is obsolete.
return;
}
@@ -524,8 +533,9 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
if (mLastUpdatedProgram == null
|| !TextUtils.equals(program.getTitle(), mLastUpdatedProgram.getTitle())
- || !TextUtils.equals(program.getEpisodeDisplayTitle(getContext()),
- mLastUpdatedProgram.getEpisodeDisplayTitle(getContext()))) {
+ || !TextUtils.equals(
+ program.getEpisodeDisplayTitle(getContext()),
+ mLastUpdatedProgram.getEpisodeDisplayTitle(getContext()))) {
updateProgramTextView(program);
}
updateProgramTimeInfo(program);
@@ -548,8 +558,9 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
mProgramDescriptionText = program.getDescription();
}
String description = mProgramDescriptionTextView.getText().toString();
- boolean programDescriptionNeedFadeAnimation = (isProgramChanged
- || !description.equals(mProgramDescriptionText)) && !mUpdateOnTune;
+ boolean programDescriptionNeedFadeAnimation =
+ (isProgramChanged || !description.equals(mProgramDescriptionText))
+ && !mUpdateOnTune;
updateBannerHeight(programDescriptionNeedFadeAnimation);
} else {
mProgramInfoUpdatePendingByResizing = true;
@@ -561,19 +572,21 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
if (program == null) {
return;
}
- updateProgramTextView(program == mLockedChannelProgram, program.getTitle(),
+ updateProgramTextView(
+ program.equals(mLockedChannelProgram),
+ program.getTitle(),
program.getEpisodeDisplayTitle(getContext()));
}
- private void updateProgramTextView(boolean dimText, String title,
- String episodeDisplayTitle) {
+ private void updateProgramTextView(boolean dimText, String title, String episodeDisplayTitle) {
mProgramTextView.setVisibility(View.VISIBLE);
if (dimText) {
mProgramTextView.setTextColor(mChannelBannerDimTextColor);
} else {
mProgramTextView.setTextColor(mChannelBannerTextColor);
}
- updateTextView(mProgramTextView,
+ updateTextView(
+ mProgramTextView,
R.dimen.channel_banner_program_large_text_size,
R.dimen.channel_banner_program_large_margin_top);
if (TextUtils.isEmpty(episodeDisplayTitle)) {
@@ -582,18 +595,24 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
String fullTitle = title + " " + episodeDisplayTitle;
SpannableString text = new SpannableString(fullTitle);
- text.setSpan(new TextAppearanceSpan(getContext(),
- R.style.text_appearance_channel_banner_episode_title),
- fullTitle.length() - episodeDisplayTitle.length(), fullTitle.length(),
+ text.setSpan(
+ new TextAppearanceSpan(
+ getContext(), R.style.text_appearance_channel_banner_episode_title),
+ fullTitle.length() - episodeDisplayTitle.length(),
+ fullTitle.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mProgramTextView.setText(text);
}
- int width = mProgramDescriptionTextViewWidth + (mCurrentChannelLogoExists ?
- 0 : mChannelLogoImageViewWidth + mChannelLogoImageViewMarginStart);
+ int width =
+ mProgramDescriptionTextViewWidth
+ + (mCurrentChannelLogoExists
+ ? 0
+ : mChannelLogoImageViewWidth + mChannelLogoImageViewMarginStart);
ViewGroup.LayoutParams lp = mProgramTextView.getLayoutParams();
lp.width = width;
mProgramTextView.setLayoutParams(lp);
- mProgramTextView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+ mProgramTextView.measure(
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
boolean oneline = (mProgramTextView.getLineCount() == 1);
@@ -602,13 +621,16 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
mProgramTextView,
R.dimen.channel_banner_program_medium_text_size,
R.dimen.channel_banner_program_medium_margin_top);
- mProgramTextView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+ mProgramTextView.measure(
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
oneline = (mProgramTextView.getLineCount() == 1);
}
- updateTopMargin(mAnchorView, oneline
- ? R.dimen.channel_banner_anchor_one_line_y
- : R.dimen.channel_banner_anchor_two_line_y);
+ updateTopMargin(
+ mAnchorView,
+ oneline
+ ? R.dimen.channel_banner_anchor_one_line_y
+ : R.dimen.channel_banner_anchor_two_line_y);
}
private void updateProgramRatings(Program program) {
@@ -650,8 +672,8 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
if (mLockType != LOCK_CHANNEL_INFO && durationMs > 0 && startTimeMs > 0) {
mProgramTimeTextView.setVisibility(View.VISIBLE);
mRemainingTimeView.setVisibility(View.VISIBLE);
- mProgramTimeTextView.setText(Utils.getDurationString(
- getContext(), startTimeMs, endTimeMs, true));
+ mProgramTimeTextView.setText(
+ Utils.getDurationString(getContext(), startTimeMs, endTimeMs, true));
} else {
mProgramTimeTextView.setVisibility(View.GONE);
mRemainingTimeView.setVisibility(View.GONE);
@@ -673,8 +695,10 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
updateProgressBarAndRecIcon(program, null);
return;
}
- ScheduledRecording currentRecording = (mCurrentChannel == null) ? null
- : mDvrManager.getCurrentRecording(mCurrentChannel.getId());
+ ScheduledRecording currentRecording =
+ (mCurrentChannel == null)
+ ? null
+ : mDvrManager.getCurrentRecording(mCurrentChannel.getId());
if (DEBUG) {
Log.d(TAG, currentRecording == null ? "No Recording" : "Recording:" + currentRecording);
}
@@ -685,22 +709,23 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
}
}
- private void updateProgressBarAndRecIcon(Program program,
- @Nullable ScheduledRecording recording) {
+ private void updateProgressBarAndRecIcon(
+ Program program, @Nullable ScheduledRecording recording) {
long programStartTime = program.getStartTimeUtcMillis();
long programEndTime = program.getEndTimeUtcMillis();
long currentPosition = mMainActivity.getCurrentPlayingPosition();
updateRecordingIndicator(recording);
if (recording != null) {
// Recording now. Use recording-style progress bar.
- mRemainingTimeView.setProgress(getProgressPercent(recording.getStartTimeMs(),
- programStartTime, programEndTime));
- mRemainingTimeView.setSecondaryProgress(getProgressPercent(currentPosition,
- programStartTime, programEndTime));
+ mRemainingTimeView.setProgress(
+ getProgressPercent(
+ recording.getStartTimeMs(), programStartTime, programEndTime));
+ mRemainingTimeView.setSecondaryProgress(
+ getProgressPercent(currentPosition, programStartTime, programEndTime));
} else {
// No recording is going now. Recover progress bar.
- mRemainingTimeView.setProgress(getProgressPercent(currentPosition,
- programStartTime, programEndTime));
+ mRemainingTimeView.setProgress(
+ getProgressPercent(currentPosition, programStartTime, programEndTime));
mRemainingTimeView.setSecondaryProgress(0);
}
}
@@ -708,9 +733,15 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
private void updateRecordingIndicator(@Nullable ScheduledRecording recording) {
if (recording != null) {
if (mRemainingTimeView.getVisibility() == View.GONE) {
- mRecordingIndicatorView.setText(mMainActivity.getResources().getString(
- R.string.dvr_recording_till_format, DateUtils.formatDateTime(mMainActivity,
- recording.getEndTimeMs(), DateUtils.FORMAT_SHOW_TIME)));
+ mRecordingIndicatorView.setText(
+ mMainActivity
+ .getResources()
+ .getString(
+ R.string.dvr_recording_till_format,
+ DateUtils.formatDateTime(
+ mMainActivity,
+ recording.getEndTimeMs(),
+ DateUtils.FORMAT_SHOW_TIME)));
mRecordingIndicatorView.setCompoundDrawablePadding(mRecordingIconPadding);
} else {
mRecordingIndicatorView.setText("");
@@ -725,10 +756,10 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
private boolean isCurrentProgram(ScheduledRecording recording, Program program) {
long currentPosition = mMainActivity.getCurrentPlayingPosition();
return (recording.getType() == ScheduledRecording.TYPE_PROGRAM
- && recording.getProgramId() == program.getId())
+ && recording.getProgramId() == program.getId())
|| (recording.getType() == ScheduledRecording.TYPE_TIMED
- && currentPosition >= recording.getStartTimeMs()
- && currentPosition <= recording.getEndTimeMs());
+ && currentPosition >= recording.getStartTimeMs()
+ && currentPosition <= recording.getEndTimeMs());
}
private void setLastUpdatedProgram(Program program) {
@@ -764,18 +795,20 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
private Animator createResizeAnimator(int targetHeight, boolean addFadeAnimation) {
final ValueAnimator heightAnimator = ValueAnimator.ofInt(mCurrentHeight, targetHeight);
- heightAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- int value = (Integer) animation.getAnimatedValue();
- LayoutParams layoutParams = (LayoutParams) ChannelBannerView.this.getLayoutParams();
- if (value != layoutParams.height) {
- layoutParams.height = value;
- ChannelBannerView.this.setLayoutParams(layoutParams);
- }
- mCurrentHeight = value;
- }
- });
+ heightAnimator.addUpdateListener(
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ int value = (Integer) animation.getAnimatedValue();
+ LayoutParams layoutParams =
+ (LayoutParams) ChannelBannerView.this.getLayoutParams();
+ if (value != layoutParams.height) {
+ layoutParams.height = value;
+ ChannelBannerView.this.setLayoutParams(layoutParams);
+ }
+ mCurrentHeight = value;
+ }
+ });
heightAnimator.setDuration(mResizeAnimDuration);
heightAnimator.setInterpolator(mResizeInterpolator);
@@ -792,4 +825,9 @@ public class ChannelBannerView extends FrameLayout implements TvTransitionManage
animator.addListener(mResizeAnimatorListener);
return animator;
}
-} \ No newline at end of file
+
+ @Override
+ public void onAccessibilityStateChanged(boolean enabled) {
+ mAutoHideScheduler.onAccessibilityStateChanged(enabled);
+ }
+}
diff --git a/src/com/android/tv/ui/DialogUtils.java b/src/com/android/tv/ui/DialogUtils.java
index acbaf8c8..341db2e1 100644
--- a/src/com/android/tv/ui/DialogUtils.java
+++ b/src/com/android/tv/ui/DialogUtils.java
@@ -20,7 +20,6 @@ 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 {
@@ -31,31 +30,28 @@ public final class DialogUtils {
* @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) {
+ 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();
- }
- };
+ 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();
+ new AlertDialog.Builder(context).setItems(items, onClickListener).create().show();
}
- private DialogUtils() { }
+ private DialogUtils() {}
}
diff --git a/src/com/android/tv/ui/FullscreenDialogView.java b/src/com/android/tv/ui/FullscreenDialogView.java
index e2220722..800fa85a 100644
--- a/src/com/android/tv/ui/FullscreenDialogView.java
+++ b/src/com/android/tv/ui/FullscreenDialogView.java
@@ -26,7 +26,6 @@ import android.view.View;
import android.view.ViewTreeObserver;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
-
import com.android.tv.MainActivity;
import com.android.tv.R;
import com.android.tv.dialog.FullscreenDialogFragment;
@@ -58,41 +57,39 @@ public class FullscreenDialogView extends FrameLayout
public FullscreenDialogView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mLinearOutSlowIn = AnimationUtils.loadInterpolator(context,
- android.R.interpolator.linear_out_slow_in);
- mFastOutLinearIn = AnimationUtils.loadInterpolator(context,
- android.R.interpolator.fast_out_linear_in);
- getViewTreeObserver().addOnGlobalLayoutListener(
- new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- getViewTreeObserver().removeOnGlobalLayoutListener(this);
- startEnterAnimation();
- }
- });
+ mLinearOutSlowIn =
+ AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in);
+ mFastOutLinearIn =
+ AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_linear_in);
+ getViewTreeObserver()
+ .addOnGlobalLayoutListener(
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ startEnterAnimation();
+ }
+ });
}
protected MainActivity getActivity() {
return mActivity;
}
- /**
- * Gets the host {@link Dialog}.
- */
+ /** Gets the host {@link Dialog}. */
protected Dialog getDialog() {
return mDialog;
}
- /**
- * Dismisses the host {@link Dialog}.
- */
+ /** Dismisses the host {@link Dialog}. */
protected void dismiss() {
- startExitAnimation(new Runnable() {
- @Override
- public void run() {
- mDialog.dismiss();
- }
- });
+ startExitAnimation(
+ new Runnable() {
+ @Override
+ public void run() {
+ mDialog.dismiss();
+ }
+ });
}
@Override
@@ -102,51 +99,48 @@ public class FullscreenDialogView extends FrameLayout
}
@Override
- public void onBackPressed() { }
+ public void onBackPressed() {}
@Override
- public void onDestroy() { }
+ public void onDestroy() {}
- /**
- * Transitions to another view inside the host {@link Dialog}.
- */
+ /** Transitions to another view inside the host {@link Dialog}. */
public void transitionTo(final FullscreenDialogView v) {
mSkipExitAlphaAnimation = true;
v.mSkipEnterAlphaAnimation = true;
v.initialize(mActivity, mDialog);
- startExitAnimation(new Runnable() {
- @Override
- public void run() {
- new Handler().postDelayed(new Runnable() {
+ startExitAnimation(
+ new Runnable() {
@Override
public void run() {
- v.initialize(getActivity(), getDialog());
- getDialog().setContentView(v);
+ new Handler()
+ .postDelayed(
+ new Runnable() {
+ @Override
+ public void run() {
+ v.initialize(getActivity(), getDialog());
+ getDialog().setContentView(v);
+ }
+ },
+ TRANSITION_INTERVAL_MS);
}
- }, TRANSITION_INTERVAL_MS);
- }
- });
+ });
}
- /**
- * Called when an enter animation starts. Sub-view specific animation can be implemented.
- */
- protected void onStartEnterAnimation(TimeInterpolator interpolator, long duration) {
- }
+ /** Called when an enter animation starts. Sub-view specific animation can be implemented. */
+ protected void onStartEnterAnimation(TimeInterpolator interpolator, long duration) {}
- /**
- * Called when an exit animation starts. Sub-view specific animation can be implemented.
- */
- protected void onStartExitAnimation(TimeInterpolator interpolator, long duration,
- Runnable onAnimationEnded) {
- }
+ /** Called when an exit animation starts. Sub-view specific animation can be implemented. */
+ protected void onStartExitAnimation(
+ TimeInterpolator interpolator, long duration, Runnable onAnimationEnded) {}
private void startEnterAnimation() {
if (DEBUG) Log.d(TAG, "start an enter animation");
View backgroundView = findViewById(R.id.background);
if (!mSkipEnterAlphaAnimation) {
backgroundView.setAlpha(0);
- backgroundView.animate()
+ backgroundView
+ .animate()
.alpha(1.0f)
.setInterpolator(mLinearOutSlowIn)
.setDuration(FADE_IN_DURATION_MS)
@@ -160,7 +154,8 @@ public class FullscreenDialogView extends FrameLayout
if (DEBUG) Log.d(TAG, "start an exit animation");
View backgroundView = findViewById(R.id.background);
if (!mSkipExitAlphaAnimation) {
- backgroundView.animate()
+ backgroundView
+ .animate()
.alpha(0.0f)
.setInterpolator(mFastOutLinearIn)
.setDuration(FADE_OUT_DURATION_MS)
diff --git a/src/com/android/tv/ui/GuidedActionsStylistWithDivider.java b/src/com/android/tv/ui/GuidedActionsStylistWithDivider.java
index 39ec1279..9685b04b 100644
--- a/src/com/android/tv/ui/GuidedActionsStylistWithDivider.java
+++ b/src/com/android/tv/ui/GuidedActionsStylistWithDivider.java
@@ -20,17 +20,13 @@ import android.content.Context;
import android.support.v17.leanback.app.GuidedStepFragment;
import android.support.v17.leanback.widget.GuidedAction;
import android.support.v17.leanback.widget.GuidedActionsStylist;
-
import com.android.tv.R;
-/**
- * Extended stylist class used for {@link GuidedStepFragment} with divider support.
- */
+/** Extended stylist class used for {@link GuidedStepFragment} with divider support. */
public class GuidedActionsStylistWithDivider extends GuidedActionsStylist {
- /**
- * ID used mark a divider.
- */
+ /** ID used mark a divider. */
public static final int ACTION_DIVIDER = -100;
+
private static final int VIEW_TYPE_DIVIDER = 1;
@Override
@@ -50,8 +46,8 @@ public class GuidedActionsStylistWithDivider extends GuidedActionsStylist {
}
/**
- * Creates a divider for {@link GuidedStepFragment}, targeted fragments must use
- * {@link GuidedActionsStylistWithDivider} as its actions' stylist for divider to work.
+ * Creates a divider for {@link GuidedStepFragment}, targeted fragments must use {@link
+ * GuidedActionsStylistWithDivider} as its actions' stylist for divider to work.
*/
public static GuidedAction createDividerAction(Context context) {
return new GuidedAction.Builder(context)
diff --git a/src/com/android/tv/ui/InputBannerView.java b/src/com/android/tv/ui/InputBannerView.java
index 4e254c62..5ac715bf 100644
--- a/src/com/android/tv/ui/InputBannerView.java
+++ b/src/com/android/tv/ui/InputBannerView.java
@@ -23,25 +23,27 @@ import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
-
import com.android.tv.MainActivity;
import com.android.tv.R;
-import com.android.tv.data.Channel;
+import com.android.tv.data.api.Channel;
public class InputBannerView extends LinearLayout implements TvTransitionManager.TransitionLayout {
private final long mShowDurationMillis;
- private final Runnable mHideRunnable = new Runnable() {
- @Override
- public void run() {
- ((MainActivity) getContext()).getOverlayManager().hideOverlays(
- TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_DIALOG
- | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANELS
- | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_PROGRAM_GUIDE
- | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_MENU
- | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_FRAGMENT);
- }
- };
+ private final Runnable mHideRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ ((MainActivity) getContext())
+ .getOverlayManager()
+ .hideOverlays(
+ TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_DIALOG
+ | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANELS
+ | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_PROGRAM_GUIDE
+ | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_MENU
+ | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_FRAGMENT);
+ }
+ };
private TextView mInputLabelTextView;
private TextView mSecondaryInputLabelTextView;
@@ -56,8 +58,8 @@ public class InputBannerView extends LinearLayout implements TvTransitionManager
public InputBannerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mShowDurationMillis = context.getResources().getInteger(
- R.integer.select_input_show_duration);
+ mShowDurationMillis =
+ context.getResources().getInteger(R.integer.select_input_show_duration);
}
@Override
@@ -73,8 +75,8 @@ public class InputBannerView extends LinearLayout implements TvTransitionManager
if (channel == null || !channel.isPassthrough()) {
return;
}
- TvInputInfo input = mainActivity.getTvInputManagerHelper().getTvInputInfo(
- channel.getInputId());
+ TvInputInfo input =
+ mainActivity.getTvInputManagerHelper().getTvInputInfo(channel.getInputId());
CharSequence customLabel = input.loadCustomLabel(getContext());
CharSequence label = input.loadLabel(getContext());
if (TextUtils.isEmpty(customLabel) || customLabel.equals(label)) {
diff --git a/src/com/android/tv/ui/IntroView.java b/src/com/android/tv/ui/IntroView.java
index 7530f283..be9fb691 100644
--- a/src/com/android/tv/ui/IntroView.java
+++ b/src/com/android/tv/ui/IntroView.java
@@ -22,7 +22,6 @@ import android.graphics.drawable.AnimationDrawable;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.View;
-
import com.android.tv.R;
import com.android.tv.menu.Menu;
@@ -95,20 +94,21 @@ public class IntroView extends FullscreenDialogView {
}
@Override
- protected void onStartExitAnimation(TimeInterpolator interpolator, long duration,
- final Runnable onAnimationEnded) {
+ protected void onStartExitAnimation(
+ TimeInterpolator interpolator, long duration, final Runnable onAnimationEnded) {
View v = findViewById(R.id.container);
v.animate()
.alpha(0.0f)
.setInterpolator(interpolator)
.setDuration(duration)
.withLayer()
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- onAnimationEnded.run();
- }
- })
+ .withEndAction(
+ new Runnable() {
+ @Override
+ public void run() {
+ onAnimationEnded.run();
+ }
+ })
.start();
}
}
diff --git a/src/com/android/tv/ui/KeypadChannelSwitchView.java b/src/com/android/tv/ui/KeypadChannelSwitchView.java
index ac5d841d..e2625811 100644
--- a/src/com/android/tv/ui/KeypadChannelSwitchView.java
+++ b/src/com/android/tv/ui/KeypadChannelSwitchView.java
@@ -35,21 +35,19 @@ import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
-
import com.android.tv.MainActivity;
import com.android.tv.R;
-import com.android.tv.TvApplication;
-import com.android.tv.util.DurationTimer;
+import com.android.tv.TvSingletons;
import com.android.tv.analytics.Tracker;
import com.android.tv.common.SoftPreconditions;
-import com.android.tv.data.Channel;
+import com.android.tv.common.util.DurationTimer;
import com.android.tv.data.ChannelNumber;
-
+import com.android.tv.data.api.Channel;
import java.util.ArrayList;
import java.util.List;
-public class KeypadChannelSwitchView extends LinearLayout implements
- TvTransitionManager.TransitionLayout {
+public class KeypadChannelSwitchView extends LinearLayout
+ implements TvTransitionManager.TransitionLayout {
private static final String TAG = "KeypadChannelSwitchView";
private static final int MAX_CHANNEL_NUMBER_DIGIT = 4;
@@ -62,7 +60,7 @@ public class KeypadChannelSwitchView extends LinearLayout implements
private final Tracker mTracker;
private final DurationTimer mViewDurationTimer = new DurationTimer();
private boolean mNavigated = false;
- @Nullable //Once mChannels is set to null it should not be used again.
+ @Nullable // Once mChannels is set to null it should not be used again.
private List<Channel> mChannels;
private TextView mChannelNumberView;
private ListView mChannelItemListView;
@@ -72,23 +70,29 @@ public class KeypadChannelSwitchView extends LinearLayout implements
private final LayoutInflater mLayoutInflater;
private Channel mSelectedChannel;
- private final Runnable mHideRunnable = new Runnable() {
- @Override
- public void run() {
- mCurrentHeight = 0;
- if (mSelectedChannel != null) {
- mMainActivity.tuneToChannel(mSelectedChannel);
- mTracker.sendChannelNumberItemChosenByTimeout();
- } else {
- mMainActivity.getOverlayManager().hideOverlays(
- TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_DIALOG
- | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANELS
- | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_PROGRAM_GUIDE
- | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_MENU
- | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_FRAGMENT);
- }
- }
- };
+ private final Runnable mHideRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ mCurrentHeight = 0;
+ if (mSelectedChannel != null) {
+ mMainActivity.tuneToChannel(mSelectedChannel);
+ mTracker.sendChannelNumberItemChosenByTimeout();
+ } else {
+ mMainActivity
+ .getOverlayManager()
+ .hideOverlays(
+ TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_DIALOG
+ | TvOverlayManager
+ .FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANELS
+ | TvOverlayManager
+ .FLAG_HIDE_OVERLAYS_KEEP_PROGRAM_GUIDE
+ | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_MENU
+ | TvOverlayManager
+ .FLAG_HIDE_OVERLAYS_KEEP_FRAGMENT);
+ }
+ }
+ };
private final long mShowDurationMillis;
private final long mRippleAnimDurationMillis;
private final int mBaseViewHeight;
@@ -112,65 +116,71 @@ public class KeypadChannelSwitchView extends LinearLayout implements
super(context, attrs, defStyleAttr);
mMainActivity = (MainActivity) context;
- mTracker = TvApplication.getSingletons(context).getTracker();
+ mTracker = TvSingletons.getSingletons(context).getTracker();
Resources resources = getResources();
mLayoutInflater = LayoutInflater.from(context);
mShowDurationMillis = resources.getInteger(R.integer.keypad_channel_switch_show_duration);
- mRippleAnimDurationMillis = resources.getInteger(
- R.integer.keypad_channel_switch_ripple_anim_duration);
- mBaseViewHeight = resources.getDimensionPixelSize(
- R.dimen.keypad_channel_switch_base_height);
+ mRippleAnimDurationMillis =
+ resources.getInteger(R.integer.keypad_channel_switch_ripple_anim_duration);
+ mBaseViewHeight =
+ resources.getDimensionPixelSize(R.dimen.keypad_channel_switch_base_height);
mItemHeight = resources.getDimensionPixelSize(R.dimen.keypad_channel_switch_item_height);
mResizeAnimDuration = resources.getInteger(R.integer.keypad_channel_switch_anim_duration);
- mResizeInterpolator = AnimationUtils.loadInterpolator(context,
- android.R.interpolator.linear_out_slow_in);
+ mResizeInterpolator =
+ AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in);
}
@Override
- protected void onFinishInflate(){
+ protected void onFinishInflate() {
super.onFinishInflate();
mChannelNumberView = (TextView) findViewById(R.id.channel_number);
mChannelItemListView = (ListView) findViewById(R.id.channel_list);
mChannelItemListView.setAdapter(mAdapter);
- mChannelItemListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- if (position >= mAdapter.getCount()) {
- // It can happen during closing.
- return;
- }
- mChannelItemListView.setFocusable(false);
- final Channel channel = ((Channel) mAdapter.getItem(position));
- postDelayed(new Runnable() {
+ mChannelItemListView.setOnItemClickListener(
+ new AdapterView.OnItemClickListener() {
@Override
- public void run() {
- mChannelItemListView.setFocusable(true);
- mMainActivity.tuneToChannel(channel);
- mTracker.sendChannelNumberItemClicked();
+ public void onItemClick(
+ AdapterView<?> parent, View view, int position, long id) {
+ if (position >= mAdapter.getCount()) {
+ // It can happen during closing.
+ return;
+ }
+ mChannelItemListView.setFocusable(false);
+ final Channel channel = ((Channel) mAdapter.getItem(position));
+ postDelayed(
+ new Runnable() {
+ @Override
+ public void run() {
+ mChannelItemListView.setFocusable(true);
+ mMainActivity.tuneToChannel(channel);
+ mTracker.sendChannelNumberItemClicked();
+ }
+ },
+ mRippleAnimDurationMillis);
+ }
+ });
+ mChannelItemListView.setOnItemSelectedListener(
+ new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(
+ AdapterView<?> parent, View view, int position, long id) {
+ if (position >= mAdapter.getCount()) {
+ // It can happen during closing.
+ mSelectedChannel = null;
+ } else {
+ mSelectedChannel = (Channel) mAdapter.getItem(position);
+ }
+ if (position != 0 && !mNavigated) {
+ mNavigated = true;
+ mTracker.sendChannelInputNavigated();
+ }
}
- }, mRippleAnimDurationMillis);
- }
- });
- mChannelItemListView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- if (position >= mAdapter.getCount()) {
- // It can happen during closing.
- mSelectedChannel = null;
- } else {
- mSelectedChannel = (Channel) mAdapter.getItem(position);
- }
- if (position != 0 && !mNavigated) {
- mNavigated = true;
- mTracker.sendChannelInputNavigated();
- }
- }
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- mSelectedChannel = null;
- }
- });
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ mSelectedChannel = null;
+ }
+ });
}
@Override
@@ -276,8 +286,13 @@ public class KeypadChannelSwitchView extends LinearLayout implements
for (Channel channel : mChannels) {
ChannelNumber chNumber = ChannelNumber.parseChannelNumber(channel.getDisplayNumber());
if (chNumber == null) {
- Log.i(TAG, "Malformed channel number (name=" + channel.getDisplayName()
- + ", number=" + channel.getDisplayNumber() + ")");
+ Log.i(
+ TAG,
+ "Malformed channel number (name="
+ + channel.getDisplayName()
+ + ", number="
+ + channel.getDisplayNumber()
+ + ")");
continue;
}
if (matchChannelNumber(mTypedChannelNumber, chNumber)) {
@@ -286,7 +301,8 @@ public class KeypadChannelSwitchView extends LinearLayout implements
// Even if a user doesn't type '-', we need to match the typed number to not only
// the major number but also the minor number. For example, when a user types '111'
// without delimiter, it should be matched to '111', '1-11' and '11-1'.
- if (channel.getDisplayNumber().replaceAll(CHANNEL_DELIMITERS_REGEX, "")
+ if (channel.getDisplayNumber()
+ .replaceAll(CHANNEL_DELIMITERS_REGEX, "")
.startsWith(mTypedChannelNumber.majorNumber)) {
secondaryChannelCandidates.add(channel);
}
@@ -315,7 +331,7 @@ public class KeypadChannelSwitchView extends LinearLayout implements
// Do not add the resize animation when the banner has not been shown before.
mCurrentHeight = targetHeight;
setViewHeight(this, targetHeight);
- } else if (mCurrentHeight != targetHeight){
+ } else if (mCurrentHeight != targetHeight) {
mResizeAnimator = createResizeAnimator(targetHeight);
mResizeAnimator.start();
}
@@ -323,21 +339,23 @@ public class KeypadChannelSwitchView extends LinearLayout implements
private Animator createResizeAnimator(int targetHeight) {
ValueAnimator animator = ValueAnimator.ofInt(mCurrentHeight, targetHeight);
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- int value = (Integer) animation.getAnimatedValue();
- setViewHeight(KeypadChannelSwitchView.this, value);
- mCurrentHeight = value;
- }
- });
+ animator.addUpdateListener(
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ int value = (Integer) animation.getAnimatedValue();
+ setViewHeight(KeypadChannelSwitchView.this, value);
+ mCurrentHeight = value;
+ }
+ });
animator.setDuration(mResizeAnimDuration);
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animator) {
- mResizeAnimator = null;
- }
- });
+ animator.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ mResizeAnimator = null;
+ }
+ });
animator.setInterpolator(mResizeInterpolator);
return animator;
}
diff --git a/src/com/android/tv/ui/OnRepeatedKeyInterceptListener.java b/src/com/android/tv/ui/OnRepeatedKeyInterceptListener.java
index 63ee199d..9b916afe 100644
--- a/src/com/android/tv/ui/OnRepeatedKeyInterceptListener.java
+++ b/src/com/android/tv/ui/OnRepeatedKeyInterceptListener.java
@@ -21,18 +21,15 @@ import android.support.v17.leanback.widget.VerticalGridView;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
-
import com.android.tv.common.WeakHandler;
-/**
- * Listener to make focus change faster over time.
- */
+/** Listener to make focus change faster over time. */
public class OnRepeatedKeyInterceptListener implements VerticalGridView.OnKeyInterceptListener {
private static final String TAG = "OnRepeatedKeyListener";
private static final boolean DEBUG = false;
- private static final int[] THRESHOLD_FAST_FOCUS_CHANGE_TIME_MS = { 2000, 5000 };
- private static final int[] MAX_SKIPPED_VIEW_COUNT = { 1, 4 };
+ private static final int[] THRESHOLD_FAST_FOCUS_CHANGE_TIME_MS = {2000, 5000};
+ private static final int[] MAX_SKIPPED_VIEW_COUNT = {1, 4};
private static final int MSG_MOVE_FOCUS = 1000;
private final VerticalGridView mView;
@@ -52,21 +49,20 @@ public class OnRepeatedKeyInterceptListener implements VerticalGridView.OnKeyInt
@Override
public boolean onInterceptKeyEvent(KeyEvent event) {
mHandler.removeMessages(MSG_MOVE_FOCUS);
- if (event.getKeyCode() != KeyEvent.KEYCODE_DPAD_UP &&
- event.getKeyCode() != KeyEvent.KEYCODE_DPAD_DOWN) {
+ if (event.getKeyCode() != KeyEvent.KEYCODE_DPAD_UP
+ && event.getKeyCode() != KeyEvent.KEYCODE_DPAD_DOWN) {
return false;
}
long duration = event.getEventTime() - event.getDownTime();
- if (duration < THRESHOLD_FAST_FOCUS_CHANGE_TIME_MS[0]
- || event.isCanceled()) {
+ if (duration < THRESHOLD_FAST_FOCUS_CHANGE_TIME_MS[0] || event.isCanceled()) {
mFocusAccelerated = false;
return false;
}
- mDirection = event.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP ? View.FOCUS_UP
- : View.FOCUS_DOWN;
+ mDirection =
+ event.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP ? View.FOCUS_UP : View.FOCUS_DOWN;
int skippedViewCount = MAX_SKIPPED_VIEW_COUNT[0];
- for (int i = 1 ;i < THRESHOLD_FAST_FOCUS_CHANGE_TIME_MS.length; ++i) {
+ for (int i = 1; i < THRESHOLD_FAST_FOCUS_CHANGE_TIME_MS.length; ++i) {
if (THRESHOLD_FAST_FOCUS_CHANGE_TIME_MS[i] < duration) {
skippedViewCount = MAX_SKIPPED_VIEW_COUNT[i];
} else {
@@ -83,8 +79,8 @@ public class OnRepeatedKeyInterceptListener implements VerticalGridView.OnKeyInt
mFocusAccelerated = false;
}
for (int i = 0; i < skippedViewCount; ++i) {
- mHandler.sendEmptyMessageDelayed(MSG_MOVE_FOCUS,
- mRepeatedKeyInterval * i / (skippedViewCount + 1));
+ mHandler.sendEmptyMessageDelayed(
+ MSG_MOVE_FOCUS, mRepeatedKeyInterval * i / (skippedViewCount + 1));
}
if (DEBUG) Log.d(TAG, "onInterceptKeyEvent: focused view " + mView.findFocus());
return false;
diff --git a/src/com/android/tv/ui/SelectInputView.java b/src/com/android/tv/ui/SelectInputView.java
index dc92111c..f4949f08 100644
--- a/src/com/android/tv/ui/SelectInputView.java
+++ b/src/com/android/tv/ui/SelectInputView.java
@@ -32,23 +32,20 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
-
-import com.android.tv.ApplicationSingletons;
import com.android.tv.R;
-import com.android.tv.TvApplication;
-import com.android.tv.util.DurationTimer;
+import com.android.tv.TvSingletons;
import com.android.tv.analytics.Tracker;
-import com.android.tv.data.Channel;
+import com.android.tv.common.util.DurationTimer;
+import com.android.tv.data.api.Channel;
import com.android.tv.util.TvInputManagerHelper;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-public class SelectInputView extends VerticalGridView implements
- TvTransitionManager.TransitionLayout {
+public class SelectInputView extends VerticalGridView
+ implements TvTransitionManager.TransitionLayout {
private static final String TAG = "SelectInputView";
private static final boolean DEBUG = false;
public static final String SCREEN_NAME = "Input selection";
@@ -59,66 +56,68 @@ public class SelectInputView extends VerticalGridView implements
private final TvInputManagerHelper.HardwareInputComparator mComparator;
private final Tracker mTracker;
private final DurationTimer mViewDurationTimer = new DurationTimer();
- private final TvInputCallback mTvInputCallback = new TvInputCallback() {
- @Override
- public void onInputAdded(String inputId) {
- buildInputListAndNotify();
- updateSelectedPositionIfNeeded();
- }
+ private final TvInputCallback mTvInputCallback =
+ new TvInputCallback() {
+ @Override
+ public void onInputAdded(String inputId) {
+ buildInputListAndNotify();
+ updateSelectedPositionIfNeeded();
+ }
- @Override
- public void onInputRemoved(String inputId) {
- buildInputListAndNotify();
- updateSelectedPositionIfNeeded();
- }
+ @Override
+ public void onInputRemoved(String inputId) {
+ buildInputListAndNotify();
+ updateSelectedPositionIfNeeded();
+ }
- @Override
- public void onInputUpdated(String inputId) {
- buildInputListAndNotify();
- updateSelectedPositionIfNeeded();
- }
+ @Override
+ public void onInputUpdated(String inputId) {
+ buildInputListAndNotify();
+ updateSelectedPositionIfNeeded();
+ }
- @Override
- public void onInputStateChanged(String inputId, int state) {
- buildInputListAndNotify();
- updateSelectedPositionIfNeeded();
- }
+ @Override
+ public void onInputStateChanged(String inputId, int state) {
+ buildInputListAndNotify();
+ updateSelectedPositionIfNeeded();
+ }
- private void updateSelectedPositionIfNeeded() {
- if (!isFocusable() || mSelectedInput == null) {
- return;
- }
- if (!isInputEnabled(mSelectedInput)) {
- setSelectedPosition(TUNER_INPUT_POSITION);
- return;
- }
- if (getInputPosition(mSelectedInput.getId()) != getSelectedPosition()) {
- setSelectedPosition(getInputPosition(mSelectedInput.getId()));
- }
- }
- };
+ private void updateSelectedPositionIfNeeded() {
+ if (!isFocusable() || mSelectedInput == null) {
+ return;
+ }
+ if (!isInputEnabled(mSelectedInput)) {
+ setSelectedPosition(TUNER_INPUT_POSITION);
+ return;
+ }
+ if (getInputPosition(mSelectedInput.getId()) != getSelectedPosition()) {
+ setSelectedPosition(getInputPosition(mSelectedInput.getId()));
+ }
+ }
+ };
private Channel mCurrentChannel;
private OnInputSelectedCallback mCallback;
- private final Runnable mHideRunnable = new Runnable() {
- @Override
- public void run() {
- if (mSelectedInput == null) {
- return;
- }
- // TODO: pass english label to tracker http://b/22355024
- final String label = mSelectedInput.loadLabel(getContext()).toString();
- mTracker.sendInputSelected(label);
- if (mCallback != null) {
- if (mSelectedInput.isPassthroughInput()) {
- mCallback.onPassthroughInputSelected(mSelectedInput);
- } else {
- mCallback.onTunerInputSelected();
+ private final Runnable mHideRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mSelectedInput == null) {
+ return;
+ }
+ // TODO: pass english label to tracker http://b/22355024
+ final String label = mSelectedInput.loadLabel(getContext()).toString();
+ mTracker.sendInputSelected(label);
+ if (mCallback != null) {
+ if (mSelectedInput.isPassthroughInput()) {
+ mCallback.onPassthroughInputSelected(mSelectedInput);
+ } else {
+ mCallback.onTunerInputSelected();
+ }
+ }
}
- }
- }
- };
+ };
private final int mInputItemHeight;
private final long mShowDurationMillis;
@@ -144,23 +143,23 @@ public class SelectInputView extends VerticalGridView implements
super(context, attrs, defStyleAttr);
setAdapter(new InputListAdapter());
- ApplicationSingletons appSingletons = TvApplication.getSingletons(context);
- mTracker = appSingletons.getTracker();
- mTvInputManagerHelper = appSingletons.getTvInputManagerHelper();
+ TvSingletons tvSingletons = TvSingletons.getSingletons(context);
+ mTracker = tvSingletons.getTracker();
+ mTvInputManagerHelper = tvSingletons.getTvInputManagerHelper();
mComparator =
new TvInputManagerHelper.HardwareInputComparator(context, mTvInputManagerHelper);
Resources resources = context.getResources();
mInputItemHeight = resources.getDimensionPixelSize(R.dimen.input_banner_item_height);
mShowDurationMillis = resources.getInteger(R.integer.select_input_show_duration);
- mRippleAnimDurationMillis = resources.getInteger(
- R.integer.select_input_ripple_anim_duration);
+ mRippleAnimDurationMillis =
+ resources.getInteger(R.integer.select_input_ripple_anim_duration);
mTextColorPrimary = resources.getColor(R.color.select_input_text_color_primary, null);
mTextColorSecondary = resources.getColor(R.color.select_input_text_color_secondary, null);
mTextColorDisabled = resources.getColor(R.color.select_input_text_color_disabled, null);
- mItemViewForMeasure = LayoutInflater.from(context).inflate(
- R.layout.select_input_item, this, false);
+ mItemViewForMeasure =
+ LayoutInflater.from(context).inflate(R.layout.select_input_item, this, false);
buildInputListAndNotify();
}
@@ -199,8 +198,10 @@ public class SelectInputView extends VerticalGridView implements
mResetTransitionAlpha = fromEmptyScene;
buildInputListAndNotify();
mTvInputManagerHelper.addCallback(mTvInputCallback);
- String currentInputId = mCurrentChannel != null && mCurrentChannel.isPassthrough() ?
- mCurrentChannel.getInputId() : null;
+ String currentInputId =
+ mCurrentChannel != null && mCurrentChannel.isPassthrough()
+ ? mCurrentChannel.getInputId()
+ : null;
if (currentInputId != null
&& !isInputEnabled(mTvInputManagerHelper.getTvInputInfo(currentInputId))) {
// If current input is disabled, the tuner input will be focused.
@@ -233,7 +234,8 @@ public class SelectInputView extends VerticalGridView implements
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = mInputItemHeight * mInputList.size();
- super.onMeasure(MeasureSpec.makeMeasureSpec(mMaxItemWidth, MeasureSpec.EXACTLY),
+ super.onMeasure(
+ MeasureSpec.makeMeasureSpec(mMaxItemWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
}
@@ -299,16 +301,14 @@ public class SelectInputView extends VerticalGridView implements
!= TvInputManager.INPUT_STATE_DISCONNECTED;
}
- /**
- * Sets a callback which receives the notifications of input selection.
- */
+ /** Sets a callback which receives the notifications of input selection. */
public void setOnInputSelectedCallback(OnInputSelectedCallback callback) {
mCallback = callback;
}
/**
- * Sets the current channel. The initial selection will be the input which contains the
- * {@code channel}.
+ * Sets the current channel. The initial selection will be the input which contains the {@code
+ * channel}.
*/
public void setCurrentChannel(Channel channel) {
mCurrentChannel = channel;
@@ -317,8 +317,9 @@ public class SelectInputView extends VerticalGridView implements
class InputListAdapter extends RecyclerView.Adapter<InputListAdapter.ViewHolder> {
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- View v = LayoutInflater.from(parent.getContext()).inflate(
- R.layout.select_input_item, parent, false);
+ View v =
+ LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.select_input_item, parent, false);
return new ViewHolder(v);
}
@@ -343,25 +344,29 @@ public class SelectInputView extends VerticalGridView implements
holder.secondaryInputLabelView.setVisibility(View.GONE);
}
- holder.itemView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mSelectedInput = mInputList.get(position);
- // The user made a selection. Hide this view after the ripple animation. But
- // first, disable focus to avoid any further focus change during the animation.
- setFocusable(false);
- removeCallbacks(mHideRunnable);
- postDelayed(mHideRunnable, mRippleAnimDurationMillis);
- }
- });
- holder.itemView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
- @Override
- public void onFocusChange(View view, boolean hasFocus) {
- if (hasFocus) {
- mSelectedInput = mInputList.get(position);
- }
- }
- });
+ holder.itemView.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mSelectedInput = mInputList.get(position);
+ // The user made a selection. Hide this view after the ripple animation.
+ // But
+ // first, disable focus to avoid any further focus change during the
+ // animation.
+ setFocusable(false);
+ removeCallbacks(mHideRunnable);
+ postDelayed(mHideRunnable, mRippleAnimDurationMillis);
+ }
+ });
+ holder.itemView.setOnFocusChangeListener(
+ new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View view, boolean hasFocus) {
+ if (hasFocus) {
+ mSelectedInput = mInputList.get(position);
+ }
+ }
+ });
if (mResetTransitionAlpha) {
ViewUtils.setTransitionAlpha(holder.itemView, 1f);
@@ -385,18 +390,12 @@ public class SelectInputView extends VerticalGridView implements
}
}
- /**
- * A callback interface for the input selection.
- */
+ /** A callback interface for the input selection. */
public interface OnInputSelectedCallback {
- /**
- * Called when the tuner input is selected.
- */
+ /** Called when the tuner input is selected. */
void onTunerInputSelected();
- /**
- * Called when the passthrough input is selected.
- */
+ /** Called when the passthrough input is selected. */
void onPassthroughInputSelected(@NonNull TvInputInfo input);
}
}
diff --git a/src/com/android/tv/ui/TunableTvView.java b/src/com/android/tv/ui/TunableTvView.java
index 48386698..bb98d974 100644
--- a/src/com/android/tv/ui/TunableTvView.java
+++ b/src/com/android/tv/ui/TunableTvView.java
@@ -57,37 +57,37 @@ import android.view.SurfaceView;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
-
-import com.android.tv.ApplicationSingletons;
-import com.android.tv.Features;
import com.android.tv.InputSessionManager;
import com.android.tv.InputSessionManager.TvViewSession;
import com.android.tv.R;
-import com.android.tv.TvApplication;
-import com.android.tv.data.Program;
-import com.android.tv.data.ProgramDataManager;
-import com.android.tv.parental.ParentalControlSettings;
-import com.android.tv.util.DurationTimer;
-import com.android.tv.util.Debug;
+import com.android.tv.TvFeatures;
+import com.android.tv.TvSingletons;
import com.android.tv.analytics.Tracker;
import com.android.tv.common.BuildConfig;
+import com.android.tv.common.CommonConstants;
import com.android.tv.common.feature.CommonFeatures;
-import com.android.tv.data.Channel;
+import com.android.tv.common.util.CommonUtils;
+import com.android.tv.common.util.Debug;
+import com.android.tv.common.util.DurationTimer;
+import com.android.tv.common.util.PermissionUtils;
+import com.android.tv.data.Program;
+import com.android.tv.data.ProgramDataManager;
import com.android.tv.data.StreamInfo;
import com.android.tv.data.WatchedHistoryManager;
+import com.android.tv.data.api.Channel;
import com.android.tv.parental.ContentRatingsManager;
+import com.android.tv.parental.ParentalControlSettings;
import com.android.tv.recommendation.NotificationService;
-import com.android.tv.util.ImageLoader;
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 com.android.tv.util.images.ImageLoader;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
-public class TunableTvView extends FrameLayout implements StreamInfo {
+/** Includes the real {@link AppLayerTvView} handling tuning, block and other display events. */
+public class TunableTvView extends FrameLayout implements StreamInfo, TunableTvViewPlayingApi {
private static final boolean DEBUG = false;
private static final String TAG = "TunableTvView";
@@ -96,20 +96,29 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
public static final int VIDEO_UNAVAILABLE_REASON_SCREEN_BLOCKED = -3;
public static final int VIDEO_UNAVAILABLE_REASON_NONE = -100;
+ private OnTalkBackDpadKeyListener mOnTalkBackDpadKeyListener;
+
@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";
+ CommonConstants.BASE_PACKAGE + ".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 })
+ @IntDef({
+ TIME_SHIFT_STATE_NONE,
+ TIME_SHIFT_STATE_PLAY,
+ TIME_SHIFT_STATE_PAUSE,
+ 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;
@@ -128,8 +137,7 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
private ContentRatingsManager mContentRatingsManager;
private ParentalControlSettings mParentalControlSettings;
private ProgramDataManager mProgramDataManager;
- @Nullable
- private WatchedHistoryManager mWatchedHistoryManager;
+ @Nullable private WatchedHistoryManager mWatchedHistoryManager;
private boolean mStarted;
private String mTagetInputId;
private TvInputInfo mInputInfo;
@@ -182,230 +190,258 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
private final ConnectivityManager mConnectivityManager;
private final InputSessionManager mInputSessionManager;
- private final TvInputCallback mCallback = new TvInputCallback() {
- @Override
- public void onConnectionFailed(String inputId) {
- Log.w(TAG, "Failed to bind an input");
- mTracker.sendInputConnectionFailure(inputId);
- Channel channel = mCurrentChannel;
- mCurrentChannel = null;
- mInputInfo = null;
- mCanReceiveInputEvent = false;
- if (mOnTuneListener != null) {
- // If tune is called inside onTuneFailed, mOnTuneListener will be set to
- // a new instance. In order to avoid to clear the new mOnTuneListener,
- // we copy mOnTuneListener to l and clear mOnTuneListener before
- // calling onTuneFailed.
- OnTuneListener listener = mOnTuneListener;
- mOnTuneListener = null;
- listener.onTuneFailed(channel);
- }
- }
-
- @Override
- public void onDisconnected(String inputId) {
- Log.w(TAG, "Session is released by crash");
- mTracker.sendInputDisconnected(inputId);
- Channel channel = mCurrentChannel;
- mCurrentChannel = null;
- mInputInfo = null;
- mCanReceiveInputEvent = false;
- if (mOnTuneListener != null) {
- OnTuneListener listener = mOnTuneListener;
- mOnTuneListener = null;
- listener.onUnexpectedStop(channel);
- }
- }
-
- @Override
- public void onChannelRetuned(String inputId, Uri channelUri) {
- if (DEBUG) {
- Log.d(TAG, "onChannelRetuned(inputId=" + inputId + ", channelUri="
- + channelUri + ")");
- }
- if (mOnTuneListener != null) {
- mOnTuneListener.onChannelRetuned(channelUri);
- }
- }
+ private final TvInputCallback mCallback =
+ new TvInputCallback() {
+ @Override
+ public void onConnectionFailed(String inputId) {
+ Log.w(TAG, "Failed to bind an input");
+ mTracker.sendInputConnectionFailure(inputId);
+ Channel channel = mCurrentChannel;
+ mCurrentChannel = null;
+ mInputInfo = null;
+ mCanReceiveInputEvent = false;
+ if (mOnTuneListener != null) {
+ // If tune is called inside onTuneFailed, mOnTuneListener will be set to
+ // a new instance. In order to avoid to clear the new mOnTuneListener,
+ // we copy mOnTuneListener to l and clear mOnTuneListener before
+ // calling onTuneFailed.
+ OnTuneListener listener = mOnTuneListener;
+ mOnTuneListener = null;
+ listener.onTuneFailed(channel);
+ }
+ }
- @Override
- public void onTracksChanged(String inputId, List<TvTrackInfo> tracks) {
- mHasClosedCaption = false;
- for (TvTrackInfo track : tracks) {
- if (track.getType() == TvTrackInfo.TYPE_SUBTITLE) {
- mHasClosedCaption = true;
- break;
+ @Override
+ public void onDisconnected(String inputId) {
+ Log.w(TAG, "Session is released by crash");
+ mTracker.sendInputDisconnected(inputId);
+ Channel channel = mCurrentChannel;
+ mCurrentChannel = null;
+ mInputInfo = null;
+ mCanReceiveInputEvent = false;
+ if (mOnTuneListener != null) {
+ OnTuneListener listener = mOnTuneListener;
+ mOnTuneListener = null;
+ listener.onUnexpectedStop(channel);
+ }
}
- }
- if (mOnTuneListener != null) {
- mOnTuneListener.onStreamInfoChanged(TunableTvView.this);
- }
- }
- @Override
- public void onTrackSelected(String inputId, int type, String trackId) {
- if (trackId == null) {
- // A track is unselected.
- if (type == TvTrackInfo.TYPE_VIDEO) {
- mVideoWidth = 0;
- mVideoHeight = 0;
- mVideoFormat = StreamInfo.VIDEO_DEFINITION_LEVEL_UNKNOWN;
- mVideoFrameRate = 0f;
- mVideoDisplayAspectRatio = 0f;
- } else if (type == TvTrackInfo.TYPE_AUDIO) {
- mAudioChannelCount = StreamInfo.AUDIO_CHANNEL_COUNT_UNKNOWN;
+ @Override
+ public void onChannelRetuned(String inputId, Uri channelUri) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "onChannelRetuned(inputId="
+ + inputId
+ + ", channelUri="
+ + channelUri
+ + ")");
+ }
+ if (mOnTuneListener != null) {
+ mOnTuneListener.onChannelRetuned(channelUri);
+ }
}
- } else {
- List<TvTrackInfo> tracks = getTracks(type);
- boolean trackFound = false;
- if (tracks != null) {
+
+ @Override
+ public void onTracksChanged(String inputId, List<TvTrackInfo> tracks) {
+ mHasClosedCaption = false;
for (TvTrackInfo track : tracks) {
- if (track.getId().equals(trackId)) {
- if (type == TvTrackInfo.TYPE_VIDEO) {
- mVideoWidth = track.getVideoWidth();
- mVideoHeight = track.getVideoHeight();
- mVideoFormat = Utils.getVideoDefinitionLevelFromSize(
- mVideoWidth, mVideoHeight);
- mVideoFrameRate = track.getVideoFrameRate();
- if (mVideoWidth <= 0 || mVideoHeight <= 0) {
- mVideoDisplayAspectRatio = 0.0f;
- } else {
- float VideoPixelAspectRatio =
- track.getVideoPixelAspectRatio();
- mVideoDisplayAspectRatio = VideoPixelAspectRatio
- * mVideoWidth / mVideoHeight;
- }
- } else if (type == TvTrackInfo.TYPE_AUDIO) {
- mAudioChannelCount = track.getAudioChannelCount();
- }
- trackFound = true;
+ if (track.getType() == TvTrackInfo.TYPE_SUBTITLE) {
+ mHasClosedCaption = true;
break;
}
}
+ if (mOnTuneListener != null) {
+ mOnTuneListener.onStreamInfoChanged(TunableTvView.this);
+ }
}
- if (!trackFound) {
- Log.w(TAG, "Invalid track ID: " + trackId);
+
+ @Override
+ public void onTrackSelected(String inputId, int type, String trackId) {
+ if (trackId == null) {
+ // A track is unselected.
+ if (type == TvTrackInfo.TYPE_VIDEO) {
+ mVideoWidth = 0;
+ mVideoHeight = 0;
+ mVideoFormat = StreamInfo.VIDEO_DEFINITION_LEVEL_UNKNOWN;
+ mVideoFrameRate = 0f;
+ mVideoDisplayAspectRatio = 0f;
+ } else if (type == TvTrackInfo.TYPE_AUDIO) {
+ mAudioChannelCount = StreamInfo.AUDIO_CHANNEL_COUNT_UNKNOWN;
+ }
+ } else {
+ List<TvTrackInfo> tracks = getTracks(type);
+ boolean trackFound = false;
+ if (tracks != null) {
+ for (TvTrackInfo track : tracks) {
+ if (track.getId().equals(trackId)) {
+ if (type == TvTrackInfo.TYPE_VIDEO) {
+ mVideoWidth = track.getVideoWidth();
+ mVideoHeight = track.getVideoHeight();
+ mVideoFormat =
+ Utils.getVideoDefinitionLevelFromSize(
+ mVideoWidth, mVideoHeight);
+ mVideoFrameRate = track.getVideoFrameRate();
+ if (mVideoWidth <= 0 || mVideoHeight <= 0) {
+ mVideoDisplayAspectRatio = 0.0f;
+ } else {
+ float VideoPixelAspectRatio =
+ track.getVideoPixelAspectRatio();
+ mVideoDisplayAspectRatio =
+ VideoPixelAspectRatio
+ * mVideoWidth
+ / mVideoHeight;
+ }
+ } else if (type == TvTrackInfo.TYPE_AUDIO) {
+ mAudioChannelCount = track.getAudioChannelCount();
+ }
+ trackFound = true;
+ break;
+ }
+ }
+ }
+ if (!trackFound) {
+ Log.w(TAG, "Invalid track ID: " + trackId);
+ }
+ }
+ if (mOnTuneListener != null) {
+ mOnTuneListener.onStreamInfoChanged(TunableTvView.this);
+ }
}
- }
- if (mOnTuneListener != null) {
- mOnTuneListener.onStreamInfoChanged(TunableTvView.this);
- }
- }
- @Override
- public void onVideoAvailable(String inputId) {
- if (DEBUG) Log.d(TAG, "onVideoAvailable: {inputId=" + inputId + "}");
- Debug.getTimer(Debug.TAG_START_UP_TIMER).log("Start up of Live TV ends," +
- " TunableTvView.onVideoAvailable resets timer");
- long startUpDurationTime = Debug.getTimer(Debug.TAG_START_UP_TIMER).reset();
- Debug.removeTimer(Debug.TAG_START_UP_TIMER);
- if (BuildConfig.ENG && startUpDurationTime > Debug.TIME_START_UP_DURATION_THRESHOLD) {
- showAlertDialogForLongStartUp();
- }
- mVideoUnavailableReason = VIDEO_UNAVAILABLE_REASON_NONE;
- updateBlockScreenAndMuting();
- if (mOnTuneListener != null) {
- mOnTuneListener.onStreamInfoChanged(TunableTvView.this);
- }
- }
+ @Override
+ public void onVideoAvailable(String inputId) {
+ if (DEBUG) Log.d(TAG, "onVideoAvailable: {inputId=" + inputId + "}");
+ Debug.getTimer(Debug.TAG_START_UP_TIMER)
+ .log(
+ "Start up of Live TV ends,"
+ + " TunableTvView.onVideoAvailable resets timer");
+ long startUpDurationTime = Debug.getTimer(Debug.TAG_START_UP_TIMER).reset();
+ Debug.removeTimer(Debug.TAG_START_UP_TIMER);
+ if (BuildConfig.ENG
+ && startUpDurationTime > Debug.TIME_START_UP_DURATION_THRESHOLD) {
+ showAlertDialogForLongStartUp();
+ }
+ mVideoUnavailableReason = VIDEO_UNAVAILABLE_REASON_NONE;
+ updateBlockScreenAndMuting();
+ if (mOnTuneListener != null) {
+ mOnTuneListener.onStreamInfoChanged(TunableTvView.this);
+ }
+ }
- private void showAlertDialogForLongStartUp() {
- new AlertDialog.Builder(getContext()).setTitle(
- getContext().getString(R.string.settings_send_feedback))
- .setMessage("Because the start up time of Live channels is too long," +
- " please send feedback")
- .setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- Intent intent = new Intent(Intent.ACTION_APP_ERROR);
- ApplicationErrorReport report = new ApplicationErrorReport();
- report.packageName = report.processName = getContext()
- .getApplicationContext().getPackageName();
- report.time = System.currentTimeMillis();
- report.type = ApplicationErrorReport.TYPE_CRASH;
-
- // Add the crash info to add title of feedback automatically.
- ApplicationErrorReport.CrashInfo crash = new
- ApplicationErrorReport.CrashInfo();
- crash.exceptionClassName =
- "Live TV start up takes long time";
- crash.exceptionMessage =
- "The start up time of Live TV is too long";
- report.crashInfo = crash;
-
- intent.putExtra(Intent.EXTRA_BUG_REPORT, report);
- getContext().startActivity(intent);
- }
- })
- .setNegativeButton(android.R.string.no, null)
- .show();
- }
+ private void showAlertDialogForLongStartUp() {
+ new AlertDialog.Builder(getContext())
+ .setTitle(getContext().getString(R.string.settings_send_feedback))
+ .setMessage(
+ "Because the start up time of Live channels is too long,"
+ + " please send feedback")
+ .setPositiveButton(
+ android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(
+ DialogInterface dialogInterface, int i) {
+ Intent intent = new Intent(Intent.ACTION_APP_ERROR);
+ ApplicationErrorReport report =
+ new ApplicationErrorReport();
+ report.packageName =
+ report.processName =
+ getContext()
+ .getApplicationContext()
+ .getPackageName();
+ report.time = System.currentTimeMillis();
+ report.type = ApplicationErrorReport.TYPE_CRASH;
+
+ // Add the crash info to add title of feedback
+ // automatically.
+ ApplicationErrorReport.CrashInfo crash =
+ new ApplicationErrorReport.CrashInfo();
+ crash.exceptionClassName =
+ "Live TV start up takes long time";
+ crash.exceptionMessage =
+ "The start up time of Live TV is too long";
+ report.crashInfo = crash;
+
+ intent.putExtra(Intent.EXTRA_BUG_REPORT, report);
+ getContext().startActivity(intent);
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, null)
+ .show();
+ }
- @Override
- public void onVideoUnavailable(String inputId, int reason) {
- if (reason != TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING
- && reason != TvInputManager.VIDEO_UNAVAILABLE_REASON_BUFFERING) {
- Debug.getTimer(Debug.TAG_START_UP_TIMER).log(
- "TunableTvView.onVideoUnAvailable reason = (" + reason
- + ") and removes timer");
- Debug.removeTimer(Debug.TAG_START_UP_TIMER);
- } else {
- Debug.getTimer(Debug.TAG_START_UP_TIMER).log(
- "TunableTvView.onVideoUnAvailable reason = (" + reason + ")");
- }
- mVideoUnavailableReason = reason;
- if (closePipIfNeeded()) {
- return;
- }
- updateBlockScreenAndMuting();
- if (mOnTuneListener != null) {
- mOnTuneListener.onStreamInfoChanged(TunableTvView.this);
- }
- switch (reason) {
- case TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN:
- case TvInputManager.VIDEO_UNAVAILABLE_REASON_BUFFERING:
- case TvInputManager.VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL:
- mTracker.sendChannelVideoUnavailable(mCurrentChannel, reason);
- default:
- // do nothing
- }
- }
+ @Override
+ public void onVideoUnavailable(String inputId, int reason) {
+ if (reason != TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING
+ && reason != TvInputManager.VIDEO_UNAVAILABLE_REASON_BUFFERING) {
+ Debug.getTimer(Debug.TAG_START_UP_TIMER)
+ .log(
+ "TunableTvView.onVideoUnAvailable reason = ("
+ + reason
+ + ") and removes timer");
+ Debug.removeTimer(Debug.TAG_START_UP_TIMER);
+ } else {
+ Debug.getTimer(Debug.TAG_START_UP_TIMER)
+ .log("TunableTvView.onVideoUnAvailable reason = (" + reason + ")");
+ }
+ mVideoUnavailableReason = reason;
+ if (closePipIfNeeded()) {
+ return;
+ }
+ updateBlockScreenAndMuting();
+ if (mOnTuneListener != null) {
+ mOnTuneListener.onStreamInfoChanged(TunableTvView.this);
+ }
+ switch (reason) {
+ case TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN:
+ case TvInputManager.VIDEO_UNAVAILABLE_REASON_BUFFERING:
+ case TvInputManager.VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL:
+ mTracker.sendChannelVideoUnavailable(mCurrentChannel, reason);
+ break;
+ default:
+ // do nothing
+ }
+ }
- @Override
- public void onContentAllowed(String inputId) {
- mBlockedContentRating = null;
- updateBlockScreenAndMuting();
- if (mOnTuneListener != null) {
- mOnTuneListener.onContentAllowed();
- }
- }
+ @Override
+ public void onContentAllowed(String inputId) {
+ mBlockedContentRating = null;
+ updateBlockScreenAndMuting();
+ if (mOnTuneListener != null) {
+ mOnTuneListener.onContentAllowed();
+ }
+ }
- @Override
- public void onContentBlocked(String inputId, TvContentRating rating) {
- if (rating != null && rating.equals(mBlockedContentRating)) {
- return;
- }
- mBlockedContentRating = rating;
- if (closePipIfNeeded()) {
- return;
- }
- updateBlockScreenAndMuting();
- if (mOnTuneListener != null) {
- mOnTuneListener.onContentBlocked();
- }
- }
+ @Override
+ public void onContentBlocked(String inputId, TvContentRating rating) {
+ if (rating != null && rating.equals(mBlockedContentRating)) {
+ return;
+ }
+ mBlockedContentRating = rating;
+ if (closePipIfNeeded()) {
+ return;
+ }
+ updateBlockScreenAndMuting();
+ if (mOnTuneListener != null) {
+ mOnTuneListener.onContentBlocked();
+ }
+ }
- @Override
- public void onTimeShiftStatusChanged(String inputId, int status) {
- if (DEBUG) {
- Log.d(TAG, "onTimeShiftStatusChanged: {inputId=" + inputId + ", status=" + status +
- "}");
- }
- boolean available = status == TvInputManager.TIME_SHIFT_STATUS_AVAILABLE;
- setTimeShiftAvailable(available);
- }
- };
+ @Override
+ public void onTimeShiftStatusChanged(String inputId, int status) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "onTimeShiftStatusChanged: {inputId="
+ + inputId
+ + ", status="
+ + status
+ + "}");
+ }
+ boolean available = status == TvInputManager.TIME_SHIFT_STATUS_AVAILABLE;
+ setTimeShiftAvailable(available);
+ }
+ };
public TunableTvView(Context context) {
this(context, null);
@@ -423,49 +459,77 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
super(context, attrs, defStyleAttr, defStyleRes);
inflate(getContext(), R.layout.tunable_tv_view, this);
- ApplicationSingletons appSingletons = TvApplication.getSingletons(context);
+ TvSingletons tvSingletons = TvSingletons.getSingletons(context);
if (CommonFeatures.DVR.isEnabled(context)) {
- mInputSessionManager = appSingletons.getInputSessionManager();
+ mInputSessionManager = tvSingletons.getInputSessionManager();
} else {
mInputSessionManager = null;
}
- mInputManager = appSingletons.getTvInputManagerHelper();
- mConnectivityManager = (ConnectivityManager) context
- .getSystemService(Context.CONNECTIVITY_SERVICE);
+ mInputManager = tvSingletons.getTvInputManagerHelper();
+ mConnectivityManager =
+ (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
mCanModifyParentalControls = PermissionUtils.hasModifyParentalControls(context);
- mTracker = appSingletons.getTracker();
+ mTracker = tvSingletons.getTracker();
mBlockScreenType = BLOCK_SCREEN_TYPE_NORMAL;
mBlockScreenView = (BlockScreenView) findViewById(R.id.block_screen);
- mBlockScreenView.addInfoFadeInAnimationListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- adjustBlockScreenSpacingAndText();
- }
- });
+ mBlockScreenView.addInfoFadeInAnimationListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ adjustBlockScreenSpacingAndText();
+ }
+ });
mBufferingSpinnerView = findViewById(R.id.buffering_spinner);
- mTuningImageColorFilter = getResources()
- .getColor(R.color.tvview_block_image_color_filter, null);
+ mTuningImageColorFilter =
+ getResources().getColor(R.color.tvview_block_image_color_filter, null);
mDimScreenView = findViewById(R.id.dim_screen);
- mDimScreenView.animate().setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mActionAfterFade != null) {
- mActionAfterFade.run();
- }
- }
+ mDimScreenView
+ .animate()
+ .setListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mActionAfterFade != null) {
+ mActionAfterFade.run();
+ }
+ }
- @Override
- public void onAnimationCancel(Animator animation) {
- if (mActionAfterFade != null) {
- mActionAfterFade.run();
- }
- }
- });
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ if (mActionAfterFade != null) {
+ mActionAfterFade.run();
+ }
+ }
+ });
+ View placeholder = findViewById(R.id.placeholder);
+ placeholder.requestFocus();
+ findViewById(R.id.channel_up)
+ .setOnFocusChangeListener(
+ (v, hasFocus) -> {
+ if (hasFocus) {
+ placeholder.requestFocus();
+ if (mOnTalkBackDpadKeyListener != null) {
+ mOnTalkBackDpadKeyListener.onTalkBackDpadKey(
+ KeyEvent.KEYCODE_DPAD_UP);
+ }
+ }
+ });
+ findViewById(R.id.channel_down)
+ .setOnFocusChangeListener(
+ (v, hasFocus) -> {
+ if (hasFocus) {
+ placeholder.requestFocus();
+ if (mOnTalkBackDpadKeyListener != null) {
+ mOnTalkBackDpadKeyListener.onTalkBackDpadKey(
+ KeyEvent.KEYCODE_DPAD_DOWN);
+ }
+ }
+ });
}
- public void initialize(ProgramDataManager programDataManager,
- TvInputManagerHelper tvInputManagerHelper) {
+ public void initialize(
+ ProgramDataManager programDataManager, TvInputManagerHelper tvInputManagerHelper) {
mTvView = (AppLayerTvView) findViewById(R.id.tv_view);
mProgramDataManager = programDataManager;
mInputManagerHelper = tvInputManagerHelper;
@@ -482,9 +546,7 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
mStarted = true;
}
- /**
- * Warms up the input to reduce the start time.
- */
+ /** Warms up the input to reduce the start time. */
public void warmUpInput(String inputId, Uri channelUri) {
if (!mStarted && inputId != null && channelUri != null) {
if (mTvViewSession != null) {
@@ -506,16 +568,14 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
long duration = mChannelViewTimer.reset();
mTracker.sendChannelViewStop(mCurrentChannel, duration);
if (mWatchedHistoryManager != null && !mCurrentChannel.isPassthrough()) {
- mWatchedHistoryManager.logChannelViewStop(mCurrentChannel,
- System.currentTimeMillis(), duration);
+ mWatchedHistoryManager.logChannelViewStop(
+ mCurrentChannel, System.currentTimeMillis(), duration);
}
}
reset();
}
- /**
- * Releases the resources.
- */
+ /** Releases the resources. */
public void release() {
if (mInputSessionManager != null) {
mInputSessionManager.releaseTvViewSession(mTvViewSession);
@@ -523,18 +583,14 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
}
}
- /**
- * Resets TV view.
- */
+ /** Resets TV view. */
public void reset() {
resetInternal();
mVideoUnavailableReason = VIDEO_UNAVAILABLE_REASON_NOT_TUNED;
updateBlockScreenAndMuting();
}
- /**
- * Resets TV view to acquire the recording session.
- */
+ /** Resets TV view to acquire the recording session. */
public void resetByRecording() {
resetInternal();
}
@@ -560,20 +616,17 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
mWatchedHistoryManager = watchedHistoryManager;
}
- /**
- * Sets if the TunableTvView is under shrunken.
- */
+ /** Sets if the TunableTvView is under shrunken. */
public void setIsUnderShrunken(boolean isUnderShrunken) {
mIsUnderShrunken = isUnderShrunken;
}
+ @Override
public boolean isPlaying() {
return mStarted;
}
- /**
- * Called when parental control is changed.
- */
+ /** Called when parental control is changed. */
public void onParentalControlChanged(boolean enabled) {
mParentControlEnabled = enabled;
if (!enabled) {
@@ -586,8 +639,8 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
* Tunes to a channel with the {@code channelId}.
*
* @param params extra data to send it to TIS and store the data in TIMS.
- * @return false, if the TV input is not a proper state to tune to a channel. For example,
- * if the state is disconnected or channelId doesn't exist, it returns false.
+ * @return false, if the TV input is not a proper state to tune to a channel. For example, if
+ * the state is disconnected or channelId doesn't exist, it returns false.
*/
public boolean tuneTo(Channel channel, Bundle params, OnTuneListener listener) {
Debug.getTimer(Debug.TAG_START_UP_TIMER).log("TunableTvView.tuneTo");
@@ -603,24 +656,34 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
long duration = mChannelViewTimer.reset();
mTracker.sendChannelViewStop(mCurrentChannel, duration);
if (mWatchedHistoryManager != null && !mCurrentChannel.isPassthrough()) {
- mWatchedHistoryManager.logChannelViewStop(mCurrentChannel,
- System.currentTimeMillis(), duration);
+ mWatchedHistoryManager.logChannelViewStop(
+ mCurrentChannel, System.currentTimeMillis(), duration);
}
}
mOnTuneListener = listener;
mCurrentChannel = channel;
- boolean tunedByRecommendation = params != null
- && params.getString(NotificationService.TUNE_PARAMS_RECOMMENDATION_TYPE) != null;
+ boolean tunedByRecommendation =
+ params != null
+ && params.getString(NotificationService.TUNE_PARAMS_RECOMMENDATION_TYPE)
+ != null;
boolean needSurfaceSizeUpdate = false;
if (!inputInfo.equals(mInputInfo)) {
mTagetInputId = inputInfo.getId();
mInputInfo = inputInfo;
- mCanReceiveInputEvent = getContext().getPackageManager().checkPermission(
- PERMISSION_RECEIVE_INPUT_EVENT, mInputInfo.getServiceInfo().packageName)
+ mCanReceiveInputEvent =
+ getContext()
+ .getPackageManager()
+ .checkPermission(
+ PERMISSION_RECEIVE_INPUT_EVENT,
+ mInputInfo.getServiceInfo().packageName)
== PackageManager.PERMISSION_GRANTED;
if (DEBUG) {
- Log.d(TAG, "Input \'" + mInputInfo.getId() + "\' can receive input event: "
- + mCanReceiveInputEvent);
+ Log.d(
+ TAG,
+ "Input \'"
+ + mInputInfo.getId()
+ + "\' can receive input event: "
+ + mCanReceiveInputEvent);
}
needSurfaceSizeUpdate = true;
}
@@ -671,6 +734,7 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
mCurrentChannel = currentChannel;
}
+ @Override
public void setStreamVolume(float volume) {
if (!mStarted) {
throw new IllegalStateException("TvView isn't started");
@@ -683,13 +747,13 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
}
/**
- * Sets fixed size for the internal {@link android.view.Surface} of
- * {@link android.media.tv.TvView}. If either {@code width} or {@code height} is non positive,
- * the {@link android.view.Surface}'s size will be matched to the layout.
+ * Sets fixed size for the internal {@link android.view.Surface} of {@link
+ * android.media.tv.TvView}. If either {@code width} or {@code height} is non positive, the
+ * {@link android.view.Surface}'s size will be matched to the layout.
*
- * 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
+ * <p>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 android.view.SurfaceView.updateWindow().
*/
public void setFixedSurfaceSize(int width, int height) {
@@ -728,10 +792,15 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
public interface OnTuneListener {
void onTuneFailed(Channel channel);
+
void onUnexpectedStop(Channel channel);
+
void onStreamInfoChanged(StreamInfo info);
+
void onChannelRetuned(Uri channel);
+
void onContentBlocked();
+
void onContentAllowed();
}
@@ -759,9 +828,7 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
return mVideoFrameRate;
}
- /**
- * Returns displayed aspect ratio (video width / video height * pixel ratio).
- */
+ /** Returns displayed aspect ratio (video width / video height * pixel ratio). */
@Override
public float getVideoDisplayAspectRatio() {
return mVideoDisplayAspectRatio;
@@ -793,9 +860,7 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
return mVideoUnavailableReason;
}
- /**
- * Returns the {@link android.view.SurfaceView} of the {@link android.media.tv.TvView}.
- */
+ /** Returns the {@link android.view.SurfaceView} of the {@link android.media.tv.TvView}. */
private SurfaceView getSurfaceView() {
return (SurfaceView) mTvView.getChildAt(0);
}
@@ -804,6 +869,10 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
mTvView.setOnUnhandledInputEventListener(listener);
}
+ public void setOnTalkBackDpadKeyListener(OnTalkBackDpadKeyListener listener) {
+ mOnTalkBackDpadKeyListener = listener;
+ }
+
public void setClosedCaptionEnabled(boolean enabled) {
mTvView.setCaptionEnabled(enabled);
}
@@ -821,16 +890,16 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
}
/**
- * Gets {@link android.view.ViewGroup.MarginLayoutParams} of the underlying
- * {@link TvView}, which is the actual view to play live TV videos.
+ * Gets {@link android.view.ViewGroup.MarginLayoutParams} of the underlying {@link TvView},
+ * which is the actual view to play live TV videos.
*/
public MarginLayoutParams getTvViewLayoutParams() {
return (MarginLayoutParams) mTvView.getLayoutParams();
}
/**
- * Sets {@link android.view.ViewGroup.MarginLayoutParams} of the underlying
- * {@link TvView}, which is the actual view to play live TV videos.
+ * Sets {@link android.view.ViewGroup.MarginLayoutParams} of the underlying {@link TvView},
+ * which is the actual view to play live TV videos.
*/
public void setTvViewLayoutParams(MarginLayoutParams layoutParams) {
mTvView.setLayoutParams(layoutParams);
@@ -851,16 +920,12 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
return isScreenBlocked() || isContentBlocked();
}
- /**
- * Returns if the screen is blocked by {@link #blockOrUnblockScreen(boolean)}.
- */
+ /** Returns if the screen is blocked by {@link #blockOrUnblockScreen(boolean)}. */
public boolean isScreenBlocked() {
return mScreenBlocked;
}
- /**
- * Returns {@code true} if the content is blocked, otherwise {@code false}.
- */
+ /** Returns {@code true} if the content is blocked, otherwise {@code false}. */
public boolean isContentBlocked() {
return mBlockedContentRating != null;
}
@@ -869,18 +934,15 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
mOnScreenBlockedListener = listener;
}
- /**
- * Returns currently blocked content rating. {@code null} if it's not blocked.
- */
+ /** Returns currently blocked content rating. {@code null} if it's not blocked. */
@Override
public TvContentRating getBlockedContentRating() {
return mBlockedContentRating;
}
/**
- * Blocks/unblocks current TV screen and mutes.
- * There would be black screen with lock icon in order to show that
- * screen block is intended and not an error.
+ * Blocks/unblocks current TV screen and mutes. There would be black screen with lock icon in
+ * order to show that screen block is intended and not an error.
*
* @param blockOrUnblock {@code true} to block the screen, or {@code false} to unblock.
*/
@@ -909,8 +971,8 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
/**
* Set the type of block screen. If {@code type} is set to {@code BLOCK_SCREEN_TYPE_NO_UI}, the
* block screen will not show any description such as a lock icon and a text for the blocked
- * reason, if {@code type} is set to {@code BLOCK_SCREEN_TYPE_SHRUNKEN_TV_VIEW}, the block screen
- * will show the description for shrunken tv view (Small icon and short text), and if
+ * reason, if {@code type} is set to {@code BLOCK_SCREEN_TYPE_SHRUNKEN_TV_VIEW}, the block
+ * screen will show the description for shrunken tv view (Small icon and short text), and if
* {@code type} is set to {@code BLOCK_SCREEN_TYPE_NORMAL}, the block screen will show the
* description for normal tv view (Big icon and long text).
*
@@ -925,14 +987,16 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
private void updateBlockScreen(boolean animation) {
mBlockScreenView.endAnimations();
- int blockReason = (mScreenBlocked || mBlockedContentRating != null)
- && mParentControlEnabled ? VIDEO_UNAVAILABLE_REASON_SCREEN_BLOCKED
+ int blockReason =
+ (mScreenBlocked || mBlockedContentRating != null) && mParentControlEnabled
+ ? VIDEO_UNAVAILABLE_REASON_SCREEN_BLOCKED
: mVideoUnavailableReason;
if (blockReason != VIDEO_UNAVAILABLE_REASON_NONE) {
mBufferingSpinnerView.setVisibility(
blockReason == TvInputManager.VIDEO_UNAVAILABLE_REASON_BUFFERING
- || blockReason == TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING ?
- VISIBLE : GONE);
+ || blockReason == TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING
+ ? VISIBLE
+ : GONE);
if (!animation) {
adjustBlockScreenSpacingAndText();
}
@@ -959,7 +1023,8 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
if (blockReason == TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING) {
showImageForTuningIfNeeded();
} else if (blockReason == TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN
- && mCurrentChannel != null && !mCurrentChannel.isPhysicalTunerChannel()) {
+ && mCurrentChannel != null
+ && !mCurrentChannel.isPhysicalTunerChannel()) {
mInternetCheckTask = new InternetCheckTask();
mInternetCheckTask.execute();
}
@@ -982,8 +1047,8 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
}
/**
- * 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.
+ * 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() {
// TODO: add a test for this method
@@ -1051,7 +1116,7 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
}
private boolean closePipIfNeeded() {
- if (Features.PICTURE_IN_PICTURE.isEnabled(getContext())
+ if (TvFeatures.PICTURE_IN_PICTURE.isEnabled(getContext())
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
&& ((Activity) getContext()).isInPictureInPictureMode()
&& (mScreenBlocked
@@ -1071,8 +1136,13 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
private boolean shouldShowImageForTuning() {
if (mVideoUnavailableReason != TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING
- || mScreenBlocked || mBlockedContentRating != null || mCurrentChannel == null
- || mIsUnderShrunken || getWidth() == 0 || getWidth() == 0 || !isBundledInput()) {
+ || mScreenBlocked
+ || mBlockedContentRating != null
+ || mCurrentChannel == null
+ || mIsUnderShrunken
+ || getWidth() == 0
+ || getWidth() == 0
+ || !isBundledInput()) {
return false;
}
Program currentProgram = mProgramDataManager.getCurrentProgram(mCurrentChannel.getId());
@@ -1091,7 +1161,10 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
}
Program currentProgram = mProgramDataManager.getCurrentProgram(mCurrentChannel.getId());
if (currentProgram != null) {
- currentProgram.loadPosterArt(getContext(), getWidth(), getHeight(),
+ currentProgram.loadPosterArt(
+ getContext(),
+ getWidth(),
+ getHeight(),
createProgramPosterArtCallback(mCurrentChannel.getId()));
}
}
@@ -1102,16 +1175,19 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
TvInputInfo input = mInputManager.getTvInputInfo(mTagetInputId);
Long timeMs = mInputSessionManager.getEarliestRecordingSessionEndTimeMs(mTagetInputId);
if (timeMs != null) {
- return getResources().getQuantityString(R.plurals.tvview_msg_input_no_resource,
- input.getTunerCount(),
- DateUtils.formatDateTime(getContext(), timeMs, DateUtils.FORMAT_SHOW_TIME));
+ return getResources()
+ .getQuantityString(
+ R.plurals.tvview_msg_input_no_resource,
+ input.getTunerCount(),
+ DateUtils.formatDateTime(
+ getContext(), timeMs, DateUtils.FORMAT_SHOW_TIME));
}
}
return null;
}
private void updateMuteStatus() {
- // Workaround: TunerTvInputService uses AC3 pass-through implementation, which disables
+ // Workaround: BaseTunerTvInputService uses AC3 pass-through implementation, which disables
// audio tracks to enforce the mute request. We don't want to send mute request if we are
// not going to block the screen to prevent the video jankiness resulted by disabling audio
// track before the playback is started. In other way, we should send unmute request before
@@ -1119,7 +1195,8 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
// itself right way when the playback is going to be started, which results the initial
// jankiness, too.
boolean isBundledInput = isBundledInput();
- if ((isBundledInput || isVideoOrAudioAvailable()) && !mScreenBlocked
+ if ((isBundledInput || isVideoOrAudioAvailable())
+ && !mScreenBlocked
&& mBlockedContentRating == null) {
if (mIsMuted) {
mIsMuted = false;
@@ -1128,7 +1205,8 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
} else {
if (!mIsMuted) {
if ((mInputInfo == null || isBundledInput)
- && !mScreenBlocked && mBlockedContentRating == null) {
+ && !mScreenBlocked
+ && mBlockedContentRating == null) {
return;
}
mIsMuted = true;
@@ -1138,8 +1216,9 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
}
private boolean isBundledInput() {
- return mInputInfo != null && mInputInfo.getType() == TvInputInfo.TYPE_TUNER
- && Utils.isBundledInput(mInputInfo.getId());
+ return mInputInfo != null
+ && mInputInfo.getType() == TvInputInfo.TYPE_TUNER
+ && CommonUtils.isBundledInput(mInputInfo.getId());
}
/** Returns true if this view is faded out. */
@@ -1148,52 +1227,58 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
}
/** Fade out this TunableTvView. Fade out by increasing the dimming. */
- public void fadeOut(int durationMillis, TimeInterpolator interpolator,
- final Runnable actionAfterFade) {
+ public void fadeOut(
+ int durationMillis, TimeInterpolator interpolator, final Runnable actionAfterFade) {
mDimScreenView.setAlpha(0f);
mDimScreenView.setVisibility(View.VISIBLE);
- mDimScreenView.animate()
+ mDimScreenView
+ .animate()
.alpha(1f)
.setDuration(durationMillis)
.setInterpolator(interpolator)
- .withStartAction(new Runnable() {
- @Override
- public void run() {
- mFadeState = FADING_OUT;
- mActionAfterFade = actionAfterFade;
- }
- })
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- mFadeState = FADED_OUT;
- }
- });
+ .withStartAction(
+ new Runnable() {
+ @Override
+ public void run() {
+ mFadeState = FADING_OUT;
+ mActionAfterFade = actionAfterFade;
+ }
+ })
+ .withEndAction(
+ new Runnable() {
+ @Override
+ public void run() {
+ mFadeState = FADED_OUT;
+ }
+ });
}
/** Fade in this TunableTvView. Fade in by decreasing the dimming. */
- public void fadeIn(int durationMillis, TimeInterpolator interpolator,
- final Runnable actionAfterFade) {
+ public void fadeIn(
+ int durationMillis, TimeInterpolator interpolator, final Runnable actionAfterFade) {
mDimScreenView.setAlpha(1f);
mDimScreenView.setVisibility(View.VISIBLE);
- mDimScreenView.animate()
+ mDimScreenView
+ .animate()
.alpha(0f)
.setDuration(durationMillis)
.setInterpolator(interpolator)
- .withStartAction(new Runnable() {
- @Override
- public void run() {
- mFadeState = FADING_IN;
- mActionAfterFade = actionAfterFade;
- }
- })
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- mFadeState = FADED_IN;
- mDimScreenView.setVisibility(View.GONE);
- }
- });
+ .withStartAction(
+ new Runnable() {
+ @Override
+ public void run() {
+ mFadeState = FADING_IN;
+ mActionAfterFade = actionAfterFade;
+ }
+ })
+ .withEndAction(
+ new Runnable() {
+ @Override
+ public void run() {
+ mFadeState = FADED_IN;
+ mDimScreenView.setVisibility(View.GONE);
+ }
+ });
}
/** Remove the fade effect. */
@@ -1208,6 +1293,7 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
*
* @param listener The instance of {@link TimeShiftListener}.
*/
+ @Override
public void setTimeShiftListener(TimeShiftListener listener) {
mTimeShiftListener = listener;
}
@@ -1218,20 +1304,22 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
}
mTimeShiftAvailable = isTimeShiftAvailable;
if (isTimeShiftAvailable) {
- mTvView.setTimeShiftPositionCallback(new TvView.TimeShiftPositionCallback() {
- @Override
- public void onTimeShiftStartPositionChanged(String inputId, long timeMs) {
- if (mTimeShiftListener != null && mCurrentChannel != null
- && mCurrentChannel.getInputId().equals(inputId)) {
- mTimeShiftListener.onRecordStartTimeChanged(timeMs);
- }
- }
+ mTvView.setTimeShiftPositionCallback(
+ new TvView.TimeShiftPositionCallback() {
+ @Override
+ public void onTimeShiftStartPositionChanged(String inputId, long timeMs) {
+ if (mTimeShiftListener != null
+ && mCurrentChannel != null
+ && mCurrentChannel.getInputId().equals(inputId)) {
+ mTimeShiftListener.onRecordStartTimeChanged(timeMs);
+ }
+ }
- @Override
- public void onTimeShiftCurrentPositionChanged(String inputId, long timeMs) {
- mTimeShiftCurrentPositionMs = timeMs;
- }
- });
+ @Override
+ public void onTimeShiftCurrentPositionChanged(String inputId, long timeMs) {
+ mTimeShiftCurrentPositionMs = timeMs;
+ }
+ });
} else {
mTvView.setTimeShiftPositionCallback(null);
}
@@ -1240,16 +1328,14 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
}
}
- /**
- * Returns if the time shift is available for the current channel.
- */
+ /** Returns if the time shift is available for the current channel. */
+ @Override
public boolean isTimeShiftAvailable() {
return mTimeShiftAvailable;
}
- /**
- * Plays the media, if the current input supports time-shifting.
- */
+ /** Plays the media, if the current input supports time-shifting. */
+ @Override
public void timeshiftPlay() {
if (!isTimeShiftAvailable()) {
throw new IllegalStateException("Time-shift is not supported for the current channel");
@@ -1260,9 +1346,8 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
mTvView.timeShiftResume();
}
- /**
- * Pauses the media, if the current input supports time-shifting.
- */
+ /** Pauses the media, if the current input supports time-shifting. */
+ @Override
public void timeshiftPause() {
if (!isTimeShiftAvailable()) {
throw new IllegalStateException("Time-shift is not supported for the current channel");
@@ -1278,6 +1363,7 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
*
* @param speed The speed to rewind the media. e.g. 2 for 2x, 3 for 3x and 4 for 4x.
*/
+ @Override
public void timeshiftRewind(int speed) {
if (!isTimeShiftAvailable()) {
throw new IllegalStateException("Time-shift is not supported for the current channel");
@@ -1297,6 +1383,7 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
*
* @param speed The speed to forward the media. e.g. 2 for 2x, 3 for 3x and 4 for 4x.
*/
+ @Override
public void timeshiftFastForward(int speed) {
if (!isTimeShiftAvailable()) {
throw new IllegalStateException("Time-shift is not supported for the current channel");
@@ -1316,6 +1403,7 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
*
* @param timeMs The time in milliseconds to seek to.
*/
+ @Override
public void timeshiftSeekTo(long timeMs) {
if (!isTimeShiftAvailable()) {
throw new IllegalStateException("Time-shift is not supported for the current channel");
@@ -1323,16 +1411,17 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
mTvView.timeShiftSeekTo(timeMs);
}
- /**
- * Returns the current playback position in milliseconds.
- */
+ /** Returns the current playback position in milliseconds. */
+ @Override
public long timeshiftGetCurrentPositionMs() {
if (!isTimeShiftAvailable()) {
throw new IllegalStateException("Time-shift is not supported for the current channel");
}
if (DEBUG) {
- Log.d(TAG, "timeshiftGetCurrentPositionMs: current position ="
- + Utils.toTimeString(mTimeShiftCurrentPositionMs));
+ Log.d(
+ TAG,
+ "timeshiftGetCurrentPositionMs: current position ="
+ + Utils.toTimeString(mTimeShiftCurrentPositionMs));
}
return mTimeShiftCurrentPositionMs;
}
@@ -1342,43 +1431,30 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
return new ImageLoader.ImageLoaderCallback<BlockScreenView>(mBlockScreenView) {
@Override
public void onBitmapLoaded(BlockScreenView view, @Nullable Bitmap posterArt) {
- if (posterArt == null || getCurrentChannel() == null
+ if (posterArt == null
+ || getCurrentChannel() == null
|| channelId != getCurrentChannel().getId()
|| !shouldShowImageForTuning()) {
return;
}
Drawable drawablePosterArt = new BitmapDrawable(view.getResources(), posterArt);
- drawablePosterArt.mutate().setColorFilter(
- mTuningImageColorFilter, PorterDuff.Mode.SRC_OVER);
+ drawablePosterArt
+ .mutate()
+ .setColorFilter(mTuningImageColorFilter, PorterDuff.Mode.SRC_OVER);
view.setBackgroundImage(drawablePosterArt);
}
};
}
- /**
- * Used to receive the time-shift events.
- */
- public static abstract class TimeShiftListener {
- /**
- * Called when the availability of the time-shift for the current channel has been changed.
- * It should be guaranteed that this is called only when the availability is really changed.
- */
- public abstract void onAvailabilityChanged();
+ /** Listens for dpad actions that are otherwise trapped by talkback */
+ public interface OnTalkBackDpadKeyListener {
- /**
- * Called when the record start time has been changed.
- * This is not called when the recorded programs is played.
- */
- public abstract void onRecordStartTimeChanged(long recordStartTimeMs);
+ void onTalkBackDpadKey(int keycode);
}
- /**
- * A listener which receives the notification when the screen is blocked/unblocked.
- */
- public static abstract class OnScreenBlockingChangedListener {
- /**
- * Called when the screen is blocked/unblocked.
- */
+ /** A listener which receives the notification when the screen is blocked/unblocked. */
+ public abstract static class OnScreenBlockingChangedListener {
+ /** Called when the screen is blocked/unblocked. */
public abstract void onScreenBlockingChanged(boolean blocked);
}
@@ -1391,8 +1467,10 @@ public class TunableTvView extends FrameLayout implements StreamInfo {
@Override
protected void onPostExecute(Boolean networkAvailable) {
mInternetCheckTask = null;
- if (!networkAvailable && isAttachedToWindow()
- && !mScreenBlocked && mBlockedContentRating == null
+ if (!networkAvailable
+ && isAttachedToWindow()
+ && !mScreenBlocked
+ && mBlockedContentRating == null
&& mVideoUnavailableReason == TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN) {
mBlockScreenView.setIconVisibility(true);
mBlockScreenView.setIconImage(R.drawable.ic_sad_cloud);
diff --git a/src/com/android/tv/ui/TunableTvViewPlayingApi.java b/src/com/android/tv/ui/TunableTvViewPlayingApi.java
new file mode 100644
index 00000000..3f19b61f
--- /dev/null
+++ b/src/com/android/tv/ui/TunableTvViewPlayingApi.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 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;
+
+/** API to play pause and set the volume of a TunableTvView */
+public interface TunableTvViewPlayingApi {
+
+ boolean isPlaying();
+
+ void setStreamVolume(float volume);
+
+ void setTimeShiftListener(TimeShiftListener listener);
+
+ boolean isTimeShiftAvailable();
+
+ void timeshiftPlay();
+
+ void timeshiftPause();
+
+ void timeshiftRewind(int speed);
+
+ void timeshiftFastForward(int speed);
+
+ void timeshiftSeekTo(long timeMs);
+
+ long timeshiftGetCurrentPositionMs();
+
+ /** Used to receive the time-shift events. */
+ abstract class TimeShiftListener {
+ /**
+ * Called when the availability of the time-shift for the current channel has been changed.
+ * It should be guaranteed that this is called only when the availability is really changed.
+ */
+ public abstract void onAvailabilityChanged();
+
+ /**
+ * Called when the record start time has been changed. This is not called when the recorded
+ * programs is played.
+ */
+ public abstract void onRecordStartTimeChanged(long recordStartTimeMs);
+ }
+}
diff --git a/src/com/android/tv/ui/TvOverlayManager.java b/src/com/android/tv/ui/TvOverlayManager.java
index 9324742e..222fcb3a 100644
--- a/src/com/android/tv/ui/TvOverlayManager.java
+++ b/src/com/android/tv/ui/TvOverlayManager.java
@@ -32,15 +32,14 @@ import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.ViewGroup;
-
-import com.android.tv.ApplicationSingletons;
+import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
import com.android.tv.ChannelTuner;
import com.android.tv.MainActivity;
import com.android.tv.MainActivity.KeyHandlerResultType;
import com.android.tv.R;
import com.android.tv.TimeShiftManager;
-import com.android.tv.TvApplication;
import com.android.tv.TvOptionsManager;
+import com.android.tv.TvSingletons;
import com.android.tv.analytics.Tracker;
import com.android.tv.common.WeakHandler;
import com.android.tv.common.feature.CommonFeatures;
@@ -69,7 +68,6 @@ import com.android.tv.ui.TvTransitionManager.SceneType;
import com.android.tv.ui.sidepanel.SideFragmentManager;
import com.android.tv.ui.sidepanel.parentalcontrols.RatingsFragment;
import com.android.tv.util.TvInputManagerHelper;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -79,122 +77,111 @@ import java.util.List;
import java.util.Queue;
import java.util.Set;
-/**
- * A class responsible for the life cycle and event handling of the pop-ups over TV view.
- */
+/** A class responsible for the life cycle and event handling of the pop-ups over TV view. */
@UiThread
-public class TvOverlayManager {
+public class TvOverlayManager implements AccessibilityStateChangeListener {
private static final String TAG = "TvOverlayManager";
private static final boolean DEBUG = false;
private static final String INTRO_TRACKER_LABEL = "Intro dialog";
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true,
- value = {FLAG_HIDE_OVERLAYS_DEFAULT, FLAG_HIDE_OVERLAYS_WITHOUT_ANIMATION,
- FLAG_HIDE_OVERLAYS_KEEP_SCENE, FLAG_HIDE_OVERLAYS_KEEP_DIALOG,
- FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANELS, FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANEL_HISTORY,
- FLAG_HIDE_OVERLAYS_KEEP_PROGRAM_GUIDE, FLAG_HIDE_OVERLAYS_KEEP_MENU,
- FLAG_HIDE_OVERLAYS_KEEP_FRAGMENT})
+ @IntDef(
+ flag = true,
+ value = {
+ FLAG_HIDE_OVERLAYS_DEFAULT,
+ FLAG_HIDE_OVERLAYS_WITHOUT_ANIMATION,
+ FLAG_HIDE_OVERLAYS_KEEP_SCENE,
+ FLAG_HIDE_OVERLAYS_KEEP_DIALOG,
+ FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANELS,
+ FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANEL_HISTORY,
+ FLAG_HIDE_OVERLAYS_KEEP_PROGRAM_GUIDE,
+ FLAG_HIDE_OVERLAYS_KEEP_MENU,
+ FLAG_HIDE_OVERLAYS_KEEP_FRAGMENT
+ }
+ )
private @interface HideOverlayFlag {}
// FLAG_HIDE_OVERLAYs must be bitwise exclusive.
- public static final int FLAG_HIDE_OVERLAYS_DEFAULT = 0b000000000;
- public static final int FLAG_HIDE_OVERLAYS_WITHOUT_ANIMATION = 0b000000010;
- public static final int FLAG_HIDE_OVERLAYS_KEEP_SCENE = 0b000000100;
- public static final int FLAG_HIDE_OVERLAYS_KEEP_DIALOG = 0b000001000;
- public static final int FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANELS = 0b000010000;
+ public static final int FLAG_HIDE_OVERLAYS_DEFAULT = 0b000000000;
+ public static final int FLAG_HIDE_OVERLAYS_WITHOUT_ANIMATION = 0b000000010;
+ public static final int FLAG_HIDE_OVERLAYS_KEEP_SCENE = 0b000000100;
+ public static final int FLAG_HIDE_OVERLAYS_KEEP_DIALOG = 0b000001000;
+ public static final int FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANELS = 0b000010000;
public static final int FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANEL_HISTORY = 0b000100000;
- public static final int FLAG_HIDE_OVERLAYS_KEEP_PROGRAM_GUIDE = 0b001000000;
- public static final int FLAG_HIDE_OVERLAYS_KEEP_MENU = 0b010000000;
- public static final int FLAG_HIDE_OVERLAYS_KEEP_FRAGMENT = 0b100000000;
+ public static final int FLAG_HIDE_OVERLAYS_KEEP_PROGRAM_GUIDE = 0b001000000;
+ public static final int FLAG_HIDE_OVERLAYS_KEEP_MENU = 0b010000000;
+ public static final int FLAG_HIDE_OVERLAYS_KEEP_FRAGMENT = 0b100000000;
private static final int MSG_OVERLAY_CLOSED = 1000;
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true,
- value = {OVERLAY_TYPE_NONE, OVERLAY_TYPE_MENU, OVERLAY_TYPE_SIDE_FRAGMENT,
- OVERLAY_TYPE_DIALOG, OVERLAY_TYPE_GUIDE, OVERLAY_TYPE_SCENE_CHANNEL_BANNER,
- OVERLAY_TYPE_SCENE_INPUT_BANNER, OVERLAY_TYPE_SCENE_KEYPAD_CHANNEL_SWITCH,
- OVERLAY_TYPE_SCENE_SELECT_INPUT, OVERLAY_TYPE_FRAGMENT})
+ @IntDef(
+ flag = true,
+ value = {
+ OVERLAY_TYPE_NONE,
+ OVERLAY_TYPE_MENU,
+ OVERLAY_TYPE_SIDE_FRAGMENT,
+ OVERLAY_TYPE_DIALOG,
+ OVERLAY_TYPE_GUIDE,
+ OVERLAY_TYPE_SCENE_CHANNEL_BANNER,
+ OVERLAY_TYPE_SCENE_INPUT_BANNER,
+ OVERLAY_TYPE_SCENE_KEYPAD_CHANNEL_SWITCH,
+ OVERLAY_TYPE_SCENE_SELECT_INPUT,
+ OVERLAY_TYPE_FRAGMENT
+ }
+ )
private @interface TvOverlayType {}
// OVERLAY_TYPEs must be bitwise exclusive.
- /**
- * The overlay type which indicates that there are no overlays.
- */
- private static final int OVERLAY_TYPE_NONE = 0b000000000;
- /**
- * The overlay type for menu.
- */
- private static final int OVERLAY_TYPE_MENU = 0b000000001;
- /**
- * The overlay type for the side fragment.
- */
- private static final int OVERLAY_TYPE_SIDE_FRAGMENT = 0b000000010;
- /**
- * The overlay type for dialog fragment.
- */
- private static final int OVERLAY_TYPE_DIALOG = 0b000000100;
- /**
- * The overlay type for program guide.
- */
- private static final int OVERLAY_TYPE_GUIDE = 0b000001000;
- /**
- * The overlay type for channel banner.
- */
- private static final int OVERLAY_TYPE_SCENE_CHANNEL_BANNER = 0b000010000;
- /**
- * The overlay type for input banner.
- */
- private static final int OVERLAY_TYPE_SCENE_INPUT_BANNER = 0b000100000;
- /**
- * The overlay type for keypad channel switch view.
- */
+ /** The overlay type which indicates that there are no overlays. */
+ private static final int OVERLAY_TYPE_NONE = 0b000000000;
+ /** The overlay type for menu. */
+ private static final int OVERLAY_TYPE_MENU = 0b000000001;
+ /** The overlay type for the side fragment. */
+ private static final int OVERLAY_TYPE_SIDE_FRAGMENT = 0b000000010;
+ /** The overlay type for dialog fragment. */
+ private static final int OVERLAY_TYPE_DIALOG = 0b000000100;
+ /** The overlay type for program guide. */
+ private static final int OVERLAY_TYPE_GUIDE = 0b000001000;
+ /** The overlay type for channel banner. */
+ private static final int OVERLAY_TYPE_SCENE_CHANNEL_BANNER = 0b000010000;
+ /** The overlay type for input banner. */
+ private static final int OVERLAY_TYPE_SCENE_INPUT_BANNER = 0b000100000;
+ /** The overlay type for keypad channel switch view. */
private static final int OVERLAY_TYPE_SCENE_KEYPAD_CHANNEL_SWITCH = 0b001000000;
- /**
- * The overlay type for select input view.
- */
- private static final int OVERLAY_TYPE_SCENE_SELECT_INPUT = 0b010000000;
- /**
- * The overlay type for fragment other than the side fragment and dialog fragment.
- */
- private static final int OVERLAY_TYPE_FRAGMENT = 0b100000000;
+ /** The overlay type for select input view. */
+ private static final int OVERLAY_TYPE_SCENE_SELECT_INPUT = 0b010000000;
+ /** The overlay type for fragment other than the side fragment and dialog fragment. */
+ private static final int OVERLAY_TYPE_FRAGMENT = 0b100000000;
// Used for the padded print of the overlay type.
private static final int NUM_OVERLAY_TYPES = 9;
@Retention(RetentionPolicy.SOURCE)
- @IntDef({UPDATE_CHANNEL_BANNER_REASON_FORCE_SHOW, UPDATE_CHANNEL_BANNER_REASON_TUNE,
- UPDATE_CHANNEL_BANNER_REASON_TUNE_FAST, UPDATE_CHANNEL_BANNER_REASON_UPDATE_INFO,
- UPDATE_CHANNEL_BANNER_REASON_LOCK_OR_UNLOCK,
- UPDATE_CHANNEL_BANNER_REASON_UPDATE_STREAM_INFO})
+ @IntDef({
+ UPDATE_CHANNEL_BANNER_REASON_FORCE_SHOW,
+ UPDATE_CHANNEL_BANNER_REASON_TUNE,
+ UPDATE_CHANNEL_BANNER_REASON_TUNE_FAST,
+ UPDATE_CHANNEL_BANNER_REASON_UPDATE_INFO,
+ UPDATE_CHANNEL_BANNER_REASON_LOCK_OR_UNLOCK,
+ UPDATE_CHANNEL_BANNER_REASON_UPDATE_STREAM_INFO
+ })
private @interface ChannelBannerUpdateReason {}
- /**
- * Updates channel banner because the channel banner is forced to show.
- */
+ /** Updates channel banner because the channel banner is forced to show. */
public static final int UPDATE_CHANNEL_BANNER_REASON_FORCE_SHOW = 1;
- /**
- * Updates channel banner because of tuning.
- */
+ /** Updates channel banner because of tuning. */
public static final int UPDATE_CHANNEL_BANNER_REASON_TUNE = 2;
- /**
- * Updates channel banner because of fast tuning.
- */
+ /** Updates channel banner because of fast tuning. */
public static final int UPDATE_CHANNEL_BANNER_REASON_TUNE_FAST = 3;
- /**
- * Updates channel banner because of info updating.
- */
+ /** Updates channel banner because of info updating. */
public static final int UPDATE_CHANNEL_BANNER_REASON_UPDATE_INFO = 4;
- /**
- * Updates channel banner because the current watched channel is locked or unlocked.
- */
+ /** Updates channel banner because the current watched channel is locked or unlocked. */
public static final int UPDATE_CHANNEL_BANNER_REASON_LOCK_OR_UNLOCK = 5;
- /**
- * Updates channel banner because of stream info updating.
- */
+ /** Updates channel banner because of stream info updating. */
public static final int UPDATE_CHANNEL_BANNER_REASON_UPDATE_STREAM_INFO = 6;
private static final String FRAGMENT_TAG_SETUP_SOURCES = "tag_setup_sources";
private static final String FRAGMENT_TAG_NEW_SOURCES = "tag_new_sources";
private static final Set<String> AVAILABLE_DIALOG_TAGS = new HashSet<>();
+
static {
AVAILABLE_DIALOG_TAGS.add(RecentlyWatchedDialogFragment.DIALOG_TAG);
AVAILABLE_DIALOG_TAGS.add(DvrHistoryDialogFragment.DIALOG_TAG);
@@ -232,14 +219,20 @@ public class TvOverlayManager {
private OnBackStackChangedListener mOnBackStackChangedListener;
- public TvOverlayManager(MainActivity mainActivity, ChannelTuner channelTuner,
- TunableTvView tvView, TvOptionsManager optionsManager,
- KeypadChannelSwitchView keypadChannelSwitchView, ChannelBannerView channelBannerView,
- InputBannerView inputBannerView, SelectInputView selectInputView,
- ViewGroup sceneContainer, ProgramGuideSearchFragment searchFragment) {
+ public TvOverlayManager(
+ MainActivity mainActivity,
+ ChannelTuner channelTuner,
+ TunableTvView tvView,
+ TvOptionsManager optionsManager,
+ KeypadChannelSwitchView keypadChannelSwitchView,
+ ChannelBannerView channelBannerView,
+ InputBannerView inputBannerView,
+ SelectInputView selectInputView,
+ ViewGroup sceneContainer,
+ ProgramGuideSearchFragment searchFragment) {
mMainActivity = mainActivity;
mChannelTuner = channelTuner;
- ApplicationSingletons singletons = TvApplication.getSingletons(mainActivity);
+ TvSingletons singletons = TvSingletons.getSingletons(mainActivity);
mChannelDataManager = singletons.getChannelDataManager();
mInputManager = singletons.getTvInputManagerHelper();
mTvView = tvView;
@@ -248,115 +241,144 @@ public class TvOverlayManager {
mSelectInputView = selectInputView;
mSearchFragment = searchFragment;
mTracker = singletons.getTracker();
- mTransitionManager = new TvTransitionManager(mainActivity, sceneContainer,
- channelBannerView, inputBannerView, mKeypadChannelSwitchView, selectInputView);
- mTransitionManager.setListener(new TvTransitionManager.Listener() {
- @Override
- public void onSceneChanged(int fromScene, int toScene) {
- // Call onOverlayOpened first so that the listener can know that a new scene
- // will be opened when the onOverlayClosed is called.
- if (toScene != TvTransitionManager.SCENE_TYPE_EMPTY) {
- onOverlayOpened(convertSceneToOverlayType(toScene));
- }
- if (fromScene != TvTransitionManager.SCENE_TYPE_EMPTY) {
- onOverlayClosed(convertSceneToOverlayType(fromScene));
- }
- }
- });
- // Menu
- MenuView menuView = (MenuView) mainActivity.findViewById(R.id.menu);
- mMenu = new Menu(mainActivity, tvView, optionsManager, menuView,
- new MenuRowFactory(mainActivity, tvView),
- new Menu.OnMenuVisibilityChangeListener() {
+ mTransitionManager =
+ new TvTransitionManager(
+ mainActivity,
+ sceneContainer,
+ channelBannerView,
+ inputBannerView,
+ mKeypadChannelSwitchView,
+ selectInputView);
+ mTransitionManager.setListener(
+ new TvTransitionManager.Listener() {
@Override
- public void onMenuVisibilityChange(boolean visible) {
- if (visible) {
- onOverlayOpened(OVERLAY_TYPE_MENU);
- } else {
- onOverlayClosed(OVERLAY_TYPE_MENU);
+ public void onSceneChanged(int fromScene, int toScene) {
+ // Call onOverlayOpened first so that the listener can know that a new scene
+ // will be opened when the onOverlayClosed is called.
+ if (toScene != TvTransitionManager.SCENE_TYPE_EMPTY) {
+ onOverlayOpened(convertSceneToOverlayType(toScene));
+ }
+ if (fromScene != TvTransitionManager.SCENE_TYPE_EMPTY) {
+ onOverlayClosed(convertSceneToOverlayType(fromScene));
}
}
});
+ // Menu
+ MenuView menuView = (MenuView) mainActivity.findViewById(R.id.menu);
+ mMenu =
+ new Menu(
+ mainActivity,
+ tvView,
+ optionsManager,
+ menuView,
+ new MenuRowFactory(mainActivity, tvView),
+ new Menu.OnMenuVisibilityChangeListener() {
+ @Override
+ public void onMenuVisibilityChange(boolean visible) {
+ if (visible) {
+ onOverlayOpened(OVERLAY_TYPE_MENU);
+ } else {
+ onOverlayClosed(OVERLAY_TYPE_MENU);
+ }
+ }
+ });
mMenu.setChannelTuner(mChannelTuner);
// Side Fragment
- mSideFragmentManager = new SideFragmentManager(mainActivity,
+ mSideFragmentManager =
+ new SideFragmentManager(
+ mainActivity,
+ new Runnable() {
+ @Override
+ public void run() {
+ onOverlayOpened(OVERLAY_TYPE_SIDE_FRAGMENT);
+ hideOverlays(FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANELS);
+ }
+ },
+ new Runnable() {
+ @Override
+ public void run() {
+ showChannelBannerIfHiddenBySideFragment();
+ onOverlayClosed(OVERLAY_TYPE_SIDE_FRAGMENT);
+ }
+ });
+ // Program Guide
+ Runnable preShowRunnable =
new Runnable() {
@Override
public void run() {
- onOverlayOpened(OVERLAY_TYPE_SIDE_FRAGMENT);
- hideOverlays(FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANELS);
+ onOverlayOpened(OVERLAY_TYPE_GUIDE);
}
- },
+ };
+ Runnable postHideRunnable =
new Runnable() {
@Override
public void run() {
- showChannelBannerIfHiddenBySideFragment();
- onOverlayClosed(OVERLAY_TYPE_SIDE_FRAGMENT);
+ onOverlayClosed(OVERLAY_TYPE_GUIDE);
}
- });
- // Program Guide
- Runnable preShowRunnable = new Runnable() {
- @Override
- public void run() {
- onOverlayOpened(OVERLAY_TYPE_GUIDE);
- }
- };
- Runnable postHideRunnable = new Runnable() {
- @Override
- public void run() {
- onOverlayClosed(OVERLAY_TYPE_GUIDE);
- }
- };
- DvrDataManager dvrDataManager = CommonFeatures.DVR.isEnabled(mainActivity)
- ? singletons.getDvrDataManager() : null;
- mProgramGuide = new ProgramGuide(mainActivity, channelTuner,
- singletons.getTvInputManagerHelper(), mChannelDataManager,
- singletons.getProgramDataManager(), dvrDataManager,
- singletons.getDvrScheduleManager(), singletons.getTracker(), preShowRunnable,
- postHideRunnable);
- mMainActivity.addOnActionClickListener(new OnActionClickListener() {
- @Override
- public boolean onActionClick(String category, int id, Bundle params) {
- switch (category) {
- case SetupSourcesFragment.ACTION_CATEGORY:
- switch (id) {
- case SetupMultiPaneFragment.ACTION_DONE:
- closeSetupFragment(true);
- return true;
- case SetupSourcesFragment.ACTION_ONLINE_STORE:
- mMainActivity.showMerchantCollection();
- return true;
- case SetupSourcesFragment.ACTION_SETUP_INPUT: {
- String inputId = params.getString(
- SetupSourcesFragment.ACTION_PARAM_KEY_INPUT_ID);
- TvInputInfo input = mInputManager.getTvInputInfo(inputId);
- mMainActivity.startSetupActivity(input, true);
- return true;
- }
- }
- break;
- case NewSourcesFragment.ACTION_CATEOGRY:
- switch (id) {
- case NewSourcesFragment.ACTION_SETUP:
- closeNewSourcesFragment(false);
- showSetupFragment();
- return true;
- case NewSourcesFragment.ACTION_SKIP:
- // Don't remove the fragment because new fragment will be replaced
- // with this fragment.
- closeNewSourcesFragment(true);
- return true;
+ };
+ DvrDataManager dvrDataManager =
+ CommonFeatures.DVR.isEnabled(mainActivity) ? singletons.getDvrDataManager() : null;
+ mProgramGuide =
+ new ProgramGuide(
+ mainActivity,
+ channelTuner,
+ singletons.getTvInputManagerHelper(),
+ mChannelDataManager,
+ singletons.getProgramDataManager(),
+ dvrDataManager,
+ singletons.getDvrScheduleManager(),
+ singletons.getTracker(),
+ preShowRunnable,
+ postHideRunnable);
+ mMainActivity.addOnActionClickListener(
+ new OnActionClickListener() {
+ @Override
+ public boolean onActionClick(String category, int id, Bundle params) {
+ switch (category) {
+ case SetupSourcesFragment.ACTION_CATEGORY:
+ switch (id) {
+ case SetupMultiPaneFragment.ACTION_DONE:
+ closeSetupFragment(true);
+ return true;
+ case SetupSourcesFragment.ACTION_ONLINE_STORE:
+ mMainActivity.showMerchantCollection();
+ return true;
+ case SetupSourcesFragment.ACTION_SETUP_INPUT:
+ {
+ String inputId =
+ params.getString(
+ SetupSourcesFragment
+ .ACTION_PARAM_KEY_INPUT_ID);
+ TvInputInfo input =
+ mInputManager.getTvInputInfo(inputId);
+ mMainActivity.startSetupActivity(input, true);
+ return true;
+ }
+ }
+ break;
+ case NewSourcesFragment.ACTION_CATEOGRY:
+ switch (id) {
+ case NewSourcesFragment.ACTION_SETUP:
+ closeNewSourcesFragment(false);
+ showSetupFragment();
+ return true;
+ case NewSourcesFragment.ACTION_SKIP:
+ // Don't remove the fragment because new fragment will be
+ // replaced
+ // with this fragment.
+ closeNewSourcesFragment(true);
+ return true;
+ }
+ break;
}
- break;
- }
- return false;
- }
- });
+ return false;
+ }
+ });
}
/**
- * A method to release all the allocated resources or unregister listeners.
- * This is called from {@link MainActivity#onDestroy}.
+ * A method to release all the allocated resources or unregister listeners. This is called from
+ * {@link MainActivity#onDestroy}.
*/
public void release() {
mMenu.release();
@@ -366,30 +388,22 @@ public class TvOverlayManager {
}
}
- /**
- * Returns the instance of {@link Menu}.
- */
+ /** Returns the instance of {@link Menu}. */
public Menu getMenu() {
return mMenu;
}
- /**
- * Returns the instance of {@link SideFragmentManager}.
- */
+ /** Returns the instance of {@link SideFragmentManager}. */
public SideFragmentManager getSideFragmentManager() {
return mSideFragmentManager;
}
- /**
- * Returns the currently opened dialog.
- */
+ /** Returns the currently opened dialog. */
public SafeDismissDialogFragment getCurrentDialog() {
return mCurrentDialog;
}
- /**
- * Checks whether the setup fragment is active or not.
- */
+ /** Checks whether the setup fragment is active or not. */
public boolean isSetupFragmentActive() {
// "getSetupSourcesFragment() != null" doesn't return the correct state. That's because,
// when we call showSetupFragment(), we need to put off showing the fragment until the side
@@ -402,9 +416,7 @@ public class TvOverlayManager {
return mMainActivity.getFragmentManager().findFragmentByTag(FRAGMENT_TAG_SETUP_SOURCES);
}
- /**
- * Checks whether the new sources fragment is active or not.
- */
+ /** Checks whether the new sources fragment is active or not. */
public boolean isNewSourcesFragmentActive() {
// See the comment in "isSetupFragmentActive".
return mNewSourcesFragmentActive;
@@ -414,25 +426,19 @@ public class TvOverlayManager {
return mMainActivity.getFragmentManager().findFragmentByTag(FRAGMENT_TAG_NEW_SOURCES);
}
- /**
- * Returns the instance of {@link ProgramGuide}.
- */
+ /** Returns the instance of {@link ProgramGuide}. */
public ProgramGuide getProgramGuide() {
return mProgramGuide;
}
- /**
- * Shows the main menu.
- */
+ /** Shows the main menu. */
public void showMenu(@MenuShowReason int reason) {
if (mChannelTuner != null && mChannelTuner.areAllChannelsLoaded()) {
mMenu.show(reason);
}
}
- /**
- * Shows the play controller of the menu if the playback is paused.
- */
+ /** Shows the play controller of the menu if the playback is paused. */
public boolean showMenuWithTimeShiftPauseIfNeeded() {
if (mMainActivity.getTimeShiftManager().isPaused()) {
showMenu(Menu.REASON_PLAY_CONTROLS_PAUSE);
@@ -441,16 +447,17 @@ public class TvOverlayManager {
return false;
}
- /**
- * Shows the given dialog.
- */
- public void showDialogFragment(String tag, SafeDismissDialogFragment dialog,
- boolean keepSidePanelHistory) {
+ /** Shows the given dialog. */
+ 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) {
+ 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;
@@ -466,8 +473,8 @@ public class TvOverlayManager {
// Do not open two dialogs at the same time.
if (mCurrentDialog != null) {
- mPendingDialogActionQueue.offer(new PendingDialogAction(tag, dialog,
- keepSidePanelHistory, keepProgramGuide));
+ mPendingDialogActionQueue.offer(
+ new PendingDialogAction(tag, dialog, keepSidePanelHistory, keepProgramGuide));
return;
}
@@ -492,16 +499,17 @@ public class TvOverlayManager {
// When the side panel is closing, it closes all the fragments, so the new fragment
// should be opened after the side fragment becomes invisible.
final FragmentManager manager = mMainActivity.getFragmentManager();
- mOnBackStackChangedListener = new OnBackStackChangedListener() {
- @Override
- public void onBackStackChanged() {
- if (manager.getBackStackEntryCount() == 0) {
- manager.removeOnBackStackChangedListener(this);
- mOnBackStackChangedListener = null;
- runnable.run();
- }
- }
- };
+ mOnBackStackChangedListener =
+ new OnBackStackChangedListener() {
+ @Override
+ public void onBackStackChanged() {
+ if (manager.getBackStackEntryCount() == 0) {
+ manager.removeOnBackStackChangedListener(this);
+ mOnBackStackChangedListener = null;
+ runnable.run();
+ }
+ }
+ };
manager.addOnBackStackChangedListener(mOnBackStackChangedListener);
} else {
runnable.run();
@@ -511,46 +519,54 @@ public class TvOverlayManager {
private void showFragment(final Fragment fragment, final String tag) {
hideOverlays(FLAG_HIDE_OVERLAYS_KEEP_FRAGMENT);
onOverlayOpened(OVERLAY_TYPE_FRAGMENT);
- runAfterSideFragmentsAreClosed(new Runnable() {
- @Override
- public void run() {
- if (DEBUG) Log.d(TAG, "showFragment(" + fragment + ")");
- mMainActivity.getFragmentManager().beginTransaction()
- .replace(R.id.fragment_container, fragment, tag).commit();
- }
- });
+ runAfterSideFragmentsAreClosed(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (DEBUG) Log.d(TAG, "showFragment(" + fragment + ")");
+ mMainActivity
+ .getFragmentManager()
+ .beginTransaction()
+ .replace(R.id.fragment_container, fragment, tag)
+ .commit();
+ }
+ });
}
private void closeFragment(String fragmentTagToRemove) {
if (DEBUG) Log.d(TAG, "closeFragment(" + fragmentTagToRemove + ")");
onOverlayClosed(OVERLAY_TYPE_FRAGMENT);
if (fragmentTagToRemove != null) {
- Fragment fragmentToRemove = mMainActivity.getFragmentManager()
- .findFragmentByTag(fragmentTagToRemove);
+ Fragment fragmentToRemove =
+ mMainActivity.getFragmentManager().findFragmentByTag(fragmentTagToRemove);
if (fragmentToRemove == null) {
// If the fragment has not been added to the fragment manager yet, just remove the
// listener not to add the fragment. This is needed because the side fragment is
// closed asynchronously.
- mMainActivity.getFragmentManager().removeOnBackStackChangedListener(
- mOnBackStackChangedListener);
+ mMainActivity
+ .getFragmentManager()
+ .removeOnBackStackChangedListener(mOnBackStackChangedListener);
mOnBackStackChangedListener = null;
} else {
- mMainActivity.getFragmentManager().beginTransaction().remove(fragmentToRemove)
+ mMainActivity
+ .getFragmentManager()
+ .beginTransaction()
+ .remove(fragmentToRemove)
.commit();
}
}
}
- /**
- * Shows setup dialog.
- */
+ /** Shows setup dialog. */
public void showSetupFragment() {
if (DEBUG) Log.d(TAG, "showSetupFragment");
mSetupFragmentActive = true;
SetupSourcesFragment setupFragment = new SetupSourcesFragment();
- setupFragment.enableFragmentTransition(SetupFragment.FRAGMENT_ENTER_TRANSITION
- | SetupFragment.FRAGMENT_EXIT_TRANSITION | SetupFragment.FRAGMENT_RETURN_TRANSITION
- | SetupFragment.FRAGMENT_REENTER_TRANSITION);
+ setupFragment.enableFragmentTransition(
+ SetupFragment.FRAGMENT_ENTER_TRANSITION
+ | SetupFragment.FRAGMENT_EXIT_TRANSITION
+ | SetupFragment.FRAGMENT_RETURN_TRANSITION
+ | SetupFragment.FRAGMENT_REENTER_TRANSITION);
setupFragment.setFragmentTransition(SetupFragment.FRAGMENT_EXIT_TRANSITION, Gravity.END);
showFragment(setupFragment, FRAGMENT_TAG_SETUP_SOURCES);
}
@@ -569,9 +585,7 @@ public class TvOverlayManager {
}
}
- /**
- * Shows new sources dialog.
- */
+ /** Shows new sources dialog. */
public void showNewSourcesFragment() {
if (DEBUG) Log.d(TAG, "showNewSourcesFragment");
mNewSourcesFragmentActive = true;
@@ -588,43 +602,36 @@ public class TvOverlayManager {
closeFragment(removeFragment ? FRAGMENT_TAG_NEW_SOURCES : null);
}
- /**
- * Shows DVR manager.
- */
+ /** Shows DVR manager. */
public void showDvrManager() {
Intent intent = new Intent(mMainActivity, DvrBrowseActivity.class);
mMainActivity.startActivity(intent);
}
- /**
- * Shows intro dialog.
- */
+ /** Shows intro dialog. */
public void showIntroDialog() {
- if (DEBUG) Log.d(TAG,"showIntroDialog");
- showDialogFragment(FullscreenDialogFragment.DIALOG_TAG,
+ if (DEBUG) Log.d(TAG, "showIntroDialog");
+ showDialogFragment(
+ FullscreenDialogFragment.DIALOG_TAG,
FullscreenDialogFragment.newInstance(R.layout.intro_dialog, INTRO_TRACKER_LABEL),
false);
}
- /**
- * Shows recently watched dialog.
- */
+ /** Shows recently watched dialog. */
public void showRecentlyWatchedDialog() {
- showDialogFragment(RecentlyWatchedDialogFragment.DIALOG_TAG,
- new RecentlyWatchedDialogFragment(), false);
+ showDialogFragment(
+ RecentlyWatchedDialogFragment.DIALOG_TAG,
+ new RecentlyWatchedDialogFragment(),
+ false);
}
- /**
- * Shows DVR history dialog.
- */
+ /** Shows DVR history dialog. */
public void showDvrHistoryDialog() {
- showDialogFragment(DvrHistoryDialogFragment.DIALOG_TAG,
- new DvrHistoryDialogFragment(), false);
+ showDialogFragment(
+ DvrHistoryDialogFragment.DIALOG_TAG, new DvrHistoryDialogFragment(), false);
}
- /**
- * Shows banner view.
- */
+ /** Shows banner view. */
public void showBanner() {
mTransitionManager.goToChannelBannerScene();
}
@@ -636,33 +643,28 @@ public class TvOverlayManager {
*/
public void showKeypadChannelSwitch(int keyCode) {
if (mChannelTuner.areAllChannelsLoaded()) {
- hideOverlays(TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_SCENE
- | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANELS
- | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_DIALOG
- | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_FRAGMENT);
+ hideOverlays(
+ TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_SCENE
+ | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANELS
+ | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_DIALOG
+ | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_FRAGMENT);
mTransitionManager.goToKeypadChannelSwitchScene();
mKeypadChannelSwitchView.onNumberKeyUp(keyCode - KeyEvent.KEYCODE_0);
}
}
- /**
- * Shows select input view.
- */
+ /** Shows select input view. */
public void showSelectInputView() {
hideOverlays(TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_SCENE);
mTransitionManager.goToSelectInputScene();
}
- /**
- * Initializes animators if animators are not initialized yet.
- */
+ /** Initializes animators if animators are not initialized yet. */
public void initAnimatorIfNeeded() {
mTransitionManager.initIfNeeded();
}
- /**
- * It is called when a SafeDismissDialogFragment is destroyed.
- */
+ /** It is called when a SafeDismissDialogFragment is destroyed. */
public void onDialogDestroyed() {
mCurrentDialog = null;
PendingDialogAction action = mPendingDialogActionQueue.poll();
@@ -673,16 +675,15 @@ public class TvOverlayManager {
}
}
- /**
- * Shows the program guide.
- */
+ /** Shows the program guide. */
public void showProgramGuide() {
- mProgramGuide.show(new Runnable() {
- @Override
- public void run() {
- hideOverlays(TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_PROGRAM_GUIDE);
- }
- });
+ mProgramGuide.show(
+ new Runnable() {
+ @Override
+ public void run() {
+ hideOverlays(TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_PROGRAM_GUIDE);
+ }
+ });
}
/**
@@ -700,9 +701,7 @@ public class TvOverlayManager {
}
}
- /**
- * Sets blocking content rating of the currently playing TV channel.
- */
+ /** Sets blocking content rating of the currently playing TV channel. */
public void setBlockingContentRating(TvContentRating rating) {
if (!mMainActivity.isChannelChangeKeyDownReceived()) {
mChannelBannerView.setBlockingContentRating(rating);
@@ -710,9 +709,11 @@ public class TvOverlayManager {
}
}
- /**
- * Hides all the opened overlays according to the flags.
- */
+ public boolean isOverlayOpened() {
+ return mOpenedOverlays != OVERLAY_TYPE_NONE;
+ }
+
+ /** Hides all the opened overlays according to the flags. */
// TODO: Add test for this method.
public void hideOverlays(@HideOverlayFlag int flags) {
if (mMainActivity.needToKeepSetupScreenWhenHidingOverlay()) {
@@ -780,9 +781,17 @@ public class TvOverlayManager {
}
}
+ @Override
+ public void onAccessibilityStateChanged(boolean enabled) {
+ // Propagate this to all elements that need it
+ mChannelBannerView.onAccessibilityStateChanged(enabled);
+ mProgramGuide.onAccessibilityStateChanged(enabled);
+ mSideFragmentManager.onAccessibilityStateChanged(enabled);
+ }
+
/**
- * Returns true, if a main view needs to hide informational text. Specifically, when overlay
- * UIs except banner is shown, the informational text needs to be hidden for clean UI.
+ * Returns true, if a main view needs to hide informational text. Specifically, when overlay UIs
+ * except banner is shown, the informational text needs to be hidden for clean UI.
*/
public boolean needHideTextOnMainView() {
return mSideFragmentManager.isActive()
@@ -793,11 +802,9 @@ public class TvOverlayManager {
|| mNewSourcesFragmentActive;
}
- /**
- * Updates and shows channel banner if it's needed.
- */
+ /** Updates and shows channel banner if it's needed. */
public void updateChannelBannerAndShowIfNeeded(@ChannelBannerUpdateReason int reason) {
- if(DEBUG) Log.d(TAG, "updateChannelBannerAndShowIfNeeded(reason=" + reason + ")");
+ if (DEBUG) Log.d(TAG, "updateChannelBannerAndShowIfNeeded(reason=" + reason + ")");
if (mMainActivity.isChannelChangeKeyDownReceived()
&& reason != UPDATE_CHANNEL_BANNER_REASON_TUNE
&& reason != UPDATE_CHANNEL_BANNER_REASON_TUNE_FAST) {
@@ -826,8 +833,9 @@ public class TvOverlayManager {
}
} else if (mTvView.isScreenBlocked()) {
lockType = ChannelBannerView.LOCK_CHANNEL_INFO;
- } else if (mTvView.isContentBlocked() || (mMainActivity.getParentalControlSettings()
- .isParentalControlsEnabled() && !mTvView.isVideoOrAudioAvailable())) {
+ } else if (mTvView.isContentBlocked()
+ || (mMainActivity.getParentalControlSettings().isParentalControlsEnabled()
+ && !mTvView.isVideoOrAudioAvailable())) {
// If the parental control is enabled, do not show the program detail until the
// video becomes available.
lockType = ChannelBannerView.LOCK_PROGRAM_DETAIL;
@@ -843,19 +851,22 @@ public class TvOverlayManager {
// If parental control is enabled, we shows program description when the video is
// available, instead of tuning. Therefore we need to check it here if the program
// description is previously hidden by parental control.
- if (previousLockType == ChannelBannerView.LOCK_PROGRAM_DETAIL &&
- lockType != ChannelBannerView.LOCK_PROGRAM_DETAIL) {
+ if (previousLockType == ChannelBannerView.LOCK_PROGRAM_DETAIL
+ && lockType != ChannelBannerView.LOCK_PROGRAM_DETAIL) {
mChannelBannerView.updateViews(false);
}
} else {
- mChannelBannerView.updateViews(reason == UPDATE_CHANNEL_BANNER_REASON_TUNE
- || reason == UPDATE_CHANNEL_BANNER_REASON_TUNE_FAST);
+ mChannelBannerView.updateViews(
+ reason == UPDATE_CHANNEL_BANNER_REASON_TUNE
+ || reason == UPDATE_CHANNEL_BANNER_REASON_TUNE_FAST);
}
}
- boolean needToShowBanner = (reason == UPDATE_CHANNEL_BANNER_REASON_FORCE_SHOW
- || reason == UPDATE_CHANNEL_BANNER_REASON_TUNE
- || reason == UPDATE_CHANNEL_BANNER_REASON_TUNE_FAST);
- if (needToShowBanner && !mMainActivity.willShowOverlayUiWhenResume()
+ boolean needToShowBanner =
+ (reason == UPDATE_CHANNEL_BANNER_REASON_FORCE_SHOW
+ || reason == UPDATE_CHANNEL_BANNER_REASON_TUNE
+ || reason == UPDATE_CHANNEL_BANNER_REASON_TUNE_FAST);
+ if (needToShowBanner
+ && !mMainActivity.willShowOverlayUiWhenResume()
&& getCurrentDialog() == null
&& !isSetupFragmentActive()
&& !isNewSourcesFragmentActive()) {
@@ -870,7 +881,8 @@ public class TvOverlayManager {
}
}
- @TvOverlayType private int convertSceneToOverlayType(@SceneType int sceneType) {
+ @TvOverlayType
+ private int convertSceneToOverlayType(@SceneType int sceneType) {
switch (sceneType) {
case TvTransitionManager.SCENE_TYPE_CHANNEL_BANNER:
return OVERLAY_TYPE_SCENE_CHANNEL_BANNER;
@@ -940,13 +952,13 @@ public class TvOverlayManager {
}
private boolean isOnlyBannerOrNoneOpened() {
- return (mOpenedOverlays & ~OVERLAY_TYPE_SCENE_CHANNEL_BANNER
- & ~OVERLAY_TYPE_SCENE_INPUT_BANNER) == 0;
+ return (mOpenedOverlays
+ & ~OVERLAY_TYPE_SCENE_CHANNEL_BANNER
+ & ~OVERLAY_TYPE_SCENE_INPUT_BANNER)
+ == 0;
}
- /**
- * Runs a given {@code action} after all the overlays are closed.
- */
+ /** Runs a given {@code action} after all the overlays are closed. */
public void runAfterOverlaysAreClosed(Runnable action) {
if (canExecuteCloseAction()) {
action.run();
@@ -955,9 +967,7 @@ public class TvOverlayManager {
}
}
- /**
- * Handles the onUserInteraction event of the {@link MainActivity}.
- */
+ /** Handles the onUserInteraction event of the {@link MainActivity}. */
public void onUserInteraction() {
if (mSideFragmentManager.isActive()) {
mSideFragmentManager.scheduleHideAll();
@@ -968,10 +978,9 @@ public class TvOverlayManager {
}
}
- /**
- * Handles the onKeyDown event of the {@link MainActivity}.
- */
- @KeyHandlerResultType public int onKeyDown(int keyCode, KeyEvent event) {
+ /** Handles the onKeyDown event of the {@link MainActivity}. */
+ @KeyHandlerResultType
+ public int onKeyDown(int keyCode, KeyEvent event) {
if (mCurrentDialog != null) {
// Consumes the keys while a Dialog is creating.
return MainActivity.KEY_EVENT_HANDLER_RESULT_HANDLED;
@@ -981,31 +990,34 @@ public class TvOverlayManager {
// Consumes the keys which may trigger system's default music player.
return MainActivity.KEY_EVENT_HANDLER_RESULT_HANDLED;
}
- if (mMenu.isActive() || mSideFragmentManager.isActive() || mProgramGuide.isActive()
- || mSetupFragmentActive || mNewSourcesFragmentActive) {
+ if (mMenu.isActive()
+ || mSideFragmentManager.isActive()
+ || mProgramGuide.isActive()
+ || mSetupFragmentActive
+ || mNewSourcesFragmentActive) {
return MainActivity.KEY_EVENT_HANDLER_RESULT_DISPATCH_TO_OVERLAY;
}
if (mTransitionManager.isKeypadChannelSwitchActive()) {
- return mKeypadChannelSwitchView.onKeyDown(keyCode, event) ?
- MainActivity.KEY_EVENT_HANDLER_RESULT_HANDLED
+ return mKeypadChannelSwitchView.onKeyDown(keyCode, event)
+ ? MainActivity.KEY_EVENT_HANDLER_RESULT_HANDLED
: MainActivity.KEY_EVENT_HANDLER_RESULT_NOT_HANDLED;
}
if (mTransitionManager.isSelectInputActive()) {
- return mSelectInputView.onKeyDown(keyCode, event) ?
- MainActivity.KEY_EVENT_HANDLER_RESULT_HANDLED
+ return mSelectInputView.onKeyDown(keyCode, event)
+ ? MainActivity.KEY_EVENT_HANDLER_RESULT_HANDLED
: MainActivity.KEY_EVENT_HANDLER_RESULT_NOT_HANDLED;
}
return MainActivity.KEY_EVENT_HANDLER_RESULT_PASSTHROUGH;
}
- /**
- * Handles the onKeyUp event of the {@link MainActivity}.
- */
- @KeyHandlerResultType public int onKeyUp(int keyCode, KeyEvent event) {
+ /** Handles the onKeyUp event of the {@link MainActivity}. */
+ @KeyHandlerResultType
+ public int onKeyUp(int keyCode, KeyEvent event) {
// Handle media key here because it is related to the menu.
if (isMediaStartKey(keyCode)) {
// The media key should not be passed up to the system in any cases.
- if (mCurrentDialog != null || mProgramGuide.isActive()
+ if (mCurrentDialog != null
+ || mProgramGuide.isActive()
|| mSideFragmentManager.isActive()
|| mSearchFragment.isVisible()
|| mTransitionManager.isKeypadChannelSwitchActive()
@@ -1015,6 +1027,10 @@ public class TvOverlayManager {
// Do not handle media key when any pop-ups which can handle keys are active.
return MainActivity.KEY_EVENT_HANDLER_RESULT_HANDLED;
}
+ if (mTvView.isScreenBlocked()) {
+ // Do not handle media key when screen is blocked.
+ return MainActivity.KEY_EVENT_HANDLER_RESULT_HANDLED;
+ }
TimeShiftManager timeShiftManager = mMainActivity.getTimeShiftManager();
if (!timeShiftManager.isAvailable()) {
return MainActivity.KEY_EVENT_HANDLER_RESULT_HANDLED;
@@ -1091,9 +1107,10 @@ public class TvOverlayManager {
if (timeShiftManager.isPaused()) {
timeShiftManager.play();
}
- hideOverlays(TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANELS
- | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_DIALOG
- | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_FRAGMENT);
+ hideOverlays(
+ TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANELS
+ | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_DIALOG
+ | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_FRAGMENT);
return MainActivity.KEY_EVENT_HANDLER_RESULT_HANDLED;
}
if (mMenu.isActive()) {
@@ -1109,8 +1126,8 @@ public class TvOverlayManager {
mTransitionManager.goToEmptyScene(true);
return MainActivity.KEY_EVENT_HANDLER_RESULT_HANDLED;
}
- return mKeypadChannelSwitchView.onKeyUp(keyCode, event) ?
- MainActivity.KEY_EVENT_HANDLER_RESULT_HANDLED
+ return mKeypadChannelSwitchView.onKeyUp(keyCode, event)
+ ? MainActivity.KEY_EVENT_HANDLER_RESULT_HANDLED
: MainActivity.KEY_EVENT_HANDLER_RESULT_NOT_HANDLED;
}
if (mTransitionManager.isSelectInputActive()) {
@@ -1118,8 +1135,8 @@ public class TvOverlayManager {
mTransitionManager.goToEmptyScene(true);
return MainActivity.KEY_EVENT_HANDLER_RESULT_HANDLED;
}
- return mSelectInputView.onKeyUp(keyCode, event) ?
- MainActivity.KEY_EVENT_HANDLER_RESULT_HANDLED
+ return mSelectInputView.onKeyUp(keyCode, event)
+ ? MainActivity.KEY_EVENT_HANDLER_RESULT_HANDLED
: MainActivity.KEY_EVENT_HANDLER_RESULT_NOT_HANDLED;
}
if (mSetupFragmentActive) {
@@ -1139,9 +1156,7 @@ public class TvOverlayManager {
return MainActivity.KEY_EVENT_HANDLER_RESULT_PASSTHROUGH;
}
- /**
- * Checks whether the given {@code keyCode} can start the system's music app or not.
- */
+ /** Checks whether the given {@code keyCode} can start the system's music app or not. */
private static boolean isMediaStartKey(int keyCode) {
switch (keyCode) {
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
@@ -1190,8 +1205,11 @@ public class TvOverlayManager {
private final boolean mKeepSidePanelHistory;
private final boolean mKeepProgramGuide;
- PendingDialogAction(String tag, SafeDismissDialogFragment dialog,
- boolean keepSidePanelHistory, boolean keepProgramGuide) {
+ PendingDialogAction(
+ String tag,
+ SafeDismissDialogFragment dialog,
+ boolean keepSidePanelHistory,
+ boolean keepProgramGuide) {
mTag = tag;
mDialog = dialog;
mKeepSidePanelHistory = keepSidePanelHistory;
diff --git a/src/com/android/tv/ui/TvTransitionManager.java b/src/com/android/tv/ui/TvTransitionManager.java
index 628bbb72..5af3e6f2 100644
--- a/src/com/android/tv/ui/TvTransitionManager.java
+++ b/src/com/android/tv/ui/TvTransitionManager.java
@@ -30,19 +30,23 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.FrameLayout.LayoutParams;
-
import com.android.tv.MainActivity;
import com.android.tv.R;
-import com.android.tv.data.Channel;
-
+import com.android.tv.data.api.Channel;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public class TvTransitionManager extends TransitionManager {
@Retention(RetentionPolicy.SOURCE)
- @IntDef({SCENE_TYPE_EMPTY, SCENE_TYPE_CHANNEL_BANNER, SCENE_TYPE_INPUT_BANNER,
- SCENE_TYPE_KEYPAD_CHANNEL_SWITCH, SCENE_TYPE_SELECT_INPUT})
+ @IntDef({
+ SCENE_TYPE_EMPTY,
+ SCENE_TYPE_CHANNEL_BANNER,
+ SCENE_TYPE_INPUT_BANNER,
+ SCENE_TYPE_KEYPAD_CHANNEL_SWITCH,
+ SCENE_TYPE_SELECT_INPUT
+ })
public @interface SceneType {}
+
public static final int SCENE_TYPE_EMPTY = 0;
public static final int SCENE_TYPE_CHANNEL_BANNER = 1;
public static final int SCENE_TYPE_INPUT_BANNER = 2;
@@ -70,17 +74,24 @@ public class TvTransitionManager extends TransitionManager {
private Listener mListener;
- public TvTransitionManager(MainActivity mainActivity, ViewGroup sceneContainer,
- ChannelBannerView channelBannerView, InputBannerView inputBannerView,
- KeypadChannelSwitchView keypadChannelSwitchView, SelectInputView selectInputView) {
+ public TvTransitionManager(
+ MainActivity mainActivity,
+ ViewGroup sceneContainer,
+ ChannelBannerView channelBannerView,
+ InputBannerView inputBannerView,
+ KeypadChannelSwitchView keypadChannelSwitchView,
+ SelectInputView selectInputView) {
mMainActivity = mainActivity;
mSceneContainer = sceneContainer;
mChannelBannerView = channelBannerView;
mInputBannerView = inputBannerView;
mKeypadChannelSwitchView = keypadChannelSwitchView;
mSelectInputView = selectInputView;
- mEmptyView = (FrameLayout) mMainActivity.getLayoutInflater().inflate(
- R.layout.empty_info_banner, sceneContainer, false);
+ mEmptyView =
+ (FrameLayout)
+ mMainActivity
+ .getLayoutInflater()
+ .inflate(R.layout.empty_info_banner, sceneContainer, false);
mCurrentSceneView = mEmptyView;
}
@@ -108,8 +119,10 @@ public class TvTransitionManager extends TransitionManager {
if (mCurrentScene != mInputBannerScene) {
// Show the input banner instead.
LayoutParams lp = (LayoutParams) mInputBannerView.getLayoutParams();
- lp.width = mCurrentScene == mSelectInputScene ? mSelectInputView.getWidth()
- : FrameLayout.LayoutParams.WRAP_CONTENT;
+ lp.width =
+ mCurrentScene == mSelectInputScene
+ ? mSelectInputView.getWidth()
+ : FrameLayout.LayoutParams.WRAP_CONTENT;
mInputBannerView.setLayoutParams(lp);
mInputBannerView.updateLabel();
transitionTo(mInputBannerScene);
@@ -154,33 +167,35 @@ public class TvTransitionManager extends TransitionManager {
if (mInitialized) {
return;
}
- mEnterAnimator = AnimatorInflater.loadAnimator(mMainActivity,
- R.animator.channel_banner_enter);
- mExitAnimator = AnimatorInflater.loadAnimator(mMainActivity,
- R.animator.channel_banner_exit);
+ mEnterAnimator =
+ AnimatorInflater.loadAnimator(mMainActivity, R.animator.channel_banner_enter);
+ mExitAnimator =
+ AnimatorInflater.loadAnimator(mMainActivity, R.animator.channel_banner_exit);
mEmptyScene = new Scene(mSceneContainer, (View) mEmptyView);
- mEmptyScene.setEnterAction(new Runnable() {
- @Override
- public void run() {
- FrameLayout.LayoutParams emptySceneLayoutParams =
- (FrameLayout.LayoutParams) mEmptyView.getLayoutParams();
- ViewGroup.MarginLayoutParams lp =
- (ViewGroup.MarginLayoutParams) mCurrentSceneView.getLayoutParams();
- emptySceneLayoutParams.topMargin = mCurrentSceneView.getTop();
- emptySceneLayoutParams.setMarginStart(lp.getMarginStart());
- emptySceneLayoutParams.height = mCurrentSceneView.getHeight();
- emptySceneLayoutParams.width = mCurrentSceneView.getWidth();
- mEmptyView.setLayoutParams(emptySceneLayoutParams);
- setCurrentScene(mEmptyScene, mEmptyView);
- }
- });
- mEmptyScene.setExitAction(new Runnable() {
- @Override
- public void run() {
- removeAllViewsFromOverlay();
- }
- });
+ mEmptyScene.setEnterAction(
+ new Runnable() {
+ @Override
+ public void run() {
+ FrameLayout.LayoutParams emptySceneLayoutParams =
+ (FrameLayout.LayoutParams) mEmptyView.getLayoutParams();
+ ViewGroup.MarginLayoutParams lp =
+ (ViewGroup.MarginLayoutParams) mCurrentSceneView.getLayoutParams();
+ emptySceneLayoutParams.topMargin = mCurrentSceneView.getTop();
+ emptySceneLayoutParams.setMarginStart(lp.getMarginStart());
+ emptySceneLayoutParams.height = mCurrentSceneView.getHeight();
+ emptySceneLayoutParams.width = mCurrentSceneView.getWidth();
+ mEmptyView.setLayoutParams(emptySceneLayoutParams);
+ setCurrentScene(mEmptyScene, mEmptyView);
+ }
+ });
+ mEmptyScene.setExitAction(
+ new Runnable() {
+ @Override
+ public void run() {
+ removeAllViewsFromOverlay();
+ }
+ });
mChannelBannerScene = buildScene(mSceneContainer, mChannelBannerView);
mInputBannerScene = buildScene(mSceneContainer, mInputBannerView);
@@ -189,18 +204,20 @@ public class TvTransitionManager extends TransitionManager {
mCurrentScene = mEmptyScene;
// Enter transitions
- TransitionSet enter = new TransitionSet()
- .addTransition(new SceneTransition(SceneTransition.ENTER))
- .addTransition(new Fade(Fade.IN));
+ TransitionSet enter =
+ new TransitionSet()
+ .addTransition(new SceneTransition(SceneTransition.ENTER))
+ .addTransition(new Fade(Fade.IN));
setTransition(mEmptyScene, mChannelBannerScene, enter);
setTransition(mEmptyScene, mInputBannerScene, enter);
setTransition(mEmptyScene, mKeypadChannelSwitchScene, enter);
setTransition(mEmptyScene, mSelectInputScene, enter);
// Exit transitions
- TransitionSet exit = new TransitionSet()
- .addTransition(new SceneTransition(SceneTransition.EXIT))
- .addTransition(new Fade(Fade.OUT));
+ TransitionSet exit =
+ new TransitionSet()
+ .addTransition(new SceneTransition(SceneTransition.EXIT))
+ .addTransition(new Fade(Fade.OUT));
setTransition(mChannelBannerScene, mEmptyScene, exit);
setTransition(mInputBannerScene, mEmptyScene, exit);
setTransition(mKeypadChannelSwitchScene, mEmptyScene, exit);
@@ -220,10 +237,9 @@ public class TvTransitionManager extends TransitionManager {
mInitialized = true;
}
- /**
- * Returns the type of the given scene.
- */
- @SceneType public int getSceneType(Scene scene) {
+ /** Returns the type of the given scene. */
+ @SceneType
+ public int getSceneType(Scene scene) {
if (scene == mChannelBannerScene) {
return SCENE_TYPE_CHANNEL_BANNER;
} else if (scene == mInputBannerScene) {
@@ -257,21 +273,23 @@ public class TvTransitionManager extends TransitionManager {
private Scene buildScene(ViewGroup sceneRoot, final TransitionLayout layout) {
final Scene scene = new Scene(sceneRoot, (View) layout);
- scene.setEnterAction(new Runnable() {
- @Override
- public void run() {
- boolean wasEmptyScene = (mCurrentScene == mEmptyScene);
- setCurrentScene(scene, (ViewGroup) layout);
- layout.onEnterAction(wasEmptyScene);
- }
- });
- scene.setExitAction(new Runnable() {
- @Override
- public void run() {
- removeAllViewsFromOverlay();
- layout.onExitAction();
- }
- });
+ scene.setEnterAction(
+ new Runnable() {
+ @Override
+ public void run() {
+ boolean wasEmptyScene = (mCurrentScene == mEmptyScene);
+ setCurrentScene(scene, (ViewGroup) layout);
+ layout.onEnterAction(wasEmptyScene);
+ }
+ });
+ scene.setExitAction(
+ new Runnable() {
+ @Override
+ public void run() {
+ removeAllViewsFromOverlay();
+ layout.onExitAction();
+ }
+ });
return scene;
}
@@ -294,12 +312,10 @@ public class TvTransitionManager extends TransitionManager {
}
@Override
- public void captureStartValues(TransitionValues transitionValues) {
- }
+ public void captureStartValues(TransitionValues transitionValues) {}
@Override
- public void captureEndValues(TransitionValues transitionValues) {
- }
+ public void captureEndValues(TransitionValues transitionValues) {}
@Override
public Animator createAnimator(
@@ -311,9 +327,7 @@ public class TvTransitionManager extends TransitionManager {
}
}
- /**
- * An interface for notification of the scene transition.
- */
+ /** An interface for notification of the scene transition. */
public interface Listener {
/**
* Called when the scene changes. This method is called just before the scene transition.
diff --git a/src/com/android/tv/ui/TvViewUiManager.java b/src/com/android/tv/ui/TvViewUiManager.java
index f042987a..7e354db3 100644
--- a/src/com/android/tv/ui/TvViewUiManager.java
+++ b/src/com/android/tv/ui/TvViewUiManager.java
@@ -42,16 +42,15 @@ import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
-
-import com.android.tv.Features;
import com.android.tv.R;
+import com.android.tv.TvFeatures;
import com.android.tv.TvOptionsManager;
import com.android.tv.data.DisplayMode;
import com.android.tv.util.TvSettings;
/**
- * The TvViewUiManager is responsible for handling UI layouting and animation of main TvView.
- * It also control the settings regarding TvView UI such as display mode.
+ * The TvViewUiManager is responsible for handling UI layouting and animation of main TvView. It
+ * also control the settings regarding TvView UI such as display mode.
*/
public class TvViewUiManager {
private static final String TAG = "TvViewManager";
@@ -94,7 +93,7 @@ public class TvViewUiManager {
mTvView.setLayoutParams(mTvViewFrame);
// Smooth PIP size change, we don't change surface size when
// isInPictureInPictureMode is true.
- if (!Features.PICTURE_IN_PICTURE.isEnabled(mContext)
+ if (!TvFeatures.PICTURE_IN_PICTURE.isEnabled(mContext)
|| (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
&& !((Activity) mContext).isInPictureInPictureMode())) {
mTvView.setFixedSurfaceSize(
@@ -126,16 +125,19 @@ public class TvViewUiManager {
private int mAppliedTvViewEndMargin;
private float mAppliedVideoDisplayAspectRatio;
- public TvViewUiManager(Context context, TunableTvView tvView,
- FrameLayout contentView, TvOptionsManager tvOptionManager) {
+ public TvViewUiManager(
+ Context context,
+ TunableTvView tvView,
+ FrameLayout contentView,
+ TvOptionsManager tvOptionManager) {
mContext = context;
mResources = mContext.getResources();
mTvView = tvView;
mContentView = contentView;
mTvOptionsManager = tvOptionManager;
- DisplayManager displayManager = (DisplayManager) mContext
- .getSystemService(Context.DISPLAY_SERVICE);
+ DisplayManager displayManager =
+ (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
Point size = new Point();
display.getSize(size);
@@ -143,8 +145,8 @@ public class TvViewUiManager {
mWindowHeight = size.y;
// Have an assumption that TvView Shrinking happens only in full screen.
- mTvViewShrunkenStartMargin = mResources
- .getDimensionPixelOffset(R.dimen.shrunken_tvview_margin_start);
+ mTvViewShrunkenStartMargin =
+ mResources.getDimensionPixelOffset(R.dimen.shrunken_tvview_margin_start);
mTvViewShrunkenEndMargin =
mResources.getDimensionPixelOffset(R.dimen.shrunken_tvview_margin_end)
+ mResources.getDimensionPixelSize(R.dimen.side_panel_width);
@@ -152,10 +154,12 @@ public class TvViewUiManager {
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
- mLinearOutSlowIn = AnimationUtils
- .loadInterpolator(mContext, android.R.interpolator.linear_out_slow_in);
- mFastOutLinearIn = AnimationUtils
- .loadInterpolator(mContext, android.R.interpolator.fast_out_linear_in);
+ mLinearOutSlowIn =
+ AnimationUtils.loadInterpolator(
+ mContext, android.R.interpolator.linear_out_slow_in);
+ mFastOutLinearIn =
+ AnimationUtils.loadInterpolator(
+ mContext, android.R.interpolator.fast_out_linear_in);
}
public void onConfigurationChanged(final int windowWidth, final int windowHeight) {
@@ -169,9 +173,9 @@ public class TvViewUiManager {
}
/**
- * Initializes animator in advance of using the animator to improve animation performance.
- * For fast first tune, it is not expected to be called in Activity.onCreate, but called
- * a few seconds later after onCreate.
+ * Initializes animator in advance of using the animator to improve animation performance. For
+ * fast first tune, it is not expected to be called in Activity.onCreate, but called a few
+ * seconds later after onCreate.
*/
public void initAnimatorIfNeeded() {
initTvAnimatorIfNeeded();
@@ -203,16 +207,14 @@ public class TvViewUiManager {
setDisplayMode(mDisplayModeBeforeShrunken, false, true);
}
- /**
- * Returns true, if TvView is shrunken.
- */
+ /** Returns true, if TvView is shrunken. */
public boolean isUnderShrunkenTvView() {
return mIsUnderShrunkenTvView;
}
/**
- * Returns true, if {@code displayMode} is available now. If screen ratio is matched to
- * video ratio, other display modes than {@link DisplayMode#MODE_NORMAL} are not available.
+ * Returns true, if {@code displayMode} is available now. If screen ratio is matched to video
+ * ratio, other display modes than {@link DisplayMode#MODE_NORMAL} are not available.
*/
public boolean isDisplayModeAvailable(int displayMode) {
if (displayMode == DisplayMode.MODE_NORMAL) {
@@ -226,11 +228,15 @@ public class TvViewUiManager {
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
- + ", videoDisplayAspectRatio=" + videoDisplayAspectRatio
- );
+ Log.d(
+ TAG,
+ "isDisplayModeAvailable: "
+ + "viewWidth="
+ + viewWidth
+ + ", viewHeight="
+ + viewHeight
+ + ", videoDisplayAspectRatio="
+ + videoDisplayAspectRatio);
}
return false;
}
@@ -239,9 +245,7 @@ public class TvViewUiManager {
return Math.abs(viewRatio - videoDisplayAspectRatio) >= DISPLAY_MODE_EPSILON;
}
- /**
- * Returns a constant defined in DisplayMode.
- */
+ /** Returns a constant defined in DisplayMode. */
public int getDisplayMode() {
if (isDisplayModeAvailable(mDisplayMode)) {
return mDisplayMode;
@@ -264,49 +268,45 @@ public class TvViewUiManager {
return prev;
}
- /**
- * Restores the display mode to the display mode stored in preference.
- */
+ /** Restores the display mode to the display mode stored in preference. */
public void restoreDisplayMode(boolean animate) {
- int displayMode = mSharedPreferences
- .getInt(TvSettings.PREF_DISPLAY_MODE, DisplayMode.MODE_NORMAL);
+ int displayMode =
+ mSharedPreferences.getInt(TvSettings.PREF_DISPLAY_MODE, DisplayMode.MODE_NORMAL);
setDisplayMode(displayMode, false, animate);
}
- /**
- * Updates TvView's aspect ratio. It should be called when video resolution is changed.
- */
+ /** Updates TvView's aspect ratio. It should be called when video resolution is changed. */
public void updateTvAspectRatio() {
applyDisplayMode(mTvView.getVideoDisplayAspectRatio(), false, false);
if (mTvView.isVideoAvailable() && mTvView.isFadedOut()) {
- mTvView.fadeIn(mResources.getInteger(R.integer.tvview_fade_in_duration),
- mFastOutLinearIn, null);
+ mTvView.fadeIn(
+ mResources.getInteger(R.integer.tvview_fade_in_duration),
+ mFastOutLinearIn,
+ null);
}
}
- /**
- * Fades in TvView.
- */
+ /** Fades in TvView. */
public void fadeInTvView() {
if (mTvView.isFadedOut()) {
- mTvView.fadeIn(mResources.getInteger(R.integer.tvview_fade_in_duration),
- mFastOutLinearIn, null);
+ mTvView.fadeIn(
+ mResources.getInteger(R.integer.tvview_fade_in_duration),
+ mFastOutLinearIn,
+ null);
}
}
- /**
- * Fades out TvView.
- */
+ /** Fades out TvView. */
public void fadeOutTvView(Runnable postAction) {
if (!mTvView.isFadedOut()) {
- mTvView.fadeOut(mResources.getInteger(R.integer.tvview_fade_out_duration),
- mLinearOutSlowIn, postAction);
+ mTvView.fadeOut(
+ mResources.getInteger(R.integer.tvview_fade_out_duration),
+ mLinearOutSlowIn,
+ postAction);
}
}
- /**
- * This margins will be applied when applyDisplayMode is called.
- */
+ /** This margins will be applied when applyDisplayMode is called. */
private void setTvViewMargin(int tvViewStartMargin, int tvViewEndMargin) {
mTvViewStartMargin = tvViewStartMargin;
mTvViewEndMargin = tvViewEndMargin;
@@ -316,8 +316,8 @@ public class TvViewUiManager {
return mTvViewStartMargin == 0 && mTvViewEndMargin == 0;
}
- private void setBackgroundColor(int color, FrameLayout.LayoutParams targetLayoutParams,
- boolean animate) {
+ private void setBackgroundColor(
+ int color, FrameLayout.LayoutParams targetLayoutParams, boolean animate) {
if (animate) {
initBackgroundAnimatorIfNeeded();
if (mBackgroundAnimator.isStarted()) {
@@ -327,12 +327,13 @@ public class TvViewUiManager {
int decorViewWidth = mContentView.getWidth();
int decorViewHeight = mContentView.getHeight();
- boolean hasPillarBox = mTvView.getWidth() != decorViewWidth
- || mTvView.getHeight() != decorViewHeight;
- boolean willHavePillarBox = ((targetLayoutParams.width != LayoutParams.MATCH_PARENT)
- && targetLayoutParams.width != decorViewWidth) || (
- (targetLayoutParams.height != LayoutParams.MATCH_PARENT)
- && targetLayoutParams.height != decorViewHeight);
+ boolean hasPillarBox =
+ mTvView.getWidth() != decorViewWidth || mTvView.getHeight() != decorViewHeight;
+ boolean willHavePillarBox =
+ ((targetLayoutParams.width != LayoutParams.MATCH_PARENT)
+ && targetLayoutParams.width != decorViewWidth)
+ || ((targetLayoutParams.height != LayoutParams.MATCH_PARENT)
+ && targetLayoutParams.height != decorViewHeight);
if (!isTvViewFullScreen() && !hasPillarBox) {
// If there is no pillar box, no animation is needed.
@@ -351,13 +352,27 @@ public class TvViewUiManager {
mBackgroundColor = color;
}
- private void setTvViewPosition(final FrameLayout.LayoutParams layoutParams,
- FrameLayout.LayoutParams tvViewFrame, boolean animate) {
+ private void setTvViewPosition(
+ final FrameLayout.LayoutParams layoutParams,
+ FrameLayout.LayoutParams tvViewFrame,
+ boolean animate) {
if (DEBUG) {
- Log.d(TAG, "setTvViewPosition: w=" + layoutParams.width + " h=" + layoutParams.height
- + " s=" + layoutParams.getMarginStart() + " t=" + layoutParams.topMargin
- + " e=" + layoutParams.getMarginEnd() + " b=" + layoutParams.bottomMargin
- + " animate=" + animate);
+ Log.d(
+ TAG,
+ "setTvViewPosition: w="
+ + layoutParams.width
+ + " h="
+ + layoutParams.height
+ + " s="
+ + layoutParams.getMarginStart()
+ + " t="
+ + layoutParams.topMargin
+ + " e="
+ + layoutParams.getMarginEnd()
+ + " b="
+ + layoutParams.bottomMargin
+ + " animate="
+ + animate);
}
FrameLayout.LayoutParams oldTvViewFrame = mTvViewFrame;
mTvViewLayoutParams = layoutParams;
@@ -372,21 +387,25 @@ public class TvViewUiManager {
mOldTvViewFrame = new FrameLayout.LayoutParams(oldTvViewFrame);
}
mTvViewAnimator.setObjectValues(mTvView.getTvViewLayoutParams(), layoutParams);
- mTvViewAnimator.setEvaluator(new TypeEvaluator<FrameLayout.LayoutParams>() {
- FrameLayout.LayoutParams lp;
- @Override
- public FrameLayout.LayoutParams evaluate(float fraction,
- FrameLayout.LayoutParams startValue, FrameLayout.LayoutParams endValue) {
- if (lp == null) {
- lp = new FrameLayout.LayoutParams(0, 0);
- lp.gravity = startValue.gravity;
- }
- interpolateMargins(lp, startValue, endValue, fraction);
- return lp;
- }
- });
- mTvViewAnimator
- .setInterpolator(isTvViewFullScreen() ? mFastOutLinearIn : mLinearOutSlowIn);
+ mTvViewAnimator.setEvaluator(
+ new TypeEvaluator<FrameLayout.LayoutParams>() {
+ FrameLayout.LayoutParams lp;
+
+ @Override
+ public FrameLayout.LayoutParams evaluate(
+ float fraction,
+ FrameLayout.LayoutParams startValue,
+ FrameLayout.LayoutParams endValue) {
+ if (lp == null) {
+ lp = new FrameLayout.LayoutParams(0, 0);
+ lp.gravity = startValue.gravity;
+ }
+ interpolateMargins(lp, startValue, endValue, fraction);
+ return lp;
+ }
+ });
+ mTvViewAnimator.setInterpolator(
+ isTvViewFullScreen() ? mFastOutLinearIn : mLinearOutSlowIn);
mTvViewAnimator.start();
} else {
if (mTvViewAnimator != null && mTvViewAnimator.isStarted()) {
@@ -425,38 +444,42 @@ public class TvViewUiManager {
mTvViewAnimator.setProperty(
Property.of(FrameLayout.class, ViewGroup.LayoutParams.class, "layoutParams"));
mTvViewAnimator.setDuration(mResources.getInteger(R.integer.tvview_anim_duration));
- mTvViewAnimator.addListener(new AnimatorListenerAdapter() {
- private boolean mCanceled = false;
+ mTvViewAnimator.addListener(
+ new AnimatorListenerAdapter() {
+ private boolean mCanceled = false;
- @Override
- public void onAnimationCancel(Animator animation) {
- mCanceled = true;
- }
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCanceled = true;
+ }
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mCanceled) {
- mCanceled = false;
- return;
- }
- mHandler.post(new Runnable() {
@Override
- public void run() {
- setTvViewPosition(mTvViewLayoutParams, mTvViewFrame, false);
+ public void onAnimationEnd(Animator animation) {
+ if (mCanceled) {
+ mCanceled = false;
+ return;
+ }
+ mHandler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ setTvViewPosition(mTvViewLayoutParams, mTvViewFrame, false);
+ }
+ });
+ }
+ });
+ mTvViewAnimator.addUpdateListener(
+ new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animator) {
+ float fraction = animator.getAnimatedFraction();
+ mLastAnimatedTvViewFrame =
+ (FrameLayout.LayoutParams) mTvView.getLayoutParams();
+ interpolateMargins(
+ mLastAnimatedTvViewFrame, mOldTvViewFrame, mTvViewFrame, fraction);
+ mTvView.setLayoutParams(mLastAnimatedTvViewFrame);
}
});
- }
- });
- mTvViewAnimator.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animator) {
- float fraction = animator.getAnimatedFraction();
- mLastAnimatedTvViewFrame = (FrameLayout.LayoutParams) mTvView.getLayoutParams();
- interpolateMargins(mLastAnimatedTvViewFrame,
- mOldTvViewFrame, mTvViewFrame, fraction);
- mTvView.setLayoutParams(mLastAnimatedTvViewFrame);
- }
- });
}
private void initBackgroundAnimatorIfNeeded() {
@@ -467,31 +490,33 @@ public class TvViewUiManager {
mBackgroundAnimator = new ObjectAnimator();
mBackgroundAnimator.setTarget(mContentView);
mBackgroundAnimator.setPropertyName("backgroundColor");
- mBackgroundAnimator
- .setDuration(mResources.getInteger(R.integer.tvactivity_background_anim_duration));
- mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mHandler.post(new Runnable() {
+ mBackgroundAnimator.setDuration(
+ mResources.getInteger(R.integer.tvactivity_background_anim_duration));
+ mBackgroundAnimator.addListener(
+ new AnimatorListenerAdapter() {
@Override
- public void run() {
- mContentView.setBackgroundColor(mBackgroundColor);
+ public void onAnimationEnd(Animator animation) {
+ mHandler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ mContentView.setBackgroundColor(mBackgroundColor);
+ }
+ });
}
});
- }
- });
}
- private void applyDisplayMode(float videoDisplayAspectRatio, boolean animate,
- boolean forceUpdate) {
+ private void applyDisplayMode(
+ float videoDisplayAspectRatio, boolean animate, boolean forceUpdate) {
if (videoDisplayAspectRatio <= 0f) {
videoDisplayAspectRatio = (float) mWindowWidth / mWindowHeight;
}
if (mAppliedDisplayedMode == mDisplayMode
&& mAppliedTvViewStartMargin == mTvViewStartMargin
&& mAppliedTvViewEndMargin == mTvViewEndMargin
- && Math.abs(mAppliedVideoDisplayAspectRatio - videoDisplayAspectRatio) <
- DISPLAY_ASPECT_RATIO_EPSILON) {
+ && Math.abs(mAppliedVideoDisplayAspectRatio - videoDisplayAspectRatio)
+ < DISPLAY_ASPECT_RATIO_EPSILON) {
if (!forceUpdate) {
return;
}
@@ -507,14 +532,20 @@ public class TvViewUiManager {
float availableAreaRatio = 0;
if (availableAreaWidth <= 0 || availableAreaHeight <= 0) {
displayMode = DisplayMode.MODE_FULL;
- Log.w(TAG, "Some resolution info is missing during applyDisplayMode. ("
- + "availableAreaWidth=" + availableAreaWidth + ", availableAreaHeight="
- + availableAreaHeight + ")");
+ Log.w(
+ TAG,
+ "Some resolution info is missing during applyDisplayMode. ("
+ + "availableAreaWidth="
+ + availableAreaWidth
+ + ", availableAreaHeight="
+ + availableAreaHeight
+ + ")");
} else {
availableAreaRatio = (float) availableAreaWidth / availableAreaHeight;
}
- FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(0, 0,
- ((FrameLayout.LayoutParams) mTvView.getTvViewLayoutParams()).gravity);
+ FrameLayout.LayoutParams layoutParams =
+ new FrameLayout.LayoutParams(
+ 0, 0, ((FrameLayout.LayoutParams) mTvView.getTvViewLayoutParams()).gravity);
switch (displayMode) {
case DisplayMode.MODE_ZOOM:
if (videoDisplayAspectRatio < availableAreaRatio) {
@@ -549,12 +580,18 @@ public class TvViewUiManager {
int marginStart = (availableAreaWidth - layoutParams.width) / 2;
layoutParams.setMarginStart(marginStart);
int tvViewFrameTop = (mWindowHeight - availableAreaHeight) / 2;
- FrameLayout.LayoutParams tvViewFrame = createMarginLayoutParams(
- mTvViewStartMargin, mTvViewEndMargin, tvViewFrameTop, tvViewFrameTop);
+ FrameLayout.LayoutParams tvViewFrame =
+ createMarginLayoutParams(
+ mTvViewStartMargin, mTvViewEndMargin, tvViewFrameTop, tvViewFrameTop);
setTvViewPosition(layoutParams, tvViewFrame, animate);
- setBackgroundColor(mResources.getColor(isTvViewFullScreen()
- ? R.color.tvactivity_background : R.color.tvactivity_background_on_shrunken_tvview,
- null), layoutParams, animate);
+ setBackgroundColor(
+ mResources.getColor(
+ isTvViewFullScreen()
+ ? R.color.tvactivity_background
+ : R.color.tvactivity_background_on_shrunken_tvview,
+ null),
+ layoutParams,
+ animate);
// Update the current display mode.
mTvOptionsManager.onDisplayModeChanged(displayMode);
@@ -564,12 +601,15 @@ public class TvViewUiManager {
return (int) (start + (end - start) * fraction);
}
- private static void interpolateMargins(MarginLayoutParams out,
- MarginLayoutParams startValue, MarginLayoutParams endValue, float fraction) {
+ private static void interpolateMargins(
+ MarginLayoutParams out,
+ MarginLayoutParams startValue,
+ MarginLayoutParams endValue,
+ float fraction) {
out.topMargin = interpolate(startValue.topMargin, endValue.topMargin, fraction);
out.bottomMargin = interpolate(startValue.bottomMargin, endValue.bottomMargin, fraction);
- out.setMarginStart(interpolate(startValue.getMarginStart(), endValue.getMarginStart(),
- fraction));
+ out.setMarginStart(
+ interpolate(startValue.getMarginStart(), endValue.getMarginStart(), fraction));
out.setMarginEnd(interpolate(startValue.getMarginEnd(), endValue.getMarginEnd(), fraction));
out.width = interpolate(startValue.width, endValue.width, fraction);
out.height = interpolate(startValue.height, endValue.height, fraction);
@@ -586,4 +626,4 @@ public class TvViewUiManager {
lp.height = mWindowHeight - topMargin - bottomMargin;
return lp;
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/ui/ViewUtils.java b/src/com/android/tv/ui/ViewUtils.java
index ac181752..f64a70b2 100644
--- a/src/com/android/tv/ui/ViewUtils.java
+++ b/src/com/android/tv/ui/ViewUtils.java
@@ -21,13 +21,10 @@ import android.animation.ValueAnimator;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
-
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-/**
- * A class that includes convenience methods for view classes.
- */
+/** A class that includes convenience methods for view classes. */
public class ViewUtils {
private static final String TAG = "ViewUtils";
@@ -40,49 +37,49 @@ public class ViewUtils {
try {
method = View.class.getDeclaredMethod("setTransitionAlpha", Float.TYPE);
method.invoke(v, alpha);
- } catch (NoSuchMethodException|IllegalAccessException|IllegalArgumentException
- |InvocationTargetException e) {
+ } catch (NoSuchMethodException
+ | IllegalAccessException
+ | IllegalArgumentException
+ | InvocationTargetException e) {
Log.e(TAG, "Fail to call View.setTransitionAlpha", e);
}
}
/**
* Creates an animator in view's height
+ *
* @param target the {@link view} animator performs on.
*/
public static Animator createHeightAnimator(
final View target, int initialHeight, int targetHeight) {
ValueAnimator animator = ValueAnimator.ofInt(initialHeight, targetHeight);
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- int value = (Integer) animation.getAnimatedValue();
- if (value == 0) {
- if (target.getVisibility() != View.GONE) {
- target.setVisibility(View.GONE);
+ animator.addUpdateListener(
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ int value = (Integer) animation.getAnimatedValue();
+ if (value == 0) {
+ if (target.getVisibility() != View.GONE) {
+ target.setVisibility(View.GONE);
+ }
+ } else {
+ if (target.getVisibility() != View.VISIBLE) {
+ target.setVisibility(View.VISIBLE);
+ }
+ setLayoutHeight(target, value);
+ }
}
- } else {
- if (target.getVisibility() != View.VISIBLE) {
- target.setVisibility(View.VISIBLE);
- }
- setLayoutHeight(target, value);
- }
- }
- });
+ });
return animator;
}
- /**
- * Gets view's layout height.
- */
+ /** Gets view's layout height. */
public static int getLayoutHeight(View view) {
LayoutParams layoutParams = view.getLayoutParams();
return layoutParams.height;
}
- /**
- * Sets view's layout height.
- */
+ /** Sets view's layout height. */
public static void setLayoutHeight(View view, int height) {
LayoutParams layoutParams = view.getLayoutParams();
if (height != layoutParams.height) {
@@ -90,4 +87,4 @@ public class ViewUtils {
view.setLayoutParams(layoutParams);
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/ui/hideable/AutoHideScheduler.java b/src/com/android/tv/ui/hideable/AutoHideScheduler.java
new file mode 100644
index 00000000..75859792
--- /dev/null
+++ b/src/com/android/tv/ui/hideable/AutoHideScheduler.java
@@ -0,0 +1,98 @@
+package com.android.tv.ui.hideable;
+
+import android.content.Context;
+import android.os.Looper;
+import android.os.Message;
+import android.support.annotation.NonNull;
+import android.support.annotation.UiThread;
+import android.support.annotation.VisibleForTesting;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
+import com.android.tv.common.WeakHandler;
+
+/**
+ * Schedules a view element to be hidden after a delay.
+ *
+ * <p>When accessibility is turned on elements are not automatically hidden.
+ *
+ * <p>Users of this class must pass it to {@link
+ * AccessibilityManager#addAccessibilityStateChangeListener(AccessibilityStateChangeListener)} and
+ * {@link
+ * AccessibilityManager#removeAccessibilityStateChangeListener(AccessibilityStateChangeListener)}
+ * during the appropriate live cycle event, or handle calling {@link
+ * #onAccessibilityStateChanged(boolean)}.
+ */
+@UiThread
+public final class AutoHideScheduler implements AccessibilityStateChangeListener {
+ private static final int MSG_HIDE = 1;
+
+ private final HideHandler mHandler;
+ private final Runnable mRunnable;
+
+ public AutoHideScheduler(Context context, Runnable runnable) {
+ this(
+ runnable,
+ context.getSystemService(AccessibilityManager.class),
+ Looper.getMainLooper());
+ }
+
+ @VisibleForTesting
+ AutoHideScheduler(Runnable runnable, AccessibilityManager accessibilityManager, Looper looper) {
+ // Keep a reference here because HideHandler only has a weak reference to it.
+ mRunnable = runnable;
+ mHandler = new HideHandler(looper, mRunnable);
+ mHandler.setAllowAutoHide(!accessibilityManager.isEnabled());
+ }
+
+ public void cancel() {
+ mHandler.removeMessages(MSG_HIDE);
+ }
+
+ public void schedule(long delayMs) {
+ cancel();
+ if (mHandler.mAllowAutoHide) {
+ mHandler.sendEmptyMessageDelayed(MSG_HIDE, delayMs);
+ }
+ }
+
+ @Override
+ public void onAccessibilityStateChanged(boolean enabled) {
+ mHandler.onAccessibilityStateChanged(enabled);
+ }
+
+ public boolean isScheduled() {
+ return mHandler.hasMessages(MSG_HIDE);
+ }
+
+ private static class HideHandler extends WeakHandler<Runnable>
+ implements AccessibilityStateChangeListener {
+
+ private boolean mAllowAutoHide;
+
+ public HideHandler(Looper looper, Runnable hideRunner) {
+ super(looper, hideRunner);
+ }
+
+ @Override
+ protected void handleMessage(Message msg, @NonNull Runnable runnable) {
+ switch (msg.what) {
+ case MSG_HIDE:
+ if (mAllowAutoHide) {
+ runnable.run();
+ }
+ break;
+ default:
+ // do nothing
+ }
+ }
+
+ public void setAllowAutoHide(boolean mAllowAutoHide) {
+ this.mAllowAutoHide = mAllowAutoHide;
+ }
+
+ @Override
+ public void onAccessibilityStateChanged(boolean enabled) {
+ mAllowAutoHide = !enabled;
+ }
+ }
+}
diff --git a/src/com/android/tv/ui/sidepanel/ActionItem.java b/src/com/android/tv/ui/sidepanel/ActionItem.java
index cd70a886..73c12080 100644
--- a/src/com/android/tv/ui/sidepanel/ActionItem.java
+++ b/src/com/android/tv/ui/sidepanel/ActionItem.java
@@ -18,7 +18,6 @@ package com.android.tv.ui.sidepanel;
import android.view.View;
import android.widget.TextView;
-
import com.android.tv.R;
public abstract class ActionItem extends Item {
@@ -52,4 +51,4 @@ public abstract class ActionItem extends Item {
descriptionView.setVisibility(View.GONE);
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/ui/sidepanel/ChannelCheckItem.java b/src/com/android/tv/ui/sidepanel/ChannelCheckItem.java
index 8389675e..2726839c 100644
--- a/src/com/android/tv/ui/sidepanel/ChannelCheckItem.java
+++ b/src/com/android/tv/ui/sidepanel/ChannelCheckItem.java
@@ -19,14 +19,13 @@ package com.android.tv.ui.sidepanel;
import android.text.TextUtils;
import android.view.View;
import android.widget.TextView;
-
import com.android.tv.R;
-import com.android.tv.data.Channel;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.ChannelDataManager.ChannelListener;
import com.android.tv.data.OnCurrentProgramUpdatedListener;
import com.android.tv.data.Program;
import com.android.tv.data.ProgramDataManager;
+import com.android.tv.data.api.Channel;
public abstract class ChannelCheckItem extends CompoundButtonItem {
private final ChannelDataManager mChannelDataManager;
@@ -34,25 +33,27 @@ public abstract class ChannelCheckItem extends CompoundButtonItem {
private Channel mChannel;
private TextView mProgramTitleView;
private TextView mChannelNumberView;
- private final ChannelListener mChannelListener = new ChannelListener() {
- @Override
- public void onChannelRemoved(Channel channel) { }
-
- @Override
- public void onChannelUpdated(Channel channel) {
- mChannel = channel;
- }
- };
-
- private final OnCurrentProgramUpdatedListener mOnCurrentProgramUpdatedListener
- = new OnCurrentProgramUpdatedListener() {
- @Override
- public void onCurrentProgramUpdated(long channelId, Program program) {
- updateProgramTitle(program);
- }
- };
-
- public ChannelCheckItem(Channel channel,
+ private final ChannelListener mChannelListener =
+ new ChannelListener() {
+ @Override
+ public void onChannelRemoved(Channel channel) {}
+
+ @Override
+ public void onChannelUpdated(Channel channel) {
+ mChannel = channel;
+ }
+ };
+
+ private final OnCurrentProgramUpdatedListener mOnCurrentProgramUpdatedListener =
+ new OnCurrentProgramUpdatedListener() {
+ @Override
+ public void onCurrentProgramUpdated(long channelId, Program program) {
+ updateProgramTitle(program);
+ }
+ };
+
+ public ChannelCheckItem(
+ Channel channel,
ChannelDataManager channelDataManager,
ProgramDataManager programDataManager) {
super(channel.getDisplayName(), "");
@@ -91,8 +92,8 @@ public abstract class ChannelCheckItem extends CompoundButtonItem {
mChannelNumberView = (TextView) view.findViewById(R.id.channel_number);
mProgramTitleView = (TextView) view.findViewById(R.id.program_title);
mChannelDataManager.addChannelListener(mChannel.getId(), mChannelListener);
- mProgramDataManager.addOnCurrentProgramUpdatedListener(mChannel.getId(),
- mOnCurrentProgramUpdatedListener);
+ mProgramDataManager.addOnCurrentProgramUpdatedListener(
+ mChannel.getId(), mOnCurrentProgramUpdatedListener);
}
@Override
@@ -105,8 +106,8 @@ public abstract class ChannelCheckItem extends CompoundButtonItem {
@Override
protected void onUnbind() {
mChannelDataManager.removeChannelListener(mChannel.getId(), mChannelListener);
- mProgramDataManager.removeOnCurrentProgramUpdatedListener(mChannel.getId(),
- mOnCurrentProgramUpdatedListener);
+ mProgramDataManager.removeOnCurrentProgramUpdatedListener(
+ mChannel.getId(), mOnCurrentProgramUpdatedListener);
mProgramTitleView = null;
mChannelNumberView = null;
super.onUnbind();
diff --git a/src/com/android/tv/ui/sidepanel/CheckBoxItem.java b/src/com/android/tv/ui/sidepanel/CheckBoxItem.java
index 79c2b0a7..ef828945 100644
--- a/src/com/android/tv/ui/sidepanel/CheckBoxItem.java
+++ b/src/com/android/tv/ui/sidepanel/CheckBoxItem.java
@@ -22,7 +22,6 @@ import android.view.View;
import android.widget.CompoundButton;
import android.widget.LinearLayout;
import android.widget.TextView;
-
import com.android.tv.R;
public class CheckBoxItem extends CompoundButtonItem {
@@ -46,16 +45,17 @@ public class CheckBoxItem extends CompoundButtonItem {
super.onBind(view);
if (mLayoutForLargeDescription) {
CompoundButton checkBox = (CompoundButton) view.findViewById(getCompoundButtonId());
- LinearLayout.LayoutParams lp =
- (LinearLayout.LayoutParams) checkBox.getLayoutParams();
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) checkBox.getLayoutParams();
lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
- lp.topMargin = view.getResources().getDimensionPixelOffset(
- R.dimen.option_item_check_box_margin_top);
+ lp.topMargin =
+ view.getResources()
+ .getDimensionPixelOffset(R.dimen.option_item_check_box_margin_top);
checkBox.setLayoutParams(lp);
TypedValue outValue = new TypedValue();
- view.getResources().getValue(R.dimen.option_item_check_box_line_spacing_multiplier,
- outValue, true);
+ view.getResources()
+ .getValue(
+ R.dimen.option_item_check_box_line_spacing_multiplier, outValue, true);
TextView descriptionTextView = (TextView) view.findViewById(getDescriptionViewId());
descriptionTextView.setMaxLines(Integer.MAX_VALUE);
@@ -77,4 +77,4 @@ public class CheckBoxItem extends CompoundButtonItem {
protected void onSelected() {
setChecked(!isChecked());
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/ui/sidepanel/ClosedCaptionFragment.java b/src/com/android/tv/ui/sidepanel/ClosedCaptionFragment.java
index 341e4350..c1e1d18a 100644
--- a/src/com/android/tv/ui/sidepanel/ClosedCaptionFragment.java
+++ b/src/com/android/tv/ui/sidepanel/ClosedCaptionFragment.java
@@ -23,16 +23,14 @@ import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-
import com.android.tv.R;
import com.android.tv.util.CaptionSettings;
-
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class ClosedCaptionFragment extends SideFragment {
- private static final String TRACKER_LABEL ="closed caption" ;
+ private static final String TRACKER_LABEL = "closed caption";
private boolean mResetClosedCaption;
private int mClosedCaptionOption;
private String mClosedCaptionLanguage;
@@ -66,8 +64,10 @@ public class ClosedCaptionFragment extends SideFragment {
List<TvTrackInfo> tracks = getMainActivity().getTracks(TvTrackInfo.TYPE_SUBTITLE);
if (tracks != null && !tracks.isEmpty()) {
- String selectedTrackId = captionSettings.isEnabled() ?
- getMainActivity().getSelectedTrack(TvTrackInfo.TYPE_SUBTITLE) : null;
+ String selectedTrackId =
+ captionSettings.isEnabled()
+ ? getMainActivity().getSelectedTrack(TvTrackInfo.TYPE_SUBTITLE)
+ : null;
ClosedCaptionOptionItem item = new ClosedCaptionOptionItem(null, null);
items.add(item);
if (selectedTrackId == null) {
@@ -86,37 +86,41 @@ public class ClosedCaptionFragment extends SideFragment {
}
}
if (getMainActivity().hasCaptioningSettingsActivity()) {
- items.add(new ActionItem(getString(R.string.closed_caption_system_settings),
- getString(R.string.closed_caption_system_settings_description)) {
- @Override
- protected void onSelected() {
- getMainActivity().startSystemCaptioningSettingsActivity();
- }
-
- @Override
- protected void onFocused() {
- super.onFocused();
- if (mSelectedItem != null) {
- getMainActivity().selectSubtitleTrack(
- mSelectedItem.mOption, mSelectedItem.mTrackId);
- }
- }
- });
+ items.add(
+ new ActionItem(
+ getString(R.string.closed_caption_system_settings),
+ getString(R.string.closed_caption_system_settings_description)) {
+ @Override
+ protected void onSelected() {
+ getMainActivity().startSystemCaptioningSettingsActivity();
+ }
+
+ @Override
+ protected void onFocused() {
+ super.onFocused();
+ if (mSelectedItem != null) {
+ getMainActivity()
+ .selectSubtitleTrack(
+ mSelectedItem.mOption, mSelectedItem.mTrackId);
+ }
+ }
+ });
}
return items;
}
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onDestroyView() {
if (mResetClosedCaption) {
- getMainActivity().selectSubtitleLanguage(mClosedCaptionOption, mClosedCaptionLanguage,
- mClosedCaptionTrackId);
+ getMainActivity()
+ .selectSubtitleLanguage(
+ mClosedCaptionOption, mClosedCaptionLanguage, mClosedCaptionTrackId);
}
super.onDestroyView();
}
diff --git a/src/com/android/tv/ui/sidepanel/CompoundButtonItem.java b/src/com/android/tv/ui/sidepanel/CompoundButtonItem.java
index c2746937..cc09affb 100644
--- a/src/com/android/tv/ui/sidepanel/CompoundButtonItem.java
+++ b/src/com/android/tv/ui/sidepanel/CompoundButtonItem.java
@@ -19,7 +19,6 @@ package com.android.tv.ui.sidepanel;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.TextView;
-
import com.android.tv.R;
public abstract class CompoundButtonItem extends Item {
@@ -44,8 +43,8 @@ public abstract class CompoundButtonItem extends Item {
mMaxLine = 0;
}
- public CompoundButtonItem(String checkedTitle, String uncheckedTitle, String description,
- int maxLine) {
+ public CompoundButtonItem(
+ String checkedTitle, String uncheckedTitle, String description, int maxLine) {
mCheckedTitle = checkedTitle;
mUncheckedTitle = uncheckedTitle;
mDescription = description;
@@ -73,8 +72,10 @@ public abstract class CompoundButtonItem extends Item {
descriptionView.setMaxLines(mMaxLine);
} else {
if (sDefaultMaxLine == 0) {
- sDefaultMaxLine = view.getContext().getResources()
- .getInteger(R.integer.option_item_description_max_lines);
+ sDefaultMaxLine =
+ view.getContext()
+ .getResources()
+ .getInteger(R.integer.option_item_description_max_lines);
}
descriptionView.setMaxLines(sDefaultMaxLine);
}
diff --git a/src/com/android/tv/ui/sidepanel/CustomizeChannelListFragment.java b/src/com/android/tv/ui/sidepanel/CustomizeChannelListFragment.java
index 297e69d9..48b80723 100644
--- a/src/com/android/tv/ui/sidepanel/CustomizeChannelListFragment.java
+++ b/src/com/android/tv/ui/sidepanel/CustomizeChannelListFragment.java
@@ -26,16 +26,15 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
-
import com.android.tv.MainActivity;
import com.android.tv.R;
-import com.android.tv.common.SharedPreferencesUtils;
-import com.android.tv.data.Channel;
+import com.android.tv.common.util.SharedPreferencesUtils;
+import com.android.tv.data.ChannelImpl;
import com.android.tv.data.ChannelNumber;
+import com.android.tv.data.api.Channel;
import com.android.tv.ui.OnRepeatedKeyInterceptListener;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.Utils;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -55,7 +54,7 @@ public class CustomizeChannelListFragment extends SideFragment {
private static Integer sGroupingType;
private TvInputManagerHelper mInputManager;
- private Channel.DefaultComparator mChannelComparator;
+ private ChannelImpl.DefaultComparator mChannelComparator;
private boolean mGroupByFragmentRunning;
private final List<Item> mItems = new ArrayList<>();
@@ -65,38 +64,46 @@ public class CustomizeChannelListFragment extends SideFragment {
super.onCreate(savedInstanceState);
mInputManager = getMainActivity().getTvInputManagerHelper();
mInitialChannelId = getMainActivity().getCurrentChannelId();
- mChannelComparator = new Channel.DefaultComparator(getActivity(), mInputManager);
+ mChannelComparator = new ChannelImpl.DefaultComparator(getActivity(), mInputManager);
if (sGroupingType == null) {
- SharedPreferences sharedPreferences = getContext().getSharedPreferences(
- SharedPreferencesUtils.SHARED_PREF_UI_SETTINGS, Context.MODE_PRIVATE);
+ SharedPreferences sharedPreferences =
+ getContext()
+ .getSharedPreferences(
+ SharedPreferencesUtils.SHARED_PREF_UI_SETTINGS,
+ Context.MODE_PRIVATE);
sGroupingType = sharedPreferences.getInt(PREF_KEY_GROUP_SETTINGS, GROUP_BY_SOURCE);
}
}
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
VerticalGridView listView = (VerticalGridView) view.findViewById(R.id.side_panel_list);
- listView.setOnKeyInterceptListener(new OnRepeatedKeyInterceptListener(listView) {
- @Override
- public boolean onInterceptKeyEvent(KeyEvent event) {
- // In order to send tune operation once for continuous channel up/down events,
- // we only call the moveToChannel method on ACTION_UP event of channel switch keys.
- if (event.getAction() == KeyEvent.ACTION_UP) {
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_DPAD_UP:
- case KeyEvent.KEYCODE_DPAD_DOWN:
- if (mLastFocusedChannelId != Channel.INVALID_ID) {
- getMainActivity().tuneToChannel(
- getChannelDataManager().getChannel(mLastFocusedChannelId));
+ listView.setOnKeyInterceptListener(
+ new OnRepeatedKeyInterceptListener(listView) {
+ @Override
+ public boolean onInterceptKeyEvent(KeyEvent event) {
+ // In order to send tune operation once for continuous channel up/down
+ // events,
+ // we only call the moveToChannel method on ACTION_UP event of channel
+ // switch keys.
+ if (event.getAction() == KeyEvent.ACTION_UP) {
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_DPAD_UP:
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ if (mLastFocusedChannelId != Channel.INVALID_ID) {
+ getMainActivity()
+ .tuneToChannel(
+ getChannelDataManager()
+ .getChannel(mLastFocusedChannelId));
+ }
+ break;
}
- break;
+ }
+ return super.onInterceptKeyEvent(event);
}
- }
- return super.onInterceptKeyEvent(event);
- }
- });
+ });
if (!mGroupByFragmentRunning) {
getMainActivity().startShrunkenTvView(false, true);
@@ -118,8 +125,8 @@ public class CustomizeChannelListFragment extends SideFragment {
}
mLastFocusedChannelId = mInitialChannelId;
MainActivity tvActivity = getMainActivity();
- if (mLastFocusedChannelId != Channel.INVALID_ID &&
- mLastFocusedChannelId != tvActivity.getCurrentChannelId()) {
+ if (mLastFocusedChannelId != Channel.INVALID_ID
+ && mLastFocusedChannelId != tvActivity.getCurrentChannelId()) {
tvActivity.tuneToChannel(getChannelDataManager().getChannel(mLastFocusedChannelId));
}
}
@@ -184,11 +191,11 @@ public class CustomizeChannelListFragment extends SideFragment {
Collections.sort(channels, mChannelComparator);
String inputId = null;
- for (Channel channel: channels) {
+ for (Channel channel : channels) {
if (!channel.getInputId().equals(inputId)) {
inputId = channel.getInputId();
- String inputLabel = Utils.loadLabel(getActivity(),
- mInputManager.getTvInputInfo(inputId));
+ String inputLabel =
+ Utils.loadLabel(getActivity(), mInputManager.getTvInputInfo(inputId));
items.add(new DividerItem(inputLabel));
selectGroupItem = new SelectGroupItem();
items.add(selectGroupItem);
@@ -204,27 +211,32 @@ public class CustomizeChannelListFragment extends SideFragment {
items.add(new GroupBySubMenu(getString(R.string.edit_channels_group_by_hd_sd)));
SelectGroupItem selectGroupItem = null;
ArrayList<Channel> channels = new ArrayList<>(mChannels);
- Collections.sort(channels, new Comparator<Channel>() {
- @Override
- public int compare(Channel lhs, Channel rhs) {
- boolean lhsHd = isHdChannel(lhs);
- boolean rhsHd = isHdChannel(rhs);
- if (lhsHd == rhsHd) {
- return ChannelNumber.compare(lhs.getDisplayNumber(), rhs.getDisplayNumber());
- } else {
- return lhsHd ? -1 : 1;
- }
- }
- });
+ Collections.sort(
+ channels,
+ new Comparator<Channel>() {
+ @Override
+ public int compare(Channel lhs, Channel rhs) {
+ boolean lhsHd = isHdChannel(lhs);
+ boolean rhsHd = isHdChannel(rhs);
+ if (lhsHd == rhsHd) {
+ return ChannelNumber.compare(
+ lhs.getDisplayNumber(), rhs.getDisplayNumber());
+ } else {
+ return lhsHd ? -1 : 1;
+ }
+ }
+ });
Boolean isHdGroup = null;
- for (Channel channel: channels) {
+ for (Channel channel : channels) {
boolean isHd = isHdChannel(channel);
if (isHdGroup == null || isHd != isHdGroup) {
isHdGroup = isHd;
- items.add(new DividerItem(isHd
- ? getString(R.string.edit_channels_group_divider_for_hd)
- : getString(R.string.edit_channels_group_divider_for_sd)));
+ items.add(
+ new DividerItem(
+ isHd
+ ? getString(R.string.edit_channels_group_divider_for_hd)
+ : getString(R.string.edit_channels_group_divider_for_sd)));
selectGroupItem = new SelectGroupItem();
items.add(selectGroupItem);
}
@@ -237,8 +249,8 @@ public class CustomizeChannelListFragment extends SideFragment {
private static boolean isHdChannel(Channel channel) {
String videoFormat = channel.getVideoFormat();
- return videoFormat != null &&
- (Channels.VIDEO_FORMAT_720P.equals(videoFormat)
+ return videoFormat != null
+ && (Channels.VIDEO_FORMAT_720P.equals(videoFormat)
|| Channels.VIDEO_FORMAT_1080I.equals(videoFormat)
|| Channels.VIDEO_FORMAT_1080P.equals(videoFormat)
|| Channels.VIDEO_FORMAT_2160P.equals(videoFormat)
@@ -274,9 +286,11 @@ public class CustomizeChannelListFragment extends SideFragment {
break;
}
}
- mTextView.setText(getString(mAllChecked
- ? R.string.edit_channels_item_deselect_group
- : R.string.edit_channels_item_select_group));
+ mTextView.setText(
+ getString(
+ mAllChecked
+ ? R.string.edit_channels_item_deselect_group
+ : R.string.edit_channels_item_select_group));
}
@Override
@@ -290,9 +304,11 @@ public class CustomizeChannelListFragment extends SideFragment {
}
getChannelDataManager().notifyChannelBrowsableChanged();
mAllChecked = !mAllChecked;
- mTextView.setText(getString(mAllChecked
- ? R.string.edit_channels_item_deselect_group
- : R.string.edit_channels_item_select_group));
+ mTextView.setText(
+ getString(
+ mAllChecked
+ ? R.string.edit_channels_item_deselect_group
+ : R.string.edit_channels_item_select_group));
}
}
@@ -331,6 +347,7 @@ public class CustomizeChannelListFragment extends SideFragment {
protected String getTitle() {
return getString(R.string.side_panel_title_group_by);
}
+
@Override
public String getTrackerLabel() {
return GroupBySubMenu.TRACKER_LABEL;
@@ -339,40 +356,46 @@ public class CustomizeChannelListFragment extends SideFragment {
@Override
protected List<Item> getItemList() {
List<Item> items = new ArrayList<>();
- items.add(new RadioButtonItem(
- getString(R.string.edit_channels_group_by_sources)) {
- @Override
- protected void onSelected() {
- super.onSelected();
- setGroupingType(GROUP_BY_SOURCE);
- closeFragment();
- }
- });
- items.add(new RadioButtonItem(
- getString(R.string.edit_channels_group_by_hd_sd)) {
- @Override
- protected void onSelected() {
- super.onSelected();
- setGroupingType(GROUP_BY_HD_SD);
- closeFragment();
- }
- });
+ items.add(
+ new RadioButtonItem(getString(R.string.edit_channels_group_by_sources)) {
+ @Override
+ protected void onSelected() {
+ super.onSelected();
+ setGroupingType(GROUP_BY_SOURCE);
+ closeFragment();
+ }
+ });
+ items.add(
+ new RadioButtonItem(getString(R.string.edit_channels_group_by_hd_sd)) {
+ @Override
+ protected void onSelected() {
+ super.onSelected();
+ setGroupingType(GROUP_BY_HD_SD);
+ closeFragment();
+ }
+ });
((RadioButtonItem) items.get(sGroupingType)).setChecked(true);
return items;
}
private void setGroupingType(int groupingType) {
sGroupingType = groupingType;
- SharedPreferences sharedPreferences = getContext().getSharedPreferences(
- SharedPreferencesUtils.SHARED_PREF_UI_SETTINGS, Context.MODE_PRIVATE);
+ SharedPreferences sharedPreferences =
+ getContext()
+ .getSharedPreferences(
+ SharedPreferencesUtils.SHARED_PREF_UI_SETTINGS,
+ Context.MODE_PRIVATE);
sharedPreferences.edit().putInt(PREF_KEY_GROUP_SETTINGS, groupingType).apply();
}
}
private class GroupBySubMenu extends SubMenuItem {
private static final String TRACKER_LABEL = "Group by";
+
public GroupBySubMenu(String description) {
- super(getString(R.string.edit_channels_item_group_by), description,
+ super(
+ getString(R.string.edit_channels_item_group_by),
+ description,
getMainActivity().getOverlayManager().getSideFragmentManager());
}
diff --git a/src/com/android/tv/ui/sidepanel/DeveloperOptionFragment.java b/src/com/android/tv/ui/sidepanel/DeveloperOptionFragment.java
index f633fa5a..36ee5a2d 100644
--- a/src/com/android/tv/ui/sidepanel/DeveloperOptionFragment.java
+++ b/src/com/android/tv/ui/sidepanel/DeveloperOptionFragment.java
@@ -21,22 +21,20 @@ import android.app.Activity;
import android.support.annotation.NonNull;
import android.util.Log;
import android.widget.Toast;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
-import com.android.tv.common.BuildConfig;
+import com.android.tv.TvSingletons;
+import com.android.tv.common.CommonPreferences;
import com.android.tv.common.feature.CommonFeatures;
-import com.android.tv.data.epg.EpgFetcher;
-import com.android.tv.experiments.Experiments;
-import com.android.tv.tuner.TunerPreferences;
-import com.android.tv.util.Utils;
+import com.android.tv.common.util.CommonUtils;
+
+
+
+
import java.util.ArrayList;
import java.util.List;
-/**
- * Options for developers only
- */
+/** Options for developers only */
public class DeveloperOptionFragment extends SideFragment {
private static final String TAG = "DeveloperOptionFragment";
private static final String TRACKER_LABEL = "debug options";
@@ -55,42 +53,46 @@ public class DeveloperOptionFragment extends SideFragment {
protected List<Item> getItemList() {
List<Item> items = new ArrayList<>();
if (CommonFeatures.DVR.isEnabled(getContext())) {
- items.add(new ActionItem(getString(R.string.dev_item_dvr_history)) {
- @Override
- protected void onSelected() {
- getMainActivity().getOverlayManager().showDvrHistoryDialog();
- }
- });
+ items.add(
+ new ActionItem(getString(R.string.dev_item_dvr_history)) {
+ @Override
+ protected void onSelected() {
+ getMainActivity().getOverlayManager().showDvrHistoryDialog();
+ }
+ });
}
- if (Utils.isDeveloper()) {
- items.add(new ActionItem(getString(R.string.dev_item_watch_history)) {
- @Override
- protected void onSelected() {
- getMainActivity().getOverlayManager().showRecentlyWatchedDialog();
- }
- });
+ if (CommonUtils.isDeveloper()) {
+ items.add(
+ new ActionItem(getString(R.string.dev_item_watch_history)) {
+ @Override
+ protected void onSelected() {
+ getMainActivity().getOverlayManager().showRecentlyWatchedDialog();
+ }
+ });
}
- items.add(new SwitchItem(getString(R.string.dev_item_store_ts_on),
- getString(R.string.dev_item_store_ts_off),
- getString(R.string.dev_item_store_ts_description)) {
- @Override
- protected void onUpdate() {
- super.onUpdate();
- setChecked(TunerPreferences.getStoreTsStream(getContext()));
- }
+ items.add(
+ new SwitchItem(
+ getString(R.string.dev_item_store_ts_on),
+ getString(R.string.dev_item_store_ts_off),
+ getString(R.string.dev_item_store_ts_description)) {
+ @Override
+ protected void onUpdate() {
+ super.onUpdate();
+ setChecked(CommonPreferences.getStoreTsStream(getContext()));
+ }
- @Override
- protected void onSelected() {
- super.onSelected();
- TunerPreferences.setStoreTsStream(getContext(), isChecked());
- }
- });
- if (Utils.isDeveloper()) {
+ @Override
+ protected void onSelected() {
+ super.onSelected();
+ CommonPreferences.setStoreTsStream(getContext(), isChecked());
+ }
+ });
+ if (CommonUtils.isDeveloper()) {
items.add(
new ActionItem(getString(R.string.dev_item_show_performance_monitor_log)) {
@Override
protected void onSelected() {
- TvApplication.getSingletons(getContext())
+ TvSingletons.getSingletons(getContext())
.getPerformanceMonitor()
.startPerformanceMonitorEventDebugActivity(getContext());
}
@@ -98,5 +100,4 @@ public class DeveloperOptionFragment extends SideFragment {
}
return items;
}
-
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/ui/sidepanel/DisplayModeFragment.java b/src/com/android/tv/ui/sidepanel/DisplayModeFragment.java
index 29792757..c51fb38e 100644
--- a/src/com/android/tv/ui/sidepanel/DisplayModeFragment.java
+++ b/src/com/android/tv/ui/sidepanel/DisplayModeFragment.java
@@ -17,11 +17,9 @@
package com.android.tv.ui.sidepanel;
import android.content.Context;
-
import com.android.tv.R;
import com.android.tv.data.DisplayMode;
import com.android.tv.ui.TvViewUiManager;
-
import java.util.ArrayList;
import java.util.List;
diff --git a/src/com/android/tv/ui/sidepanel/DividerItem.java b/src/com/android/tv/ui/sidepanel/DividerItem.java
index 0edf8488..3c237ffc 100644
--- a/src/com/android/tv/ui/sidepanel/DividerItem.java
+++ b/src/com/android/tv/ui/sidepanel/DividerItem.java
@@ -18,14 +18,13 @@ package com.android.tv.ui.sidepanel;
import android.view.View;
import android.widget.TextView;
-
import com.android.tv.R;
public class DividerItem extends Item {
private TextView mTitleView;
private String mTitle;
- public DividerItem() { }
+ public DividerItem() {}
public DividerItem(String title) {
mTitle = title;
@@ -46,8 +45,10 @@ public class DividerItem extends Item {
} else {
mTitleView.setVisibility(View.VISIBLE);
mTitleView.setText(mTitle);
- view.setMinimumHeight(view.getContext().getResources().getDimensionPixelOffset(
- R.dimen.option_item_height));
+ view.setMinimumHeight(
+ view.getContext()
+ .getResources()
+ .getDimensionPixelOffset(R.dimen.option_item_height));
}
}
@@ -57,5 +58,5 @@ public class DividerItem extends Item {
}
@Override
- protected void onSelected() { }
+ protected void onSelected() {}
}
diff --git a/src/com/android/tv/ui/sidepanel/Item.java b/src/com/android/tv/ui/sidepanel/Item.java
index 4e47e75b..693b7536 100644
--- a/src/com/android/tv/ui/sidepanel/Item.java
+++ b/src/com/android/tv/ui/sidepanel/Item.java
@@ -35,9 +35,7 @@ public abstract class Item {
}
}
- /**
- * Sets the item to be clickable or not.
- */
+ /** Sets the item to be clickable or not. */
public void setClickable(boolean clickable) {
mClickable = clickable;
if (mItemView != null) {
@@ -45,9 +43,7 @@ public abstract class Item {
}
}
- /**
- * Returns whether this item is enabled.
- */
+ /** Returns whether this item is enabled. */
public boolean isEnabled() {
return mEnabled;
}
@@ -69,9 +65,9 @@ public abstract class Item {
}
/**
- * Called after onBind is called and when {@link #notifyUpdated} is called.
- * {@link #notifyUpdated} is usually called by {@link SideFragment#notifyItemChanged} and
- * {@link SideFragment#notifyItemsChanged}.
+ * Called after onBind is called and when {@link #notifyUpdated} is called. {@link
+ * #notifyUpdated} is usually called by {@link SideFragment#notifyItemChanged} and {@link
+ * SideFragment#notifyItemsChanged}.
*/
protected void onUpdate() {
setEnabledInternal(mItemView, mEnabled);
@@ -80,12 +76,9 @@ public abstract class Item {
protected abstract void onSelected();
- protected void onFocused() {
- }
+ protected void onFocused() {}
- /**
- * Returns true if the item is bound, i.e., onBind is called.
- */
+ /** Returns true if the item is bound, i.e., onBind is called. */
protected boolean isBound() {
return mItemView != null;
}
@@ -100,4 +93,4 @@ public abstract class Item {
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/ui/sidepanel/MultiAudioFragment.java b/src/com/android/tv/ui/sidepanel/MultiAudioFragment.java
index 10c8c3e2..03b71c8c 100644
--- a/src/com/android/tv/ui/sidepanel/MultiAudioFragment.java
+++ b/src/com/android/tv/ui/sidepanel/MultiAudioFragment.java
@@ -19,10 +19,8 @@ package com.android.tv.ui.sidepanel;
import android.media.tv.TvTrackInfo;
import android.text.TextUtils;
import android.view.KeyEvent;
-
import com.android.tv.R;
import com.android.tv.util.Utils;
-
import java.util.ArrayList;
import java.util.List;
@@ -56,9 +54,11 @@ public class MultiAudioFragment extends SideFragment {
boolean needToShowSampleRate = Utils.needToShowSampleRate(getActivity(), tracks);
int pos = 0;
for (final TvTrackInfo track : tracks) {
- RadioButtonItem item = new MultiAudioOptionItem(
- Utils.getMultiAudioString(getActivity(), track, needToShowSampleRate),
- track.getId());
+ RadioButtonItem item =
+ new MultiAudioOptionItem(
+ Utils.getMultiAudioString(
+ getActivity(), track, needToShowSampleRate),
+ track.getId());
if (track.getId().equals(mSelectedTrackId)) {
item.setChecked(true);
mInitialSelectedPosition = pos;
diff --git a/src/com/android/tv/ui/sidepanel/RadioButtonItem.java b/src/com/android/tv/ui/sidepanel/RadioButtonItem.java
index e0477493..b6c36795 100644
--- a/src/com/android/tv/ui/sidepanel/RadioButtonItem.java
+++ b/src/com/android/tv/ui/sidepanel/RadioButtonItem.java
@@ -41,4 +41,4 @@ public class RadioButtonItem extends CompoundButtonItem {
protected void onSelected() {
setChecked(true);
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/ui/sidepanel/SettingsFragment.java b/src/com/android/tv/ui/sidepanel/SettingsFragment.java
index 6a5b510c..31d00fa6 100644
--- a/src/com/android/tv/ui/sidepanel/SettingsFragment.java
+++ b/src/com/android/tv/ui/sidepanel/SettingsFragment.java
@@ -16,32 +16,28 @@
package com.android.tv.ui.sidepanel;
-import static com.android.tv.Features.TUNER;
+import static com.android.tv.TvFeatures.TUNER;
import android.app.ApplicationErrorReport;
import android.content.Intent;
import android.media.tv.TvInputInfo;
import android.view.View;
import android.widget.Toast;
-
import com.android.tv.MainActivity;
import com.android.tv.R;
import com.android.tv.TvApplication;
-import com.android.tv.customization.TvCustomizationManager;
+import com.android.tv.TvSingletons;
+import com.android.tv.common.CommonPreferences;
+import com.android.tv.common.customization.CustomizationManager;
+import com.android.tv.common.util.PermissionUtils;
import com.android.tv.dialog.PinDialogFragment;
import com.android.tv.license.LicenseSideFragment;
import com.android.tv.license.Licenses;
-import com.android.tv.tuner.TunerPreferences;
-import com.android.tv.util.PermissionUtils;
-import com.android.tv.util.SetupUtils;
import com.android.tv.util.Utils;
-
import java.util.ArrayList;
import java.util.List;
-/**
- * Shows Live TV settings.
- */
+/** Shows Live TV settings. */
public class SettingsFragment extends SideFragment {
private static final String TRACKER_LABEL = "settings";
@@ -58,58 +54,73 @@ public class SettingsFragment extends SideFragment {
@Override
protected List<Item> getItemList() {
List<Item> items = new ArrayList<>();
- final Item customizeChannelListItem = new SubMenuItem(
- getString(R.string.settings_channel_source_item_customize_channels),
- getString(R.string.settings_channel_source_item_customize_channels_description),
- getMainActivity().getOverlayManager().getSideFragmentManager()) {
- @Override
- protected SideFragment getFragment() {
- return new CustomizeChannelListFragment();
- }
+ final Item customizeChannelListItem =
+ new SubMenuItem(
+ getString(R.string.settings_channel_source_item_customize_channels),
+ getString(
+ R.string
+ .settings_channel_source_item_customize_channels_description),
+ getMainActivity().getOverlayManager().getSideFragmentManager()) {
+ @Override
+ protected SideFragment getFragment() {
+ return new CustomizeChannelListFragment();
+ }
- @Override
- protected void onBind(View view) {
- super.onBind(view);
- setEnabled(false);
- }
+ @Override
+ protected void onBind(View view) {
+ super.onBind(view);
+ setEnabled(false);
+ }
- @Override
- protected void onUpdate() {
- super.onUpdate();
- setEnabled(getChannelDataManager().getChannelCount() != 0);
- }
- };
+ @Override
+ protected void onUpdate() {
+ super.onUpdate();
+ setEnabled(getChannelDataManager().getChannelCount() != 0);
+ }
+ };
customizeChannelListItem.setEnabled(false);
items.add(customizeChannelListItem);
final MainActivity activity = getMainActivity();
- boolean hasNewInput = SetupUtils.getInstance(activity)
- .hasNewInput(activity.getTvInputManagerHelper());
- items.add(new ActionItem(
- getString(R.string.settings_channel_source_item_setup),
- hasNewInput ? getString(R.string.settings_channel_source_item_setup_new_inputs)
- : null) {
- @Override
- protected void onSelected() {
- closeFragment();
- activity.getOverlayManager().showSetupFragment();
- }
- });
+ boolean hasNewInput =
+ TvSingletons.getSingletons(getContext())
+ .getSetupUtils()
+ .hasNewInput(activity.getTvInputManagerHelper());
+ items.add(
+ new ActionItem(
+ getString(R.string.settings_channel_source_item_setup),
+ hasNewInput
+ ? getString(R.string.settings_channel_source_item_setup_new_inputs)
+ : null) {
+ @Override
+ protected void onSelected() {
+ closeFragment();
+ activity.getOverlayManager().showSetupFragment();
+ }
+ });
if (PermissionUtils.hasModifyParentalControls(getMainActivity())) {
- items.add(new ActionItem(
- getString(R.string.settings_parental_controls), getString(
- activity.getParentalControlSettings().isParentalControlsEnabled()
- ? R.string.option_toggle_parental_controls_on
- : R.string.option_toggle_parental_controls_off)) {
- @Override
- protected void onSelected() {
- getMainActivity().getOverlayManager()
- .getSideFragmentManager().hideSidePanel(true);
- PinDialogFragment fragment = PinDialogFragment
- .create(PinDialogFragment.PIN_DIALOG_TYPE_ENTER_PIN);
- getMainActivity().getOverlayManager()
- .showDialogFragment(PinDialogFragment.DIALOG_TAG, fragment, true);
- }
- });
+ items.add(
+ new ActionItem(
+ getString(R.string.settings_parental_controls),
+ getString(
+ activity.getParentalControlSettings()
+ .isParentalControlsEnabled()
+ ? R.string.option_toggle_parental_controls_on
+ : R.string.option_toggle_parental_controls_off)) {
+ @Override
+ protected void onSelected() {
+ getMainActivity()
+ .getOverlayManager()
+ .getSideFragmentManager()
+ .hideSidePanel(true);
+ PinDialogFragment fragment =
+ PinDialogFragment.create(
+ PinDialogFragment.PIN_DIALOG_TYPE_ENTER_PIN);
+ getMainActivity()
+ .getOverlayManager()
+ .showDialogFragment(
+ PinDialogFragment.DIALOG_TAG, fragment, true);
+ }
+ });
} else {
// Note: parental control is turned off, when MODIFY_PARENTAL_CONTROLS is not granted.
// But, we may be able to turn on channel lock feature regardless of the permission.
@@ -117,8 +128,10 @@ public class SettingsFragment extends SideFragment {
}
boolean showTrickplaySetting = false;
if (TUNER.isEnabled(getContext())) {
- for (TvInputInfo inputInfo : TvApplication.getSingletons(getContext())
- .getTvInputManagerHelper().getTvInputInfos(true, true)) {
+ for (TvInputInfo inputInfo :
+ TvSingletons.getSingletons(getContext())
+ .getTvInputManagerHelper()
+ .getTvInputInfos(true, true)) {
if (Utils.isInternalTvInput(getContext(), inputInfo.getId())) {
showTrickplaySetting = true;
break;
@@ -126,46 +139,51 @@ public class SettingsFragment extends SideFragment {
}
if (showTrickplaySetting) {
showTrickplaySetting =
- TvCustomizationManager.getTrickplayMode(getContext())
- == TvCustomizationManager.TRICKPLAY_MODE_ENABLED;
+ CustomizationManager.getTrickplayMode(getContext())
+ == CustomizationManager.TRICKPLAY_MODE_ENABLED;
}
}
if (showTrickplaySetting) {
items.add(
- new SwitchItem(getString(R.string.settings_trickplay),
+ new SwitchItem(
+ getString(R.string.settings_trickplay),
getString(R.string.settings_trickplay),
getString(R.string.settings_trickplay_description),
getResources().getInteger(R.integer.trickplay_description_max_lines)) {
@Override
protected void onUpdate() {
super.onUpdate();
- boolean enabled = TunerPreferences.getTrickplaySetting(getContext())
- != TunerPreferences.TRICKPLAY_SETTING_DISABLED;
+ boolean enabled =
+ CommonPreferences.getTrickplaySetting(getContext())
+ != CommonPreferences.TRICKPLAY_SETTING_DISABLED;
setChecked(enabled);
}
@Override
protected void onSelected() {
super.onSelected();
- @TunerPreferences.TrickplaySetting int setting =
- isChecked() ? TunerPreferences.TRICKPLAY_SETTING_ENABLED
- : TunerPreferences.TRICKPLAY_SETTING_DISABLED;
- TunerPreferences.setTrickplaySetting(getContext(), setting);
+ @CommonPreferences.TrickplaySetting
+ int setting =
+ isChecked()
+ ? CommonPreferences.TRICKPLAY_SETTING_ENABLED
+ : CommonPreferences.TRICKPLAY_SETTING_DISABLED;
+ CommonPreferences.setTrickplaySetting(getContext(), setting);
}
});
}
- items.add(new ActionItem(getString(R.string.settings_send_feedback)) {
- @Override
- protected void onSelected() {
- Intent intent = new Intent(Intent.ACTION_APP_ERROR);
- ApplicationErrorReport report = new ApplicationErrorReport();
- report.packageName = report.processName = getContext().getPackageName();
- report.time = System.currentTimeMillis();
- report.type = ApplicationErrorReport.TYPE_NONE;
- intent.putExtra(Intent.EXTRA_BUG_REPORT, report);
- startActivityForResult(intent, 0);
- }
- });
+ items.add(
+ new ActionItem(getString(R.string.settings_send_feedback)) {
+ @Override
+ protected void onSelected() {
+ Intent intent = new Intent(Intent.ACTION_APP_ERROR);
+ ApplicationErrorReport report = new ApplicationErrorReport();
+ report.packageName = report.processName = getContext().getPackageName();
+ report.time = System.currentTimeMillis();
+ report.type = ApplicationErrorReport.TYPE_NONE;
+ intent.putExtra(Intent.EXTRA_BUG_REPORT, report);
+ startActivityForResult(intent, 0);
+ }
+ });
if (Licenses.hasLicenses(getContext())) {
items.add(
new SubMenuItem(
@@ -178,8 +196,10 @@ public class SettingsFragment extends SideFragment {
});
}
// Show version.
- SimpleActionItem version = new SimpleActionItem(getString(R.string.settings_menu_version),
- ((TvApplication) activity.getApplicationContext()).getVersionName());
+ SimpleActionItem version =
+ new SimpleActionItem(
+ getString(R.string.settings_menu_version),
+ ((TvApplication) activity.getApplicationContext()).getVersionName());
version.setClickable(false);
items.add(version);
return items;
diff --git a/src/com/android/tv/ui/sidepanel/SideFragment.java b/src/com/android/tv/ui/sidepanel/SideFragment.java
index 6bd921a2..2902ea7f 100644
--- a/src/com/android/tv/ui/sidepanel/SideFragment.java
+++ b/src/com/android/tv/ui/sidepanel/SideFragment.java
@@ -28,18 +28,16 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
-
import com.android.tv.MainActivity;
import com.android.tv.R;
-import com.android.tv.TvApplication;
-import com.android.tv.util.DurationTimer;
+import com.android.tv.TvSingletons;
import com.android.tv.analytics.HasTrackerLabel;
import com.android.tv.analytics.Tracker;
+import com.android.tv.common.util.DurationTimer;
+import com.android.tv.common.util.SystemProperties;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.ProgramDataManager;
-import com.android.tv.util.SystemProperties;
import com.android.tv.util.ViewCache;
-
import java.util.List;
public abstract class SideFragment<T extends Item> extends Fragment implements HasTrackerLabel {
@@ -74,8 +72,8 @@ public abstract class SideFragment<T extends Item> extends Fragment implements H
/**
* @param hideKey the KeyCode used to hide the fragment
- * @param debugHideKey the KeyCode used to hide the fragment if
- * {@link SystemProperties#USE_DEBUG_KEYS}.
+ * @param debugHideKey the KeyCode used to hide the fragment if {@link
+ * SystemProperties#USE_DEBUG_KEYS}.
*/
public SideFragment(int hideKey, int debugHideKey) {
mHideKey = hideKey;
@@ -87,14 +85,15 @@ public abstract class SideFragment<T extends Item> extends Fragment implements H
super.onAttach(context);
mChannelDataManager = getMainActivity().getChannelDataManager();
mProgramDataManager = getMainActivity().getProgramDataManager();
- mTracker = TvApplication.getSingletons(context).getTracker();
+ mTracker = TvSingletons.getSingletons(context).getTracker();
}
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View view = ViewCache.getInstance().getOrCreateView(
- inflater, getFragmentLayoutResourceId(), container);
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view =
+ ViewCache.getInstance()
+ .getOrCreateView(inflater, getFragmentLayoutResourceId(), container);
TextView textView = (TextView) view.findViewById(R.id.side_panel_title);
textView.setText(getTitle());
@@ -131,8 +130,8 @@ public abstract class SideFragment<T extends Item> extends Fragment implements H
public final boolean isHideKeyForThisPanel(int keyCode) {
boolean debugKeysEnabled = SystemProperties.USE_DEBUG_KEYS.getValue();
- return mHideKey != KeyEvent.KEYCODE_UNKNOWN &&
- (mHideKey == keyCode || (debugKeysEnabled && mDebugHideKey == keyCode));
+ return mHideKey != KeyEvent.KEYCODE_UNKNOWN
+ && (mHideKey == keyCode || (debugKeysEnabled && mDebugHideKey == keyCode));
}
@Override
@@ -195,16 +194,14 @@ public abstract class SideFragment<T extends Item> extends Fragment implements H
item.notifyUpdated();
}
- /**
- * Notifies all items of ItemAdapter has changed without structural changes.
- */
+ /** Notifies all items of ItemAdapter has changed without structural changes. */
protected void notifyItemsChanged() {
notifyItemsChanged(0, mAdapter.getItemCount());
}
/**
- * Notifies some items of ItemAdapter has changed starting from position
- * <code>positionStart</code> to the end without structural changes.
+ * Notifies some items of ItemAdapter has changed starting from position <code>positionStart
+ * </code> to the end without structural changes.
*/
protected void notifyItemsChanged(int positionStart) {
notifyItemsChanged(positionStart, mAdapter.getItemCount() - positionStart);
@@ -225,20 +222,20 @@ public abstract class SideFragment<T extends Item> extends Fragment implements H
}
protected abstract String getTitle();
+
@Override
public abstract String getTrackerLabel();
+
protected abstract List<T> getItemList();
public interface SideFragmentListener {
void onSideFragmentViewDestroyed();
}
- /**
- * Preloads the item views.
- */
+ /** Preloads the item views. */
public static void preloadItemViews(Context context) {
- ViewCache.getInstance().putView(
- context, R.layout.option_fragment, new FrameLayout(context), 1);
+ ViewCache.getInstance()
+ .putView(context, R.layout.option_fragment, new FrameLayout(context), 1);
VerticalGridView fakeParent = new VerticalGridView(context);
for (int id : PRELOAD_VIEW_IDS) {
sRecycledViewPool.setMaxRecycledViews(id, PRELOAD_VIEW_SIZE);
@@ -246,9 +243,7 @@ public abstract class SideFragment<T extends Item> extends Fragment implements H
}
}
- /**
- * Releases the recycled view pool.
- */
+ /** Releases the recycled view pool. */
public static void releaseRecycledViewPool() {
sRecycledViewPool.clear();
}
diff --git a/src/com/android/tv/ui/sidepanel/SideFragmentManager.java b/src/com/android/tv/ui/sidepanel/SideFragmentManager.java
index d02d3fb7..5bba4097 100644
--- a/src/com/android/tv/ui/sidepanel/SideFragmentManager.java
+++ b/src/com/android/tv/ui/sidepanel/SideFragmentManager.java
@@ -22,13 +22,14 @@ import android.animation.AnimatorListenerAdapter;
import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
-import android.os.Handler;
import android.view.View;
import android.view.ViewTreeObserver;
-
+import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
import com.android.tv.R;
+import com.android.tv.ui.hideable.AutoHideScheduler;
-public class SideFragmentManager {
+/** Manages {@link SideFragment}s. */
+public class SideFragmentManager implements AccessibilityStateChangeListener {
private static final String FIRST_BACKSTACK_RECORD_NAME = "0";
private final Activity mActivity;
@@ -45,17 +46,11 @@ public class SideFragmentManager {
private final Animator mShowAnimator;
private final Animator mHideAnimator;
- private final Handler mHandler = new Handler();
- private final Runnable mHideAllRunnable = new Runnable() {
- @Override
- public void run() {
- hideAll(true);
- }
- };
+ private final AutoHideScheduler mAutoHideScheduler;
private final long mShowDurationMillis;
- public SideFragmentManager(Activity activity, Runnable preShowRunnable,
- Runnable postHideRunnable) {
+ public SideFragmentManager(
+ Activity activity, Runnable preShowRunnable, Runnable postHideRunnable) {
mActivity = activity;
mFragmentManager = mActivity.getFragmentManager();
mPreShowRunnable = preShowRunnable;
@@ -66,16 +61,18 @@ public class SideFragmentManager {
mShowAnimator.setTarget(mPanel);
mHideAnimator = AnimatorInflater.loadAnimator(mActivity, R.animator.side_panel_exit);
mHideAnimator.setTarget(mPanel);
- mHideAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // Animation is still in running state at this point.
- hideAllInternal();
- }
- });
+ mHideAnimator.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Animation is still in running state at this point.
+ hideAllInternal();
+ }
+ });
- mShowDurationMillis = mActivity.getResources().getInteger(
- R.integer.side_panel_show_duration);
+ mShowDurationMillis =
+ mActivity.getResources().getInteger(R.integer.side_panel_show_duration);
+ mAutoHideScheduler = new AutoHideScheduler(activity, () -> hideAll(true));
}
public int getCount() {
@@ -90,16 +87,12 @@ public class SideFragmentManager {
return mHideAnimator.isStarted();
}
- /**
- * Shows the given {@link SideFragment}.
- */
+ /** Shows the given {@link SideFragment}. */
public void show(SideFragment sideFragment) {
show(sideFragment, true);
}
- /**
- * Shows the given {@link SideFragment}.
- */
+ /** Shows the given {@link SideFragment}. */
public void show(SideFragment sideFragment, boolean showEnterAnimation) {
if (isHiding()) {
mHideAnimator.end();
@@ -114,7 +107,8 @@ public class SideFragmentManager {
R.animator.side_panel_fragment_pop_exit);
}
ft.replace(R.id.side_fragment_container, sideFragment)
- .addToBackStack(Integer.toString(mFragmentCount)).commit();
+ .addToBackStack(Integer.toString(mFragmentCount))
+ .commit();
mFragmentCount++;
if (isFirst) {
@@ -122,17 +116,18 @@ public class SideFragmentManager {
// slide-in animation to prevent jankiness resulted by performing transition and
// layouting at the same time with animation.
mPanel.setVisibility(View.VISIBLE);
- mShowOnGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- mPanel.getViewTreeObserver().removeOnGlobalLayoutListener(this);
- mShowOnGlobalLayoutListener = null;
- if (mPreShowRunnable != null) {
- mPreShowRunnable.run();
- }
- mShowAnimator.start();
- }
- };
+ mShowOnGlobalLayoutListener =
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ mPanel.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ mShowOnGlobalLayoutListener = null;
+ if (mPreShowRunnable != null) {
+ mPreShowRunnable.run();
+ }
+ mShowAnimator.start();
+ }
+ };
mPanel.getViewTreeObserver().addOnGlobalLayoutListener(mShowOnGlobalLayoutListener);
}
scheduleHideAll();
@@ -177,14 +172,14 @@ public class SideFragmentManager {
}
private void hideAllInternal() {
- mHandler.removeCallbacksAndMessages(null);
+ mAutoHideScheduler.cancel();
if (mFragmentCount == 0) {
return;
}
mPanel.setVisibility(View.GONE);
- mFragmentManager.popBackStack(FIRST_BACKSTACK_RECORD_NAME,
- FragmentManager.POP_BACK_STACK_INCLUSIVE);
+ mFragmentManager.popBackStack(
+ FIRST_BACKSTACK_RECORD_NAME, FragmentManager.POP_BACK_STACK_INCLUSIVE);
mFragmentCount = 0;
if (mPostHideRunnable != null) {
@@ -193,8 +188,8 @@ public class SideFragmentManager {
}
/**
- * Show the side panel with animation. If there are many entries in the fragment stack,
- * the animation look like that there's only one fragment.
+ * Show the side panel with animation. If there are many entries in the fragment stack, the
+ * animation look like that there's only one fragment.
*
* @param withAnimation specifies if animation should be shown.
*/
@@ -211,22 +206,23 @@ public class SideFragmentManager {
}
/**
- * Hide the side panel. This method just hide the panel and preserves the back
- * stack. If you want to empty the back stack, call {@link #hideAll}.
+ * Hide the side panel. This method just hide the panel and preserves the back stack. If you
+ * want to empty the back stack, call {@link #hideAll}.
*/
public void hideSidePanel(boolean withAnimation) {
- mHandler.removeCallbacks(mHideAllRunnable);
+ mAutoHideScheduler.cancel();
if (withAnimation) {
Animator hideAnimator =
AnimatorInflater.loadAnimator(mActivity, R.animator.side_panel_exit);
hideAnimator.setTarget(mPanel);
hideAnimator.start();
- hideAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mPanel.setVisibility(View.GONE);
- }
- });
+ hideAnimator.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mPanel.setVisibility(View.GONE);
+ }
+ });
} else {
mPanel.setVisibility(View.GONE);
}
@@ -236,23 +232,23 @@ public class SideFragmentManager {
return mPanel.getVisibility() == View.VISIBLE;
}
- /**
- * Resets the timer for hiding side fragment.
- */
+ /** Resets the timer for hiding side fragment. */
public void scheduleHideAll() {
- mHandler.removeCallbacks(mHideAllRunnable);
- mHandler.postDelayed(mHideAllRunnable, mShowDurationMillis);
+ mAutoHideScheduler.schedule(mShowDurationMillis);
}
- /**
- * Should {@code keyCode} hide the current panel.
- */
+ /** Should {@code keyCode} hide the current panel. */
public boolean isHideKeyForCurrentPanel(int keyCode) {
if (isActive()) {
- SideFragment current = (SideFragment) mFragmentManager.findFragmentById(
- R.id.side_fragment_container);
+ SideFragment current =
+ (SideFragment) mFragmentManager.findFragmentById(R.id.side_fragment_container);
return current != null && current.isHideKeyForThisPanel(keyCode);
}
return false;
}
+
+ @Override
+ public void onAccessibilityStateChanged(boolean enabled) {
+ mAutoHideScheduler.onAccessibilityStateChanged(enabled);
+ }
}
diff --git a/src/com/android/tv/ui/sidepanel/SimpleActionItem.java b/src/com/android/tv/ui/sidepanel/SimpleActionItem.java
index 42553b66..4457086d 100644
--- a/src/com/android/tv/ui/sidepanel/SimpleActionItem.java
+++ b/src/com/android/tv/ui/sidepanel/SimpleActionItem.java
@@ -16,9 +16,7 @@
package com.android.tv.ui.sidepanel;
-/**
- * A simple item which shows title and description.
- */
+/** A simple item which shows title and description. */
public class SimpleActionItem extends ActionItem {
public SimpleActionItem(String title) {
super(title);
@@ -29,6 +27,5 @@ public class SimpleActionItem extends ActionItem {
}
@Override
- protected void onSelected() {
- }
+ protected void onSelected() {}
}
diff --git a/src/com/android/tv/ui/sidepanel/SubMenuItem.java b/src/com/android/tv/ui/sidepanel/SubMenuItem.java
index 4b0e8e2c..5e4c77ca 100644
--- a/src/com/android/tv/ui/sidepanel/SubMenuItem.java
+++ b/src/com/android/tv/ui/sidepanel/SubMenuItem.java
@@ -16,7 +16,6 @@
package com.android.tv.ui.sidepanel;
-
public abstract class SubMenuItem extends ActionItem {
private final SideFragmentManager mSideFragmentManager;
diff --git a/src/com/android/tv/ui/sidepanel/SwitchItem.java b/src/com/android/tv/ui/sidepanel/SwitchItem.java
index 06591b62..8b5e1b64 100644
--- a/src/com/android/tv/ui/sidepanel/SwitchItem.java
+++ b/src/com/android/tv/ui/sidepanel/SwitchItem.java
@@ -31,8 +31,8 @@ public class SwitchItem extends CompoundButtonItem {
super(checkedTitle, uncheckedTitle, description);
}
- public SwitchItem(String checkedTitle, String uncheckedTitle, String description,
- int maxLines) {
+ public SwitchItem(
+ String checkedTitle, String uncheckedTitle, String description, int maxLines) {
super(checkedTitle, uncheckedTitle, description, maxLines);
}
@@ -50,4 +50,4 @@ public class SwitchItem extends CompoundButtonItem {
protected void onSelected() {
setChecked(!isChecked());
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/ui/sidepanel/parentalcontrols/ChannelsBlockedFragment.java b/src/com/android/tv/ui/sidepanel/parentalcontrols/ChannelsBlockedFragment.java
index ede5c268..4e3cf7fb 100644
--- a/src/com/android/tv/ui/sidepanel/parentalcontrols/ChannelsBlockedFragment.java
+++ b/src/com/android/tv/ui/sidepanel/parentalcontrols/ChannelsBlockedFragment.java
@@ -19,6 +19,8 @@ package com.android.tv.ui.sidepanel.parentalcontrols;
import android.database.ContentObserver;
import android.media.tv.TvContract;
import android.net.Uri;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Handler;
import android.support.v17.leanback.widget.VerticalGridView;
@@ -27,17 +29,16 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
-
import com.android.tv.R;
-import com.android.tv.data.Channel;
import com.android.tv.data.ChannelNumber;
+import com.android.tv.data.api.Channel;
+import com.android.tv.recommendation.ChannelPreviewUpdater;
import com.android.tv.ui.OnRepeatedKeyInterceptListener;
import com.android.tv.ui.sidepanel.ActionItem;
import com.android.tv.ui.sidepanel.ChannelCheckItem;
import com.android.tv.ui.sidepanel.DividerItem;
import com.android.tv.ui.sidepanel.Item;
import com.android.tv.ui.sidepanel.SideFragment;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -49,45 +50,55 @@ public class ChannelsBlockedFragment extends SideFragment {
private final List<Channel> mChannels = new ArrayList<>();
private long mLastFocusedChannelId = Channel.INVALID_ID;
private int mSelectedPosition = INVALID_POSITION;
- private final ContentObserver mProgramUpdateObserver = new ContentObserver(new Handler()) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- notifyItemsChanged();
- }
- };
+ private boolean mUpdated;
+ private final ContentObserver mProgramUpdateObserver =
+ new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ notifyItemsChanged();
+ }
+ };
private final Item mLockAllItem = new BlockAllItem();
private final List<Item> mItems = new ArrayList<>();
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
if (mSelectedPosition != INVALID_POSITION) {
setSelectedPosition(mSelectedPosition);
}
VerticalGridView listView = (VerticalGridView) view.findViewById(R.id.side_panel_list);
- listView.setOnKeyInterceptListener(new OnRepeatedKeyInterceptListener(listView) {
- @Override
- public boolean onInterceptKeyEvent(KeyEvent event) {
- // In order to send tune operation once for continuous channel up/down events,
- // we only call the moveToChannel method on ACTION_UP event of channel switch keys.
- if (event.getAction() == KeyEvent.ACTION_UP) {
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_DPAD_UP:
- case KeyEvent.KEYCODE_DPAD_DOWN:
- if (mLastFocusedChannelId != Channel.INVALID_ID) {
- getMainActivity().tuneToChannel(
- getChannelDataManager().getChannel(mLastFocusedChannelId));
+ listView.setOnKeyInterceptListener(
+ new OnRepeatedKeyInterceptListener(listView) {
+ @Override
+ public boolean onInterceptKeyEvent(KeyEvent event) {
+ // In order to send tune operation once for continuous channel up/down
+ // events,
+ // we only call the moveToChannel method on ACTION_UP event of channel
+ // switch keys.
+ if (event.getAction() == KeyEvent.ACTION_UP) {
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_DPAD_UP:
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ if (mLastFocusedChannelId != Channel.INVALID_ID) {
+ getMainActivity()
+ .tuneToChannel(
+ getChannelDataManager()
+ .getChannel(mLastFocusedChannelId));
+ }
+ break;
}
- break;
+ }
+ return super.onInterceptKeyEvent(event);
}
- }
- return super.onInterceptKeyEvent(event);
- }
- });
- getActivity().getContentResolver().registerContentObserver(TvContract.Programs.CONTENT_URI,
- true, mProgramUpdateObserver);
+ });
+ getActivity()
+ .getContentResolver()
+ .registerContentObserver(
+ TvContract.Programs.CONTENT_URI, true, mProgramUpdateObserver);
getMainActivity().startShrunkenTvView(true, true);
+ mUpdated = false;
return view;
}
@@ -96,6 +107,10 @@ public class ChannelsBlockedFragment extends SideFragment {
getActivity().getContentResolver().unregisterContentObserver(mProgramUpdateObserver);
getChannelDataManager().applyUpdatedValuesToDb();
getMainActivity().endShrunkenTvView();
+ if (VERSION.SDK_INT >= VERSION_CODES.O && mUpdated) {
+ ChannelPreviewUpdater.getInstance(getMainActivity())
+ .updatePreviewDataForChannelsImmediately();
+ }
super.onDestroyView();
}
@@ -115,22 +130,24 @@ public class ChannelsBlockedFragment extends SideFragment {
mItems.add(mLockAllItem);
mChannels.clear();
mChannels.addAll(getChannelDataManager().getChannelList());
- Collections.sort(mChannels, new Comparator<Channel>() {
- @Override
- public int compare(Channel lhs, Channel rhs) {
- if (lhs.isBrowsable() != rhs.isBrowsable()) {
- return lhs.isBrowsable() ? -1 : 1;
- }
- return ChannelNumber.compare(lhs.getDisplayNumber(), rhs.getDisplayNumber());
- }
- });
+ Collections.sort(
+ mChannels,
+ new Comparator<Channel>() {
+ @Override
+ public int compare(Channel lhs, Channel rhs) {
+ if (lhs.isBrowsable() != rhs.isBrowsable()) {
+ return lhs.isBrowsable() ? -1 : 1;
+ }
+ return ChannelNumber.compare(
+ lhs.getDisplayNumber(), rhs.getDisplayNumber());
+ }
+ });
final long currentChannelId = getMainActivity().getCurrentChannelId();
boolean hasHiddenChannels = false;
for (Channel channel : mChannels) {
if (!channel.isBrowsable() && !hasHiddenChannels) {
- mItems.add(new DividerItem(
- getString(R.string.option_channels_subheader_hidden)));
+ mItems.add(new DividerItem(getString(R.string.option_channels_subheader_hidden)));
hasHiddenChannels = true;
}
mItems.add(new ChannelBlockedItem(channel));
@@ -177,6 +194,7 @@ public class ChannelsBlockedFragment extends SideFragment {
}
mBlockedChannelCount = lock ? mChannels.size() : 0;
notifyItemsChanged();
+ mUpdated = true;
}
@Override
@@ -186,8 +204,11 @@ public class ChannelsBlockedFragment extends SideFragment {
}
private void updateText() {
- mTextView.setText(getString(areAllChannelsBlocked() ?
- R.string.option_channels_unlock_all : R.string.option_channels_lock_all));
+ mTextView.setText(
+ getString(
+ areAllChannelsBlocked()
+ ? R.string.option_channels_unlock_all
+ : R.string.option_channels_lock_all));
}
private boolean areAllChannelsBlocked() {
@@ -217,6 +238,7 @@ public class ChannelsBlockedFragment extends SideFragment {
getChannelDataManager().updateLocked(getChannel().getId(), isChecked());
mBlockedChannelCount += isChecked() ? 1 : -1;
notifyItemChanged(mLockAllItem);
+ mUpdated = true;
}
@Override
diff --git a/src/com/android/tv/ui/sidepanel/parentalcontrols/ParentalControlsFragment.java b/src/com/android/tv/ui/sidepanel/parentalcontrols/ParentalControlsFragment.java
index 9a4879fc..1c91ba9b 100644
--- a/src/com/android/tv/ui/sidepanel/parentalcontrols/ParentalControlsFragment.java
+++ b/src/com/android/tv/ui/sidepanel/parentalcontrols/ParentalControlsFragment.java
@@ -18,17 +18,15 @@ package com.android.tv.ui.sidepanel.parentalcontrols;
import android.view.View;
import android.widget.TextView;
-
import com.android.tv.MainActivity;
import com.android.tv.R;
-import com.android.tv.data.Channel;
+import com.android.tv.data.api.Channel;
import com.android.tv.dialog.PinDialogFragment;
import com.android.tv.ui.sidepanel.ActionItem;
import com.android.tv.ui.sidepanel.Item;
import com.android.tv.ui.sidepanel.SideFragment;
import com.android.tv.ui.sidepanel.SubMenuItem;
import com.android.tv.ui.sidepanel.SwitchItem;
-
import java.util.ArrayList;
import java.util.List;
@@ -36,12 +34,13 @@ public class ParentalControlsFragment extends SideFragment {
private static final String TRACKER_LABEL = "Parental controls";
private List<ActionItem> mActionItems;
- private final SideFragmentListener mSideFragmentListener = new SideFragmentListener() {
- @Override
- public void onSideFragmentViewDestroyed() {
- notifyDataSetChanged();
- }
- };
+ private final SideFragmentListener mSideFragmentListener =
+ new SideFragmentListener() {
+ @Override
+ public void onSideFragmentViewDestroyed() {
+ notifyDataSetChanged();
+ }
+ };
@Override
protected String getTitle() {
@@ -56,89 +55,103 @@ public class ParentalControlsFragment extends SideFragment {
@Override
protected List<Item> getItemList() {
List<Item> items = new ArrayList<>();
- items.add(new SwitchItem(getString(R.string.option_toggle_parental_controls_on),
- getString(R.string.option_toggle_parental_controls_off)) {
- @Override
- protected void onUpdate() {
- super.onUpdate();
- setChecked(getMainActivity().getParentalControlSettings()
- .isParentalControlsEnabled());
- }
+ items.add(
+ new SwitchItem(
+ getString(R.string.option_toggle_parental_controls_on),
+ getString(R.string.option_toggle_parental_controls_off)) {
+ @Override
+ protected void onUpdate() {
+ super.onUpdate();
+ setChecked(
+ getMainActivity()
+ .getParentalControlSettings()
+ .isParentalControlsEnabled());
+ }
- @Override
- protected void onSelected() {
- super.onSelected();
- boolean checked = isChecked();
- getMainActivity().getParentalControlSettings().setParentalControlsEnabled(checked);
- enableActionItems(checked);
- }
- });
+ @Override
+ protected void onSelected() {
+ super.onSelected();
+ boolean checked = isChecked();
+ getMainActivity()
+ .getParentalControlSettings()
+ .setParentalControlsEnabled(checked);
+ enableActionItems(checked);
+ }
+ });
mActionItems = new ArrayList<>();
- mActionItems.add(new SubMenuItem(getString(R.string.option_channels_locked), "",
- getMainActivity().getOverlayManager().getSideFragmentManager()) {
- TextView mDescriptionView;
-
- @Override
- protected SideFragment getFragment() {
- SideFragment fragment = new ChannelsBlockedFragment();
- fragment.setListener(mSideFragmentListener);
- return fragment;
- }
+ mActionItems.add(
+ new SubMenuItem(
+ getString(R.string.option_channels_locked),
+ "",
+ getMainActivity().getOverlayManager().getSideFragmentManager()) {
+ TextView mDescriptionView;
+
+ @Override
+ protected SideFragment getFragment() {
+ SideFragment fragment = new ChannelsBlockedFragment();
+ fragment.setListener(mSideFragmentListener);
+ return fragment;
+ }
- @Override
- protected void onBind(View view) {
- super.onBind(view);
- mDescriptionView = (TextView) view.findViewById(R.id.description);
- }
+ @Override
+ protected void onBind(View view) {
+ super.onBind(view);
+ mDescriptionView = (TextView) view.findViewById(R.id.description);
+ }
- @Override
- protected void onUpdate() {
- super.onUpdate();
- int lockedAndBrowsableChannelCount = 0;
- for (Channel channel : getChannelDataManager().getChannelList()) {
- if (channel.isLocked() && channel.isBrowsable()) {
- ++lockedAndBrowsableChannelCount;
+ @Override
+ protected void onUpdate() {
+ super.onUpdate();
+ int lockedAndBrowsableChannelCount = 0;
+ for (Channel channel : getChannelDataManager().getChannelList()) {
+ if (channel.isLocked() && channel.isBrowsable()) {
+ ++lockedAndBrowsableChannelCount;
+ }
+ }
+ if (lockedAndBrowsableChannelCount > 0) {
+ mDescriptionView.setText(
+ Integer.toString(lockedAndBrowsableChannelCount));
+ } else {
+ mDescriptionView.setText(
+ getMainActivity().getString(R.string.option_no_locked_channel));
+ }
}
- }
- if (lockedAndBrowsableChannelCount > 0) {
- mDescriptionView.setText(Integer.toString(lockedAndBrowsableChannelCount));
- } else {
- mDescriptionView.setText(
- getMainActivity().getString(R.string.option_no_locked_channel));
- }
- }
- @Override
- protected void onUnbind() {
- super.onUnbind();
- mDescriptionView = null;
- }
- });
- mActionItems.add(new SubMenuItem(getString(R.string.option_program_restrictions),
- ProgramRestrictionsFragment.getDescription(getMainActivity()),
- getMainActivity().getOverlayManager().getSideFragmentManager()) {
- @Override
- protected SideFragment getFragment() {
- SideFragment fragment = new ProgramRestrictionsFragment();
- fragment.setListener(mSideFragmentListener);
- return fragment;
- }
- });
- mActionItems.add(new ActionItem(getString(R.string.option_change_pin)) {
- @Override
- protected void onSelected() {
- final MainActivity tvActivity = getMainActivity();
- tvActivity.getOverlayManager().getSideFragmentManager().hideSidePanel(true);
- PinDialogFragment fragment = PinDialogFragment.create(
- PinDialogFragment.PIN_DIALOG_TYPE_NEW_PIN);
- tvActivity.getOverlayManager().showDialogFragment(PinDialogFragment.DIALOG_TAG,
- fragment, true);
- }
- });
+ @Override
+ protected void onUnbind() {
+ super.onUnbind();
+ mDescriptionView = null;
+ }
+ });
+ mActionItems.add(
+ new SubMenuItem(
+ getString(R.string.option_program_restrictions),
+ ProgramRestrictionsFragment.getDescription(getMainActivity()),
+ getMainActivity().getOverlayManager().getSideFragmentManager()) {
+ @Override
+ protected SideFragment getFragment() {
+ SideFragment fragment = new ProgramRestrictionsFragment();
+ fragment.setListener(mSideFragmentListener);
+ return fragment;
+ }
+ });
+ mActionItems.add(
+ new ActionItem(getString(R.string.option_change_pin)) {
+ @Override
+ protected void onSelected() {
+ final MainActivity tvActivity = getMainActivity();
+ tvActivity.getOverlayManager().getSideFragmentManager().hideSidePanel(true);
+ PinDialogFragment fragment =
+ PinDialogFragment.create(PinDialogFragment.PIN_DIALOG_TYPE_NEW_PIN);
+ tvActivity
+ .getOverlayManager()
+ .showDialogFragment(PinDialogFragment.DIALOG_TAG, fragment, true);
+ }
+ });
items.addAll(mActionItems);
- enableActionItems(getMainActivity().getParentalControlSettings()
- .isParentalControlsEnabled());
+ enableActionItems(
+ getMainActivity().getParentalControlSettings().isParentalControlsEnabled());
return items;
}
diff --git a/src/com/android/tv/ui/sidepanel/parentalcontrols/ProgramRestrictionsFragment.java b/src/com/android/tv/ui/sidepanel/parentalcontrols/ProgramRestrictionsFragment.java
index 1df7fe59..ead3c105 100644
--- a/src/com/android/tv/ui/sidepanel/parentalcontrols/ProgramRestrictionsFragment.java
+++ b/src/com/android/tv/ui/sidepanel/parentalcontrols/ProgramRestrictionsFragment.java
@@ -21,19 +21,19 @@ import com.android.tv.R;
import com.android.tv.ui.sidepanel.Item;
import com.android.tv.ui.sidepanel.SideFragment;
import com.android.tv.ui.sidepanel.SubMenuItem;
-
import java.util.ArrayList;
import java.util.List;
public class ProgramRestrictionsFragment extends SideFragment {
private static final String TRACKER_LABEL = "Program restrictions";
- private final SideFragmentListener mSideFragmentListener = new SideFragmentListener() {
- @Override
- public void onSideFragmentViewDestroyed() {
- notifyDataSetChanged();
- }
- };
+ private final SideFragmentListener mSideFragmentListener =
+ new SideFragmentListener() {
+ @Override
+ public void onSideFragmentViewDestroyed() {
+ notifyDataSetChanged();
+ }
+ };
public static String getDescription(MainActivity tvActivity) {
return RatingsFragment.getDescription(tvActivity);
@@ -53,33 +53,37 @@ public class ProgramRestrictionsFragment extends SideFragment {
protected List<Item> getItemList() {
List<Item> items = new ArrayList<>();
- items.add(new SubMenuItem(getString(R.string.option_country_rating_systems),
- RatingSystemsFragment.getDescription(getMainActivity()),
- getMainActivity().getOverlayManager().getSideFragmentManager()) {
- @Override
- protected SideFragment getFragment() {
- SideFragment fragment = new RatingSystemsFragment();
- fragment.setListener(mSideFragmentListener);
- return fragment;
- }
- });
+ items.add(
+ new SubMenuItem(
+ getString(R.string.option_country_rating_systems),
+ RatingSystemsFragment.getDescription(getMainActivity()),
+ getMainActivity().getOverlayManager().getSideFragmentManager()) {
+ @Override
+ protected SideFragment getFragment() {
+ SideFragment fragment = new RatingSystemsFragment();
+ fragment.setListener(mSideFragmentListener);
+ return fragment;
+ }
+ });
String ratingsDescription = RatingsFragment.getDescription(getMainActivity());
- SubMenuItem ratingsItem = new SubMenuItem(getString(R.string.option_ratings),
- ratingsDescription,
- getMainActivity().getOverlayManager().getSideFragmentManager()) {
- @Override
- protected SideFragment getFragment() {
- SideFragment fragment = new RatingsFragment();
- fragment.setListener(mSideFragmentListener);
- return fragment;
- }
- };
+ SubMenuItem ratingsItem =
+ new SubMenuItem(
+ getString(R.string.option_ratings),
+ ratingsDescription,
+ getMainActivity().getOverlayManager().getSideFragmentManager()) {
+ @Override
+ protected SideFragment getFragment() {
+ SideFragment fragment = new RatingsFragment();
+ fragment.setListener(mSideFragmentListener);
+ return fragment;
+ }
+ };
// When "None" is selected for rating systems, disable the Ratings option.
- if (RatingSystemsFragment.getDescription(getMainActivity()).equals(
- getString(R.string.option_no_enabled_rating_system))) {
+ if (RatingSystemsFragment.getDescription(getMainActivity())
+ .equals(getString(R.string.option_no_enabled_rating_system))) {
ratingsItem.setEnabled(false);
}
items.add(ratingsItem);
return items;
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/ui/sidepanel/parentalcontrols/RatingSystemsFragment.java b/src/com/android/tv/ui/sidepanel/parentalcontrols/RatingSystemsFragment.java
index ba9adc50..e0f546f9 100644
--- a/src/com/android/tv/ui/sidepanel/parentalcontrols/RatingSystemsFragment.java
+++ b/src/com/android/tv/ui/sidepanel/parentalcontrols/RatingSystemsFragment.java
@@ -17,7 +17,6 @@
package com.android.tv.ui.sidepanel.parentalcontrols;
import android.os.Bundle;
-
import com.android.tv.MainActivity;
import com.android.tv.R;
import com.android.tv.parental.ContentRatingSystem;
@@ -28,7 +27,6 @@ import com.android.tv.ui.sidepanel.CheckBoxItem;
import com.android.tv.ui.sidepanel.Item;
import com.android.tv.ui.sidepanel.SideFragment;
import com.android.tv.util.TvSettings;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -57,7 +55,8 @@ public class RatingSystemsFragment extends SideFragment {
builder.append(s.getDisplayName());
builder.append(", ");
}
- return builder.length() > 0 ? builder.substring(0, builder.length() - 2)
+ return builder.length() > 0
+ ? builder.substring(0, builder.length() - 2)
: tvActivity.getString(R.string.option_no_enabled_rating_system);
}
@@ -85,7 +84,8 @@ public class RatingSystemsFragment extends SideFragment {
// Add default, custom and preselected content rating systems to the "short" list.
for (ContentRatingSystem s : contentRatingSystems) {
- if (!s.isCustom() && s.getCountries() != null
+ if (!s.isCustom()
+ && s.getCountries() != null
&& s.getCountries().contains(Locale.getDefault().getCountry())) {
items.add(new RatingSystemItem(s));
} else if (s.isCustom() || parentalControlSettings.isContentRatingSystemEnabled(s)) {
@@ -117,12 +117,13 @@ public class RatingSystemsFragment extends SideFragment {
allItems.addAll(itemsHiddenMultipleCountries);
// Add "See All" to the "short" list.
- items.add(new ActionItem(getString(R.string.option_see_all_rating_systems)) {
- @Override
- protected void onSelected() {
- setItems(allItems);
- }
- });
+ items.add(
+ new ActionItem(getString(R.string.option_see_all_rating_systems)) {
+ @Override
+ protected void onSelected() {
+ setItems(allItems);
+ }
+ });
return items;
}
@@ -136,7 +137,8 @@ public class RatingSystemsFragment extends SideFragment {
ContentRatingsManager manager = tvActivity.getContentRatingsManager();
ParentalControlSettings settings = tvActivity.getParentalControlSettings();
for (ContentRatingSystem s : contentRatingSystems) {
- if (!s.isCustom() && s.getCountries() != null
+ if (!s.isCustom()
+ && s.getCountries() != null
&& s.getCountries().contains(Locale.getDefault().getCountry())) {
settings.setContentRatingSystemEnabled(manager, s, true);
}
@@ -158,16 +160,21 @@ public class RatingSystemsFragment extends SideFragment {
@Override
protected void onUpdate() {
super.onUpdate();
- setChecked(getMainActivity().getParentalControlSettings()
- .isContentRatingSystemEnabled(mContentRatingSystem));
+ setChecked(
+ getMainActivity()
+ .getParentalControlSettings()
+ .isContentRatingSystemEnabled(mContentRatingSystem));
}
@Override
protected void onSelected() {
super.onSelected();
- getMainActivity().getParentalControlSettings().setContentRatingSystemEnabled(
- getMainActivity().getContentRatingsManager(), mContentRatingSystem,
- isChecked());
+ getMainActivity()
+ .getParentalControlSettings()
+ .setContentRatingSystemEnabled(
+ getMainActivity().getContentRatingsManager(),
+ mContentRatingSystem,
+ isChecked());
}
}
}
diff --git a/src/com/android/tv/ui/sidepanel/parentalcontrols/RatingsFragment.java b/src/com/android/tv/ui/sidepanel/parentalcontrols/RatingsFragment.java
index 7c8cecbe..128fcd1a 100644
--- a/src/com/android/tv/ui/sidepanel/parentalcontrols/RatingsFragment.java
+++ b/src/com/android/tv/ui/sidepanel/parentalcontrols/RatingsFragment.java
@@ -26,8 +26,8 @@ import android.widget.CompoundButton;
import android.widget.ImageView;
import com.android.tv.MainActivity;
import com.android.tv.R;
+import com.android.tv.common.experiments.Experiments;
import com.android.tv.dialog.WebDialogFragment;
-import com.android.tv.experiments.Experiments;
import com.android.tv.license.LicenseUtils;
import com.android.tv.parental.ContentRatingSystem;
import com.android.tv.parental.ContentRatingSystem.Rating;
@@ -52,26 +52,23 @@ public class RatingsFragment extends SideFragment {
static {
sLevelResourceIdMap = new SparseIntArray(5);
- sLevelResourceIdMap.put(TvSettings.CONTENT_RATING_LEVEL_NONE,
- R.string.option_rating_none);
- sLevelResourceIdMap.put(TvSettings.CONTENT_RATING_LEVEL_HIGH,
- R.string.option_rating_high);
- sLevelResourceIdMap.put(TvSettings.CONTENT_RATING_LEVEL_MEDIUM,
- R.string.option_rating_medium);
- sLevelResourceIdMap.put(TvSettings.CONTENT_RATING_LEVEL_LOW,
- R.string.option_rating_low);
- sLevelResourceIdMap.put(TvSettings.CONTENT_RATING_LEVEL_CUSTOM,
- R.string.option_rating_custom);
+ sLevelResourceIdMap.put(TvSettings.CONTENT_RATING_LEVEL_NONE, R.string.option_rating_none);
+ sLevelResourceIdMap.put(TvSettings.CONTENT_RATING_LEVEL_HIGH, R.string.option_rating_high);
+ sLevelResourceIdMap.put(
+ TvSettings.CONTENT_RATING_LEVEL_MEDIUM, R.string.option_rating_medium);
+ sLevelResourceIdMap.put(TvSettings.CONTENT_RATING_LEVEL_LOW, R.string.option_rating_low);
+ sLevelResourceIdMap.put(
+ TvSettings.CONTENT_RATING_LEVEL_CUSTOM, R.string.option_rating_custom);
sDescriptionResourceIdMap = new SparseIntArray(sLevelResourceIdMap.size());
- sDescriptionResourceIdMap.put(TvSettings.CONTENT_RATING_LEVEL_HIGH,
- R.string.option_rating_high_description);
- sDescriptionResourceIdMap.put(TvSettings.CONTENT_RATING_LEVEL_MEDIUM,
- R.string.option_rating_medium_description);
- sDescriptionResourceIdMap.put(TvSettings.CONTENT_RATING_LEVEL_LOW,
- R.string.option_rating_low_description);
- sDescriptionResourceIdMap.put(TvSettings.CONTENT_RATING_LEVEL_CUSTOM,
- R.string.option_rating_custom_description);
+ sDescriptionResourceIdMap.put(
+ TvSettings.CONTENT_RATING_LEVEL_HIGH, R.string.option_rating_high_description);
+ sDescriptionResourceIdMap.put(
+ TvSettings.CONTENT_RATING_LEVEL_MEDIUM, R.string.option_rating_medium_description);
+ sDescriptionResourceIdMap.put(
+ TvSettings.CONTENT_RATING_LEVEL_LOW, R.string.option_rating_low_description);
+ sDescriptionResourceIdMap.put(
+ TvSettings.CONTENT_RATING_LEVEL_CUSTOM, R.string.option_rating_custom_description);
}
private final List<RatingLevelItem> mRatingLevelItems = new ArrayList<>();
@@ -81,8 +78,8 @@ public class RatingsFragment extends SideFragment {
private ParentalControlSettings mParentalControlSettings;
public static String getDescription(MainActivity tvActivity) {
- @ContentRatingLevel int currentLevel =
- tvActivity.getParentalControlSettings().getContentRatingLevel();
+ @ContentRatingLevel
+ int currentLevel = tvActivity.getParentalControlSettings().getContentRatingLevel();
if (sLevelResourceIdMap.indexOfKey(currentLevel) >= 0) {
return tvActivity.getString(sLevelResourceIdMap.get(currentLevel));
}
@@ -128,9 +125,10 @@ public class RatingsFragment extends SideFragment {
boolean hasSubRating = false;
items.add(new DividerItem(s.getDisplayName()));
for (Rating rating : s.getRatings()) {
- RatingItem item = rating.getSubRatings().isEmpty() ?
- new RatingItem(s, rating) :
- new RatingWithSubItem(s, rating);
+ RatingItem item =
+ rating.getSubRatings().isEmpty()
+ ? new RatingItem(s, rating)
+ : new RatingWithSubItem(s, rating);
items.add(item);
if (rating.getSubRatings().isEmpty()) {
ratingItems.add(item);
@@ -201,15 +199,18 @@ public class RatingsFragment extends SideFragment {
}
}
- private void updateDependentRatingItems(ContentRatingSystem.Order order,
- int selectedRatingOrderIndex, String contentRatingSystemId, boolean isChecked) {
+ 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))) {
+ || (ratingOrderIndex < selectedRatingOrderIndex && !isChecked))) {
item.setRatingBlocked(isChecked);
}
}
@@ -220,9 +221,11 @@ public class RatingsFragment extends SideFragment {
private final int mRatingLevel;
private RatingLevelItem(int ratingLevel) {
- super(getString(sLevelResourceIdMap.get(ratingLevel)),
- (sDescriptionResourceIdMap.indexOfKey(ratingLevel) >= 0) ?
- getString(sDescriptionResourceIdMap.get(ratingLevel)) : null);
+ super(
+ getString(sLevelResourceIdMap.get(ratingLevel)),
+ (sDescriptionResourceIdMap.indexOfKey(ratingLevel) >= 0)
+ ? getString(sDescriptionResourceIdMap.get(ratingLevel))
+ : null);
mRatingLevel = ratingLevel;
}
@@ -302,8 +305,11 @@ public class RatingsFragment extends SideFragment {
}
// Automatically check/uncheck dependent ratings.
for (int i = 0; i < mOrders.size(); i++) {
- updateDependentRatingItems(mOrders.get(i), mOrderIndexes.get(i),
- mContentRatingSystem.getId(), isChecked());
+ updateDependentRatingItems(
+ mOrders.get(i),
+ mOrderIndexes.get(i),
+ mContentRatingSystem.getId(),
+ isChecked());
}
}
@@ -337,14 +343,16 @@ public class RatingsFragment extends SideFragment {
@Override
protected void onSelected() {
- getMainActivity().getOverlayManager().getSideFragmentManager()
+ getMainActivity()
+ .getOverlayManager()
+ .getSideFragmentManager()
.show(SubRatingsFragment.create(mContentRatingSystem, mRating.getName()));
}
@Override
protected int getButtonDrawable() {
- int blockedStatus = mParentalControlSettings.getBlockedStatus(
- mContentRatingSystem, mRating);
+ int blockedStatus =
+ mParentalControlSettings.getBlockedStatus(mContentRatingSystem, mRating);
if (blockedStatus == ParentalControlSettings.RATING_BLOCKED) {
return R.drawable.btn_lock_material;
} else if (blockedStatus == ParentalControlSettings.RATING_BLOCKED_PARTIAL) {
@@ -354,11 +362,9 @@ public class RatingsFragment extends SideFragment {
}
}
- /**
- * Opens a dialog showing the sources of the rating descriptions.
- */
+ /** Opens a dialog showing the sources of the rating descriptions. */
public static class AttributionItem extends Item {
- public final static String DIALOG_TAG = AttributionItem.class.getSimpleName();
+ public static final String DIALOG_TAG = AttributionItem.class.getSimpleName();
public static final String TRACKER_LABEL = "Sources for content rating systems";
private final MainActivity mMainActivity;
@@ -373,9 +379,11 @@ public class RatingsFragment extends SideFragment {
@Override
protected void onSelected() {
- WebDialogFragment dialog = WebDialogFragment.newInstance(
- LicenseUtils.RATING_SOURCE_FILE,
- mMainActivity.getString(R.string.option_attribution), TRACKER_LABEL);
+ WebDialogFragment dialog =
+ WebDialogFragment.newInstance(
+ LicenseUtils.RATING_SOURCE_FILE,
+ mMainActivity.getString(R.string.option_attribution),
+ TRACKER_LABEL);
mMainActivity.getOverlayManager().showDialogFragment(DIALOG_TAG, dialog, false);
}
}
diff --git a/src/com/android/tv/ui/sidepanel/parentalcontrols/SubRatingsFragment.java b/src/com/android/tv/ui/sidepanel/parentalcontrols/SubRatingsFragment.java
index 4634b74c..b9b03ed1 100644
--- a/src/com/android/tv/ui/sidepanel/parentalcontrols/SubRatingsFragment.java
+++ b/src/com/android/tv/ui/sidepanel/parentalcontrols/SubRatingsFragment.java
@@ -21,7 +21,6 @@ import android.os.Bundle;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.ImageView;
-
import com.android.tv.R;
import com.android.tv.parental.ContentRatingSystem;
import com.android.tv.parental.ContentRatingSystem.Rating;
@@ -30,7 +29,6 @@ import com.android.tv.ui.sidepanel.CheckBoxItem;
import com.android.tv.ui.sidepanel.DividerItem;
import com.android.tv.ui.sidepanel.Item;
import com.android.tv.ui.sidepanel.SideFragment;
-
import java.util.ArrayList;
import java.util.List;
@@ -44,8 +42,8 @@ public class SubRatingsFragment extends SideFragment {
private Rating mRating;
private final List<SubRatingItem> mSubRatingItems = new ArrayList<>();
- public static SubRatingsFragment create(ContentRatingSystem contentRatingSystem,
- String ratingName) {
+ public static SubRatingsFragment create(
+ ContentRatingSystem contentRatingSystem, String ratingName) {
SubRatingsFragment fragment = new SubRatingsFragment();
Bundle args = new Bundle();
args.putString(ARGS_CONTENT_RATING_SYSTEM_ID, contentRatingSystem.getId());
@@ -57,8 +55,11 @@ public class SubRatingsFragment extends SideFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mContentRatingSystem = getMainActivity().getContentRatingsManager()
- .getContentRatingSystem(getArguments().getString(ARGS_CONTENT_RATING_SYSTEM_ID));
+ mContentRatingSystem =
+ getMainActivity()
+ .getContentRatingsManager()
+ .getContentRatingSystem(
+ getArguments().getString(ARGS_CONTENT_RATING_SYSTEM_ID));
if (mContentRatingSystem != null) {
mRating = mContentRatingSystem.getRating(getArguments().getString(ARGS_RATING_NAME));
}
@@ -194,22 +195,26 @@ public class SubRatingsFragment extends SideFragment {
}
private boolean isRatingEnabled() {
- return getMainActivity().getParentalControlSettings()
+ return getMainActivity()
+ .getParentalControlSettings()
.isRatingBlocked(mContentRatingSystem, mRating);
}
private boolean isSubRatingEnabled(SubRating subRating) {
- return getMainActivity().getParentalControlSettings()
+ return getMainActivity()
+ .getParentalControlSettings()
.isSubRatingEnabled(mContentRatingSystem, mRating, subRating);
}
private void setRatingEnabled(boolean enabled) {
- getMainActivity().getParentalControlSettings()
+ getMainActivity()
+ .getParentalControlSettings()
.setRatingBlocked(mContentRatingSystem, mRating, enabled);
}
private void setSubRatingEnabled(SubRating subRating, boolean enabled) {
- getMainActivity().getParentalControlSettings()
+ getMainActivity()
+ .getParentalControlSettings()
.setSubRatingBlocked(mContentRatingSystem, mRating, subRating, enabled);
}
}
diff --git a/src/com/android/tv/util/AsyncDbTask.java b/src/com/android/tv/util/AsyncDbTask.java
index 477412e4..60fa3018 100644
--- a/src/com/android/tv/util/AsyncDbTask.java
+++ b/src/com/android/tv/util/AsyncDbTask.java
@@ -27,24 +27,20 @@ import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import android.util.Log;
import android.util.Range;
-
+import com.android.tv.TvSingletons;
+import com.android.tv.common.BuildConfig;
import com.android.tv.common.SoftPreconditions;
-import com.android.tv.data.Channel;
+import com.android.tv.data.ChannelImpl;
import com.android.tv.data.Program;
+import com.android.tv.data.api.Channel;
import com.android.tv.dvr.data.RecordedProgram;
-
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.Executor;
/**
* {@link AsyncTask} that defaults to executing on its own single threaded Executor Service.
*
- * <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.
@@ -54,38 +50,19 @@ public abstract class AsyncDbTask<Params, Progress, Result>
private static final String TAG = "AsyncDbTask";
private static final boolean DEBUG = false;
- private static final NamedThreadFactory THREAD_FACTORY = new NamedThreadFactory(
- AsyncDbTask.class.getSimpleName());
- private static final ExecutorService DB_EXECUTOR = Executors
- .newSingleThreadExecutor(THREAD_FACTORY);
+ private final Executor mExecutor;
+ boolean mCalledExecuteOnDbThread;
- /**
- * Returns the single tread executor used for DbTasks.
- */
- public static ExecutorService getExecutor() {
- return DB_EXECUTOR;
- }
-
- /**
- * Executes the given command at some time in the future.
- *
- * <p>The command will be executed by {@link #getExecutor()}.
- *
- * @param command the runnable task
- * @throws RejectedExecutionException if this task cannot be
- * accepted for execution
- * @throws NullPointerException if command is null
- */
- public static void executeOnDbThread(Runnable command) {
- DB_EXECUTOR.execute(command);
+ protected AsyncDbTask(Executor mExecutor) {
+ this.mExecutor = mExecutor;
}
/**
* Returns the result of a {@link ContentResolver#query(Uri, String[], String, String[],
* String)}.
*
- * <p> {@link #doInBackground(Void...)} executes the query on call {@link #onQuery(Cursor)}
- * which is implemented by subclasses.
+ * <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)}
*/
@@ -97,9 +74,15 @@ public abstract class AsyncDbTask<Params, Progress, Result>
private final String[] mSelectionArgs;
private final String mOrderBy;
-
- public AsyncQueryTask(ContentResolver contentResolver, Uri uri, String[] projection,
- String selection, String[] selectionArgs, String orderBy) {
+ public AsyncQueryTask(
+ Executor executor,
+ ContentResolver contentResolver,
+ Uri uri,
+ String[] projection,
+ String selection,
+ String[] selectionArgs,
+ String orderBy) {
+ super(executor);
mContentResolver = contentResolver;
mUri = uri;
mProjection = projection;
@@ -110,13 +93,15 @@ 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());
+ if (!mCalledExecuteOnDbThread) {
+ 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) {
+ if (BuildConfig.ENG) {
throw e;
}
}
@@ -128,8 +113,9 @@ public abstract class AsyncDbTask<Params, Progress, Result>
if (DEBUG) {
Log.v(TAG, "Starting query for " + this);
}
- try (Cursor c = mContentResolver
- .query(mUri, mProjection, mSelection, mSelectionArgs, mOrderBy)) {
+ try (Cursor c =
+ mContentResolver.query(
+ mUri, mProjection, mSelection, mSelectionArgs, mOrderBy)) {
if (c != null && !isCancelled()) {
Result result = onQuery(c);
if (DEBUG) {
@@ -147,7 +133,7 @@ public abstract class AsyncDbTask<Params, Progress, Result>
return null;
}
} catch (Exception e) {
- SoftPreconditions.warn(TAG, null, "Error querying " + this, e);
+ SoftPreconditions.warn(TAG, null, e, "Error querying " + this);
return null;
}
}
@@ -176,14 +162,35 @@ public abstract class AsyncDbTask<Params, Progress, Result>
public abstract static class AsyncQueryListTask<T> extends AsyncQueryTask<List<T>> {
private final CursorFilter mFilter;
- public AsyncQueryListTask(ContentResolver contentResolver, Uri uri, String[] projection,
- String selection, String[] selectionArgs, String orderBy) {
- this(contentResolver, uri, projection, selection, selectionArgs, orderBy, null);
+ public AsyncQueryListTask(
+ Executor executor,
+ ContentResolver contentResolver,
+ Uri uri,
+ String[] projection,
+ String selection,
+ String[] selectionArgs,
+ String orderBy) {
+ this(
+ executor,
+ contentResolver,
+ uri,
+ projection,
+ selection,
+ selectionArgs,
+ orderBy,
+ null);
}
- public AsyncQueryListTask(ContentResolver contentResolver, Uri uri, String[] projection,
- String selection, String[] selectionArgs, String orderBy, CursorFilter filter) {
- super(contentResolver, uri, projection, selection, selectionArgs, orderBy);
+ public AsyncQueryListTask(
+ Executor executor,
+ ContentResolver contentResolver,
+ Uri uri,
+ String[] projection,
+ String selection,
+ String[] selectionArgs,
+ String orderBy,
+ CursorFilter filter) {
+ super(executor, contentResolver, uri, projection, selection, selectionArgs, orderBy);
mFilter = filter;
}
@@ -228,9 +235,15 @@ public abstract class AsyncDbTask<Params, Progress, Result>
*/
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);
+ public AsyncQueryItemTask(
+ Executor executor,
+ ContentResolver contentResolver,
+ Uri uri,
+ String[] projection,
+ String selection,
+ String[] selectionArgs,
+ String orderBy) {
+ super(executor, contentResolver, uri, projection, selection, selectionArgs, orderBy);
}
@Override
@@ -251,7 +264,6 @@ public abstract class AsyncDbTask<Params, Progress, Result>
}
return null;
}
-
}
/**
@@ -268,33 +280,55 @@ public abstract class AsyncDbTask<Params, Progress, Result>
protected abstract T fromCursor(Cursor c);
}
- /**
- * Gets an {@link List} of {@link Channel}s from {@link TvContract.Channels#CONTENT_URI}.
- */
+ /** Gets an {@link List} of {@link Channel}s from {@link TvContract.Channels#CONTENT_URI}. */
public abstract static class AsyncChannelQueryTask extends AsyncQueryListTask<Channel> {
- public AsyncChannelQueryTask(ContentResolver contentResolver) {
- super(contentResolver, TvContract.Channels.CONTENT_URI, Channel.PROJECTION,
- null, null, null);
+ public AsyncChannelQueryTask(Executor executor, ContentResolver contentResolver) {
+ super(
+ executor,
+ contentResolver,
+ TvContract.Channels.CONTENT_URI,
+ ChannelImpl.PROJECTION,
+ null,
+ null,
+ null);
}
@Override
protected final Channel fromCursor(Cursor c) {
- return Channel.fromCursor(c);
+ return ChannelImpl.fromCursor(c);
}
}
- /**
- * Gets an {@link List} of {@link Program}s from {@link TvContract.Programs#CONTENT_URI}.
- */
+ /** Gets an {@link List} of {@link Program}s from {@link TvContract.Programs#CONTENT_URI}. */
public abstract static class AsyncProgramQueryTask extends AsyncQueryListTask<Program> {
- public AsyncProgramQueryTask(ContentResolver contentResolver) {
- super(contentResolver, Programs.CONTENT_URI, Program.PROJECTION, null, null, null);
+ public AsyncProgramQueryTask(Executor executor, ContentResolver contentResolver) {
+ super(
+ executor,
+ contentResolver,
+ Programs.CONTENT_URI,
+ Program.PROJECTION,
+ null,
+ null,
+ null);
}
- public AsyncProgramQueryTask(ContentResolver contentResolver, Uri uri, String selection,
- String[] selectionArgs, String sortOrder, CursorFilter filter) {
- super(contentResolver, uri, Program.PROJECTION, selection, selectionArgs, sortOrder,
+ public AsyncProgramQueryTask(
+ Executor executor,
+ ContentResolver contentResolver,
+ Uri uri,
+ String selection,
+ String[] selectionArgs,
+ String sortOrder,
+ CursorFilter filter) {
+ super(
+ executor,
+ contentResolver,
+ uri,
+ Program.PROJECTION,
+ selection,
+ selectionArgs,
+ sortOrder,
filter);
}
@@ -304,13 +338,12 @@ public abstract class AsyncDbTask<Params, Progress, Result>
}
}
- /**
- * Gets an {@link List} of {@link TvContract.RecordedPrograms}s.
- */
+ /** Gets an {@link List} of {@link TvContract.RecordedPrograms}s. */
public abstract static class AsyncRecordedProgramQueryTask
extends AsyncQueryListTask<RecordedProgram> {
- public AsyncRecordedProgramQueryTask(ContentResolver contentResolver, Uri uri) {
- super(contentResolver, uri, RecordedProgram.PROJECTION, null, null, null);
+ public AsyncRecordedProgramQueryTask(
+ Executor executor, ContentResolver contentResolver, Uri uri) {
+ super(executor, contentResolver, uri, RecordedProgram.PROJECTION, null, null, null);
}
@Override
@@ -319,31 +352,39 @@ public abstract class AsyncDbTask<Params, Progress, Result>
}
}
- /**
- * Execute the task on the {@link #DB_EXECUTOR} thread.
- */
+ /** Execute the task on {@link TvSingletons#getDbExecutor()}. */
@SafeVarargs
@MainThread
public final void executeOnDbThread(Params... params) {
- executeOnExecutor(DB_EXECUTOR, params);
+ mCalledExecuteOnDbThread = true;
+ executeOnExecutor(mExecutor, params);
}
/**
* Gets an {@link List} of {@link Program}s for a given channel and period {@link
- * TvContract#buildProgramsUriForChannel(long, long, long)}. If the {@code period} is
- * {@code null}, then all the programs is queried.
+ * TvContract#buildProgramsUriForChannel(long, long, long)}. If the {@code period} is {@code
+ * null}, then all the programs is queried.
*/
public static class LoadProgramsForChannelTask extends AsyncProgramQueryTask {
protected final Range<Long> mPeriod;
protected final long mChannelId;
- public LoadProgramsForChannelTask(ContentResolver contentResolver, long channelId,
+ public LoadProgramsForChannelTask(
+ Executor executor,
+ ContentResolver contentResolver,
+ long channelId,
@Nullable Range<Long> period) {
- super(contentResolver, period == null
- ? TvContract.buildProgramsUriForChannel(channelId)
- : TvContract.buildProgramsUriForChannel(channelId, period.getLower(),
- period.getUpper()),
- null, null, null, null);
+ super(
+ executor,
+ contentResolver,
+ period == null
+ ? TvContract.buildProgramsUriForChannel(channelId)
+ : TvContract.buildProgramsUriForChannel(
+ channelId, period.getLower(), period.getUpper()),
+ null,
+ null,
+ null,
+ null);
mPeriod = period;
mChannelId = channelId;
}
@@ -357,14 +398,19 @@ public abstract class AsyncDbTask<Params, Progress, Result>
}
}
- /**
- * Gets a single {@link Program} from {@link TvContract.Programs#CONTENT_URI}.
- */
+ /** Gets a single {@link Program} from {@link TvContract.Programs#CONTENT_URI}. */
public static class AsyncQueryProgramTask extends AsyncQueryItemTask<Program> {
- public AsyncQueryProgramTask(ContentResolver contentResolver, long programId) {
- super(contentResolver, TvContract.buildProgramUri(programId), Program.PROJECTION, null,
- null, null);
+ public AsyncQueryProgramTask(
+ Executor executor, ContentResolver contentResolver, long programId) {
+ super(
+ executor,
+ contentResolver,
+ TvContract.buildProgramUri(programId),
+ Program.PROJECTION,
+ null,
+ null,
+ null);
}
@Override
@@ -373,8 +419,6 @@ public abstract class AsyncDbTask<Params, Progress, Result>
}
}
- /**
- * An interface which filters the row.
- */
- public interface CursorFilter extends Filter<Cursor> { }
+ /** An interface which filters the row. */
+ public interface CursorFilter extends Filter<Cursor> {}
}
diff --git a/src/com/android/tv/util/CaptionSettings.java b/src/com/android/tv/util/CaptionSettings.java
index 3b38905b..6d7e9901 100644
--- a/src/com/android/tv/util/CaptionSettings.java
+++ b/src/com/android/tv/util/CaptionSettings.java
@@ -18,7 +18,6 @@ package com.android.tv.util;
import android.content.Context;
import android.view.accessibility.CaptioningManager;
-
import java.util.Locale;
public class CaptionSettings {
@@ -32,8 +31,8 @@ public class CaptionSettings {
private String mTrackId;
public CaptionSettings(Context context) {
- mCaptioningManager = (CaptioningManager) context.getSystemService(
- Context.CAPTIONING_SERVICE);
+ mCaptioningManager =
+ (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE);
}
public final String getSystemLanguage() {
@@ -84,16 +83,12 @@ public class CaptionSettings {
mLanguage = language;
}
- /**
- * Returns the track ID to be used as an alternative key.
- */
+ /** Returns the track ID to be used as an alternative key. */
public String getTrackId() {
return mTrackId;
}
- /**
- * Sets the track ID to be used as an alternative key.
- */
+ /** Sets the track ID to be used as an alternative key. */
public void setTrackId(String trackId) {
mTrackId = trackId;
}
diff --git a/src/com/android/tv/util/CompositeComparator.java b/src/com/android/tv/util/CompositeComparator.java
index 47cf50fe..ccf4e944 100644
--- a/src/com/android/tv/util/CompositeComparator.java
+++ b/src/com/android/tv/util/CompositeComparator.java
@@ -18,9 +18,7 @@ package com.android.tv.util;
import java.util.Comparator;
-/**
- * A comparator which runs multiple comparators sequentially.
- */
+/** A comparator which runs multiple comparators sequentially. */
public class CompositeComparator<T> implements Comparator<T> {
private final Comparator<T>[] mComparators;
diff --git a/src/com/android/tv/util/Filter.java b/src/com/android/tv/util/Filter.java
index d5b356e4..3e24a496 100644
--- a/src/com/android/tv/util/Filter.java
+++ b/src/com/android/tv/util/Filter.java
@@ -16,12 +16,8 @@
package com.android.tv.util;
-/**
- * Interface to decide whether an input is filtered out or not.
- */
+/** Interface to decide whether an input is filtered out or not. */
public interface Filter<T> {
- /**
- * Returns true, if {@code input} is acceptable.
- */
+ /** Returns true, if {@code input} is acceptable. */
boolean filter(T input);
}
diff --git a/src/com/android/tv/util/MainThreadExecutor.java b/src/com/android/tv/util/MainThreadExecutor.java
index ce8f8ff3..5102ddbd 100644
--- a/src/com/android/tv/util/MainThreadExecutor.java
+++ b/src/com/android/tv/util/MainThreadExecutor.java
@@ -18,7 +18,6 @@ package com.android.tv.util;
import android.os.Handler;
import android.os.Looper;
-
import java.util.List;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.TimeUnit;
@@ -26,11 +25,11 @@ import java.util.concurrent.TimeUnit;
/**
* An executor service that executes its tasks on the main thread.
*
- * Shutting down this executor is not supported.
+ * <p>Shutting down this executor is not supported.
*/
public class MainThreadExecutor extends AbstractExecutorService {
- private final static MainThreadExecutor INSTANCE = new MainThreadExecutor();
+ private static final MainThreadExecutor INSTANCE = new MainThreadExecutor();
public static MainThreadExecutor getInstance() {
return INSTANCE;
@@ -47,18 +46,14 @@ public class MainThreadExecutor extends AbstractExecutorService {
}
}
- /**
- * Not supported and throws an exception when used.
- */
+ /** Not supported and throws an exception when used. */
@Override
@Deprecated
public void shutdown() {
throw new UnsupportedOperationException();
}
- /**
- * Not supported and throws an exception when used.
- */
+ /** Not supported and throws an exception when used. */
@Override
@Deprecated
public List<Runnable> shutdownNow() {
@@ -75,12 +70,10 @@ public class MainThreadExecutor extends AbstractExecutorService {
return false;
}
- /**
- * Not supported and throws an exception when used.
- */
+ /** Not supported and throws an exception when used. */
@Override
@Deprecated
public boolean awaitTermination(long l, TimeUnit timeUnit) throws InterruptedException {
throw new UnsupportedOperationException();
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/util/MultiLongSparseArray.java b/src/com/android/tv/util/MultiLongSparseArray.java
index 1d5fa80b..a456df91 100644
--- a/src/com/android/tv/util/MultiLongSparseArray.java
+++ b/src/com/android/tv/util/MultiLongSparseArray.java
@@ -19,7 +19,6 @@ package com.android.tv.util;
import android.support.annotation.VisibleForTesting;
import android.util.ArraySet;
import android.util.LongSparseArray;
-
import java.util.Collections;
import java.util.Set;
@@ -29,8 +28,7 @@ import java.util.Set;
* <p>This has the same memory and performance trade offs listed in {@link LongSparseArray}.
*/
public class MultiLongSparseArray<T> {
- @VisibleForTesting
- static final int DEFAULT_MAX_EMPTIES_KEPT = 4;
+ @VisibleForTesting static final int DEFAULT_MAX_EMPTIES_KEPT = 4;
private final LongSparseArray<Set<T>> mSparseArray;
private final Set<T>[] mEmptySets;
private int mEmptyIndex = -1;
@@ -46,9 +44,8 @@ public class MultiLongSparseArray<T> {
}
/**
- * Adds a mapping from the specified key to the specified value,
- * replacing the previous mapping from the specified key if there
- * was one.
+ * Adds a mapping from the specified key to the specified value, replacing the previous mapping
+ * from the specified key if there was one.
*/
public void put(long key, T value) {
Set<T> values = mSparseArray.get(key);
@@ -59,9 +56,7 @@ public class MultiLongSparseArray<T> {
values.add(value);
}
- /**
- * Removes the value at the specified index.
- */
+ /** Removes the value at the specified index. */
public void remove(long key, T value) {
Set<T> values = mSparseArray.get(key);
if (values != null) {
@@ -74,17 +69,15 @@ public class MultiLongSparseArray<T> {
}
/**
- * Gets the set of Objects mapped from the specified key, or an empty set
- * if no such mapping has been made.
+ * Gets the set of Objects mapped from the specified key, or an empty set if no such mapping has
+ * been made.
*/
public Iterable<T> get(long key) {
Set<T> values = mSparseArray.get(key);
return values == null ? Collections.EMPTY_SET : values;
}
- /**
- * Clears cached empty sets.
- */
+ /** Clears cached empty sets. */
public void clearEmptyCache() {
while (mEmptyIndex >= 0) {
mEmptySets[mEmptyIndex--] = null;
diff --git a/src/com/android/tv/util/NetworkUtils.java b/src/com/android/tv/util/NetworkUtils.java
index ed3ce383..94581d5a 100644
--- a/src/com/android/tv/util/NetworkUtils.java
+++ b/src/com/android/tv/util/NetworkUtils.java
@@ -20,21 +20,16 @@ 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.
- */
+/** 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.
- */
+ /** Checks if the internet connection is available. */
public static boolean isNetworkAvailable(@Nullable ConnectivityManager connectivityManager) {
if (connectivityManager == null) {
return false;
@@ -62,5 +57,5 @@ public class NetworkUtils {
return false;
}
- private NetworkUtils() { }
+ private NetworkUtils() {}
}
diff --git a/src/com/android/tv/util/OnboardingUtils.java b/src/com/android/tv/util/OnboardingUtils.java
index 49b02b82..3b72e091 100644
--- a/src/com/android/tv/util/OnboardingUtils.java
+++ b/src/com/android/tv/util/OnboardingUtils.java
@@ -21,9 +21,7 @@ import android.content.Intent;
import android.net.Uri;
import android.preference.PreferenceManager;
-/**
- * A utility class related to onboarding experience.
- */
+/** A utility class related to onboarding experience. */
public final class OnboardingUtils {
private static final String PREF_KEY_IS_FIRST_BOOT = "pref_onbaording_is_first_boot";
private static final String PREF_KEY_ONBOARDING_VERSION_CODE = "pref_onbaording_versionCode";
@@ -31,23 +29,17 @@ public final class OnboardingUtils {
private static final String MERCHANT_COLLECTION_URL_STRING = getMerchantCollectionUrl();
- /**
- * Intent to show merchant collection in online store.
- */
- public static final Intent ONLINE_STORE_INTENT = new Intent(Intent.ACTION_VIEW,
- Uri.parse(MERCHANT_COLLECTION_URL_STRING));
+ /** Intent to show merchant collection in online store. */
+ public static final Intent ONLINE_STORE_INTENT =
+ new Intent(Intent.ACTION_VIEW, Uri.parse(MERCHANT_COLLECTION_URL_STRING));
- /**
- * Checks if this is the first boot after the onboarding experience has been applied.
- */
+ /** Checks if this is the first boot after the onboarding experience has been applied. */
public static boolean isFirstBoot(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean(PREF_KEY_IS_FIRST_BOOT, true);
}
- /**
- * Marks that the first boot has been completed.
- */
+ /** Marks that the first boot has been completed. */
public static void setFirstBootCompleted(Context context) {
PreferenceManager.getDefaultSharedPreferences(context)
.edit()
@@ -56,27 +48,28 @@ public final class OnboardingUtils {
}
/**
- * Checks if this is the first run of {@link com.android.tv.MainActivity} with the
- * current onboarding version.
+ * Checks if this is the first run of {@link com.android.tv.MainActivity} with the current
+ * onboarding version.
*/
public static boolean isFirstRunWithCurrentVersion(Context context) {
- int versionCode = PreferenceManager.getDefaultSharedPreferences(context)
- .getInt(PREF_KEY_ONBOARDING_VERSION_CODE, 0);
+ int versionCode =
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .getInt(PREF_KEY_ONBOARDING_VERSION_CODE, 0);
return versionCode != ONBOARDING_VERSION;
}
/**
- * Marks that the first run of {@link com.android.tv.MainActivity} with the current
- * onboarding version has been completed.
+ * Marks that the first run of {@link com.android.tv.MainActivity} with the current onboarding
+ * version has been completed.
*/
public static void setFirstRunWithCurrentVersionCompleted(Context context) {
- PreferenceManager.getDefaultSharedPreferences(context).edit()
- .putInt(PREF_KEY_ONBOARDING_VERSION_CODE, ONBOARDING_VERSION).apply();
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putInt(PREF_KEY_ONBOARDING_VERSION_CODE, ONBOARDING_VERSION)
+ .apply();
}
- /**
- * Returns merchant collection URL.
- */
+ /** Returns merchant collection URL. */
private static String getMerchantCollectionUrl() {
return "TODO: add a merchant collection url";
}
diff --git a/src/com/android/tv/util/Partner.java b/src/com/android/tv/util/Partner.java
index e3688392..c5e9aad2 100644
--- a/src/com/android/tv/util/Partner.java
+++ b/src/com/android/tv/util/Partner.java
@@ -26,7 +26,6 @@ import android.content.res.Resources;
import android.media.tv.TvInputInfo;
import android.text.TextUtils;
import android.util.Log;
-
import java.util.HashMap;
import java.util.Map;
@@ -42,6 +41,7 @@ public class Partner {
/** ID tags for device input types */
public static final String INPUT_TYPE_BUNDLED_TUNER = "input_type_combined_tuners";
+
public static final String INPUT_TYPE_TUNER = "input_type_tuner";
public static final String INPUT_TYPE_CEC_LOGICAL = "input_type_cec_logical";
public static final String INPUT_TYPE_CEC_RECORDER = "input_type_cec_recorder";
@@ -68,6 +68,7 @@ public class Partner {
private final Resources mResources;
private static final Map<String, Integer> INPUT_TYPE_MAP = new HashMap<>();
+
static {
INPUT_TYPE_MAP.put(INPUT_TYPE_BUNDLED_TUNER, TvInputManagerHelper.TYPE_BUNDLED_TUNER);
INPUT_TYPE_MAP.put(INPUT_TYPE_TUNER, TvInputInfo.TYPE_TUNER);
diff --git a/src/com/android/tv/util/PermissionUtils.java b/src/com/android/tv/util/PermissionUtils.java
deleted file mode 100644
index a355be99..00000000
--- a/src/com/android/tv/util/PermissionUtils.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package com.android.tv.util;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-
-/**
- * Util class to handle permissions.
- */
-public class PermissionUtils {
- /**
- * Permission to read the TV listings.
- */
- public static final String PERMISSION_READ_TV_LISTINGS = "android.permission.READ_TV_LISTINGS";
-
- private static Boolean sHasAccessAllEpgPermission;
- private static Boolean sHasAccessWatchedHistoryPermission;
- private static Boolean sHasModifyParentalControlsPermission;
-
- public static boolean hasAccessAllEpg(Context context) {
- if (sHasAccessAllEpgPermission == null) {
- sHasAccessAllEpgPermission = context.checkSelfPermission(
- "com.android.providers.tv.permission.ACCESS_ALL_EPG_DATA")
- == PackageManager.PERMISSION_GRANTED;
- }
- return sHasAccessAllEpgPermission;
- }
-
- public static boolean hasAccessWatchedHistory(Context context) {
- if (sHasAccessWatchedHistoryPermission == null) {
- sHasAccessWatchedHistoryPermission = context.checkSelfPermission(
- "com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS")
- == PackageManager.PERMISSION_GRANTED;
- }
- return sHasAccessWatchedHistoryPermission;
- }
-
- public static boolean hasModifyParentalControls(Context context) {
- if (sHasModifyParentalControlsPermission == null) {
- sHasModifyParentalControlsPermission = context.checkSelfPermission(
- "android.permission.MODIFY_PARENTAL_CONTROLS")
- == PackageManager.PERMISSION_GRANTED;
- }
- return sHasModifyParentalControlsPermission;
- }
-
- public static boolean hasReadTvListings(Context context) {
- return context.checkSelfPermission(PERMISSION_READ_TV_LISTINGS)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- public static boolean hasInternet(Context context) {
- return context.checkSelfPermission("android.permission.INTERNET")
- == PackageManager.PERMISSION_GRANTED;
- }
-}
diff --git a/src/com/android/tv/util/RecurringRunner.java b/src/com/android/tv/util/RecurringRunner.java
index 8b45131b..764689c2 100644
--- a/src/com/android/tv/util/RecurringRunner.java
+++ b/src/com/android/tv/util/RecurringRunner.java
@@ -22,10 +22,8 @@ import android.os.AsyncTask;
import android.os.Handler;
import android.support.annotation.WorkerThread;
import android.util.Log;
-
-import com.android.tv.common.SharedPreferencesUtils;
import com.android.tv.common.SoftPreconditions;
-
+import com.android.tv.common.util.SharedPreferencesUtils;
import java.util.Date;
/**
@@ -46,8 +44,8 @@ public final class RecurringRunner {
private final String mName;
private boolean mRunning;
- public RecurringRunner(Context context, long intervalMs, Runnable runnable,
- Runnable onStopRunnable) {
+ public RecurringRunner(
+ Context context, long intervalMs, Runnable runnable, Runnable onStopRunnable) {
mContext = context.getApplicationContext();
mRunnable = runnable;
mOnStopRunnable = onStopRunnable;
@@ -99,18 +97,21 @@ public final class RecurringRunner {
// Run it anyways even if it is in the past
if (DEBUG) Log.i(TAG, "Next run of " + mName + " at " + new Date(next));
long delay = Math.max(next - now, 0);
- boolean posted = mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- try {
- if (DEBUG) Log.i(TAG, "Starting " + mName);
- mRunnable.run();
- } catch (Exception e) {
- Log.w(TAG, "Error running " + mName, e);
- }
- postAt(resetNextRunTime());
- }
- }, delay);
+ boolean posted =
+ mHandler.postDelayed(
+ new Runnable() {
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) Log.i(TAG, "Starting " + mName);
+ mRunnable.run();
+ } catch (Exception e) {
+ Log.w(TAG, "Error running " + mName, e);
+ }
+ postAt(resetNextRunTime());
+ }
+ },
+ delay);
if (!posted) {
Log.w(TAG, "Scheduling a future run of " + mName + " at " + new Date(next) + "failed");
}
@@ -118,8 +119,8 @@ public final class RecurringRunner {
}
private SharedPreferences getSharedPreferences() {
- return mContext.getSharedPreferences(SharedPreferencesUtils.SHARED_PREF_RECURRING_RUNNER,
- Context.MODE_PRIVATE);
+ return mContext.getSharedPreferences(
+ SharedPreferencesUtils.SHARED_PREF_RECURRING_RUNNER, Context.MODE_PRIVATE);
}
@WorkerThread
diff --git a/src/com/android/tv/util/SetupUtils.java b/src/com/android/tv/util/SetupUtils.java
index 32e3a81f..0d536320 100644
--- a/src/com/android/tv/util/SetupUtils.java
+++ b/src/com/android/tv/util/SetupUtils.java
@@ -28,24 +28,20 @@ import android.media.tv.TvInputManager;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
+import android.support.annotation.VisibleForTesting;
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.TvSingletons;
+import com.android.tv.common.BaseApplication;
import com.android.tv.common.SoftPreconditions;
-import com.android.tv.data.Channel;
import com.android.tv.data.ChannelDataManager;
-import com.android.tv.tuner.tvinput.TunerTvInputService;
-
+import com.android.tv.data.api.Channel;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
-/**
- * A utility class related to input setup.
- */
+/** A utility class related to input setup. */
public class SetupUtils {
private static final String TAG = "SetupUtils";
private static final boolean DEBUG = false;
@@ -58,9 +54,8 @@ public class SetupUtils {
// Recognized inputs means that the user already knows the inputs are installed.
private static final String PREF_KEY_RECOGNIZED_INPUTS = "recognized_inputs";
private static final String PREF_KEY_IS_FIRST_TUNE = "is_first_tune";
- private static SetupUtils sSetupUtils;
- private final TvApplication mTvApplication;
+ private final Context mContext;
private final SharedPreferences mSharedPreferences;
private final Set<String> mKnownInputs;
private final Set<String> mSetUpInputs;
@@ -68,106 +63,104 @@ public class SetupUtils {
private boolean mIsFirstTune;
private final String mTunerInputId;
- private SetupUtils(TvApplication tvApplication) {
- mTvApplication = tvApplication;
- mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(tvApplication);
+ @VisibleForTesting
+ protected SetupUtils(Context context) {
+ mContext = context;
+ mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
mSetUpInputs = new ArraySet<>();
- mSetUpInputs.addAll(mSharedPreferences.getStringSet(PREF_KEY_SET_UP_INPUTS,
- Collections.emptySet()));
+ mSetUpInputs.addAll(
+ mSharedPreferences.getStringSet(PREF_KEY_SET_UP_INPUTS, Collections.emptySet()));
mKnownInputs = new ArraySet<>();
- mKnownInputs.addAll(mSharedPreferences.getStringSet(PREF_KEY_KNOWN_INPUTS,
- Collections.emptySet()));
+ mKnownInputs.addAll(
+ mSharedPreferences.getStringSet(PREF_KEY_KNOWN_INPUTS, Collections.emptySet()));
mRecognizedInputs = new ArraySet<>();
- mRecognizedInputs.addAll(mSharedPreferences.getStringSet(PREF_KEY_RECOGNIZED_INPUTS,
- mKnownInputs));
+ mRecognizedInputs.addAll(
+ mSharedPreferences.getStringSet(PREF_KEY_RECOGNIZED_INPUTS, mKnownInputs));
mIsFirstTune = mSharedPreferences.getBoolean(PREF_KEY_IS_FIRST_TUNE, true);
- mTunerInputId = TvContract.buildInputId(new ComponentName(tvApplication,
- TunerTvInputService.class));
+ mTunerInputId = BaseApplication.getSingletons(context).getEmbeddedTunerInputId();
}
/**
- * Gets an instance of {@link SetupUtils}.
+ * Creates an instance of {@link SetupUtils}.
+ *
+ * <p><b>WARNING</b> this should only be called by the top level application.
*/
- public static SetupUtils getInstance(Context context) {
- if (sSetupUtils != null) {
- return sSetupUtils;
- }
- sSetupUtils = new SetupUtils((TvApplication) context.getApplicationContext());
- return sSetupUtils;
+ public static SetupUtils createForTvSingletons(Context context) {
+ return new SetupUtils(context.getApplicationContext());
}
- /**
- * Additional work after the setup of TV input.
- */
- public void onTvInputSetupFinished(final String inputId,
- @Nullable final Runnable postRunnable) {
+ /** Additional work after the setup of TV input. */
+ public void onTvInputSetupFinished(
+ final String inputId, @Nullable final Runnable postRunnable) {
// When TIS adds several channels, ChannelDataManager.Listener.onChannelList
// Updated() can be called several times. In this case, it is hard to detect
// which one is the last callback. To reduce error prune, we update channel
// list again and make all channels of {@code inputId} browsable.
onSetupDone(inputId);
- final ChannelDataManager manager = mTvApplication.getChannelDataManager();
+ final ChannelDataManager manager =
+ TvSingletons.getSingletons(mContext).getChannelDataManager();
if (!manager.isDbLoadFinished()) {
- manager.addListener(new ChannelDataManager.Listener() {
- @Override
- public void onLoadFinished() {
- manager.removeListener(this);
- updateChannelsAfterSetup(mTvApplication, inputId, postRunnable);
- }
+ manager.addListener(
+ new ChannelDataManager.Listener() {
+ @Override
+ public void onLoadFinished() {
+ manager.removeListener(this);
+ updateChannelsAfterSetup(mContext, inputId, postRunnable);
+ }
- @Override
- public void onChannelListUpdated() { }
+ @Override
+ public void onChannelListUpdated() {}
- @Override
- public void onChannelBrowsableChanged() { }
- });
+ @Override
+ public void onChannelBrowsableChanged() {}
+ });
} else {
- updateChannelsAfterSetup(mTvApplication, inputId, postRunnable);
+ updateChannelsAfterSetup(mContext, inputId, postRunnable);
}
}
- private static void updateChannelsAfterSetup(Context context, final String inputId,
- final Runnable postRunnable) {
- ApplicationSingletons appSingletons = TvApplication.getSingletons(context);
- final ChannelDataManager manager = appSingletons.getChannelDataManager();
- manager.updateChannels(new Runnable() {
- @Override
- public void run() {
- Channel firstChannelForInput = null;
- boolean browsableChanged = false;
- for (Channel channel : manager.getChannelList()) {
- if (channel.getInputId().equals(inputId)) {
- if (!channel.isBrowsable()) {
- manager.updateBrowsable(channel.getId(), true, true);
- browsableChanged = true;
+ private static void updateChannelsAfterSetup(
+ Context context, final String inputId, final Runnable postRunnable) {
+ TvSingletons tvSingletons = TvSingletons.getSingletons(context);
+ final ChannelDataManager manager = tvSingletons.getChannelDataManager();
+ manager.updateChannels(
+ new Runnable() {
+ @Override
+ public void run() {
+ Channel firstChannelForInput = null;
+ boolean browsableChanged = false;
+ for (Channel channel : manager.getChannelList()) {
+ if (channel.getInputId().equals(inputId)) {
+ if (!channel.isBrowsable()) {
+ manager.updateBrowsable(channel.getId(), true, true);
+ browsableChanged = true;
+ }
+ if (firstChannelForInput == null) {
+ firstChannelForInput = channel;
+ }
+ }
+ }
+ if (firstChannelForInput != null) {
+ Utils.setLastWatchedChannel(context, firstChannelForInput);
}
- if (firstChannelForInput == null) {
- firstChannelForInput = channel;
+ if (browsableChanged) {
+ manager.notifyChannelBrowsableChanged();
+ manager.applyUpdatedValuesToDb();
+ }
+ if (postRunnable != null) {
+ postRunnable.run();
}
}
- }
- if (firstChannelForInput != null) {
- Utils.setLastWatchedChannel(context, firstChannelForInput);
- }
- if (browsableChanged) {
- manager.notifyChannelBrowsableChanged();
- manager.applyUpdatedValuesToDb();
- }
- if (postRunnable != null) {
- postRunnable.run();
- }
- }
- });
+ });
}
- /**
- * Marks the channels in newly installed inputs browsable.
- */
+ /** Marks the channels in newly installed inputs browsable. */
@UiThread
public void markNewChannelsBrowsable() {
Set<String> newInputsWithChannels = new HashSet<>();
- TvInputManagerHelper tvInputManagerHelper = mTvApplication.getTvInputManagerHelper();
- ChannelDataManager channelDataManager = mTvApplication.getChannelDataManager();
+ TvSingletons singletons = TvSingletons.getSingletons(mContext);
+ TvInputManagerHelper tvInputManagerHelper = singletons.getTvInputManagerHelper();
+ ChannelDataManager channelDataManager = singletons.getChannelDataManager();
SoftPreconditions.checkState(channelDataManager.isDbLoadFinished());
for (TvInputInfo input : tvInputManagerHelper.getTvInputInfos(true, true)) {
String inputId = input.getId();
@@ -175,9 +168,13 @@ public class SetupUtils {
onSetupDone(inputId);
newInputsWithChannels.add(inputId);
if (DEBUG) {
- Log.d(TAG, "New input " + inputId + " has "
- + channelDataManager.getChannelCountForInput(inputId)
- + " channels");
+ Log.d(
+ TAG,
+ "New input "
+ + inputId
+ + " has "
+ + channelDataManager.getChannelCountForInput(inputId)
+ + " channels");
}
}
}
@@ -195,9 +192,7 @@ public class SetupUtils {
return mIsFirstTune;
}
- /**
- * Returns true, if the input with {@code inputId} is newly installed.
- */
+ /** Returns true, if the input with {@code inputId} is newly installed. */
public boolean isNewInput(String inputId) {
return !mKnownInputs.contains(inputId);
}
@@ -209,13 +204,14 @@ public class SetupUtils {
public void markAsKnownInput(String inputId) {
mKnownInputs.add(inputId);
mRecognizedInputs.add(inputId);
- mSharedPreferences.edit().putStringSet(PREF_KEY_KNOWN_INPUTS, mKnownInputs)
- .putStringSet(PREF_KEY_RECOGNIZED_INPUTS, mRecognizedInputs).apply();
+ mSharedPreferences
+ .edit()
+ .putStringSet(PREF_KEY_KNOWN_INPUTS, mKnownInputs)
+ .putStringSet(PREF_KEY_RECOGNIZED_INPUTS, mRecognizedInputs)
+ .apply();
}
- /**
- * Returns {@code true}, if {@code inputId}'s setup has been done before.
- */
+ /** Returns {@code true}, if {@code inputId}'s setup has been done before. */
public boolean isSetupDone(String inputId) {
boolean done = mSetUpInputs.contains(inputId);
if (DEBUG) {
@@ -224,9 +220,7 @@ public class SetupUtils {
return done;
}
- /**
- * Returns true, if there is any newly installed input.
- */
+ /** Returns true, if there is any newly installed input. */
public boolean hasNewInput(TvInputManagerHelper inputManager) {
for (TvInputInfo input : inputManager.getTvInputInfos(true, true)) {
if (isNewInput(input.getId())) {
@@ -236,9 +230,7 @@ public class SetupUtils {
return false;
}
- /**
- * Checks whether the given input is already recognized by the user or not.
- */
+ /** Checks whether the given input is already recognized by the user or not. */
private boolean isRecognizedInput(String inputId) {
return mRecognizedInputs.contains(inputId);
}
@@ -251,13 +243,13 @@ public class SetupUtils {
for (TvInputInfo input : inputManager.getTvInputInfos(true, true)) {
mRecognizedInputs.add(input.getId());
}
- mSharedPreferences.edit().putStringSet(PREF_KEY_RECOGNIZED_INPUTS, mRecognizedInputs)
+ mSharedPreferences
+ .edit()
+ .putStringSet(PREF_KEY_RECOGNIZED_INPUTS, mRecognizedInputs)
.apply();
}
- /**
- * Checks whether there are any unrecognized inputs.
- */
+ /** Checks whether there are any unrecognized inputs. */
public boolean hasUnrecognizedInput(TvInputManagerHelper inputManager) {
for (TvInputInfo input : inputManager.getTvInputInfos(true, true)) {
if (!isRecognizedInput(input.getId())) {
@@ -276,8 +268,8 @@ public class SetupUtils {
// 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.<String>emptySet())) {
+ for (String input :
+ sp.getStringSet(PREF_KEY_SET_UP_INPUTS, Collections.<String>emptySet())) {
if (!TextUtils.isEmpty(input)) {
ComponentName componentName = ComponentName.unflattenFromString(input);
if (componentName != null) {
@@ -299,23 +291,28 @@ public class SetupUtils {
*/
public static void grantEpgPermission(Context context, String packageName) {
if (DEBUG) {
- Log.d(TAG, "grantEpgPermission(context=" + context + ", packageName=" + packageName
- + ")");
+ Log.d(
+ TAG,
+ "grantEpgPermission(context=" + context + ", packageName=" + packageName + ")");
}
try {
- int modeFlags = Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION;
+ int modeFlags =
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION;
context.grantUriPermission(packageName, TvContract.Channels.CONTENT_URI, modeFlags);
context.grantUriPermission(packageName, TvContract.Programs.CONTENT_URI, modeFlags);
} catch (SecurityException e) {
- Log.e(TAG, "Either TvProvider does not allow granting of Uri permissions or the app"
- + " does not have permission.", e);
+ Log.e(
+ TAG,
+ "Either TvProvider does not allow granting of Uri permissions or the app"
+ + " does not have permission.",
+ e);
}
}
/**
- * Called when Live channels app is launched. Once it is called, {@link
- * #isFirstTune} will return false.
+ * Called when Live channels app is launched. Once it is called, {@link #isFirstTune} will
+ * return false.
*/
public void onTuned() {
if (!mIsFirstTune) {
@@ -325,9 +322,7 @@ public class SetupUtils {
mSharedPreferences.edit().putBoolean(PREF_KEY_IS_FIRST_TUNE, false).apply();
}
- /**
- * Called when input list is changed. It mainly handles input removals.
- */
+ /** Called when input list is changed. It mainly handles input removals. */
public void onInputListUpdated(TvInputManager manager) {
// mRecognizedInputs > mKnownInputs > mSetUpInputs.
Set<String> removedInputList = new HashSet<>(mRecognizedInputs);
@@ -345,9 +340,10 @@ public class SetupUtils {
try {
// Just after booting, input list from TvInputManager are not reliable.
// So we need to double-check package existence. b/29034900
- mTvApplication.getPackageManager().getPackageInfo(
- ComponentName.unflattenFromString(input)
- .getPackageName(), PackageManager.GET_ACTIVITIES);
+ mContext.getPackageManager()
+ .getPackageInfo(
+ ComponentName.unflattenFromString(input).getPackageName(),
+ PackageManager.GET_ACTIVITIES);
Log.i(TAG, "TV input (" + input + ") is removed but package is not deleted");
} catch (NameNotFoundException e) {
Log.i(TAG, "TV input (" + input + ") and its package are removed");
@@ -358,9 +354,12 @@ public class SetupUtils {
}
}
if (inputPackageDeleted) {
- mSharedPreferences.edit().putStringSet(PREF_KEY_SET_UP_INPUTS, mSetUpInputs)
+ mSharedPreferences
+ .edit()
+ .putStringSet(PREF_KEY_SET_UP_INPUTS, mSetUpInputs)
.putStringSet(PREF_KEY_KNOWN_INPUTS, mKnownInputs)
- .putStringSet(PREF_KEY_RECOGNIZED_INPUTS, mRecognizedInputs).apply();
+ .putStringSet(PREF_KEY_RECOGNIZED_INPUTS, mRecognizedInputs)
+ .apply();
}
}
}
@@ -375,7 +374,9 @@ 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, mRecognizedInputs)
+ mSharedPreferences
+ .edit()
+ .putStringSet(PREF_KEY_RECOGNIZED_INPUTS, mRecognizedInputs)
.apply();
}
if (!mKnownInputs.contains(inputId)) {
@@ -388,4 +389,4 @@ public class SetupUtils {
mSharedPreferences.edit().putStringSet(PREF_KEY_SET_UP_INPUTS, mSetUpInputs).apply();
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/util/SqlParams.java b/src/com/android/tv/util/SqlParams.java
new file mode 100644
index 00000000..c4b803b6
--- /dev/null
+++ b/src/com/android/tv/util/SqlParams.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 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.database.DatabaseUtils;
+import java.util.Arrays;
+
+/** Convenience class for SQL operations. */
+public class SqlParams {
+ private String mTables;
+ private String mSelection;
+ private String[] mSelectionArgs;
+
+ public SqlParams(String tables, String selection, String... selectionArgs) {
+ setTables(tables);
+ setWhere(selection, selectionArgs);
+ }
+
+ public String getTables() {
+ return mTables;
+ }
+
+ public String getSelection() {
+ return mSelection;
+ }
+
+ public String[] getSelectionArgs() {
+ return mSelectionArgs;
+ }
+
+ public void setTables(String tables) {
+ mTables = tables;
+ }
+
+ public void setWhere(String selection, String... selectionArgs) {
+ mSelection = selection;
+ mSelectionArgs = selectionArgs;
+ }
+
+ public void appendWhere(String selection, String... selectionArgs) {
+ mSelection = DatabaseUtils.concatenateWhere(mSelection, selection);
+ if (selectionArgs != null) {
+ mSelectionArgs = DatabaseUtils.appendSelectionArgs(mSelectionArgs, selectionArgs);
+ }
+ }
+
+ public void appendWhereEquals(String name, String value) {
+ appendWhere(name + "=?", value);
+ }
+
+ @Override
+ public String toString() {
+ return "tables "
+ + getTables()
+ + " where "
+ + getSelection()
+ + " with "
+ + Arrays.toString(getSelectionArgs());
+ }
+}
diff --git a/src/com/android/tv/util/SystemProperties.java b/src/com/android/tv/util/SystemProperties.java
deleted file mode 100644
index e737f233..00000000
--- a/src/com/android/tv/util/SystemProperties.java
+++ /dev/null
@@ -1,68 +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.util;
-
-import com.android.tv.common.BooleanSystemProperty;
-
-/**
- * A convenience class for getting TV related system properties.
- */
-public final class SystemProperties {
-
- /**
- * Allow Google Analytics for eng builds.
- */
- public static final BooleanSystemProperty ALLOW_ANALYTICS_IN_ENG = new BooleanSystemProperty(
- "tv_allow_analytics_in_eng", false);
-
- /**
- * Allow Strict mode for debug builds.
- */
- public static final BooleanSystemProperty ALLOW_STRICT_MODE = new BooleanSystemProperty(
- "tv_allow_strict_mode", true);
-
- /**
- * When true {@link android.view.KeyEvent}s are logged. Defaults to false.
- */
- public static final BooleanSystemProperty LOG_KEYEVENT = new BooleanSystemProperty(
- "tv_log_keyevent", false);
- /**
- * When true debug keys are used. Defaults to false.
- */
- public static final BooleanSystemProperty USE_DEBUG_KEYS = new BooleanSystemProperty(
- "tv_use_debug_keys", false);
-
- /**
- * Send {@link com.android.tv.analytics.Tracker} information. Defaults to {@code true}.
- */
- public static final BooleanSystemProperty USE_TRACKER = new BooleanSystemProperty(
- "tv_use_tracker", true);
-
- static {
- updateSystemProperties();
- }
-
- private SystemProperties() {
- }
-
- /**
- * Update the TV related system properties.
- */
- public static void updateSystemProperties() {
- BooleanSystemProperty.resetAll();
- }
-}
diff --git a/src/com/android/tv/util/TimeShiftUtils.java b/src/com/android/tv/util/TimeShiftUtils.java
index 8038a78f..977f3333 100644
--- a/src/com/android/tv/util/TimeShiftUtils.java
+++ b/src/com/android/tv/util/TimeShiftUtils.java
@@ -18,9 +18,7 @@ package com.android.tv.util;
import java.util.concurrent.TimeUnit;
-/**
- * A class that includes convenience methods for time shift plays.
- */
+/** A class that includes convenience methods for time shift plays. */
public class TimeShiftUtils {
private static final String TAG = "TimeShiftUtils";
private static final boolean DEBUG = false;
@@ -30,8 +28,8 @@ public class TimeShiftUtils {
private static final int[] LONG_PROGRAM_SPEED_FACTORS = new int[] {2, 8, 32, 128};
/**
- * The maximum play speed level support by time shift play. In other words, the valid
- * speed levels are ranged from 0 to MAX_SPEED_LEVEL (included).
+ * The maximum play speed level support by time shift play. In other words, the valid speed
+ * levels are ranged from 0 to MAX_SPEED_LEVEL (included).
*/
public static final int MAX_SPEED_LEVEL = SHORT_PROGRAM_SPEED_FACTORS.length - 1;
@@ -45,17 +43,19 @@ public class TimeShiftUtils {
*/
public static int getPlaybackSpeed(int speedLevel, long programDurationMillis)
throws IndexOutOfBoundsException {
- return (programDurationMillis > SHORT_PROGRAM_THRESHOLD_MILLIS) ?
- LONG_PROGRAM_SPEED_FACTORS[speedLevel] : SHORT_PROGRAM_SPEED_FACTORS[speedLevel];
+ return (programDurationMillis > SHORT_PROGRAM_THRESHOLD_MILLIS)
+ ? LONG_PROGRAM_SPEED_FACTORS[speedLevel]
+ : SHORT_PROGRAM_SPEED_FACTORS[speedLevel];
}
/**
* Returns the maxium possible play speed according to the program's length.
+ *
* @param programDurationMillis the length of program under playing.
*/
public static int getMaxPlaybackSpeed(long programDurationMillis) {
- return (programDurationMillis > SHORT_PROGRAM_THRESHOLD_MILLIS) ?
- LONG_PROGRAM_SPEED_FACTORS[MAX_SPEED_LEVEL]
+ return (programDurationMillis > SHORT_PROGRAM_THRESHOLD_MILLIS)
+ ? LONG_PROGRAM_SPEED_FACTORS[MAX_SPEED_LEVEL]
: SHORT_PROGRAM_SPEED_FACTORS[MAX_SPEED_LEVEL];
}
}
diff --git a/src/com/android/tv/util/ToastUtils.java b/src/com/android/tv/util/ToastUtils.java
index 34346b2a..a25653f6 100644
--- a/src/com/android/tv/util/ToastUtils.java
+++ b/src/com/android/tv/util/ToastUtils.java
@@ -19,18 +19,13 @@ package com.android.tv.util;
import android.content.Context;
import android.support.annotation.MainThread;
import android.widget.Toast;
-
import java.lang.ref.WeakReference;
-/**
- * A utility class for the toast message.
- */
+/** A utility class for the toast message. */
public class ToastUtils {
private static WeakReference<Toast> sToast;
- /**
- * Shows the toast message after canceling the previous one.
- */
+ /** Shows the toast message after canceling the previous one. */
@MainThread
public static void show(Context context, CharSequence text, int duration) {
if (sToast != null && sToast.get() != null) {
diff --git a/src/com/android/tv/util/TvInputManagerHelper.java b/src/com/android/tv/util/TvInputManagerHelper.java
index 730a985b..625fb7b2 100644
--- a/src/com/android/tv/util/TvInputManagerHelper.java
+++ b/src/com/android/tv/util/TvInputManagerHelper.java
@@ -21,21 +21,23 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.hardware.hdmi.HdmiDeviceInfo;
+import android.media.tv.TvContentRatingSystemInfo;
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager;
import android.media.tv.TvInputManager.TvInputCallback;
import android.os.Handler;
+import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
-
-import com.android.tv.Features;
+import com.android.tv.TvFeatures;
import com.android.tv.common.SoftPreconditions;
-import com.android.tv.common.TvCommonUtils;
+import com.android.tv.common.util.CommonUtils;
import com.android.tv.parental.ContentRatingsManager;
import com.android.tv.parental.ParentalControlSettings;
-
+import com.android.tv.util.images.ImageCache;
+import com.android.tv.util.images.ImageLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -49,10 +51,61 @@ public class TvInputManagerHelper {
private static final String TAG = "TvInputManagerHelper";
private static final boolean DEBUG = false;
- /**
- * Types of HDMI device and bundled tuner.
- */
+ public interface TvInputManagerInterface {
+ TvInputInfo getTvInputInfo(String inputId);
+
+ Integer getInputState(String inputId);
+
+ void registerCallback(TvInputCallback internalCallback, Handler handler);
+
+ void unregisterCallback(TvInputCallback internalCallback);
+
+ List<TvInputInfo> getTvInputList();
+
+ List<TvContentRatingSystemInfo> getTvContentRatingSystemList();
+ }
+
+ private static final class TvInputManagerImpl implements TvInputManagerInterface {
+ private final TvInputManager delegate;
+
+ private TvInputManagerImpl(TvInputManager delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public TvInputInfo getTvInputInfo(String inputId) {
+ return delegate.getTvInputInfo(inputId);
+ }
+
+ @Override
+ public Integer getInputState(String inputId) {
+ return delegate.getInputState(inputId);
+ }
+
+ @Override
+ public void registerCallback(TvInputCallback internalCallback, Handler handler) {
+ delegate.registerCallback(internalCallback, handler);
+ }
+
+ @Override
+ public void unregisterCallback(TvInputCallback internalCallback) {
+ delegate.unregisterCallback(internalCallback);
+ }
+
+ @Override
+ public List<TvInputInfo> getTvInputList() {
+ return delegate.getTvInputList();
+ }
+
+ @Override
+ public List<TvContentRatingSystemInfo> getTvContentRatingSystemList() {
+ return delegate.getTvContentRatingSystemList();
+ }
+ }
+
+ /** Types of HDMI device and bundled tuner. */
public static final int TYPE_CEC_DEVICE = -2;
+
public static final int TYPE_BUNDLED_TUNER = -3;
public static final int TYPE_CEC_DEVICE_RECORDER = -4;
public static final int TYPE_CEC_DEVICE_PLAYBACK = -5;
@@ -60,14 +113,13 @@ public class TvInputManagerHelper {
private static final String PERMISSION_ACCESS_ALL_EPG_DATA =
"com.android.providers.tv.permission.ACCESS_ALL_EPG_DATA";
- private static final String [] mPhysicalTunerBlackList = {
+ private static final String[] mPhysicalTunerBlackList = {
};
private static final String META_LABEL_SORT_KEY = "input_sort_key";
- /**
- * The default tv input priority to show.
- */
+ /** The default tv input priority to show. */
private static final ArrayList<Integer> DEFAULT_TV_INPUT_PRIORITY = new ArrayList<>();
+
static {
DEFAULT_TV_INPUT_PRIORITY.add(TYPE_BUNDLED_TUNER);
DEFAULT_TV_INPUT_PRIORITY.add(TvInputInfo.TYPE_TUNER);
@@ -90,12 +142,12 @@ public class TvInputManagerHelper {
};
private static final String[] TESTABLE_INPUTS = {
- "com.android.tv.testinput/.TestTvInputService"
+ "com.android.tv.testinput/.TestTvInputService"
};
private final Context mContext;
private final PackageManager mPackageManager;
- private final TvInputManager mTvInputManager;
+ protected final TvInputManagerInterface mTvInputManager;
private final Map<String, Integer> mInputStateMap = new HashMap<>();
private final Map<String, TvInputInfo> mInputMap = new HashMap<>();
private final Map<String, String> mTvInputLabels = new ArrayMap<>();
@@ -106,100 +158,105 @@ public class TvInputManagerHelper {
private final Map<String, Drawable> mTvInputApplicationIcons = new ArrayMap<>();
private final Map<String, Drawable> mTvInputAppliactionBanners = new ArrayMap<>();
- private final TvInputCallback mInternalCallback = new TvInputCallback() {
- @Override
- public void onInputStateChanged(String inputId, int state) {
- if (DEBUG) Log.d(TAG, "onInputStateChanged " + inputId + " state=" + state);
- if (isInBlackList(inputId)) {
- return;
- }
- mInputStateMap.put(inputId, state);
- for (TvInputCallback callback : mCallbacks) {
- callback.onInputStateChanged(inputId, state);
- }
- }
+ private final TvInputCallback mInternalCallback =
+ new TvInputCallback() {
+ @Override
+ public void onInputStateChanged(String inputId, int state) {
+ if (DEBUG) Log.d(TAG, "onInputStateChanged " + inputId + " state=" + state);
+ if (isInBlackList(inputId)) {
+ return;
+ }
+ mInputStateMap.put(inputId, state);
+ for (TvInputCallback callback : mCallbacks) {
+ callback.onInputStateChanged(inputId, state);
+ }
+ }
- @Override
- public void onInputAdded(String inputId) {
- if (DEBUG) Log.d(TAG, "onInputAdded " + inputId);
- if (isInBlackList(inputId)) {
- return;
- }
- TvInputInfo info = mTvInputManager.getTvInputInfo(inputId);
- if (info != null) {
- mInputMap.put(inputId, info);
- mTvInputLabels.put(inputId, info.loadLabel(mContext).toString());
- CharSequence inputCustomLabel = info.loadCustomLabel(mContext);
- if (inputCustomLabel != null) {
- mTvInputCustomLabels.put(inputId, inputCustomLabel.toString());
+ @Override
+ public void onInputAdded(String inputId) {
+ if (DEBUG) Log.d(TAG, "onInputAdded " + inputId);
+ if (isInBlackList(inputId)) {
+ return;
+ }
+ TvInputInfo info = mTvInputManager.getTvInputInfo(inputId);
+ if (info != null) {
+ mInputMap.put(inputId, info);
+ CharSequence label = info.loadLabel(mContext);
+ // in tests the label may be missing just use the input id
+ mTvInputLabels.put(inputId, label != null ? label.toString() : inputId);
+ CharSequence inputCustomLabel = info.loadCustomLabel(mContext);
+ if (inputCustomLabel != null) {
+ mTvInputCustomLabels.put(inputId, inputCustomLabel.toString());
+ }
+ mInputStateMap.put(inputId, mTvInputManager.getInputState(inputId));
+ mInputIdToPartnerInputMap.put(inputId, isPartnerInput(info));
+ }
+ mContentRatingsManager.update();
+ for (TvInputCallback callback : mCallbacks) {
+ callback.onInputAdded(inputId);
+ }
}
- mInputStateMap.put(inputId, mTvInputManager.getInputState(inputId));
- mInputIdToPartnerInputMap.put(inputId, isPartnerInput(info));
- }
- mContentRatingsManager.update();
- for (TvInputCallback callback : mCallbacks) {
- callback.onInputAdded(inputId);
- }
- }
- @Override
- public void onInputRemoved(String inputId) {
- if (DEBUG) Log.d(TAG, "onInputRemoved " + inputId);
- mInputMap.remove(inputId);
- mTvInputLabels.remove(inputId);
- mTvInputCustomLabels.remove(inputId);
- mTvInputApplicationLabels.remove(inputId);
- mTvInputApplicationIcons.remove(inputId);
- mTvInputAppliactionBanners.remove(inputId);
- mInputStateMap.remove(inputId);
- mInputIdToPartnerInputMap.remove(inputId);
- mContentRatingsManager.update();
- for (TvInputCallback callback : mCallbacks) {
- callback.onInputRemoved(inputId);
- }
- ImageCache.getInstance().remove(ImageLoader.LoadTvInputLogoTask.getTvInputLogoKey(
- inputId));
- }
+ @Override
+ public void onInputRemoved(String inputId) {
+ if (DEBUG) Log.d(TAG, "onInputRemoved " + inputId);
+ mInputMap.remove(inputId);
+ mTvInputLabels.remove(inputId);
+ mTvInputCustomLabels.remove(inputId);
+ mTvInputApplicationLabels.remove(inputId);
+ mTvInputApplicationIcons.remove(inputId);
+ mTvInputAppliactionBanners.remove(inputId);
+ mInputStateMap.remove(inputId);
+ mInputIdToPartnerInputMap.remove(inputId);
+ mContentRatingsManager.update();
+ for (TvInputCallback callback : mCallbacks) {
+ callback.onInputRemoved(inputId);
+ }
+ ImageCache.getInstance()
+ .remove(ImageLoader.LoadTvInputLogoTask.getTvInputLogoKey(inputId));
+ }
- @Override
- public void onInputUpdated(String inputId) {
- if (DEBUG) Log.d(TAG, "onInputUpdated " + inputId);
- if (isInBlackList(inputId)) {
- return;
- }
- TvInputInfo info = mTvInputManager.getTvInputInfo(inputId);
- mInputMap.put(inputId, info);
- mTvInputLabels.put(inputId, info.loadLabel(mContext).toString());
- CharSequence inputCustomLabel = info.loadCustomLabel(mContext);
- if (inputCustomLabel != null) {
- mTvInputCustomLabels.put(inputId, inputCustomLabel.toString());
- }
- mTvInputApplicationLabels.remove(inputId);
- mTvInputApplicationIcons.remove(inputId);
- mTvInputAppliactionBanners.remove(inputId);
- for (TvInputCallback callback : mCallbacks) {
- callback.onInputUpdated(inputId);
- }
- ImageCache.getInstance().remove(ImageLoader.LoadTvInputLogoTask.getTvInputLogoKey(
- inputId));
- }
+ @Override
+ public void onInputUpdated(String inputId) {
+ if (DEBUG) Log.d(TAG, "onInputUpdated " + inputId);
+ if (isInBlackList(inputId)) {
+ return;
+ }
+ TvInputInfo info = mTvInputManager.getTvInputInfo(inputId);
+ mInputMap.put(inputId, info);
+ mTvInputLabels.put(inputId, info.loadLabel(mContext).toString());
+ CharSequence inputCustomLabel = info.loadCustomLabel(mContext);
+ if (inputCustomLabel != null) {
+ mTvInputCustomLabels.put(inputId, inputCustomLabel.toString());
+ }
+ mTvInputApplicationLabels.remove(inputId);
+ mTvInputApplicationIcons.remove(inputId);
+ mTvInputAppliactionBanners.remove(inputId);
+ for (TvInputCallback callback : mCallbacks) {
+ callback.onInputUpdated(inputId);
+ }
+ ImageCache.getInstance()
+ .remove(ImageLoader.LoadTvInputLogoTask.getTvInputLogoKey(inputId));
+ }
- @Override
- public void onTvInputInfoUpdated(TvInputInfo inputInfo) {
- if (DEBUG) Log.d(TAG, "onTvInputInfoUpdated " + inputInfo);
- mInputMap.put(inputInfo.getId(), inputInfo);
- mTvInputLabels.put(inputInfo.getId(), inputInfo.loadLabel(mContext).toString());
- CharSequence inputCustomLabel = inputInfo.loadCustomLabel(mContext);
- if (inputCustomLabel != null) {
- mTvInputCustomLabels.put(inputInfo.getId(), inputCustomLabel.toString());
- }
- for (TvInputCallback callback : mCallbacks) {
- callback.onTvInputInfoUpdated(inputInfo);
- }
- ImageCache.getInstance().remove(ImageLoader.LoadTvInputLogoTask.getTvInputLogoKey(
- inputInfo.getId()));
- }
- };
+ @Override
+ public void onTvInputInfoUpdated(TvInputInfo inputInfo) {
+ if (DEBUG) Log.d(TAG, "onTvInputInfoUpdated " + inputInfo);
+ mInputMap.put(inputInfo.getId(), inputInfo);
+ mTvInputLabels.put(inputInfo.getId(), inputInfo.loadLabel(mContext).toString());
+ CharSequence inputCustomLabel = inputInfo.loadCustomLabel(mContext);
+ if (inputCustomLabel != null) {
+ mTvInputCustomLabels.put(inputInfo.getId(), inputCustomLabel.toString());
+ }
+ for (TvInputCallback callback : mCallbacks) {
+ callback.onTvInputInfoUpdated(inputInfo);
+ }
+ ImageCache.getInstance()
+ .remove(
+ ImageLoader.LoadTvInputLogoTask.getTvInputLogoKey(
+ inputInfo.getId()));
+ }
+ };
private final Handler mHandler = new Handler();
private boolean mStarted;
@@ -209,10 +266,23 @@ public class TvInputManagerHelper {
private final Comparator<TvInputInfo> mTvInputInfoComparator;
public TvInputManagerHelper(Context context) {
+ this(context, createTvInputManagerWrapper(context));
+ }
+
+ @Nullable
+ protected static TvInputManagerImpl createTvInputManagerWrapper(Context context) {
+ TvInputManager tvInputManager =
+ (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE);
+ return tvInputManager == null ? null : new TvInputManagerImpl(tvInputManager);
+ }
+
+ @VisibleForTesting
+ protected TvInputManagerHelper(
+ Context context, @Nullable TvInputManagerInterface tvInputManager) {
mContext = context.getApplicationContext();
mPackageManager = context.getPackageManager();
- mTvInputManager = (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE);
- mContentRatingsManager = new ContentRatingsManager(context);
+ mTvInputManager = tvInputManager;
+ mContentRatingsManager = new ContentRatingsManager(context, tvInputManager);
mParentalControlSettings = new ParentalControlSettings(context);
mTvInputInfoComparator = new InputComparatorInternal(this);
}
@@ -247,7 +317,9 @@ public class TvInputManagerHelper {
mInputStateMap.put(inputId, state);
mInputIdToPartnerInputMap.put(inputId, isPartnerInput(input));
}
- SoftPreconditions.checkState(mInputStateMap.size() == mInputMap.size(), TAG,
+ SoftPreconditions.checkState(
+ mInputStateMap.size() == mInputMap.size(),
+ TAG,
"mInputStateMap not the same size as mInputMap");
mContentRatingsManager.update();
}
@@ -264,13 +336,12 @@ public class TvInputManagerHelper {
mTvInputCustomLabels.clear();
mTvInputApplicationLabels.clear();
mTvInputApplicationIcons.clear();
- mTvInputAppliactionBanners.clear();;
+ mTvInputAppliactionBanners.clear();
+ ;
mInputIdToPartnerInputMap.clear();
}
- /**
- * Clears the TvInput labels map.
- */
+ /** Clears the TvInput labels map. */
public void clearTvInputLabels() {
mTvInputLabels.clear();
mTvInputCustomLabels.clear();
@@ -294,8 +365,8 @@ public class TvInputManagerHelper {
}
/**
- * Returns the default comparator for {@link TvInputInfo}.
- * See {@link InputComparatorInternal} for detail.
+ * Returns the default comparator for {@link TvInputInfo}. See {@link InputComparatorInternal}
+ * for detail.
*/
public Comparator<TvInputInfo> getDefaultTvInputInfoComparator() {
return mTvInputInfoComparator;
@@ -304,35 +375,31 @@ public class TvInputManagerHelper {
/**
* Checks if the input is from a partner.
*
- * It's visible for comparator test.
- * Package private is enough for this method, but public is necessary to workaround mockito
- * bug.
+ * <p>It's visible for comparator test. Package private is enough for this method, but public is
+ * necessary to workaround mockito bug.
*/
@VisibleForTesting
public boolean isPartnerInput(TvInputInfo inputInfo) {
return isSystemInput(inputInfo) && !isBundledInput(inputInfo);
}
- /**
- * Does the input have {@link ApplicationInfo#FLAG_SYSTEM} set.
- */
+ /** Does the input have {@link ApplicationInfo#FLAG_SYSTEM} set. */
public boolean isSystemInput(TvInputInfo inputInfo) {
return inputInfo != null
- && (inputInfo.getServiceInfo().applicationInfo.flags
- & ApplicationInfo.FLAG_SYSTEM) != 0;
+ && (inputInfo.getServiceInfo().applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
+ != 0;
}
- /**
- * Is the input one known bundled inputs not written by OEM/SOCs.
- */
+ /** Is the input one known bundled inputs not written by OEM/SOCs. */
public boolean isBundledInput(TvInputInfo inputInfo) {
- return inputInfo != null && Utils.isInBundledPackageSet(inputInfo.getServiceInfo()
- .applicationInfo.packageName);
+ return inputInfo != null
+ && CommonUtils.isInBundledPackageSet(
+ inputInfo.getServiceInfo().applicationInfo.packageName);
}
/**
- * Returns if the given input is bundled and written by OEM/SOCs.
- * This returns the cached result.
+ * Returns if the given input is bundled and written by OEM/SOCs. This returns the cached
+ * result.
*/
public boolean isPartnerInput(String inputId) {
Boolean isPartnerInput = mInputIdToPartnerInputMap.get(inputId);
@@ -348,9 +415,7 @@ public class TvInputManagerHelper {
return mTvInputManager != null;
}
- /**
- * Loads label of {@code info}.
- */
+ /** Loads label of {@code info}. */
public String loadLabel(TvInputInfo info) {
String label = mTvInputLabels.get(info.getId());
if (label == null) {
@@ -360,9 +425,7 @@ public class TvInputManagerHelper {
return label;
}
- /**
- * Loads custom label of {@code info}
- */
+ /** Loads custom label of {@code info} */
public String loadCustomLabel(TvInputInfo info) {
String customLabel = mTvInputCustomLabels.get(info.getId());
if (customLabel == null) {
@@ -375,60 +438,46 @@ public class TvInputManagerHelper {
return customLabel;
}
- /**
- * Gets the tv input application's label.
- */
+ /** Gets the tv input application's label. */
public CharSequence getTvInputApplicationLabel(CharSequence inputId) {
return mTvInputApplicationLabels.get(inputId);
}
- /**
- * Stores the tv input application's label.
- */
+ /** Stores the tv input application's label. */
public void setTvInputApplicationLabel(String inputId, CharSequence label) {
mTvInputApplicationLabels.put(inputId, label);
}
- /**
- * Gets the tv input application's icon.
- */
+ /** Gets the tv input application's icon. */
public Drawable getTvInputApplicationIcon(String inputId) {
return mTvInputApplicationIcons.get(inputId);
}
- /**
- * Stores the tv input application's icon.
- */
+ /** Stores the tv input application's icon. */
public void setTvInputApplicationIcon(String inputId, Drawable icon) {
mTvInputApplicationIcons.put(inputId, icon);
}
- /**
- * Gets the tv input application's banner.
- */
+ /** Gets the tv input application's banner. */
public Drawable getTvInputApplicationBanner(String inputId) {
return mTvInputAppliactionBanners.get(inputId);
}
- /**
- * Stores the tv input application's banner.
- */
+ /** Stores the tv input application's banner. */
public void setTvInputApplicationBanner(String inputId, Drawable banner) {
mTvInputAppliactionBanners.put(inputId, banner);
}
- /**
- * Returns if TV input exists with the input id.
- */
+ /** Returns if TV input exists with the input id. */
public boolean hasTvInputInfo(String inputId) {
- SoftPreconditions.checkState(mStarted, TAG,
- "hasTvInputInfo() called before TvInputManagerHelper was started.");
+ SoftPreconditions.checkState(
+ mStarted, TAG, "hasTvInputInfo() called before TvInputManagerHelper was started.");
return mStarted && !TextUtils.isEmpty(inputId) && mInputMap.get(inputId) != null;
}
public TvInputInfo getTvInputInfo(String inputId) {
- SoftPreconditions.checkState(mStarted, TAG,
- "getTvInputInfo() called before TvInputManagerHelper was started.");
+ SoftPreconditions.checkState(
+ mStarted, TAG, "getTvInputInfo() called before TvInputManagerHelper was started.");
if (!mStarted) {
return null;
}
@@ -452,16 +501,23 @@ public class TvInputManagerHelper {
}
return size;
}
-
- public int getInputState(TvInputInfo inputInfo) {
- return getInputState(inputInfo.getId());
+ /**
+ * Returns TvInputInfo's input state.
+ *
+ * @param inputInfo
+ * @return An Integer which stands for the input state {@link
+ * TvInputManager.INPUT_STATE_DISCONNECTED} if inputInfo is null
+ */
+ public int getInputState(@Nullable TvInputInfo inputInfo) {
+ return inputInfo == null
+ ? TvInputManager.INPUT_STATE_DISCONNECTED
+ : getInputState(inputInfo.getId());
}
public int getInputState(String inputId) {
SoftPreconditions.checkState(mStarted, TAG, "AvailabilityManager not started");
if (!mStarted) {
return TvInputManager.INPUT_STATE_DISCONNECTED;
-
}
Integer state = mInputStateMap.get(inputId);
if (state == null) {
@@ -483,16 +539,13 @@ public class TvInputManagerHelper {
return mParentalControlSettings;
}
- /**
- * Returns a ContentRatingsManager instance for a given application context.
- */
+ /** Returns a ContentRatingsManager instance for a given application context. */
public ContentRatingsManager getContentRatingsManager() {
return mContentRatingsManager;
}
private int getInputSortKey(TvInputInfo input) {
- return input.getServiceInfo().metaData.getInt(META_LABEL_SORT_KEY,
- Integer.MAX_VALUE);
+ return input.getServiceInfo().metaData.getInt(META_LABEL_SORT_KEY, Integer.MAX_VALUE);
}
private boolean isInputPhysicalTuner(TvInputInfo input) {
@@ -504,15 +557,20 @@ public class TvInputManagerHelper {
if (input.createSetupIntent() == null) {
return false;
} else {
- boolean mayBeTunerInput = mPackageManager.checkPermission(
- PERMISSION_ACCESS_ALL_EPG_DATA, input.getServiceInfo().packageName)
- == PackageManager.PERMISSION_GRANTED;
+ boolean mayBeTunerInput =
+ mPackageManager.checkPermission(
+ PERMISSION_ACCESS_ALL_EPG_DATA,
+ input.getServiceInfo().packageName)
+ == PackageManager.PERMISSION_GRANTED;
if (!mayBeTunerInput) {
try {
- ApplicationInfo ai = mPackageManager.getApplicationInfo(
- input.getServiceInfo().packageName, 0);
- if ((ai.flags & (ApplicationInfo.FLAG_SYSTEM
- | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) == 0) {
+ ApplicationInfo ai =
+ mPackageManager.getApplicationInfo(
+ input.getServiceInfo().packageName, 0);
+ if ((ai.flags
+ & (ApplicationInfo.FLAG_SYSTEM
+ | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP))
+ == 0) {
return false;
}
} catch (PackageManager.NameNotFoundException e) {
@@ -524,14 +582,15 @@ public class TvInputManagerHelper {
}
private boolean isInBlackList(String inputId) {
- if (Features.USE_PARTNER_INPUT_BLACKLIST.isEnabled(mContext)) {
+ if (TvFeatures.USE_PARTNER_INPUT_BLACKLIST.isEnabled(mContext)) {
for (String disabledTunerInputPrefix : PARTNER_TUNER_INPUT_PREFIX_BLACKLIST) {
if (inputId.contains(disabledTunerInputPrefix)) {
return true;
}
}
}
- if (TvCommonUtils.isRunningInTest()) {
+ if (CommonUtils.isRoboTest()) return false;
+ if (CommonUtils.isRunningInTest()) {
for (String testableInput : TESTABLE_INPUTS) {
if (testableInput.equals(inputId)) {
return false;
@@ -545,10 +604,9 @@ public class TvInputManagerHelper {
/**
* Default comparator for TvInputInfo.
*
- * It's static class that accepts {@link TvInputManagerHelper} as parameter to test.
- * To test comparator, we need to mock API in parent class such as {@link #isPartnerInput},
- * but it's impossible for an inner class to use mocked methods.
- * (i.e. Mockito's spy doesn't work)
+ * <p>It's static class that accepts {@link TvInputManagerHelper} as parameter to test. To test
+ * comparator, we need to mock API in parent class such as {@link #isPartnerInput}, but it's
+ * impossible for an inner class to use mocked methods. (i.e. Mockito's spy doesn't work)
*/
@VisibleForTesting
static class InputComparatorInternal implements Comparator<TvInputInfo> {
@@ -568,8 +626,8 @@ public class TvInputManagerHelper {
}
/**
- * A comparator used for {@link com.android.tv.ui.SelectInputView} to show the list of
- * TV inputs.
+ * A comparator used for {@link com.android.tv.ui.SelectInputView} to show the list of TV
+ * inputs.
*/
public static class HardwareInputComparator implements Comparator<TvInputInfo> {
private Map<Integer, Integer> mTypePriorities = new HashMap<>();
@@ -591,10 +649,12 @@ public class TvInputManagerHelper {
return -1;
}
- boolean enabledL = (mTvInputManagerHelper.getInputState(lhs)
- != TvInputManager.INPUT_STATE_DISCONNECTED);
- boolean enabledR = (mTvInputManagerHelper.getInputState(rhs)
- != TvInputManager.INPUT_STATE_DISCONNECTED);
+ boolean enabledL =
+ (mTvInputManagerHelper.getInputState(lhs)
+ != TvInputManager.INPUT_STATE_DISCONNECTED);
+ boolean enabledR =
+ (mTvInputManagerHelper.getInputState(rhs)
+ != TvInputManager.INPUT_STATE_DISCONNECTED);
if (enabledL != enabledR) {
return enabledL ? -1 : 1;
}
@@ -620,11 +680,13 @@ public class TvInputManagerHelper {
return sortKeyR - sortKeyL;
}
- String parentLabelL = lhs.getParentId() != null
- ? getLabel(mTvInputManagerHelper.getTvInputInfo(lhs.getParentId()))
+ String parentLabelL =
+ lhs.getParentId() != null
+ ? getLabel(mTvInputManagerHelper.getTvInputInfo(lhs.getParentId()))
: getLabel(mTvInputManagerHelper.getTvInputInfo(lhs.getId()));
- String parentLabelR = rhs.getParentId() != null
- ? getLabel(mTvInputManagerHelper.getTvInputInfo(rhs.getParentId()))
+ String parentLabelR =
+ rhs.getParentId() != null
+ ? getLabel(mTvInputManagerHelper.getTvInputInfo(rhs.getParentId()))
: getLabel(mTvInputManagerHelper.getTvInputInfo(rhs.getId()));
if (!TextUtils.equals(parentLabelL, parentLabelR)) {
diff --git a/src/com/android/tv/util/TvSettings.java b/src/com/android/tv/util/TvSettings.java
index c5fde317..ae79e7e5 100644
--- a/src/com/android/tv/util/TvSettings.java
+++ b/src/com/android/tv/util/TvSettings.java
@@ -21,7 +21,6 @@ import android.content.SharedPreferences;
import android.media.tv.TvTrackInfo;
import android.preference.PreferenceManager;
import android.support.annotation.IntDef;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
@@ -29,11 +28,11 @@ import java.util.HashSet;
import java.util.Set;
/**
- * A class about the constants for TV settings.
- * Objects that are returned from the various {@code get} methods must be treated as immutable.
+ * A class about the constants for TV settings. Objects that are returned from the various {@code
+ * get} methods must be treated as immutable.
*/
public final class TvSettings {
- public static final String PREF_DISPLAY_MODE = "display_mode"; // int value
+ public static final String PREF_DISPLAY_MODE = "display_mode"; // int value
public static final String PREF_PIN = "pin"; // 4-digit string value. Otherwise, it's not set.
// Multi-track audio settings
@@ -56,9 +55,14 @@ public final class TvSettings {
@Retention(RetentionPolicy.SOURCE)
@IntDef({
- CONTENT_RATING_LEVEL_NONE, CONTENT_RATING_LEVEL_HIGH, CONTENT_RATING_LEVEL_MEDIUM,
- CONTENT_RATING_LEVEL_LOW, CONTENT_RATING_LEVEL_CUSTOM })
+ CONTENT_RATING_LEVEL_NONE,
+ CONTENT_RATING_LEVEL_HIGH,
+ CONTENT_RATING_LEVEL_MEDIUM,
+ CONTENT_RATING_LEVEL_LOW,
+ CONTENT_RATING_LEVEL_CUSTOM
+ })
public @interface ContentRatingLevel {}
+
public static final int CONTENT_RATING_LEVEL_NONE = 0;
public static final int CONTENT_RATING_LEVEL_HIGH = 1;
public static final int CONTENT_RATING_LEVEL_MEDIUM = 2;
@@ -69,61 +73,74 @@ public final class TvSettings {
// Multi-track audio settings
public static String getMultiAudioId(Context context) {
- return PreferenceManager.getDefaultSharedPreferences(context).getString(
- PREF_MULTI_AUDIO_ID, null);
+ return PreferenceManager.getDefaultSharedPreferences(context)
+ .getString(PREF_MULTI_AUDIO_ID, null);
}
public static void setMultiAudioId(Context context, String language) {
- PreferenceManager.getDefaultSharedPreferences(context).edit().putString(
- PREF_MULTI_AUDIO_ID, language).apply();
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putString(PREF_MULTI_AUDIO_ID, language)
+ .apply();
}
public static String getMultiAudioLanguage(Context context) {
- return PreferenceManager.getDefaultSharedPreferences(context).getString(
- PREF_MULTI_AUDIO_LANGUAGE, null);
+ return PreferenceManager.getDefaultSharedPreferences(context)
+ .getString(PREF_MULTI_AUDIO_LANGUAGE, null);
}
public static void setMultiAudioLanguage(Context context, String language) {
- PreferenceManager.getDefaultSharedPreferences(context).edit().putString(
- PREF_MULTI_AUDIO_LANGUAGE, language).apply();
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putString(PREF_MULTI_AUDIO_LANGUAGE, language)
+ .apply();
}
public static int getMultiAudioChannelCount(Context context) {
- return PreferenceManager.getDefaultSharedPreferences(context).getInt(
- PREF_MULTI_AUDIO_CHANNEL_COUNT, 0);
+ return PreferenceManager.getDefaultSharedPreferences(context)
+ .getInt(PREF_MULTI_AUDIO_CHANNEL_COUNT, 0);
}
public static void setMultiAudioChannelCount(Context context, int channelCount) {
- PreferenceManager.getDefaultSharedPreferences(context).edit().putInt(
- PREF_MULTI_AUDIO_CHANNEL_COUNT, channelCount).apply();
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putInt(PREF_MULTI_AUDIO_CHANNEL_COUNT, channelCount)
+ .apply();
}
- public static void setDvrPlaybackTrackSettings(Context context, int trackType,
- TvTrackInfo info) {
+ public static void setDvrPlaybackTrackSettings(
+ Context context, int trackType, TvTrackInfo info) {
if (trackType == TvTrackInfo.TYPE_AUDIO) {
if (info == null) {
- PreferenceManager.getDefaultSharedPreferences(context).edit()
- .remove(PREF_DVR_MULTI_AUDIO_ID).apply();
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .remove(PREF_DVR_MULTI_AUDIO_ID)
+ .apply();
} else {
- PreferenceManager.getDefaultSharedPreferences(context).edit()
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
.putString(PREF_DVR_MULTI_AUDIO_LANGUAGE, info.getLanguage())
.putInt(PREF_DVR_MULTI_AUDIO_CHANNEL_COUNT, info.getAudioChannelCount())
- .putString(PREF_DVR_MULTI_AUDIO_ID, info.getId()).apply();
+ .putString(PREF_DVR_MULTI_AUDIO_ID, info.getId())
+ .apply();
}
} else if (trackType == TvTrackInfo.TYPE_SUBTITLE) {
if (info == null) {
- PreferenceManager.getDefaultSharedPreferences(context).edit()
- .remove(PREF_DVR_SUBTITLE_ID).apply();
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .remove(PREF_DVR_SUBTITLE_ID)
+ .apply();
} else {
- PreferenceManager.getDefaultSharedPreferences(context).edit()
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
.putString(PREF_DVR_SUBTITLE_LANGUAGE, info.getLanguage())
- .putString(PREF_DVR_SUBTITLE_ID, info.getId()).apply();
+ .putString(PREF_DVR_SUBTITLE_ID, info.getId())
+ .apply();
}
}
}
- public static TvTrackInfo getDvrPlaybackTrackSettings(Context context,
- int trackType) {
+ public static TvTrackInfo getDvrPlaybackTrackSettings(Context context, int trackType) {
String language;
String trackId;
int channelCount;
@@ -136,7 +153,9 @@ public final class TvSettings {
language = pref.getString(PREF_DVR_MULTI_AUDIO_LANGUAGE, null);
channelCount = pref.getInt(PREF_DVR_MULTI_AUDIO_CHANNEL_COUNT, 0);
return new TvTrackInfo.Builder(trackType, trackId)
- .setLanguage(language).setAudioChannelCount(channelCount).build();
+ .setLanguage(language)
+ .setAudioChannelCount(channelCount)
+ .build();
} else if (trackType == TvTrackInfo.TYPE_SUBTITLE) {
trackId = pref.getString(PREF_DVR_SUBTITLE_ID, null);
if (trackId == null) {
@@ -153,16 +172,20 @@ public final class TvSettings {
public static void addContentRatingSystem(Context context, String id) {
Set<String> contentRatingSystemSet = getContentRatingSystemSet(context);
if (contentRatingSystemSet.add(id)) {
- PreferenceManager.getDefaultSharedPreferences(context).edit()
- .putStringSet(PREF_CONTENT_RATING_SYSTEMS, contentRatingSystemSet).apply();
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putStringSet(PREF_CONTENT_RATING_SYSTEMS, contentRatingSystemSet)
+ .apply();
}
}
public static void removeContentRatingSystem(Context context, String id) {
Set<String> contentRatingSystemSet = getContentRatingSystemSet(context);
if (contentRatingSystemSet.remove(id)) {
- PreferenceManager.getDefaultSharedPreferences(context).edit()
- .putStringSet(PREF_CONTENT_RATING_SYSTEMS, contentRatingSystemSet).apply();
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putStringSet(PREF_CONTENT_RATING_SYSTEMS, contentRatingSystemSet)
+ .apply();
}
}
@@ -176,25 +199,28 @@ public final class TvSettings {
*/
public static boolean isContentRatingSystemSet(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context)
- .getStringSet(PREF_CONTENT_RATING_SYSTEMS, null) != null;
+ .getStringSet(PREF_CONTENT_RATING_SYSTEMS, null)
+ != null;
}
private static Set<String> getContentRatingSystemSet(Context context) {
- return new HashSet<>(PreferenceManager.getDefaultSharedPreferences(context)
- .getStringSet(PREF_CONTENT_RATING_SYSTEMS, Collections.emptySet()));
+ return new HashSet<>(
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .getStringSet(PREF_CONTENT_RATING_SYSTEMS, Collections.emptySet()));
}
@ContentRatingLevel
@SuppressWarnings("ResourceType")
public static int getContentRatingLevel(Context context) {
- return PreferenceManager.getDefaultSharedPreferences(context).getInt(
- PREF_CONTENT_RATING_LEVEL, CONTENT_RATING_LEVEL_NONE);
+ return PreferenceManager.getDefaultSharedPreferences(context)
+ .getInt(PREF_CONTENT_RATING_LEVEL, CONTENT_RATING_LEVEL_NONE);
}
- public static void setContentRatingLevel(Context context,
- @ContentRatingLevel int level) {
- PreferenceManager.getDefaultSharedPreferences(context).edit().putInt(
- PREF_CONTENT_RATING_LEVEL, level).apply();
+ public static void setContentRatingLevel(Context context, @ContentRatingLevel int level) {
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putInt(PREF_CONTENT_RATING_LEVEL, level)
+ .apply();
}
/**
@@ -202,8 +228,8 @@ public final class TvSettings {
* repeatedly).
*/
public static long getDisablePinUntil(Context context) {
- return PreferenceManager.getDefaultSharedPreferences(context).getLong(
- PREF_DISABLE_PIN_UNTIL, 0);
+ return PreferenceManager.getDefaultSharedPreferences(context)
+ .getLong(PREF_DISABLE_PIN_UNTIL, 0);
}
/**
@@ -211,7 +237,9 @@ public final class TvSettings {
* repeatedly).
*/
public static void setDisablePinUntil(Context context, long timeMillis) {
- PreferenceManager.getDefaultSharedPreferences(context).edit().putLong(
- PREF_DISABLE_PIN_UNTIL, timeMillis).apply();
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putLong(PREF_DISABLE_PIN_UNTIL, timeMillis)
+ .apply();
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/util/TvTrackInfoUtils.java b/src/com/android/tv/util/TvTrackInfoUtils.java
index 667cc9bf..09874502 100644
--- a/src/com/android/tv/util/TvTrackInfoUtils.java
+++ b/src/com/android/tv/util/TvTrackInfoUtils.java
@@ -16,27 +16,24 @@
package com.android.tv.util;
import android.media.tv.TvTrackInfo;
-
import java.util.Comparator;
import java.util.List;
-/**
- * Static utilities for {@link TvTrackInfo}.
- */
+/** Static utilities for {@link TvTrackInfo}. */
public class TvTrackInfoUtils {
/**
* Compares how closely two {@link android.media.tv.TvTrackInfo}s match {@code language}, {@code
* channelCount} and {@code id} in that precedence.
*
- * @param id The track id to match.
- * @param language The language to match.
+ * @param id The track id to match.
+ * @param language The language to match.
* @param channelCount The channel count to match.
* @return -1 if lhs is a worse match, 0 if lhs and rhs match equally and 1 if lhs is a better
- * match.
+ * match.
*/
- public static Comparator<TvTrackInfo> createComparator(final String id, final String language,
- final int channelCount) {
+ public static Comparator<TvTrackInfo> createComparator(
+ final String id, final String language, final int channelCount) {
return new Comparator<TvTrackInfo>() {
@Override
@@ -52,15 +49,17 @@ public class TvTrackInfoUtils {
}
// Assumes {@code null} language matches to any language since it means user hasn't
// selected any track before or selected a track without language information.
- boolean lhsLangMatch = language == null || Utils.isEqualLanguage(lhs.getLanguage(),
- language);
- boolean rhsLangMatch = language == null || Utils.isEqualLanguage(rhs.getLanguage(),
- language);
+ boolean lhsLangMatch =
+ language == null || Utils.isEqualLanguage(lhs.getLanguage(), language);
+ boolean rhsLangMatch =
+ language == null || Utils.isEqualLanguage(rhs.getLanguage(), language);
if (lhsLangMatch && rhsLangMatch) {
- boolean lhsCountMatch = lhs.getType() != TvTrackInfo.TYPE_AUDIO
- || lhs.getAudioChannelCount() == channelCount;
- boolean rhsCountMatch = rhs.getType() != TvTrackInfo.TYPE_AUDIO
- || rhs.getAudioChannelCount() == channelCount;
+ boolean lhsCountMatch =
+ lhs.getType() != TvTrackInfo.TYPE_AUDIO
+ || lhs.getAudioChannelCount() == channelCount;
+ boolean rhsCountMatch =
+ rhs.getType() != TvTrackInfo.TYPE_AUDIO
+ || rhs.getAudioChannelCount() == channelCount;
if (lhsCountMatch && rhsCountMatch) {
return Boolean.compare(lhs.getId().equals(id), rhs.getId().equals(id));
} else {
@@ -74,16 +73,16 @@ public class TvTrackInfoUtils {
}
/**
- * Selects the best TvTrackInfo available or the first if none matches.
+ * Selects the best TvTrackInfo available or the first if none matches.
*
- * @param tracks The tracks to choose from
- * @param id The track id to match.
- * @param language The language to match.
+ * @param tracks The tracks to choose from
+ * @param id The track id to match.
+ * @param language The language to match.
* @param channelCount The channel count to match.
* @return the best matching track or the first one if none matches.
*/
- public static TvTrackInfo getBestTrackInfo(List<TvTrackInfo> tracks, String id, String language,
- int channelCount) {
+ public static TvTrackInfo getBestTrackInfo(
+ List<TvTrackInfo> tracks, String id, String language, int channelCount) {
if (tracks == null) {
return null;
}
@@ -97,6 +96,5 @@ public class TvTrackInfoUtils {
return best;
}
- private TvTrackInfoUtils() {
- }
-} \ No newline at end of file
+ private TvTrackInfoUtils() {}
+}
diff --git a/src/com/android/tv/util/TvUriMatcher.java b/src/com/android/tv/util/TvUriMatcher.java
index 3d91cdad..9e74117e 100644
--- a/src/com/android/tv/util/TvUriMatcher.java
+++ b/src/com/android/tv/util/TvUriMatcher.java
@@ -21,22 +21,25 @@ import android.content.UriMatcher;
import android.media.tv.TvContract;
import android.net.Uri;
import android.support.annotation.IntDef;
-
import com.android.tv.search.LocalSearchProvider;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-/**
- * Utility class to aid in matching URIs in TvProvider.
- */
+/** Utility class to aid in matching URIs in TvProvider. */
public class TvUriMatcher {
private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
@Retention(RetentionPolicy.SOURCE)
- @IntDef({MATCH_CHANNEL, MATCH_CHANNEL_ID, MATCH_PROGRAM, MATCH_PROGRAM_ID,
- MATCH_RECORDED_PROGRAM, MATCH_RECORDED_PROGRAM_ID, MATCH_WATCHED_PROGRAM_ID,
- MATCH_ON_DEVICE_SEARCH})
+ @IntDef({
+ MATCH_CHANNEL,
+ MATCH_CHANNEL_ID,
+ MATCH_PROGRAM,
+ MATCH_PROGRAM_ID,
+ MATCH_RECORDED_PROGRAM,
+ MATCH_RECORDED_PROGRAM_ID,
+ MATCH_WATCHED_PROGRAM_ID,
+ MATCH_ON_DEVICE_SEARCH
+ })
private @interface TvProviderUriMatchCode {}
/** The code for the channels URI. */
public static final int MATCH_CHANNEL = 1;
@@ -54,6 +57,7 @@ public class TvUriMatcher {
public static final int MATCH_WATCHED_PROGRAM_ID = 7;
/** The code for the on-device search URI. */
public static final int MATCH_ON_DEVICE_SEARCH = 8;
+
static {
URI_MATCHER.addURI(TvContract.AUTHORITY, "channel", MATCH_CHANNEL);
URI_MATCHER.addURI(TvContract.AUTHORITY, "channel/#", MATCH_CHANNEL_ID);
@@ -62,11 +66,13 @@ public class TvUriMatcher {
URI_MATCHER.addURI(TvContract.AUTHORITY, "recorded_program", MATCH_RECORDED_PROGRAM);
URI_MATCHER.addURI(TvContract.AUTHORITY, "recorded_program/#", MATCH_RECORDED_PROGRAM_ID);
URI_MATCHER.addURI(TvContract.AUTHORITY, "watched_program/#", MATCH_WATCHED_PROGRAM_ID);
- URI_MATCHER.addURI(LocalSearchProvider.AUTHORITY,
- SearchManager.SUGGEST_URI_PATH_QUERY + "/*", MATCH_ON_DEVICE_SEARCH);
+ URI_MATCHER.addURI(
+ LocalSearchProvider.AUTHORITY,
+ SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
+ MATCH_ON_DEVICE_SEARCH);
}
- private TvUriMatcher() { }
+ private TvUriMatcher() {}
/**
* Try to match against the path in a url.
@@ -74,7 +80,8 @@ public class TvUriMatcher {
* @see UriMatcher#match
*/
@SuppressWarnings("WrongConstant")
- @TvProviderUriMatchCode public static int match(Uri uri) {
+ @TvProviderUriMatchCode
+ public static int match(Uri uri) {
return URI_MATCHER.match(uri);
}
}
diff --git a/src/com/android/tv/util/Utils.java b/src/com/android/tv/util/Utils.java
index d11bab3c..a75bd446 100644
--- a/src/com/android/tv/util/Utils.java
+++ b/src/com/android/tv/util/Utils.java
@@ -38,22 +38,16 @@ import android.support.annotation.VisibleForTesting;
import android.support.annotation.WorkerThread;
import android.text.TextUtils;
import android.text.format.DateUtils;
-import android.util.ArraySet;
import android.util.Log;
import android.view.View;
-
-import com.android.tv.ApplicationSingletons;
import com.android.tv.R;
-import com.android.tv.TvApplication;
-import com.android.tv.common.BuildConfig;
+import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
-import com.android.tv.data.Channel;
+import com.android.tv.common.util.Clock;
import com.android.tv.data.GenreItems;
import com.android.tv.data.Program;
import com.android.tv.data.StreamInfo;
-import com.android.tv.experiments.Experiments;
-
-import java.io.File;
+import com.android.tv.data.api.Channel;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -69,18 +63,13 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
-/**
- * A class that includes convenience methods for accessing TvProvider database.
- */
+/** A class that includes convenience methods for accessing TvProvider database. */
public class Utils {
private static final String TAG = "Utils";
private static final boolean DEBUG = false;
- private static final SimpleDateFormat ISO_8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ",
- Locale.US);
-
public static final String EXTRA_KEY_ACTION = "action";
- public static final String EXTRA_ACTION_SHOW_TV_INPUT ="show_tv_input";
+ public static final String EXTRA_ACTION_SHOW_TV_INPUT = "show_tv_input";
public static final String EXTRA_KEY_FROM_LAUNCHER = "from_launcher";
public static final String EXTRA_KEY_RECORDED_PROGRAM_ID = "recorded_program_id";
public static final String EXTRA_KEY_RECORDED_PROGRAM_SEEK_TIME = "recorded_program_seek_time";
@@ -97,8 +86,7 @@ public class Utils {
private static final String PREF_KEY_LAST_WATCHED_CHANNEL_URI = "last_watched_channel_uri";
private static final String PREF_KEY_LAST_WATCHED_TUNER_INPUT_ID =
"last_watched_tuner_input_id";
- private static final String PREF_KEY_RECORDING_FAILED_REASONS =
- "recording_failed_reasons";
+ private static final String PREF_KEY_RECORDING_FAILED_REASONS = "recording_failed_reasons";
private static final String PREF_KEY_FAILED_SCHEDULED_RECORDING_INFO_SET =
"failed_scheduled_recording_info_set";
@@ -121,15 +109,6 @@ public class Utils {
private static final long HALF_MINUTE_MS = TimeUnit.SECONDS.toMillis(30);
private static final long ONE_DAY_MS = TimeUnit.DAYS.toMillis(1);
- // Hardcoded list for known bundled inputs not written by OEM/SOCs.
- // Bundled (system) inputs not in the list will get the high priority
- // so they and their channels come first in the UI.
- private static final Set<String> BUNDLED_PACKAGE_SET = new ArraySet<>();
-
- static {
- BUNDLED_PACKAGE_SET.add("com.android.tv");
- }
-
private enum AspectRatio {
ASPECT_RATIO_4_3(4, 3),
ASPECT_RATIO_16_9(16, 9),
@@ -150,13 +129,11 @@ public class Utils {
}
}
- private Utils() {
- }
+ private Utils() {}
public static String buildSelectionForIds(String idName, List<Long> ids) {
StringBuilder sb = new StringBuilder();
- sb.append(idName).append(" in (")
- .append(ids.get(0));
+ sb.append(idName).append(" in (").append(ids.get(0));
for (int i = 1; i < ids.size(); ++i) {
sb.append(",").append(ids.get(i));
}
@@ -171,8 +148,8 @@ public class Utils {
}
Uri channelUri = TvContract.buildChannelUri(channelId);
String[] projection = {TvContract.Channels.COLUMN_INPUT_ID};
- try (Cursor cursor = context.getContentResolver()
- .query(channelUri, projection, null, null, null)) {
+ try (Cursor cursor =
+ context.getContentResolver().query(channelUri, projection, null, null, null)) {
if (cursor != null && cursor.moveToNext()) {
return Utils.intern(cursor.getString(0));
}
@@ -185,60 +162,61 @@ public class Utils {
Log.e(TAG, "setLastWatchedChannel: channel cannot be null");
return;
}
- PreferenceManager.getDefaultSharedPreferences(context).edit()
- .putString(PREF_KEY_LAST_WATCHED_CHANNEL_URI, channel.getUri().toString()).apply();
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putString(PREF_KEY_LAST_WATCHED_CHANNEL_URI, channel.getUri().toString())
+ .apply();
if (!channel.isPassthrough()) {
long channelId = channel.getId();
if (channel.getId() < 0) {
throw new IllegalArgumentException("channelId should be equal to or larger than 0");
}
- PreferenceManager.getDefaultSharedPreferences(context).edit()
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
.putLong(PREF_KEY_LAST_WATCHED_CHANNEL_ID, channelId)
- .putLong(PREF_KEY_LAST_WATCHED_CHANNEL_ID_FOR_INPUT + channel.getInputId(),
+ .putLong(
+ PREF_KEY_LAST_WATCHED_CHANNEL_ID_FOR_INPUT + channel.getInputId(),
channelId)
.putString(PREF_KEY_LAST_WATCHED_TUNER_INPUT_ID, channel.getInputId())
.apply();
}
}
- /**
- * Sets recording failed reason.
- */
+ /** Sets recording failed reason. */
public static void setRecordingFailedReason(Context context, int reason) {
long reasons = getRecordingFailedReasons(context) | 0x1 << reason;
- PreferenceManager.getDefaultSharedPreferences(context).edit()
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
.putLong(PREF_KEY_RECORDING_FAILED_REASONS, reasons)
.apply();
}
- /**
- * Adds the info of failed scheduled recording.
- */
- public static void addFailedScheduledRecordingInfo(Context context,
- String scheduledRecordingInfo) {
+ /** Adds the info of failed scheduled recording. */
+ public static void addFailedScheduledRecordingInfo(
+ Context context, String scheduledRecordingInfo) {
Set<String> failedScheduledRecordingInfoSet = getFailedScheduledRecordingInfoSet(context);
failedScheduledRecordingInfoSet.add(scheduledRecordingInfo);
- PreferenceManager.getDefaultSharedPreferences(context).edit()
- .putStringSet(PREF_KEY_FAILED_SCHEDULED_RECORDING_INFO_SET,
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putStringSet(
+ PREF_KEY_FAILED_SCHEDULED_RECORDING_INFO_SET,
failedScheduledRecordingInfoSet)
.apply();
}
- /**
- * Clears the failed scheduled recording info set.
- */
+ /** Clears the failed scheduled recording info set. */
public static void clearFailedScheduledRecordingInfoSet(Context context) {
- PreferenceManager.getDefaultSharedPreferences(context).edit()
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
.remove(PREF_KEY_FAILED_SCHEDULED_RECORDING_INFO_SET)
.apply();
}
- /**
- * Clears recording failed reason.
- */
+ /** Clears recording failed reason. */
public static void clearRecordingFailedReason(Context context, int reason) {
long reasons = getRecordingFailedReasons(context) & ~(0x1 << reason);
- PreferenceManager.getDefaultSharedPreferences(context).edit()
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
.putLong(PREF_KEY_RECORDING_FAILED_REASONS, reasons)
.apply();
}
@@ -258,9 +236,7 @@ public class Utils {
.getString(PREF_KEY_LAST_WATCHED_CHANNEL_URI, null);
}
- /**
- * Returns the last watched tuner input id.
- */
+ /** Returns the last watched tuner input id. */
public static String getLastWatchedTunerInputId(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context)
.getString(PREF_KEY_LAST_WATCHED_TUNER_INPUT_ID, null);
@@ -268,32 +244,28 @@ public class Utils {
private static long getRecordingFailedReasons(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context)
- .getLong(PREF_KEY_RECORDING_FAILED_REASONS,
- RECORDING_FAILED_REASON_NONE);
+ .getLong(PREF_KEY_RECORDING_FAILED_REASONS, RECORDING_FAILED_REASON_NONE);
}
- /**
- * Returns the failed scheduled recordings info set.
- */
+ /** Returns the failed scheduled recordings info set. */
public static Set<String> getFailedScheduledRecordingInfoSet(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context)
.getStringSet(PREF_KEY_FAILED_SCHEDULED_RECORDING_INFO_SET, new HashSet<>());
}
- /**
- * Checks do recording failed reason exist.
- */
+ /** Checks do recording failed reason exist. */
public static boolean hasRecordingFailedReason(Context context, int reason) {
long reasons = getRecordingFailedReasons(context);
return (reasons & 0x1 << reason) != 0;
}
/**
- * Returns {@code true}, if {@code uri} specifies an input, which is usually generated
- * from {@link TvContract#buildChannelsUriForInput}.
+ * Returns {@code true}, if {@code uri} specifies an input, which is usually generated from
+ * {@link TvContract#buildChannelsUriForInput}.
*/
public static boolean isChannelUriForInput(Uri uri) {
- return isTvUri(uri) && PATH_CHANNEL.equals(uri.getPathSegments().get(0))
+ return isTvUri(uri)
+ && PATH_CHANNEL.equals(uri.getPathSegments().get(0))
&& !TextUtils.isEmpty(uri.getQueryParameter("input"));
}
@@ -314,7 +286,8 @@ public class Utils {
}
private static boolean isTvUri(Uri uri) {
- return uri != null && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())
+ return uri != null
+ && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())
&& TvContract.AUTHORITY.equals(uri.getAuthority());
}
@@ -323,23 +296,17 @@ public class Utils {
return pathSegments.size() == 2 && pathSegment.equals(pathSegments.get(0));
}
- /**
- * Returns {@code true}, if {@code uri} is a programs URI.
- */
+ /** Returns {@code true}, if {@code uri} is a programs URI. */
public static boolean isProgramsUri(Uri uri) {
return isTvUri(uri) && PATH_PROGRAM.equals(uri.getPathSegments().get(0));
}
- /**
- * Returns {@code true}, if {@code uri} is a programs URI.
- */
+ /** Returns {@code true}, if {@code uri} is a programs URI. */
public static boolean isRecordedProgramsUri(Uri uri) {
return isTvUri(uri) && PATH_RECORDED_PROGRAM.equals(uri.getPathSegments().get(0));
}
- /**
- * Gets the info of the program on particular time.
- */
+ /** Gets the info of the program on particular time. */
@WorkerThread
public static Program getProgramAt(Context context, long channelId, long timeMs) {
if (channelId == Channel.INVALID_ID) {
@@ -355,10 +322,11 @@ public class Utils {
Log.w(TAG, message);
}
}
- Uri uri = TvContract.buildProgramsUriForChannel(TvContract.buildChannelUri(channelId),
- timeMs, timeMs);
- try (Cursor cursor = context.getContentResolver().query(uri, Program.PROJECTION,
- null, null, null)) {
+ Uri uri =
+ TvContract.buildProgramsUriForChannel(
+ TvContract.buildChannelUri(channelId), timeMs, timeMs);
+ try (Cursor cursor =
+ context.getContentResolver().query(uri, Program.PROJECTION, null, null, null)) {
if (cursor != null && cursor.moveToNext()) {
return Program.fromCursor(cursor);
}
@@ -366,55 +334,98 @@ public class Utils {
return null;
}
- /**
- * Gets the info of the current program.
- */
+ /** Gets the info of the current program. */
@WorkerThread
public static Program getCurrentProgram(Context context, long channelId) {
return getProgramAt(context, channelId, System.currentTimeMillis());
}
- /**
- * Returns the round off minutes when convert milliseconds to minutes.
- */
+ /** Returns the round off minutes when convert milliseconds to minutes. */
public static int getRoundOffMinsFromMs(long millis) {
// Round off the result by adding half minute to the original ms.
return (int) TimeUnit.MILLISECONDS.toMinutes(millis + HALF_MINUTE_MS);
}
/**
- * Returns duration string according to the date & time format.
- * If {@code startUtcMillis} and {@code endUtcMills} are equal,
- * formatted time will be returned instead.
+ * Returns duration string according to the date & time format. If {@code startUtcMillis} and
+ * {@code endUtcMills} are equal, formatted time will be returned instead.
*
* @param startUtcMillis start of duration in millis. Should be less than {code endUtcMillis}.
* @param endUtcMillis end of duration in millis. Should be larger than {@code startUtcMillis}.
- * @param useShortFormat {@code true} if abbreviation is needed to save space.
- * In that case, date will be omitted if duration starts from today
- * and is less than a day. If it's necessary,
- * {@link DateUtils#FORMAT_NUMERIC_DATE} is used otherwise.
+ * @param useShortFormat {@code true} if abbreviation is needed to save space. In that case,
+ * date will be omitted if duration starts from today and is less than a day. If it's
+ * necessary, {@link DateUtils#FORMAT_NUMERIC_DATE} is used otherwise.
*/
public static String getDurationString(
Context context, long startUtcMillis, long endUtcMillis, boolean useShortFormat) {
- return getDurationString(context, System.currentTimeMillis(), startUtcMillis, endUtcMillis,
- useShortFormat, 0);
+ return getDurationString(
+ context,
+ System.currentTimeMillis(),
+ startUtcMillis,
+ endUtcMillis,
+ useShortFormat,
+ 0);
}
- @VisibleForTesting
- static String getDurationString(Context context, long baseMillis, long startUtcMillis,
- long endUtcMillis, boolean useShortFormat, int flag) {
- return getDurationString(context, startUtcMillis, endUtcMillis,
- useShortFormat, !isInGivenDay(baseMillis, startUtcMillis), true, flag);
+ /**
+ * Returns duration string according to the date & time format. If {@code startUtcMillis} and
+ * {@code endUtcMills} are equal, formatted time will be returned instead.
+ *
+ * @param clock the clock used to get the current time.
+ * @param startUtcMillis start of duration in millis. Should be less than {code endUtcMillis}.
+ * @param endUtcMillis end of duration in millis. Should be larger than {@code startUtcMillis}.
+ * @param useShortFormat {@code true} if abbreviation is needed to save space. In that case,
+ * date will be omitted if duration starts from today and is less than a day. If it's
+ * necessary, {@link DateUtils#FORMAT_NUMERIC_DATE} is used otherwise.
+ */
+ public static String getDurationString(
+ Context context,
+ Clock clock,
+ long startUtcMillis,
+ long endUtcMillis,
+ boolean useShortFormat) {
+ return getDurationString(
+ context,
+ clock.currentTimeMillis(),
+ startUtcMillis,
+ endUtcMillis,
+ useShortFormat,
+ 0);
}
- /**
- * Returns duration string according to the time format, may not contain date information.
- * Note: At least one of showDate and showTime should be true.
+ @VisibleForTesting
+ static String getDurationString(
+ Context context,
+ long baseMillis,
+ long startUtcMillis,
+ long endUtcMillis,
+ boolean useShortFormat,
+ int flag) {
+ return getDurationString(
+ context,
+ startUtcMillis,
+ endUtcMillis,
+ useShortFormat,
+ !isInGivenDay(baseMillis, startUtcMillis),
+ true,
+ flag);
+ }
+
+ /**
+ * Returns duration string according to the time format, may not contain date information. Note:
+ * At least one of showDate and showTime should be true.
*/
- public static String getDurationString(Context context, long startUtcMillis, long endUtcMillis,
- boolean useShortFormat, boolean showDate, boolean showTime, int flag) {
- flag |= DateUtils.FORMAT_ABBREV_MONTH
- | ((useShortFormat) ? DateUtils.FORMAT_NUMERIC_DATE : 0);
+ public static String getDurationString(
+ Context context,
+ long startUtcMillis,
+ long endUtcMillis,
+ boolean useShortFormat,
+ boolean showDate,
+ boolean showTime,
+ int flag) {
+ flag |=
+ DateUtils.FORMAT_ABBREV_MONTH
+ | ((useShortFormat) ? DateUtils.FORMAT_NUMERIC_DATE : 0);
SoftPreconditions.checkArgument(showTime || showDate);
if (showTime) {
flag |= DateUtils.FORMAT_SHOW_TIME;
@@ -431,20 +442,21 @@ public class Utils {
// Do not show date for short format.
// Subtracting one day is needed because {@link DateUtils@formatDateRange}
// automatically shows date if the duration covers multiple days.
- return DateUtils.formatDateRange(context,
- startUtcMillis, endUtcMillis - TimeUnit.DAYS.toMillis(1), flag);
+ return DateUtils.formatDateRange(
+ context, startUtcMillis, endUtcMillis - TimeUnit.DAYS.toMillis(1), flag);
}
}
// Workaround of b/28740989.
// Add 1 msec to endUtcMillis to avoid DateUtils' bug with a duration of 12:00AM~12:00AM.
String dateRange = DateUtils.formatDateRange(context, startUtcMillis, endUtcMillis, flag);
- return startUtcMillis == endUtcMillis || dateRange.contains("–") ? dateRange
+ return startUtcMillis == endUtcMillis || dateRange.contains("–")
+ ? dateRange
: DateUtils.formatDateRange(context, startUtcMillis, endUtcMillis + 1, flag);
}
/**
- * Checks if two given time (in milliseconds) are in the same day with regard to the
- * locale timezone.
+ * Checks if two given time (in milliseconds) are in the same day with regard to the locale
+ * timezone.
*/
public static boolean isInGivenDay(long dayToMatchInMillis, long subjectTimeInMillis) {
TimeZone timeZone = Calendar.getInstance().getTimeZone();
@@ -456,9 +468,7 @@ public class Utils {
== Utils.floorTime(subjectTimeInMillis + offset, ONE_DAY_MS);
}
- /**
- * Calculate how many days between two milliseconds.
- */
+ /** Calculate how many days between two milliseconds. */
public static int computeDateDifference(long startTimeMs, long endTimeMs) {
Calendar calFrom = Calendar.getInstance();
Calendar calTo = Calendar.getInstance();
@@ -476,17 +486,23 @@ public class Utils {
cal.set(Calendar.MILLISECOND, 0);
}
- /**
- * Returns the last millisecond of a day which the millis belongs to.
- */
+ /** Returns the last millisecond of a day which the millis belongs to. */
public static long getLastMillisecondOfDay(long millis) {
- Calendar calender = Calendar.getInstance();
- calender.setTime(new Date(millis));
- calender.set(Calendar.HOUR_OF_DAY, 23);
- calender.set(Calendar.MINUTE, 59);
- calender.set(Calendar.SECOND, 59);
- calender.set(Calendar.MILLISECOND, 999);
- return calender.getTimeInMillis();
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(new Date(millis));
+ calendar.set(Calendar.HOUR_OF_DAY, 23);
+ calendar.set(Calendar.MINUTE, 59);
+ calendar.set(Calendar.SECOND, 59);
+ calendar.set(Calendar.MILLISECOND, 999);
+ return calendar.getTimeInMillis();
+ }
+
+ /** Returns the last millisecond of a day which the millis belongs to. */
+ public static long getFirstMillisecondOfDay(long millis) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(new Date(millis));
+ resetCalendar(calendar);
+ return calendar.getTimeInMillis();
}
public static String getAspectRatioString(int width, int height) {
@@ -494,7 +510,7 @@ public class Utils {
return "";
}
- for (AspectRatio ratio: AspectRatio.values()) {
+ for (AspectRatio ratio : AspectRatio.values()) {
if (Math.abs((float) ratio.height / ratio.width - (float) height / width) < 0.05f) {
return ratio.toString();
}
@@ -531,11 +547,9 @@ public class Utils {
public static String getVideoDefinitionLevelString(Context context, int videoFormat) {
switch (videoFormat) {
case StreamInfo.VIDEO_DEFINITION_LEVEL_ULTRA_HD:
- return context.getResources().getString(
- R.string.video_definition_level_ultra_hd);
+ return context.getResources().getString(R.string.video_definition_level_ultra_hd);
case StreamInfo.VIDEO_DEFINITION_LEVEL_FULL_HD:
- return context.getResources().getString(
- R.string.video_definition_level_full_hd);
+ return context.getResources().getString(R.string.video_definition_level_full_hd);
case StreamInfo.VIDEO_DEFINITION_LEVEL_HD:
return context.getResources().getString(R.string.video_definition_level_hd);
case StreamInfo.VIDEO_DEFINITION_LEVEL_SD:
@@ -570,8 +584,8 @@ public class Utils {
return false;
}
- public static String getMultiAudioString(Context context, TvTrackInfo track,
- boolean showSampleRate) {
+ public static String getMultiAudioString(
+ Context context, TvTrackInfo track, boolean showSampleRate) {
if (track.getType() != TvTrackInfo.TYPE_AUDIO) {
throw new IllegalArgumentException("Not an audio track: " + track);
}
@@ -600,11 +614,17 @@ public class Utils {
break;
default:
if (track.getAudioChannelCount() > 0) {
- metadata.append(context.getString(R.string.multi_audio_channel_suffix,
- track.getAudioChannelCount()));
+ metadata.append(
+ context.getString(
+ R.string.multi_audio_channel_suffix,
+ track.getAudioChannelCount()));
} else {
- Log.d(TAG, "Invalid audio channel count (" + track.getAudioChannelCount()
- + ") found for the audio track: " + track);
+ Log.d(
+ TAG,
+ "Invalid audio channel count ("
+ + track.getAudioChannelCount()
+ + ") found for the audio track: "
+ + track);
}
break;
}
@@ -628,8 +648,8 @@ public class Utils {
if (metadata.length() == 0) {
return language;
}
- return context.getString(R.string.multi_audio_display_string_with_channel, language,
- metadata.toString());
+ return context.getString(
+ R.string.multi_audio_display_string_with_channel, language, metadata.toString());
}
public static boolean isEqualLanguage(String lang1, String lang2) {
@@ -647,19 +667,19 @@ public class Utils {
}
public static boolean isIntentAvailable(Context context, Intent intent) {
- return context.getPackageManager().queryIntentActivities(
- intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
+ return context.getPackageManager()
+ .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
+ .size()
+ > 0;
}
- /**
- * Returns the label for a given input. Returns the custom label, if any.
- */
+ /** Returns the label for a given input. Returns the custom label, if any. */
public static String loadLabel(Context context, TvInputInfo input) {
if (input == null) {
return null;
}
TvInputManagerHelper inputManager =
- TvApplication.getSingletons(context).getTvInputManagerHelper();
+ TvSingletons.getSingletons(context).getTvInputManagerHelper();
CharSequence customLabel = inputManager.loadCustomLabel(input);
String label = (customLabel == null) ? null : customLabel.toString();
if (TextUtils.isEmpty(label)) {
@@ -668,9 +688,7 @@ public class Utils {
return label;
}
- /**
- * Enable all channels synchronously.
- */
+ /** Enable all channels synchronously. */
@WorkerThread
public static void enableAllChannels(Context context) {
ContentValues values = new ContentValues();
@@ -681,46 +699,48 @@ public class Utils {
/**
* Converts time in milliseconds to a String.
*
- * @param fullFormat {@code true} for returning date string with a full format
- * (e.g., Mon Aug 15 20:08:35 GMT 2016). {@code false} for a short format,
- * {e.g., [8/15/16] 8:08 AM}, in which date information would only appears
- * when the target time is not today.
+ * @param fullFormat {@code true} for returning date string with a full format (e.g., Mon Aug 15
+ * 20:08:35 GMT 2016). {@code false} for a short format, {e.g., 8/15/16 or 8:08 AM}, in
+ * which only the time is shown if the time is on the same day as now, and only the date is
+ * shown if it's a different day.
*/
public static String toTimeString(long timeMillis, boolean fullFormat) {
if (fullFormat) {
return new Date(timeMillis).toString();
} else {
long currentTime = System.currentTimeMillis();
- return (String) DateUtils.formatSameDayTime(timeMillis, System.currentTimeMillis(),
- SimpleDateFormat.SHORT, SimpleDateFormat.SHORT);
+ return (String)
+ DateUtils.formatSameDayTime(
+ timeMillis,
+ System.currentTimeMillis(),
+ SimpleDateFormat.SHORT,
+ SimpleDateFormat.SHORT);
}
}
- /**
- * Converts time in milliseconds to a String.
- */
+ /** Converts time in milliseconds to a String. */
public static String toTimeString(long timeMillis) {
return toTimeString(timeMillis, true);
}
/**
- * Converts time in milliseconds to a ISO 8061 string.
- */
- public static String toIsoDateTimeString(long timeMillis) {
- return ISO_8601.format(new Date(timeMillis));
- }
-
- /**
* Returns a {@link String} object which contains the layout information of the {@code view}.
*/
public static String toRectString(View view) {
return "{"
- + "l=" + view.getLeft()
- + ",r=" + view.getRight()
- + ",t=" + view.getTop()
- + ",b=" + view.getBottom()
- + ",w=" + view.getWidth()
- + ",h=" + view.getHeight() + "}";
+ + "l="
+ + view.getLeft()
+ + ",r="
+ + view.getRight()
+ + ",t="
+ + view.getTop()
+ + ",b="
+ + view.getBottom()
+ + ",w="
+ + view.getWidth()
+ + ",h="
+ + view.getHeight()
+ + "}";
}
/**
@@ -732,16 +752,14 @@ public class Utils {
}
/**
- * Ceils time to the given {@code timeUnit}. For example, if time is 5:32:11 and timeUnit is
- * one hour (60 * 60 * 1000), then the output will be 6:00:00.
+ * Ceils time to the given {@code timeUnit}. For example, if time is 5:32:11 and timeUnit is one
+ * hour (60 * 60 * 1000), then the output will be 6:00:00.
*/
public static long ceilTime(long timeMs, long timeUnit) {
return timeMs + timeUnit - (timeMs % timeUnit);
}
- /**
- * Returns an {@link String#intern() interned} string or null if the input is null.
- */
+ /** Returns an {@link String#intern() interned} string or null if the input is null. */
@Nullable
public static String intern(@Nullable String string) {
return string == null ? null : string.intern();
@@ -749,6 +767,7 @@ public class Utils {
/**
* Check if the index is valid for the collection,
+ *
* @param collection the collection
* @param index the index position to test
* @return index >= 0 && index < collection.size().
@@ -757,9 +776,7 @@ public class Utils {
return collection != null && (index >= 0 && index < collection.size());
}
- /**
- * Returns a localized version of the text resource specified by resourceId.
- */
+ /** Returns a localized version of the text resource specified by resourceId. */
public static CharSequence getTextForLocale(Context context, Locale locale, int resourceId) {
if (locale.equals(context.getResources().getConfiguration().locale)) {
return context.getText(resourceId);
@@ -769,12 +786,12 @@ public class Utils {
return context.createConfigurationContext(config).getText(resourceId);
}
- /**
- * Checks where there is any internal TV input.
- */
+ /** Checks where there is any internal TV input. */
public static boolean hasInternalTvInputs(Context context, boolean tunerInputOnly) {
- for (TvInputInfo input : TvApplication.getSingletons(context).getTvInputManagerHelper()
- .getTvInputInfos(true, tunerInputOnly)) {
+ for (TvInputInfo input :
+ TvSingletons.getSingletons(context)
+ .getTvInputManagerHelper()
+ .getTvInputInfos(true, tunerInputOnly)) {
if (isInternalTvInput(context, input.getId())) {
return true;
}
@@ -782,13 +799,13 @@ public class Utils {
return false;
}
- /**
- * Returns the internal TV inputs.
- */
+ /** Returns the internal TV inputs. */
public static List<TvInputInfo> getInternalTvInputs(Context context, boolean tunerInputOnly) {
List<TvInputInfo> inputs = new ArrayList<>();
- for (TvInputInfo input : TvApplication.getSingletons(context).getTvInputManagerHelper()
- .getTvInputInfos(true, tunerInputOnly)) {
+ for (TvInputInfo input :
+ TvSingletons.getSingletons(context)
+ .getTvInputManagerHelper()
+ .getTvInputInfos(true, tunerInputOnly)) {
if (isInternalTvInput(context, input.getId())) {
inputs.add(input);
}
@@ -796,81 +813,41 @@ public class Utils {
return inputs;
}
- /**
- * Checks whether the input is internal or not.
- */
+ /** Checks whether the input is internal or not. */
public static boolean isInternalTvInput(Context context, String inputId) {
- return context.getPackageName().equals(ComponentName.unflattenFromString(inputId)
- .getPackageName());
+ return context.getPackageName()
+ .equals(ComponentName.unflattenFromString(inputId).getPackageName());
}
- /**
- * Returns the TV input for the given {@code program}.
- */
+ /** Returns the TV input for the given {@code program}. */
@Nullable
public static TvInputInfo getTvInputInfoForProgram(Context context, Program program) {
- if (!Program.isValid(program)) {
+ if (!Program.isProgramValid(program)) {
return null;
}
return getTvInputInfoForChannelId(context, program.getChannelId());
}
- /**
- * Returns the TV input for the given channel ID.
- */
+ /** Returns the TV input for the given channel ID. */
@Nullable
public static TvInputInfo getTvInputInfoForChannelId(Context context, long channelId) {
- ApplicationSingletons appSingletons = TvApplication.getSingletons(context);
- Channel channel = appSingletons.getChannelDataManager().getChannel(channelId);
+ TvSingletons tvSingletons = TvSingletons.getSingletons(context);
+ Channel channel = tvSingletons.getChannelDataManager().getChannel(channelId);
if (channel == null) {
return null;
}
- return appSingletons.getTvInputManagerHelper().getTvInputInfo(channel.getInputId());
+ return tvSingletons.getTvInputManagerHelper().getTvInputInfo(channel.getInputId());
}
- /**
- * Returns the {@link TvInputInfo} for the given input ID.
- */
+ /** Returns the {@link TvInputInfo} for the given input ID. */
@Nullable
public static TvInputInfo getTvInputInfoForInputId(Context context, String inputId) {
- return TvApplication.getSingletons(context).getTvInputManagerHelper()
+ return TvSingletons.getSingletons(context)
+ .getTvInputManagerHelper()
.getTvInputInfo(inputId);
}
- /**
- * Deletes a file or a directory.
- */
- public static void deleteDirOrFile(File fileOrDirectory) {
- if (fileOrDirectory.isDirectory()) {
- for (File child : fileOrDirectory.listFiles()) {
- deleteDirOrFile(child);
- }
- }
- fileOrDirectory.delete();
- }
-
- /**
- * Checks whether a given package is in our bundled package set.
- */
- public static boolean isInBundledPackageSet(String packageName) {
- return BUNDLED_PACKAGE_SET.contains(packageName);
- }
-
- /**
- * Checks whether a given input is a bundled input.
- */
- public static boolean isBundledInput(String inputId) {
- for (String prefix : BUNDLED_PACKAGE_SET) {
- if (inputId.startsWith(prefix + "/")) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Returns the canonical genre ID's from the {@code genres}.
- */
+ /** Returns the canonical genre ID's from the {@code genres}. */
public static int[] getCanonicalGenreIds(String genres) {
if (TextUtils.isEmpty(genres)) {
return null;
@@ -878,9 +855,7 @@ public class Utils {
return getCanonicalGenreIds(Genres.decode(genres));
}
- /**
- * Returns the canonical genre ID's from the {@code genres}.
- */
+ /** Returns the canonical genre ID's from the {@code genres}. */
public static int[] getCanonicalGenreIds(String[] canonicalGenres) {
if (canonicalGenres != null && canonicalGenres.length > 0) {
int[] results = new int[canonicalGenres.length];
@@ -901,9 +876,7 @@ public class Utils {
return null;
}
- /**
- * Returns the canonical genres for database.
- */
+ /** Returns the canonical genres for database. */
public static String getCanonicalGenre(int[] canonicalGenreIds) {
if (canonicalGenreIds == null || canonicalGenreIds.length == 0) {
return null;
@@ -916,15 +889,8 @@ public class Utils {
}
/**
- * Returns true if the current user is a developer.
- */
- public static boolean isDeveloper() {
- return BuildConfig.ENG || Experiments.ENABLE_DEVELOPER_FEATURES.get();
- }
-
- /**
- * Runs the method in main thread. If the current thread is not main thread, block it util
- * the method is finished.
+ * Runs the method in main thread. If the current thread is not main thread, block it util the
+ * method is finished.
*/
public static void runInMainThreadAndWait(Runnable runnable) {
if (Looper.myLooper() == Looper.getMainLooper()) {
diff --git a/src/com/android/tv/util/ViewCache.java b/src/com/android/tv/util/ViewCache.java
index ed9a8ff6..b8bdb6b8 100644
--- a/src/com/android/tv/util/ViewCache.java
+++ b/src/com/android/tv/util/ViewCache.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2017 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.content.Context;
@@ -5,22 +20,17 @@ import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-
import java.util.ArrayList;
-/**
- * A cache for the views.
- */
+/** A cache for the views. */
public class ViewCache {
- private final static SparseArray<ArrayList<View>> mViews = new SparseArray();
+ private static final SparseArray<ArrayList<View>> mViews = new SparseArray();
private static ViewCache sViewCache;
- private ViewCache() { }
+ private ViewCache() {}
- /**
- * Returns an instance of the view cache.
- */
+ /** Returns an instance of the view cache. */
public static ViewCache getInstance() {
if (sViewCache == null) {
sViewCache = new ViewCache();
@@ -28,16 +38,12 @@ public class ViewCache {
return sViewCache;
}
- /**
- * Returns if the view cache is empty.
- */
+ /** Returns if the view cache is empty. */
public boolean isEmpty() {
return mViews.size() == 0;
}
- /**
- * Stores a view into this view cache.
- */
+ /** Stores a view into this view cache. */
public void putView(int resId, View view) {
ArrayList<View> views = mViews.get(resId);
if (views == null) {
@@ -47,9 +53,7 @@ public class ViewCache {
views.add(view);
}
- /**
- * Stores multi specific views into the view cache.
- */
+ /** Stores multi specific views into the view cache. */
public void putView(Context context, int resId, ViewGroup fakeParent, int num) {
LayoutInflater inflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@@ -64,9 +68,7 @@ public class ViewCache {
}
}
- /**
- * Returns the view for specific resource id.
- */
+ /** Returns the view for specific resource id. */
public View getView(int resId) {
ArrayList<View> views = mViews.get(resId);
if (views != null && !views.isEmpty()) {
@@ -80,9 +82,7 @@ public class ViewCache {
}
}
- /**
- * Returns the view if exists, or create a new view for the specific resource id.
- */
+ /** Returns the view if exists, or create a new view for the specific resource id. */
public View getOrCreateView(LayoutInflater inflater, int resId, ViewGroup container) {
View view = getView(resId);
if (view == null) {
@@ -91,9 +91,7 @@ public class ViewCache {
return view;
}
- /**
- * Clears the view cache.
- */
+ /** Clears the view cache. */
public void clear() {
mViews.clear();
}
diff --git a/src/com/android/tv/util/account/AccountHelper.java b/src/com/android/tv/util/account/AccountHelper.java
new file mode 100644
index 00000000..e98b42ec
--- /dev/null
+++ b/src/com/android/tv/util/account/AccountHelper.java
@@ -0,0 +1,38 @@
+/*
+ * 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.util.account;
+
+import android.accounts.Account;
+import android.support.annotation.Nullable;
+
+/** Helper methods for getting and selecting a user account. */
+public interface AccountHelper {
+ /** Returns the currently selected account or {@code null} if none is selected. */
+ @Nullable
+ Account getSelectedAccount();
+ /**
+ * Selects the first account available.
+ *
+ * @return selected account or {@code null} if none is selected.
+ */
+ @Nullable
+ Account selectFirstAccount();
+
+ /** Returns all eligible accounts . */
+ @Nullable
+ Account getFirstEligibleAccount();
+}
diff --git a/src/com/android/tv/util/AccountHelper.java b/src/com/android/tv/util/account/AccountHelperImpl.java
index ece13de1..58fbd27e 100644
--- a/src/com/android/tv/util/AccountHelper.java
+++ b/src/com/android/tv/util/account/AccountHelperImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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,43 +14,32 @@
* limitations under the License.
*/
-package com.android.tv.util;
+package com.android.tv.util.account;
import android.accounts.Account;
import android.content.Context;
import android.content.SharedPreferences;
-import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
-import android.util.Log;
-
-import java.util.Arrays;
-
-/**
- * Helper methods for getting and selecting a user account.
- */
-public class AccountHelper {
- private static final String TAG = "AccountHelper";
- private static final boolean DEBUG = false;
+/** Helper methods for getting and selecting a user account. */
+public class AccountHelperImpl implements com.android.tv.util.account.AccountHelper {
private static final String SELECTED_ACCOUNT = "android.tv.livechannels.selected_account";
- private final Context mContext;
+ protected final Context mContext;
private final SharedPreferences mDefaultPreferences;
- @Nullable
- private Account mSelectedAccount;
+ @Nullable private Account mSelectedAccount;
- public AccountHelper(Context context) {
+ public AccountHelperImpl(Context context) {
mContext = context.getApplicationContext();
mDefaultPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
}
- /**
- * Returns the currently selected account or {@code null} if none is selected.
- */
+ /** Returns the currently selected account or {@code null} if none is selected. */
+ @Override
@Nullable
- public Account getSelectedAccount() {
+ public final Account getSelectedAccount() {
String accountId = mDefaultPreferences.getString(SELECTED_ACCOUNT, null);
if (accountId == null) {
return null;
@@ -68,9 +57,11 @@ public class AccountHelper {
}
/**
- * Returns all eligible accounts .
+ * Returns all eligible accounts.
+ *
+ * <p>Override this method to return the accounts needed.
*/
- private Account[] getEligibleAccounts() {
+ protected Account[] getEligibleAccounts() {
return new Account[0];
}
@@ -79,8 +70,9 @@ public class AccountHelper {
*
* @return selected account or {@code null} if none is selected.
*/
+ @Override
@Nullable
- public Account selectFirstAccount() {
+ public final Account selectFirstAccount() {
Account account = getFirstEligibleAccount();
if (account != null) {
selectAccount(account);
@@ -93,19 +85,17 @@ public class AccountHelper {
*
* @return first account or {@code null} if none is eligible.
*/
+ @Override
@Nullable
- public Account getFirstEligibleAccount() {
+ public final Account getFirstEligibleAccount() {
Account[] accounts = getEligibleAccounts();
return accounts.length == 0 ? null : accounts[0];
}
- /**
- * Sets the given account as the selected account.
- */
+ /** Sets the given account as the selected account. */
private void selectAccount(Account account) {
- SharedPreferences defaultPreferences = PreferenceManager
- .getDefaultSharedPreferences(mContext);
+ SharedPreferences defaultPreferences =
+ PreferenceManager.getDefaultSharedPreferences(mContext);
defaultPreferences.edit().putString(SELECTED_ACCOUNT, account.name).commit();
}
}
-
diff --git a/src/com/android/tv/util/BitmapUtils.java b/src/com/android/tv/util/images/BitmapUtils.java
index fbaab023..d6bd5a31 100644
--- a/src/com/android/tv/util/BitmapUtils.java
+++ b/src/com/android/tv/util/images/BitmapUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.tv.util;
+package com.android.tv.util.images;
import android.content.ContentResolver;
import android.content.Context;
@@ -29,7 +29,7 @@ import android.net.Uri;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
-
+import com.android.tv.common.util.NetworkTrafficTags;
import java.io.BufferedInputStream;
import java.io.Closeable;
import java.io.IOException;
@@ -45,12 +45,14 @@ public final class BitmapUtils {
// The value of 64K, for MARK_READ_LIMIT, is chosen to be eight times the default buffer size
// of BufferedInputStream (8K) allowing it to double its buffers three times. Also it is a
// fairly reasonable value, not using too much memory and being large enough for most cases.
- private static final int MARK_READ_LIMIT = 64 * 1024; // 64K
+ private static final int MARK_READ_LIMIT = 64 * 1024; // 64K
- private static final int CONNECTION_TIMEOUT_MS_FOR_URLCONNECTION = 3000; // 3 sec
- private static final int READ_TIMEOUT_MS_FOR_URLCONNECTION = 10000; // 10 sec
+ private static final int CONNECTION_TIMEOUT_MS_FOR_URLCONNECTION = 3000; // 3 sec
+ private static final int READ_TIMEOUT_MS_FOR_URLCONNECTION = 10000; // 10 sec
- private BitmapUtils() { /* cannot be instantiated */ }
+ private BitmapUtils() {
+ /* cannot be instantiated */
+ }
public static Bitmap scaleBitmap(Bitmap bm, int maxWidth, int maxHeight) {
Rect rect = calculateNewSize(bm, maxWidth, maxHeight);
@@ -59,7 +61,8 @@ public final class BitmapUtils {
public static Bitmap getScaledMutableBitmap(Bitmap bm, int maxWidth, int maxHeight) {
Bitmap scaledBitmap = scaleBitmap(bm, maxWidth, maxHeight);
- return scaledBitmap.isMutable() ? scaledBitmap
+ return scaledBitmap.isMutable()
+ ? scaledBitmap
: scaledBitmap.copy(Bitmap.Config.ARGB_8888, true);
}
@@ -77,17 +80,17 @@ public final class BitmapUtils {
return rect;
}
- public static ScaledBitmapInfo createScaledBitmapInfo(String id, Bitmap bm, int maxWidth,
- int maxHeight) {
- return new ScaledBitmapInfo(id, scaleBitmap(bm, maxWidth, maxHeight),
+ public static ScaledBitmapInfo createScaledBitmapInfo(
+ String id, Bitmap bm, int maxWidth, int maxHeight) {
+ return new ScaledBitmapInfo(
+ id,
+ scaleBitmap(bm, maxWidth, maxHeight),
calculateInSampleSize(bm.getWidth(), bm.getHeight(), maxWidth, maxHeight));
}
- /**
- * Decode large sized bitmap into requested size.
- */
- public static ScaledBitmapInfo decodeSampledBitmapFromUriString(Context context,
- String uriString, int reqWidth, int reqHeight) {
+ /** Decode large sized bitmap into requested size. */
+ public static ScaledBitmapInfo decodeSampledBitmapFromUriString(
+ Context context, String uriString, int reqWidth, int reqHeight) {
if (TextUtils.isEmpty(uriString)) {
return null;
}
@@ -162,8 +165,8 @@ public final class BitmapUtils {
return urlConnection;
}
- private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth,
- int reqHeight) {
+ private static int calculateInSampleSize(
+ BitmapFactory.Options options, int reqWidth, int reqHeight) {
return calculateInSampleSize(options.outWidth, options.outHeight, reqWidth, reqHeight);
}
@@ -187,7 +190,7 @@ public final class BitmapUtils {
closeable.close();
} catch (IOException e) {
// Log and continue.
- Log.w(TAG,"Error closing " + closeable, e);
+ Log.w(TAG, "Error closing " + closeable, e);
}
}
if (urlConnection instanceof HttpURLConnection) {
@@ -195,21 +198,13 @@ public final class BitmapUtils {
}
}
- /**
- * A wrapper class which contains the loaded bitmap and the scaling information.
- */
+ /** A wrapper class which contains the loaded bitmap and the scaling information. */
public static class ScaledBitmapInfo {
- /**
- * The id of bitmap, usually this is the URI of the original.
- */
- @NonNull
- public final String id;
+ /** The id of bitmap, usually this is the URI of the original. */
+ @NonNull public final String id;
- /**
- * The loaded bitmap object.
- */
- @NonNull
- public final Bitmap bitmap;
+ /** The loaded bitmap object. */
+ @NonNull public final Bitmap bitmap;
/**
* The scaling factor to the original bitmap. It should be an positive integer.
@@ -222,8 +217,8 @@ public final class BitmapUtils {
* A constructor.
*
* @param bitmap The loaded bitmap object.
- * @param inSampleSize The sampling size.
- * See {@link android.graphics.BitmapFactory.Options#inSampleSize}
+ * @param inSampleSize The sampling size. See {@link
+ * android.graphics.BitmapFactory.Options#inSampleSize}
*/
public ScaledBitmapInfo(@NonNull String id, @NonNull Bitmap bitmap, int inSampleSize) {
this.id = id;
@@ -232,10 +227,9 @@ public final class BitmapUtils {
}
/**
- * Checks if the bitmap needs to be reloaded. The scaling is performed by power 2.
- * The bitmap can be reloaded only if the required width or height is greater then or equal
- * to the existing bitmap.
- * If the full sized bitmap is already loaded, returns {@code false}.
+ * Checks if the bitmap needs to be reloaded. The scaling is performed by power 2. The
+ * bitmap can be reloaded only if the required width or height is greater then or equal to
+ * the existing bitmap. If the full sized bitmap is already loaded, returns {@code false}.
*
* @see android.graphics.BitmapFactory.Options#inSampleSize
*/
@@ -245,26 +239,41 @@ public final class BitmapUtils {
return false;
}
Rect size = calculateNewSize(this.bitmap, reqWidth, reqHeight);
- boolean reload = (size.right >= bitmap.getWidth() * 2
- || size.bottom >= bitmap.getHeight() * 2);
+ boolean reload =
+ (size.right >= bitmap.getWidth() * 2 || size.bottom >= bitmap.getHeight() * 2);
if (DEBUG) {
- Log.d(TAG, "needToReload(" + reqWidth + ", " + reqHeight + ")=" + reload
- + " because the new size would be " + size + " for " + this);
+ Log.d(
+ TAG,
+ "needToReload("
+ + reqWidth
+ + ", "
+ + reqHeight
+ + ")="
+ + reload
+ + " because the new size would be "
+ + size
+ + " for "
+ + this);
}
return reload;
}
- /**
- * Returns {@code true} if a request the size of {@code other} would need a reload.
- */
- public boolean needToReload(ScaledBitmapInfo other){
+ /** Returns {@code true} if a request the size of {@code other} would need a reload. */
+ public boolean needToReload(ScaledBitmapInfo other) {
return needToReload(other.bitmap.getWidth(), other.bitmap.getHeight());
}
@Override
public String toString() {
- return "ScaledBitmapInfo[" + id + "](in=" + inSampleSize + ", w=" + bitmap.getWidth()
- + ", h=" + bitmap.getHeight() + ")";
+ return "ScaledBitmapInfo["
+ + id
+ + "](in="
+ + inSampleSize
+ + ", w="
+ + bitmap.getWidth()
+ + ", h="
+ + bitmap.getHeight()
+ + ")";
}
}
diff --git a/src/com/android/tv/util/ImageCache.java b/src/com/android/tv/util/images/ImageCache.java
index b413c364..e260d67a 100644
--- a/src/com/android/tv/util/ImageCache.java
+++ b/src/com/android/tv/util/images/ImageCache.java
@@ -14,18 +14,15 @@
* limitations under the License.
*/
-package com.android.tv.util;
+package com.android.tv.util.images;
import android.support.annotation.VisibleForTesting;
import android.util.Log;
import android.util.LruCache;
+import com.android.tv.common.memory.MemoryManageable;
+import com.android.tv.util.images.BitmapUtils.ScaledBitmapInfo;
-import com.android.tv.common.MemoryManageable;
-import com.android.tv.util.BitmapUtils.ScaledBitmapInfo;
-
-/**
- * A convenience class for caching bitmap.
- */
+/** A convenience class for caching bitmap. */
public class ImageCache implements MemoryManageable {
private static final float MAX_CACHE_SIZE_PERCENT = 0.8f;
private static final float MIN_CACHE_SIZE_PERCENT = 0.05f;
@@ -48,16 +45,17 @@ public class ImageCache implements MemoryManageable {
if (DEBUG) {
Log.d(TAG, "Memory cache created (size = " + memCacheSize + " Kbytes)");
}
- mMemoryCache = new LruCache<String, ScaledBitmapInfo>(memCacheSize) {
- /**
- * Measure item size in kilobytes rather than units which is more practical for a bitmap
- * cache
- */
- @Override
- protected int sizeOf(String key, ScaledBitmapInfo bitmapInfo) {
- return (bitmapInfo.bitmap.getByteCount() + 1023) / 1024;
- }
- };
+ mMemoryCache =
+ new LruCache<String, ScaledBitmapInfo>(memCacheSize) {
+ /**
+ * Measure item size in kilobytes rather than units which is more practical for
+ * a bitmap cache
+ */
+ @Override
+ protected int sizeOf(String key, ScaledBitmapInfo bitmapInfo) {
+ return (bitmapInfo.bitmap.getByteCount() + 1023) / 1024;
+ }
+ };
}
private static ImageCache sImageCache;
@@ -67,7 +65,7 @@ public class ImageCache implements MemoryManageable {
* param.
*
* @param memCacheSizePercent The cache size as a percent of available app memory. Should be in
- * range of MIN_CACHE_SIZE_PERCENT(0.05) ~ MAX_CACHE_SIZE_PERCENT(0.8).
+ * range of MIN_CACHE_SIZE_PERCENT(0.05) ~ MAX_CACHE_SIZE_PERCENT(0.8).
* @return An existing retained ImageCache object or a new one if one did not exist
*/
public static synchronized ImageCache getInstance(float memCacheSizePercent) {
@@ -82,7 +80,6 @@ public class ImageCache implements MemoryManageable {
return new ImageCache(memCacheSizePercent);
}
-
/**
* Returns an existing ImageCache, if it doesn't exist, a new one is created using
* DEFAULT_CACHE_SIZE_PERCENT (0.1).
@@ -96,8 +93,8 @@ public class ImageCache implements MemoryManageable {
/**
* Adds a bitmap to memory cache.
*
- * <p>If there is an existing bitmap only replace it if
- * {@link ScaledBitmapInfo#needToReload(ScaledBitmapInfo)} is true.
+ * <p>If there is an existing bitmap only replace it if {@link
+ * ScaledBitmapInfo#needToReload(ScaledBitmapInfo)} is true.
*
* @param bitmapInfo The {@link ScaledBitmapInfo} object to store
*/
@@ -112,14 +109,25 @@ public class ImageCache implements MemoryManageable {
if (old != null && !old.needToReload(bitmapInfo)) {
mMemoryCache.put(key, old);
if (DEBUG) {
- Log.d(TAG,
- "Kept original " + old + " in memory cache because it was larger than "
- + bitmapInfo + ".");
+ Log.d(
+ TAG,
+ "Kept original "
+ + old
+ + " in memory cache because it was larger than "
+ + bitmapInfo
+ + ".");
}
} else {
if (DEBUG) {
- Log.d(TAG, "Add " + bitmapInfo + " to memory cache. Current size is " +
- mMemoryCache.size() + " / " + mMemoryCache.maxSize() + " Kbytes");
+ Log.d(
+ TAG,
+ "Add "
+ + bitmapInfo
+ + " to memory cache. Current size is "
+ + mMemoryCache.size()
+ + " / "
+ + mMemoryCache.maxSize()
+ + " Kbytes");
}
}
}
@@ -158,19 +166,21 @@ public class ImageCache implements MemoryManageable {
* Calculates the memory cache size based on a percentage of the max available VM memory. Eg.
* setting percent to 0.2 would set the memory cache to one fifth of the available memory.
* Throws {@link IllegalArgumentException} if percent is < 0.05 or > .8. memCacheSize is stored
- * in kilobytes instead of bytes as this will eventually be passed to construct a LruCache
- * which takes an int in its constructor. This value should be chosen carefully based on a
- * number of factors Refer to the corresponding Android Training class for more discussion:
+ * in kilobytes instead of bytes as this will eventually be passed to construct a LruCache which
+ * takes an int in its constructor. This value should be chosen carefully based on a number of
+ * factors Refer to the corresponding Android Training class for more discussion:
* http://developer.android.com/training/displaying-bitmaps/
*
* @param percent Percent of available app memory to use to size memory cache.
*/
public static int calculateMemCacheSize(float percent) {
if (percent < MIN_CACHE_SIZE_PERCENT || percent > MAX_CACHE_SIZE_PERCENT) {
- throw new IllegalArgumentException("setMemCacheSizePercent - percent must be "
- + "between 0.05 and 0.8 (inclusive)");
+ throw new IllegalArgumentException(
+ "setMemCacheSizePercent - percent must be "
+ + "between 0.05 and 0.8 (inclusive)");
}
- return Math.max(MIN_CACHE_SIZE_KBYTES,
+ return Math.max(
+ MIN_CACHE_SIZE_KBYTES,
Math.round(percent * Runtime.getRuntime().maxMemory() / 1024));
}
diff --git a/src/com/android/tv/util/ImageLoader.java b/src/com/android/tv/util/images/ImageLoader.java
index 86bb94c1..e844e2ca 100644
--- a/src/com/android/tv/util/ImageLoader.java
+++ b/src/com/android/tv/util/images/ImageLoader.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.tv.util;
+package com.android.tv.util.images;
import android.content.Context;
import android.graphics.Bitmap;
@@ -30,10 +30,9 @@ 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.util.BitmapUtils.ScaledBitmapInfo;
-
+import com.android.tv.common.concurrent.NamedThreadFactory;
+import com.android.tv.util.images.BitmapUtils.ScaledBitmapInfo;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
@@ -47,8 +46,8 @@ 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
- * handles things like using a memory cache, running the work in a background thread.
+ * This class wraps up completing some arbitrary long running work when loading a bitmap. It handles
+ * things like using a memory cache, running the work in a background thread.
*/
public final class ImageLoader {
private static final String TAG = "ImageLoader";
@@ -69,19 +68,23 @@ public final class ImageLoader {
/**
* 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>{@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 threadPoolExecutor =
+ new ThreadPoolExecutor(
+ CORE_POOL_SIZE,
+ MAXIMUM_POOL_SIZE,
+ KEEP_ALIVE_SECONDS,
+ TimeUnit.SECONDS,
+ sPoolWorkQueue,
+ sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
IMAGE_THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
@@ -91,28 +94,26 @@ public final class ImageLoader {
/**
* Handles when image loading is finished.
*
- * <p>Use this to prevent leaking an Activity or other Context while image loading is
- * still pending. When you extend this class you <strong>MUST NOT</strong> use a non static
- * inner class, or the containing object will still be leaked.
+ * <p>Use this to prevent leaking an Activity or other Context while image loading is still
+ * pending. When you extend this class you <strong>MUST NOT</strong> use a non static inner
+ * class, or the containing object will still be leaked.
*/
@UiThread
- public static abstract class ImageLoaderCallback<T> {
+ public abstract static class ImageLoaderCallback<T> {
private final WeakReference<T> mWeakReference;
/**
* Creates an callback keeping a weak reference to {@code referent}.
*
- * <p> If the "referent" is no longer valid, it no longer makes sense to run the
- * callback. The referent is the View, or Activity or whatever that actually needs to
- * receive the Bitmap. If the referent has been GC, then no need to run the callback.
+ * <p>If the "referent" is no longer valid, it no longer makes sense to run the callback.
+ * The referent is the View, or Activity or whatever that actually needs to receive the
+ * Bitmap. If the referent has been GC, then no need to run the callback.
*/
public ImageLoaderCallback(T referent) {
mWeakReference = new WeakReference<>(referent);
}
- /**
- * Called when bitmap is loaded.
- */
+ /** Called when bitmap is loaded. */
private void onBitmapLoaded(@Nullable Bitmap bitmap) {
T referent = mWeakReference.get();
if (referent != null) {
@@ -122,9 +123,7 @@ public final class ImageLoader {
}
}
- /**
- * Called when bitmap is loaded if the weak reference is still valid.
- */
+ /** Called when bitmap is loaded if the weak reference is still valid. */
public abstract void onBitmapLoaded(T referent, @Nullable Bitmap bitmap);
}
@@ -134,62 +133,80 @@ public final class ImageLoader {
* Preload a bitmap image into the cache.
*
* <p>Not to make heavy CPU load, AsyncTask.SERIAL_EXECUTOR is used for the image loading.
+ *
* <p>This method is thread safe.
*/
- public static void prefetchBitmap(Context context, final String uriString, final int maxWidth,
- final int maxHeight) {
+ public static void prefetchBitmap(
+ Context context, final String uriString, final int maxWidth, final int maxHeight) {
if (DEBUG) Log.d(TAG, "prefetchBitmap() " + uriString);
if (Looper.getMainLooper() == Looper.myLooper()) {
doLoadBitmap(context, uriString, maxWidth, maxHeight, null, AsyncTask.SERIAL_EXECUTOR);
} else {
final Context appContext = context.getApplicationContext();
- getMainHandler().post(new Runnable() {
- @Override
- @MainThread
- public void run() {
- // Calling from the main thread prevents a ConcurrentModificationException
- // in LoadBitmapTask.onPostExecute
- doLoadBitmap(appContext, uriString, maxWidth, maxHeight, null,
- AsyncTask.SERIAL_EXECUTOR);
- }
- });
+ getMainHandler()
+ .post(
+ new Runnable() {
+ @Override
+ @MainThread
+ public void run() {
+ // Calling from the main thread prevents a
+ // ConcurrentModificationException
+ // in LoadBitmapTask.onPostExecute
+ doLoadBitmap(
+ appContext,
+ uriString,
+ maxWidth,
+ maxHeight,
+ null,
+ AsyncTask.SERIAL_EXECUTOR);
+ }
+ });
}
}
/**
* Load a bitmap image with the cache using a ContentResolver.
*
- * <p><b>Note</b> that the callback will be called synchronously if the bitmap already is in
- * the cache.
+ * <p><b>Note</b> that the callback will be called synchronously if the bitmap already is in the
+ * cache.
*
* @return {@code true} if the load is complete and the callback is executed.
*/
@UiThread
- public static boolean loadBitmap(Context context, String uriString,
- ImageLoaderCallback callback) {
+ public static boolean loadBitmap(
+ Context context, String uriString, ImageLoaderCallback callback) {
return loadBitmap(context, uriString, Integer.MAX_VALUE, Integer.MAX_VALUE, callback);
}
/**
* Load a bitmap image with the cache and resize it with given params.
*
- * <p><b>Note</b> that the callback will be called synchronously if the bitmap already is in
- * the cache.
+ * <p><b>Note</b> that the callback will be called synchronously if the bitmap already is in the
+ * cache.
*
* @return {@code true} if the load is complete and the callback is executed.
*/
@UiThread
- public static boolean loadBitmap(Context context, String uriString, int maxWidth, int maxHeight,
+ public static boolean loadBitmap(
+ Context context,
+ String uriString,
+ int maxWidth,
+ int maxHeight,
ImageLoaderCallback callback) {
if (DEBUG) {
Log.d(TAG, "loadBitmap() " + uriString);
}
- return doLoadBitmap(context, uriString, maxWidth, maxHeight, callback,
- IMAGE_THREAD_POOL_EXECUTOR);
+ return doLoadBitmap(
+ context, uriString, maxWidth, maxHeight, callback, IMAGE_THREAD_POOL_EXECUTOR);
}
- private static boolean doLoadBitmap(Context context, String uriString,
- int maxWidth, int maxHeight, ImageLoaderCallback callback, Executor executor) {
+ private static boolean doLoadBitmap(
+ Context context,
+ String uriString,
+ int maxWidth,
+ int maxHeight,
+ ImageLoaderCallback callback,
+ Executor executor) {
// Check the cache before creating a Task. The cache will be checked again in doLoadBitmap
// but checking a cache is much cheaper than creating an new task.
ImageCache imageCache = ImageCache.getInstance();
@@ -200,7 +217,9 @@ public final class ImageLoader {
}
return true;
}
- return doLoadBitmap(callback, executor,
+ return doLoadBitmap(
+ callback,
+ executor,
new LoadBitmapFromUriTask(context, imageCache, uriString, maxWidth, maxHeight));
}
@@ -219,12 +238,10 @@ public final class ImageLoader {
return doLoadBitmap(callback, IMAGE_THREAD_POOL_EXECUTOR, loadBitmapTask);
}
- /**
- * @return {@code true} if the load is complete and the callback is executed.
- */
+ /** @return {@code true} if the load is complete and the callback is executed. */
@UiThread
- private static boolean doLoadBitmap(ImageLoaderCallback callback, Executor executor,
- LoadBitmapTask loadBitmapTask) {
+ private static boolean doLoadBitmap(
+ ImageLoaderCallback callback, Executor executor, LoadBitmapTask loadBitmapTask) {
ScaledBitmapInfo bitmapInfo = loadBitmapTask.getFromCache();
boolean needToReload = loadBitmapTask.isReloadNeeded();
if (bitmapInfo != null && !needToReload) {
@@ -259,7 +276,7 @@ public final class ImageLoader {
*
* <p>Implement {@link #doGetBitmapInBackground} to do the actual loading.
*/
- public static abstract class LoadBitmapTask extends AsyncTask<Void, Void, ScaledBitmapInfo> {
+ public abstract static class LoadBitmapTask extends AsyncTask<Void, Void, ScaledBitmapInfo> {
protected final Context mAppContext;
protected final int mMaxWidth;
protected final int mMaxHeight;
@@ -273,24 +290,28 @@ public final class ImageLoader {
*/
private boolean isReloadNeeded() {
ScaledBitmapInfo bitmapInfo = getFromCache();
- boolean needToReload = bitmapInfo != null && bitmapInfo
- .needToReload(mMaxWidth, mMaxHeight);
+ boolean needToReload =
+ bitmapInfo != null && bitmapInfo.needToReload(mMaxWidth, mMaxHeight);
if (DEBUG) {
if (needToReload) {
- Log.d(TAG, "Bitmap needs to be reloaded. {"
- + "originalWidth=" + bitmapInfo.bitmap.getWidth()
- + ", originalHeight=" + bitmapInfo.bitmap.getHeight()
- + ", reqWidth=" + mMaxWidth
- + ", reqHeight=" + mMaxHeight
- + "}");
+ Log.d(
+ TAG,
+ "Bitmap needs to be reloaded. {"
+ + "originalWidth="
+ + bitmapInfo.bitmap.getWidth()
+ + ", originalHeight="
+ + bitmapInfo.bitmap.getHeight()
+ + ", reqWidth="
+ + mMaxWidth
+ + ", reqHeight="
+ + mMaxHeight
+ + "}");
}
}
return needToReload;
}
- /**
- * Checks if a reload would be needed if the results of other was available.
- */
+ /** Checks if a reload would be needed if the results of other was available. */
private boolean isReloadNeeded(LoadBitmapTask other) {
return (other.mMaxHeight != Integer.MAX_VALUE && mMaxHeight >= other.mMaxHeight * 2)
|| (other.mMaxWidth != Integer.MAX_VALUE && mMaxWidth >= other.mMaxWidth * 2);
@@ -301,11 +322,14 @@ public final class ImageLoader {
return mImageCache.get(mKey);
}
- public LoadBitmapTask(Context context, ImageCache imageCache, String key, int maxHeight,
- int maxWidth) {
+ public LoadBitmapTask(
+ Context context, ImageCache imageCache, String key, int maxHeight, int maxWidth) {
if (maxWidth == 0 || maxHeight == 0) {
throw new IllegalArgumentException(
- "Image size should not be 0. {width=" + maxWidth + ", height=" + maxHeight
+ "Image size should not be 0. {width="
+ + maxWidth
+ + ", height="
+ + maxHeight
+ "}");
}
mAppContext = context.getApplicationContext();
@@ -315,9 +339,7 @@ public final class ImageLoader {
mMaxWidth = maxWidth;
}
- /**
- * Loads the bitmap returning a possibly scaled down version.
- */
+ /** Loads the bitmap returning a possibly scaled down version. */
@Nullable
@WorkerThread
public abstract ScaledBitmapInfo doGetBitmapInBackground();
@@ -352,40 +374,48 @@ public final class ImageLoader {
@Override
public String toString() {
- return this.getClass().getSimpleName() + "(" + mKey + " " + mMaxWidth + "x" + mMaxHeight
+ return this.getClass().getSimpleName()
+ + "("
+ + mKey
+ + " "
+ + mMaxWidth
+ + "x"
+ + mMaxHeight
+ ")";
}
}
private static final class LoadBitmapFromUriTask extends LoadBitmapTask {
- private LoadBitmapFromUriTask(Context context, ImageCache imageCache, String uriString,
- int maxWidth, int maxHeight) {
+ private LoadBitmapFromUriTask(
+ Context context,
+ ImageCache imageCache,
+ String uriString,
+ int maxWidth,
+ int maxHeight) {
super(context, imageCache, uriString, maxHeight, maxWidth);
}
@Override
@Nullable
public final ScaledBitmapInfo doGetBitmapInBackground() {
- return BitmapUtils
- .decodeSampledBitmapFromUriString(mAppContext, getKey(), mMaxWidth, mMaxHeight);
+ return BitmapUtils.decodeSampledBitmapFromUriString(
+ mAppContext, getKey(), mMaxWidth, mMaxHeight);
}
}
- /**
- * Loads and caches the logo for a given {@link TvInputInfo}
- */
+ /** Loads and caches the logo for a given {@link TvInputInfo} */
public static final class LoadTvInputLogoTask extends LoadBitmapTask {
private final TvInputInfo mInfo;
public LoadTvInputLogoTask(Context context, ImageCache cache, TvInputInfo info) {
- super(context,
+ super(
+ context,
cache,
getTvInputLogoKey(info.getId()),
context.getResources()
.getDimensionPixelSize(R.dimen.channel_banner_input_logo_size),
context.getResources()
- .getDimensionPixelSize(R.dimen.channel_banner_input_logo_size)
- );
+ .getDimensionPixelSize(R.dimen.channel_banner_input_logo_size));
mInfo = info;
}
@@ -403,9 +433,7 @@ public final class ImageLoader {
return BitmapUtils.createScaledBitmapInfo(getKey(), original, mMaxWidth, mMaxHeight);
}
- /**
- * Returns key of TV input logo.
- */
+ /** Returns key of TV input logo. */
public static String getTvInputLogoKey(String inputId) {
return inputId + "-logo";
}
@@ -418,6 +446,5 @@ public final class ImageLoader {
return sMainHandler;
}
- private ImageLoader() {
- }
+ private ImageLoader() {}
}
diff --git a/tests/common/Android.mk b/tests/common/Android.mk
index 27c9f031..2e80aa2b 100644
--- a/tests/common/Android.mk
+++ b/tests/common/Android.mk
@@ -8,8 +8,12 @@ LOCAL_SRC_FILES := \
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-annotations \
+ android-support-test \
+ guava \
mockito-target \
- ub-uiautomator
+ platform-robolectric-3.6.1-prebuilt \
+ truth-0-36-prebuilt-jar \
+ ub-uiautomator \
# Link tv-common as shared library to avoid the problem of initialization of the constants
LOCAL_JAVA_LIBRARIES := tv-common
@@ -17,7 +21,7 @@ LOCAL_JAVA_LIBRARIES := tv-common
LOCAL_INSTRUMENTATION_FOR := LiveTv
LOCAL_MODULE := tv-test-common
LOCAL_MODULE_TAGS := optional
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_AIDL_INCLUDES += $(LOCAL_PATH)/src
diff --git a/tests/common/AndroidManifest.xml b/tests/common/AndroidManifest.xml
index f3ed9a9f..8afd8dc9 100644
--- a/tests/common/AndroidManifest.xml
+++ b/tests/common/AndroidManifest.xml
@@ -18,6 +18,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.tv.testing"
android:versionCode="1">
- <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="21"/>
+ <uses-sdk android:targetSdkVersion="26" android:minSdkVersion="21"/>
<application />
</manifest>
diff --git a/tests/common/src/com/android/tv/input/TunerHelper.java b/tests/common/src/com/android/tv/input/TunerHelper.java
index 126d5027..08c4b041 100644
--- a/tests/common/src/com/android/tv/input/TunerHelper.java
+++ b/tests/common/src/com/android/tv/input/TunerHelper.java
@@ -19,14 +19,11 @@ package com.android.tv.input;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.util.Log;
-
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
-/**
- * A class to manage fake tuners for the tune and the recording.
- */
+/** A class to manage fake tuners for the tune and the recording. */
public class TunerHelper {
private static final String TAG = "TunerHelper";
private static final boolean DEBUG = false;
@@ -38,9 +35,7 @@ public class TunerHelper {
mTunerCount = tunerCount;
}
- /**
- * Checks whether there are available tuners for the recording.
- */
+ /** Checks whether there are available tuners for the recording. */
public boolean tunerAvailableForRecording() {
if (mTuners.size() < mTunerCount) {
return true;
@@ -54,8 +49,8 @@ public class TunerHelper {
}
/**
- * Checks whether there is available tuner.
- * If there's available tuner, it is assigned to the channel.
+ * Checks whether there is available tuner. If there's available tuner, it is assigned to the
+ * channel.
*/
public boolean tune(@Nullable Uri channelUri, boolean forRecording) {
if (channelUri == null) {
@@ -82,9 +77,7 @@ public class TunerHelper {
return false;
}
- /**
- * Releases the tuner which was being used for the tune.
- */
+ /** Releases the tuner which was being used for the tune. */
public void stopTune(@Nullable Uri channelUri) {
if (channelUri == null) {
return;
@@ -110,9 +103,7 @@ public class TunerHelper {
}
}
- /**
- * Releases the tuner which was being used for the recording.
- */
+ /** Releases the tuner which was being used for the recording. */
public void stopRecording(@Nullable Uri channelUri) {
if (channelUri == null) {
return;
@@ -146,7 +137,7 @@ public class TunerHelper {
public boolean tuning;
public boolean recording;
- public Tuner (Uri channelUri, boolean forRecording) {
+ public Tuner(Uri channelUri, boolean forRecording) {
this.channelUri = channelUri;
this.tuning = !forRecording;
this.recording = forRecording;
diff --git a/tests/common/src/com/android/tv/testing/ChannelNumberSubject.java b/tests/common/src/com/android/tv/testing/ChannelNumberSubject.java
new file mode 100644
index 00000000..ba4662ee
--- /dev/null
+++ b/tests/common/src/com/android/tv/testing/ChannelNumberSubject.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 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.testing;
+
+import android.support.annotation.Nullable;
+import com.android.tv.data.ChannelNumber;
+import com.google.common.truth.ComparableSubject;
+import com.google.common.truth.FailureMetadata;
+import com.google.common.truth.Subject;
+import com.google.common.truth.Truth;
+
+/** Propositions for {@link ChannelNumber} subjects. */
+public final class ChannelNumberSubject
+ extends ComparableSubject<ChannelNumberSubject, ChannelNumber> {
+ private static final Subject.Factory<ChannelNumberSubject, ChannelNumber> FACTORY =
+ ChannelNumberSubject::new;
+
+ public static Subject.Factory<ChannelNumberSubject, ChannelNumber> channelNumbers() {
+ return FACTORY;
+ }
+
+ public static ChannelNumberSubject assertThat(@Nullable ChannelNumber actual) {
+ return Truth.assertAbout(channelNumbers()).that(actual);
+ }
+
+ public ChannelNumberSubject(FailureMetadata failureMetadata, @Nullable ChannelNumber subject) {
+ super(failureMetadata, subject);
+ }
+
+ public void displaysAs(int major) {
+ if (!getSubject().majorNumber.equals(Integer.toString(major))
+ || getSubject().hasDelimiter) {
+ fail("displaysAs", major);
+ }
+ }
+
+ public void displaysAs(int major, int minor) {
+ if (!getSubject().majorNumber.equals(Integer.toString(major))
+ || !getSubject().minorNumber.equals(Integer.toString(minor))
+ || !getSubject().hasDelimiter) {
+ fail("displaysAs", major + "-" + minor);
+ }
+ }
+
+ public void isEmpty() {
+ if (!getSubject().majorNumber.isEmpty()
+ || !getSubject().minorNumber.isEmpty()
+ || getSubject().hasDelimiter) {
+ fail("isEmpty");
+ }
+ }
+}
diff --git a/tests/common/src/com/android/tv/testing/ComparableTester.java b/tests/common/src/com/android/tv/testing/ComparableTester.java
index fe6e72f5..4328deb9 100644
--- a/tests/common/src/com/android/tv/testing/ComparableTester.java
+++ b/tests/common/src/com/android/tv/testing/ComparableTester.java
@@ -16,22 +16,19 @@
package com.android.tv.testing;
-import junit.framework.Assert;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
+import junit.framework.Assert;
/**
* Tester for {@link java.lang.Comparable}s.
*
- * <p>
- * To use, create a new {@link ComparableTester} and add comparable groups
- * where each group contains objects that are
- * {@link java.util.Comparator#compare(Object, Object)} == 0 to each other.
- * Groups are added in order asserting that all earlier groups have compare < 0
- * for all later groups.
+ * <p>To use, create a new {@link ComparableTester} and add comparable groups where each group
+ * contains objects that are {@link java.util.Comparator#compare(Object, Object)} == 0 to each
+ * other. Groups are added in order asserting that all earlier groups have compare < 0 for all later
+ * groups.
*
* <pre>{@code
* new ComparableTester<String>()
@@ -39,8 +36,7 @@ import java.util.List;
* .addEquivalentGroup("World", "wORLD")
* .addEquivalentGroup("ZEBRA")
* .test();
- * }
- * </pre>
+ * }</pre>
*
* @param <T> the type of objects to compare.
*/
@@ -74,8 +70,8 @@ public class ComparableTester<T extends Comparable<T>> {
assertMore(more, less, moreGroup, lessGroup);
}
- private void assertLess(int left, int right, Collection<T> leftGroup,
- Collection<T> rightGroup) {
+ private void assertLess(
+ int left, int right, Collection<T> leftGroup, Collection<T> rightGroup) {
int leftSub = 0;
for (T leftItem : leftGroup) {
int rightSub = 0;
@@ -83,14 +79,22 @@ public class ComparableTester<T extends Comparable<T>> {
for (T rightItem : rightGroup) {
String rightName = "Item[" + right + "," + (rightSub++) + "]";
Assert.assertEquals(
- leftName + " " + leftItem + " compareTo " + rightName + " " + rightItem
- + " is <0", true, leftItem.compareTo(rightItem) < 0);
+ leftName
+ + " "
+ + leftItem
+ + " compareTo "
+ + rightName
+ + " "
+ + rightItem
+ + " is <0",
+ true,
+ leftItem.compareTo(rightItem) < 0);
}
}
}
- private void assertMore(int left, int right, Collection<T> leftGroup,
- Collection<T> rightGroup) {
+ private void assertMore(
+ int left, int right, Collection<T> leftGroup, Collection<T> rightGroup) {
int leftSub = 0;
for (T leftItem : leftGroup) {
int rightSub = 0;
@@ -98,8 +102,16 @@ public class ComparableTester<T extends Comparable<T>> {
for (T rightItem : rightGroup) {
String rightName = "Item[" + right + "," + (rightSub++) + "]";
Assert.assertEquals(
- leftName + " " + leftItem + " compareTo " + rightName + " " + rightItem
- + " is >0", true, leftItem.compareTo(rightItem) > 0);
+ leftName
+ + " "
+ + leftItem
+ + " compareTo "
+ + rightName
+ + " "
+ + rightItem
+ + " is >0",
+ true,
+ leftItem.compareTo(rightItem) > 0);
}
}
}
diff --git a/tests/common/src/com/android/tv/testing/ComparatorTester.java b/tests/common/src/com/android/tv/testing/ComparatorTester.java
index 3774532f..6ebd8b4e 100644
--- a/tests/common/src/com/android/tv/testing/ComparatorTester.java
+++ b/tests/common/src/com/android/tv/testing/ComparatorTester.java
@@ -27,12 +27,9 @@ import java.util.List;
/**
* Tester for {@link Comparator} relationships between groups of T.
*
- * <p>
- * To use, create a new {@link ComparatorTester} and add comparable groups
- * where each group contains objects that are
- * {@link Comparator#compare(Object, Object)} == 0 to each other.
- * Groups are added in order asserting that all earlier groups have compare < 0
- * for all later groups.
+ * <p>To use, create a new {@link ComparatorTester} and add comparable groups where each group
+ * contains objects that are {@link Comparator#compare(Object, Object)} == 0 to each other. Groups
+ * are added in order asserting that all earlier groups have compare < 0 for all later groups.
*
* <pre>{@code
* ComparatorTester
@@ -41,8 +38,7 @@ import java.util.List;
* .addComparableGroup("World", "wORLD")
* .addComparableGroup("ZEBRA")
* .test();
- * }
- * </pre>
+ * }</pre>
*
* @param <T> the type of objects to compare.
*/
@@ -52,7 +48,6 @@ public class ComparatorTester<T> {
private final Comparator<T> comparator;
-
public static <T> ComparatorTester<T> withoutEqualsTest(Comparator<T> comparator) {
return new ComparatorTester<>(comparator);
}
@@ -80,7 +75,7 @@ public class ComparatorTester<T> {
assertOrder(i, j, currentGroup, rhs);
}
}
- //TODO: also test equals
+ // TODO: also test equals
}
private void assertOrder(int less, int more, List<T> lessGroup, List<T> moreGroup) {
@@ -88,30 +83,48 @@ public class ComparatorTester<T> {
assertMore(more, less, moreGroup, lessGroup);
}
- private void assertLess(int left, int right, Collection<T> leftGroup,
- Collection<T> rightGroup) {
+ private void assertLess(
+ int left, int right, Collection<T> leftGroup, Collection<T> rightGroup) {
int leftSub = 0;
for (T leftItem : leftGroup) {
int rightSub = 0;
for (T rightItem : rightGroup) {
String leftName = "Item[" + left + "," + (leftSub++) + "]";
String rName = "Item[" + right + "," + (rightSub++) + "]";
- assertEquals(leftName + " " + leftItem + " compareTo " + rName + " " + rightItem
- + " is <0", true, comparator.compare(leftItem, rightItem) < 0);
+ assertEquals(
+ leftName
+ + " "
+ + leftItem
+ + " compareTo "
+ + rName
+ + " "
+ + rightItem
+ + " is <0",
+ true,
+ comparator.compare(leftItem, rightItem) < 0);
}
}
}
- private void assertMore(int left, int right, Collection<T> leftGroup,
- Collection<T> rightGroup) {
+ private void assertMore(
+ int left, int right, Collection<T> leftGroup, Collection<T> rightGroup) {
int leftSub = 0;
for (T leftItem : leftGroup) {
int rightSub = 0;
for (T rightItem : rightGroup) {
String leftName = "Item[" + left + "," + (leftSub++) + "]";
String rName = "Item[" + right + "," + (rightSub++) + "]";
- assertEquals(leftName + " " + leftItem + " compareTo " + rName + " " + rightItem
- + " is >0", true, comparator.compare(leftItem, rightItem) > 0);
+ assertEquals(
+ leftName
+ + " "
+ + leftItem
+ + " compareTo "
+ + rName
+ + " "
+ + rightItem
+ + " is >0",
+ true,
+ comparator.compare(leftItem, rightItem) > 0);
}
}
}
@@ -120,9 +133,11 @@ public class ComparatorTester<T> {
// Test everything against everything in both directions, including against itself.
for (T leftItem : group) {
for (T rightItem : group) {
- assertEquals(leftItem + " compareTo " + rightItem, 0,
+ assertEquals(
+ leftItem + " compareTo " + rightItem,
+ 0,
comparator.compare(leftItem, rightItem));
}
}
}
-} \ No newline at end of file
+}
diff --git a/tests/common/src/com/android/tv/testing/Constants.java b/tests/common/src/com/android/tv/testing/Constants.java
deleted file mode 100644
index 4c9cb5fb..00000000
--- a/tests/common/src/com/android/tv/testing/Constants.java
+++ /dev/null
@@ -1,42 +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.testing;
-
-import android.media.tv.TvTrackInfo;
-
-/**
- * Constants for testing.
- */
-public final class Constants {
- public static final int FUNC_TEST_CHANNEL_COUNT = 100;
- public static final int UNIT_TEST_CHANNEL_COUNT = 4;
- public static final int JANK_TEST_CHANNEL_COUNT = 500; // TODO: increase to 1000 see b/23526997
-
- public static final TvTrackInfo EN_STEREO_AUDIO_TRACK = new TvTrackInfo.Builder(
- TvTrackInfo.TYPE_AUDIO, "English Stereo Audio").setLanguage("en")
- .setAudioChannelCount(2).build();
- public static final TvTrackInfo GENERIC_AUDIO_TRACK = new TvTrackInfo.Builder(
- TvTrackInfo.TYPE_AUDIO, "Generic Audio").build();
-
- public static final TvTrackInfo FHD1080P50_VIDEO_TRACK = new TvTrackInfo.Builder(
- TvTrackInfo.TYPE_VIDEO, "FHD Video").setVideoHeight(1080).setVideoWidth(1920)
- .setVideoFrameRate(50).build();
- public static final TvTrackInfo SVGA_VIDEO_TRACK = new TvTrackInfo.Builder(
- TvTrackInfo.TYPE_VIDEO, "SVGA Video").setVideoHeight(600).setVideoWidth(800).build();
-
- private Constants() {
- }
-}
diff --git a/tests/common/src/com/android/tv/testing/DbTestingUtils.java b/tests/common/src/com/android/tv/testing/DbTestingUtils.java
new file mode 100644
index 00000000..53e26ca7
--- /dev/null
+++ b/tests/common/src/com/android/tv/testing/DbTestingUtils.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 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.testing;
+
+import android.database.Cursor;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Static utilities for testing using databases. */
+public final class DbTestingUtils {
+
+ public static List<List<String>> toList(Cursor cursor) {
+ ArrayList<List<String>> result = new ArrayList<>();
+ int colCount = cursor.getColumnCount();
+ while (cursor.moveToNext()) {
+ List<String> row = new ArrayList<>(colCount);
+ for (int i = 0; i < colCount; i++) {
+ row.add(cursor.getString(i));
+ }
+ result.add(row);
+ }
+ return result;
+ }
+
+ private DbTestingUtils() {}
+}
diff --git a/tests/common/src/com/android/tv/testing/EpgTestData.java b/tests/common/src/com/android/tv/testing/EpgTestData.java
new file mode 100644
index 00000000..49a92181
--- /dev/null
+++ b/tests/common/src/com/android/tv/testing/EpgTestData.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2017 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.testing;
+
+import com.android.tv.data.ChannelImpl;
+import com.android.tv.data.Lineup;
+import com.android.tv.data.Program;
+import com.android.tv.data.api.Channel;
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.ListMultimap;
+import java.util.concurrent.TimeUnit;
+
+/** EPG data for use in tests. */
+public abstract class EpgTestData {
+
+ public static final android.support.media.tv.Channel CHANNEL_10 =
+ new android.support.media.tv.Channel.Builder()
+ .setDisplayName("Channel TEN")
+ .setDisplayNumber("10")
+ .build();
+ public static final android.support.media.tv.Channel CHANNEL_11 =
+ new android.support.media.tv.Channel.Builder()
+ .setDisplayName("Channel Eleven")
+ .setDisplayNumber("11")
+ .build();
+ public static final android.support.media.tv.Channel CHANNEL_90_2 =
+ new android.support.media.tv.Channel.Builder()
+ .setDisplayName("Channel Ninety dot Two")
+ .setDisplayNumber("90.2")
+ .build();
+
+ public static final Lineup LINEUP_1 =
+ new Lineup(
+ "lineup1",
+ Lineup.LINEUP_SATELLITE,
+ "Lineup one",
+ "Location one",
+ ImmutableList.of("1", "2.2"));
+ public static final Lineup LINEUP_2 =
+ new Lineup(
+ "lineup2",
+ Lineup.LINEUP_SATELLITE,
+ "Lineup two",
+ "Location two",
+ ImmutableList.of("1", "2.3"));
+
+ public static final Lineup LINEUP_90210 =
+ new Lineup(
+ "test90210",
+ Lineup.LINEUP_BROADCAST_DIGITAL,
+ "Test 90210",
+ "Beverly Hills",
+ ImmutableList.of("90.2", "10"));
+
+ // Programs start and end times are set relative to 0.
+ // Then when loaded they are offset by the {@link #getStartTimeMs}.
+ // Start and end time may be negative meaning they happen before "now".
+
+ public static final Program PROGRAM_1 =
+ new Program.Builder()
+ .setTitle("Program 1")
+ .setStartTimeUtcMillis(0)
+ .setEndTimeUtcMillis(TimeUnit.MINUTES.toMillis(30))
+ .build();
+
+ public static final Program PROGRAM_2 =
+ new Program.Builder()
+ .setTitle("Program 2")
+ .setStartTimeUtcMillis(TimeUnit.MINUTES.toMillis(30))
+ .setEndTimeUtcMillis(TimeUnit.MINUTES.toMillis(60))
+ .build();
+
+ public static final EpgTestData DATA_90210 =
+ new EpgTestData() {
+
+ // Thursday, June 1, 2017 4:00:00 PM GMT-07:00
+ private final long testStartTimeMs = 1496358000000L;
+
+ @Override
+ public ListMultimap<String, Lineup> getLineups() {
+ ImmutableListMultimap.Builder<String, Lineup> builder =
+ ImmutableListMultimap.builder();
+ return builder.putAll("90210", LINEUP_1, LINEUP_2, LINEUP_90210).build();
+ }
+
+ @Override
+ public ListMultimap<String, Channel> getLineupChannels() {
+ ImmutableListMultimap.Builder<String, Channel> builder =
+ ImmutableListMultimap.builder();
+ return builder.putAll(
+ LINEUP_90210.getId(), toTvChannels(CHANNEL_90_2, CHANNEL_10))
+ .putAll(LINEUP_1.getId(), toTvChannels(CHANNEL_10, CHANNEL_11))
+ .build();
+ }
+
+ @Override
+ public ListMultimap<String, Program> getEpgPrograms() {
+ ImmutableListMultimap.Builder<String, Program> builder =
+ ImmutableListMultimap.builder();
+ return builder.putAll(
+ CHANNEL_10.getDisplayNumber(),
+ EpgTestData.updateTime(getStartTimeMs(), PROGRAM_1))
+ .putAll(
+ CHANNEL_11.getDisplayNumber(),
+ EpgTestData.updateTime(getStartTimeMs(), PROGRAM_2))
+ .build();
+ }
+
+ @Override
+ public long getStartTimeMs() {
+ return testStartTimeMs;
+ }
+ };
+
+ public abstract ListMultimap<String, Lineup> getLineups();
+
+ public abstract ListMultimap<String, Channel> getLineupChannels();
+
+ public abstract ListMultimap<String, Program> getEpgPrograms();
+
+ /** The starting time for this test data */
+ public abstract long getStartTimeMs();
+
+ /**
+ * Loads test data
+ *
+ * <p>
+ *
+ * <ul>
+ * <li>Sets clock to {@link #getStartTimeMs()} and boot time to 12 hours before that
+ * <li>Loads lineups
+ * <li>Loads lineupChannels
+ * <li>Loads epgPrograms
+ * </ul>
+ */
+ public final void loadData(FakeClock clock, FakeEpgReader epgReader) {
+ clock.setBootTimeMillis(getStartTimeMs() + TimeUnit.HOURS.toMillis(-12));
+ clock.setCurrentTimeMillis(getStartTimeMs());
+ epgReader.zip2lineups.putAll(getLineups());
+ epgReader.lineup2Channels.putAll(getLineupChannels());
+ epgReader.epgChannelId2Programs.putAll(getEpgPrograms());
+ }
+
+ public final void loadData(TestSingletonApp testSingletonApp) {
+ loadData(testSingletonApp.fakeClock, testSingletonApp.epgReader);
+ }
+
+ private static Iterable<Channel> toTvChannels(android.support.media.tv.Channel... channels) {
+ return Iterables.transform(
+ ImmutableList.copyOf(channels),
+ new Function<android.support.media.tv.Channel, Channel>() {
+ @Override
+ public Channel apply(android.support.media.tv.Channel original) {
+ return toTvChannel(original);
+ }
+ });
+ }
+
+ public static Channel toTvChannel(android.support.media.tv.Channel original) {
+ return new ChannelImpl.Builder()
+ .setDisplayName(original.getDisplayName())
+ .setDisplayNumber(original.getDisplayNumber())
+ // TODO implement the reset
+ .build();
+ }
+
+ /** Add time to the startTime and stopTime of each program */
+ private static Iterable<Program> updateTime(long time, Program... programs) {
+ return Iterables.transform(
+ ImmutableList.copyOf(programs),
+ new Function<Program, Program>() {
+ @Override
+ public Program apply(Program p) {
+ return new Program.Builder(p)
+ .setStartTimeUtcMillis(p.getStartTimeUtcMillis() + time)
+ .setEndTimeUtcMillis(p.getEndTimeUtcMillis() + time)
+ .build();
+ }
+ });
+ }
+}
diff --git a/tests/common/src/com/android/tv/testing/FakeClock.java b/tests/common/src/com/android/tv/testing/FakeClock.java
index d88e53a8..f5941939 100644
--- a/tests/common/src/com/android/tv/testing/FakeClock.java
+++ b/tests/common/src/com/android/tv/testing/FakeClock.java
@@ -16,8 +16,7 @@
package com.android.tv.testing;
-import com.android.tv.util.Clock;
-
+import com.android.tv.common.util.Clock;
import java.util.concurrent.TimeUnit;
/**
@@ -27,21 +26,20 @@ import java.util.concurrent.TimeUnit;
* {@link #sleep(long)} is called.
*/
public class FakeClock implements Clock {
- /**
- * Creates a fake clock with the time set to now and the boot time set to now - 100,000.
- */
+ /** Creates a fake clock with the time set to now and the boot time set to now - 100,000. */
public static FakeClock createWithCurrentTime() {
long now = System.currentTimeMillis();
return new FakeClock(now, now - 100_000);
}
- /**
- * Creates a fake clock with the time set to zero.
- */
+ /** Creates a fake clock with the time set to zero. */
public static FakeClock createWithTimeOne() {
return new FakeClock(1L, 0L);
}
-
+ /** Creates a fake clock with the time set to {@code time}. */
+ public static FakeClock createWithTime(long time) {
+ return new FakeClock(time, 0L);
+ }
private long mCurrentTimeMillis;
@@ -95,9 +93,12 @@ public class FakeClock implements Clock {
return mCurrentTimeMillis - mBootTimeMillis;
}
- /**
- * Sleep does not block it just updates the current time.
- */
+ @Override
+ public long uptimeMillis() {
+ return elapsedRealtime();
+ }
+
+ /** Sleep does not block it just updates the current time. */
@Override
public void sleep(long ms) {
// TODO: implement blocking if needed.
diff --git a/tests/common/src/com/android/tv/testing/FakeEpgFetcher.java b/tests/common/src/com/android/tv/testing/FakeEpgFetcher.java
new file mode 100644
index 00000000..d1018a5c
--- /dev/null
+++ b/tests/common/src/com/android/tv/testing/FakeEpgFetcher.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 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.testing;
+
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import com.android.tv.data.epg.EpgFetcher;
+
+/** Fake {@link EpgFetcher} for testing. */
+public class FakeEpgFetcher implements EpgFetcher {
+ public boolean fetchStarted = false;
+
+ @Override
+ public void startRoutineService() {}
+
+ @Override
+ public void fetchImmediatelyIfNeeded() {}
+
+ @Override
+ public void fetchImmediately() {
+ fetchStarted = true;
+ }
+
+ @Override
+ public void onChannelScanStarted() {}
+
+ @Override
+ public void onChannelScanFinished() {}
+
+ @Override
+ public boolean executeFetchTaskIfPossible(JobService jobService, JobParameters params) {
+ return false;
+ }
+
+ @Override
+ public void stopFetchingJob() {
+ fetchStarted = false;
+ }
+
+ public void reset() {
+ fetchStarted = false;
+ }
+}
diff --git a/tests/common/src/com/android/tv/testing/FakeEpgReader.java b/tests/common/src/com/android/tv/testing/FakeEpgReader.java
new file mode 100644
index 00000000..710ada55
--- /dev/null
+++ b/tests/common/src/com/android/tv/testing/FakeEpgReader.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2017 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.testing;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.Range;
+import com.android.tv.data.ChannelImpl;
+import com.android.tv.data.ChannelNumber;
+import com.android.tv.data.Lineup;
+import com.android.tv.data.Program;
+import com.android.tv.data.api.Channel;
+import com.android.tv.data.epg.EpgReader;
+import com.android.tv.dvr.data.SeriesInfo;
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.ListMultimap;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/** Fake {@link EpgReader} for testing. */
+public final class FakeEpgReader implements EpgReader {
+ public final ListMultimap<String, Lineup> zip2lineups = LinkedListMultimap.create(2);
+ public final ListMultimap<String, Channel> lineup2Channels = LinkedListMultimap.create(2);
+ public final ListMultimap<String, Program> epgChannelId2Programs = LinkedListMultimap.create(2);
+ public final FakeClock fakeClock;
+
+ public FakeEpgReader(FakeClock fakeClock) {
+ this.fakeClock = fakeClock;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public long getEpgTimestamp() {
+ return fakeClock.currentTimeMillis();
+ }
+
+ @Override
+ public void setRegionCode(String regionCode) {}
+
+ @Override
+ public List<Lineup> getLineups(@NonNull String postalCode) {
+ return zip2lineups.get(postalCode);
+ }
+
+ @Override
+ public List<String> getChannelNumbers(@NonNull String lineupId) {
+ return null;
+ }
+
+ @Override
+ public Set<EpgChannel> getChannels(Set<Channel> inputChannels, @NonNull String lineupId) {
+ Set<EpgChannel> result = new HashSet<>();
+ List<Channel> lineupChannels = lineup2Channels.get(lineupId);
+ for (Channel channel : lineupChannels) {
+ Channel match =
+ Iterables.find(
+ inputChannels,
+ new Predicate<Channel>() {
+ @Override
+ public boolean apply(@Nullable Channel inputChannel) {
+ return ChannelNumber.equivalent(
+ inputChannel.getDisplayNumber(),
+ channel.getDisplayNumber());
+ }
+ },
+ null);
+ if (match != null) {
+ ChannelImpl updatedChannel = new ChannelImpl.Builder(match).build();
+ updatedChannel.setLogoUri(channel.getLogoUri());
+ result.add(EpgChannel.createEpgChannel(updatedChannel, channel.getDisplayNumber()));
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public void preloadChannels(@NonNull String lineupId) {}
+
+ @Override
+ public void clearCachedChannels(@NonNull String lineupId) {}
+
+ @Override
+ public List<Program> getPrograms(EpgChannel epgChannel) {
+ return ImmutableList.copyOf(
+ Iterables.transform(
+ epgChannelId2Programs.get(epgChannel.getEpgChannelId()),
+ updateWith(epgChannel)));
+ }
+
+ @Override
+ public Map<EpgChannel, Collection<Program>> getPrograms(
+ @NonNull Set<EpgChannel> epgChannels, long duration) {
+ Range<Long> validRange =
+ Range.create(
+ fakeClock.currentTimeMillis(), fakeClock.currentTimeMillis() + duration);
+ ImmutableMap.Builder<EpgChannel, Collection<Program>> mapBuilder = ImmutableMap.builder();
+ for (EpgChannel epgChannel : epgChannels) {
+ Iterable<Program> programs = getPrograms(epgChannel);
+
+ mapBuilder.put(
+ epgChannel,
+ ImmutableList.copyOf(Iterables.filter(programs, isProgramDuring(validRange))));
+ }
+ return mapBuilder.build();
+ }
+
+ protected Function<Program, Program> updateWith(final EpgChannel channel) {
+ return new Function<Program, Program>() {
+ @Nullable
+ @Override
+ public Program apply(@Nullable Program program) {
+ return new Program.Builder(program)
+ .setChannelId(channel.getChannel().getId())
+ .setPackageName(channel.getChannel().getPackageName())
+ .build();
+ }
+ };
+ }
+
+ /**
+ * True if the start time or the end time is {@link Range#contains contained (inclusive)} in the
+ * range
+ */
+ protected Predicate<Program> isProgramDuring(final Range<Long> validRange) {
+ return new Predicate<Program>() {
+ @Override
+ public boolean apply(@Nullable Program program) {
+ return validRange.contains(program.getStartTimeUtcMillis())
+ || validRange.contains(program.getEndTimeUtcMillis());
+ }
+ };
+ }
+
+ @Override
+ public SeriesInfo getSeriesInfo(@NonNull String seriesId) {
+ return null;
+ }
+}
diff --git a/tests/common/src/com/android/tv/testing/FakeRemoteConfig.java b/tests/common/src/com/android/tv/testing/FakeRemoteConfig.java
new file mode 100644
index 00000000..89e6a0a2
--- /dev/null
+++ b/tests/common/src/com/android/tv/testing/FakeRemoteConfig.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 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.testing;
+
+import android.text.TextUtils;
+import com.android.tv.common.config.api.RemoteConfig;
+import java.util.HashMap;
+import java.util.Map;
+
+/** Fake {@link RemoteConfig} suitable for testing. */
+public class FakeRemoteConfig implements RemoteConfig {
+ public final Map<String, String> values = new HashMap();
+
+ @Override
+ public void fetch(OnRemoteConfigUpdatedListener listener) {}
+
+ @Override
+ public String getString(String key) {
+ return values.get(key);
+ }
+
+ @Override
+ public boolean getBoolean(String key) {
+ String value = values.get(key);
+ return TextUtils.isEmpty(value) ? false : Boolean.valueOf(key);
+ }
+
+ @Override
+ public long getLong(String key) {
+ return getLong(key, 0);
+ }
+
+ @Override
+ public long getLong(String key, long defaultValue) {
+ if (values.containsKey(key)) {
+ String value = values.get(key);
+ return TextUtils.isEmpty(value) ? defaultValue : Long.valueOf(value);
+ }
+ return defaultValue;
+ }
+}
diff --git a/tests/common/src/com/android/tv/testing/FakeTvInputManager.java b/tests/common/src/com/android/tv/testing/FakeTvInputManager.java
new file mode 100644
index 00000000..397b4052
--- /dev/null
+++ b/tests/common/src/com/android/tv/testing/FakeTvInputManager.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2017 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.testing;
+
+import android.media.tv.TvContentRatingSystemInfo;
+import android.media.tv.TvInputInfo;
+import android.media.tv.TvInputManager;
+import android.os.Handler;
+import com.android.tv.util.TvInputManagerHelper;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** Fake implementation for testing. */
+public class FakeTvInputManager implements TvInputManagerHelper.TvInputManagerInterface {
+
+ private final Map<String, Integer> mInputStateMap = new HashMap<>();
+ private final Map<String, TvInputInfo> mInputMap = new HashMap<>();
+ private final Map<TvInputManager.TvInputCallback, Handler> mCallbacks = new HashMap<>();
+
+ public void add(TvInputInfo inputInfo, int state) {
+ final String inputId = inputInfo.getId();
+ if (mInputStateMap.containsKey(inputId)) {
+ throw new IllegalArgumentException("inputId " + inputId);
+ }
+ mInputMap.put(inputId, inputInfo);
+ mInputStateMap.put(inputId, state);
+ for (final Map.Entry<TvInputManager.TvInputCallback, Handler> e : mCallbacks.entrySet()) {
+ e.getValue()
+ .post(
+ new Runnable() {
+ @Override
+ public void run() {
+ e.getKey().onInputAdded(inputId);
+ }
+ });
+ }
+ }
+
+ public void setInputState(final String inputId, final int state) {
+ if (!mInputStateMap.containsKey(inputId)) {
+ throw new IllegalArgumentException("inputId " + inputId);
+ }
+ mInputStateMap.put(inputId, state);
+ for (final Map.Entry<TvInputManager.TvInputCallback, Handler> e : mCallbacks.entrySet()) {
+ e.getValue()
+ .post(
+ new Runnable() {
+ @Override
+ public void run() {
+ e.getKey().onInputStateChanged(inputId, state);
+ }
+ });
+ }
+ }
+
+ public void remove(final String inputId) {
+ mInputMap.remove(inputId);
+ mInputStateMap.remove(inputId);
+ for (final Map.Entry<TvInputManager.TvInputCallback, Handler> e : mCallbacks.entrySet()) {
+ e.getValue()
+ .post(
+ new Runnable() {
+ @Override
+ public void run() {
+ e.getKey().onInputRemoved(inputId);
+ }
+ });
+ }
+ }
+
+ @Override
+ public TvInputInfo getTvInputInfo(String inputId) {
+ return mInputMap.get(inputId);
+ }
+
+ @Override
+ public Integer getInputState(String inputId) {
+ return mInputStateMap.get(inputId);
+ }
+
+ @Override
+ public void registerCallback(TvInputManager.TvInputCallback internalCallback, Handler handler) {
+ mCallbacks.put(internalCallback, handler);
+ }
+
+ @Override
+ public void unregisterCallback(TvInputManager.TvInputCallback internalCallback) {
+ mCallbacks.remove(internalCallback);
+ }
+
+ @Override
+ public List<TvInputInfo> getTvInputList() {
+ return new ArrayList(mInputMap.values());
+ }
+
+ @Override
+ public List<TvContentRatingSystemInfo> getTvContentRatingSystemList() {
+ // TODO implement
+ return new ArrayList<>();
+ }
+}
diff --git a/tests/common/src/com/android/tv/testing/FakeTvInputManagerHelper.java b/tests/common/src/com/android/tv/testing/FakeTvInputManagerHelper.java
new file mode 100644
index 00000000..85bdcf04
--- /dev/null
+++ b/tests/common/src/com/android/tv/testing/FakeTvInputManagerHelper.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 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.testing;
+
+import android.content.Context;
+import com.android.tv.util.TvInputManagerHelper;
+
+/** Fake TvInputManagerHelper. */
+public class FakeTvInputManagerHelper extends TvInputManagerHelper {
+
+ public FakeTvInputManagerHelper(Context context) {
+ super(context, new FakeTvInputManager());
+ }
+
+ public FakeTvInputManager getFakeTvInputManager() {
+ return (FakeTvInputManager) mTvInputManager;
+ }
+}
diff --git a/tests/common/src/com/android/tv/testing/FakeTvProvider.java b/tests/common/src/com/android/tv/testing/FakeTvProvider.java
new file mode 100644
index 00000000..24c26f39
--- /dev/null
+++ b/tests/common/src/com/android/tv/testing/FakeTvProvider.java
@@ -0,0 +1,2605 @@
+/*
+ * Copyright (C) 2017 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.testing;
+
+import android.annotation.SuppressLint;
+import android.content.ContentProvider;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.OperationApplicationException;
+import android.content.SharedPreferences;
+import android.content.UriMatcher;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.media.tv.TvContract;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.ParcelFileDescriptor.AutoCloseInputStream;
+import android.preference.PreferenceManager;
+import android.provider.BaseColumns;
+import android.support.annotation.VisibleForTesting;
+import android.support.media.tv.TvContractCompat;
+import android.support.media.tv.TvContractCompat.BaseTvColumns;
+import android.support.media.tv.TvContractCompat.Channels;
+import android.support.media.tv.TvContractCompat.PreviewPrograms;
+import android.support.media.tv.TvContractCompat.Programs;
+import android.support.media.tv.TvContractCompat.Programs.Genres;
+import android.support.media.tv.TvContractCompat.RecordedPrograms;
+import android.support.media.tv.TvContractCompat.WatchNextPrograms;
+import android.text.TextUtils;
+import android.util.Log;
+import com.android.tv.util.SqlParams;
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Fake TV content provider suitable for unit tests. The contract between this provider and
+ * applications is defined in {@link TvContractCompat}.
+ */
+// TODO(b/62143348): remove when error prone check fixed
+@SuppressWarnings({"AndroidApiChecker", "TryWithResources"})
+public class FakeTvProvider extends ContentProvider {
+ // TODO either make this a shadow or move it to the support library
+
+ private static final boolean DEBUG = false;
+ private static final String TAG = "TvProvider";
+
+ static final int DATABASE_VERSION = 34;
+ static final String SHARED_PREF_BLOCKED_PACKAGES_KEY = "blocked_packages";
+ static final String CHANNELS_TABLE = "channels";
+ static final String PROGRAMS_TABLE = "programs";
+ static final String RECORDED_PROGRAMS_TABLE = "recorded_programs";
+ static final String PREVIEW_PROGRAMS_TABLE = "preview_programs";
+ static final String WATCH_NEXT_PROGRAMS_TABLE = "watch_next_programs";
+ static final String WATCHED_PROGRAMS_TABLE = "watched_programs";
+ static final String PROGRAMS_TABLE_PACKAGE_NAME_INDEX = "programs_package_name_index";
+ static final String PROGRAMS_TABLE_CHANNEL_ID_INDEX = "programs_channel_id_index";
+ static final String PROGRAMS_TABLE_START_TIME_INDEX = "programs_start_time_index";
+ static final String PROGRAMS_TABLE_END_TIME_INDEX = "programs_end_time_index";
+ static final String WATCHED_PROGRAMS_TABLE_CHANNEL_ID_INDEX =
+ "watched_programs_channel_id_index";
+ // The internal column in the watched programs table to indicate whether the current log entry
+ // is consolidated or not. Unconsolidated entries may have columns with missing data.
+ static final String WATCHED_PROGRAMS_COLUMN_CONSOLIDATED = "consolidated";
+ static final String CHANNELS_COLUMN_LOGO = "logo";
+ private static final String DATABASE_NAME = "tv.db";
+ private static final String DELETED_CHANNELS_TABLE = "deleted_channels"; // Deprecated
+ private static final String DEFAULT_PROGRAMS_SORT_ORDER =
+ Programs.COLUMN_START_TIME_UTC_MILLIS + " ASC";
+ private static final String DEFAULT_WATCHED_PROGRAMS_SORT_ORDER =
+ WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS + " DESC";
+ private static final String CHANNELS_TABLE_INNER_JOIN_PROGRAMS_TABLE =
+ CHANNELS_TABLE
+ + " INNER JOIN "
+ + PROGRAMS_TABLE
+ + " ON ("
+ + CHANNELS_TABLE
+ + "."
+ + Channels._ID
+ + "="
+ + PROGRAMS_TABLE
+ + "."
+ + Programs.COLUMN_CHANNEL_ID
+ + ")";
+
+ // Operation names for createSqlParams().
+ private static final String OP_QUERY = "query";
+ private static final String OP_UPDATE = "update";
+ private static final String OP_DELETE = "delete";
+
+ private static final UriMatcher sUriMatcher;
+ private static final int MATCH_CHANNEL = 1;
+ private static final int MATCH_CHANNEL_ID = 2;
+ private static final int MATCH_CHANNEL_ID_LOGO = 3;
+ private static final int MATCH_PASSTHROUGH_ID = 4;
+ private static final int MATCH_PROGRAM = 5;
+ private static final int MATCH_PROGRAM_ID = 6;
+ private static final int MATCH_WATCHED_PROGRAM = 7;
+ private static final int MATCH_WATCHED_PROGRAM_ID = 8;
+ private static final int MATCH_RECORDED_PROGRAM = 9;
+ private static final int MATCH_RECORDED_PROGRAM_ID = 10;
+ private static final int MATCH_PREVIEW_PROGRAM = 11;
+ private static final int MATCH_PREVIEW_PROGRAM_ID = 12;
+ private static final int MATCH_WATCH_NEXT_PROGRAM = 13;
+ private static final int MATCH_WATCH_NEXT_PROGRAM_ID = 14;
+
+ private static final int MAX_LOGO_IMAGE_SIZE = 256;
+
+ private static final String EMPTY_STRING = "";
+
+ private static final Map<String, String> sChannelProjectionMap;
+ private static final Map<String, String> sProgramProjectionMap;
+ private static final Map<String, String> sWatchedProgramProjectionMap;
+ private static final Map<String, String> sRecordedProgramProjectionMap;
+ private static final Map<String, String> sPreviewProgramProjectionMap;
+ private static final Map<String, String> sWatchNextProgramProjectionMap;
+
+ // TvContract hidden
+ private static final String PARAM_PACKAGE = "package";
+ private static final String PARAM_PREVIEW = "preview";
+
+ private static boolean sInitialized;
+
+ static {
+ sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+ sUriMatcher.addURI(TvContractCompat.AUTHORITY, "channel", MATCH_CHANNEL);
+ sUriMatcher.addURI(TvContractCompat.AUTHORITY, "channel/#", MATCH_CHANNEL_ID);
+ sUriMatcher.addURI(TvContractCompat.AUTHORITY, "channel/#/logo", MATCH_CHANNEL_ID_LOGO);
+ sUriMatcher.addURI(TvContractCompat.AUTHORITY, "passthrough/*", MATCH_PASSTHROUGH_ID);
+ sUriMatcher.addURI(TvContractCompat.AUTHORITY, "program", MATCH_PROGRAM);
+ sUriMatcher.addURI(TvContractCompat.AUTHORITY, "program/#", MATCH_PROGRAM_ID);
+ sUriMatcher.addURI(TvContractCompat.AUTHORITY, "watched_program", MATCH_WATCHED_PROGRAM);
+ sUriMatcher.addURI(
+ TvContractCompat.AUTHORITY, "watched_program/#", MATCH_WATCHED_PROGRAM_ID);
+ sUriMatcher.addURI(TvContractCompat.AUTHORITY, "recorded_program", MATCH_RECORDED_PROGRAM);
+ sUriMatcher.addURI(
+ TvContractCompat.AUTHORITY, "recorded_program/#", MATCH_RECORDED_PROGRAM_ID);
+ sUriMatcher.addURI(TvContractCompat.AUTHORITY, "preview_program", MATCH_PREVIEW_PROGRAM);
+ sUriMatcher.addURI(
+ TvContractCompat.AUTHORITY, "preview_program/#", MATCH_PREVIEW_PROGRAM_ID);
+ sUriMatcher.addURI(
+ TvContractCompat.AUTHORITY, "watch_next_program", MATCH_WATCH_NEXT_PROGRAM);
+ sUriMatcher.addURI(
+ TvContractCompat.AUTHORITY, "watch_next_program/#", MATCH_WATCH_NEXT_PROGRAM_ID);
+
+ sChannelProjectionMap = new HashMap<>();
+ sChannelProjectionMap.put(Channels._ID, CHANNELS_TABLE + "." + Channels._ID);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_PACKAGE_NAME, CHANNELS_TABLE + "." + Channels.COLUMN_PACKAGE_NAME);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_INPUT_ID, CHANNELS_TABLE + "." + Channels.COLUMN_INPUT_ID);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_TYPE, CHANNELS_TABLE + "." + Channels.COLUMN_TYPE);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_SERVICE_TYPE, CHANNELS_TABLE + "." + Channels.COLUMN_SERVICE_TYPE);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_ORIGINAL_NETWORK_ID,
+ CHANNELS_TABLE + "." + Channels.COLUMN_ORIGINAL_NETWORK_ID);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_TRANSPORT_STREAM_ID,
+ CHANNELS_TABLE + "." + Channels.COLUMN_TRANSPORT_STREAM_ID);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_SERVICE_ID, CHANNELS_TABLE + "." + Channels.COLUMN_SERVICE_ID);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_DISPLAY_NUMBER,
+ CHANNELS_TABLE + "." + Channels.COLUMN_DISPLAY_NUMBER);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_DISPLAY_NAME, CHANNELS_TABLE + "." + Channels.COLUMN_DISPLAY_NAME);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_NETWORK_AFFILIATION,
+ CHANNELS_TABLE + "." + Channels.COLUMN_NETWORK_AFFILIATION);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_DESCRIPTION, CHANNELS_TABLE + "." + Channels.COLUMN_DESCRIPTION);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_VIDEO_FORMAT, CHANNELS_TABLE + "." + Channels.COLUMN_VIDEO_FORMAT);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_BROWSABLE, CHANNELS_TABLE + "." + Channels.COLUMN_BROWSABLE);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_SEARCHABLE, CHANNELS_TABLE + "." + Channels.COLUMN_SEARCHABLE);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_LOCKED, CHANNELS_TABLE + "." + Channels.COLUMN_LOCKED);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_APP_LINK_ICON_URI,
+ CHANNELS_TABLE + "." + Channels.COLUMN_APP_LINK_ICON_URI);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_APP_LINK_POSTER_ART_URI,
+ CHANNELS_TABLE + "." + Channels.COLUMN_APP_LINK_POSTER_ART_URI);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_APP_LINK_TEXT,
+ CHANNELS_TABLE + "." + Channels.COLUMN_APP_LINK_TEXT);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_APP_LINK_COLOR,
+ CHANNELS_TABLE + "." + Channels.COLUMN_APP_LINK_COLOR);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_APP_LINK_INTENT_URI,
+ CHANNELS_TABLE + "." + Channels.COLUMN_APP_LINK_INTENT_URI);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_INTERNAL_PROVIDER_DATA,
+ CHANNELS_TABLE + "." + Channels.COLUMN_INTERNAL_PROVIDER_DATA);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_INTERNAL_PROVIDER_FLAG1,
+ CHANNELS_TABLE + "." + Channels.COLUMN_INTERNAL_PROVIDER_FLAG1);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_INTERNAL_PROVIDER_FLAG2,
+ CHANNELS_TABLE + "." + Channels.COLUMN_INTERNAL_PROVIDER_FLAG2);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_INTERNAL_PROVIDER_FLAG3,
+ CHANNELS_TABLE + "." + Channels.COLUMN_INTERNAL_PROVIDER_FLAG3);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_INTERNAL_PROVIDER_FLAG4,
+ CHANNELS_TABLE + "." + Channels.COLUMN_INTERNAL_PROVIDER_FLAG4);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_VERSION_NUMBER,
+ CHANNELS_TABLE + "." + Channels.COLUMN_VERSION_NUMBER);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_TRANSIENT, CHANNELS_TABLE + "." + Channels.COLUMN_TRANSIENT);
+ sChannelProjectionMap.put(
+ Channels.COLUMN_INTERNAL_PROVIDER_ID,
+ CHANNELS_TABLE + "." + Channels.COLUMN_INTERNAL_PROVIDER_ID);
+
+ sProgramProjectionMap = new HashMap<>();
+ sProgramProjectionMap.put(Programs._ID, Programs._ID);
+ sProgramProjectionMap.put(Programs.COLUMN_PACKAGE_NAME, Programs.COLUMN_PACKAGE_NAME);
+ sProgramProjectionMap.put(Programs.COLUMN_CHANNEL_ID, Programs.COLUMN_CHANNEL_ID);
+ sProgramProjectionMap.put(Programs.COLUMN_TITLE, Programs.COLUMN_TITLE);
+ // COLUMN_SEASON_NUMBER is deprecated. Return COLUMN_SEASON_DISPLAY_NUMBER instead.
+ sProgramProjectionMap.put(
+ Programs.COLUMN_SEASON_NUMBER,
+ Programs.COLUMN_SEASON_DISPLAY_NUMBER + " AS " + Programs.COLUMN_SEASON_NUMBER);
+ sProgramProjectionMap.put(
+ Programs.COLUMN_SEASON_DISPLAY_NUMBER, Programs.COLUMN_SEASON_DISPLAY_NUMBER);
+ sProgramProjectionMap.put(Programs.COLUMN_SEASON_TITLE, Programs.COLUMN_SEASON_TITLE);
+ // COLUMN_EPISODE_NUMBER is deprecated. Return COLUMN_EPISODE_DISPLAY_NUMBER instead.
+ sProgramProjectionMap.put(
+ Programs.COLUMN_EPISODE_NUMBER,
+ Programs.COLUMN_EPISODE_DISPLAY_NUMBER + " AS " + Programs.COLUMN_EPISODE_NUMBER);
+ sProgramProjectionMap.put(
+ Programs.COLUMN_EPISODE_DISPLAY_NUMBER, Programs.COLUMN_EPISODE_DISPLAY_NUMBER);
+ sProgramProjectionMap.put(Programs.COLUMN_EPISODE_TITLE, Programs.COLUMN_EPISODE_TITLE);
+ sProgramProjectionMap.put(
+ Programs.COLUMN_START_TIME_UTC_MILLIS, Programs.COLUMN_START_TIME_UTC_MILLIS);
+ sProgramProjectionMap.put(
+ Programs.COLUMN_END_TIME_UTC_MILLIS, Programs.COLUMN_END_TIME_UTC_MILLIS);
+ sProgramProjectionMap.put(Programs.COLUMN_BROADCAST_GENRE, Programs.COLUMN_BROADCAST_GENRE);
+ sProgramProjectionMap.put(Programs.COLUMN_CANONICAL_GENRE, Programs.COLUMN_CANONICAL_GENRE);
+ sProgramProjectionMap.put(
+ Programs.COLUMN_SHORT_DESCRIPTION, Programs.COLUMN_SHORT_DESCRIPTION);
+ sProgramProjectionMap.put(
+ Programs.COLUMN_LONG_DESCRIPTION, Programs.COLUMN_LONG_DESCRIPTION);
+ sProgramProjectionMap.put(Programs.COLUMN_VIDEO_WIDTH, Programs.COLUMN_VIDEO_WIDTH);
+ sProgramProjectionMap.put(Programs.COLUMN_VIDEO_HEIGHT, Programs.COLUMN_VIDEO_HEIGHT);
+ sProgramProjectionMap.put(Programs.COLUMN_AUDIO_LANGUAGE, Programs.COLUMN_AUDIO_LANGUAGE);
+ sProgramProjectionMap.put(Programs.COLUMN_CONTENT_RATING, Programs.COLUMN_CONTENT_RATING);
+ sProgramProjectionMap.put(Programs.COLUMN_POSTER_ART_URI, Programs.COLUMN_POSTER_ART_URI);
+ sProgramProjectionMap.put(Programs.COLUMN_THUMBNAIL_URI, Programs.COLUMN_THUMBNAIL_URI);
+ sProgramProjectionMap.put(Programs.COLUMN_SEARCHABLE, Programs.COLUMN_SEARCHABLE);
+ sProgramProjectionMap.put(
+ Programs.COLUMN_RECORDING_PROHIBITED, Programs.COLUMN_RECORDING_PROHIBITED);
+ sProgramProjectionMap.put(
+ Programs.COLUMN_INTERNAL_PROVIDER_DATA, Programs.COLUMN_INTERNAL_PROVIDER_DATA);
+ sProgramProjectionMap.put(
+ Programs.COLUMN_INTERNAL_PROVIDER_FLAG1, Programs.COLUMN_INTERNAL_PROVIDER_FLAG1);
+ sProgramProjectionMap.put(
+ Programs.COLUMN_INTERNAL_PROVIDER_FLAG2, Programs.COLUMN_INTERNAL_PROVIDER_FLAG2);
+ sProgramProjectionMap.put(
+ Programs.COLUMN_INTERNAL_PROVIDER_FLAG3, Programs.COLUMN_INTERNAL_PROVIDER_FLAG3);
+ sProgramProjectionMap.put(
+ Programs.COLUMN_INTERNAL_PROVIDER_FLAG4, Programs.COLUMN_INTERNAL_PROVIDER_FLAG4);
+ sProgramProjectionMap.put(Programs.COLUMN_VERSION_NUMBER, Programs.COLUMN_VERSION_NUMBER);
+ sProgramProjectionMap.put(
+ Programs.COLUMN_REVIEW_RATING_STYLE, Programs.COLUMN_REVIEW_RATING_STYLE);
+ sProgramProjectionMap.put(Programs.COLUMN_REVIEW_RATING, Programs.COLUMN_REVIEW_RATING);
+
+ sWatchedProgramProjectionMap = new HashMap<>();
+ sWatchedProgramProjectionMap.put(WatchedPrograms._ID, WatchedPrograms._ID);
+ sWatchedProgramProjectionMap.put(
+ WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
+ WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS);
+ sWatchedProgramProjectionMap.put(
+ WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS,
+ WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS);
+ sWatchedProgramProjectionMap.put(
+ WatchedPrograms.COLUMN_CHANNEL_ID, WatchedPrograms.COLUMN_CHANNEL_ID);
+ sWatchedProgramProjectionMap.put(
+ WatchedPrograms.COLUMN_TITLE, WatchedPrograms.COLUMN_TITLE);
+ sWatchedProgramProjectionMap.put(
+ WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS,
+ WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS);
+ sWatchedProgramProjectionMap.put(
+ WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS,
+ WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS);
+ sWatchedProgramProjectionMap.put(
+ WatchedPrograms.COLUMN_DESCRIPTION, WatchedPrograms.COLUMN_DESCRIPTION);
+ sWatchedProgramProjectionMap.put(
+ WatchedPrograms.COLUMN_INTERNAL_TUNE_PARAMS,
+ WatchedPrograms.COLUMN_INTERNAL_TUNE_PARAMS);
+ sWatchedProgramProjectionMap.put(
+ WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN,
+ WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN);
+ sWatchedProgramProjectionMap.put(
+ WATCHED_PROGRAMS_COLUMN_CONSOLIDATED, WATCHED_PROGRAMS_COLUMN_CONSOLIDATED);
+
+ sRecordedProgramProjectionMap = new HashMap<>();
+ sRecordedProgramProjectionMap.put(RecordedPrograms._ID, RecordedPrograms._ID);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_PACKAGE_NAME, RecordedPrograms.COLUMN_PACKAGE_NAME);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_INPUT_ID, RecordedPrograms.COLUMN_INPUT_ID);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_CHANNEL_ID, RecordedPrograms.COLUMN_CHANNEL_ID);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_TITLE, RecordedPrograms.COLUMN_TITLE);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_SEASON_DISPLAY_NUMBER,
+ RecordedPrograms.COLUMN_SEASON_DISPLAY_NUMBER);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_SEASON_TITLE, RecordedPrograms.COLUMN_SEASON_TITLE);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_EPISODE_DISPLAY_NUMBER,
+ RecordedPrograms.COLUMN_EPISODE_DISPLAY_NUMBER);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_EPISODE_TITLE, RecordedPrograms.COLUMN_EPISODE_TITLE);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_START_TIME_UTC_MILLIS,
+ RecordedPrograms.COLUMN_START_TIME_UTC_MILLIS);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_END_TIME_UTC_MILLIS,
+ RecordedPrograms.COLUMN_END_TIME_UTC_MILLIS);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_BROADCAST_GENRE, RecordedPrograms.COLUMN_BROADCAST_GENRE);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_CANONICAL_GENRE, RecordedPrograms.COLUMN_CANONICAL_GENRE);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_SHORT_DESCRIPTION,
+ RecordedPrograms.COLUMN_SHORT_DESCRIPTION);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_LONG_DESCRIPTION, RecordedPrograms.COLUMN_LONG_DESCRIPTION);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_VIDEO_WIDTH, RecordedPrograms.COLUMN_VIDEO_WIDTH);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_VIDEO_HEIGHT, RecordedPrograms.COLUMN_VIDEO_HEIGHT);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_AUDIO_LANGUAGE, RecordedPrograms.COLUMN_AUDIO_LANGUAGE);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_CONTENT_RATING, RecordedPrograms.COLUMN_CONTENT_RATING);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_POSTER_ART_URI, RecordedPrograms.COLUMN_POSTER_ART_URI);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_THUMBNAIL_URI, RecordedPrograms.COLUMN_THUMBNAIL_URI);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_SEARCHABLE, RecordedPrograms.COLUMN_SEARCHABLE);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_RECORDING_DATA_URI,
+ RecordedPrograms.COLUMN_RECORDING_DATA_URI);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_RECORDING_DATA_BYTES,
+ RecordedPrograms.COLUMN_RECORDING_DATA_BYTES);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_RECORDING_DURATION_MILLIS,
+ RecordedPrograms.COLUMN_RECORDING_DURATION_MILLIS);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS,
+ RecordedPrograms.COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_DATA,
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_DATA);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG1,
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG1);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2,
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3,
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4,
+ RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_VERSION_NUMBER, RecordedPrograms.COLUMN_VERSION_NUMBER);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_REVIEW_RATING_STYLE,
+ RecordedPrograms.COLUMN_REVIEW_RATING_STYLE);
+ sRecordedProgramProjectionMap.put(
+ RecordedPrograms.COLUMN_REVIEW_RATING, RecordedPrograms.COLUMN_REVIEW_RATING);
+
+ sPreviewProgramProjectionMap = new HashMap<>();
+ sPreviewProgramProjectionMap.put(PreviewPrograms._ID, PreviewPrograms._ID);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_PACKAGE_NAME, PreviewPrograms.COLUMN_PACKAGE_NAME);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_CHANNEL_ID, PreviewPrograms.COLUMN_CHANNEL_ID);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_TITLE, PreviewPrograms.COLUMN_TITLE);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_SEASON_DISPLAY_NUMBER,
+ PreviewPrograms.COLUMN_SEASON_DISPLAY_NUMBER);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_SEASON_TITLE, PreviewPrograms.COLUMN_SEASON_TITLE);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_EPISODE_DISPLAY_NUMBER,
+ PreviewPrograms.COLUMN_EPISODE_DISPLAY_NUMBER);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_EPISODE_TITLE, PreviewPrograms.COLUMN_EPISODE_TITLE);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_CANONICAL_GENRE, PreviewPrograms.COLUMN_CANONICAL_GENRE);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_SHORT_DESCRIPTION, PreviewPrograms.COLUMN_SHORT_DESCRIPTION);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_LONG_DESCRIPTION, PreviewPrograms.COLUMN_LONG_DESCRIPTION);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_VIDEO_WIDTH, PreviewPrograms.COLUMN_VIDEO_WIDTH);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_VIDEO_HEIGHT, PreviewPrograms.COLUMN_VIDEO_HEIGHT);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_AUDIO_LANGUAGE, PreviewPrograms.COLUMN_AUDIO_LANGUAGE);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_CONTENT_RATING, PreviewPrograms.COLUMN_CONTENT_RATING);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_POSTER_ART_URI, PreviewPrograms.COLUMN_POSTER_ART_URI);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_THUMBNAIL_URI, PreviewPrograms.COLUMN_THUMBNAIL_URI);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_SEARCHABLE, PreviewPrograms.COLUMN_SEARCHABLE);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_INTERNAL_PROVIDER_DATA,
+ PreviewPrograms.COLUMN_INTERNAL_PROVIDER_DATA);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_INTERNAL_PROVIDER_FLAG1,
+ PreviewPrograms.COLUMN_INTERNAL_PROVIDER_FLAG1);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2,
+ PreviewPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3,
+ PreviewPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4,
+ PreviewPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_VERSION_NUMBER, PreviewPrograms.COLUMN_VERSION_NUMBER);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_INTERNAL_PROVIDER_ID,
+ PreviewPrograms.COLUMN_INTERNAL_PROVIDER_ID);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_PREVIEW_VIDEO_URI, PreviewPrograms.COLUMN_PREVIEW_VIDEO_URI);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_LAST_PLAYBACK_POSITION_MILLIS,
+ PreviewPrograms.COLUMN_LAST_PLAYBACK_POSITION_MILLIS);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_DURATION_MILLIS, PreviewPrograms.COLUMN_DURATION_MILLIS);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_INTENT_URI, PreviewPrograms.COLUMN_INTENT_URI);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_WEIGHT, PreviewPrograms.COLUMN_WEIGHT);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_TRANSIENT, PreviewPrograms.COLUMN_TRANSIENT);
+ sPreviewProgramProjectionMap.put(PreviewPrograms.COLUMN_TYPE, PreviewPrograms.COLUMN_TYPE);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_POSTER_ART_ASPECT_RATIO,
+ PreviewPrograms.COLUMN_POSTER_ART_ASPECT_RATIO);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_THUMBNAIL_ASPECT_RATIO,
+ PreviewPrograms.COLUMN_THUMBNAIL_ASPECT_RATIO);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_LOGO_URI, PreviewPrograms.COLUMN_LOGO_URI);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_AVAILABILITY, PreviewPrograms.COLUMN_AVAILABILITY);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_STARTING_PRICE, PreviewPrograms.COLUMN_STARTING_PRICE);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_OFFER_PRICE, PreviewPrograms.COLUMN_OFFER_PRICE);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_RELEASE_DATE, PreviewPrograms.COLUMN_RELEASE_DATE);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_ITEM_COUNT, PreviewPrograms.COLUMN_ITEM_COUNT);
+ sPreviewProgramProjectionMap.put(PreviewPrograms.COLUMN_LIVE, PreviewPrograms.COLUMN_LIVE);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_INTERACTION_TYPE, PreviewPrograms.COLUMN_INTERACTION_TYPE);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_INTERACTION_COUNT, PreviewPrograms.COLUMN_INTERACTION_COUNT);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_AUTHOR, PreviewPrograms.COLUMN_AUTHOR);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_REVIEW_RATING_STYLE,
+ PreviewPrograms.COLUMN_REVIEW_RATING_STYLE);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_REVIEW_RATING, PreviewPrograms.COLUMN_REVIEW_RATING);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_BROWSABLE, PreviewPrograms.COLUMN_BROWSABLE);
+ sPreviewProgramProjectionMap.put(
+ PreviewPrograms.COLUMN_CONTENT_ID, PreviewPrograms.COLUMN_CONTENT_ID);
+
+ sWatchNextProgramProjectionMap = new HashMap<>();
+ sWatchNextProgramProjectionMap.put(WatchNextPrograms._ID, WatchNextPrograms._ID);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_PACKAGE_NAME, WatchNextPrograms.COLUMN_PACKAGE_NAME);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_TITLE, WatchNextPrograms.COLUMN_TITLE);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_SEASON_DISPLAY_NUMBER,
+ WatchNextPrograms.COLUMN_SEASON_DISPLAY_NUMBER);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_SEASON_TITLE, WatchNextPrograms.COLUMN_SEASON_TITLE);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_EPISODE_DISPLAY_NUMBER,
+ WatchNextPrograms.COLUMN_EPISODE_DISPLAY_NUMBER);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_EPISODE_TITLE, WatchNextPrograms.COLUMN_EPISODE_TITLE);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_CANONICAL_GENRE, WatchNextPrograms.COLUMN_CANONICAL_GENRE);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_SHORT_DESCRIPTION,
+ WatchNextPrograms.COLUMN_SHORT_DESCRIPTION);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_LONG_DESCRIPTION,
+ WatchNextPrograms.COLUMN_LONG_DESCRIPTION);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_VIDEO_WIDTH, WatchNextPrograms.COLUMN_VIDEO_WIDTH);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_VIDEO_HEIGHT, WatchNextPrograms.COLUMN_VIDEO_HEIGHT);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_AUDIO_LANGUAGE, WatchNextPrograms.COLUMN_AUDIO_LANGUAGE);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_CONTENT_RATING, WatchNextPrograms.COLUMN_CONTENT_RATING);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_POSTER_ART_URI, WatchNextPrograms.COLUMN_POSTER_ART_URI);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_THUMBNAIL_URI, WatchNextPrograms.COLUMN_THUMBNAIL_URI);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_SEARCHABLE, WatchNextPrograms.COLUMN_SEARCHABLE);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_INTERNAL_PROVIDER_DATA,
+ WatchNextPrograms.COLUMN_INTERNAL_PROVIDER_DATA);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_INTERNAL_PROVIDER_FLAG1,
+ WatchNextPrograms.COLUMN_INTERNAL_PROVIDER_FLAG1);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2,
+ WatchNextPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3,
+ WatchNextPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4,
+ WatchNextPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_VERSION_NUMBER, WatchNextPrograms.COLUMN_VERSION_NUMBER);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_INTERNAL_PROVIDER_ID,
+ WatchNextPrograms.COLUMN_INTERNAL_PROVIDER_ID);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_PREVIEW_VIDEO_URI,
+ WatchNextPrograms.COLUMN_PREVIEW_VIDEO_URI);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_LAST_PLAYBACK_POSITION_MILLIS,
+ WatchNextPrograms.COLUMN_LAST_PLAYBACK_POSITION_MILLIS);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_DURATION_MILLIS, WatchNextPrograms.COLUMN_DURATION_MILLIS);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_INTENT_URI, WatchNextPrograms.COLUMN_INTENT_URI);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_TRANSIENT, WatchNextPrograms.COLUMN_TRANSIENT);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_TYPE, WatchNextPrograms.COLUMN_TYPE);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_WATCH_NEXT_TYPE, WatchNextPrograms.COLUMN_WATCH_NEXT_TYPE);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_POSTER_ART_ASPECT_RATIO,
+ WatchNextPrograms.COLUMN_POSTER_ART_ASPECT_RATIO);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_THUMBNAIL_ASPECT_RATIO,
+ WatchNextPrograms.COLUMN_THUMBNAIL_ASPECT_RATIO);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_LOGO_URI, WatchNextPrograms.COLUMN_LOGO_URI);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_AVAILABILITY, WatchNextPrograms.COLUMN_AVAILABILITY);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_STARTING_PRICE, WatchNextPrograms.COLUMN_STARTING_PRICE);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_OFFER_PRICE, WatchNextPrograms.COLUMN_OFFER_PRICE);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_RELEASE_DATE, WatchNextPrograms.COLUMN_RELEASE_DATE);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_ITEM_COUNT, WatchNextPrograms.COLUMN_ITEM_COUNT);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_LIVE, WatchNextPrograms.COLUMN_LIVE);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_INTERACTION_TYPE,
+ WatchNextPrograms.COLUMN_INTERACTION_TYPE);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_INTERACTION_COUNT,
+ WatchNextPrograms.COLUMN_INTERACTION_COUNT);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_AUTHOR, WatchNextPrograms.COLUMN_AUTHOR);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_REVIEW_RATING_STYLE,
+ WatchNextPrograms.COLUMN_REVIEW_RATING_STYLE);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_REVIEW_RATING, WatchNextPrograms.COLUMN_REVIEW_RATING);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_BROWSABLE, WatchNextPrograms.COLUMN_BROWSABLE);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_CONTENT_ID, WatchNextPrograms.COLUMN_CONTENT_ID);
+ sWatchNextProgramProjectionMap.put(
+ WatchNextPrograms.COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS,
+ WatchNextPrograms.COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS);
+ }
+
+ // Mapping from broadcast genre to canonical genre.
+ private static Map<String, String> sGenreMap;
+
+ private static final String PERMISSION_READ_TV_LISTINGS = "android.permission.READ_TV_LISTINGS";
+
+ private static final String PERMISSION_ACCESS_ALL_EPG_DATA =
+ "com.android.providers.tv.permission.ACCESS_ALL_EPG_DATA";
+
+ private static final String PERMISSION_ACCESS_WATCHED_PROGRAMS =
+ "com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS";
+
+ private static final String CREATE_RECORDED_PROGRAMS_TABLE_SQL =
+ "CREATE TABLE "
+ + RECORDED_PROGRAMS_TABLE
+ + " ("
+ + RecordedPrograms._ID
+ + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + RecordedPrograms.COLUMN_PACKAGE_NAME
+ + " TEXT NOT NULL,"
+ + RecordedPrograms.COLUMN_INPUT_ID
+ + " TEXT NOT NULL,"
+ + RecordedPrograms.COLUMN_CHANNEL_ID
+ + " INTEGER,"
+ + RecordedPrograms.COLUMN_TITLE
+ + " TEXT,"
+ + RecordedPrograms.COLUMN_SEASON_DISPLAY_NUMBER
+ + " TEXT,"
+ + RecordedPrograms.COLUMN_SEASON_TITLE
+ + " TEXT,"
+ + RecordedPrograms.COLUMN_EPISODE_DISPLAY_NUMBER
+ + " TEXT,"
+ + RecordedPrograms.COLUMN_EPISODE_TITLE
+ + " TEXT,"
+ + RecordedPrograms.COLUMN_START_TIME_UTC_MILLIS
+ + " INTEGER,"
+ + RecordedPrograms.COLUMN_END_TIME_UTC_MILLIS
+ + " INTEGER,"
+ + RecordedPrograms.COLUMN_BROADCAST_GENRE
+ + " TEXT,"
+ + RecordedPrograms.COLUMN_CANONICAL_GENRE
+ + " TEXT,"
+ + RecordedPrograms.COLUMN_SHORT_DESCRIPTION
+ + " TEXT,"
+ + RecordedPrograms.COLUMN_LONG_DESCRIPTION
+ + " TEXT,"
+ + RecordedPrograms.COLUMN_VIDEO_WIDTH
+ + " INTEGER,"
+ + RecordedPrograms.COLUMN_VIDEO_HEIGHT
+ + " INTEGER,"
+ + RecordedPrograms.COLUMN_AUDIO_LANGUAGE
+ + " TEXT,"
+ + RecordedPrograms.COLUMN_CONTENT_RATING
+ + " TEXT,"
+ + RecordedPrograms.COLUMN_POSTER_ART_URI
+ + " TEXT,"
+ + RecordedPrograms.COLUMN_THUMBNAIL_URI
+ + " TEXT,"
+ + RecordedPrograms.COLUMN_SEARCHABLE
+ + " INTEGER NOT NULL DEFAULT 1,"
+ + RecordedPrograms.COLUMN_RECORDING_DATA_URI
+ + " TEXT,"
+ + RecordedPrograms.COLUMN_RECORDING_DATA_BYTES
+ + " INTEGER,"
+ + RecordedPrograms.COLUMN_RECORDING_DURATION_MILLIS
+ + " INTEGER,"
+ + RecordedPrograms.COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS
+ + " INTEGER,"
+ + RecordedPrograms.COLUMN_INTERNAL_PROVIDER_DATA
+ + " BLOB,"
+ + RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG1
+ + " INTEGER,"
+ + RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2
+ + " INTEGER,"
+ + RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3
+ + " INTEGER,"
+ + RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4
+ + " INTEGER,"
+ + RecordedPrograms.COLUMN_VERSION_NUMBER
+ + " INTEGER,"
+ + RecordedPrograms.COLUMN_REVIEW_RATING_STYLE
+ + " INTEGER,"
+ + RecordedPrograms.COLUMN_REVIEW_RATING
+ + " TEXT,"
+ + "FOREIGN KEY("
+ + RecordedPrograms.COLUMN_CHANNEL_ID
+ + ") "
+ + "REFERENCES "
+ + CHANNELS_TABLE
+ + "("
+ + Channels._ID
+ + ") "
+ + "ON UPDATE CASCADE ON DELETE SET NULL);";
+
+ private static final String CREATE_PREVIEW_PROGRAMS_TABLE_SQL =
+ "CREATE TABLE "
+ + PREVIEW_PROGRAMS_TABLE
+ + " ("
+ + PreviewPrograms._ID
+ + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + PreviewPrograms.COLUMN_PACKAGE_NAME
+ + " TEXT NOT NULL,"
+ + PreviewPrograms.COLUMN_CHANNEL_ID
+ + " INTEGER,"
+ + PreviewPrograms.COLUMN_TITLE
+ + " TEXT,"
+ + PreviewPrograms.COLUMN_SEASON_DISPLAY_NUMBER
+ + " TEXT,"
+ + PreviewPrograms.COLUMN_SEASON_TITLE
+ + " TEXT,"
+ + PreviewPrograms.COLUMN_EPISODE_DISPLAY_NUMBER
+ + " TEXT,"
+ + PreviewPrograms.COLUMN_EPISODE_TITLE
+ + " TEXT,"
+ + PreviewPrograms.COLUMN_CANONICAL_GENRE
+ + " TEXT,"
+ + PreviewPrograms.COLUMN_SHORT_DESCRIPTION
+ + " TEXT,"
+ + PreviewPrograms.COLUMN_LONG_DESCRIPTION
+ + " TEXT,"
+ + PreviewPrograms.COLUMN_VIDEO_WIDTH
+ + " INTEGER,"
+ + PreviewPrograms.COLUMN_VIDEO_HEIGHT
+ + " INTEGER,"
+ + PreviewPrograms.COLUMN_AUDIO_LANGUAGE
+ + " TEXT,"
+ + PreviewPrograms.COLUMN_CONTENT_RATING
+ + " TEXT,"
+ + PreviewPrograms.COLUMN_POSTER_ART_URI
+ + " TEXT,"
+ + PreviewPrograms.COLUMN_THUMBNAIL_URI
+ + " TEXT,"
+ + PreviewPrograms.COLUMN_SEARCHABLE
+ + " INTEGER NOT NULL DEFAULT 1,"
+ + PreviewPrograms.COLUMN_INTERNAL_PROVIDER_DATA
+ + " BLOB,"
+ + PreviewPrograms.COLUMN_INTERNAL_PROVIDER_FLAG1
+ + " INTEGER,"
+ + PreviewPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2
+ + " INTEGER,"
+ + PreviewPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3
+ + " INTEGER,"
+ + PreviewPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4
+ + " INTEGER,"
+ + PreviewPrograms.COLUMN_VERSION_NUMBER
+ + " INTEGER,"
+ + PreviewPrograms.COLUMN_INTERNAL_PROVIDER_ID
+ + " TEXT,"
+ + PreviewPrograms.COLUMN_PREVIEW_VIDEO_URI
+ + " TEXT,"
+ + PreviewPrograms.COLUMN_LAST_PLAYBACK_POSITION_MILLIS
+ + " INTEGER,"
+ + PreviewPrograms.COLUMN_DURATION_MILLIS
+ + " INTEGER,"
+ + PreviewPrograms.COLUMN_INTENT_URI
+ + " TEXT,"
+ + PreviewPrograms.COLUMN_WEIGHT
+ + " INTEGER,"
+ + PreviewPrograms.COLUMN_TRANSIENT
+ + " INTEGER NOT NULL DEFAULT 0,"
+ + PreviewPrograms.COLUMN_TYPE
+ + " INTEGER NOT NULL,"
+ + PreviewPrograms.COLUMN_POSTER_ART_ASPECT_RATIO
+ + " INTEGER,"
+ + PreviewPrograms.COLUMN_THUMBNAIL_ASPECT_RATIO
+ + " INTEGER,"
+ + PreviewPrograms.COLUMN_LOGO_URI
+ + " TEXT,"
+ + PreviewPrograms.COLUMN_AVAILABILITY
+ + " INTERGER,"
+ + PreviewPrograms.COLUMN_STARTING_PRICE
+ + " TEXT,"
+ + PreviewPrograms.COLUMN_OFFER_PRICE
+ + " TEXT,"
+ + PreviewPrograms.COLUMN_RELEASE_DATE
+ + " TEXT,"
+ + PreviewPrograms.COLUMN_ITEM_COUNT
+ + " INTEGER,"
+ + PreviewPrograms.COLUMN_LIVE
+ + " INTEGER NOT NULL DEFAULT 0,"
+ + PreviewPrograms.COLUMN_INTERACTION_TYPE
+ + " INTEGER,"
+ + PreviewPrograms.COLUMN_INTERACTION_COUNT
+ + " INTEGER,"
+ + PreviewPrograms.COLUMN_AUTHOR
+ + " TEXT,"
+ + PreviewPrograms.COLUMN_REVIEW_RATING_STYLE
+ + " INTEGER,"
+ + PreviewPrograms.COLUMN_REVIEW_RATING
+ + " TEXT,"
+ + PreviewPrograms.COLUMN_BROWSABLE
+ + " INTEGER NOT NULL DEFAULT 1,"
+ + PreviewPrograms.COLUMN_CONTENT_ID
+ + " TEXT,"
+ + "FOREIGN KEY("
+ + PreviewPrograms.COLUMN_CHANNEL_ID
+ + ","
+ + PreviewPrograms.COLUMN_PACKAGE_NAME
+ + ") REFERENCES "
+ + CHANNELS_TABLE
+ + "("
+ + Channels._ID
+ + ","
+ + Channels.COLUMN_PACKAGE_NAME
+ + ") ON UPDATE CASCADE ON DELETE CASCADE"
+ + ");";
+ private static final String CREATE_PREVIEW_PROGRAMS_PACKAGE_NAME_INDEX_SQL =
+ "CREATE INDEX preview_programs_package_name_index ON "
+ + PREVIEW_PROGRAMS_TABLE
+ + "("
+ + PreviewPrograms.COLUMN_PACKAGE_NAME
+ + ");";
+ private static final String CREATE_PREVIEW_PROGRAMS_CHANNEL_ID_INDEX_SQL =
+ "CREATE INDEX preview_programs_id_index ON "
+ + PREVIEW_PROGRAMS_TABLE
+ + "("
+ + PreviewPrograms.COLUMN_CHANNEL_ID
+ + ");";
+ private static final String CREATE_WATCH_NEXT_PROGRAMS_TABLE_SQL =
+ "CREATE TABLE "
+ + WATCH_NEXT_PROGRAMS_TABLE
+ + " ("
+ + WatchNextPrograms._ID
+ + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + WatchNextPrograms.COLUMN_PACKAGE_NAME
+ + " TEXT NOT NULL,"
+ + WatchNextPrograms.COLUMN_TITLE
+ + " TEXT,"
+ + WatchNextPrograms.COLUMN_SEASON_DISPLAY_NUMBER
+ + " TEXT,"
+ + WatchNextPrograms.COLUMN_SEASON_TITLE
+ + " TEXT,"
+ + WatchNextPrograms.COLUMN_EPISODE_DISPLAY_NUMBER
+ + " TEXT,"
+ + WatchNextPrograms.COLUMN_EPISODE_TITLE
+ + " TEXT,"
+ + WatchNextPrograms.COLUMN_CANONICAL_GENRE
+ + " TEXT,"
+ + WatchNextPrograms.COLUMN_SHORT_DESCRIPTION
+ + " TEXT,"
+ + WatchNextPrograms.COLUMN_LONG_DESCRIPTION
+ + " TEXT,"
+ + WatchNextPrograms.COLUMN_VIDEO_WIDTH
+ + " INTEGER,"
+ + WatchNextPrograms.COLUMN_VIDEO_HEIGHT
+ + " INTEGER,"
+ + WatchNextPrograms.COLUMN_AUDIO_LANGUAGE
+ + " TEXT,"
+ + WatchNextPrograms.COLUMN_CONTENT_RATING
+ + " TEXT,"
+ + WatchNextPrograms.COLUMN_POSTER_ART_URI
+ + " TEXT,"
+ + WatchNextPrograms.COLUMN_THUMBNAIL_URI
+ + " TEXT,"
+ + WatchNextPrograms.COLUMN_SEARCHABLE
+ + " INTEGER NOT NULL DEFAULT 1,"
+ + WatchNextPrograms.COLUMN_INTERNAL_PROVIDER_DATA
+ + " BLOB,"
+ + WatchNextPrograms.COLUMN_INTERNAL_PROVIDER_FLAG1
+ + " INTEGER,"
+ + WatchNextPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2
+ + " INTEGER,"
+ + WatchNextPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3
+ + " INTEGER,"
+ + WatchNextPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4
+ + " INTEGER,"
+ + WatchNextPrograms.COLUMN_VERSION_NUMBER
+ + " INTEGER,"
+ + WatchNextPrograms.COLUMN_INTERNAL_PROVIDER_ID
+ + " TEXT,"
+ + WatchNextPrograms.COLUMN_PREVIEW_VIDEO_URI
+ + " TEXT,"
+ + WatchNextPrograms.COLUMN_LAST_PLAYBACK_POSITION_MILLIS
+ + " INTEGER,"
+ + WatchNextPrograms.COLUMN_DURATION_MILLIS
+ + " INTEGER,"
+ + WatchNextPrograms.COLUMN_INTENT_URI
+ + " TEXT,"
+ + WatchNextPrograms.COLUMN_TRANSIENT
+ + " INTEGER NOT NULL DEFAULT 0,"
+ + WatchNextPrograms.COLUMN_TYPE
+ + " INTEGER NOT NULL,"
+ + WatchNextPrograms.COLUMN_WATCH_NEXT_TYPE
+ + " INTEGER,"
+ + WatchNextPrograms.COLUMN_POSTER_ART_ASPECT_RATIO
+ + " INTEGER,"
+ + WatchNextPrograms.COLUMN_THUMBNAIL_ASPECT_RATIO
+ + " INTEGER,"
+ + WatchNextPrograms.COLUMN_LOGO_URI
+ + " TEXT,"
+ + WatchNextPrograms.COLUMN_AVAILABILITY
+ + " INTEGER,"
+ + WatchNextPrograms.COLUMN_STARTING_PRICE
+ + " TEXT,"
+ + WatchNextPrograms.COLUMN_OFFER_PRICE
+ + " TEXT,"
+ + WatchNextPrograms.COLUMN_RELEASE_DATE
+ + " TEXT,"
+ + WatchNextPrograms.COLUMN_ITEM_COUNT
+ + " INTEGER,"
+ + WatchNextPrograms.COLUMN_LIVE
+ + " INTEGER NOT NULL DEFAULT 0,"
+ + WatchNextPrograms.COLUMN_INTERACTION_TYPE
+ + " INTEGER,"
+ + WatchNextPrograms.COLUMN_INTERACTION_COUNT
+ + " INTEGER,"
+ + WatchNextPrograms.COLUMN_AUTHOR
+ + " TEXT,"
+ + WatchNextPrograms.COLUMN_REVIEW_RATING_STYLE
+ + " INTEGER,"
+ + WatchNextPrograms.COLUMN_REVIEW_RATING
+ + " TEXT,"
+ + WatchNextPrograms.COLUMN_BROWSABLE
+ + " INTEGER NOT NULL DEFAULT 1,"
+ + WatchNextPrograms.COLUMN_CONTENT_ID
+ + " TEXT,"
+ + WatchNextPrograms.COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS
+ + " INTEGER"
+ + ");";
+ private static final String CREATE_WATCH_NEXT_PROGRAMS_PACKAGE_NAME_INDEX_SQL =
+ "CREATE INDEX watch_next_programs_package_name_index ON "
+ + WATCH_NEXT_PROGRAMS_TABLE
+ + "("
+ + WatchNextPrograms.COLUMN_PACKAGE_NAME
+ + ");";
+
+ private String mCallingPackage = "com.android.tv";
+
+ static class DatabaseHelper extends SQLiteOpenHelper {
+ private Context mContext;
+
+ public static synchronized DatabaseHelper createInstance(Context context) {
+ return new DatabaseHelper(context);
+ }
+
+ private DatabaseHelper(Context context) {
+ this(context, DATABASE_NAME, DATABASE_VERSION);
+ }
+
+ @VisibleForTesting
+ DatabaseHelper(Context context, String databaseName, int databaseVersion) {
+ super(context, databaseName, null, databaseVersion);
+ mContext = context;
+ }
+
+ @Override
+ public void onConfigure(SQLiteDatabase db) {
+ db.setForeignKeyConstraintsEnabled(true);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ if (DEBUG) {
+ Log.d(TAG, "Creating database");
+ }
+ // Set up the database schema.
+ db.execSQL(
+ "CREATE TABLE "
+ + CHANNELS_TABLE
+ + " ("
+ + Channels._ID
+ + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + Channels.COLUMN_PACKAGE_NAME
+ + " TEXT NOT NULL,"
+ + Channels.COLUMN_INPUT_ID
+ + " TEXT NOT NULL,"
+ + Channels.COLUMN_TYPE
+ + " TEXT NOT NULL DEFAULT '"
+ + Channels.TYPE_OTHER
+ + "',"
+ + Channels.COLUMN_SERVICE_TYPE
+ + " TEXT NOT NULL DEFAULT '"
+ + Channels.SERVICE_TYPE_AUDIO_VIDEO
+ + "',"
+ + Channels.COLUMN_ORIGINAL_NETWORK_ID
+ + " INTEGER NOT NULL DEFAULT 0,"
+ + Channels.COLUMN_TRANSPORT_STREAM_ID
+ + " INTEGER NOT NULL DEFAULT 0,"
+ + Channels.COLUMN_SERVICE_ID
+ + " INTEGER NOT NULL DEFAULT 0,"
+ + Channels.COLUMN_DISPLAY_NUMBER
+ + " TEXT,"
+ + Channels.COLUMN_DISPLAY_NAME
+ + " TEXT,"
+ + Channels.COLUMN_NETWORK_AFFILIATION
+ + " TEXT,"
+ + Channels.COLUMN_DESCRIPTION
+ + " TEXT,"
+ + Channels.COLUMN_VIDEO_FORMAT
+ + " TEXT,"
+ + Channels.COLUMN_BROWSABLE
+ + " INTEGER NOT NULL DEFAULT 0,"
+ + Channels.COLUMN_SEARCHABLE
+ + " INTEGER NOT NULL DEFAULT 1,"
+ + Channels.COLUMN_LOCKED
+ + " INTEGER NOT NULL DEFAULT 0,"
+ + Channels.COLUMN_APP_LINK_ICON_URI
+ + " TEXT,"
+ + Channels.COLUMN_APP_LINK_POSTER_ART_URI
+ + " TEXT,"
+ + Channels.COLUMN_APP_LINK_TEXT
+ + " TEXT,"
+ + Channels.COLUMN_APP_LINK_COLOR
+ + " INTEGER,"
+ + Channels.COLUMN_APP_LINK_INTENT_URI
+ + " TEXT,"
+ + Channels.COLUMN_INTERNAL_PROVIDER_DATA
+ + " BLOB,"
+ + Channels.COLUMN_INTERNAL_PROVIDER_FLAG1
+ + " INTEGER,"
+ + Channels.COLUMN_INTERNAL_PROVIDER_FLAG2
+ + " INTEGER,"
+ + Channels.COLUMN_INTERNAL_PROVIDER_FLAG3
+ + " INTEGER,"
+ + Channels.COLUMN_INTERNAL_PROVIDER_FLAG4
+ + " INTEGER,"
+ + CHANNELS_COLUMN_LOGO
+ + " BLOB,"
+ + Channels.COLUMN_VERSION_NUMBER
+ + " INTEGER,"
+ + Channels.COLUMN_TRANSIENT
+ + " INTEGER NOT NULL DEFAULT 0,"
+ + Channels.COLUMN_INTERNAL_PROVIDER_ID
+ + " TEXT,"
+ // Needed for foreign keys in other tables.
+ + "UNIQUE("
+ + Channels._ID
+ + ","
+ + Channels.COLUMN_PACKAGE_NAME
+ + ")"
+ + ");");
+ db.execSQL(
+ "CREATE TABLE "
+ + PROGRAMS_TABLE
+ + " ("
+ + Programs._ID
+ + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + Programs.COLUMN_PACKAGE_NAME
+ + " TEXT NOT NULL,"
+ + Programs.COLUMN_CHANNEL_ID
+ + " INTEGER,"
+ + Programs.COLUMN_TITLE
+ + " TEXT,"
+ + Programs.COLUMN_SEASON_DISPLAY_NUMBER
+ + " TEXT,"
+ + Programs.COLUMN_SEASON_TITLE
+ + " TEXT,"
+ + Programs.COLUMN_EPISODE_DISPLAY_NUMBER
+ + " TEXT,"
+ + Programs.COLUMN_EPISODE_TITLE
+ + " TEXT,"
+ + Programs.COLUMN_START_TIME_UTC_MILLIS
+ + " INTEGER,"
+ + Programs.COLUMN_END_TIME_UTC_MILLIS
+ + " INTEGER,"
+ + Programs.COLUMN_BROADCAST_GENRE
+ + " TEXT,"
+ + Programs.COLUMN_CANONICAL_GENRE
+ + " TEXT,"
+ + Programs.COLUMN_SHORT_DESCRIPTION
+ + " TEXT,"
+ + Programs.COLUMN_LONG_DESCRIPTION
+ + " TEXT,"
+ + Programs.COLUMN_VIDEO_WIDTH
+ + " INTEGER,"
+ + Programs.COLUMN_VIDEO_HEIGHT
+ + " INTEGER,"
+ + Programs.COLUMN_AUDIO_LANGUAGE
+ + " TEXT,"
+ + Programs.COLUMN_CONTENT_RATING
+ + " TEXT,"
+ + Programs.COLUMN_POSTER_ART_URI
+ + " TEXT,"
+ + Programs.COLUMN_THUMBNAIL_URI
+ + " TEXT,"
+ + Programs.COLUMN_SEARCHABLE
+ + " INTEGER NOT NULL DEFAULT 1,"
+ + Programs.COLUMN_RECORDING_PROHIBITED
+ + " INTEGER NOT NULL DEFAULT 0,"
+ + Programs.COLUMN_INTERNAL_PROVIDER_DATA
+ + " BLOB,"
+ + Programs.COLUMN_INTERNAL_PROVIDER_FLAG1
+ + " INTEGER,"
+ + Programs.COLUMN_INTERNAL_PROVIDER_FLAG2
+ + " INTEGER,"
+ + Programs.COLUMN_INTERNAL_PROVIDER_FLAG3
+ + " INTEGER,"
+ + Programs.COLUMN_INTERNAL_PROVIDER_FLAG4
+ + " INTEGER,"
+ + Programs.COLUMN_REVIEW_RATING_STYLE
+ + " INTEGER,"
+ + Programs.COLUMN_REVIEW_RATING
+ + " TEXT,"
+ + Programs.COLUMN_VERSION_NUMBER
+ + " INTEGER,"
+ + "FOREIGN KEY("
+ + Programs.COLUMN_CHANNEL_ID
+ + ","
+ + Programs.COLUMN_PACKAGE_NAME
+ + ") REFERENCES "
+ + CHANNELS_TABLE
+ + "("
+ + Channels._ID
+ + ","
+ + Channels.COLUMN_PACKAGE_NAME
+ + ") ON UPDATE CASCADE ON DELETE CASCADE"
+ + ");");
+ db.execSQL(
+ "CREATE INDEX "
+ + PROGRAMS_TABLE_PACKAGE_NAME_INDEX
+ + " ON "
+ + PROGRAMS_TABLE
+ + "("
+ + Programs.COLUMN_PACKAGE_NAME
+ + ");");
+ db.execSQL(
+ "CREATE INDEX "
+ + PROGRAMS_TABLE_CHANNEL_ID_INDEX
+ + " ON "
+ + PROGRAMS_TABLE
+ + "("
+ + Programs.COLUMN_CHANNEL_ID
+ + ");");
+ db.execSQL(
+ "CREATE INDEX "
+ + PROGRAMS_TABLE_START_TIME_INDEX
+ + " ON "
+ + PROGRAMS_TABLE
+ + "("
+ + Programs.COLUMN_START_TIME_UTC_MILLIS
+ + ");");
+ db.execSQL(
+ "CREATE INDEX "
+ + PROGRAMS_TABLE_END_TIME_INDEX
+ + " ON "
+ + PROGRAMS_TABLE
+ + "("
+ + Programs.COLUMN_END_TIME_UTC_MILLIS
+ + ");");
+ db.execSQL(
+ "CREATE TABLE "
+ + WATCHED_PROGRAMS_TABLE
+ + " ("
+ + WatchedPrograms._ID
+ + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + WatchedPrograms.COLUMN_PACKAGE_NAME
+ + " TEXT NOT NULL,"
+ + WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS
+ + " INTEGER NOT NULL DEFAULT 0,"
+ + WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS
+ + " INTEGER NOT NULL DEFAULT 0,"
+ + WatchedPrograms.COLUMN_CHANNEL_ID
+ + " INTEGER,"
+ + WatchedPrograms.COLUMN_TITLE
+ + " TEXT,"
+ + WatchedPrograms.COLUMN_START_TIME_UTC_MILLIS
+ + " INTEGER,"
+ + WatchedPrograms.COLUMN_END_TIME_UTC_MILLIS
+ + " INTEGER,"
+ + WatchedPrograms.COLUMN_DESCRIPTION
+ + " TEXT,"
+ + WatchedPrograms.COLUMN_INTERNAL_TUNE_PARAMS
+ + " TEXT,"
+ + WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN
+ + " TEXT NOT NULL,"
+ + WATCHED_PROGRAMS_COLUMN_CONSOLIDATED
+ + " INTEGER NOT NULL DEFAULT 0,"
+ + "FOREIGN KEY("
+ + WatchedPrograms.COLUMN_CHANNEL_ID
+ + ","
+ + WatchedPrograms.COLUMN_PACKAGE_NAME
+ + ") REFERENCES "
+ + CHANNELS_TABLE
+ + "("
+ + Channels._ID
+ + ","
+ + Channels.COLUMN_PACKAGE_NAME
+ + ") ON UPDATE CASCADE ON DELETE CASCADE"
+ + ");");
+ db.execSQL(
+ "CREATE INDEX "
+ + WATCHED_PROGRAMS_TABLE_CHANNEL_ID_INDEX
+ + " ON "
+ + WATCHED_PROGRAMS_TABLE
+ + "("
+ + WatchedPrograms.COLUMN_CHANNEL_ID
+ + ");");
+ db.execSQL(CREATE_RECORDED_PROGRAMS_TABLE_SQL);
+ db.execSQL(CREATE_PREVIEW_PROGRAMS_TABLE_SQL);
+ db.execSQL(CREATE_PREVIEW_PROGRAMS_PACKAGE_NAME_INDEX_SQL);
+ db.execSQL(CREATE_PREVIEW_PROGRAMS_CHANNEL_ID_INDEX_SQL);
+ db.execSQL(CREATE_WATCH_NEXT_PROGRAMS_TABLE_SQL);
+ db.execSQL(CREATE_WATCH_NEXT_PROGRAMS_PACKAGE_NAME_INDEX_SQL);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ if (oldVersion < 23) {
+ Log.i(
+ TAG,
+ "Upgrading from version "
+ + oldVersion
+ + " to "
+ + newVersion
+ + ", data will be lost!");
+ db.execSQL("DROP TABLE IF EXISTS " + DELETED_CHANNELS_TABLE);
+ db.execSQL("DROP TABLE IF EXISTS " + WATCHED_PROGRAMS_TABLE);
+ db.execSQL("DROP TABLE IF EXISTS " + PROGRAMS_TABLE);
+ db.execSQL("DROP TABLE IF EXISTS " + CHANNELS_TABLE);
+
+ onCreate(db);
+ return;
+ }
+
+ Log.i(TAG, "Upgrading from version " + oldVersion + " to " + newVersion + ".");
+ if (oldVersion <= 23) {
+ db.execSQL(
+ "ALTER TABLE "
+ + CHANNELS_TABLE
+ + " ADD "
+ + Channels.COLUMN_INTERNAL_PROVIDER_FLAG1
+ + " INTEGER;");
+ db.execSQL(
+ "ALTER TABLE "
+ + CHANNELS_TABLE
+ + " ADD "
+ + Channels.COLUMN_INTERNAL_PROVIDER_FLAG2
+ + " INTEGER;");
+ db.execSQL(
+ "ALTER TABLE "
+ + CHANNELS_TABLE
+ + " ADD "
+ + Channels.COLUMN_INTERNAL_PROVIDER_FLAG3
+ + " INTEGER;");
+ db.execSQL(
+ "ALTER TABLE "
+ + CHANNELS_TABLE
+ + " ADD "
+ + Channels.COLUMN_INTERNAL_PROVIDER_FLAG4
+ + " INTEGER;");
+ }
+ if (oldVersion <= 24) {
+ db.execSQL(
+ "ALTER TABLE "
+ + PROGRAMS_TABLE
+ + " ADD "
+ + Programs.COLUMN_INTERNAL_PROVIDER_FLAG1
+ + " INTEGER;");
+ db.execSQL(
+ "ALTER TABLE "
+ + PROGRAMS_TABLE
+ + " ADD "
+ + Programs.COLUMN_INTERNAL_PROVIDER_FLAG2
+ + " INTEGER;");
+ db.execSQL(
+ "ALTER TABLE "
+ + PROGRAMS_TABLE
+ + " ADD "
+ + Programs.COLUMN_INTERNAL_PROVIDER_FLAG3
+ + " INTEGER;");
+ db.execSQL(
+ "ALTER TABLE "
+ + PROGRAMS_TABLE
+ + " ADD "
+ + Programs.COLUMN_INTERNAL_PROVIDER_FLAG4
+ + " INTEGER;");
+ }
+ if (oldVersion <= 25) {
+ db.execSQL(
+ "ALTER TABLE "
+ + CHANNELS_TABLE
+ + " ADD "
+ + Channels.COLUMN_APP_LINK_ICON_URI
+ + " TEXT;");
+ db.execSQL(
+ "ALTER TABLE "
+ + CHANNELS_TABLE
+ + " ADD "
+ + Channels.COLUMN_APP_LINK_POSTER_ART_URI
+ + " TEXT;");
+ db.execSQL(
+ "ALTER TABLE "
+ + CHANNELS_TABLE
+ + " ADD "
+ + Channels.COLUMN_APP_LINK_TEXT
+ + " TEXT;");
+ db.execSQL(
+ "ALTER TABLE "
+ + CHANNELS_TABLE
+ + " ADD "
+ + Channels.COLUMN_APP_LINK_COLOR
+ + " INTEGER;");
+ db.execSQL(
+ "ALTER TABLE "
+ + CHANNELS_TABLE
+ + " ADD "
+ + Channels.COLUMN_APP_LINK_INTENT_URI
+ + " TEXT;");
+ db.execSQL(
+ "ALTER TABLE "
+ + PROGRAMS_TABLE
+ + " ADD "
+ + Programs.COLUMN_SEARCHABLE
+ + " INTEGER NOT NULL DEFAULT 1;");
+ }
+ if (oldVersion <= 28) {
+ db.execSQL(
+ "ALTER TABLE "
+ + PROGRAMS_TABLE
+ + " ADD "
+ + Programs.COLUMN_SEASON_TITLE
+ + " TEXT;");
+ migrateIntegerColumnToTextColumn(
+ db,
+ PROGRAMS_TABLE,
+ Programs.COLUMN_SEASON_NUMBER,
+ Programs.COLUMN_SEASON_DISPLAY_NUMBER);
+ migrateIntegerColumnToTextColumn(
+ db,
+ PROGRAMS_TABLE,
+ Programs.COLUMN_EPISODE_NUMBER,
+ Programs.COLUMN_EPISODE_DISPLAY_NUMBER);
+ }
+ if (oldVersion <= 29) {
+ db.execSQL("DROP TABLE IF EXISTS " + RECORDED_PROGRAMS_TABLE);
+ db.execSQL(CREATE_RECORDED_PROGRAMS_TABLE_SQL);
+ }
+ if (oldVersion <= 30) {
+ db.execSQL(
+ "ALTER TABLE "
+ + PROGRAMS_TABLE
+ + " ADD "
+ + Programs.COLUMN_RECORDING_PROHIBITED
+ + " INTEGER NOT NULL DEFAULT 0;");
+ }
+ if (oldVersion <= 32) {
+ db.execSQL(
+ "ALTER TABLE "
+ + CHANNELS_TABLE
+ + " ADD "
+ + Channels.COLUMN_TRANSIENT
+ + " INTEGER NOT NULL DEFAULT 0;");
+ db.execSQL(
+ "ALTER TABLE "
+ + CHANNELS_TABLE
+ + " ADD "
+ + Channels.COLUMN_INTERNAL_PROVIDER_ID
+ + " TEXT;");
+ db.execSQL(
+ "ALTER TABLE "
+ + PROGRAMS_TABLE
+ + " ADD "
+ + Programs.COLUMN_REVIEW_RATING_STYLE
+ + " INTEGER;");
+ db.execSQL(
+ "ALTER TABLE "
+ + PROGRAMS_TABLE
+ + " ADD "
+ + Programs.COLUMN_REVIEW_RATING
+ + " TEXT;");
+ if (oldVersion > 29) {
+ db.execSQL(
+ "ALTER TABLE "
+ + RECORDED_PROGRAMS_TABLE
+ + " ADD "
+ + RecordedPrograms.COLUMN_REVIEW_RATING_STYLE
+ + " INTEGER;");
+ db.execSQL(
+ "ALTER TABLE "
+ + RECORDED_PROGRAMS_TABLE
+ + " ADD "
+ + RecordedPrograms.COLUMN_REVIEW_RATING
+ + " TEXT;");
+ }
+ }
+ if (oldVersion <= 33) {
+ db.execSQL("DROP TABLE IF EXISTS " + PREVIEW_PROGRAMS_TABLE);
+ db.execSQL("DROP TABLE IF EXISTS " + WATCH_NEXT_PROGRAMS_TABLE);
+ db.execSQL(CREATE_PREVIEW_PROGRAMS_TABLE_SQL);
+ db.execSQL(CREATE_PREVIEW_PROGRAMS_PACKAGE_NAME_INDEX_SQL);
+ db.execSQL(CREATE_PREVIEW_PROGRAMS_CHANNEL_ID_INDEX_SQL);
+ db.execSQL(CREATE_WATCH_NEXT_PROGRAMS_TABLE_SQL);
+ db.execSQL(CREATE_WATCH_NEXT_PROGRAMS_PACKAGE_NAME_INDEX_SQL);
+ }
+ Log.i(TAG, "Upgrading from version " + oldVersion + " to " + newVersion + " is done.");
+ }
+
+ @Override
+ public void onOpen(SQLiteDatabase db) {
+ // Call a static method on the TvProvider because changes to sInitialized must
+ // be guarded by a lock on the class.
+ initOnOpenIfNeeded(mContext, db);
+ }
+
+ private static void migrateIntegerColumnToTextColumn(
+ SQLiteDatabase db, String table, String integerColumn, String textColumn) {
+ db.execSQL("ALTER TABLE " + table + " ADD " + textColumn + " TEXT;");
+ db.execSQL(
+ "UPDATE "
+ + table
+ + " SET "
+ + textColumn
+ + " = CAST("
+ + integerColumn
+ + " AS TEXT);");
+ }
+ }
+
+ private DatabaseHelper mOpenHelper;
+ private static SharedPreferences sBlockedPackagesSharedPreference;
+ private static Map<String, Boolean> sBlockedPackages;
+
+ @Override
+ public boolean onCreate() {
+ if (DEBUG) {
+ Log.d(TAG, "Creating TvProvider");
+ }
+ mOpenHelper = DatabaseHelper.createInstance(getContext());
+ return true;
+ }
+
+ @VisibleForTesting
+ String getCallingPackage_() {
+ return mCallingPackage;
+ }
+
+ public void setCallingPackage(String packageName) {
+ mCallingPackage = packageName;
+ }
+
+ void setOpenHelper(DatabaseHelper helper) {
+ mOpenHelper = helper;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ switch (sUriMatcher.match(uri)) {
+ case MATCH_CHANNEL:
+ return Channels.CONTENT_TYPE;
+ case MATCH_CHANNEL_ID:
+ return Channels.CONTENT_ITEM_TYPE;
+ case MATCH_CHANNEL_ID_LOGO:
+ return "image/png";
+ case MATCH_PASSTHROUGH_ID:
+ return Channels.CONTENT_ITEM_TYPE;
+ case MATCH_PROGRAM:
+ return Programs.CONTENT_TYPE;
+ case MATCH_PROGRAM_ID:
+ return Programs.CONTENT_ITEM_TYPE;
+ case MATCH_WATCHED_PROGRAM:
+ return WatchedPrograms.CONTENT_TYPE;
+ case MATCH_WATCHED_PROGRAM_ID:
+ return WatchedPrograms.CONTENT_ITEM_TYPE;
+ case MATCH_RECORDED_PROGRAM:
+ return RecordedPrograms.CONTENT_TYPE;
+ case MATCH_RECORDED_PROGRAM_ID:
+ return RecordedPrograms.CONTENT_ITEM_TYPE;
+ case MATCH_PREVIEW_PROGRAM:
+ return PreviewPrograms.CONTENT_TYPE;
+ case MATCH_PREVIEW_PROGRAM_ID:
+ return PreviewPrograms.CONTENT_ITEM_TYPE;
+ case MATCH_WATCH_NEXT_PROGRAM:
+ return WatchNextPrograms.CONTENT_TYPE;
+ case MATCH_WATCH_NEXT_PROGRAM_ID:
+ return WatchNextPrograms.CONTENT_ITEM_TYPE;
+ default:
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ }
+
+ @Override
+ public Bundle call(String method, String arg, Bundle extras) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Cursor query(
+ Uri uri,
+ String[] projection,
+ String selection,
+ String[] selectionArgs,
+ String sortOrder) {
+ ensureInitialized();
+ boolean needsToValidateSortOrder = !callerHasAccessAllEpgDataPermission();
+ SqlParams params = createSqlParams(OP_QUERY, uri, selection, selectionArgs);
+
+ SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
+ queryBuilder.setStrict(needsToValidateSortOrder);
+ queryBuilder.setTables(params.getTables());
+ String orderBy = null;
+ Map<String, String> projectionMap;
+ switch (params.getTables()) {
+ case PROGRAMS_TABLE:
+ projectionMap = sProgramProjectionMap;
+ orderBy = DEFAULT_PROGRAMS_SORT_ORDER;
+ break;
+ case WATCHED_PROGRAMS_TABLE:
+ projectionMap = sWatchedProgramProjectionMap;
+ orderBy = DEFAULT_WATCHED_PROGRAMS_SORT_ORDER;
+ break;
+ case RECORDED_PROGRAMS_TABLE:
+ projectionMap = sRecordedProgramProjectionMap;
+ break;
+ case PREVIEW_PROGRAMS_TABLE:
+ projectionMap = sPreviewProgramProjectionMap;
+ break;
+ case WATCH_NEXT_PROGRAMS_TABLE:
+ projectionMap = sWatchNextProgramProjectionMap;
+ break;
+ default:
+ projectionMap = sChannelProjectionMap;
+ break;
+ }
+ queryBuilder.setProjectionMap(createProjectionMapForQuery(projection, projectionMap));
+ if (needsToValidateSortOrder) {
+ validateSortOrder(sortOrder, projectionMap.keySet());
+ }
+
+ // Use the default sort order only if no sort order is specified.
+ if (!TextUtils.isEmpty(sortOrder)) {
+ orderBy = sortOrder;
+ }
+
+ // Get the database and run the query.
+ SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ Cursor c =
+ queryBuilder.query(
+ db,
+ projection,
+ params.getSelection(),
+ params.getSelectionArgs(),
+ null,
+ null,
+ orderBy);
+
+ // Tell the cursor what URI to watch, so it knows when its source data changes.
+ c.setNotificationUri(getContext().getContentResolver(), uri);
+ return c;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ ensureInitialized();
+ switch (sUriMatcher.match(uri)) {
+ case MATCH_CHANNEL:
+ // Preview channels are not necessarily associated with TV input service.
+ // Therefore, we fill a fake ID to meet not null restriction for preview channels.
+ if (values.get(Channels.COLUMN_INPUT_ID) == null
+ && TvContractCompat.PARAM_CHANNEL.equals(
+ values.get(Channels.COLUMN_TYPE))) {
+ values.put(Channels.COLUMN_INPUT_ID, EMPTY_STRING);
+ }
+ filterContentValues(values, sChannelProjectionMap);
+ return insertChannel(uri, values);
+ case MATCH_PROGRAM:
+ filterContentValues(values, sProgramProjectionMap);
+ return insertProgram(uri, values);
+ case MATCH_WATCHED_PROGRAM:
+ return insertWatchedProgram(uri, values);
+ case MATCH_RECORDED_PROGRAM:
+ filterContentValues(values, sRecordedProgramProjectionMap);
+ return insertRecordedProgram(uri, values);
+ case MATCH_PREVIEW_PROGRAM:
+ filterContentValues(values, sPreviewProgramProjectionMap);
+ return insertPreviewProgram(uri, values);
+ case MATCH_WATCH_NEXT_PROGRAM:
+ filterContentValues(values, sWatchNextProgramProjectionMap);
+ return insertWatchNextProgram(uri, values);
+ case MATCH_CHANNEL_ID:
+ case MATCH_CHANNEL_ID_LOGO:
+ case MATCH_PASSTHROUGH_ID:
+ case MATCH_PROGRAM_ID:
+ case MATCH_WATCHED_PROGRAM_ID:
+ case MATCH_RECORDED_PROGRAM_ID:
+ case MATCH_PREVIEW_PROGRAM_ID:
+ throw new UnsupportedOperationException("Cannot insert into that URI: " + uri);
+ default:
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ }
+
+ private Uri insertChannel(Uri uri, ContentValues values) {
+ if (TextUtils.equals(
+ values.getAsString(Channels.COLUMN_TYPE), TvContractCompat.Channels.TYPE_PREVIEW)) {
+ blockIllegalAccessFromBlockedPackage();
+ }
+ // Mark the owner package of this channel.
+ values.put(Channels.COLUMN_PACKAGE_NAME, getCallingPackage_());
+ blockIllegalAccessToChannelsSystemColumns(values);
+
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ long rowId = db.insert(CHANNELS_TABLE, null, values);
+ if (rowId > 0) {
+ Uri channelUri = TvContractCompat.buildChannelUri(rowId);
+ notifyChange(channelUri);
+ return channelUri;
+ }
+
+ throw new SQLException("Failed to insert row into " + uri);
+ }
+
+ private Uri insertProgram(Uri uri, ContentValues values) {
+ if (!callerHasAccessAllEpgDataPermission()
+ || !values.containsKey(Programs.COLUMN_PACKAGE_NAME)) {
+ // Mark the owner package of this program. System app with a proper permission may
+ // change the owner of the program.
+ values.put(Programs.COLUMN_PACKAGE_NAME, getCallingPackage_());
+ }
+
+ checkAndConvertGenre(values);
+ checkAndConvertDeprecatedColumns(values);
+
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ long rowId = db.insert(PROGRAMS_TABLE, null, values);
+ if (rowId > 0) {
+ Uri programUri = TvContractCompat.buildProgramUri(rowId);
+ notifyChange(programUri);
+ return programUri;
+ }
+
+ throw new SQLException("Failed to insert row into " + uri);
+ }
+
+ private Uri insertWatchedProgram(Uri uri, ContentValues values) {
+ if (DEBUG) {
+ Log.d(TAG, "insertWatchedProgram(uri=" + uri + ", values={" + values + "})");
+ }
+ Long watchStartTime = values.getAsLong(WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS);
+ Long watchEndTime = values.getAsLong(WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS);
+ // The system sends only two kinds of watch events:
+ // 1. The user tunes to a new channel. (COLUMN_WATCH_START_TIME_UTC_MILLIS)
+ // 2. The user stops watching. (COLUMN_WATCH_END_TIME_UTC_MILLIS)
+ if (watchStartTime != null && watchEndTime == null) {
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ long rowId = db.insert(WATCHED_PROGRAMS_TABLE, null, values);
+ if (rowId > 0) {
+ return ContentUris.withAppendedId(WatchedPrograms.CONTENT_URI, rowId);
+ }
+ Log.w(TAG, "Failed to insert row for " + values + ". Channel does not exist.");
+ return null;
+ } else if (watchStartTime == null && watchEndTime != null) {
+ return null;
+ }
+ // All the other cases are invalid.
+ throw new IllegalArgumentException(
+ "Only one of COLUMN_WATCH_START_TIME_UTC_MILLIS and"
+ + " COLUMN_WATCH_END_TIME_UTC_MILLIS should be specified");
+ }
+
+ private Uri insertRecordedProgram(Uri uri, ContentValues values) {
+ // Mark the owner package of this program.
+ values.put(Programs.COLUMN_PACKAGE_NAME, getCallingPackage_());
+
+ checkAndConvertGenre(values);
+
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ long rowId = db.insert(RECORDED_PROGRAMS_TABLE, null, values);
+ if (rowId > 0) {
+ Uri recordedProgramUri = TvContractCompat.buildRecordedProgramUri(rowId);
+ notifyChange(recordedProgramUri);
+ return recordedProgramUri;
+ }
+
+ throw new SQLException("Failed to insert row into " + uri);
+ }
+
+ private Uri insertPreviewProgram(Uri uri, ContentValues values) {
+ if (!values.containsKey(PreviewPrograms.COLUMN_TYPE)) {
+ throw new IllegalArgumentException(
+ "Missing the required column: " + PreviewPrograms.COLUMN_TYPE);
+ }
+ blockIllegalAccessFromBlockedPackage();
+ // Mark the owner package of this program.
+ values.put(Programs.COLUMN_PACKAGE_NAME, getCallingPackage_());
+ blockIllegalAccessToPreviewProgramsSystemColumns(values);
+
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ long rowId = db.insert(PREVIEW_PROGRAMS_TABLE, null, values);
+ if (rowId > 0) {
+ Uri previewProgramUri = TvContractCompat.buildPreviewProgramUri(rowId);
+ notifyChange(previewProgramUri);
+ return previewProgramUri;
+ }
+
+ throw new SQLException("Failed to insert row into " + uri);
+ }
+
+ private Uri insertWatchNextProgram(Uri uri, ContentValues values) {
+ if (!values.containsKey(WatchNextPrograms.COLUMN_TYPE)) {
+ throw new IllegalArgumentException(
+ "Missing the required column: " + WatchNextPrograms.COLUMN_TYPE);
+ }
+ blockIllegalAccessFromBlockedPackage();
+ if (!callerHasAccessAllEpgDataPermission()
+ || !values.containsKey(Programs.COLUMN_PACKAGE_NAME)) {
+ // Mark the owner package of this program. System app with a proper permission may
+ // change the owner of the program.
+ values.put(Programs.COLUMN_PACKAGE_NAME, getCallingPackage_());
+ }
+ blockIllegalAccessToPreviewProgramsSystemColumns(values);
+
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ long rowId = db.insert(WATCH_NEXT_PROGRAMS_TABLE, null, values);
+ if (rowId > 0) {
+ Uri watchNextProgramUri = TvContractCompat.buildWatchNextProgramUri(rowId);
+ notifyChange(watchNextProgramUri);
+ return watchNextProgramUri;
+ }
+
+ throw new SQLException("Failed to insert row into " + uri);
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ SqlParams params = createSqlParams(OP_DELETE, uri, selection, selectionArgs);
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ int count;
+ switch (sUriMatcher.match(uri)) {
+ case MATCH_CHANNEL_ID_LOGO:
+ ContentValues values = new ContentValues();
+ values.putNull(CHANNELS_COLUMN_LOGO);
+ count =
+ db.update(
+ params.getTables(),
+ values,
+ params.getSelection(),
+ params.getSelectionArgs());
+ break;
+ case MATCH_CHANNEL:
+ case MATCH_PROGRAM:
+ case MATCH_WATCHED_PROGRAM:
+ case MATCH_RECORDED_PROGRAM:
+ case MATCH_PREVIEW_PROGRAM:
+ case MATCH_WATCH_NEXT_PROGRAM:
+ case MATCH_CHANNEL_ID:
+ case MATCH_PASSTHROUGH_ID:
+ case MATCH_PROGRAM_ID:
+ case MATCH_WATCHED_PROGRAM_ID:
+ case MATCH_RECORDED_PROGRAM_ID:
+ case MATCH_PREVIEW_PROGRAM_ID:
+ case MATCH_WATCH_NEXT_PROGRAM_ID:
+ count =
+ db.delete(
+ params.getTables(),
+ params.getSelection(),
+ params.getSelectionArgs());
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ if (count > 0) {
+ notifyChange(uri);
+ }
+ return count;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ SqlParams params = createSqlParams(OP_UPDATE, uri, selection, selectionArgs);
+ blockIllegalAccessToIdAndPackageName(uri, values);
+ boolean containImmutableColumn = false;
+ if (params.getTables().equals(CHANNELS_TABLE)) {
+ filterContentValues(values, sChannelProjectionMap);
+ containImmutableColumn = disallowModifyChannelType(values, params);
+ if (containImmutableColumn && sUriMatcher.match(uri) != MATCH_CHANNEL_ID) {
+ Log.i(TAG, "Updating failed. Attempt to change immutable column for channels.");
+ return 0;
+ }
+ blockIllegalAccessToChannelsSystemColumns(values);
+ } else if (params.getTables().equals(PROGRAMS_TABLE)) {
+ filterContentValues(values, sProgramProjectionMap);
+ checkAndConvertGenre(values);
+ checkAndConvertDeprecatedColumns(values);
+ } else if (params.getTables().equals(RECORDED_PROGRAMS_TABLE)) {
+ filterContentValues(values, sRecordedProgramProjectionMap);
+ checkAndConvertGenre(values);
+ } else if (params.getTables().equals(PREVIEW_PROGRAMS_TABLE)) {
+ filterContentValues(values, sPreviewProgramProjectionMap);
+ containImmutableColumn = disallowModifyChannelId(values, params);
+ if (containImmutableColumn && PreviewPrograms.CONTENT_URI.equals(uri)) {
+ Log.i(
+ TAG,
+ "Updating failed. Attempt to change unmodifiable column for "
+ + "preview programs.");
+ return 0;
+ }
+ blockIllegalAccessToPreviewProgramsSystemColumns(values);
+ } else if (params.getTables().equals(WATCH_NEXT_PROGRAMS_TABLE)) {
+ filterContentValues(values, sWatchNextProgramProjectionMap);
+ blockIllegalAccessToPreviewProgramsSystemColumns(values);
+ }
+ if (values.size() == 0) {
+ // All values may be filtered out, no need to update
+ return 0;
+ }
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ int count =
+ db.update(
+ params.getTables(),
+ values,
+ params.getSelection(),
+ params.getSelectionArgs());
+ if (count > 0) {
+ notifyChange(uri);
+ } else if (containImmutableColumn) {
+ Log.i(
+ TAG,
+ "Updating failed. The item may not exist or attempt to change "
+ + "immutable column.");
+ }
+ return count;
+ }
+
+ private synchronized void ensureInitialized() {
+ if (!sInitialized) {
+ // Database is not accessed before and the projection maps and the blocked package list
+ // are not updated yet. Gets database here to make it initialized.
+ mOpenHelper.getReadableDatabase();
+ }
+ }
+
+ private static synchronized void initOnOpenIfNeeded(Context context, SQLiteDatabase db) {
+ if (!sInitialized) {
+ updateProjectionMap(db, CHANNELS_TABLE, sChannelProjectionMap);
+ updateProjectionMap(db, PROGRAMS_TABLE, sProgramProjectionMap);
+ updateProjectionMap(db, WATCHED_PROGRAMS_TABLE, sWatchedProgramProjectionMap);
+ updateProjectionMap(db, RECORDED_PROGRAMS_TABLE, sRecordedProgramProjectionMap);
+ updateProjectionMap(db, PREVIEW_PROGRAMS_TABLE, sPreviewProgramProjectionMap);
+ updateProjectionMap(db, WATCH_NEXT_PROGRAMS_TABLE, sWatchNextProgramProjectionMap);
+ sBlockedPackagesSharedPreference =
+ PreferenceManager.getDefaultSharedPreferences(context);
+ sBlockedPackages = new ConcurrentHashMap<>();
+ for (String packageName :
+ sBlockedPackagesSharedPreference.getStringSet(
+ SHARED_PREF_BLOCKED_PACKAGES_KEY, new HashSet<>())) {
+ sBlockedPackages.put(packageName, true);
+ }
+ sInitialized = true;
+ }
+ }
+
+ private static void updateProjectionMap(
+ SQLiteDatabase db, String tableName, Map<String, String> projectionMap) {
+ try (Cursor cursor = db.rawQuery("SELECT * FROM " + tableName + " LIMIT 0", null)) {
+ for (String columnName : cursor.getColumnNames()) {
+ if (!projectionMap.containsKey(columnName)) {
+ projectionMap.put(columnName, tableName + '.' + columnName);
+ }
+ }
+ }
+ }
+
+ private Map<String, String> createProjectionMapForQuery(
+ String[] projection, Map<String, String> projectionMap) {
+ if (projection == null) {
+ return projectionMap;
+ }
+ Map<String, String> columnProjectionMap = new HashMap<>();
+ for (String columnName : projection) {
+ // Value NULL will be provided if the requested column does not exist in the database.
+ columnProjectionMap.put(
+ columnName, projectionMap.getOrDefault(columnName, "NULL as " + columnName));
+ }
+ return columnProjectionMap;
+ }
+
+ private void filterContentValues(ContentValues values, Map<String, String> projectionMap) {
+ Iterator<String> iter = values.keySet().iterator();
+ while (iter.hasNext()) {
+ String columnName = iter.next();
+ if (!projectionMap.containsKey(columnName)) {
+ iter.remove();
+ }
+ }
+ }
+
+ private SqlParams createSqlParams(
+ String operation, Uri uri, String selection, String[] selectionArgs) {
+ int match = sUriMatcher.match(uri);
+ SqlParams params = new SqlParams(null, selection, selectionArgs);
+
+ // Control access to EPG data (excluding watched programs) when the caller doesn't have all
+ // access.
+ String prefix = match == MATCH_CHANNEL ? CHANNELS_TABLE + "." : "";
+ if (!callerHasAccessAllEpgDataPermission()
+ && match != MATCH_WATCHED_PROGRAM
+ && match != MATCH_WATCHED_PROGRAM_ID) {
+ if (!TextUtils.isEmpty(selection)) {
+ throw new SecurityException("Selection not allowed for " + uri);
+ }
+ // Limit the operation only to the data that the calling package owns except for when
+ // the caller tries to read TV listings and has the appropriate permission.
+ if (operation.equals(OP_QUERY) && callerHasReadTvListingsPermission()) {
+ params.setWhere(
+ prefix
+ + BaseTvColumns.COLUMN_PACKAGE_NAME
+ + "=? OR "
+ + Channels.COLUMN_SEARCHABLE
+ + "=?",
+ getCallingPackage_(),
+ "1");
+ } else {
+ params.setWhere(
+ prefix + BaseTvColumns.COLUMN_PACKAGE_NAME + "=?", getCallingPackage_());
+ }
+ }
+ String packageName = uri.getQueryParameter(PARAM_PACKAGE);
+ if (packageName != null) {
+ params.appendWhere(prefix + BaseTvColumns.COLUMN_PACKAGE_NAME + "=?", packageName);
+ }
+
+ switch (match) {
+ case MATCH_CHANNEL:
+ String genre = uri.getQueryParameter(TvContractCompat.PARAM_CANONICAL_GENRE);
+ if (genre == null) {
+ params.setTables(CHANNELS_TABLE);
+ } else {
+ if (!operation.equals(OP_QUERY)) {
+ throw new SecurityException(
+ capitalize(operation) + " not allowed for " + uri);
+ }
+ if (!Genres.isCanonical(genre)) {
+ throw new IllegalArgumentException("Not a canonical genre : " + genre);
+ }
+ params.setTables(CHANNELS_TABLE_INNER_JOIN_PROGRAMS_TABLE);
+ String curTime = String.valueOf(System.currentTimeMillis());
+ params.appendWhere(
+ "LIKE(?, "
+ + Programs.COLUMN_CANONICAL_GENRE
+ + ") AND "
+ + Programs.COLUMN_START_TIME_UTC_MILLIS
+ + "<=? AND "
+ + Programs.COLUMN_END_TIME_UTC_MILLIS
+ + ">=?",
+ "%" + genre + "%",
+ curTime,
+ curTime);
+ }
+ String inputId = uri.getQueryParameter(TvContractCompat.PARAM_INPUT);
+ if (inputId != null) {
+ params.appendWhere(Channels.COLUMN_INPUT_ID + "=?", inputId);
+ }
+ boolean browsableOnly =
+ uri.getBooleanQueryParameter(TvContractCompat.PARAM_BROWSABLE_ONLY, false);
+ if (browsableOnly) {
+ params.appendWhere(Channels.COLUMN_BROWSABLE + "=1");
+ }
+ String preview = uri.getQueryParameter(PARAM_PREVIEW);
+ if (preview != null) {
+ String previewSelection =
+ Channels.COLUMN_TYPE
+ + (preview.equals(String.valueOf(true)) ? "=?" : "!=?");
+ params.appendWhere(previewSelection, Channels.TYPE_PREVIEW);
+ }
+ break;
+ case MATCH_CHANNEL_ID:
+ params.setTables(CHANNELS_TABLE);
+ params.appendWhere(Channels._ID + "=?", uri.getLastPathSegment());
+ break;
+ case MATCH_PROGRAM:
+ params.setTables(PROGRAMS_TABLE);
+ String paramChannelId = uri.getQueryParameter(TvContractCompat.PARAM_CHANNEL);
+ if (paramChannelId != null) {
+ String channelId = String.valueOf(Long.parseLong(paramChannelId));
+ params.appendWhere(Programs.COLUMN_CHANNEL_ID + "=?", channelId);
+ }
+ String paramStartTime = uri.getQueryParameter(TvContractCompat.PARAM_START_TIME);
+ String paramEndTime = uri.getQueryParameter(TvContractCompat.PARAM_END_TIME);
+ if (paramStartTime != null && paramEndTime != null) {
+ String startTime = String.valueOf(Long.parseLong(paramStartTime));
+ String endTime = String.valueOf(Long.parseLong(paramEndTime));
+ params.appendWhere(
+ Programs.COLUMN_START_TIME_UTC_MILLIS
+ + "<=? AND "
+ + Programs.COLUMN_END_TIME_UTC_MILLIS
+ + ">=? AND ?<=?",
+ endTime,
+ startTime,
+ startTime,
+ endTime);
+ }
+ break;
+ case MATCH_PROGRAM_ID:
+ params.setTables(PROGRAMS_TABLE);
+ params.appendWhere(Programs._ID + "=?", uri.getLastPathSegment());
+ break;
+ case MATCH_WATCHED_PROGRAM:
+ if (!callerHasAccessWatchedProgramsPermission()) {
+ throw new SecurityException("Access not allowed for " + uri);
+ }
+ params.setTables(WATCHED_PROGRAMS_TABLE);
+ params.appendWhere(WATCHED_PROGRAMS_COLUMN_CONSOLIDATED + "=?", "1");
+ break;
+ case MATCH_WATCHED_PROGRAM_ID:
+ if (!callerHasAccessWatchedProgramsPermission()) {
+ throw new SecurityException("Access not allowed for " + uri);
+ }
+ params.setTables(WATCHED_PROGRAMS_TABLE);
+ params.appendWhere(WatchedPrograms._ID + "=?", uri.getLastPathSegment());
+ params.appendWhere(WATCHED_PROGRAMS_COLUMN_CONSOLIDATED + "=?", "1");
+ break;
+ case MATCH_RECORDED_PROGRAM_ID:
+ params.appendWhere(RecordedPrograms._ID + "=?", uri.getLastPathSegment());
+ // fall-through
+ case MATCH_RECORDED_PROGRAM:
+ params.setTables(RECORDED_PROGRAMS_TABLE);
+ paramChannelId = uri.getQueryParameter(TvContractCompat.PARAM_CHANNEL);
+ if (paramChannelId != null) {
+ String channelId = String.valueOf(Long.parseLong(paramChannelId));
+ params.appendWhere(Programs.COLUMN_CHANNEL_ID + "=?", channelId);
+ }
+ break;
+ case MATCH_PREVIEW_PROGRAM_ID:
+ params.appendWhere(PreviewPrograms._ID + "=?", uri.getLastPathSegment());
+ // fall-through
+ case MATCH_PREVIEW_PROGRAM:
+ params.setTables(PREVIEW_PROGRAMS_TABLE);
+ paramChannelId = uri.getQueryParameter(TvContractCompat.PARAM_CHANNEL);
+ if (paramChannelId != null) {
+ String channelId = String.valueOf(Long.parseLong(paramChannelId));
+ params.appendWhere(PreviewPrograms.COLUMN_CHANNEL_ID + "=?", channelId);
+ }
+ break;
+ case MATCH_WATCH_NEXT_PROGRAM_ID:
+ params.appendWhere(WatchNextPrograms._ID + "=?", uri.getLastPathSegment());
+ // fall-through
+ case MATCH_WATCH_NEXT_PROGRAM:
+ params.setTables(WATCH_NEXT_PROGRAMS_TABLE);
+ break;
+ case MATCH_CHANNEL_ID_LOGO:
+ if (operation.equals(OP_DELETE)) {
+ params.setTables(CHANNELS_TABLE);
+ params.appendWhere(Channels._ID + "=?", uri.getPathSegments().get(1));
+ break;
+ }
+ // fall-through
+ case MATCH_PASSTHROUGH_ID:
+ throw new UnsupportedOperationException(operation + " not permmitted on " + uri);
+ default:
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ return params;
+ }
+
+ private static String capitalize(String str) {
+ return Character.toUpperCase(str.charAt(0)) + str.substring(1);
+ }
+
+ @SuppressLint("DefaultLocale")
+ private void checkAndConvertGenre(ContentValues values) {
+ String canonicalGenres = values.getAsString(Programs.COLUMN_CANONICAL_GENRE);
+
+ if (!TextUtils.isEmpty(canonicalGenres)) {
+ // Check if the canonical genres are valid. If not, clear them.
+ String[] genres = Genres.decode(canonicalGenres);
+ for (String genre : genres) {
+ if (!Genres.isCanonical(genre)) {
+ values.putNull(Programs.COLUMN_CANONICAL_GENRE);
+ canonicalGenres = null;
+ break;
+ }
+ }
+ }
+
+ if (TextUtils.isEmpty(canonicalGenres)) {
+ // If the canonical genre is not set, try to map the broadcast genre to the canonical
+ // genre.
+ String broadcastGenres = values.getAsString(Programs.COLUMN_BROADCAST_GENRE);
+ if (!TextUtils.isEmpty(broadcastGenres)) {
+ Set<String> genreSet = new HashSet<>();
+ String[] genres = Genres.decode(broadcastGenres);
+ for (String genre : genres) {
+ String canonicalGenre = sGenreMap.get(genre.toUpperCase());
+ if (Genres.isCanonical(canonicalGenre)) {
+ genreSet.add(canonicalGenre);
+ }
+ }
+ if (genreSet.size() > 0) {
+ values.put(
+ Programs.COLUMN_CANONICAL_GENRE,
+ Genres.encode(genreSet.toArray(new String[genreSet.size()])));
+ }
+ }
+ }
+ }
+
+ private void checkAndConvertDeprecatedColumns(ContentValues values) {
+ if (values.containsKey(Programs.COLUMN_SEASON_NUMBER)) {
+ if (!values.containsKey(Programs.COLUMN_SEASON_DISPLAY_NUMBER)) {
+ values.put(
+ Programs.COLUMN_SEASON_DISPLAY_NUMBER,
+ values.getAsInteger(Programs.COLUMN_SEASON_NUMBER));
+ }
+ values.remove(Programs.COLUMN_SEASON_NUMBER);
+ }
+ if (values.containsKey(Programs.COLUMN_EPISODE_NUMBER)) {
+ if (!values.containsKey(Programs.COLUMN_EPISODE_DISPLAY_NUMBER)) {
+ values.put(
+ Programs.COLUMN_EPISODE_DISPLAY_NUMBER,
+ values.getAsInteger(Programs.COLUMN_EPISODE_NUMBER));
+ }
+ values.remove(Programs.COLUMN_EPISODE_NUMBER);
+ }
+ }
+
+ // We might have more than one thread trying to make its way through applyBatch() so the
+ // notification coalescing needs to be thread-local to work correctly.
+ private final ThreadLocal<Set<Uri>> mTLBatchNotifications = new ThreadLocal<>();
+
+ private Set<Uri> getBatchNotificationsSet() {
+ return mTLBatchNotifications.get();
+ }
+
+ private void setBatchNotificationsSet(Set<Uri> batchNotifications) {
+ mTLBatchNotifications.set(batchNotifications);
+ }
+
+ @Override
+ public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
+ throws OperationApplicationException {
+ setBatchNotificationsSet(new HashSet<Uri>());
+ Context context = getContext();
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ db.beginTransaction();
+ try {
+ ContentProviderResult[] results = super.applyBatch(operations);
+ db.setTransactionSuccessful();
+ return results;
+ } finally {
+ db.endTransaction();
+ final Set<Uri> notifications = getBatchNotificationsSet();
+ setBatchNotificationsSet(null);
+ for (final Uri uri : notifications) {
+ context.getContentResolver().notifyChange(uri, null);
+ }
+ }
+ }
+
+ @Override
+ public int bulkInsert(Uri uri, ContentValues[] values) {
+ setBatchNotificationsSet(new HashSet<Uri>());
+ Context context = getContext();
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ db.beginTransaction();
+ try {
+ int result = super.bulkInsert(uri, values);
+ db.setTransactionSuccessful();
+ return result;
+ } finally {
+ db.endTransaction();
+ final Set<Uri> notifications = getBatchNotificationsSet();
+ setBatchNotificationsSet(null);
+ for (final Uri notificationUri : notifications) {
+ context.getContentResolver().notifyChange(notificationUri, null);
+ }
+ }
+ }
+
+ private void notifyChange(Uri uri) {
+ final Set<Uri> batchNotifications = getBatchNotificationsSet();
+ if (batchNotifications != null) {
+ batchNotifications.add(uri);
+ } else {
+ getContext().getContentResolver().notifyChange(uri, null);
+ }
+ }
+
+ private boolean callerHasReadTvListingsPermission() {
+ return getContext().checkCallingOrSelfPermission(PERMISSION_READ_TV_LISTINGS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private boolean callerHasAccessAllEpgDataPermission() {
+ return getContext().checkCallingOrSelfPermission(PERMISSION_ACCESS_ALL_EPG_DATA)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private boolean callerHasAccessWatchedProgramsPermission() {
+ return getContext().checkCallingOrSelfPermission(PERMISSION_ACCESS_WATCHED_PROGRAMS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private boolean callerHasModifyParentalControlsPermission() {
+ return getContext()
+ .checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_PARENTAL_CONTROLS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private void blockIllegalAccessToIdAndPackageName(Uri uri, ContentValues values) {
+ if (values.containsKey(BaseColumns._ID)) {
+ int match = sUriMatcher.match(uri);
+ switch (match) {
+ case MATCH_CHANNEL_ID:
+ case MATCH_PROGRAM_ID:
+ case MATCH_PREVIEW_PROGRAM_ID:
+ case MATCH_RECORDED_PROGRAM_ID:
+ case MATCH_WATCH_NEXT_PROGRAM_ID:
+ case MATCH_WATCHED_PROGRAM_ID:
+ if (TextUtils.equals(
+ values.getAsString(BaseColumns._ID), uri.getLastPathSegment())) {
+ break;
+ }
+ // fall through
+ default:
+ throw new IllegalArgumentException("Not allowed to change ID.");
+ }
+ }
+ if (values.containsKey(BaseTvColumns.COLUMN_PACKAGE_NAME)
+ && !callerHasAccessAllEpgDataPermission()
+ && !TextUtils.equals(
+ values.getAsString(BaseTvColumns.COLUMN_PACKAGE_NAME),
+ getCallingPackage_())) {
+ throw new SecurityException("Not allowed to change package name.");
+ }
+ }
+
+ private void blockIllegalAccessToChannelsSystemColumns(ContentValues values) {
+ if (values.containsKey(Channels.COLUMN_LOCKED)
+ && !callerHasModifyParentalControlsPermission()) {
+ throw new SecurityException("Not allowed to access Channels.COLUMN_LOCKED");
+ }
+ Boolean hasAccessAllEpgDataPermission = null;
+ if (values.containsKey(Channels.COLUMN_BROWSABLE)) {
+ hasAccessAllEpgDataPermission = callerHasAccessAllEpgDataPermission();
+ if (!hasAccessAllEpgDataPermission) {
+ throw new SecurityException("Not allowed to access Channels.COLUMN_BROWSABLE");
+ }
+ }
+ }
+
+ private void blockIllegalAccessToPreviewProgramsSystemColumns(ContentValues values) {
+ if (values.containsKey(PreviewPrograms.COLUMN_BROWSABLE)
+ && !callerHasAccessAllEpgDataPermission()) {
+ throw new SecurityException("Not allowed to access Programs.COLUMN_BROWSABLE");
+ }
+ }
+
+ private void blockIllegalAccessFromBlockedPackage() {
+ String callingPackageName = getCallingPackage_();
+ if (sBlockedPackages.containsKey(callingPackageName)) {
+ throw new SecurityException(
+ "Not allowed to access "
+ + TvContractCompat.AUTHORITY
+ + ", "
+ + callingPackageName
+ + " is blocked");
+ }
+ }
+
+ private boolean disallowModifyChannelType(ContentValues values, SqlParams params) {
+ if (values.containsKey(Channels.COLUMN_TYPE)) {
+ params.appendWhere(
+ Channels.COLUMN_TYPE + "=?", values.getAsString(Channels.COLUMN_TYPE));
+ return true;
+ }
+ return false;
+ }
+
+ private boolean disallowModifyChannelId(ContentValues values, SqlParams params) {
+ if (values.containsKey(PreviewPrograms.COLUMN_CHANNEL_ID)) {
+ params.appendWhere(
+ PreviewPrograms.COLUMN_CHANNEL_ID + "=?",
+ values.getAsString(PreviewPrograms.COLUMN_CHANNEL_ID));
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+ switch (sUriMatcher.match(uri)) {
+ case MATCH_CHANNEL_ID_LOGO:
+ return openLogoFile(uri, mode);
+ default:
+ throw new FileNotFoundException(uri.toString());
+ }
+ }
+
+ private ParcelFileDescriptor openLogoFile(Uri uri, String mode) throws FileNotFoundException {
+ long channelId = Long.parseLong(uri.getPathSegments().get(1));
+
+ SqlParams params =
+ new SqlParams(CHANNELS_TABLE, Channels._ID + "=?", String.valueOf(channelId));
+ if (!callerHasAccessAllEpgDataPermission()) {
+ if (callerHasReadTvListingsPermission()) {
+ params.appendWhere(
+ Channels.COLUMN_PACKAGE_NAME + "=? OR " + Channels.COLUMN_SEARCHABLE + "=?",
+ getCallingPackage_(),
+ "1");
+ } else {
+ params.appendWhere(Channels.COLUMN_PACKAGE_NAME + "=?", getCallingPackage_());
+ }
+ }
+
+ SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
+ queryBuilder.setTables(params.getTables());
+
+ // We don't write the database here.
+ SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ if (mode.equals("r")) {
+ String sql =
+ queryBuilder.buildQuery(
+ new String[] {CHANNELS_COLUMN_LOGO},
+ params.getSelection(),
+ null,
+ null,
+ null,
+ null);
+ ParcelFileDescriptor fd =
+ DatabaseUtils.blobFileDescriptorForQuery(db, sql, params.getSelectionArgs());
+ if (fd == null) {
+ throw new FileNotFoundException(uri.toString());
+ }
+ return fd;
+ } else {
+ try (Cursor cursor =
+ queryBuilder.query(
+ db,
+ new String[] {Channels._ID},
+ params.getSelection(),
+ params.getSelectionArgs(),
+ null,
+ null,
+ null)) {
+ if (cursor.getCount() < 1) {
+ // Fails early if corresponding channel does not exist.
+ // PipeMonitor may still fail to update DB later.
+ throw new FileNotFoundException(uri.toString());
+ }
+ }
+
+ try {
+ ParcelFileDescriptor[] pipeFds = ParcelFileDescriptor.createPipe();
+ PipeMonitor pipeMonitor = new PipeMonitor(pipeFds[0], channelId, params);
+ pipeMonitor.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ return pipeFds[1];
+ } catch (IOException ioe) {
+ FileNotFoundException fne = new FileNotFoundException(uri.toString());
+ fne.initCause(ioe);
+ throw fne;
+ }
+ }
+ }
+
+ /**
+ * Validates the sort order based on the given field set.
+ *
+ * @throws IllegalArgumentException if there is any unknown field.
+ */
+ @SuppressLint("DefaultLocale")
+ private static void validateSortOrder(String sortOrder, Set<String> possibleFields) {
+ if (TextUtils.isEmpty(sortOrder) || possibleFields.isEmpty()) {
+ return;
+ }
+ String[] orders = sortOrder.split(",");
+ for (String order : orders) {
+ String field =
+ order.replaceAll("\\s+", " ")
+ .trim()
+ .toLowerCase()
+ .replace(" asc", "")
+ .replace(" desc", "");
+ if (!possibleFields.contains(field)) {
+ throw new IllegalArgumentException("Illegal field in sort order " + order);
+ }
+ }
+ }
+
+ private class PipeMonitor extends AsyncTask<Void, Void, Void> {
+ private final ParcelFileDescriptor mPfd;
+ private final long mChannelId;
+ private final SqlParams mParams;
+
+ private PipeMonitor(ParcelFileDescriptor pfd, long channelId, SqlParams params) {
+ mPfd = pfd;
+ mChannelId = channelId;
+ mParams = params;
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ int count = 0;
+ try (AutoCloseInputStream is = new AutoCloseInputStream(mPfd);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+ Bitmap bitmap = BitmapFactory.decodeStream(is);
+ if (bitmap == null) {
+ Log.e(TAG, "Failed to decode logo image for channel ID " + mChannelId);
+ return null;
+ }
+
+ float scaleFactor =
+ Math.min(
+ 1f,
+ ((float) MAX_LOGO_IMAGE_SIZE)
+ / Math.max(bitmap.getWidth(), bitmap.getHeight()));
+ if (scaleFactor < 1f) {
+ bitmap =
+ Bitmap.createScaledBitmap(
+ bitmap,
+ (int) (bitmap.getWidth() * scaleFactor),
+ (int) (bitmap.getHeight() * scaleFactor),
+ false);
+ }
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
+ byte[] bytes = baos.toByteArray();
+
+ ContentValues values = new ContentValues();
+ values.put(CHANNELS_COLUMN_LOGO, bytes);
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ count =
+ db.update(
+ mParams.getTables(),
+ values,
+ mParams.getSelection(),
+ mParams.getSelectionArgs());
+ if (count > 0) {
+ Uri uri = TvContractCompat.buildChannelLogoUri(mChannelId);
+ notifyChange(uri);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to write logo for channel ID " + mChannelId, e);
+
+ } finally {
+ if (count == 0) {
+ try {
+ mPfd.closeWithError("Failed to write logo for channel ID " + mChannelId);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Failed to close pipe", ioe);
+ }
+ }
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Column definitions for the TV programs that the user watched. Applications do not have access
+ * to this table.
+ *
+ * <p>
+ *
+ * <p>By default, the query results will be sorted by {@link
+ * WatchedPrograms#COLUMN_WATCH_START_TIME_UTC_MILLIS} in descending order.
+ *
+ * @hide
+ */
+ public static final class WatchedPrograms implements BaseTvColumns {
+
+ /** The content:// style URI for this table. */
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://" + TvContract.AUTHORITY + "/watched_program");
+
+ /** The MIME type of a directory of watched programs. */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/watched_program";
+
+ /** The MIME type of a single item in this table. */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/watched_program";
+
+ /**
+ * The UTC time that the user started watching this TV program, in milliseconds since the
+ * epoch.
+ *
+ * <p>
+ *
+ * <p>Type: INTEGER (long)
+ */
+ public static final String COLUMN_WATCH_START_TIME_UTC_MILLIS =
+ "watch_start_time_utc_millis";
+
+ /**
+ * The UTC time that the user stopped watching this TV program, in milliseconds since the
+ * epoch.
+ *
+ * <p>
+ *
+ * <p>Type: INTEGER (long)
+ */
+ public static final String COLUMN_WATCH_END_TIME_UTC_MILLIS = "watch_end_time_utc_millis";
+
+ /**
+ * The ID of the TV channel that provides this TV program.
+ *
+ * <p>
+ *
+ * <p>This is a required field.
+ *
+ * <p>
+ *
+ * <p>Type: INTEGER (long)
+ */
+ public static final String COLUMN_CHANNEL_ID = "channel_id";
+
+ /**
+ * The title of this TV program.
+ *
+ * <p>
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_TITLE = "title";
+
+ /**
+ * The start time of this TV program, in milliseconds since the epoch.
+ *
+ * <p>
+ *
+ * <p>Type: INTEGER (long)
+ */
+ public static final String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+
+ /**
+ * The end time of this TV program, in milliseconds since the epoch.
+ *
+ * <p>
+ *
+ * <p>Type: INTEGER (long)
+ */
+ public static final String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+
+ /**
+ * The description of this TV program.
+ *
+ * <p>
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_DESCRIPTION = "description";
+
+ /**
+ * Extra parameters given to {@link TvInputService.Session#tune(Uri, android.os.Bundle)
+ * TvInputService.Session.tune(Uri, android.os.Bundle)} when tuning to the channel that
+ * provides this TV program. (Used internally.)
+ *
+ * <p>
+ *
+ * <p>This column contains an encoded string that represents comma-separated key-value pairs
+ * of the tune parameters. (Ex. "[key1]=[value1], [key2]=[value2]"). '%' is used as an
+ * escape character for '%', '=', and ','.
+ *
+ * <p>
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_INTERNAL_TUNE_PARAMS = "tune_params";
+
+ /**
+ * The session token of this TV program. (Used internally.)
+ *
+ * <p>
+ *
+ * <p>This contains a String representation of {@link IBinder} for {@link
+ * TvInputService.Session} that provides the current TV program. It is used internally to
+ * distinguish watched programs entries from different TV input sessions.
+ *
+ * <p>
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_INTERNAL_SESSION_TOKEN = "session_token";
+
+ private WatchedPrograms() {}
+ }
+}
diff --git a/tests/common/src/com/android/tv/testing/SingletonProvider.java b/tests/common/src/com/android/tv/testing/SingletonProvider.java
new file mode 100644
index 00000000..d9c2d409
--- /dev/null
+++ b/tests/common/src/com/android/tv/testing/SingletonProvider.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 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.testing;
+
+import javax.inject.Provider;
+
+/** A Provider that always returns the same instance. */
+public class SingletonProvider<T> implements Provider<T> {
+ private final T t;
+
+ private SingletonProvider(T t) {
+ this.t = t;
+ }
+
+ @Override
+ public T get() {
+ return t;
+ }
+
+ public static <S, T extends S> Provider<S> create(T t) {
+ return new SingletonProvider<S>(t);
+ }
+}
diff --git a/tests/common/src/com/android/tv/testing/TestSingletonApp.java b/tests/common/src/com/android/tv/testing/TestSingletonApp.java
new file mode 100644
index 00000000..f55ed8d4
--- /dev/null
+++ b/tests/common/src/com/android/tv/testing/TestSingletonApp.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2017 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.testing;
+
+import android.app.Application;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.media.tv.TvInputManager;
+import android.os.AsyncTask;
+import com.android.tv.InputSessionManager;
+import com.android.tv.MainActivityWrapper;
+import com.android.tv.TvSingletons;
+import com.android.tv.analytics.Analytics;
+import com.android.tv.analytics.Tracker;
+import com.android.tv.common.BaseApplication;
+import com.android.tv.common.config.api.RemoteConfig;
+import com.android.tv.common.experiments.ExperimentLoader;
+import com.android.tv.common.recording.RecordingStorageStatusManager;
+import com.android.tv.common.util.Clock;
+import com.android.tv.data.ChannelDataManager;
+import com.android.tv.data.PreviewDataManager;
+import com.android.tv.data.ProgramDataManager;
+import com.android.tv.data.epg.EpgFetcher;
+import com.android.tv.data.epg.EpgReader;
+import com.android.tv.dvr.DvrDataManager;
+import com.android.tv.dvr.DvrManager;
+import com.android.tv.dvr.DvrScheduleManager;
+import com.android.tv.dvr.DvrWatchedPositionManager;
+import com.android.tv.dvr.recorder.RecordingScheduler;
+import com.android.tv.perf.PerformanceMonitor;
+import com.android.tv.perf.StubPerformanceMonitor;
+import com.android.tv.testing.dvr.DvrDataManagerInMemoryImpl;
+import com.android.tv.testing.testdata.TestData;
+import com.android.tv.tuner.TunerInputController;
+import com.android.tv.util.SetupUtils;
+import com.android.tv.util.TvInputManagerHelper;
+import com.android.tv.util.account.AccountHelper;
+import java.util.concurrent.Executor;
+import javax.inject.Provider;
+
+/** Test application for Live TV. */
+public class TestSingletonApp extends Application implements TvSingletons {
+ public final FakeClock fakeClock = FakeClock.createWithCurrentTime();
+ public final FakeEpgReader epgReader = new FakeEpgReader(fakeClock);
+ public final FakeRemoteConfig remoteConfig = new FakeRemoteConfig();
+ public final FakeEpgFetcher epgFetcher = new FakeEpgFetcher();
+
+ public FakeTvInputManagerHelper tvInputManagerHelper;
+ public SetupUtils setupUtils;
+ public DvrManager dvrManager;
+ public DvrDataManager mDvrDataManager;
+
+ private final Provider<EpgReader> mEpgReaderProvider = SingletonProvider.create(epgReader);
+ private TunerInputController mTunerInputController;
+ private PerformanceMonitor mPerformanceMonitor;
+ private ChannelDataManager mChannelDataManager;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mTunerInputController =
+ new TunerInputController(
+ ComponentName.unflattenFromString(getEmbeddedTunerInputId()));
+
+ tvInputManagerHelper = new FakeTvInputManagerHelper(this);
+ setupUtils = SetupUtils.createForTvSingletons(this);
+ tvInputManagerHelper.start();
+ mChannelDataManager = new ChannelDataManager(this, tvInputManagerHelper);
+ mChannelDataManager.start();
+ mDvrDataManager = new DvrDataManagerInMemoryImpl(this, fakeClock);
+ // HACK reset the singleton for tests
+ BaseApplication.sSingletons = this;
+ }
+
+ public void loadTestData(TestData testData, long durationMs) {
+ tvInputManagerHelper
+ .getFakeTvInputManager()
+ .add(testData.getTvInputInfo(), TvInputManager.INPUT_STATE_CONNECTED);
+ testData.init(this, fakeClock, durationMs);
+ }
+
+ @Override
+ public Analytics getAnalytics() {
+ return null;
+ }
+
+ @Override
+ public void handleInputCountChanged() {}
+
+ @Override
+ public ChannelDataManager getChannelDataManager() {
+ return mChannelDataManager;
+ }
+
+ @Override
+ public boolean isChannelDataManagerLoadFinished() {
+ return false;
+ }
+
+ @Override
+ public ProgramDataManager getProgramDataManager() {
+ return null;
+ }
+
+ @Override
+ public boolean isProgramDataManagerCurrentProgramsLoadFinished() {
+ return false;
+ }
+
+ @Override
+ public PreviewDataManager getPreviewDataManager() {
+ return null;
+ }
+
+ @Override
+ public DvrDataManager getDvrDataManager() {
+ return mDvrDataManager;
+ }
+
+ @Override
+ public DvrScheduleManager getDvrScheduleManager() {
+ return null;
+ }
+
+ @Override
+ public DvrManager getDvrManager() {
+ return dvrManager;
+ }
+
+ @Override
+ public RecordingScheduler getRecordingScheduler() {
+ return null;
+ }
+
+ @Override
+ public DvrWatchedPositionManager getDvrWatchedPositionManager() {
+ return null;
+ }
+
+ @Override
+ public InputSessionManager getInputSessionManager() {
+ return null;
+ }
+
+ @Override
+ public Tracker getTracker() {
+ return null;
+ }
+
+ @Override
+ public TvInputManagerHelper getTvInputManagerHelper() {
+ return tvInputManagerHelper;
+ }
+
+ @Override
+ public Provider<EpgReader> providesEpgReader() {
+ return mEpgReaderProvider;
+ }
+
+ @Override
+ public EpgFetcher getEpgFetcher() {
+ return epgFetcher;
+ }
+
+ @Override
+ public SetupUtils getSetupUtils() {
+ return setupUtils;
+ }
+
+ @Override
+ public TunerInputController getTunerInputController() {
+ return mTunerInputController;
+ }
+
+ @Override
+ public ExperimentLoader getExperimentLoader() {
+ return new ExperimentLoader();
+ }
+
+ @Override
+ public MainActivityWrapper getMainActivityWrapper() {
+ return null;
+ }
+
+ @Override
+ public AccountHelper getAccountHelper() {
+ return null;
+ }
+
+ @Override
+ public Clock getClock() {
+ return fakeClock;
+ }
+
+ @Override
+ public RecordingStorageStatusManager getRecordingStorageStatusManager() {
+ return null;
+ }
+
+ @Override
+ public RemoteConfig getRemoteConfig() {
+ return remoteConfig;
+ }
+
+ @Override
+ public Intent getTunerSetupIntent(Context context) {
+ return null;
+ }
+
+ @Override
+ public boolean isRunningInMainProcess() {
+ return false;
+ }
+
+ @Override
+ public PerformanceMonitor getPerformanceMonitor() {
+ if (mPerformanceMonitor == null) {
+ mPerformanceMonitor = new StubPerformanceMonitor();
+ }
+ return mPerformanceMonitor;
+ }
+
+ @Override
+ public String getEmbeddedTunerInputId() {
+ return "com.android.tv/.tuner.tvinput.TunerTvInputService";
+ }
+
+ @Override
+ public Executor getDbExecutor() {
+ return AsyncTask.SERIAL_EXECUTOR;
+ }
+}
diff --git a/tests/unit/src/com/android/tv/BaseMainActivityTestCase.java b/tests/common/src/com/android/tv/testing/activities/BaseMainActivityTestCase.java
index e6f1af7e..666f8181 100644
--- a/tests/unit/src/com/android/tv/BaseMainActivityTestCase.java
+++ b/tests/common/src/com/android/tv/testing/activities/BaseMainActivityTestCase.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.tv;
+package com.android.tv.testing.activities;
import static android.support.test.InstrumentationRegistry.getInstrumentation;
@@ -21,23 +21,19 @@ import android.content.Context;
import android.os.SystemClock;
import android.support.test.rule.ActivityTestRule;
import android.text.TextUtils;
-
-import com.android.tv.data.Channel;
+import com.android.tv.MainActivity;
import com.android.tv.data.ChannelDataManager;
-import com.android.tv.testing.ChannelInfo;
+import com.android.tv.data.api.Channel;
+import com.android.tv.testing.data.ChannelInfo;
import com.android.tv.testing.testinput.ChannelStateData;
import com.android.tv.testing.testinput.TestInputControlConnection;
import com.android.tv.testing.testinput.TestInputControlUtils;
import com.android.tv.testing.testinput.TvTestInputConstants;
-
+import java.util.List;
import org.junit.Before;
import org.junit.Rule;
-import java.util.List;
-
-/**
- * Base TestCase for tests that need a {@link MainActivity}.
- */
+/** Base TestCase for tests that need a {@link MainActivity}. */
public abstract class BaseMainActivityTestCase {
private static final String TAG = "BaseMainActivityTest";
private static final int CHANNEL_LOADING_CHECK_INTERVAL_MS = 10;
@@ -54,8 +50,11 @@ public abstract class BaseMainActivityTestCase {
public void setUp() {
mActivity = mActivityTestRule.getActivity();
// TODO: ensure the SampleInputs are setup.
- getInstrumentation().getTargetContext()
- .bindService(TestInputControlUtils.createIntent(), mConnection,
+ getInstrumentation()
+ .getTargetContext()
+ .bindService(
+ TestInputControlUtils.createIntent(),
+ mConnection,
Context.BIND_AUTO_CREATE);
}
@@ -73,17 +72,17 @@ public abstract class BaseMainActivityTestCase {
*/
protected void tuneToChannel(final Channel channel) {
// Run on UI thread so views can be modified
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mActivity.tuneToChannel(channel);
- }
- });
+ getInstrumentation()
+ .runOnMainSync(
+ new Runnable() {
+ @Override
+ public void run() {
+ mActivity.tuneToChannel(channel);
+ }
+ });
}
- /**
- * Sleep until @{@link ChannelDataManager#isDbLoadFinished()} is true.
- */
+ /** Sleep until @{@link ChannelDataManager#isDbLoadFinished()} is true. */
protected void waitUntilChannelLoadingFinish() {
ChannelDataManager channelDataManager = mActivity.getChannelDataManager();
while (!channelDataManager.isDbLoadFinished()) {
@@ -102,9 +101,7 @@ public abstract class BaseMainActivityTestCase {
tuneToChannel(c);
}
- /**
- * Tune to channel.
- */
+ /** Tune to channel. */
protected void tuneToChannel(ChannelInfo channel) {
tuneToChannel(channel.name);
}
@@ -112,13 +109,14 @@ public abstract class BaseMainActivityTestCase {
/**
* Update the channel state to {@code data} then tune to that channel.
*
- * @param data the state to update the channel with.
+ * @param data the state to update the channel with.
* @param channel the channel to tune to
*/
protected void updateThenTune(ChannelStateData data, ChannelInfo channel) {
if (channel.equals(TvTestInputConstants.CH_1_DEFAULT_DONT_MODIFY)) {
throw new IllegalArgumentException(
- "By convention " + TvTestInputConstants.CH_1_DEFAULT_DONT_MODIFY.name
+ "By convention "
+ + TvTestInputConstants.CH_1_DEFAULT_DONT_MODIFY.name
+ " should not be modified.");
}
mConnection.updateChannelState(channel, data);
@@ -128,7 +126,7 @@ public abstract class BaseMainActivityTestCase {
private Channel findChannelWithName(String displayName) {
waitUntilChannelLoadingFinish();
Channel channel = null;
- List <Channel> channelList = mActivity.getChannelDataManager().getChannelList();
+ List<Channel> channelList = mActivity.getChannelDataManager().getChannelList();
for (Channel c : channelList) {
if (TextUtils.equals(c.getDisplayName(), displayName)) {
channel = c;
diff --git a/src/com/android/tv/tuner/exoplayer/ffmpeg/IFfmpegDecoder.aidl b/tests/common/src/com/android/tv/testing/constants/ConfigConstants.java
index ed053790..890c51e0 100644
--- a/src/com/android/tv/tuner/exoplayer/ffmpeg/IFfmpegDecoder.aidl
+++ b/tests/common/src/com/android/tv/testing/constants/ConfigConstants.java
@@ -11,19 +11,18 @@
* 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.
+ * limitations under the License
*/
-package com.android.tv.tuner.exoplayer.ffmpeg;
+package com.android.tv.testing.constants;
-interface IFfmpegDecoder {
- boolean isAvailable();
- void create();
- void release();
- void resetDecoderState(String mimetype);
- void decode(long timeUs, in byte[] sample);
- byte[] getDecodedSample();
- long getDecodedTimeUs();
- void testSandboxIsolatedProcess();
- void testSandboxMinijail();
-} \ No newline at end of file
+import android.os.Build;
+
+/** Constants for Robolectic Config. */
+public final class ConfigConstants {
+
+ public static final String MANIFEST = "vendor/unbundled_google/packages/TV/AndroidManifest.xml";
+ public static final int SDK = Build.VERSION_CODES.M;
+
+ private ConfigConstants() {}
+}
diff --git a/tests/common/src/com/android/tv/testing/constants/Constants.java b/tests/common/src/com/android/tv/testing/constants/Constants.java
new file mode 100644
index 00000000..09e1ada1
--- /dev/null
+++ b/tests/common/src/com/android/tv/testing/constants/Constants.java
@@ -0,0 +1,47 @@
+/*
+ * 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.testing.constants;
+
+import android.media.tv.TvTrackInfo;
+
+/** Constants for testing. */
+public final class Constants {
+ public static final int FUNC_TEST_CHANNEL_COUNT = 100;
+ public static final int UNIT_TEST_CHANNEL_COUNT = 4;
+ public static final int JANK_TEST_CHANNEL_COUNT = 500; // TODO: increase to 1000 see b/23526997
+
+ public static final TvTrackInfo EN_STEREO_AUDIO_TRACK =
+ new TvTrackInfo.Builder(TvTrackInfo.TYPE_AUDIO, "English Stereo Audio")
+ .setLanguage("en")
+ .setAudioChannelCount(2)
+ .build();
+ public static final TvTrackInfo GENERIC_AUDIO_TRACK =
+ new TvTrackInfo.Builder(TvTrackInfo.TYPE_AUDIO, "Generic Audio").build();
+
+ public static final TvTrackInfo FHD1080P50_VIDEO_TRACK =
+ new TvTrackInfo.Builder(TvTrackInfo.TYPE_VIDEO, "FHD Video")
+ .setVideoHeight(1080)
+ .setVideoWidth(1920)
+ .setVideoFrameRate(50)
+ .build();
+ public static final TvTrackInfo SVGA_VIDEO_TRACK =
+ new TvTrackInfo.Builder(TvTrackInfo.TYPE_VIDEO, "SVGA Video")
+ .setVideoHeight(600)
+ .setVideoWidth(800)
+ .build();
+
+ private Constants() {}
+}
diff --git a/tests/common/src/com/android/tv/testing/TvContentRatingConstants.java b/tests/common/src/com/android/tv/testing/constants/TvContentRatingConstants.java
index c4c96fed..e1a3d906 100644
--- a/tests/common/src/com/android/tv/testing/TvContentRatingConstants.java
+++ b/tests/common/src/com/android/tv/testing/constants/TvContentRatingConstants.java
@@ -14,20 +14,21 @@
* limitations under the License.
*/
-package com.android.tv.testing;
+package com.android.tv.testing.constants;
import android.media.tv.TvContentRating;
-/**
- * Constants for the content rating strings.
- */
+/** Constants for the content rating strings. */
public final class TvContentRatingConstants {
/**
* A content rating object.
*
* <p>Domain: com.android.tv
+ *
* <p>Rating system: US_TV
+ *
* <p>Rating: US_TV_Y7
+ *
* <p>Sub ratings: US_TV_FV
*/
public static final TvContentRating CONTENT_RATING_US_TV_Y7_US_TV_FV =
@@ -39,7 +40,9 @@ public final class TvContentRatingConstants {
* A content rating object.
*
* <p>Domain: com.android.tv
+ *
* <p>Rating system: US_TV
+ *
* <p>Rating: US_TV_MA
*/
public static final TvContentRating CONTENT_RATING_US_TV_MA =
@@ -51,11 +54,14 @@ public final class TvContentRatingConstants {
* A content rating object.
*
* <p>Domain: com.android.tv
+ *
* <p>Rating system: US_TV
+ *
* <p>Rating: US_TV_PG
+ *
* <p>Sub ratings: US_TV_L, US_TV_S
*/
public static final TvContentRating CONTENT_RATING_US_TV_PG_US_TV_L_US_TV_S =
- TvContentRating.createRating("com.android.tv", "US_TV", "US_TV_PG", "US_TV_L",
- "US_TV_S");
+ TvContentRating.createRating(
+ "com.android.tv", "US_TV", "US_TV_PG", "US_TV_L", "US_TV_S");
}
diff --git a/tests/common/src/com/android/tv/testing/ChannelInfo.java b/tests/common/src/com/android/tv/testing/data/ChannelInfo.java
index 946c0b55..e39c057d 100644
--- a/tests/common/src/com/android/tv/testing/ChannelInfo.java
+++ b/tests/common/src/com/android/tv/testing/data/ChannelInfo.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.tv.testing;
+package com.android.tv.testing.data;
import android.content.ContentResolver;
import android.content.Context;
@@ -23,14 +23,12 @@ import android.media.tv.TvContract;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.util.SparseArray;
-
import java.util.Objects;
-/**
- * Channel Information.
- */
+/** Channel Information. */
public final class ChannelInfo {
private static final SparseArray<String> VIDEO_HEIGHT_TO_FORMAT_MAP = new SparseArray<>();
+
static {
VIDEO_HEIGHT_TO_FORMAT_MAP.put(480, TvContract.Channels.VIDEO_FORMAT_480P);
VIDEO_HEIGHT_TO_FORMAT_MAP.put(576, TvContract.Channels.VIDEO_FORMAT_576P);
@@ -41,9 +39,9 @@ public final class ChannelInfo {
}
public static final String[] PROJECTION = {
- TvContract.Channels.COLUMN_DISPLAY_NUMBER,
- TvContract.Channels.COLUMN_DISPLAY_NAME,
- TvContract.Channels.COLUMN_ORIGINAL_NETWORK_ID,
+ TvContract.Channels.COLUMN_DISPLAY_NUMBER,
+ TvContract.Channels.COLUMN_DISPLAY_NAME,
+ TvContract.Channels.COLUMN_ORIGINAL_NETWORK_ID,
};
public final String number;
@@ -67,14 +65,15 @@ public final class ChannelInfo {
* Create a channel info for TVTestInput.
*
* @param context a context to insert logo. It can be null if logo isn't needed.
- * @param channelNumber a channel number to be use as an identifier.
- * {@link #originalNetworkId} will be assigned the same value, too.
+ * @param channelNumber a channel number to be use as an identifier. {@link #originalNetworkId}
+ * will be assigned the same value, too.
*/
public static ChannelInfo create(@Nullable Context context, int channelNumber) {
- Builder builder = new Builder()
- .setNumber(String.valueOf(channelNumber))
- .setName("Channel " + channelNumber)
- .setOriginalNetworkId(channelNumber);
+ Builder builder =
+ new Builder()
+ .setNumber(String.valueOf(channelNumber))
+ .setName("Channel " + channelNumber)
+ .setOriginalNetworkId(channelNumber);
if (context != null) {
// tests/input/tools/get_test_logos.sh only stores 1000 logos.
builder.setLogoUrl(getUriStringForChannelLogo(context, channelNumber));
@@ -88,7 +87,9 @@ public final class ChannelInfo {
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.authority(context.getPackageName())
.path("drawable")
- .appendPath("ch_" + index + "_logo").build().toString();
+ .appendPath("ch_" + index + "_logo")
+ .build()
+ .toString();
}
public static ChannelInfo fromCursor(Cursor c) {
@@ -109,10 +110,22 @@ public final class ChannelInfo {
return builder.build();
}
- private ChannelInfo(String number, String name, String logoUrl, int originalNetworkId,
- int videoWidth, int videoHeight, float videoPixelAspectRatio, int audioChannel,
- int audioLanguageCount, boolean hasClosedCaption, ProgramInfo program,
- String appLinkText, int appLinkColor, String appLinkIconUri, String appLinkPosterArtUri,
+ private ChannelInfo(
+ String number,
+ String name,
+ String logoUrl,
+ int originalNetworkId,
+ 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;
@@ -139,20 +152,35 @@ public final class ChannelInfo {
@Override
public String toString() {
return "Channel{"
- + "number=" + number
- + ", name=" + name
- + ", logoUri=" + logoUrl
- + ", originalNetworkId=" + originalNetworkId
- + ", videoWidth=" + videoWidth
- + ", videoHeight=" + videoHeight
- + ", audioChannel=" + audioChannel
- + ", audioLanguageCount=" + audioLanguageCount
- + ", hasClosedCaption=" + hasClosedCaption
- + ", appLinkText=" + appLinkText
- + ", appLinkColor=" + appLinkColor
- + ", appLinkIconUri=" + appLinkIconUri
- + ", appLinkPosterArtUri=" + appLinkPosterArtUri
- + ", appLinkIntentUri=" + appLinkIntentUri + "}";
+ + "number="
+ + number
+ + ", name="
+ + name
+ + ", logoUri="
+ + logoUrl
+ + ", originalNetworkId="
+ + originalNetworkId
+ + ", videoWidth="
+ + videoWidth
+ + ", videoHeight="
+ + videoHeight
+ + ", audioChannel="
+ + audioChannel
+ + ", audioLanguageCount="
+ + audioLanguageCount
+ + ", hasClosedCaption="
+ + hasClosedCaption
+ + ", appLinkText="
+ + appLinkText
+ + ", appLinkColor="
+ + appLinkColor
+ + ", appLinkIconUri="
+ + appLinkIconUri
+ + ", appLinkPosterArtUri="
+ + appLinkPosterArtUri
+ + ", appLinkIntentUri="
+ + appLinkIntentUri
+ + "}";
}
@Override
@@ -164,21 +192,21 @@ public final class ChannelInfo {
return false;
}
ChannelInfo that = (ChannelInfo) o;
- return Objects.equals(originalNetworkId, that.originalNetworkId) &&
- Objects.equals(videoWidth, that.videoWidth) &&
- Objects.equals(videoHeight, that.videoHeight) &&
- Objects.equals(audioChannel, that.audioChannel) &&
- Objects.equals(audioLanguageCount, that.audioLanguageCount) &&
- Objects.equals(hasClosedCaption, that.hasClosedCaption) &&
- Objects.equals(appLinkColor, that.appLinkColor) &&
- Objects.equals(number, that.number) &&
- Objects.equals(name, that.name) &&
- Objects.equals(logoUrl, that.logoUrl) &&
- Objects.equals(program, that.program) &&
- Objects.equals(appLinkText, that.appLinkText) &&
- Objects.equals(appLinkIconUri, that.appLinkIconUri) &&
- Objects.equals(appLinkPosterArtUri, that.appLinkPosterArtUri) &&
- Objects.equals(appLinkIntentUri, that.appLinkIntentUri);
+ return Objects.equals(originalNetworkId, that.originalNetworkId)
+ && Objects.equals(videoWidth, that.videoWidth)
+ && Objects.equals(videoHeight, that.videoHeight)
+ && Objects.equals(audioChannel, that.audioChannel)
+ && Objects.equals(audioLanguageCount, that.audioLanguageCount)
+ && Objects.equals(hasClosedCaption, that.hasClosedCaption)
+ && Objects.equals(appLinkColor, that.appLinkColor)
+ && Objects.equals(number, that.number)
+ && Objects.equals(name, that.name)
+ && Objects.equals(logoUrl, that.logoUrl)
+ && Objects.equals(program, that.program)
+ && Objects.equals(appLinkText, that.appLinkText)
+ && Objects.equals(appLinkIconUri, that.appLinkIconUri)
+ && Objects.equals(appLinkPosterArtUri, that.appLinkPosterArtUri)
+ && Objects.equals(appLinkIntentUri, that.appLinkIntentUri);
}
@Override
@@ -186,17 +214,15 @@ public final class ChannelInfo {
return Objects.hash(number, name, originalNetworkId);
}
- /**
- * Builder class for {@code ChannelInfo}.
- */
+ /** Builder class for {@code ChannelInfo}. */
public static class Builder {
private String mNumber;
private String mName;
private String mLogoUrl = null;
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 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;
@@ -207,8 +233,7 @@ public final class ChannelInfo {
private String mAppLinkPosterArtUri;
private String mAppLinkIntentUri;
- public Builder() {
- }
+ public Builder() {}
public Builder(ChannelInfo other) {
mNumber = other.number;
@@ -305,11 +330,23 @@ public final class ChannelInfo {
}
public ChannelInfo build() {
- return new ChannelInfo(mNumber, mName, mLogoUrl, mOriginalNetworkId,
- mVideoWidth, mVideoHeight, mVideoPixelAspectRatio, mAudioChannel,
- mAudioLanguageCount, mHasClosedCaption, mProgram, mAppLinkText, mAppLinkColor,
- mAppLinkIconUri, mAppLinkPosterArtUri, mAppLinkIntentUri);
-
+ return new ChannelInfo(
+ mNumber,
+ mName,
+ mLogoUrl,
+ mOriginalNetworkId,
+ mVideoWidth,
+ mVideoHeight,
+ mVideoPixelAspectRatio,
+ mAudioChannel,
+ mAudioLanguageCount,
+ mHasClosedCaption,
+ mProgram,
+ mAppLinkText,
+ mAppLinkColor,
+ mAppLinkIconUri,
+ mAppLinkPosterArtUri,
+ mAppLinkIntentUri);
}
}
}
diff --git a/tests/common/src/com/android/tv/testing/ChannelUtils.java b/tests/common/src/com/android/tv/testing/data/ChannelUtils.java
index bfb766d6..920c7087 100644
--- a/tests/common/src/com/android/tv/testing/ChannelUtils.java
+++ b/tests/common/src/com/android/tv/testing/data/ChannelUtils.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.tv.testing;
+package com.android.tv.testing.data;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -27,24 +27,22 @@ import android.support.annotation.WorkerThread;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
-
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-/**
- * Static helper methods for working with {@link android.media.tv.TvContract}.
- */
+/** Static helper methods for working with {@link android.media.tv.TvContract}. */
public class ChannelUtils {
private static final String TAG = "ChannelUtils";
private static final boolean DEBUG = false;
/**
- * Query and return the map of (channel_id, ChannelInfo).
- * See: {@link ChannelInfo#fromCursor(Cursor)}.
+ * Query and return the map of (channel_id, ChannelInfo). See: {@link
+ * com.android.tv.testing.data.ChannelInfo#fromCursor(Cursor)}.
*/
@WorkerThread
public static Map<Long, ChannelInfo> queryChannelInfoMapForTvInput(
@@ -55,8 +53,8 @@ public class ChannelUtils {
String[] projections = new String[ChannelInfo.PROJECTION.length + 1];
projections[0] = Channels._ID;
System.arraycopy(ChannelInfo.PROJECTION, 0, projections, 1, ChannelInfo.PROJECTION.length);
- try (Cursor cursor = context.getContentResolver()
- .query(uri, projections, null, null, null)) {
+ try (Cursor cursor =
+ context.getContentResolver().query(uri, projections, null, null, null)) {
if (cursor != null) {
while (cursor.moveToNext()) {
map.put(cursor.getLong(0), ChannelInfo.fromCursor(cursor));
@@ -113,10 +111,14 @@ public class ChannelUtils {
Long rowId = existingChannelsMap.get(channel.originalNetworkId);
Uri uri;
if (rowId == null) {
- if (DEBUG) Log.d(TAG, "Inserting "+ channel);
+ if (DEBUG) {
+ Log.d(TAG, "Inserting " + channel);
+ }
uri = resolver.insert(TvContract.Channels.CONTENT_URI, values);
} else {
- if (DEBUG) Log.d(TAG, "Updating "+ channel);
+ if (DEBUG) {
+ Log.d(TAG, "Updating " + channel);
+ }
uri = TvContract.buildChannelUri(rowId);
resolver.update(uri, values, null, null);
existingChannelsMap.remove(channel.originalNetworkId);
@@ -149,6 +151,14 @@ public class ChannelUtils {
// Prevent instantiation.
}
+ public static List<ChannelInfo> createChannelInfos(Context context, int channelCount) {
+ List<ChannelInfo> channels = new ArrayList<>();
+ for (int i = 1; i <= channelCount; i++) {
+ channels.add(ChannelInfo.create(context, i));
+ }
+ return channels;
+ }
+
public static class InsertLogosTask extends AsyncTask<Map<Uri, String>, Void, Void> {
private final Context mContext;
diff --git a/tests/common/src/com/android/tv/testing/ProgramInfo.java b/tests/common/src/com/android/tv/testing/data/ProgramInfo.java
index b1aaea6b..6d801425 100644
--- a/tests/common/src/com/android/tv/testing/ProgramInfo.java
+++ b/tests/common/src/com/android/tv/testing/data/ProgramInfo.java
@@ -14,80 +14,83 @@
* limitations under the License.
*/
-package com.android.tv.testing;
+package com.android.tv.testing.data;
import android.content.Context;
import android.database.Cursor;
import android.media.tv.TvContentRating;
import android.media.tv.TvContract;
-
+import com.android.tv.testing.R;
+import com.android.tv.testing.utils.Utils;
+import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
public final class ProgramInfo {
- /**
- * If this is specify for title, it will be generated by adding index.
- */
+ /** If this is specify for title, it will be generated by adding index. */
public static final String GEN_TITLE = "";
/**
- * If this is specify for episode title, it will be generated by adding index.
- * Also, season and episode numbers would be generated, too.
- * see: {@link #build} for detail.
+ * If this is specify for episode title, it will be generated by adding index. Also, season and
+ * episode numbers would be generated, too. see: {@link #build} for detail.
*/
public static final String GEN_EPISODE = "";
+
private static final int SEASON_MAX = 10;
private static final int EPISODE_MAX = 12;
/**
- * If this is specify for poster art,
- * it will be selected one of {@link #POSTER_ARTS_RES} in order.
+ * If this is specify for poster art, it will be selected one of {@link #POSTER_ARTS_RES} in
+ * order.
*/
public static final String GEN_POSTER = "GEN";
+
private static final int[] POSTER_ARTS_RES = {
- 0,
- R.drawable.blue,
- R.drawable.red_large,
- R.drawable.green,
- R.drawable.red,
- R.drawable.green_large,
- R.drawable.blue_small};
+ 0,
+ R.drawable.blue,
+ R.drawable.red_large,
+ R.drawable.green,
+ R.drawable.red,
+ R.drawable.green_large,
+ R.drawable.blue_small
+ };
/**
- * If this is specified for duration,
- * it will be selected one of {@link #DURATIONS_MS} in order.
+ * If this is specified for duration, it will be selected one of {@link #DURATIONS_MS} in order.
*/
public static final int GEN_DURATION = -1;
+
private static final long[] DURATIONS_MS = {
- TimeUnit.MINUTES.toMillis(15),
- TimeUnit.MINUTES.toMillis(45),
- TimeUnit.MINUTES.toMillis(90),
- TimeUnit.MINUTES.toMillis(60),
- TimeUnit.MINUTES.toMillis(30),
- TimeUnit.MINUTES.toMillis(45),
- TimeUnit.MINUTES.toMillis(60),
- TimeUnit.MINUTES.toMillis(90),
- TimeUnit.HOURS.toMillis(5)};
- private static long DURATIONS_SUM_MS;
+ TimeUnit.MINUTES.toMillis(15),
+ TimeUnit.MINUTES.toMillis(45),
+ TimeUnit.MINUTES.toMillis(90),
+ TimeUnit.MINUTES.toMillis(60),
+ TimeUnit.MINUTES.toMillis(30),
+ TimeUnit.MINUTES.toMillis(45),
+ TimeUnit.MINUTES.toMillis(60),
+ TimeUnit.MINUTES.toMillis(90),
+ TimeUnit.HOURS.toMillis(5)
+ };
+ private static long durationsSumMs;
+
static {
- DURATIONS_SUM_MS = 0;
+ durationsSumMs = 0;
for (long duration : DURATIONS_MS) {
- DURATIONS_SUM_MS += duration;
+ durationsSumMs += duration;
}
}
- /**
- * If this is specified for genre,
- * it will be selected one of {@link #GENRES} in order.
- */
+ /** If this is specified for genre, it will be selected one of {@link #GENRES} in order. */
public static final String GEN_GENRE = "GEN";
+
private static final String[] GENRES = {
- "",
- TvContract.Programs.Genres.SPORTS,
- TvContract.Programs.Genres.NEWS,
- TvContract.Programs.Genres.SHOPPING,
- TvContract.Programs.Genres.DRAMA,
- TvContract.Programs.Genres.ENTERTAINMENT};
+ "",
+ TvContract.Programs.Genres.SPORTS,
+ TvContract.Programs.Genres.NEWS,
+ TvContract.Programs.Genres.SHOPPING,
+ TvContract.Programs.Genres.DRAMA,
+ TvContract.Programs.Genres.ENTERTAINMENT
+ };
public final String title;
public final String episode;
@@ -118,9 +121,17 @@ public final class ProgramInfo {
return builder.build();
}
- public ProgramInfo(String title, String episode, int seasonNumber, int episodeNumber,
- String posterArtUri, String description, long durationMs,
- TvContentRating[] contentRatings, String genre, String resourceUri) {
+ public ProgramInfo(
+ String title,
+ String episode,
+ int seasonNumber,
+ int episodeNumber,
+ String posterArtUri,
+ String description,
+ long durationMs,
+ TvContentRating[] contentRatings,
+ String genre,
+ String resourceUri) {
this.title = title;
this.episode = episode;
this.seasonNumber = seasonNumber;
@@ -141,8 +152,9 @@ public final class ProgramInfo {
}
/**
- * Get index of the program whose start time equals or less than {@code timeMs} and
- * end time more than {@code timeMs}.
+ * Get index of the program whose start time equals or less than {@code timeMs} and end time
+ * more than {@code timeMs}.
+ *
* @param timeMs target time in millis to find a program.
* @param channelId used to add complexity to the index between two consequence channels.
*/
@@ -151,8 +163,8 @@ public final class ProgramInfo {
return Math.max((int) (timeMs / durationMs), 0);
}
long startTimeMs = channelId * DURATIONS_MS[((int) (channelId % DURATIONS_MS.length))];
- int index = (int) ((timeMs - startTimeMs) / DURATIONS_SUM_MS) * DURATIONS_MS.length;
- startTimeMs += (index / DURATIONS_MS.length) * DURATIONS_SUM_MS;
+ int index = (int) ((timeMs - startTimeMs) / durationsSumMs) * DURATIONS_MS.length;
+ startTimeMs += (index / DURATIONS_MS.length) * durationsSumMs;
while (startTimeMs + DURATIONS_MS[index % DURATIONS_MS.length] < timeMs) {
startTimeMs += DURATIONS_MS[index % DURATIONS_MS.length];
index++;
@@ -162,14 +174,16 @@ public final class ProgramInfo {
/**
* Returns the start time for the program with the position.
+ *
* @param index index returned by {@link #getIndex}
*/
public long getStartTimeMs(int index, long channelId) {
if (durationMs != GEN_DURATION) {
return index * durationMs;
}
- long startTimeMs = channelId * DURATIONS_MS[((int) (channelId % DURATIONS_MS.length))]
- + (index / DURATIONS_MS.length) * DURATIONS_SUM_MS;
+ long startTimeMs =
+ channelId * DURATIONS_MS[((int) (channelId % DURATIONS_MS.length))]
+ + (index / DURATIONS_MS.length) * durationsSumMs;
for (int i = 0; i < index % DURATIONS_MS.length; i++) {
startTimeMs += DURATIONS_MS[i];
}
@@ -177,9 +191,9 @@ public final class ProgramInfo {
}
/**
- * Return complete {@link ProgramInfo} with the generated value.
- * See: {@link #GEN_TITLE}, {@link #GEN_EPISODE}, {@link #GEN_POSTER}, {@link #GEN_DURATION},
- * {@link #GEN_GENRE}.
+ * Return complete {@link ProgramInfo} with the generated value. See: {@link #GEN_TITLE}, {@link
+ * #GEN_EPISODE}, {@link #GEN_POSTER}, {@link #GEN_DURATION}, {@link #GEN_GENRE}.
+ *
* @param index index returned by {@link #getIndex}
*/
public ProgramInfo build(Context context, int index) {
@@ -196,8 +210,8 @@ public final class ProgramInfo {
episode != null ? (index % SEASON_MAX + 1) : seasonNumber,
episode != null ? (index % EPISODE_MAX + 1) : episodeNumber,
GEN_POSTER.equals(posterArtUri)
- ? Utils.getUriStringForResource(context,
- POSTER_ARTS_RES[index % POSTER_ARTS_RES.length])
+ ? Utils.getUriStringForResource(
+ context, POSTER_ARTS_RES[index % POSTER_ARTS_RES.length])
: posterArtUri,
description,
durationMs == GEN_DURATION ? DURATIONS_MS[index % DURATIONS_MS.length] : durationMs,
@@ -208,9 +222,13 @@ public final class ProgramInfo {
@Override
public String toString() {
- return "ProgramInfo{title=" + title
- + ", episode=" + episode
- + ", durationMs=" + durationMs + "}";
+ return "ProgramInfo{title="
+ + title
+ + ", episode="
+ + episode
+ + ", durationMs="
+ + durationMs
+ + "}";
}
@Override
@@ -222,16 +240,16 @@ public final class ProgramInfo {
return false;
}
ProgramInfo that = (ProgramInfo) o;
- return Objects.equals(seasonNumber, that.seasonNumber) &&
- Objects.equals(episodeNumber, that.episodeNumber) &&
- Objects.equals(durationMs, that.durationMs) &&
- Objects.equals(title, that.title) &&
- Objects.equals(episode, that.episode) &&
- Objects.equals(posterArtUri, that.posterArtUri) &&
- Objects.equals(description, that.description) &&
- Objects.equals(genre, that.genre) &&
- Objects.equals(contentRatings, that.contentRatings) &&
- Objects.equals(resourceUri, that.resourceUri);
+ return Objects.equals(seasonNumber, that.seasonNumber)
+ && Objects.equals(episodeNumber, that.episodeNumber)
+ && Objects.equals(durationMs, that.durationMs)
+ && Objects.equals(title, that.title)
+ && Objects.equals(episode, that.episode)
+ && Objects.equals(posterArtUri, that.posterArtUri)
+ && Objects.equals(description, that.description)
+ && Objects.equals(genre, that.genre)
+ && Arrays.equals(contentRatings, that.contentRatings)
+ && Objects.equals(resourceUri, that.resourceUri);
}
@Override
@@ -302,8 +320,17 @@ public final class ProgramInfo {
}
public ProgramInfo build() {
- return new ProgramInfo(mTitle, mEpisode, mSeasonNumber, mEpisodeNumber, mPosterArtUri,
- mDescription, mDurationMs, mContentRatings, mGenre, mResourceUri);
+ return new ProgramInfo(
+ mTitle,
+ mEpisode,
+ mSeasonNumber,
+ mEpisodeNumber,
+ mPosterArtUri,
+ mDescription,
+ mDurationMs,
+ mContentRatings,
+ mGenre,
+ mResourceUri);
}
}
}
diff --git a/tests/common/src/com/android/tv/testing/ProgramUtils.java b/tests/common/src/com/android/tv/testing/data/ProgramUtils.java
index 08c6a033..21647719 100644
--- a/tests/common/src/com/android/tv/testing/ProgramUtils.java
+++ b/tests/common/src/com/android/tv/testing/data/ProgramUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.tv.testing;
+package com.android.tv.testing.data;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -25,36 +25,57 @@ import android.media.tv.TvContract;
import android.media.tv.TvContract.Programs;
import android.net.Uri;
import android.util.Log;
-
import com.android.tv.common.TvContentRatingCache;
-
+import com.android.tv.common.util.Clock;
import java.util.ArrayList;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
-public class ProgramUtils {
+/** Static utilities for using Programs in tests */
+public final class ProgramUtils {
private static final String TAG = "ProgramUtils";
private static final boolean DEBUG = false;
- // Populate program data for a week.
- private static final long PROGRAM_INSERT_DURATION_MS = TimeUnit.DAYS.toMillis(7);
+ /** Populate program data for a week */
+ public static final long PROGRAM_INSERT_DURATION_MS = TimeUnit.DAYS.toMillis(7);
+
private static final int MAX_DB_INSERT_COUNT_AT_ONCE = 500;
/**
- * Populate programs by repeating given program information.
- * This method will populate programs without any gap nor overlapping
- * starting from the current time.
+ * Populate programs by repeating given program information. This method will populate programs
+ * without any gap nor overlapping starting from the current time.
*/
- public static void populatePrograms(Context context, Uri channelUri, ProgramInfo program) {
+ public static void populatePrograms(
+ Context context, Uri channelUri, ProgramInfo program, Clock clock) {
+ populatePrograms(context, channelUri, program, clock, PROGRAM_INSERT_DURATION_MS);
+ }
+
+ public static void populatePrograms(
+ Context context,
+ Uri channelUri,
+ ProgramInfo program,
+ Clock clock,
+ long programInsertDurationMs) {
+ long currentTimeMs = clock.currentTimeMillis();
+ long targetEndTimeMs = currentTimeMs + programInsertDurationMs;
+ populatePrograms(context, channelUri, program, currentTimeMs, targetEndTimeMs);
+ }
+
+ public static void populatePrograms(
+ Context context,
+ Uri channelUri,
+ ProgramInfo program,
+ long currentTimeMs,
+ long targetEndTimeMs) {
ContentValues values = new ContentValues();
long channelId = ContentUris.parseId(channelUri);
values.put(Programs.COLUMN_CHANNEL_ID, channelId);
values.put(Programs.COLUMN_SHORT_DESCRIPTION, program.description);
- values.put(Programs.COLUMN_CONTENT_RATING,
+ values.put(
+ Programs.COLUMN_CONTENT_RATING,
TvContentRatingCache.contentRatingsToString(program.contentRatings));
- long currentTimeMs = System.currentTimeMillis();
- long targetEndTimeMs = currentTimeMs + PROGRAM_INSERT_DURATION_MS;
long timeMs = getLastProgramEndTimeMs(context, channelUri, currentTimeMs, targetEndTimeMs);
if (timeMs <= 0) {
timeMs = currentTimeMs;
@@ -81,11 +102,12 @@ public class ProgramUtils {
list.add(new ContentValues(values));
timeMs += programAt.durationMs;
- if (list.size() >= MAX_DB_INSERT_COUNT_AT_ONCE
- || timeMs >= targetEndTimeMs) {
+ if (list.size() >= MAX_DB_INSERT_COUNT_AT_ONCE || timeMs >= targetEndTimeMs) {
try {
- context.getContentResolver().bulkInsert(Programs.CONTENT_URI,
- list.toArray(new ContentValues[list.size()]));
+ context.getContentResolver()
+ .bulkInsert(
+ Programs.CONTENT_URI,
+ list.toArray(new ContentValues[list.size()]));
} catch (SQLiteException e) {
Log.e(TAG, "Can't insert EPG.", e);
return;
@@ -110,4 +132,16 @@ public class ProgramUtils {
}
private ProgramUtils() {}
+
+ public static void updateProgramForAllChannelsOf(
+ Context context, String inputId, Clock clock, long durationMs) {
+ // Reload channels so we have the ids.
+ Map<Long, ChannelInfo> channelIdToInfoMap =
+ ChannelUtils.queryChannelInfoMapForTvInput(context, inputId);
+ for (Long channelId : channelIdToInfoMap.keySet()) {
+ ProgramInfo programInfo = ProgramInfo.create();
+ populatePrograms(
+ context, TvContract.buildChannelUri(channelId), programInfo, clock, durationMs);
+ }
+ }
}
diff --git a/tests/unit/src/com/android/tv/dvr/DvrDataManagerInMemoryImpl.java b/tests/common/src/com/android/tv/testing/dvr/DvrDataManagerInMemoryImpl.java
index 0a7ab46c..b8a055c7 100644
--- a/tests/unit/src/com/android/tv/dvr/DvrDataManagerInMemoryImpl.java
+++ b/tests/common/src/com/android/tv/testing/dvr/DvrDataManagerInMemoryImpl.java
@@ -14,24 +14,22 @@
* limitations under the License
*/
-package com.android.tv.dvr;
+package com.android.tv.testing.dvr;
import android.content.Context;
-import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-import android.support.test.filters.SdkSuppress;
import android.text.TextUtils;
import android.util.Log;
import android.util.Range;
-
import com.android.tv.common.SoftPreconditions;
+import com.android.tv.common.util.Clock;
+import com.android.tv.dvr.BaseDvrDataManager;
+import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.data.RecordedProgram;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.data.ScheduledRecording.RecordingState;
import com.android.tv.dvr.data.SeriesRecording;
-import com.android.tv.util.Clock;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -40,9 +38,8 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
/** A DVR Data manager that stores values in memory suitable for testing. */
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
public final class DvrDataManagerInMemoryImpl extends BaseDvrDataManager {
- private final static String TAG = "DvrDataManagerInMemory";
+ private static final String TAG = "DvrDataManagerInMemory";
private final AtomicLong mNextId = new AtomicLong(1);
private final Map<Long, ScheduledRecording> mScheduledRecordings = new HashMap<>();
private final Map<Long, RecordedProgram> mRecordedPrograms = new HashMap<>();
@@ -81,6 +78,7 @@ public final class DvrDataManagerInMemoryImpl extends BaseDvrDataManager {
return new ArrayList<>(mScheduledRecordings.values());
}
+ @Override
public List<SeriesRecording> getSeriesRecordings() {
return new ArrayList<>(mSeriesRecordings.values());
}
@@ -99,7 +97,7 @@ public final class DvrDataManagerInMemoryImpl extends BaseDvrDataManager {
@Override
public long getNextScheduledStartTimeAfter(long startTime) {
- List<ScheduledRecording> temp = getNonStartedScheduledRecordings();
+ List<ScheduledRecording> temp = getNonStartedScheduledRecordings();
Collections.sort(temp, ScheduledRecording.START_TIME_COMPARATOR);
for (ScheduledRecording r : temp) {
if (r.getStartTimeMs() > startTime) {
@@ -110,8 +108,8 @@ public final class DvrDataManagerInMemoryImpl extends BaseDvrDataManager {
}
@Override
- public List<ScheduledRecording> getScheduledRecordings(Range<Long> period,
- @RecordingState int state) {
+ public List<ScheduledRecording> getScheduledRecordings(
+ Range<Long> period, @RecordingState int state) {
List<ScheduledRecording> temp = getScheduledRecordingsPrograms();
List<ScheduledRecording> result = new ArrayList<>();
for (ScheduledRecording r : temp) {
@@ -144,19 +142,20 @@ public final class DvrDataManagerInMemoryImpl extends BaseDvrDataManager {
return result;
}
- /**
- * Add a new scheduled recording.
- */
+ /** Add a new scheduled recording. */
@Override
public void addScheduledRecording(ScheduledRecording... scheduledRecordings) {
+ addScheduledRecording(false, scheduledRecordings);
+ }
+
+ public void addScheduledRecording(boolean keepIds, ScheduledRecording... scheduledRecordings) {
for (ScheduledRecording r : scheduledRecordings) {
- addScheduledRecordingInternal(r);
+ addScheduledRecordingInternal(r, keepIds);
}
}
-
public void addRecordedProgram(RecordedProgram recordedProgram) {
- addRecordedProgramInternal(recordedProgram);
+ addRecordedProgramInternal(recordedProgram, false);
}
public void updateRecordedProgram(RecordedProgram r) {
@@ -174,26 +173,43 @@ public final class DvrDataManagerInMemoryImpl extends BaseDvrDataManager {
notifyRecordedProgramsRemoved(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();
+ return addScheduledRecordingInternal(scheduledRecording, false);
+ }
+
+ public ScheduledRecording addScheduledRecordingInternal(
+ ScheduledRecording scheduledRecording, boolean keepId) {
+ if (!keepId) {
+ 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 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();
+ public RecordedProgram addRecordedProgramInternal(
+ RecordedProgram recordedProgram, boolean keepId) {
+ if (!keepId) {
+ 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);
notifyRecordedProgramsAdded(recordedProgram);
return recordedProgram;
@@ -265,7 +281,7 @@ public final class DvrDataManagerInMemoryImpl extends BaseDvrDataManager {
public ScheduledRecording getScheduledRecordingForProgramId(long programId) {
for (ScheduledRecording r : mScheduledRecordings.values()) {
if (r.getProgramId() == programId) {
- return r;
+ return r;
}
}
return null;
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 a9bfa97a..72bac8fc 100644
--- a/tests/common/src/com/android/tv/testing/dvr/RecordingTestUtils.java
+++ b/tests/common/src/com/android/tv/testing/dvr/RecordingTestUtils.java
@@ -17,40 +17,37 @@
package com.android.tv.testing.dvr;
import com.android.tv.dvr.data.ScheduledRecording;
-
import junit.framework.Assert;
-/**
- * Static utils for using {@link ScheduledRecording} in tests.
- */
+/** Static utils for using {@link ScheduledRecording} in tests. */
public final class RecordingTestUtils {
private static final String INPUT_ID = "input_id";
private static final int CHANNEL_ID = 273;
- public static ScheduledRecording createTestRecordingWithIdAndPeriod(long id, String inputId,
- long channelId, long startTime, long endTime) {
+ public static ScheduledRecording createTestRecordingWithIdAndPeriod(
+ long id, String inputId, long channelId, long startTime, long endTime) {
return ScheduledRecording.builder(inputId, channelId, startTime, endTime)
.setId(id)
.setChannelId(channelId)
.build();
}
- public static ScheduledRecording createTestRecordingWithPeriod(String inputId,
- long channelId, long startTime, long endTime) {
- return createTestRecordingWithIdAndPeriod(ScheduledRecording.ID_NOT_SET, inputId, channelId,
- startTime, endTime);
+ public static ScheduledRecording createTestRecordingWithPeriod(
+ String inputId, long channelId, long startTime, long endTime) {
+ return createTestRecordingWithIdAndPeriod(
+ ScheduledRecording.ID_NOT_SET, inputId, channelId, startTime, endTime);
}
- public static ScheduledRecording createTestRecordingWithPriorityAndPeriod(long channelId,
- long priority, long startTime, long endTime) {
+ public static ScheduledRecording createTestRecordingWithPriorityAndPeriod(
+ long channelId, long priority, long startTime, long endTime) {
return ScheduledRecording.builder(INPUT_ID, CHANNEL_ID, startTime, endTime)
.setChannelId(channelId)
.setPriority(priority)
.build();
}
- public static ScheduledRecording createTestRecordingWithIdAndPriorityAndPeriod(long id,
- long channelId, long priority, long startTime, long endTime) {
+ public static ScheduledRecording createTestRecordingWithIdAndPriorityAndPeriod(
+ long id, long channelId, long priority, long startTime, long endTime) {
return ScheduledRecording.builder(INPUT_ID, CHANNEL_ID, startTime, endTime)
.setId(id)
.setChannelId(channelId)
@@ -58,11 +55,12 @@ public final class RecordingTestUtils {
.build();
}
- public static ScheduledRecording normalizePriority(ScheduledRecording orig){
+ public static ScheduledRecording normalizePriority(ScheduledRecording orig) {
return ScheduledRecording.buildFrom(orig).setPriority(orig.getId()).build();
}
- public static void assertRecordingEquals(ScheduledRecording expected, ScheduledRecording actual) {
+ public static void assertRecordingEquals(
+ ScheduledRecording expected, ScheduledRecording actual) {
Assert.assertEquals("id", expected.getId(), actual.getId());
Assert.assertEquals("channel", expected.getChannelId(), actual.getChannelId());
Assert.assertEquals("programId", expected.getProgramId(), actual.getProgramId());
@@ -70,9 +68,11 @@ public final class RecordingTestUtils {
Assert.assertEquals("start time", expected.getStartTimeMs(), actual.getStartTimeMs());
Assert.assertEquals("end time", expected.getEndTimeMs(), actual.getEndTimeMs());
Assert.assertEquals("state", expected.getState(), actual.getState());
- Assert.assertEquals("parent series recording", expected.getSeriesRecordingId(),
+ Assert.assertEquals(
+ "parent series recording",
+ expected.getSeriesRecordingId(),
actual.getSeriesRecordingId());
}
- private RecordingTestUtils() { }
+ private RecordingTestUtils() {}
}
diff --git a/tests/common/src/com/android/tv/testing/robo/ContentProviders.java b/tests/common/src/com/android/tv/testing/robo/ContentProviders.java
new file mode 100644
index 00000000..aaaa11df
--- /dev/null
+++ b/tests/common/src/com/android/tv/testing/robo/ContentProviders.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 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.testing.robo;
+
+import android.content.ContentProvider;
+import android.content.pm.ProviderInfo;
+import org.robolectric.Robolectric;
+import org.robolectric.android.controller.ContentProviderController;
+import org.robolectric.shadows.ShadowContentResolver;
+
+/** Static utilities for using content providers in tests. */
+public final class ContentProviders {
+
+ /** Builds creates and register a ContentProvider with the given authority. */
+ public static <T extends ContentProvider> T register(Class<T> providerClass, String authority) {
+ ProviderInfo info = new ProviderInfo();
+ info.authority = authority;
+ ContentProviderController<T> contentProviderController =
+ Robolectric.buildContentProvider(providerClass);
+ T provider = contentProviderController.create(info).get();
+ provider.onCreate();
+ ShadowContentResolver.registerProviderInternal(authority, provider);
+ return provider;
+ }
+
+ private ContentProviders() {}
+}
diff --git a/tests/common/src/com/android/tv/testing/robo/RobotTestAppHelper.java b/tests/common/src/com/android/tv/testing/robo/RobotTestAppHelper.java
new file mode 100644
index 00000000..9eb79298
--- /dev/null
+++ b/tests/common/src/com/android/tv/testing/robo/RobotTestAppHelper.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 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.testing.robo;
+
+import android.media.tv.TvContract;
+import com.android.tv.testing.FakeTvProvider;
+import com.android.tv.testing.TestSingletonApp;
+import com.android.tv.testing.testdata.TestData;
+import java.util.concurrent.TimeUnit;
+import org.robolectric.Robolectric;
+
+/** Static utilities for using {@link TestSingletonApp} in roboletric tests. */
+public final class RobotTestAppHelper {
+
+ public static void loadTestData(TestSingletonApp app, TestData testData) {
+ ContentProviders.register(FakeTvProvider.class, TvContract.AUTHORITY);
+ app.loadTestData(testData, TimeUnit.DAYS.toMillis(1));
+ Robolectric.flushBackgroundThreadScheduler();
+ Robolectric.flushForegroundThreadScheduler();
+ }
+
+ private RobotTestAppHelper() {}
+}
diff --git a/tests/common/src/com/android/tv/testing/shadows/ShadowMediaSession.java b/tests/common/src/com/android/tv/testing/shadows/ShadowMediaSession.java
new file mode 100644
index 00000000..5a2c41e6
--- /dev/null
+++ b/tests/common/src/com/android/tv/testing/shadows/ShadowMediaSession.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 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.testing.shadows;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.media.MediaMetadata;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+/** Shadow {@link MediaSession}. */
+@Implements(MediaSession.class)
+public class ShadowMediaSession {
+
+ public MediaSession.Callback mCallback;
+ public PendingIntent mMediaButtonReceiver;
+ public PendingIntent mSessionActivity;
+ public PlaybackState mPlaybackState;
+ public MediaMetadata mMediaMetadata;
+ public int mFlags;
+ public boolean mActive;
+ public boolean mReleased;
+
+ /** Stand-in for the MediaSession constructor with the same parameters. */
+ public void __constructor__(Context context, String tag, int userID) {
+ // This empty method prevents the real MediaSession constructor from being called.
+ }
+
+ @Implementation
+ public void setCallback(MediaSession.Callback callback) {
+ mCallback = callback;
+ }
+
+ @Implementation
+ public void setMediaButtonReceiver(PendingIntent mbr) {
+ mMediaButtonReceiver = mbr;
+ }
+
+ @Implementation
+ public void setSessionActivity(PendingIntent activity) {
+ mSessionActivity = activity;
+ }
+
+ @Implementation
+ public void setPlaybackState(PlaybackState state) {
+ mPlaybackState = state;
+ }
+
+ @Implementation
+ public void setMetadata(MediaMetadata metadata) {
+ mMediaMetadata = metadata;
+ }
+
+ @Implementation
+ public void setFlags(int flags) {
+ mFlags = flags;
+ }
+
+ @Implementation
+ public boolean isActive() {
+ return mActive;
+ }
+
+ @Implementation
+ public void setActive(boolean active) {
+ mActive = active;
+ }
+
+ @Implementation
+ public void release() {
+ mReleased = true;
+ }
+}
diff --git a/tests/common/src/com/android/tv/testing/testdata/TestData.java b/tests/common/src/com/android/tv/testing/testdata/TestData.java
new file mode 100644
index 00000000..e7e52348
--- /dev/null
+++ b/tests/common/src/com/android/tv/testing/testdata/TestData.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 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.testing.testdata;
+
+import android.content.Context;
+import android.media.tv.TvInputInfo;
+import com.android.tv.common.util.Clock;
+import com.android.tv.testing.data.ChannelInfo;
+import com.android.tv.testing.data.ChannelUtils;
+import com.android.tv.testing.data.ProgramUtils;
+import com.android.tv.testing.utils.TestUtils;
+import java.util.List;
+
+/**
+ * A set of test data.
+ *
+ * <p>contains:
+ *
+ * <ul>
+ * <li>InputID
+ * <li>Channel List
+ * </ul>
+ *
+ * Call {@link #init(Context)}, to update the TvProvider data base with the given values.
+ */
+public abstract class TestData {
+ private List<ChannelInfo> channelList;
+
+ protected abstract List<ChannelInfo> createChannels(Context context);
+
+ public void init(Context context, Clock clock, long durationMs) {
+ channelList = createChannels(context);
+ ChannelUtils.updateChannels(context, getInputId(), channelList);
+ ProgramUtils.updateProgramForAllChannelsOf(context, getInputId(), clock, durationMs);
+ }
+
+ public abstract TvInputInfo getTvInputInfo();
+
+ public final String getInputId() {
+ return getTvInputInfo().getId();
+ }
+
+ public static final TestData DEFAULT_10_CHANNELS =
+ new TestData() {
+ private TvInputInfo mTvInputInfo = createTvInputInfo();
+
+ private TvInputInfo createTvInputInfo() {
+ try {
+ return TestUtils.createTvInputInfo(
+ TestUtils.createResolveInfo(
+ "com.android.tv.testing.testdata",
+ "com.android.tv.testing.testdata.Default10Channels"),
+ "com.android.tv.testing.testdata/.Default10Channels",
+ null,
+ TvInputInfo.TYPE_TUNER,
+ true);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ protected List<ChannelInfo> createChannels(Context context) {
+ return ChannelUtils.createChannelInfos(context, 10);
+ }
+
+ @Override
+ public TvInputInfo getTvInputInfo() {
+ return mTvInputInfo;
+ }
+ };
+}
diff --git a/tests/common/src/com/android/tv/testing/testinput/ChannelState.java b/tests/common/src/com/android/tv/testing/testinput/ChannelState.java
index 1b8f63cd..3e8bab33 100644
--- a/tests/common/src/com/android/tv/testing/testinput/ChannelState.java
+++ b/tests/common/src/com/android/tv/testing/testinput/ChannelState.java
@@ -16,24 +16,16 @@
package com.android.tv.testing.testinput;
import android.media.tv.TvTrackInfo;
-
-import com.android.tv.testing.Constants;
-
+import com.android.tv.testing.constants.Constants;
import java.util.Collections;
import java.util.List;
-/**
- * Versioned state information for a channel.
- */
+/** Versioned state information for a channel. */
public class ChannelState {
- /**
- * The video track a channel has by default.
- */
+ /** The video track a channel has by default. */
public static final TvTrackInfo DEFAULT_VIDEO_TRACK = Constants.FHD1080P50_VIDEO_TRACK;
- /**
- * The video track a channel has by default.
- */
+ /** The video track a channel has by default. */
public static final TvTrackInfo DEFAULT_AUDIO_TRACK = Constants.EN_STEREO_AUDIO_TRACK;
/**
* The channel is "tuned" and video available.
@@ -47,27 +39,23 @@ public class ChannelState {
* Default ChannelState with version @{value #CHANNEL_VERSION_DEFAULT} and default {@link
* ChannelStateData}.
*/
- public static final ChannelState DEFAULT = new ChannelState(CHANNEL_VERSION_DEFAULT,
- new ChannelStateData());
+ public static final ChannelState DEFAULT =
+ new ChannelState(CHANNEL_VERSION_DEFAULT, new ChannelStateData());
+
private final int mVersion;
private final ChannelStateData mData;
-
private ChannelState(int version, ChannelStateData channelStateData) {
mVersion = version;
mData = channelStateData;
}
- /**
- * Returns the id of the selected audio track, or null if none is selected.
- */
+ /** Returns the id of the selected audio track, or null if none is selected. */
public String getSelectedAudioTrackId() {
return mData.mSelectedAudioTrackId;
}
- /**
- * Returns the id of the selected audio track, or null if none is selected.
- */
+ /** Returns the id of the selected audio track, or null if none is selected. */
public String getSelectedVideoTrackId() {
return mData.mSelectedVideoTrackId;
}
@@ -82,9 +70,8 @@ public class ChannelState {
}
/**
- * Tune status is either {@link #TUNE_STATUS_VIDEO_AVAILABLE} or a {@link
- * android.media.tv.TvInputService.Session#notifyVideoUnavailable(int) video unavailable
- * reason}
+ * Tune status is either {@link #TUNE_STATUS_VIDEO_AVAILABLE} or a {@link
+ * android.media.tv.TvInputService.Session#notifyVideoUnavailable(int) video unavailable reason}
*/
public int getTuneStatus() {
return mData.mTuneStatus;
diff --git a/tests/common/src/com/android/tv/testing/testinput/ChannelStateData.java b/tests/common/src/com/android/tv/testing/testinput/ChannelStateData.java
index 9bac9d12..cdeb1f5c 100644
--- a/tests/common/src/com/android/tv/testing/testinput/ChannelStateData.java
+++ b/tests/common/src/com/android/tv/testing/testinput/ChannelStateData.java
@@ -19,25 +19,23 @@ package com.android.tv.testing.testinput;
import android.media.tv.TvTrackInfo;
import android.os.Parcel;
import android.os.Parcelable;
-
import java.util.ArrayList;
import java.util.List;
-/**
- * Mutable unversioned channel state.
- */
+/** Mutable unversioned channel state. */
public final class ChannelStateData implements Parcelable {
- public static final Creator<ChannelStateData> CREATOR = new Creator<ChannelStateData>() {
- @Override
- public ChannelStateData createFromParcel(Parcel in) {
- return new ChannelStateData(in);
- }
+ public static final Creator<ChannelStateData> CREATOR =
+ new Creator<ChannelStateData>() {
+ @Override
+ public ChannelStateData createFromParcel(Parcel in) {
+ return new ChannelStateData(in);
+ }
- @Override
- public ChannelStateData[] newArray(int size) {
- return new ChannelStateData[size];
- }
- };
+ @Override
+ public ChannelStateData[] newArray(int size) {
+ return new ChannelStateData[size];
+ }
+ };
public final List<TvTrackInfo> mTvTrackInfos = new ArrayList<>();
public int mTuneStatus = ChannelState.TUNE_STATUS_VIDEO_AVAILABLE;
@@ -71,9 +69,6 @@ public final class ChannelStateData implements Parcelable {
@Override
public String toString() {
- return "{"
- + "tune=" + mTuneStatus
- + ", tracks=" + mTvTrackInfos
- + "}";
+ return "{" + "tune=" + mTuneStatus + ", tracks=" + mTvTrackInfos + "}";
}
}
diff --git a/tests/common/src/com/android/tv/testing/testinput/TestInputControlConnection.java b/tests/common/src/com/android/tv/testing/testinput/TestInputControlConnection.java
index 9b3f8835..071b1d3c 100644
--- a/tests/common/src/com/android/tv/testing/testinput/TestInputControlConnection.java
+++ b/tests/common/src/com/android/tv/testing/testinput/TestInputControlConnection.java
@@ -21,8 +21,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
-
-import com.android.tv.testing.ChannelInfo;
+import com.android.tv.testing.data.ChannelInfo;
/**
* Connection for controlling the Test TV Input Service.
@@ -47,9 +46,7 @@ public class TestInputControlConnection implements ServiceConnection {
mControl = null;
}
- /**
- * Is the service currently connected.
- */
+ /** Is the service currently connected. */
public boolean isBound() {
return mControl != null;
}
@@ -58,7 +55,7 @@ public class TestInputControlConnection implements ServiceConnection {
* Update the state of the channel.
*
* @param channel the channel to update.
- * @param data the new state for the channel.
+ * @param data the new state for the channel.
*/
public void updateChannelState(ChannelInfo channel, ChannelStateData data) {
waitUntilBound();
@@ -69,9 +66,7 @@ public class TestInputControlConnection implements ServiceConnection {
}
}
- /**
- * Sleep until {@link #isBound()} is true;
- */
+ /** Sleep until {@link #isBound()} is true; */
public void waitUntilBound() {
while (!isBound()) {
SystemClock.sleep(BOUND_CHECK_INTERVAL_MS);
diff --git a/tests/common/src/com/android/tv/testing/testinput/TestInputControlUtils.java b/tests/common/src/com/android/tv/testing/testinput/TestInputControlUtils.java
index 54aacf20..330afa9b 100644
--- a/tests/common/src/com/android/tv/testing/testinput/TestInputControlUtils.java
+++ b/tests/common/src/com/android/tv/testing/testinput/TestInputControlUtils.java
@@ -18,16 +18,16 @@ package com.android.tv.testing.testinput;
import android.content.ComponentName;
import android.content.Intent;
-/**
- * Static utils for {@link ITestInputControl}.
- */
+/** Static utils for {@link ITestInputControl}. */
public final class TestInputControlUtils {
public static Intent createIntent() {
- return new Intent().setComponent(new ComponentName("com.android.tv.testinput",
- "com.android.tv.testinput.TestInputControlService"));
+ return new Intent()
+ .setComponent(
+ new ComponentName(
+ "com.android.tv.testinput",
+ "com.android.tv.testinput.TestInputControlService"));
}
- private TestInputControlUtils() {
- }
+ private TestInputControlUtils() {}
}
diff --git a/tests/common/src/com/android/tv/testing/testinput/TvTestInputConstants.java b/tests/common/src/com/android/tv/testing/testinput/TvTestInputConstants.java
index 498addfd..27d3036c 100644
--- a/tests/common/src/com/android/tv/testing/testinput/TvTestInputConstants.java
+++ b/tests/common/src/com/android/tv/testing/testinput/TvTestInputConstants.java
@@ -15,25 +15,22 @@
*/
package com.android.tv.testing.testinput;
-import com.android.tv.testing.ChannelInfo;
+import com.android.tv.testing.data.ChannelInfo;
-/**
- * Constants for interacting with TvTestInput.
- */
+/** Constants for interacting with TvTestInput. */
public final class TvTestInputConstants {
/**
* Channel 1.
*
- * <p> By convention Channel 1 should not be changed. Test often start by tuning to this
- * channel.
+ * <p>By convention Channel 1 should not be changed. Test often start by tuning to this channel.
*/
public static final ChannelInfo CH_1_DEFAULT_DONT_MODIFY = ChannelInfo.create(null, 1);
/**
* Channel 2.
*
- * <p> By convention the state of Channel 2 is changed by tests. Testcases should explicitly
- * set the state of this channel before using it in tests.
+ * <p>By convention the state of Channel 2 is changed by tests. Testcases should explicitly set
+ * the state of this channel before using it in tests.
*/
public static final ChannelInfo CH_2 = ChannelInfo.create(null, 2);
}
diff --git a/tests/common/src/com/android/tv/testing/uihelper/BaseUiDeviceHelper.java b/tests/common/src/com/android/tv/testing/uihelper/BaseUiDeviceHelper.java
index 3a2f5509..21b05d67 100644
--- a/tests/common/src/com/android/tv/testing/uihelper/BaseUiDeviceHelper.java
+++ b/tests/common/src/com/android/tv/testing/uihelper/BaseUiDeviceHelper.java
@@ -18,9 +18,7 @@ package com.android.tv.testing.uihelper;
import android.content.res.Resources;
import android.support.test.uiautomator.UiDevice;
-/**
- * Base class for building UiAutomator Helper classes.
- */
+/** Base class for building UiAutomator Helper classes. */
public abstract class BaseUiDeviceHelper {
protected final UiDevice mUiDevice;
protected final Resources mTargetResources;
diff --git a/tests/common/src/com/android/tv/testing/uihelper/ByResource.java b/tests/common/src/com/android/tv/testing/uihelper/ByResource.java
index a76ee1d3..47b8d9f9 100644
--- a/tests/common/src/com/android/tv/testing/uihelper/ByResource.java
+++ b/tests/common/src/com/android/tv/testing/uihelper/ByResource.java
@@ -19,9 +19,7 @@ import android.content.res.Resources;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.BySelector;
-/**
- * Convenience methods for creating {@link BySelector}s using resource ids.
- */
+/** Convenience methods for creating {@link BySelector}s using resource ids. */
public final class ByResource {
/**
@@ -44,6 +42,5 @@ public final class ByResource {
return By.text(text);
}
- private ByResource() {
- }
+ private ByResource() {}
}
diff --git a/tests/common/src/com/android/tv/testing/uihelper/Constants.java b/tests/common/src/com/android/tv/testing/uihelper/Constants.java
index 8dd8e14a..4b522914 100644
--- a/tests/common/src/com/android/tv/testing/uihelper/Constants.java
+++ b/tests/common/src/com/android/tv/testing/uihelper/Constants.java
@@ -17,6 +17,7 @@ package com.android.tv.testing.uihelper;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.BySelector;
+import com.android.tv.common.CommonConstants;
public final class Constants {
@@ -24,7 +25,7 @@ public final class Constants {
public static final int MIN_EXTRA_TIMEOUT = 10;
public static final long MAX_SHOW_DELAY_MILLIS = 200;
public static final long MAX_FOCUSED_DELAY_MILLIS = 1000;
- public static final String TV_APP_PACKAGE = "com.android.tv";
+ public static final String TV_APP_PACKAGE = CommonConstants.BASE_PACKAGE;
public static final BySelector TV_VIEW = By.res(TV_APP_PACKAGE, "main_tunable_tv_view");
public static final BySelector CHANNEL_BANNER = By.res(TV_APP_PACKAGE, "channel_banner_view");
public static final BySelector KEYPAD_CHANNEL_SWITCH = By.res(TV_APP_PACKAGE, "channel_number");
@@ -35,6 +36,5 @@ public final class Constants {
public static final BySelector DVR_SCHEDULES = By.res(TV_APP_PACKAGE, "dvr_schedules");
public static final BySelector FOCUSED_VIEW = By.focused(true);
- private Constants() {
- }
+ private Constants() {}
}
diff --git a/tests/common/src/com/android/tv/testing/uihelper/DialogHelper.java b/tests/common/src/com/android/tv/testing/uihelper/DialogHelper.java
index 9e4040a8..2ac4b648 100644
--- a/tests/common/src/com/android/tv/testing/uihelper/DialogHelper.java
+++ b/tests/common/src/com/android/tv/testing/uihelper/DialogHelper.java
@@ -24,12 +24,9 @@ import android.content.res.Resources;
import android.support.test.uiautomator.BySelector;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.Until;
-
import com.android.tv.R;
-/**
- * Helper for testing {@link DialogFragment}s.
- */
+/** Helper for testing {@link DialogFragment}s. */
public class DialogHelper extends BaseUiDeviceHelper {
private final BySelector byPinDialog;
@@ -39,7 +36,9 @@ public class DialogHelper extends BaseUiDeviceHelper {
}
public void assertWaitForPinDialogOpen() {
- assertWaitForCondition(mUiDevice, Until.hasObject(byPinDialog),
+ assertWaitForCondition(
+ mUiDevice,
+ Until.hasObject(byPinDialog),
Constants.MAX_SHOW_DELAY_MILLIS
+ mTargetResources.getInteger(R.integer.pin_dialog_anim_duration));
}
diff --git a/tests/common/src/com/android/tv/testing/uihelper/LiveChannelsUiDeviceHelper.java b/tests/common/src/com/android/tv/testing/uihelper/LiveChannelsUiDeviceHelper.java
index 1dc0f020..4b7c1f89 100644
--- a/tests/common/src/com/android/tv/testing/uihelper/LiveChannelsUiDeviceHelper.java
+++ b/tests/common/src/com/android/tv/testing/uihelper/LiveChannelsUiDeviceHelper.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2017 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.testing.uihelper;
import static com.android.tv.testing.uihelper.UiDeviceAsserts.waitForCondition;
@@ -11,31 +26,28 @@ import android.support.test.uiautomator.BySelector;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.Until;
import android.util.Log;
-
-import com.android.tv.testing.Utils;
-
+import com.android.tv.common.CommonConstants;
+import com.android.tv.testing.utils.Utils;
import junit.framework.Assert;
-/**
- * Helper for testing the Live TV Application.
- */
+/** Helper for testing the Live TV Application. */
public class LiveChannelsUiDeviceHelper extends BaseUiDeviceHelper {
private static final String TAG = "LiveChannelsUiDevice";
private static final int APPLICATION_START_TIMEOUT_MSEC = 5000;
private final Context mContext;
- public LiveChannelsUiDeviceHelper(UiDevice uiDevice, Resources targetResources,
- Context context) {
+ public LiveChannelsUiDeviceHelper(
+ UiDevice uiDevice, Resources targetResources, Context context) {
super(uiDevice, targetResources);
mContext = context;
}
public void assertAppStarted() {
assertTrue("TvActivity should be enabled.", Utils.isTvActivityEnabled(mContext));
- Intent intent = mContext.getPackageManager()
- .getLaunchIntentForPackage(Constants.TV_APP_PACKAGE);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); // Clear out any previous instances
+ Intent intent =
+ mContext.getPackageManager().getLaunchIntentForPackage(Constants.TV_APP_PACKAGE);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); // Clear out any previous instances
mContext.startActivity(intent);
// Wait for idle state before checking the channel banner because waitForCondition() has
// timeout.
@@ -43,8 +55,10 @@ public class LiveChannelsUiDeviceHelper extends BaseUiDeviceHelper {
// Make sure that the activity is resumed.
waitForCondition(mUiDevice, Until.hasObject(Constants.TV_VIEW));
- Assert.assertTrue(Constants.TV_APP_PACKAGE + " did not start", mUiDevice
- .wait(Until.hasObject(By.pkg(Constants.TV_APP_PACKAGE).depth(0)),
+ Assert.assertTrue(
+ Constants.TV_APP_PACKAGE + " did not start",
+ mUiDevice.wait(
+ Until.hasObject(By.pkg(Constants.TV_APP_PACKAGE).depth(0)),
APPLICATION_START_TIMEOUT_MSEC));
BySelector welcome = ByResource.id(mTargetResources, com.android.tv.R.id.intro);
if (mUiDevice.hasObject(welcome)) {
@@ -54,9 +68,9 @@ public class LiveChannelsUiDeviceHelper extends BaseUiDeviceHelper {
}
public void assertAppStopped() {
- while(mUiDevice.hasObject(By.pkg(Constants.TV_APP_PACKAGE).depth(0))) {
+ while (mUiDevice.hasObject(By.pkg(CommonConstants.BASE_PACKAGE).depth(0))) {
mUiDevice.pressBack();
mUiDevice.waitForIdle();
}
}
-} \ No newline at end of file
+}
diff --git a/tests/common/src/com/android/tv/testing/uihelper/MenuHelper.java b/tests/common/src/com/android/tv/testing/uihelper/MenuHelper.java
index 80d53242..c8ea85ac 100644
--- a/tests/common/src/com/android/tv/testing/uihelper/MenuHelper.java
+++ b/tests/common/src/com/android/tv/testing/uihelper/MenuHelper.java
@@ -25,33 +25,30 @@ import android.support.test.uiautomator.Direction;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.Until;
-
import com.android.tv.R;
-
import junit.framework.Assert;
-/**
- * Helper for testing {@link com.android.tv.menu.Menu}.
- */
+/** Helper for testing {@link com.android.tv.menu.Menu}. */
public class MenuHelper extends BaseUiDeviceHelper {
private final BySelector byChannels;
public MenuHelper(UiDevice uiDevice, Resources targetResources) {
super(uiDevice, targetResources);
- byChannels = ByResource.id(mTargetResources, R.id.item_list)
- .hasDescendant(ByResource.text(mTargetResources, R.string.menu_title_channels));
+ byChannels =
+ ByResource.id(mTargetResources, R.id.item_list)
+ .hasDescendant(
+ ByResource.text(mTargetResources, R.string.menu_title_channels));
}
public BySelector getByChannels() {
return byChannels;
}
-
/**
- * Navigate to the menu item with the text {@code itemTextResId} in the row with text
- * {@code rowTitleResId}.
- * <p>
- * Fails if the menu item can not be navigated to.
+ * Navigate to the menu item with the text {@code itemTextResId} in the row with text {@code
+ * rowTitleResId}.
+ *
+ * <p>Fails if the menu item can not be navigated to.
*
* @param rowTitleResId the resource id of the string in the desired row title.
* @param itemTextResId the resource id of the string in the desired item.
@@ -62,17 +59,20 @@ public class MenuHelper extends BaseUiDeviceHelper {
BySelector byListView = ByResource.id(mTargetResources, R.id.list_view);
UiObject2 listView = row.findObject(byListView);
Assert.assertNotNull(
- "Menu row '" + mTargetResources.getString(rowTitleResId) + "' does not have a "
- + byListView, listView);
+ "Menu row '"
+ + mTargetResources.getString(rowTitleResId)
+ + "' does not have a "
+ + byListView,
+ listView);
return assertNavigateToRowItem(listView, itemTextResId);
}
/**
* Navigate to the menu row with the text title {@code rowTitleResId}.
- * <p>
- * Fails if the menu row can not be navigated to.
- * We can't navigate to the Play controls row with this method, because the row doesn't have the
- * title when it is selected. Use {@link #assertNavigateToPlayControlsRow} for the row instead.
+ *
+ * <p>Fails if the menu row can not be navigated to. We can't navigate to the Play controls row
+ * with this method, because the row doesn't have the title when it is selected. Use {@link
+ * #assertNavigateToPlayControlsRow} for the row instead.
*
* @param rowTitleResId the resource id of the string in the desired row title.
* @return the row navigated to.
@@ -82,14 +82,17 @@ public class MenuHelper extends BaseUiDeviceHelper {
UiObject2 menu = mUiDevice.findObject(MENU);
// TODO: handle play controls. They have a different dom structure and navigation sometimes
// can get stuck on that row.
- return UiDeviceAsserts.assertNavigateTo(mUiDevice, menu,
- By.hasDescendant(ByResource.text(mTargetResources, rowTitleResId)), Direction.DOWN);
+ return UiDeviceAsserts.assertNavigateTo(
+ mUiDevice,
+ menu,
+ By.hasDescendant(ByResource.text(mTargetResources, rowTitleResId)),
+ Direction.DOWN);
}
/**
* Navigate to the Play controls row.
- * <p>
- * Fails if the row can not be navigated to.
+ *
+ * <p>Fails if the row can not be navigated to.
*
* @see #assertNavigateToRow
*/
@@ -103,27 +106,28 @@ public class MenuHelper extends BaseUiDeviceHelper {
/**
* Navigate to the menu item in the given {@code row} with the text {@code itemTextResId} .
- * <p>
- * Fails if the menu item can not be navigated to.
*
- * @param row the container to look for menu items in.
+ * <p>Fails if the menu item can not be navigated to.
+ *
+ * @param row the container to look for menu items in.
* @param itemTextResId the resource id of the string in the desired item.
* @return the item navigated to.
*/
public UiObject2 assertNavigateToRowItem(UiObject2 row, int itemTextResId) {
- return UiDeviceAsserts.assertNavigateTo(mUiDevice, row,
+ return UiDeviceAsserts.assertNavigateTo(
+ mUiDevice,
+ row,
By.hasDescendant(ByResource.text(mTargetResources, itemTextResId)),
Direction.RIGHT);
}
public UiObject2 assertPressOptionsSettings() {
- return assertPressMenuItem(R.string.menu_title_options,
- R.string.options_item_settings);
+ return assertPressMenuItem(R.string.menu_title_options, R.string.options_item_settings);
}
public UiObject2 assertPressOptionsClosedCaptions() {
- return assertPressMenuItem(R.string.menu_title_options,
- R.string.options_item_closed_caption);
+ return assertPressMenuItem(
+ R.string.menu_title_options, R.string.options_item_closed_caption);
}
public UiObject2 assertPressOptionsDisplayMode() {
@@ -135,20 +139,19 @@ public class MenuHelper extends BaseUiDeviceHelper {
}
public UiObject2 assertPressProgramGuide() {
- return assertPressMenuItem(R.string.menu_title_channels,
- R.string.channels_item_program_guide);
+ return assertPressMenuItem(
+ R.string.menu_title_channels, R.string.channels_item_program_guide);
}
public UiObject2 assertPressDvrLibrary() {
- return assertPressMenuItem(R.string.menu_title_channels,
- R.string.channels_item_dvr);
+ return assertPressMenuItem(R.string.menu_title_channels, R.string.channels_item_dvr);
}
/**
- * Navigate to the menu item with the text {@code itemTextResId} in the row with text
- * {@code rowTitleResId}.
- * <p>
- * Fails if the menu item can not be navigated to.
+ * Navigate to the menu item with the text {@code itemTextResId} in the row with text {@code
+ * rowTitleResId}.
+ *
+ * <p>Fails if the menu item can not be navigated to.
*
* @param rowTitleResId the resource id of the string in the desired row title.
* @param itemTextResId the resource id of the string in the desired item.
@@ -161,17 +164,15 @@ public class MenuHelper extends BaseUiDeviceHelper {
return item;
}
- /**
- * Waits until the menu is visible.
- */
+ /** Waits until the menu is visible. */
public void assertWaitForMenu() {
UiDeviceAsserts.assertWaitForCondition(mUiDevice, Until.hasObject(MENU));
}
/**
- * Show the menu.
- * <p>
- * Fails if the menu does not appear in {@link Constants#MAX_SHOW_DELAY_MILLIS}.
+ * Show the menu.
+ *
+ * <p>Fails if the menu does not appear in {@link Constants#MAX_SHOW_DELAY_MILLIS}.
*/
public void showMenu() {
if (!mUiDevice.hasObject(MENU)) {
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 98a19a41..ba015260 100644
--- a/tests/common/src/com/android/tv/testing/uihelper/SidePanelHelper.java
+++ b/tests/common/src/com/android/tv/testing/uihelper/SidePanelHelper.java
@@ -22,15 +22,11 @@ import android.support.test.uiautomator.BySelector;
import android.support.test.uiautomator.Direction;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
-
import com.android.tv.R;
import com.android.tv.ui.sidepanel.SideFragment;
-
import junit.framework.Assert;
-/**
- * Helper for testing {@link SideFragment}s.
- */
+/** Helper for testing {@link SideFragment}s. */
public class SidePanelHelper extends BaseUiDeviceHelper {
public SidePanelHelper(UiDevice uiDevice, Resources targetResources) {
@@ -54,6 +50,7 @@ public class SidePanelHelper extends BaseUiDeviceHelper {
String title = mTargetResources.getString(resId);
return assertNavigateToItem(title, direction);
}
+
public UiObject2 assertNavigateToItem(String title) {
return assertNavigateToItem(title, Direction.DOWN);
}
@@ -63,7 +60,7 @@ public class SidePanelHelper extends BaseUiDeviceHelper {
UiObject2 sidePanelList = mUiDevice.findObject(sidePanelSelector);
Assert.assertNotNull(sidePanelSelector + " not found", sidePanelList);
- return UiDeviceAsserts.assertNavigateTo(mUiDevice, sidePanelList,
- By.hasDescendant(By.text(title)), direction);
+ return UiDeviceAsserts.assertNavigateTo(
+ mUiDevice, sidePanelList, By.hasDescendant(By.text(title)), direction);
}
}
diff --git a/tests/common/src/com/android/tv/testing/uihelper/UiDeviceAsserts.java b/tests/common/src/com/android/tv/testing/uihelper/UiDeviceAsserts.java
index c096d7d2..28ea163e 100644
--- a/tests/common/src/com/android/tv/testing/uihelper/UiDeviceAsserts.java
+++ b/tests/common/src/com/android/tv/testing/uihelper/UiDeviceAsserts.java
@@ -27,12 +27,9 @@ import android.support.test.uiautomator.SearchCondition;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.Until;
-
import junit.framework.Assert;
-/**
- * Asserts for {@link UiDevice}s.
- */
+/** Asserts for {@link UiDevice}s. */
public final class UiDeviceAsserts {
public static void assertHas(UiDevice uiDevice, BySelector bySelector, boolean expected) {
@@ -46,25 +43,25 @@ public final class UiDeviceAsserts {
}
/**
- * Assert that {@code searchCondition} becomes true within
- * {@value Constants#MAX_SHOW_DELAY_MILLIS} milliseconds.
+ * Assert that {@code searchCondition} becomes true within {@value
+ * Constants#MAX_SHOW_DELAY_MILLIS} milliseconds.
*
- * @param uiDevice the device under test.
+ * @param uiDevice the device under test.
* @param searchCondition the condition to wait for.
*/
- public static void assertWaitForCondition(UiDevice uiDevice,
- SearchCondition<Boolean> searchCondition) {
+ public static void assertWaitForCondition(
+ UiDevice uiDevice, SearchCondition<Boolean> searchCondition) {
assertWaitForCondition(uiDevice, searchCondition, Constants.MAX_SHOW_DELAY_MILLIS);
}
/**
* Assert that {@code searchCondition} becomes true within {@code timeout} milliseconds.
*
- * @param uiDevice the device under test.
+ * @param uiDevice the device under test.
* @param searchCondition the condition to wait for.
*/
- public static void assertWaitForCondition(UiDevice uiDevice,
- SearchCondition<Boolean> searchCondition, long timeout) {
+ public static void assertWaitForCondition(
+ UiDevice uiDevice, SearchCondition<Boolean> searchCondition, long timeout) {
boolean result = waitForCondition(uiDevice, searchCondition, timeout);
assertTrue(searchCondition + " not true after " + timeout / 1000.0 + " seconds.", result);
}
@@ -72,52 +69,55 @@ public final class UiDeviceAsserts {
/**
* Wait until {@code searchCondition} becomes true.
*
- * @param uiDevice The device under test.
+ * @param uiDevice The device under test.
* @param searchCondition The condition to wait for.
* @return {@code true} if the condition is met, otherwise {@code false}.
*/
- public static boolean waitForCondition(UiDevice uiDevice,
- SearchCondition<Boolean> searchCondition) {
+ public static boolean waitForCondition(
+ UiDevice uiDevice, SearchCondition<Boolean> searchCondition) {
return waitForCondition(uiDevice, searchCondition, Constants.MAX_SHOW_DELAY_MILLIS);
}
- private static boolean waitForCondition(UiDevice uiDevice,
- SearchCondition<Boolean> searchCondition, long timeout) {
- long adjustedTimeout = timeout + Math.max(Constants.MIN_EXTRA_TIMEOUT,
- (long) (timeout * Constants.EXTRA_TIMEOUT_PERCENT));
+ private static boolean waitForCondition(
+ UiDevice uiDevice, SearchCondition<Boolean> searchCondition, long timeout) {
+ long adjustedTimeout =
+ timeout
+ + Math.max(
+ Constants.MIN_EXTRA_TIMEOUT,
+ (long) (timeout * Constants.EXTRA_TIMEOUT_PERCENT));
return uiDevice.wait(searchCondition, adjustedTimeout);
}
/**
* Navigates through the focus items in a container returning the container child that has a
* descendant matching the {@code selector}.
- * <p>
- * The navigation starts in the {@code direction} specified and
- * {@link Direction#reverse(Direction) reverses} once if needed. Fails if there is not a
- * focused
- * descendant, or if after completing both directions no focused child has a descendant
- * matching
+ *
+ * <p>The navigation starts in the {@code direction} specified and {@link
+ * Direction#reverse(Direction) reverses} once if needed. Fails if there is not a focused
+ * descendant, or if after completing both directions no focused child has a descendant matching
* {@code selector}.
- * <p>
- * Fails if the menu item can not be navigated to.
*
- * @param uiDevice the device under test.
+ * <p>Fails if the menu item can not be navigated to.
+ *
+ * @param uiDevice the device under test.
* @param container contains children to navigate over.
- * @param selector the selector for the object to navigate to.
+ * @param selector the selector for the object to navigate to.
* @param direction the direction to start navigating.
* @return the object navigated to.
*/
- public static UiObject2 assertNavigateTo(UiDevice uiDevice, UiObject2 container,
- BySelector selector, Direction direction) {
+ public static UiObject2 assertNavigateTo(
+ UiDevice uiDevice, UiObject2 container, BySelector selector, Direction direction) {
int count = 0;
while (count < 2) {
BySelector hasFocusedDescendant = By.hasDescendant(FOCUSED_VIEW);
UiObject2 focusedChild = null;
- SearchCondition<Boolean> untilHasFocusedDescendant = Until
- .hasObject(hasFocusedDescendant);
+ SearchCondition<Boolean> untilHasFocusedDescendant =
+ Until.hasObject(hasFocusedDescendant);
- boolean result = container.wait(untilHasFocusedDescendant,
- UiObject2Asserts.getAdjustedTimeout(Constants.MAX_SHOW_DELAY_MILLIS));
+ boolean result =
+ container.wait(
+ untilHasFocusedDescendant,
+ UiObject2Asserts.getAdjustedTimeout(Constants.MAX_SHOW_DELAY_MILLIS));
if (!result) {
// HACK: Try direction anyways because play control does not always have a
// focused item.
@@ -147,6 +147,5 @@ public final class UiDeviceAsserts {
return null;
}
- private UiDeviceAsserts() {
- }
+ private UiDeviceAsserts() {}
}
diff --git a/tests/common/src/com/android/tv/testing/uihelper/UiDeviceUtils.java b/tests/common/src/com/android/tv/testing/uihelper/UiDeviceUtils.java
index 98eff906..d5545023 100644
--- a/tests/common/src/com/android/tv/testing/uihelper/UiDeviceUtils.java
+++ b/tests/common/src/com/android/tv/testing/uihelper/UiDeviceUtils.java
@@ -15,21 +15,11 @@
*/
package com.android.tv.testing.uihelper;
-import static junit.framework.Assert.assertTrue;
-
-import android.app.Instrumentation;
-import android.app.UiAutomation;
-import android.os.Build;
-import android.os.SystemClock;
-import android.support.test.uiautomator.Configurator;
import android.support.test.uiautomator.Direction;
import android.support.test.uiautomator.UiDevice;
-import android.view.InputDevice;
import android.view.KeyEvent;
-/**
- * Static utility methods for {@link UiDevice}.
- */
+/** Static utility methods for {@link UiDevice}. */
public final class UiDeviceUtils {
public static void pressDpad(UiDevice uiDevice, Direction direction) {
@@ -51,7 +41,6 @@ public final class UiDeviceUtils {
}
}
-
public static void pressKeys(UiDevice uiDevice, int... keyCodes) {
for (int k : keyCodes) {
uiDevice.pressKeyCode(k);
@@ -60,8 +49,8 @@ public final class UiDeviceUtils {
/**
* Parses the string and sends the corresponding individual key presses.
- * <p>
- * <b>Note:</b> only handles 0-9, '.', and '-'.
+ *
+ * <p><b>Note:</b> only handles 0-9, '.', and '-'.
*/
public static void pressKeys(UiDevice uiDevice, String keys) {
for (char c : keys.toCharArray()) {
@@ -77,59 +66,5 @@ public final class UiDeviceUtils {
}
}
- /**
- * Sends the DPAD Center key presses with the {@code repeat} count.
- * TODO: Remove instrumentation argument once migrated to JUnit4.
- */
- public static void pressDPadCenter(Instrumentation instrumentation, int repeat) {
- pressKey(instrumentation, KeyEvent.KEYCODE_DPAD_CENTER, repeat);
- }
-
- private static void pressKey(Instrumentation instrumentation, int keyCode, int repeat) {
- UiDevice.getInstance(instrumentation).waitForIdle();
- for (int i = 0; i < repeat; ++i) {
- assertPressKeyDown(instrumentation, keyCode, false);
- if (i < repeat - 1) {
- assertPressKeyUp(instrumentation, keyCode, false);
- }
- }
- // Send last key event synchronously.
- assertPressKeyUp(instrumentation, keyCode, true);
- }
-
- private static void assertPressKeyDown(Instrumentation instrumentation, int keyCode,
- boolean sync) {
- assertPressKey(instrumentation, KeyEvent.ACTION_DOWN, keyCode, sync);
- }
-
- private static void assertPressKeyUp(Instrumentation instrumentation, int keyCode,
- boolean sync) {
- assertPressKey(instrumentation, KeyEvent.ACTION_UP, keyCode, sync);
- }
-
- private static void assertPressKey(Instrumentation instrumentation, int action, int keyCode,
- boolean sync) {
- long eventTime = SystemClock.uptimeMillis();
- KeyEvent event = new KeyEvent(eventTime, eventTime, action, keyCode, 0, 0, -1, 0, 0,
- InputDevice.SOURCE_KEYBOARD);
- assertTrue("Failed to inject key up event:" + event,
- injectEvent(instrumentation, event, sync));
- }
-
- private static boolean injectEvent(Instrumentation instrumentation, KeyEvent event,
- boolean sync) {
- return getUiAutomation(instrumentation).injectInputEvent(event, sync);
- }
-
- private static UiAutomation getUiAutomation(Instrumentation instrumentation) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- int flags = Configurator.getInstance().getUiAutomationFlags();
- return instrumentation.getUiAutomation(flags);
- } else {
- return instrumentation.getUiAutomation();
- }
- }
-
- private UiDeviceUtils() {
- }
+ private UiDeviceUtils() {}
}
diff --git a/tests/common/src/com/android/tv/testing/uihelper/UiObject2Asserts.java b/tests/common/src/com/android/tv/testing/uihelper/UiObject2Asserts.java
index aba29f0e..ee02d7f7 100644
--- a/tests/common/src/com/android/tv/testing/uihelper/UiObject2Asserts.java
+++ b/tests/common/src/com/android/tv/testing/uihelper/UiObject2Asserts.java
@@ -20,41 +20,40 @@ import static junit.framework.Assert.assertTrue;
import android.support.test.uiautomator.SearchCondition;
import android.support.test.uiautomator.UiObject2;
-/**
- * Asserts for {@link UiObject2}s.
- */
+/** Asserts for {@link UiObject2}s. */
public final class UiObject2Asserts {
/**
- * Assert that {@code searchCondition} becomes true within
- * {@value Constants#MAX_SHOW_DELAY_MILLIS} milliseconds.
+ * Assert that {@code searchCondition} becomes true within {@value
+ * Constants#MAX_SHOW_DELAY_MILLIS} milliseconds.
*
- * @param uiObject the device under test.
+ * @param uiObject the device under test.
* @param searchCondition the condition to wait for.
*/
- public static void assertWaitForCondition(UiObject2 uiObject,
- SearchCondition<Boolean> searchCondition) {
+ public static void assertWaitForCondition(
+ UiObject2 uiObject, SearchCondition<Boolean> searchCondition) {
assertWaitForCondition(uiObject, searchCondition, Constants.MAX_SHOW_DELAY_MILLIS);
}
/**
* Assert that {@code searchCondition} becomes true within {@code timeout} milliseconds.
*
- * @param uiObject the device under test.
+ * @param uiObject the device under test.
* @param searchCondition the condition to wait for.
*/
- public static void assertWaitForCondition(UiObject2 uiObject,
- SearchCondition<Boolean> searchCondition, long timeout) {
+ public static void assertWaitForCondition(
+ UiObject2 uiObject, SearchCondition<Boolean> searchCondition, long timeout) {
long adjustedTimeout = getAdjustedTimeout(timeout);
boolean result = uiObject.wait(searchCondition, adjustedTimeout);
assertTrue(searchCondition + " not true after " + timeout / 1000.0 + " seconds.", result);
}
public static long getAdjustedTimeout(long timeout) {
- return timeout + Math.max(
- Constants.MIN_EXTRA_TIMEOUT, (long) (timeout * Constants.EXTRA_TIMEOUT_PERCENT));
+ return timeout
+ + Math.max(
+ Constants.MIN_EXTRA_TIMEOUT,
+ (long) (timeout * Constants.EXTRA_TIMEOUT_PERCENT));
}
- private UiObject2Asserts() {
- }
+ private UiObject2Asserts() {}
}
diff --git a/tests/common/src/com/android/tv/testing/uihelper/UiObject2Utils.java b/tests/common/src/com/android/tv/testing/uihelper/UiObject2Utils.java
index 2a997a67..2f3779c5 100644
--- a/tests/common/src/com/android/tv/testing/uihelper/UiObject2Utils.java
+++ b/tests/common/src/com/android/tv/testing/uihelper/UiObject2Utils.java
@@ -19,9 +19,7 @@ import android.graphics.Point;
import android.support.test.uiautomator.Direction;
import android.support.test.uiautomator.UiObject2;
-/**
- * Static utility methods for {@link UiObject2}s.
- */
+/** Static utility methods for {@link UiObject2}s. */
public class UiObject2Utils {
public static boolean hasSiblingInDirection(UiObject2 theUiObject, Direction direction) {
@@ -56,6 +54,5 @@ public class UiObject2Utils {
return false;
}
- private UiObject2Utils() {
- }
+ private UiObject2Utils() {}
}
diff --git a/tests/common/src/com/android/tv/testing/utils/TestUtils.java b/tests/common/src/com/android/tv/testing/utils/TestUtils.java
new file mode 100644
index 00000000..6604c9ad
--- /dev/null
+++ b/tests/common/src/com/android/tv/testing/utils/TestUtils.java
@@ -0,0 +1,193 @@
+/*
+ * 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.testing.utils;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.graphics.drawable.Icon;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.media.tv.TvInputInfo;
+import android.os.Build;
+import android.os.Bundle;
+import java.lang.reflect.Constructor;
+
+/** A class that includes convenience methods for testing. */
+public class TestUtils {
+ /** Creates a {@link TvInputInfo}. */
+ public static TvInputInfo createTvInputInfo(
+ ResolveInfo service, String id, String parentId, int type, boolean isHardwareInput)
+ throws Exception {
+ return createTvInputInfo(service, id, parentId, type, isHardwareInput, false, 0);
+ }
+
+ /**
+ * Creates a {@link TvInputInfo}.
+ *
+ * <p>If this is called on MNC, {@code canRecord} and {@code tunerCount} are ignored.
+ */
+ public static TvInputInfo createTvInputInfo(
+ ResolveInfo service,
+ String id,
+ String parentId,
+ int type,
+ boolean isHardwareInput,
+ boolean canRecord,
+ int tunerCount)
+ throws Exception {
+ // Create a mock TvInputInfo by using private constructor
+ // Note that mockito doesn't support mock/spy on final object.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ return createTvInputInfoForO(
+ service, id, parentId, type, isHardwareInput, canRecord, tunerCount);
+
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ return createTvInputInfoForNyc(
+ service, id, parentId, type, isHardwareInput, canRecord, tunerCount);
+ }
+ return createTvInputInfoForMnc(service, id, parentId, type, isHardwareInput);
+ }
+
+ /**
+ * private TvInputInfo(ResolveInfo service, String id, int type, boolean isHardwareInput,
+ * CharSequence label, int labelResId, Icon icon, Icon iconStandby, Icon iconDisconnected,
+ * String setupActivity, boolean canRecord, int tunerCount, HdmiDeviceInfo hdmiDeviceInfo,
+ * boolean isConnectedToHdmiSwitch, String parentId, Bundle extras) {
+ */
+ private static TvInputInfo createTvInputInfoForO(
+ ResolveInfo service,
+ String id,
+ String parentId,
+ int type,
+ boolean isHardwareInput,
+ boolean canRecord,
+ int tunerCount)
+ throws Exception {
+ Constructor<TvInputInfo> constructor =
+ TvInputInfo.class.getDeclaredConstructor(
+ ResolveInfo.class,
+ String.class,
+ int.class,
+ boolean.class,
+ CharSequence.class,
+ int.class,
+ Icon.class,
+ Icon.class,
+ Icon.class,
+ String.class,
+ boolean.class,
+ int.class,
+ HdmiDeviceInfo.class,
+ boolean.class,
+ String.class,
+ Bundle.class);
+ constructor.setAccessible(true);
+ return constructor.newInstance(
+ service,
+ id,
+ type,
+ isHardwareInput,
+ null,
+ 0,
+ null,
+ null,
+ null,
+ null,
+ canRecord,
+ tunerCount,
+ null,
+ false,
+ parentId,
+ null);
+ }
+
+ /**
+ * private TvInputInfo(ResolveInfo service, String id, int type, boolean isHardwareInput,
+ * CharSequence label, int labelResId, Icon icon, Icon iconStandby, Icon iconDisconnected,
+ * String setupActivity, String settingsActivity, boolean canRecord, int tunerCount,
+ * HdmiDeviceInfo hdmiDeviceInfo, boolean isConnectedToHdmiSwitch, String parentId, Bundle
+ * extras) {
+ */
+ private static TvInputInfo createTvInputInfoForNyc(
+ ResolveInfo service,
+ String id,
+ String parentId,
+ int type,
+ boolean isHardwareInput,
+ boolean canRecord,
+ int tunerCount)
+ throws Exception {
+ Constructor<TvInputInfo> constructor =
+ TvInputInfo.class.getDeclaredConstructor(
+ ResolveInfo.class,
+ String.class,
+ int.class,
+ boolean.class,
+ CharSequence.class,
+ int.class,
+ Icon.class,
+ Icon.class,
+ Icon.class,
+ String.class,
+ String.class,
+ boolean.class,
+ int.class,
+ HdmiDeviceInfo.class,
+ boolean.class,
+ String.class,
+ Bundle.class);
+ constructor.setAccessible(true);
+ return constructor.newInstance(
+ service,
+ id,
+ type,
+ isHardwareInput,
+ null,
+ 0,
+ null,
+ null,
+ null,
+ null,
+ null,
+ canRecord,
+ tunerCount,
+ null,
+ false,
+ parentId,
+ null);
+ }
+
+ private static TvInputInfo createTvInputInfoForMnc(
+ ResolveInfo service, String id, String parentId, int type, boolean isHardwareInput)
+ throws Exception {
+ Constructor<TvInputInfo> constructor =
+ TvInputInfo.class.getDeclaredConstructor(
+ ResolveInfo.class, String.class, String.class, int.class, boolean.class);
+ constructor.setAccessible(true);
+ return constructor.newInstance(service, id, parentId, type, isHardwareInput);
+ }
+
+ public static ResolveInfo createResolveInfo(String packageName, String name) {
+ ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.serviceInfo = new ServiceInfo();
+ resolveInfo.serviceInfo.packageName = packageName;
+ resolveInfo.serviceInfo.name = name;
+ resolveInfo.serviceInfo.metaData = new Bundle();
+ resolveInfo.serviceInfo.applicationInfo = new ApplicationInfo();
+ return resolveInfo;
+ }
+}
diff --git a/tests/common/src/com/android/tv/testing/Utils.java b/tests/common/src/com/android/tv/testing/utils/Utils.java
index b2b4036e..a116db0b 100644
--- a/tests/common/src/com/android/tv/testing/Utils.java
+++ b/tests/common/src/com/android/tv/testing/utils/Utils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.tv.testing;
+package com.android.tv.testing.utils;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -26,9 +26,8 @@ import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager;
import android.net.Uri;
import android.util.Log;
-
-import com.android.tv.common.TvCommonUtils;
-
+import com.android.tv.common.CommonConstants;
+import com.android.tv.common.util.CommonUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -40,12 +39,10 @@ import java.util.Random;
/**
* An utility class for testing.
*
- * <p>This class is also used to check whether TV app is running in tests or not.
- *
- * @see TvCommonUtils#isRunningInTest
+ * @see CommonUtils#isRunningInTest
*/
public final class Utils {
- private static final String TAG ="Utils";
+ private static final String TAG = "Utils";
private static final long DEFAULT_RANDOM_SEED = getSeed();
@@ -55,10 +52,12 @@ public final class Utils {
}
Resources res = context.getResources();
return new Uri.Builder()
- .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
- .authority(res.getResourcePackageName(resId))
- .path(res.getResourceTypeName(resId))
- .appendPath(res.getResourceEntryName(resId)).build().toString();
+ .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
+ .authority(res.getResourcePackageName(resId))
+ .path(res.getResourceTypeName(resId))
+ .appendPath(res.getResourceEntryName(resId))
+ .build()
+ .toString();
}
public static void copy(InputStream is, OutputStream os) throws IOException {
@@ -91,8 +90,8 @@ public final class Utils {
}
/**
- * Return the Random class which is needed to make random data for testing.
- * Default seed of the random is today's date.
+ * Return the Random class which is needed to make random data for testing. Default seed of the
+ * random is today's date.
*/
public static Random createTestRandom() {
return new Random(DEFAULT_RANDOM_SEED);
@@ -106,17 +105,15 @@ public final class Utils {
return Long.valueOf(today);
}
- private Utils() {}
-
- /**
- * Checks whether TvActivity is enabled or not.
- */
+ /** Checks whether TvActivity is enabled or not. */
public static boolean isTvActivityEnabled(Context context) {
PackageManager pm = context.getPackageManager();
- ComponentName name = new ComponentName("com.android.tv",
- "com.android.tv.TvActivity");
+ ComponentName name =
+ new ComponentName(CommonConstants.BASE_PACKAGE, "com.android.tv.TvActivity");
int enabled = pm.getComponentEnabledSetting(name);
return enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
|| enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
}
+
+ private Utils() {}
}
diff --git a/tests/func/Android.mk b/tests/func/Android.mk
index e89ba25b..855e8ebf 100644
--- a/tests/func/Android.mk
+++ b/tests/func/Android.mk
@@ -14,9 +14,11 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
tv-test-common \
ub-uiautomator \
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
LOCAL_INSTRUMENTATION_FOR := LiveTv
-LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 23 # M
+LOCAL_SDK_VERSION := system_current
+LOCAL_PROGUARD_ENABLED := disabled
include $(BUILD_PACKAGE)
diff --git a/tests/func/AndroidManifest.xml b/tests/func/AndroidManifest.xml
index 29b018e1..708dc220 100644
--- a/tests/func/AndroidManifest.xml
+++ b/tests/func/AndroidManifest.xml
@@ -18,7 +18,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.tv.tests.ui" >
- <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="21" />
+ <uses-sdk android:targetSdkVersion="26" android:minSdkVersion="21" />
<instrumentation
android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/func/src/com/android/tv/tests/ui/ChannelBannerViewTest.java b/tests/func/src/com/android/tv/tests/ui/ChannelBannerViewTest.java
index d8a4aec1..600b52b6 100644
--- a/tests/func/src/com/android/tv/tests/ui/ChannelBannerViewTest.java
+++ b/tests/func/src/com/android/tv/tests/ui/ChannelBannerViewTest.java
@@ -16,37 +16,48 @@
package com.android.tv.tests.ui;
-import static com.android.tv.testing.uihelper.UiDeviceAsserts.assertWaitForCondition;
-
-import android.support.test.filters.SmallTest;
+import android.support.test.filters.MediumTest;
import android.support.test.uiautomator.Until;
-
import com.android.tv.R;
import com.android.tv.testing.uihelper.Constants;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link com.android.tv.ui.ChannelBannerView} */
+@MediumTest
+@RunWith(JUnit4.class)
+public class ChannelBannerViewTest {
+ @Rule public final LiveChannelsTestController controller = new LiveChannelsTestController();
-@SmallTest
-public class ChannelBannerViewTest extends LiveChannelsTestCase {
// Channel banner show duration with the grace period.
private long mShowDurationMillis;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mLiveChannelsHelper.assertAppStarted();
- mShowDurationMillis = mTargetResources.getInteger(R.integer.channel_banner_show_duration)
- + Constants.MAX_SHOW_DELAY_MILLIS;
+ @Before
+ public void setUp() throws Exception {
+ controller.liveChannelsHelper.assertAppStarted();
+ mShowDurationMillis =
+ controller.getTargetResources().getInteger(R.integer.channel_banner_show_duration)
+ + Constants.MAX_SHOW_DELAY_MILLIS;
}
+ @Ignore("b/73727914")
+ @Test
public void testChannelBannerAppearDisappear() {
- mDevice.pressDPadCenter();
- assertWaitForCondition(mDevice, Until.hasObject(Constants.CHANNEL_BANNER));
- assertWaitForCondition(mDevice, Until.gone(Constants.CHANNEL_BANNER), mShowDurationMillis);
+ controller.pressDPadCenter();
+ controller.assertWaitForCondition(Until.hasObject(Constants.CHANNEL_BANNER));
+ controller.assertWaitForCondition(
+ Until.gone(Constants.CHANNEL_BANNER), mShowDurationMillis);
}
+ @Test
public void testChannelBannerShownWhenTune() {
- mDevice.pressDPadDown();
- assertWaitForCondition(mDevice, Until.hasObject(Constants.CHANNEL_BANNER));
- mDevice.pressDPadUp();
- assertWaitForCondition(mDevice, Until.hasObject(Constants.CHANNEL_BANNER));
+ controller.pressDPadDown();
+ controller.assertWaitForCondition(Until.hasObject(Constants.CHANNEL_BANNER));
+ controller.pressDPadUp();
+ controller.assertWaitForCondition(Until.hasObject(Constants.CHANNEL_BANNER));
}
}
diff --git a/tests/func/src/com/android/tv/tests/ui/ChannelSourcesTest.java b/tests/func/src/com/android/tv/tests/ui/ChannelSourcesTest.java
index cfa5eda7..53e27f1b 100644
--- a/tests/func/src/com/android/tv/tests/ui/ChannelSourcesTest.java
+++ b/tests/func/src/com/android/tv/tests/ui/ChannelSourcesTest.java
@@ -15,56 +15,64 @@
*/
package com.android.tv.tests.ui;
-import static com.android.tv.testing.uihelper.UiDeviceAsserts.assertWaitForCondition;
-
-import android.support.test.filters.LargeTest;
+import android.support.test.filters.MediumTest;
import android.support.test.uiautomator.BySelector;
import android.support.test.uiautomator.Until;
-
import com.android.tv.R;
import com.android.tv.testing.uihelper.ByResource;
-import com.android.tv.testing.uihelper.UiDeviceUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
-/**
- * Tests for channel sources.
- */
-@LargeTest
-public class ChannelSourcesTest extends LiveChannelsTestCase {
+/** Tests for channel sources. */
+@MediumTest
+@RunWith(JUnit4.class)
+public class ChannelSourcesTest {
+ @Rule public final LiveChannelsTestController controller = new LiveChannelsTestController();
private BySelector mBySettingsSidePanel;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mBySettingsSidePanel = mSidePanelHelper.bySidePanelTitled(
- R.string.side_panel_title_settings);
+ @Before
+ public void before() throws Exception {
+ mBySettingsSidePanel =
+ controller.sidePanelHelper.bySidePanelTitled(R.string.side_panel_title_settings);
}
- //TODO: create a cancelable test channel setup.
+ // TODO: create a cancelable test channel setup.
+ @Test
public void testSetup_cancel() {
- mLiveChannelsHelper.assertAppStarted();
- mMenuHelper.assertPressOptionsSettings();
- assertWaitForCondition(mDevice, Until.hasObject(mBySettingsSidePanel));
+ controller.liveChannelsHelper.assertAppStarted();
+ controller.menuHelper.assertPressOptionsSettings();
+ controller.assertWaitForCondition(Until.hasObject(mBySettingsSidePanel));
- mSidePanelHelper.assertNavigateToItem(R.string.settings_channel_source_item_setup);
- mDevice.pressDPadCenter();
+ controller.sidePanelHelper.assertNavigateToItem(
+ R.string.settings_channel_source_item_setup);
+ controller.pressDPadCenter();
- assertWaitForCondition(mDevice,
- Until.hasObject(ByResource.text(mTargetResources, R.string.setup_sources_text)));
- mDevice.pressBack();
+ controller.assertWaitForCondition(
+ Until.hasObject(
+ ByResource.text(
+ controller.getTargetResources(), R.string.setup_sources_text)));
+ controller.pressBack();
}
// SetupSourcesFragment should have no errors if side fragment item is clicked multiple times.
+ @Test
public void testSetupTwice_cancel() {
- mLiveChannelsHelper.assertAppStarted();
- mMenuHelper.assertPressOptionsSettings();
- assertWaitForCondition(mDevice, Until.hasObject(mBySettingsSidePanel));
+ controller.liveChannelsHelper.assertAppStarted();
+ controller.menuHelper.assertPressOptionsSettings();
+ controller.assertWaitForCondition(Until.hasObject(mBySettingsSidePanel));
- mSidePanelHelper.assertNavigateToItem(R.string.settings_channel_source_item_setup);
- UiDeviceUtils.pressDPadCenter(getInstrumentation(), 2);
+ controller.sidePanelHelper.assertNavigateToItem(
+ R.string.settings_channel_source_item_setup);
+ controller.pressDPadCenter(2);
- assertWaitForCondition(mDevice,
- Until.hasObject(ByResource.text(mTargetResources, R.string.setup_sources_text)));
- mDevice.pressBack();
+ controller.assertWaitForCondition(
+ Until.hasObject(
+ ByResource.text(
+ controller.getTargetResources(), R.string.setup_sources_text)));
+ controller.pressBack();
}
}
diff --git a/tests/func/src/com/android/tv/tests/ui/LiveChannelsAppTest.java b/tests/func/src/com/android/tv/tests/ui/LiveChannelsAppTest.java
index 48224123..1a5ceb45 100644
--- a/tests/func/src/com/android/tv/tests/ui/LiveChannelsAppTest.java
+++ b/tests/func/src/com/android/tv/tests/ui/LiveChannelsAppTest.java
@@ -16,103 +16,116 @@
package com.android.tv.tests.ui;
-import static com.android.tv.testing.uihelper.UiDeviceAsserts.assertHas;
-import static com.android.tv.testing.uihelper.UiDeviceAsserts.assertWaitForCondition;
-
-import android.support.test.filters.LargeTest;
+import android.support.test.filters.MediumTest;
import android.support.test.uiautomator.BySelector;
import android.support.test.uiautomator.Until;
-
import com.android.tv.R;
import com.android.tv.testing.testinput.ChannelStateData;
import com.android.tv.testing.testinput.TvTestInputConstants;
import com.android.tv.testing.uihelper.Constants;
import com.android.tv.testing.uihelper.DialogHelper;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Basic tests for the LiveChannels app. */
+@MediumTest
+@RunWith(JUnit4.class)
+public class LiveChannelsAppTest {
+ @Rule public final LiveChannelsTestController controller = new LiveChannelsTestController();
-/**
- * Basic tests for the LiveChannels app.
- */
-@LargeTest
-public class LiveChannelsAppTest extends LiveChannelsTestCase {
private BySelector mBySettingsSidePanel;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mLiveChannelsHelper.assertAppStarted();
- pressKeysForChannel(TvTestInputConstants.CH_1_DEFAULT_DONT_MODIFY);
- getInstrumentation().waitForIdleSync();
- mBySettingsSidePanel = mSidePanelHelper.bySidePanelTitled(
- R.string.side_panel_title_settings);
+ @Before
+ public void setUp() throws Exception {
+ controller.liveChannelsHelper.assertAppStarted();
+ controller.pressKeysForChannel(TvTestInputConstants.CH_1_DEFAULT_DONT_MODIFY);
+ controller.waitForIdleSync();
+ mBySettingsSidePanel =
+ controller.sidePanelHelper.bySidePanelTitled(R.string.side_panel_title_settings);
}
+ @Test
public void testSettingsCancel() {
- mMenuHelper.assertPressOptionsSettings();
- BySelector byChannelSourcesSidePanel = mSidePanelHelper
- .bySidePanelTitled(R.string.settings_channel_source_item_customize_channels);
- assertWaitForCondition(mDevice, Until.hasObject(byChannelSourcesSidePanel));
- mDevice.pressBack();
- assertWaitForCondition(mDevice, Until.gone(byChannelSourcesSidePanel));
- assertHas(mDevice, Constants.MENU, false);
+ controller.menuHelper.assertPressOptionsSettings();
+ BySelector byChannelSourcesSidePanel =
+ controller.sidePanelHelper.bySidePanelTitled(
+ R.string.settings_channel_source_item_customize_channels);
+ controller.assertWaitForCondition(Until.hasObject(byChannelSourcesSidePanel));
+ controller.pressBack();
+ controller.assertWaitForCondition(Until.gone(byChannelSourcesSidePanel));
+ controller.assertHas(Constants.MENU, false);
}
+ @Test
public void testClosedCaptionsCancel() {
- mMenuHelper.assertPressOptionsClosedCaptions();
- BySelector byClosedCaptionSidePanel = mSidePanelHelper
- .bySidePanelTitled(R.string.side_panel_title_closed_caption);
- assertWaitForCondition(mDevice, Until.hasObject(byClosedCaptionSidePanel));
- mDevice.pressBack();
- assertWaitForCondition(mDevice, Until.gone(byClosedCaptionSidePanel));
- assertHas(mDevice, Constants.MENU, false);
+ controller.menuHelper.assertPressOptionsClosedCaptions();
+ BySelector byClosedCaptionSidePanel =
+ controller.sidePanelHelper.bySidePanelTitled(
+ R.string.side_panel_title_closed_caption);
+ controller.assertWaitForCondition(Until.hasObject(byClosedCaptionSidePanel));
+ controller.pressBack();
+ controller.assertWaitForCondition(Until.gone(byClosedCaptionSidePanel));
+ controller.assertHas(Constants.MENU, false);
}
+ @Test
public void testDisplayModeCancel() {
ChannelStateData data = new ChannelStateData();
- data.mTvTrackInfos.add(com.android.tv.testing.Constants.SVGA_VIDEO_TRACK);
- data.mSelectedVideoTrackId = com.android.tv.testing.Constants.SVGA_VIDEO_TRACK
- .getId();
- updateThenTune(data, TvTestInputConstants.CH_2);
+ data.mTvTrackInfos.add(com.android.tv.testing.constants.Constants.SVGA_VIDEO_TRACK);
+ data.mSelectedVideoTrackId =
+ com.android.tv.testing.constants.Constants.SVGA_VIDEO_TRACK.getId();
+ controller.updateThenTune(data, TvTestInputConstants.CH_2);
- mMenuHelper.assertPressOptionsDisplayMode();
- BySelector byDisplayModeSidePanel = mSidePanelHelper
- .bySidePanelTitled(R.string.side_panel_title_display_mode);
- assertWaitForCondition(mDevice, Until.hasObject(byDisplayModeSidePanel));
- mDevice.pressBack();
- assertWaitForCondition(mDevice, Until.gone(byDisplayModeSidePanel));
- assertHas(mDevice, Constants.MENU, false);
+ controller.menuHelper.assertPressOptionsDisplayMode();
+ BySelector byDisplayModeSidePanel =
+ controller.sidePanelHelper.bySidePanelTitled(
+ R.string.side_panel_title_display_mode);
+ controller.assertWaitForCondition(Until.hasObject(byDisplayModeSidePanel));
+ controller.pressBack();
+ controller.assertWaitForCondition(Until.gone(byDisplayModeSidePanel));
+ controller.assertHas(Constants.MENU, false);
}
+ @Test
public void testMenu() {
- mDevice.pressMenu();
+ controller.pressMenu();
- assertWaitForCondition(mDevice, Until.hasObject(Constants.MENU));
- assertHas(mDevice, mMenuHelper.getByChannels(), true);
+ controller.assertWaitForCondition(Until.hasObject(Constants.MENU));
+ controller.assertHas(controller.menuHelper.getByChannels(), true);
}
+ @Test
public void testMultiAudioCancel() {
ChannelStateData data = new ChannelStateData();
- data.mTvTrackInfos.add(com.android.tv.testing.Constants.GENERIC_AUDIO_TRACK);
- updateThenTune(data, TvTestInputConstants.CH_2);
+ data.mTvTrackInfos.add(com.android.tv.testing.constants.Constants.GENERIC_AUDIO_TRACK);
+ controller.updateThenTune(data, TvTestInputConstants.CH_2);
- mMenuHelper.assertPressOptionsMultiAudio();
- BySelector byMultiAudioSidePanel = mSidePanelHelper
- .bySidePanelTitled(R.string.side_panel_title_multi_audio);
- assertWaitForCondition(mDevice, Until.hasObject(byMultiAudioSidePanel));
- mDevice.pressBack();
- assertWaitForCondition(mDevice, Until.gone(byMultiAudioSidePanel));
- assertHas(mDevice, Constants.MENU, false);
+ controller.menuHelper.assertPressOptionsMultiAudio();
+ BySelector byMultiAudioSidePanel =
+ controller.sidePanelHelper.bySidePanelTitled(R.string.side_panel_title_multi_audio);
+ controller.assertWaitForCondition(Until.hasObject(byMultiAudioSidePanel));
+ controller.pressBack();
+ controller.assertWaitForCondition(Until.gone(byMultiAudioSidePanel));
+ controller.assertHas(Constants.MENU, false);
}
+ @Ignore("b/72156196")
+ @Test
public void testPinCancel() {
- mMenuHelper.showMenu();
- mMenuHelper.assertPressOptionsSettings();
- assertWaitForCondition(mDevice, Until.hasObject(mBySettingsSidePanel));
- mSidePanelHelper.assertNavigateToItem(R.string.settings_parental_controls);
- mDevice.pressDPadCenter();
- DialogHelper dialogHelper = new DialogHelper(mDevice, mTargetResources);
+ controller.menuHelper.showMenu();
+ controller.menuHelper.assertPressOptionsSettings();
+ controller.assertWaitForCondition(Until.hasObject(mBySettingsSidePanel));
+ controller.sidePanelHelper.assertNavigateToItem(R.string.settings_parental_controls);
+ controller.pressDPadCenter();
+ DialogHelper dialogHelper =
+ new DialogHelper(controller.getUiDevice(), controller.getTargetResources());
dialogHelper.assertWaitForPinDialogOpen();
- mDevice.pressBack();
+ controller.pressBack();
dialogHelper.assertWaitForPinDialogClose();
- assertHas(mDevice, Constants.MENU, false);
+ controller.assertHas(Constants.MENU, false);
}
}
diff --git a/tests/func/src/com/android/tv/tests/ui/LiveChannelsTestCase.java b/tests/func/src/com/android/tv/tests/ui/LiveChannelsTestCase.java
deleted file mode 100644
index e306e6c6..00000000
--- a/tests/func/src/com/android/tv/tests/ui/LiveChannelsTestCase.java
+++ /dev/null
@@ -1,103 +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.tests.ui;
-
-import static com.android.tv.testing.uihelper.UiDeviceAsserts.assertWaitForCondition;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.Until;
-import android.test.InstrumentationTestCase;
-
-import com.android.tv.testing.ChannelInfo;
-import com.android.tv.testing.testinput.ChannelStateData;
-import com.android.tv.testing.testinput.TestInputControlConnection;
-import com.android.tv.testing.testinput.TestInputControlUtils;
-import com.android.tv.testing.uihelper.Constants;
-import com.android.tv.testing.uihelper.LiveChannelsUiDeviceHelper;
-import com.android.tv.testing.uihelper.MenuHelper;
-import com.android.tv.testing.uihelper.SidePanelHelper;
-import com.android.tv.testing.uihelper.UiDeviceUtils;
-
-/**
- * Base test case for LiveChannel UI tests.
- */
-public abstract class LiveChannelsTestCase extends InstrumentationTestCase {
- protected final TestInputControlConnection mConnection = new TestInputControlConnection();
-
- protected UiDevice mDevice;
- protected Resources mTargetResources;
- protected MenuHelper mMenuHelper;
- protected SidePanelHelper mSidePanelHelper;
- protected LiveChannelsUiDeviceHelper mLiveChannelsHelper;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- Context context = getInstrumentation().getContext();
- context.bindService(TestInputControlUtils.createIntent(), mConnection,
- Context.BIND_AUTO_CREATE);
- mDevice = UiDevice.getInstance(getInstrumentation());
- mTargetResources = getInstrumentation().getTargetContext().getResources();
- mMenuHelper = new MenuHelper(mDevice, mTargetResources);
- mSidePanelHelper = new SidePanelHelper(mDevice, mTargetResources);
- mLiveChannelsHelper = new LiveChannelsUiDeviceHelper(mDevice, mTargetResources, context);
- }
-
- @Override
- protected void tearDown() throws Exception {
- if (mConnection.isBound()) {
- getInstrumentation().getContext().unbindService(mConnection);
- }
-
- // TODO: robustly handle left over pops from failed tests.
- // Clear any side panel, menu, ...
- // Scene container should not be checked here because pressing the BACK key in some scenes
- // might launch the home screen.
- if (mDevice.hasObject(Constants.SIDE_PANEL) || mDevice.hasObject(Constants.MENU) || mDevice
- .hasObject(Constants.PROGRAM_GUIDE)) {
- mDevice.pressBack();
- }
- // To destroy the activity to make sure next test case's activity launch check works well.
- mDevice.pressBack();
- super.tearDown();
- }
-
- /**
- * Send the keys for the channel number of {@code channel} and press the DPAD
- * center.
- *
- * <p>Usually this will tune to the given channel.
- */
- protected void pressKeysForChannel(ChannelInfo channel) {
- UiDeviceUtils.pressKeys(mDevice, channel.number);
- assertWaitForCondition(mDevice, Until.hasObject(Constants.KEYPAD_CHANNEL_SWITCH));
- mDevice.pressDPadCenter();
- }
-
- /**
- * Update the channel state to {@code data} then tune to that channel.
- *
- * @param data the state to update the channel with.
- * @param channel the channel to tune to
- */
- protected void updateThenTune(ChannelStateData data, ChannelInfo channel) {
- mConnection.updateChannelState(channel, data);
- pressKeysForChannel(channel);
- }
-}
diff --git a/tests/func/src/com/android/tv/tests/ui/LiveChannelsTestController.java b/tests/func/src/com/android/tv/tests/ui/LiveChannelsTestController.java
new file mode 100644
index 00000000..03d30ca4
--- /dev/null
+++ b/tests/func/src/com/android/tv/tests/ui/LiveChannelsTestController.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2018 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 junit.framework.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.Configurator;
+import android.support.test.uiautomator.SearchCondition;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.Until;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import com.android.tv.testing.data.ChannelInfo;
+import com.android.tv.testing.testinput.ChannelStateData;
+import com.android.tv.testing.testinput.TestInputControlConnection;
+import com.android.tv.testing.testinput.TestInputControlUtils;
+import com.android.tv.testing.uihelper.Constants;
+import com.android.tv.testing.uihelper.LiveChannelsUiDeviceHelper;
+import com.android.tv.testing.uihelper.MenuHelper;
+import com.android.tv.testing.uihelper.SidePanelHelper;
+import com.android.tv.testing.uihelper.UiDeviceAsserts;
+import com.android.tv.testing.uihelper.UiDeviceUtils;
+import org.junit.rules.ExternalResource;
+
+/** UIHelpers and TestInputController for LiveChannels. */
+public class LiveChannelsTestController extends ExternalResource {
+
+ private final TestInputControlConnection mConnection = new TestInputControlConnection();
+
+ public MenuHelper menuHelper;
+ public SidePanelHelper sidePanelHelper;
+ public LiveChannelsUiDeviceHelper liveChannelsHelper;
+
+ private UiDevice mDevice;
+ private Resources mTargetResources;
+ private Instrumentation mInstrumentation;
+
+ private void assertPressKeyUp(int keyCode, boolean sync) {
+ assertPressKey(KeyEvent.ACTION_UP, keyCode, sync);
+ }
+
+ private void assertPressKey(int action, int keyCode, boolean sync) {
+ long eventTime = SystemClock.uptimeMillis();
+ KeyEvent event =
+ new KeyEvent(
+ eventTime,
+ eventTime,
+ action,
+ keyCode,
+ 0,
+ 0,
+ -1,
+ 0,
+ 0,
+ InputDevice.SOURCE_KEYBOARD);
+ assertTrue("Failed to inject key up event:" + event, injectEvent(event, sync));
+ }
+
+ private UiAutomation getUiAutomation() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ int flags = Configurator.getInstance().getUiAutomationFlags();
+ return mInstrumentation.getUiAutomation(flags);
+ } else {
+ return mInstrumentation.getUiAutomation();
+ }
+ }
+
+ @Override
+ protected void before() throws Exception {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ Context context = mInstrumentation.getContext();
+ context.bindService(
+ TestInputControlUtils.createIntent(), mConnection, Context.BIND_AUTO_CREATE);
+ mDevice = UiDevice.getInstance(mInstrumentation);
+ mTargetResources = mInstrumentation.getTargetContext().getResources();
+ menuHelper = new MenuHelper(mDevice, mTargetResources);
+ sidePanelHelper = new SidePanelHelper(mDevice, mTargetResources);
+ liveChannelsHelper = new LiveChannelsUiDeviceHelper(mDevice, mTargetResources, context);
+ }
+
+ @Override
+ protected void after() {
+ if (mConnection.isBound()) {
+ mInstrumentation.getContext().unbindService(mConnection);
+ }
+
+ // TODO: robustly handle left over pops from failed tests.
+ // Clear any side panel, menu, ...
+ // Scene container should not be checked here because pressing the BACK key in some scenes
+ // might launch the home screen.
+ if (mDevice.hasObject(Constants.SIDE_PANEL)
+ || mDevice.hasObject(Constants.MENU)
+ || mDevice.hasObject(Constants.PROGRAM_GUIDE)) {
+ mDevice.pressBack();
+ }
+ // To destroy the activity to make sure next test case's activity launch check works well.
+ mDevice.pressBack();
+ }
+
+ /**
+ * Update the channel state to {@code data} then tune to that channel.
+ *
+ * @param data the state to update the channel with.
+ * @param channel the channel to tune to
+ */
+ protected void updateThenTune(ChannelStateData data, ChannelInfo channel) {
+ mConnection.updateChannelState(channel, data);
+ pressKeysForChannel(channel);
+ }
+
+ /**
+ * Send the keys for the channel number of {@code channel} and press the DPAD center.
+ *
+ * <p>Usually this will tune to the given channel.
+ */
+ public void pressKeysForChannel(ChannelInfo channel) {
+ UiDeviceUtils.pressKeys(mDevice, channel.number);
+ UiDeviceAsserts.assertWaitForCondition(
+ mDevice, Until.hasObject(Constants.KEYPAD_CHANNEL_SWITCH));
+ mDevice.pressDPadCenter();
+ }
+
+ public void assertHas(BySelector bySelector, boolean expected) {
+ UiDeviceAsserts.assertHas(mDevice, bySelector, expected);
+ }
+
+ public void assertWaitForCondition(SearchCondition<Boolean> booleanSearchCondition) {
+ UiDeviceAsserts.assertWaitForCondition(mDevice, booleanSearchCondition);
+ }
+
+ public void assertWaitForCondition(SearchCondition<Boolean> gone, long timeout) {
+ UiDeviceAsserts.assertWaitForCondition(mDevice, gone, timeout);
+ }
+
+ public void assertWaitUntilFocused(BySelector bySelector) {
+ UiDeviceAsserts.assertWaitUntilFocused(mDevice, bySelector);
+ }
+
+ public Resources getTargetResources() {
+ return mTargetResources;
+ }
+
+ public UiDevice getUiDevice() {
+ return mDevice;
+ }
+
+ public boolean injectEvent(KeyEvent event, boolean sync) {
+ return getUiAutomation().injectInputEvent(event, sync);
+ }
+
+ public void pressBack() {
+ mDevice.pressBack();
+ }
+
+ public void pressDPadCenter() {
+ pressDPadCenter(1);
+ }
+
+ public void pressDPadCenter(int repeat) {
+ UiDevice.getInstance(mInstrumentation).waitForIdle();
+ for (int i = 0; i < repeat; ++i) {
+ assertPressKey(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, false);
+ if (i < repeat - 1) {
+ assertPressKeyUp(KeyEvent.KEYCODE_DPAD_CENTER, false);
+ }
+ }
+ // Send last key event synchronously.
+ assertPressKeyUp(KeyEvent.KEYCODE_DPAD_CENTER, true);
+ }
+
+ public void pressDPadDown() {
+ mDevice.pressDPadDown();
+ }
+
+ public void pressDPadLeft() {
+ mDevice.pressDPadLeft();
+ }
+
+ public void pressDPadRight() {
+ mDevice.pressDPadRight();
+ }
+
+ public void pressDPadUp() {
+ mDevice.pressDPadUp();
+ }
+
+ public void pressEnter() {
+ mDevice.pressEnter();
+ }
+
+ public void pressHome() {
+ mDevice.pressHome();
+ }
+
+ public void pressKeyCode(int keyCode) {
+ mDevice.pressKeyCode(keyCode);
+ }
+
+ public void pressMenu() {
+ mDevice.pressMenu();
+ }
+
+ public void waitForIdle() {
+ mDevice.waitForIdle();
+ }
+
+ public void waitForIdleSync() {
+ mInstrumentation.waitForIdleSync();
+ }
+}
diff --git a/tests/func/src/com/android/tv/tests/ui/ParentalControlsTest.java b/tests/func/src/com/android/tv/tests/ui/ParentalControlsTest.java
index 93d14bde..ee039d7c 100644
--- a/tests/func/src/com/android/tv/tests/ui/ParentalControlsTest.java
+++ b/tests/func/src/com/android/tv/tests/ui/ParentalControlsTest.java
@@ -16,139 +16,169 @@
package com.android.tv.tests.ui;
-import static com.android.tv.testing.uihelper.UiDeviceAsserts.assertWaitForCondition;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
-import android.support.test.filters.SmallTest;
+import android.support.test.filters.MediumTest;
import android.support.test.uiautomator.BySelector;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.Until;
-
import com.android.tv.R;
import com.android.tv.testing.uihelper.ByResource;
import com.android.tv.testing.uihelper.DialogHelper;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
-@SmallTest
-public class ParentalControlsTest extends LiveChannelsTestCase {
-
+/** Tests for {@link com.android.tv.ui.sidepanel.parentalcontrols.ParentalControlsFragment} */
+@MediumTest
+@RunWith(JUnit4.class)
+public class ParentalControlsTest {
+ @Rule public final LiveChannelsTestController controller = new LiveChannelsTestController();
private BySelector mBySettingsSidePanel;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mLiveChannelsHelper.assertAppStarted();
- mBySettingsSidePanel = mSidePanelHelper.bySidePanelTitled(
- R.string.side_panel_title_settings);
- prepareParentalControl();
+ @Before
+ public void setUp() throws Exception {
+
+ controller.liveChannelsHelper.assertAppStarted();
+ mBySettingsSidePanel =
+ controller.sidePanelHelper.bySidePanelTitled(R.string.side_panel_title_settings);
+ // TODO(b/72154681): prepareParentalControl();
}
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
switchParentalControl(R.string.option_toggle_parental_controls_on);
- super.tearDown();
}
+ @Test
+ public void placeHolder() {
+ // there must be at least one test.
+ }
+
+ @Ignore("b/72154681")
+ @Test
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();
+ BySelector bySidePanel =
+ controller.sidePanelHelper.bySidePanelTitled(R.string.option_program_restrictions);
+ controller.assertWaitForCondition(Until.hasObject(bySidePanel));
+ controller.sidePanelHelper.assertNavigateToItem(R.string.option_ratings);
+ controller.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));
+ bySidePanel = controller.sidePanelHelper.bySidePanelTitled(R.string.option_ratings);
+ controller.assertWaitForCondition(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));
+ UiObject2 ratingCheckBox =
+ controller
+ .sidePanelHelper
+ .assertNavigateToItem(String.valueOf(age))
+ .findObject(
+ ByResource.id(controller.getTargetResources(), R.id.check_box));
if (ratingCheckBox.isChecked()) {
- mDevice.pressDPadCenter();
+ controller.pressDPadCenter();
}
}
- mSidePanelHelper.assertNavigateToItem(String.valueOf(blockAge));
- mDevice.pressDPadCenter();
+ controller.sidePanelHelper.assertNavigateToItem(String.valueOf(blockAge));
+ controller.pressDPadCenter();
assertRatingViewIsChecked(minAge, maxAge, blockAge, true);
- mSidePanelHelper.assertNavigateToItem(String.valueOf(unBlockAge));
- mDevice.pressDPadCenter();
+ controller.sidePanelHelper.assertNavigateToItem(String.valueOf(unBlockAge));
+ controller.pressDPadCenter();
assertRatingViewIsChecked(minAge, maxAge, unBlockAge, false);
- mDevice.pressBack();
- mDevice.pressBack();
- getInstrumentation().waitForIdleSync();
+ controller.pressBack();
+ controller.pressBack();
+ controller.waitForIdleSync();
}
- private void assertRatingViewIsChecked(int minAge, int maxAge, int selectedAge,
- boolean expectedValue) {
+ 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));
+ UiObject2 ratingCheckBox =
+ controller
+ .sidePanelHelper
+ .assertNavigateToItem(String.valueOf(age))
+ .findObject(
+ ByResource.id(controller.getTargetResources(), 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,
+ 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.
+ * 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();
+ controller.sidePanelHelper.assertNavigateToItem(R.string.option_program_restrictions);
+ controller.pressDPadCenter();
+ BySelector bySidePanel =
+ controller.sidePanelHelper.bySidePanelTitled(R.string.option_program_restrictions);
+ controller.assertWaitForCondition(Until.hasObject(bySidePanel));
+ controller.sidePanelHelper.assertNavigateToItem(R.string.option_country_rating_systems);
+ controller.pressDPadCenter();
+ bySidePanel =
+ controller.sidePanelHelper.bySidePanelTitled(
+ R.string.option_country_rating_systems);
+ controller.assertWaitForCondition(Until.hasObject(bySidePanel));
+ controller.sidePanelHelper.assertNavigateToItem(R.string.option_see_all_rating_systems);
+ controller.pressDPadCenter();
// Make sure Japan rating system is selected.
- UiObject2 ratingSystemCheckBox = mSidePanelHelper.assertNavigateToItem("Japan")
- .findObject(ByResource.id(mTargetResources, R.id.check_box));
+ UiObject2 ratingSystemCheckBox =
+ controller
+ .sidePanelHelper
+ .assertNavigateToItem("Japan")
+ .findObject(ByResource.id(controller.getTargetResources(), R.id.check_box));
if (!ratingSystemCheckBox.isChecked()) {
- mDevice.pressDPadCenter();
- getInstrumentation().waitForIdleSync();
+ controller.pressDPadCenter();
+ controller.waitForIdleSync();
}
- mDevice.pressBack();
+ controller.pressBack();
}
private void switchParentalControl(int oppositeStateResId) {
- BySelector bySidePanel = mSidePanelHelper.byViewText(oppositeStateResId);
- if (mDevice.hasObject(bySidePanel)) {
- mSidePanelHelper.assertNavigateToItem(oppositeStateResId);
- mDevice.pressDPadCenter();
- getInstrumentation().waitForIdleSync();
+ BySelector bySidePanel = controller.sidePanelHelper.byViewText(oppositeStateResId);
+ if (controller.getUiDevice().hasObject(bySidePanel)) {
+ controller.sidePanelHelper.assertNavigateToItem(oppositeStateResId);
+ controller.pressDPadCenter();
+ controller.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();
+ controller.menuHelper.showMenu();
+ controller.menuHelper.assertPressOptionsSettings();
+ controller.assertWaitForCondition(Until.hasObject(mBySettingsSidePanel));
+ controller.sidePanelHelper.assertNavigateToItem(R.string.settings_parental_controls);
+ controller.pressDPadCenter();
// Enter pin code.
- DialogHelper dialogHelper = new DialogHelper(mDevice, mTargetResources);
+ DialogHelper dialogHelper =
+ new DialogHelper(controller.getUiDevice(), controller.getTargetResources());
dialogHelper.assertWaitForPinDialogOpen();
dialogHelper.enterPinCodes();
dialogHelper.assertWaitForPinDialogClose();
- BySelector bySidePanel = mSidePanelHelper.bySidePanelTitled(
- R.string.menu_parental_controls);
- assertWaitForCondition(mDevice, Until.hasObject(bySidePanel));
+ BySelector bySidePanel =
+ controller.sidePanelHelper.bySidePanelTitled(R.string.menu_parental_controls);
+ controller.assertWaitForCondition(Until.hasObject(bySidePanel));
}
}
diff --git a/tests/func/src/com/android/tv/tests/ui/PlayControlsRowViewTest.java b/tests/func/src/com/android/tv/tests/ui/PlayControlsRowViewTest.java
index 82c6a810..7c982782 100644
--- a/tests/func/src/com/android/tv/tests/ui/PlayControlsRowViewTest.java
+++ b/tests/func/src/com/android/tv/tests/ui/PlayControlsRowViewTest.java
@@ -19,121 +19,136 @@ package com.android.tv.tests.ui;
import static com.android.tv.testing.uihelper.Constants.CHANNEL_BANNER;
import static com.android.tv.testing.uihelper.Constants.FOCUSED_VIEW;
import static com.android.tv.testing.uihelper.Constants.MENU;
-import static com.android.tv.testing.uihelper.UiDeviceAsserts.assertWaitForCondition;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
import android.support.test.filters.SmallTest;
import android.support.test.uiautomator.BySelector;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.Until;
import android.view.KeyEvent;
-
import com.android.tv.R;
import com.android.tv.testing.testinput.TvTestInputConstants;
+import com.android.tv.testing.uihelper.Constants;
import com.android.tv.testing.uihelper.DialogHelper;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
@SmallTest
-public class PlayControlsRowViewTest extends LiveChannelsTestCase {
- private static final String BUTTON_ID_PLAY_PAUSE = "com.android.tv:id/play_pause";
+@RunWith(JUnit4.class)
+public class PlayControlsRowViewTest {
+ private static final String BUTTON_ID_PLAY_PAUSE = Constants.TV_APP_PACKAGE + ":id/play_pause";
+ @Rule public final LiveChannelsTestController controller = new LiveChannelsTestController();
private BySelector mBySettingsSidePanel;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mLiveChannelsHelper.assertAppStarted();
- pressKeysForChannel(TvTestInputConstants.CH_2);
+ @Before
+ public void setUp() throws Exception {
+
+ controller.liveChannelsHelper.assertAppStarted();
+ controller.pressKeysForChannel(TvTestInputConstants.CH_2);
// Wait until KeypadChannelSwitchView closes.
- assertWaitForCondition(mDevice, Until.hasObject(CHANNEL_BANNER));
+ controller.assertWaitForCondition(Until.hasObject(CHANNEL_BANNER));
// Tune to a new channel to ensure that the channel is changed.
- mDevice.pressDPadUp();
- getInstrumentation().waitForIdleSync();
- mBySettingsSidePanel = mSidePanelHelper.bySidePanelTitled(
- R.string.side_panel_title_settings);
+ controller.pressDPadUp();
+ controller.waitForIdleSync();
+ mBySettingsSidePanel =
+ controller.sidePanelHelper.bySidePanelTitled(R.string.side_panel_title_settings);
}
- /**
- * Test the normal case. The play/pause button should have focus initially.
- */
+ /** Test the normal case. The play/pause button should have focus initially. */
+ @Ignore("b/72154153")
+ @Test
public void testFocusedViewInNormalCase() {
- mMenuHelper.showMenu();
- mMenuHelper.assertNavigateToPlayControlsRow();
+ controller.menuHelper.showMenu();
+ controller.menuHelper.assertNavigateToPlayControlsRow();
assertButtonHasFocus(BUTTON_ID_PLAY_PAUSE);
- mDevice.pressBack();
+ controller.pressBack();
}
/**
- * Tests the case when the forwarding action is disabled.
- * In this case, the button corresponding to the action is disabled, so play/pause button should
- * have the focus.
+ * Tests the case when the forwarding action is disabled. In this case, the button corresponding
+ * to the action is disabled, so play/pause button should have the focus.
*/
+ @Test
public void testFocusedViewWithDisabledActionForward() {
// Fast forward button
- mDevice.pressKeyCode(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD);
- mMenuHelper.assertWaitForMenu();
+ controller.pressKeyCode(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD);
+ controller.menuHelper.assertWaitForMenu();
assertButtonHasFocus(BUTTON_ID_PLAY_PAUSE);
- mDevice.pressBack();
+ controller.pressBack();
// Next button
- mDevice.pressKeyCode(KeyEvent.KEYCODE_MEDIA_NEXT);
- mMenuHelper.assertWaitForMenu();
+ controller.pressKeyCode(KeyEvent.KEYCODE_MEDIA_NEXT);
+ controller.menuHelper.assertWaitForMenu();
assertButtonHasFocus(BUTTON_ID_PLAY_PAUSE);
- mDevice.pressBack();
+ controller.pressBack();
}
+ @Test
public void testFocusedViewInMenu() {
- mMenuHelper.showMenu();
- mDevice.pressKeyCode(KeyEvent.KEYCODE_MEDIA_PLAY);
+ controller.menuHelper.showMenu();
+ controller.pressKeyCode(KeyEvent.KEYCODE_MEDIA_PLAY);
assertButtonHasFocus(BUTTON_ID_PLAY_PAUSE);
- mMenuHelper.assertNavigateToRow(R.string.menu_title_channels);
- mDevice.pressKeyCode(KeyEvent.KEYCODE_MEDIA_NEXT);
+ controller.menuHelper.assertNavigateToRow(R.string.menu_title_channels);
+ controller.pressKeyCode(KeyEvent.KEYCODE_MEDIA_NEXT);
assertButtonHasFocus(BUTTON_ID_PLAY_PAUSE);
}
+ @Ignore("b/72154153")
+ @Test
public void testKeepPausedWhileParentalControlChange() {
// Pause the playback.
- mDevice.pressKeyCode(KeyEvent.KEYCODE_MEDIA_PAUSE);
- mMenuHelper.assertWaitForMenu();
+ controller.pressKeyCode(KeyEvent.KEYCODE_MEDIA_PAUSE);
+ controller.menuHelper.assertWaitForMenu();
assertButtonHasFocus(BUTTON_ID_PLAY_PAUSE);
// Show parental controls fragment.
- mMenuHelper.assertPressOptionsSettings();
- assertWaitForCondition(mDevice, Until.hasObject(mBySettingsSidePanel));
- mSidePanelHelper.assertNavigateToItem(R.string.settings_parental_controls);
- mDevice.pressDPadCenter();
- DialogHelper dialogHelper = new DialogHelper(mDevice, mTargetResources);
+ controller.menuHelper.assertPressOptionsSettings();
+ controller.assertWaitForCondition(Until.hasObject(mBySettingsSidePanel));
+ controller.sidePanelHelper.assertNavigateToItem(R.string.settings_parental_controls);
+ controller.pressDPadCenter();
+ DialogHelper dialogHelper =
+ new DialogHelper(controller.getUiDevice(), controller.getTargetResources());
dialogHelper.assertWaitForPinDialogOpen();
dialogHelper.enterPinCodes();
dialogHelper.assertWaitForPinDialogClose();
- BySelector bySidePanel = mSidePanelHelper.bySidePanelTitled(
- R.string.menu_parental_controls);
- assertWaitForCondition(mDevice, Until.hasObject(bySidePanel));
- mDevice.pressEnter();
- mDevice.pressEnter();
- mDevice.pressBack();
- mDevice.pressBack();
+ BySelector bySidePanel =
+ controller.sidePanelHelper.bySidePanelTitled(R.string.menu_parental_controls);
+ controller.assertWaitForCondition(Until.hasObject(bySidePanel));
+ controller.pressEnter();
+ controller.pressEnter();
+ controller.pressBack();
+ controller.pressBack();
// Return to the main menu.
- mMenuHelper.assertWaitForMenu();
+ controller.menuHelper.assertWaitForMenu();
assertButtonHasFocus(BUTTON_ID_PLAY_PAUSE);
}
+ // TODO("b/70727167"): fix tests
+ @Test
public void testKeepPausedAfterVisitingHome() {
// Pause the playback.
- mDevice.pressKeyCode(KeyEvent.KEYCODE_MEDIA_PAUSE);
- mMenuHelper.assertWaitForMenu();
+ controller.pressKeyCode(KeyEvent.KEYCODE_MEDIA_PAUSE);
+ controller.menuHelper.assertWaitForMenu();
assertButtonHasFocus(BUTTON_ID_PLAY_PAUSE);
// Press HOME twice to visit the home screen and return to Live TV.
- mDevice.pressHome();
+ controller.pressHome();
// Wait until home screen is shown.
- mDevice.waitForIdle();
- mDevice.pressHome();
+ controller.waitForIdle();
+ controller.pressHome();
// Wait until TV is resumed.
- mDevice.waitForIdle();
+ controller.waitForIdle();
// Return to the main menu.
- mMenuHelper.assertWaitForMenu();
+ controller.menuHelper.assertWaitForMenu();
assertButtonHasFocus(BUTTON_ID_PLAY_PAUSE);
}
private void assertButtonHasFocus(String buttonId) {
- UiObject2 menu = mDevice.findObject(MENU);
+ UiObject2 menu = controller.getUiDevice().findObject(MENU);
UiObject2 focusedView = menu.findObject(FOCUSED_VIEW);
assertNotNull("Play controls row doesn't have a focused child.", focusedView);
UiObject2 focusedButtonGroup = focusedView.getParent();
diff --git a/tests/func/src/com/android/tv/tests/ui/ProgramGuideTest.java b/tests/func/src/com/android/tv/tests/ui/ProgramGuideTest.java
index 06c76b3b..4adf448a 100644
--- a/tests/func/src/com/android/tv/tests/ui/ProgramGuideTest.java
+++ b/tests/func/src/com/android/tv/tests/ui/ProgramGuideTest.java
@@ -15,28 +15,28 @@
*/
package com.android.tv.tests.ui;
-import static com.android.tv.testing.uihelper.UiDeviceAsserts.assertHas;
-import static com.android.tv.testing.uihelper.UiDeviceAsserts.assertWaitForCondition;
-
-import android.support.test.filters.LargeTest;
+import android.support.test.filters.MediumTest;
import android.support.test.uiautomator.Until;
-
import com.android.tv.guide.ProgramGuide;
import com.android.tv.testing.uihelper.Constants;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
-/**
- * Tests for {@link ProgramGuide}.
- */
-@LargeTest
-public class ProgramGuideTest extends LiveChannelsTestCase {
+/** Tests for {@link ProgramGuide}. */
+@MediumTest
+@RunWith(JUnit4.class)
+public class ProgramGuideTest {
+ @Rule public final LiveChannelsTestController controller = new LiveChannelsTestController();
+ @Test
public void testCancel() {
- mLiveChannelsHelper.assertAppStarted();
- mMenuHelper.assertPressProgramGuide();
- assertWaitForCondition(mDevice,
- Until.hasObject(Constants.PROGRAM_GUIDE));
- mDevice.pressBack();
- assertWaitForCondition(mDevice, Until.gone(Constants.PROGRAM_GUIDE));
- assertHas(mDevice, Constants.MENU, false);
+ controller.liveChannelsHelper.assertAppStarted();
+ controller.menuHelper.assertPressProgramGuide();
+ controller.assertWaitForCondition(Until.hasObject(Constants.PROGRAM_GUIDE));
+ controller.pressBack();
+ controller.assertWaitForCondition(Until.gone(Constants.PROGRAM_GUIDE));
+ controller.assertHas(Constants.MENU, false);
}
}
diff --git a/tests/func/src/com/android/tv/tests/ui/TimeoutTest.java b/tests/func/src/com/android/tv/tests/ui/TimeoutTest.java
index 9bf2ac50..4b6befe4 100644
--- a/tests/func/src/com/android/tv/tests/ui/TimeoutTest.java
+++ b/tests/func/src/com/android/tv/tests/ui/TimeoutTest.java
@@ -15,40 +15,54 @@
*/
package com.android.tv.tests.ui;
-import static com.android.tv.testing.uihelper.UiDeviceAsserts.assertHas;
-import static com.android.tv.testing.uihelper.UiDeviceAsserts.assertWaitForCondition;
-
import android.support.test.filters.LargeTest;
import android.support.test.uiautomator.Until;
-
import com.android.tv.R;
import com.android.tv.testing.uihelper.Constants;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
/**
* Test timeout events like the menu despairing after no input.
- * <p>
- * <b>WARNING</b> some of these timeouts are 60 seconds. These tests will take a long time
+ *
+ * <p><b>WARNING</b> some of these timeouts are 60 seconds. These tests will take a long time
* complete.
*/
@LargeTest
-public class TimeoutTest extends LiveChannelsTestCase {
+@RunWith(JUnit4.class)
+public class TimeoutTest {
+
+ @Rule public final LiveChannelsTestController controller = new LiveChannelsTestController();
+
+ @Test
+ public void placeholder() {
+ // There must be at least one test
+ }
+ @Ignore("b/73727914")
+ @Test
public void testMenu() {
- mLiveChannelsHelper.assertAppStarted();
- mDevice.pressMenu();
+ controller.liveChannelsHelper.assertAppStarted();
+ controller.pressMenu();
- assertWaitForCondition(mDevice, Until.hasObject(Constants.MENU));
- assertWaitForCondition(mDevice, Until.gone(Constants.MENU),
- mTargetResources.getInteger(R.integer.menu_show_duration));
+ controller.assertWaitForCondition(Until.hasObject(Constants.MENU));
+ controller.assertWaitForCondition(
+ Until.gone(Constants.MENU),
+ controller.getTargetResources().getInteger(R.integer.menu_show_duration));
}
+ @Ignore("b/73727914")
+ @Test
public void testProgramGuide() {
- mLiveChannelsHelper.assertAppStarted();
- mMenuHelper.assertPressProgramGuide();
- assertWaitForCondition(mDevice,
- Until.hasObject(Constants.PROGRAM_GUIDE));
- assertWaitForCondition(mDevice, Until.gone(Constants.PROGRAM_GUIDE),
- mTargetResources.getInteger(R.integer.program_guide_show_duration));
- assertHas(mDevice, Constants.MENU, false);
+ controller.liveChannelsHelper.assertAppStarted();
+ controller.menuHelper.assertPressProgramGuide();
+ controller.assertWaitForCondition(Until.hasObject(Constants.PROGRAM_GUIDE));
+ controller.assertWaitForCondition(
+ Until.gone(Constants.PROGRAM_GUIDE),
+ controller.getTargetResources().getInteger(R.integer.program_guide_show_duration));
+ controller.assertHas(Constants.MENU, false);
}
}
diff --git a/tests/func/src/com/android/tv/tests/ui/dvr/DvrLibraryTest.java b/tests/func/src/com/android/tv/tests/ui/dvr/DvrLibraryTest.java
index d88e67ad..d0ebed91 100644
--- a/tests/func/src/com/android/tv/tests/ui/dvr/DvrLibraryTest.java
+++ b/tests/func/src/com/android/tv/tests/ui/dvr/DvrLibraryTest.java
@@ -16,8 +16,6 @@
package com.android.tv.tests.ui.dvr;
-import static com.android.tv.testing.uihelper.UiDeviceAsserts.assertHas;
-import static com.android.tv.testing.uihelper.UiDeviceAsserts.assertWaitForCondition;
import static com.android.tv.testing.uihelper.UiDeviceAsserts.assertWaitUntilFocused;
import android.os.Build;
@@ -25,195 +23,302 @@ import android.support.test.filters.MediumTest;
import android.support.test.filters.SdkSuppress;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.BySelector;
-import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.Until;
-
import com.android.tv.R;
import com.android.tv.testing.uihelper.ByResource;
import com.android.tv.testing.uihelper.Constants;
-import com.android.tv.tests.ui.LiveChannelsTestCase;
-
+import com.android.tv.tests.ui.LiveChannelsTestController;
import java.util.regex.Pattern;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+/** Test the DVR library UI */
@MediumTest
+@RunWith(JUnit4.class)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
-public class DvrLibraryTest extends LiveChannelsTestCase {
+public class DvrLibraryTest {
private static final String PROGRAM_NAME_PREFIX = "Title(";
+ @Rule public final LiveChannelsTestController controller = new LiveChannelsTestController();
+
private BySelector mRecentRow;
private BySelector mScheduledRow;
private BySelector mSeriesRow;
private BySelector mFullScheduleCard;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mRecentRow = By.hasDescendant(ByResource.text(mTargetResources, R.string.dvr_main_recent));
- mScheduledRow = By.hasDescendant(
- ByResource.text(mTargetResources, R.string.dvr_main_scheduled));
- mSeriesRow = By.hasDescendant(ByResource.text(mTargetResources, R.string.dvr_main_series));
- mFullScheduleCard = By.focusable(true).hasDescendant(
- ByResource.text(mTargetResources, R.string.dvr_full_schedule_card_view_title));
- mLiveChannelsHelper.assertAppStarted();
+ @Before
+ public void setUp() throws Exception {
+
+ mRecentRow =
+ By.hasDescendant(
+ ByResource.text(controller.getTargetResources(), R.string.dvr_main_recent));
+ mScheduledRow =
+ By.hasDescendant(
+ ByResource.text(
+ controller.getTargetResources(), R.string.dvr_main_scheduled));
+ mSeriesRow =
+ By.hasDescendant(
+ ByResource.text(controller.getTargetResources(), R.string.dvr_main_series));
+ mFullScheduleCard =
+ By.focusable(true)
+ .hasDescendant(
+ ByResource.text(
+ controller.getTargetResources(),
+ R.string.dvr_full_schedule_card_view_title));
+ controller.liveChannelsHelper.assertAppStarted();
}
+ @Test
+ public void placeholder() {
+ // TODO(b/72153742): three must be at least one test
+ }
+
+ @Ignore("b/72153742")
+ @Test
public void testCancel() {
- mMenuHelper.assertPressDvrLibrary();
- assertWaitForCondition(mDevice, Until.hasObject(Constants.DVR_LIBRARY));
- mDevice.pressBack();
- assertWaitForCondition(mDevice, Until.gone(Constants.DVR_LIBRARY));
- assertHas(mDevice, Constants.MENU, false);
+ controller.menuHelper.assertPressDvrLibrary();
+ controller.assertWaitForCondition(Until.hasObject(Constants.DVR_LIBRARY));
+ controller.pressBack();
+ controller.assertWaitForCondition(Until.gone(Constants.DVR_LIBRARY));
+ controller.assertHas(Constants.MENU, false);
}
+ @Ignore("b/72153742")
+ @Test
public void testEmptyLibrary() {
- mMenuHelper.assertPressDvrLibrary();
- assertWaitForCondition(mDevice, Until.hasObject(Constants.DVR_LIBRARY));
+ controller.menuHelper.assertPressDvrLibrary();
+ controller.assertWaitForCondition(Until.hasObject(Constants.DVR_LIBRARY));
// DVR Library is empty, only Scheduled row and Full schedule card should be displayed.
- assertHas(mDevice, mRecentRow, false);
- assertHas(mDevice, mScheduledRow, true);
- assertHas(mDevice, mSeriesRow, false);
+ controller.assertHas(mRecentRow, false);
+ controller.assertHas(mScheduledRow, true);
+ controller.assertHas(mSeriesRow, false);
- mDevice.pressDPadCenter();
- assertWaitUntilFocused(mDevice, mFullScheduleCard);
- mDevice.pressDPadCenter();
- assertWaitForCondition(mDevice, Until.gone(Constants.DVR_LIBRARY));
+ controller.pressDPadCenter();
+ assertWaitUntilFocused(controller.getUiDevice(), mFullScheduleCard);
+ controller.pressDPadCenter();
+ controller.assertWaitForCondition(Until.gone(Constants.DVR_LIBRARY));
// Empty schedules screen should be shown.
- assertHas(mDevice, Constants.DVR_SCHEDULES, true);
- assertHas(mDevice, ByResource.text(mTargetResources, R.string.dvr_schedules_empty_state),
+ controller.assertHas(Constants.DVR_SCHEDULES, true);
+ controller.assertHas(
+ ByResource.text(
+ controller.getTargetResources(), R.string.dvr_schedules_empty_state),
true);
// Close the DVR library.
- mDevice.pressBack();
- assertWaitForCondition(mDevice, Until.hasObject(Constants.DVR_LIBRARY));
- mDevice.pressBack();
- assertWaitForCondition(mDevice, Until.gone(Constants.DVR_LIBRARY));
+ controller.pressBack();
+ controller.assertWaitForCondition(Until.hasObject(Constants.DVR_LIBRARY));
+ controller.pressBack();
+ controller.assertWaitForCondition(Until.gone(Constants.DVR_LIBRARY));
}
+ @Ignore("b/72153742")
+ @Test
public void testScheduleRecordings() {
- BySelector newScheduleCard = By.focusable(true).hasDescendant(
- By.textStartsWith(PROGRAM_NAME_PREFIX)).hasDescendant(By.textEndsWith("today"));
- BySelector seriesCardWithOneSchedule = By.focusable(true).hasDescendant(
- By.textStartsWith(PROGRAM_NAME_PREFIX)).hasDescendant(By.text(mTargetResources
- .getQuantityString(R.plurals.dvr_count_scheduled_recordings, 1, 1)));
- BySelector seriesCardWithOneRecordedProgram = By.focusable(true).hasDescendant(
- By.textStartsWith(PROGRAM_NAME_PREFIX)).hasDescendant(By.text(mTargetResources
- .getQuantityString(R.plurals.dvr_count_new_recordings, 1, 1)));
- Pattern watchButton = Pattern.compile("^" + mTargetResources
- .getString(R.string.dvr_detail_watch).toUpperCase() + "\n.*$");
-
- mMenuHelper.showMenu();
- mMenuHelper.assertNavigateToPlayControlsRow();
- mDevice.pressDPadRight();
- mDevice.pressDPadCenter();
- assertWaitForCondition(mDevice, Until.hasObject(
- ByResource.text(mTargetResources, R.string.dvr_action_record_episode)));
- mDevice.pressDPadCenter();
- assertWaitForCondition(mDevice, Until.gone(
- ByResource.text(mTargetResources, R.string.dvr_action_record_episode)));
-
- mMenuHelper.assertPressDvrLibrary();
- assertWaitForCondition(mDevice, Until.hasObject(Constants.DVR_LIBRARY));
+ BySelector newScheduleCard =
+ By.focusable(true)
+ .hasDescendant(By.textStartsWith(PROGRAM_NAME_PREFIX))
+ .hasDescendant(By.textEndsWith("today"));
+ BySelector seriesCardWithOneSchedule =
+ By.focusable(true)
+ .hasDescendant(By.textStartsWith(PROGRAM_NAME_PREFIX))
+ .hasDescendant(
+ By.text(
+ controller
+ .getTargetResources()
+ .getQuantityString(
+ R.plurals.dvr_count_scheduled_recordings,
+ 1,
+ 1)));
+ BySelector seriesCardWithOneRecordedProgram =
+ By.focusable(true)
+ .hasDescendant(By.textStartsWith(PROGRAM_NAME_PREFIX))
+ .hasDescendant(
+ By.text(
+ controller
+ .getTargetResources()
+ .getQuantityString(
+ R.plurals.dvr_count_new_recordings, 1, 1)));
+ Pattern watchButton =
+ Pattern.compile(
+ "^"
+ + controller
+ .getTargetResources()
+ .getString(R.string.dvr_detail_watch)
+ .toUpperCase()
+ + "\n.*$");
+
+ controller.menuHelper.showMenu();
+ controller.menuHelper.assertNavigateToPlayControlsRow();
+ controller.pressDPadRight();
+ controller.pressDPadCenter();
+ controller.assertWaitForCondition(
+ Until.hasObject(
+ ByResource.text(
+ controller.getTargetResources(),
+ R.string.dvr_action_record_episode)));
+ controller.pressDPadCenter();
+ controller.assertWaitForCondition(
+ Until.gone(
+ ByResource.text(
+ controller.getTargetResources(),
+ R.string.dvr_action_record_episode)));
+
+ controller.menuHelper.assertPressDvrLibrary();
+ controller.assertWaitForCondition(Until.hasObject(Constants.DVR_LIBRARY));
// Schedule should be automatically added to the series.
- assertHas(mDevice, mRecentRow, false);
- assertHas(mDevice, mScheduledRow, true);
- assertHas(mDevice, mSeriesRow, true);
- String programName = mDevice.findObject(By.textStartsWith(PROGRAM_NAME_PREFIX)).getText();
+ controller.assertHas(mRecentRow, false);
+ controller.assertHas(mScheduledRow, true);
+ controller.assertHas(mSeriesRow, true);
+ String programName =
+ controller
+ .getUiDevice()
+ .findObject(By.textStartsWith(PROGRAM_NAME_PREFIX))
+ .getText();
// Move to scheduled row, there should be one new schedule and one full schedule card.
- mDevice.pressDPadRight();
- assertWaitUntilFocused(mDevice, newScheduleCard);
- mDevice.pressDPadRight();
- assertWaitUntilFocused(mDevice, mFullScheduleCard);
+ controller.pressDPadRight();
+ controller.assertWaitUntilFocused(newScheduleCard);
+ controller.pressDPadRight();
+ controller.assertWaitUntilFocused(mFullScheduleCard);
// Enters the full schedule, there should be one schedule in the full schedule.
- mDevice.pressDPadCenter();
- assertWaitForCondition(mDevice, Until.gone(Constants.DVR_LIBRARY));
- assertHas(mDevice, Constants.DVR_SCHEDULES, true);
- assertHas(mDevice, ByResource.text(mTargetResources, R.string.dvr_schedules_empty_state),
+ controller.pressDPadCenter();
+ controller.assertWaitForCondition(Until.gone(Constants.DVR_LIBRARY));
+ controller.assertHas(Constants.DVR_SCHEDULES, true);
+ controller.assertHas(
+ ByResource.text(
+ controller.getTargetResources(), R.string.dvr_schedules_empty_state),
false);
- assertHas(mDevice, By.textStartsWith(programName), true);
+ controller.assertHas(By.textStartsWith(programName), true);
// Moves to the series card, clicks it, the detail page should be shown with "View schedule"
// button.
- mDevice.pressBack();
- assertWaitForCondition(mDevice, Until.hasObject(Constants.DVR_LIBRARY));
- mDevice.pressDPadLeft();
- assertWaitUntilFocused(mDevice, newScheduleCard);
- mDevice.pressDPadDown();
- assertWaitUntilFocused(mDevice, seriesCardWithOneSchedule);
- mDevice.pressDPadCenter();
- assertWaitForCondition(mDevice, Until.gone(Constants.DVR_LIBRARY));
- assertHas(mDevice, By.text(mTargetResources
- .getString(R.string.dvr_detail_view_schedule).toUpperCase()), true);
- assertHas(mDevice, By.text(watchButton), false);
- assertHas(mDevice, By.text(mTargetResources
- .getString(R.string.dvr_detail_series_delete).toUpperCase()), false);
+ controller.pressBack();
+ controller.assertWaitForCondition(Until.hasObject(Constants.DVR_LIBRARY));
+ controller.pressDPadLeft();
+ controller.assertWaitUntilFocused(newScheduleCard);
+ controller.pressDPadDown();
+ controller.assertWaitUntilFocused(seriesCardWithOneSchedule);
+ controller.pressDPadCenter();
+ controller.assertWaitForCondition(Until.gone(Constants.DVR_LIBRARY));
+ controller.assertHas(
+ By.text(
+ controller
+ .getTargetResources()
+ .getString(R.string.dvr_detail_view_schedule)
+ .toUpperCase()),
+ true);
+ controller.assertHas(By.text(watchButton), false);
+ controller.assertHas(
+ By.text(
+ controller
+ .getTargetResources()
+ .getString(R.string.dvr_detail_series_delete)
+ .toUpperCase()),
+ false);
// Clicks the new schedule, the detail page should be shown with "Stop recording" button.
- mDevice.pressBack();
- assertWaitForCondition(mDevice, Until.hasObject(Constants.DVR_LIBRARY));
- assertWaitUntilFocused(mDevice, seriesCardWithOneSchedule);
- mDevice.pressDPadUp();
- assertWaitUntilFocused(mDevice, newScheduleCard);
- mDevice.pressDPadCenter();
- assertWaitForCondition(mDevice, Until.gone(Constants.DVR_LIBRARY));
- assertHas(mDevice, By.text(mTargetResources
- .getString(R.string.dvr_detail_stop_recording).toUpperCase()), true);
+ controller.pressBack();
+ controller.assertWaitForCondition(Until.hasObject(Constants.DVR_LIBRARY));
+ controller.assertWaitUntilFocused(seriesCardWithOneSchedule);
+ controller.pressDPadUp();
+ controller.assertWaitUntilFocused(newScheduleCard);
+ controller.pressDPadCenter();
+ controller.assertWaitForCondition(Until.gone(Constants.DVR_LIBRARY));
+ controller.assertHas(
+ By.text(
+ controller
+ .getTargetResources()
+ .getString(R.string.dvr_detail_stop_recording)
+ .toUpperCase()),
+ true);
// Stops the recording
- mDevice.pressDPadCenter();
- assertWaitForCondition(mDevice, Until.hasObject(
- ByResource.text(mTargetResources, R.string.dvr_action_stop)));
- mDevice.pressDPadCenter();
- assertWaitForCondition(mDevice, Until.gone(
- ByResource.text(mTargetResources, R.string.dvr_action_stop)));
- assertWaitForCondition(mDevice, Until.hasObject(Constants.DVR_LIBRARY));
- assertWaitUntilFocused(mDevice, mFullScheduleCard);
+ controller.pressDPadCenter();
+ controller.assertWaitForCondition(
+ Until.hasObject(
+ ByResource.text(
+ controller.getTargetResources(), R.string.dvr_action_stop)));
+ controller.pressDPadCenter();
+ controller.assertWaitForCondition(
+ Until.gone(
+ ByResource.text(
+ controller.getTargetResources(), R.string.dvr_action_stop)));
+ controller.assertWaitForCondition(Until.hasObject(Constants.DVR_LIBRARY));
+ controller.assertWaitUntilFocused(mFullScheduleCard);
// Moves to series' detail page again, now it should have two more buttons
- mDevice.pressDPadDown();
- assertWaitUntilFocused(mDevice, seriesCardWithOneRecordedProgram);
- mDevice.pressDPadCenter();
- assertWaitForCondition(mDevice, Until.gone(Constants.DVR_LIBRARY));
- assertHas(mDevice, By.text(watchButton), true);
- assertHas(mDevice, By.text(mTargetResources
- .getString(R.string.dvr_detail_view_schedule).toUpperCase()), true);
- assertHas(mDevice, By.text(mTargetResources
- .getString(R.string.dvr_detail_series_delete).toUpperCase()), true);
+ controller.pressDPadDown();
+ controller.assertWaitUntilFocused(seriesCardWithOneRecordedProgram);
+ controller.pressDPadCenter();
+ controller.assertWaitForCondition(Until.gone(Constants.DVR_LIBRARY));
+ controller.assertHas(By.text(watchButton), true);
+ controller.assertHas(
+ By.text(
+ controller
+ .getTargetResources()
+ .getString(R.string.dvr_detail_view_schedule)
+ .toUpperCase()),
+ true);
+ controller.assertHas(
+ By.text(
+ controller
+ .getTargetResources()
+ .getString(R.string.dvr_detail_series_delete)
+ .toUpperCase()),
+ true);
// Moves to the recent row and clicks the recent recorded program.
- mDevice.pressBack();
- assertWaitForCondition(mDevice, Until.hasObject(Constants.DVR_LIBRARY));
- assertWaitUntilFocused(mDevice, seriesCardWithOneRecordedProgram);
- mDevice.pressDPadUp();
- assertWaitUntilFocused(mDevice, mFullScheduleCard);
- mDevice.pressDPadUp();
- assertWaitUntilFocused(mDevice, By.focusable(true).hasDescendant(By.text(programName)));
- mDevice.pressDPadCenter();
- assertWaitForCondition(mDevice, Until.gone(Constants.DVR_LIBRARY));
- assertHas(mDevice, By.text(mTargetResources
- .getString(R.string.dvr_detail_watch).toUpperCase()), true);
- assertHas(mDevice, By.text(mTargetResources
- .getString(R.string.dvr_detail_delete).toUpperCase()), true);
+ controller.pressBack();
+ controller.assertWaitForCondition(Until.hasObject(Constants.DVR_LIBRARY));
+ controller.assertWaitUntilFocused(seriesCardWithOneRecordedProgram);
+ controller.pressDPadUp();
+ controller.assertWaitUntilFocused(mFullScheduleCard);
+ controller.pressDPadUp();
+ controller.assertWaitUntilFocused(By.focusable(true).hasDescendant(By.text(programName)));
+ controller.pressDPadCenter();
+ controller.assertWaitForCondition(Until.gone(Constants.DVR_LIBRARY));
+ controller.assertHas(
+ By.text(
+ controller
+ .getTargetResources()
+ .getString(R.string.dvr_detail_watch)
+ .toUpperCase()),
+ true);
+ controller.assertHas(
+ By.text(
+ controller
+ .getTargetResources()
+ .getString(R.string.dvr_detail_delete)
+ .toUpperCase()),
+ true);
// Moves to the delete button and clicks to remove the recorded program.
- mDevice.pressDPadRight();
- assertWaitUntilFocused(mDevice, By.text(mTargetResources
- .getString(R.string.dvr_detail_delete).toUpperCase()));
- mDevice.pressDPadCenter();
- assertWaitForCondition(mDevice, Until.hasObject(Constants.DVR_LIBRARY));
- assertWaitUntilFocused(mDevice, mFullScheduleCard);
+ controller.pressDPadRight();
+ controller.assertWaitUntilFocused(
+ By.text(
+ controller
+ .getTargetResources()
+ .getString(R.string.dvr_detail_delete)
+ .toUpperCase()));
+ controller.pressDPadCenter();
+ controller.assertWaitForCondition(Until.hasObject(Constants.DVR_LIBRARY));
+ controller.assertWaitUntilFocused(mFullScheduleCard);
// DVR Library should be empty now.
- assertHas(mDevice, mRecentRow, false);
- assertHas(mDevice, mScheduledRow, true);
- assertHas(mDevice, mSeriesRow, false);
+ controller.assertHas(mRecentRow, false);
+ controller.assertHas(mScheduledRow, true);
+ controller.assertHas(mSeriesRow, false);
// Close the DVR library.
- mDevice.pressBack();
- assertWaitForCondition(mDevice, Until.gone(Constants.DVR_LIBRARY));
+ controller.pressBack();
+ controller.assertWaitForCondition(Until.gone(Constants.DVR_LIBRARY));
}
}
diff --git a/tests/func/src/com/android/tv/tests/ui/sidepanel/CustomizeChannelListFragmentTest.java b/tests/func/src/com/android/tv/tests/ui/sidepanel/CustomizeChannelListFragmentTest.java
index deeb9bfd..09b855e2 100644
--- a/tests/func/src/com/android/tv/tests/ui/sidepanel/CustomizeChannelListFragmentTest.java
+++ b/tests/func/src/com/android/tv/tests/ui/sidepanel/CustomizeChannelListFragmentTest.java
@@ -16,37 +16,48 @@
package com.android.tv.tests.ui.sidepanel;
-import static com.android.tv.testing.uihelper.UiDeviceAsserts.assertWaitForCondition;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
import android.graphics.Point;
-import android.support.test.filters.LargeTest;
+import android.support.test.filters.MediumTest;
import android.support.test.uiautomator.BySelector;
import android.support.test.uiautomator.Direction;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.Until;
-
import com.android.tv.R;
import com.android.tv.testing.uihelper.Constants;
-import com.android.tv.tests.ui.LiveChannelsTestCase;
+import com.android.tv.tests.ui.LiveChannelsTestController;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for @{link {@link com.android.tv.ui.sidepanel.CustomizeChannelListFragment} */
+@MediumTest
+@RunWith(JUnit4.class)
+public class CustomizeChannelListFragmentTest {
-@LargeTest
-public class CustomizeChannelListFragmentTest extends LiveChannelsTestCase {
+ @Rule public final LiveChannelsTestController controller = new LiveChannelsTestController();
private BySelector mBySettingsSidePanel;
private UiObject2 mTvView;
private Point mNormalTvViewCenter;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mLiveChannelsHelper.assertAppStarted();
- mTvView = mDevice.findObject(Constants.TV_VIEW);
+ @Before
+ public void setUp() throws Exception {
+
+ controller.liveChannelsHelper.assertAppStarted();
+ mTvView = controller.getUiDevice().findObject(Constants.TV_VIEW);
mNormalTvViewCenter = mTvView.getVisibleCenter();
assertNotNull(mNormalTvViewCenter);
- pressKeysForChannel(com.android.tv.testing.testinput.TvTestInputConstants.CH_2);
+ controller.pressKeysForChannel(com.android.tv.testing.testinput.TvTestInputConstants.CH_2);
// Wait until KeypadChannelSwitchView closes.
- assertWaitForCondition(mDevice, Until.hasObject(Constants.CHANNEL_BANNER));
- mBySettingsSidePanel = mSidePanelHelper.bySidePanelTitled(
- R.string.side_panel_title_settings);
+ controller.assertWaitForCondition(Until.hasObject(Constants.CHANNEL_BANNER));
+ mBySettingsSidePanel =
+ controller.sidePanelHelper.bySidePanelTitled(R.string.side_panel_title_settings);
}
private void assertShrunkenTvView(boolean shrunkenExpected) {
@@ -58,60 +69,71 @@ public class CustomizeChannelListFragmentTest extends LiveChannelsTestCase {
}
}
+ @Test
public void testCustomizeChannelList_noraml() {
// Show customize channel list fragment
- mMenuHelper.assertPressOptionsSettings();
- assertWaitForCondition(mDevice, Until.hasObject(mBySettingsSidePanel));
- mSidePanelHelper.assertNavigateToItem(
+ controller.menuHelper.assertPressOptionsSettings();
+ controller.assertWaitForCondition(Until.hasObject(mBySettingsSidePanel));
+ controller.sidePanelHelper.assertNavigateToItem(
R.string.settings_channel_source_item_customize_channels);
- mDevice.pressDPadCenter();
- BySelector bySidePanel = mSidePanelHelper.bySidePanelTitled(
- R.string.side_panel_title_edit_channels_for_an_input);
- assertWaitForCondition(mDevice, Until.hasObject(bySidePanel));
+ controller.pressDPadCenter();
+ BySelector bySidePanel =
+ controller.sidePanelHelper.bySidePanelTitled(
+ R.string.side_panel_title_edit_channels_for_an_input);
+ controller.assertWaitForCondition(Until.hasObject(bySidePanel));
assertShrunkenTvView(true);
// Show group by fragment
- mSidePanelHelper.assertNavigateToItem(R.string.edit_channels_item_group_by, Direction.UP);
- mDevice.pressDPadCenter();
- bySidePanel = mSidePanelHelper.bySidePanelTitled(R.string.side_panel_title_group_by);
- assertWaitForCondition(mDevice, Until.hasObject(bySidePanel));
+ controller.sidePanelHelper.assertNavigateToItem(
+ R.string.edit_channels_item_group_by, Direction.UP);
+ controller.pressDPadCenter();
+ bySidePanel =
+ controller.sidePanelHelper.bySidePanelTitled(R.string.side_panel_title_group_by);
+ controller.assertWaitForCondition(Until.hasObject(bySidePanel));
assertShrunkenTvView(true);
// Back to customize channel list fragment
- mDevice.pressBack();
- bySidePanel = mSidePanelHelper.bySidePanelTitled(
- R.string.side_panel_title_edit_channels_for_an_input);
- assertWaitForCondition(mDevice, Until.hasObject(bySidePanel));
+ controller.pressBack();
+ bySidePanel =
+ controller.sidePanelHelper.bySidePanelTitled(
+ R.string.side_panel_title_edit_channels_for_an_input);
+ controller.assertWaitForCondition(Until.hasObject(bySidePanel));
assertShrunkenTvView(true);
// Return to the main menu.
- mDevice.pressBack();
- assertWaitForCondition(mDevice, Until.hasObject(mBySettingsSidePanel));
+ controller.pressBack();
+ controller.assertWaitForCondition(Until.hasObject(mBySettingsSidePanel));
assertShrunkenTvView(false);
}
+ @Ignore("b/73727914")
+ @Test
public void testCustomizeChannelList_timeout() {
// Show customize channel list fragment
- mMenuHelper.assertPressOptionsSettings();
- assertWaitForCondition(mDevice, Until.hasObject(mBySettingsSidePanel));
- mSidePanelHelper.assertNavigateToItem(
+ controller.menuHelper.assertPressOptionsSettings();
+ controller.assertWaitForCondition(Until.hasObject(mBySettingsSidePanel));
+ controller.sidePanelHelper.assertNavigateToItem(
R.string.settings_channel_source_item_customize_channels);
- mDevice.pressDPadCenter();
- BySelector bySidePanel = mSidePanelHelper.bySidePanelTitled(
- R.string.side_panel_title_edit_channels_for_an_input);
- assertWaitForCondition(mDevice, Until.hasObject(bySidePanel));
+ controller.pressDPadCenter();
+ BySelector bySidePanel =
+ controller.sidePanelHelper.bySidePanelTitled(
+ R.string.side_panel_title_edit_channels_for_an_input);
+ controller.assertWaitForCondition(Until.hasObject(bySidePanel));
assertShrunkenTvView(true);
// Show group by fragment
- mSidePanelHelper.assertNavigateToItem(R.string.edit_channels_item_group_by, Direction.UP);
- mDevice.pressDPadCenter();
- bySidePanel = mSidePanelHelper.bySidePanelTitled(R.string.side_panel_title_group_by);
- assertWaitForCondition(mDevice, Until.hasObject(bySidePanel));
+ controller.sidePanelHelper.assertNavigateToItem(
+ R.string.edit_channels_item_group_by, Direction.UP);
+ controller.pressDPadCenter();
+ bySidePanel =
+ controller.sidePanelHelper.bySidePanelTitled(R.string.side_panel_title_group_by);
+ controller.assertWaitForCondition(Until.hasObject(bySidePanel));
assertShrunkenTvView(true);
// Wait for time-out to return to the main menu.
- assertWaitForCondition(mDevice, Until.gone(bySidePanel),
- mTargetResources.getInteger(R.integer.side_panel_show_duration));
+ controller.assertWaitForCondition(
+ Until.gone(bySidePanel),
+ controller.getTargetResources().getInteger(R.integer.side_panel_show_duration));
assertShrunkenTvView(false);
}
}
diff --git a/tests/input/Android.mk b/tests/input/Android.mk
index 2efd32b9..46b5621c 100644
--- a/tests/input/Android.mk
+++ b/tests/input/Android.mk
@@ -8,7 +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_PROGUARD_ENABLED := disabled
LOCAL_STATIC_JAVA_LIBRARIES := \
tv-test-common \
diff --git a/tests/input/AndroidManifest.xml b/tests/input/AndroidManifest.xml
index 14491449..9b5df2ff 100644
--- a/tests/input/AndroidManifest.xml
+++ b/tests/input/AndroidManifest.xml
@@ -18,7 +18,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.tv.testinput">
- <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="23"/>
+ <uses-sdk android:targetSdkVersion="26" android:minSdkVersion="23"/>
<!-- Required to update or read existing channel and program information in TvProvider. -->
<uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" />
diff --git a/tests/input/func.sh b/tests/input/func.sh
new file mode 100644
index 00000000..3b2af4da
--- /dev/null
+++ b/tests/input/func.sh
@@ -0,0 +1,24 @@
+#!/system/bin/sh
+#
+# Copyright (C) 2017 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.
+
+# text fixture setup for unit tests
+
+
+echo "text fixture setup for func tests"
+
+am instrument \
+ -e testSetupMode func \
+ -w com.android.tv.testinput/.instrument.TestSetupInstrumentation \ No newline at end of file
diff --git a/tests/input/jank.sh b/tests/input/jank.sh
new file mode 100644
index 00000000..c6311a4d
--- /dev/null
+++ b/tests/input/jank.sh
@@ -0,0 +1,24 @@
+#!/system/bin/sh
+#
+# Copyright (C) 2017 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.
+
+# text fixture setup for unit tests
+
+
+echo "text fixture setup for func tests"
+
+am instrument \
+ -e testSetupMode jank \
+ -w com.android.tv.testinput/.instrument.TestSetupInstrumentation \ No newline at end of file
diff --git a/tests/input/src/com/android/tv/testinput/TestInputControl.java b/tests/input/src/com/android/tv/testinput/TestInputControl.java
index cd85c86e..5e5ec32a 100644
--- a/tests/input/src/com/android/tv/testinput/TestInputControl.java
+++ b/tests/input/src/com/android/tv/testinput/TestInputControl.java
@@ -21,28 +21,26 @@ import android.net.Uri;
import android.os.RemoteException;
import android.util.Log;
import android.util.LongSparseArray;
-
-import com.android.tv.testing.ChannelInfo;
-import com.android.tv.testing.ChannelUtils;
+import com.android.tv.testing.data.ChannelInfo;
+import com.android.tv.testing.data.ChannelUtils;
import com.android.tv.testing.testinput.ChannelState;
import com.android.tv.testing.testinput.ChannelStateData;
import com.android.tv.testing.testinput.ITestInputControl;
-
import java.util.Map;
/**
* Maintains state for the {@link TestTvInputService}.
*
- * <p>Maintains the current state for every channel. A default is sent if the state is not
+ * <p>Maintains the current state for every channel. A default is sent if the state is not
* explicitly set. The state is versioned so TestTvInputService can tell if onNotifyXXX events need
* to be sent.
*
- * <p> Test update the state using @{link ITestInputControl} via {@link TestInputControlService}.
+ * <p>Test update the state using @{link ITestInputControl} via {@link TestInputControlService}.
*/
class TestInputControl extends ITestInputControl.Stub {
- private final static String TAG = "TestInputControl";
- private final static TestInputControl INSTANCE = new TestInputControl();
+ private static final String TAG = "TestInputControl";
+ private static final TestInputControl INSTANCE = new TestInputControl();
private final LongSparseArray<ChannelInfo> mId2ChannelInfoMap = new LongSparseArray<>();
private final LongSparseArray<ChannelState> mOrigId2StateMap = new LongSparseArray<>();
@@ -50,8 +48,7 @@ class TestInputControl extends ITestInputControl.Stub {
private java.lang.String mInputId;
private boolean initialized;
- private TestInputControl() {
- }
+ private TestInputControl() {}
public static TestInputControl getInstance() {
return INSTANCE;
@@ -73,8 +70,13 @@ class TestInputControl extends ITestInputControl.Stub {
for (Long channelId : channelIdToInfoMap.keySet()) {
mId2ChannelInfoMap.put(channelId, channelIdToInfoMap.get(channelId));
}
- Log.i(TAG, "Initialized channel map for " + mInputId + " with " + mId2ChannelInfoMap.size()
- + " channels");
+ Log.i(
+ TAG,
+ "Initialized channel map for "
+ + mInputId
+ + " with "
+ + mId2ChannelInfoMap.size()
+ + " channels");
}
public ChannelInfo getChannelInfo(Uri channelUri) {
diff --git a/tests/input/src/com/android/tv/testinput/TestInputControlService.java b/tests/input/src/com/android/tv/testinput/TestInputControlService.java
index 4a5668cc..4a1306ed 100644
--- a/tests/input/src/com/android/tv/testinput/TestInputControlService.java
+++ b/tests/input/src/com/android/tv/testinput/TestInputControlService.java
@@ -20,8 +20,8 @@ import android.content.Intent;
import android.os.IBinder;
/**
- * Testcases communicate to the {@link TestInputControl} via
- * {@link com.android.tv.testing.testinput.ITestInputControl}.
+ * Testcases communicate to the {@link TestInputControl} via {@link
+ * com.android.tv.testing.testinput.ITestInputControl}.
*/
public class TestInputControlService extends Service {
diff --git a/tests/input/src/com/android/tv/testinput/TestTvInputService.java b/tests/input/src/com/android/tv/testinput/TestTvInputService.java
index 621ceacb..840587c9 100644
--- a/tests/input/src/com/android/tv/testinput/TestTvInputService.java
+++ b/tests/input/src/com/android/tv/testinput/TestTvInputService.java
@@ -41,17 +41,13 @@ import android.os.Message;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Surface;
-
import com.android.tv.input.TunerHelper;
-import com.android.tv.testing.ChannelInfo;
+import com.android.tv.testing.data.ChannelInfo;
import com.android.tv.testing.testinput.ChannelState;
-
import java.util.Date;
import java.util.concurrent.TimeUnit;
-/**
- * Simple TV input service which provides test channels.
- */
+/** Simple TV input service which provides test channels. */
public class TestTvInputService extends TvInputService {
private static final String TAG = "TestTvInputService";
private static final int REFRESH_DELAY_MS = 1000 / 5;
@@ -93,9 +89,7 @@ public class TestTvInputService extends TvInputService {
return new SimpleRecordingSessionImpl(this, inputId);
}
- /**
- * Simple session implementation that just display some text.
- */
+ /** Simple session implementation that just display some text. */
private class SimpleSessionImpl extends Session {
private static final int MSG_SEEK = 1000;
private static final int SEEK_DELAY_MS = 300;
@@ -118,28 +112,36 @@ public class TestTvInputService extends TvInputService {
// The current playback speed rate.
private float mSpeed;
- private final Handler mHandler = new Handler(Looper.myLooper()) {
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == MSG_SEEK) {
- // Actually, this input doesn't play any videos, it just shows the image.
- // So we should simulate the playback here by changing the current playback
- // position periodically in order to test the time shift.
- // If the playback is paused, the current playback position doesn't need to be
- // changed.
- if (mPausedTimeMs == 0) {
- long currentTimeMs = System.currentTimeMillis();
- mCurrentPositionMs += (long) ((currentTimeMs
- - mLastCurrentPositionUpdateTimeMs) * mSpeed);
- mCurrentPositionMs = Math.max(mRecordStartTimeMs,
- Math.min(mCurrentPositionMs, currentTimeMs));
- mLastCurrentPositionUpdateTimeMs = currentTimeMs;
+ private final Handler mHandler =
+ new Handler(Looper.myLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_SEEK) {
+ // Actually, this input doesn't play any videos, it just shows the
+ // image.
+ // So we should simulate the playback here by changing the current
+ // playback
+ // position periodically in order to test the time shift.
+ // If the playback is paused, the current playback position doesn't need
+ // to be
+ // changed.
+ if (mPausedTimeMs == 0) {
+ long currentTimeMs = System.currentTimeMillis();
+ mCurrentPositionMs +=
+ (long)
+ ((currentTimeMs - mLastCurrentPositionUpdateTimeMs)
+ * mSpeed);
+ mCurrentPositionMs =
+ Math.max(
+ mRecordStartTimeMs,
+ Math.min(mCurrentPositionMs, currentTimeMs));
+ mLastCurrentPositionUpdateTimeMs = currentTimeMs;
+ }
+ sendEmptyMessageDelayed(MSG_SEEK, SEEK_DELAY_MS);
+ }
+ super.handleMessage(msg);
}
- sendEmptyMessageDelayed(MSG_SEEK, SEEK_DELAY_MS);
- }
- super.handleMessage(msg);
- }
- };
+ };
SimpleSessionImpl(Context context) {
super(context);
@@ -213,7 +215,8 @@ public class TestTvInputService extends TvInputService {
mChannelUri = channelUri;
ChannelInfo info = mBackend.getChannelInfo(channelUri);
synchronized (mDrawRunnable) {
- if (info == null || mChannel == null
+ if (info == null
+ || mChannel == null
|| mChannel.originalNetworkId != info.originalNetworkId) {
mCurrentState = null;
}
@@ -231,8 +234,9 @@ public class TestTvInputService extends TvInputService {
Log.i(TAG, "Tuning to " + mChannel);
}
notifyTimeShiftStatusChanged(TvInputManager.TIME_SHIFT_STATUS_AVAILABLE);
- mRecordStartTimeMs = mCurrentPositionMs = mLastCurrentPositionUpdateTimeMs
- = System.currentTimeMillis();
+ mRecordStartTimeMs =
+ mCurrentPositionMs =
+ mLastCurrentPositionUpdateTimeMs = System.currentTimeMillis();
mPausedTimeMs = 0;
mHandler.sendEmptyMessageDelayed(MSG_SEEK, SEEK_DELAY_MS);
mSpeed = 1;
@@ -269,8 +273,8 @@ public class TestTvInputService extends TvInputService {
@Override
public void onTimeShiftPause() {
- mCurrentPositionMs = mPausedTimeMs = mLastCurrentPositionUpdateTimeMs
- = System.currentTimeMillis();
+ mCurrentPositionMs =
+ mPausedTimeMs = mLastCurrentPositionUpdateTimeMs = System.currentTimeMillis();
}
@Override
@@ -283,8 +287,9 @@ public class TestTvInputService extends TvInputService {
@Override
public void onTimeShiftSeekTo(long timeMs) {
mLastCurrentPositionUpdateTimeMs = System.currentTimeMillis();
- mCurrentPositionMs = Math.max(mRecordStartTimeMs,
- Math.min(timeMs, mLastCurrentPositionUpdateTimeMs));
+ mCurrentPositionMs =
+ Math.max(
+ mRecordStartTimeMs, Math.min(timeMs, mLastCurrentPositionUpdateTimeMs));
}
@Override
@@ -354,14 +359,14 @@ public class TestTvInputService extends TvInputService {
}
}
- private void update(ChannelState oldState, ChannelState newState,
- ChannelInfo currentChannel) {
+ private void update(
+ ChannelState oldState, ChannelState newState, ChannelInfo currentChannel) {
Log.i(TAG, "Updating channel " + currentChannel.number + " state to " + newState);
notifyTracksChanged(newState.getTrackInfoList());
if (oldState == null || oldState.getTuneStatus() != newState.getTuneStatus()) {
if (newState.getTuneStatus() == ChannelState.TUNE_STATUS_VIDEO_AVAILABLE) {
notifyVideoAvailable();
- //TODO handle parental controls.
+ // TODO handle parental controls.
notifyContentAllowed();
setAudioTrack(newState.getSelectedAudioTrackId());
setVideoTrack(newState.getSelectedVideoTrackId());
@@ -379,20 +384,20 @@ public class TestTvInputService extends TvInputService {
private class SimpleRecordingSessionImpl extends RecordingSession {
private final String[] PROGRAM_PROJECTION = {
- Programs.COLUMN_TITLE,
- Programs.COLUMN_EPISODE_TITLE,
- Programs.COLUMN_SHORT_DESCRIPTION,
- Programs.COLUMN_POSTER_ART_URI,
- Programs.COLUMN_THUMBNAIL_URI,
- Programs.COLUMN_CANONICAL_GENRE,
- Programs.COLUMN_CONTENT_RATING,
- Programs.COLUMN_START_TIME_UTC_MILLIS,
- Programs.COLUMN_END_TIME_UTC_MILLIS,
- Programs.COLUMN_VIDEO_WIDTH,
- Programs.COLUMN_VIDEO_HEIGHT,
- Programs.COLUMN_SEASON_DISPLAY_NUMBER,
- Programs.COLUMN_SEASON_TITLE,
- Programs.COLUMN_EPISODE_DISPLAY_NUMBER,
+ Programs.COLUMN_TITLE,
+ Programs.COLUMN_EPISODE_TITLE,
+ Programs.COLUMN_SHORT_DESCRIPTION,
+ Programs.COLUMN_POSTER_ART_URI,
+ Programs.COLUMN_THUMBNAIL_URI,
+ Programs.COLUMN_CANONICAL_GENRE,
+ Programs.COLUMN_CONTENT_RATING,
+ Programs.COLUMN_START_TIME_UTC_MILLIS,
+ Programs.COLUMN_END_TIME_UTC_MILLIS,
+ Programs.COLUMN_VIDEO_WIDTH,
+ Programs.COLUMN_VIDEO_HEIGHT,
+ Programs.COLUMN_SEASON_DISPLAY_NUMBER,
+ Programs.COLUMN_SEASON_TITLE,
+ Programs.COLUMN_EPISODE_DISPLAY_NUMBER,
};
private final String mInputId;
@@ -442,8 +447,14 @@ public class TestTvInputService extends TvInputService {
long time = System.currentTimeMillis();
if (programHintUri != null) {
// Retrieves program info from mProgramHintUri
- try (Cursor c = getContentResolver().query(programHintUri,
- PROGRAM_PROJECTION, null, null, null)) {
+ try (Cursor c =
+ getContentResolver()
+ .query(
+ programHintUri,
+ PROGRAM_PROJECTION,
+ null,
+ null,
+ null)) {
if (c != null && c.getCount() > 0) {
storeRecordedProgram(c, startTime, endTime);
return null;
@@ -453,11 +464,19 @@ public class TestTvInputService extends TvInputService {
}
}
// Retrieves the current program
- try (Cursor c = getContentResolver().query(
- TvContract.buildProgramsUriForChannel(channelUri, startTime,
- endTime - startTime < MAX_COMMAND_DELAY ? startTime :
- endTime - MAX_COMMAND_DELAY),
- PROGRAM_PROJECTION, null, null, null)) {
+ try (Cursor c =
+ getContentResolver()
+ .query(
+ TvContract.buildProgramsUriForChannel(
+ channelUri,
+ startTime,
+ endTime - startTime < MAX_COMMAND_DELAY
+ ? startTime
+ : endTime - MAX_COMMAND_DELAY),
+ PROGRAM_PROJECTION,
+ null,
+ null,
+ null)) {
if (c != null && c.getCount() == 1) {
storeRecordedProgram(c, startTime, endTime);
return null;
@@ -472,10 +491,9 @@ public class TestTvInputService extends TvInputService {
private void storeRecordedProgram(Cursor c, long startTime, long endTime) {
ContentValues values = new ContentValues();
values.put(RecordedPrograms.COLUMN_INPUT_ID, mInputId);
- values.put(RecordedPrograms.COLUMN_CHANNEL_ID,
- ContentUris.parseId(channelUri));
- values.put(RecordedPrograms.COLUMN_RECORDING_DURATION_MILLIS,
- endTime - startTime);
+ values.put(RecordedPrograms.COLUMN_CHANNEL_ID, ContentUris.parseId(channelUri));
+ values.put(
+ RecordedPrograms.COLUMN_RECORDING_DURATION_MILLIS, endTime - startTime);
if (c != null) {
int index = 0;
c.moveToNext();
@@ -492,15 +510,15 @@ public class TestTvInputService extends TvInputService {
values.put(Programs.COLUMN_VIDEO_HEIGHT, c.getLong(index++));
values.put(Programs.COLUMN_SEASON_DISPLAY_NUMBER, c.getString(index++));
values.put(Programs.COLUMN_SEASON_TITLE, c.getString(index++));
- values.put(Programs.COLUMN_EPISODE_DISPLAY_NUMBER,
- c.getString(index++));
+ values.put(Programs.COLUMN_EPISODE_DISPLAY_NUMBER, c.getString(index++));
} else {
values.put(RecordedPrograms.COLUMN_TITLE, "No program info");
values.put(RecordedPrograms.COLUMN_START_TIME_UTC_MILLIS, startTime);
values.put(RecordedPrograms.COLUMN_END_TIME_UTC_MILLIS, endTime);
}
- Uri uri = getContentResolver()
- .insert(TvContract.RecordedPrograms.CONTENT_URI, values);
+ Uri uri =
+ getContentResolver()
+ .insert(TvContract.RecordedPrograms.CONTENT_URI, values);
notifyRecordingStopped(uri);
}
}.execute();
diff --git a/tests/input/src/com/android/tv/testinput/TestTvInputSetupActivity.java b/tests/input/src/com/android/tv/testinput/TestTvInputSetupActivity.java
index a793ac71..c9153d1c 100644
--- a/tests/input/src/com/android/tv/testinput/TestTvInputSetupActivity.java
+++ b/tests/input/src/com/android/tv/testinput/TestTvInputSetupActivity.java
@@ -22,24 +22,17 @@ import android.app.Dialog;
import android.app.DialogFragment;
import android.content.Context;
import android.content.DialogInterface;
-import android.media.tv.TvContract;
import android.media.tv.TvInputInfo;
import android.os.Bundle;
import android.util.Log;
-
-import com.android.tv.testing.ChannelInfo;
-import com.android.tv.testing.ChannelUtils;
-import com.android.tv.testing.Constants;
-import com.android.tv.testing.ProgramInfo;
-import com.android.tv.testing.ProgramUtils;
-
-import java.util.ArrayList;
+import com.android.tv.common.util.Clock;
+import com.android.tv.testing.constants.Constants;
+import com.android.tv.testing.data.ChannelInfo;
+import com.android.tv.testing.data.ChannelUtils;
+import com.android.tv.testing.data.ProgramUtils;
import java.util.List;
-import java.util.Map;
-/**
- * The setup activity for {@link TestTvInputService}.
- */
+/** The setup activity for {@link TestTvInputService}. */
public class TestTvInputSetupActivity extends Activity {
private static final String TAG = "TestTvInputSetup";
private String mInputId;
@@ -60,45 +53,41 @@ public class TestTvInputSetupActivity extends Activity {
public static void registerChannels(Context context, String inputId, int channelCount) {
Log.i(TAG, "Registering " + channelCount + " channels");
- List<ChannelInfo> channels = new ArrayList<>();
- for (int i = 1; i <= channelCount; i++) {
- channels.add(ChannelInfo.create(context, i));
- }
+ List<ChannelInfo> channels = ChannelUtils.createChannelInfos(context, channelCount);
ChannelUtils.updateChannels(context, inputId, channels);
-
- // Reload channels so we have the ids.
- Map<Long, ChannelInfo> channelIdToInfoMap =
- ChannelUtils.queryChannelInfoMapForTvInput(context, inputId);
- for (Long channelId : channelIdToInfoMap.keySet()) {
- ProgramInfo programInfo = ProgramInfo.create();
- ProgramUtils.populatePrograms(context, TvContract.buildChannelUri(channelId),
- programInfo);
- }
+ ProgramUtils.updateProgramForAllChannelsOf(
+ context, inputId, Clock.SYSTEM, ProgramUtils.PROGRAM_INSERT_DURATION_MS);
}
public static class MyAlertDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
- return new AlertDialog.Builder(getActivity()).setTitle(R.string.simple_setup_title)
+ return new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.simple_setup_title)
.setMessage(R.string.simple_setup_message)
- .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int whichButton) {
- // TODO: add UI to ask how many channels
- ((TestTvInputSetupActivity) getActivity())
- .registerChannels(Constants.UNIT_TEST_CHANNEL_COUNT);
- // Sets the results so that the application can process the
- // registered channels properly.
- getActivity().setResult(Activity.RESULT_OK);
- getActivity().finish();
- }
- }).setNegativeButton(android.R.string.cancel,
+ .setPositiveButton(
+ android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ // TODO: add UI to ask how many channels
+ ((TestTvInputSetupActivity) getActivity())
+ .registerChannels(Constants.UNIT_TEST_CHANNEL_COUNT);
+ // Sets the results so that the application can process the
+ // registered channels properly.
+ getActivity().setResult(Activity.RESULT_OK);
+ getActivity().finish();
+ }
+ })
+ .setNegativeButton(
+ android.R.string.cancel,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int whichButton) {
getActivity().finish();
}
- }).create();
+ })
+ .create();
}
}
}
diff --git a/tests/input/src/com/android/tv/testinput/instrument/TestSetupInstrumentation.java b/tests/input/src/com/android/tv/testinput/instrument/TestSetupInstrumentation.java
index 48e485c5..a4bd45c0 100644
--- a/tests/input/src/com/android/tv/testinput/instrument/TestSetupInstrumentation.java
+++ b/tests/input/src/com/android/tv/testinput/instrument/TestSetupInstrumentation.java
@@ -21,22 +21,24 @@ import android.app.Instrumentation;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
-
-import com.android.tv.testing.Constants;
+import com.android.tv.testing.constants.Constants;
import com.android.tv.testinput.TestTvInputService;
import com.android.tv.testinput.TestTvInputSetupActivity;
/**
- * An instrumentation utility to set up the needed inputs, channels, programs and other settings
- * for automated unit tests.
+ * An instrumentation utility to set up the needed inputs, channels, programs and other settings for
+ * automated unit tests.
+ *
+ * <p>
*
- * <p><pre>{@code
+ * <pre>{@code
* adb shell am instrument \
* -e testSetupMode {func,jank,unit} \
* -w com.android.tv.testinput/.instrument.TestSetupInstrumentation
* }</pre>
*
* <p>Optional arguments are:
+ *
* <pre>
* -e channelCount number
* </pre>
@@ -82,23 +84,26 @@ public class TestSetupInstrumentation extends Instrumentation {
private void setup() throws TestSetupException {
final String testSetupMode = mArguments.getString(TEST_SETUP_MODE_ARG);
if (TextUtils.isEmpty(testSetupMode)) {
- Log.i(TAG, "Performing no setup actions because " + TEST_SETUP_MODE_ARG
- + " was not passed as an argument");
+ Log.i(
+ TAG,
+ "Performing no setup actions because "
+ + TEST_SETUP_MODE_ARG
+ + " was not passed as an argument");
} else {
Log.i(TAG, "Running setup for " + testSetupMode + " tests.");
int channelCount;
switch (testSetupMode) {
case "func":
- channelCount = getArgumentAsInt(CHANNEL_COUNT_ARG,
- Constants.FUNC_TEST_CHANNEL_COUNT);
+ channelCount =
+ getArgumentAsInt(CHANNEL_COUNT_ARG, Constants.FUNC_TEST_CHANNEL_COUNT);
break;
case "jank":
- channelCount = getArgumentAsInt(CHANNEL_COUNT_ARG,
- Constants.JANK_TEST_CHANNEL_COUNT);
+ channelCount =
+ getArgumentAsInt(CHANNEL_COUNT_ARG, Constants.JANK_TEST_CHANNEL_COUNT);
break;
case "unit":
- channelCount = getArgumentAsInt(CHANNEL_COUNT_ARG,
- Constants.UNIT_TEST_CHANNEL_COUNT);
+ channelCount =
+ getArgumentAsInt(CHANNEL_COUNT_ARG, Constants.UNIT_TEST_CHANNEL_COUNT);
break;
default:
throw new TestSetupException(
@@ -114,8 +119,14 @@ public class TestSetupInstrumentation extends Instrumentation {
try {
return Integer.parseInt(stringValue);
} catch (NumberFormatException e) {
- Log.w(TAG, "Unable to parse arg " + arg + " with value " + stringValue
- + " to a integer.", e);
+ Log.w(
+ TAG,
+ "Unable to parse arg "
+ + arg
+ + " with value "
+ + stringValue
+ + " to a integer.",
+ e);
}
}
return defaultValue;
diff --git a/tests/input/tools/get_test_logos.sh b/tests/input/tools/get_test_logos.sh
new file mode 100755
index 00000000..4dd87a3a
--- /dev/null
+++ b/tests/input/tools/get_test_logos.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 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.
+
+BASE="http://www.google.com/chart?chs=32&chst=d_simple_text_icon_above"
+
+# From http://developers.google.com/chart/image/docs/gallery/dynamic_icons#basic-icons
+icons=(
+ academy activities airport amusement aquarium
+ art-gallery atm baby bank-dollar bank-euro
+ bank-intl bank-pound bank-yen bar barber
+ beach beer bicycle books bowling
+ bus cafe camping car-dealer car-rental
+ car-repair casino caution cemetery-grave cemetery-tomb
+ cinema civic-building computer corporate courthouse
+ fire flag floral helicopter home
+ info landslide legal location locomotive
+ medical mobile motorcycle music parking
+ pet petrol phone picnic postal
+ repair restaurant sail school scissors
+ ship shoppingbag shoppingcart ski snack
+ snow sport star swim taxi train
+ truck wc-female wc-male wc
+ wheelchair
+ )
+
+# The 500s from https://spec.googleplex.com/quantumpalette#
+colors=(
+ DB4437 E91E63 9C27B0 673AB7 3F51B5
+ 4285F4 03A9F4 00BCD4 009688 0F9D58
+ 8BC34A CDDC39 FFEB3B F4B400 FF9800
+ FF5722 795548 9E9E9E 607D8B
+)
+
+
+# See https://developers.google.com/chart/image/docs/gallery/dynamic_icons
+for n in `seq 1 80`;
+do
+ i=n%76
+ c=$(($RANDOM%19))
+ # <font_size>|<font_fill_color>|<icon_name>|<icon_size>|<icon_fill_color>|<icon_and_text_border_color>
+ url=${BASE}"&chld=Ch+"${n}"|7|00F|${icons[${i}]}|24|${colors[${c}]}|FFF"
+ echo ${url}
+ curl ${url} -o tests/input/res/drawable-xhdpi/ch_${n}_logo.png
+done; \ No newline at end of file
diff --git a/tests/input/unit.sh b/tests/input/unit.sh
new file mode 100644
index 00000000..a14d3292
--- /dev/null
+++ b/tests/input/unit.sh
@@ -0,0 +1,24 @@
+#!/system/bin/sh
+#
+# Copyright (C) 2017 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.
+
+# text fixture setup for unit tests
+
+
+echo "text fixture setup for unit tests"
+
+am instrument \
+ -e testSetupMode unit \
+ -w com.android.tv.testinput/.instrument.TestSetupInstrumentation \ No newline at end of file
diff --git a/tests/jank/Android.mk b/tests/jank/Android.mk
index b71ea1b7..1b67ac3f 100644
--- a/tests/jank/Android.mk
+++ b/tests/jank/Android.mk
@@ -15,9 +15,11 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
ub-janktesthelper \
ub-uiautomator \
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
LOCAL_INSTRUMENTATION_FOR := LiveTv
-LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 23 # M
+LOCAL_SDK_VERSION := system_current
+LOCAL_PROGUARD_ENABLED := disabled
include $(BUILD_PACKAGE)
diff --git a/tests/jank/AndroidManifest.xml b/tests/jank/AndroidManifest.xml
index fa09917d..5ea72b44 100644
--- a/tests/jank/AndroidManifest.xml
+++ b/tests/jank/AndroidManifest.xml
@@ -18,7 +18,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.tv.tests.jank" >
- <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="21" />
+ <uses-sdk android:targetSdkVersion="26" android:minSdkVersion="21" />
<instrumentation
android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/jank/src/com/android/tv/tests/jank/ChannelZappingJankTest.java b/tests/jank/src/com/android/tv/tests/jank/ChannelZappingJankTest.java
index ef936e32..eee2328b 100644
--- a/tests/jank/src/com/android/tv/tests/jank/ChannelZappingJankTest.java
+++ b/tests/jank/src/com/android/tv/tests/jank/ChannelZappingJankTest.java
@@ -19,9 +19,7 @@ import android.support.test.filters.MediumTest;
import android.support.test.jank.GfxMonitor;
import android.support.test.jank.JankTest;
-/**
- * Jank tests for channel zapping.
- */
+/** Jank tests for channel zapping. */
@MediumTest
public class ChannelZappingJankTest extends LiveChannelsTestCase {
private static final String TAG = "ChannelZappingJankTest";
@@ -29,15 +27,17 @@ public class ChannelZappingJankTest extends LiveChannelsTestCase {
private static final String STARTING_CHANNEL = "13";
/**
- * The minimum number of frames expected during each jank test.
- * If there is less the test will fail. To be safe we loop the action in each test to create
- * twice this many frames under normal conditions.
- * <p>At least 100 frams should be chosen so there will be enough frame
- * for the 90th, 95th, and 98th percentile measurements are significant.
+ * The minimum number of frames expected during each jank test. If there is less the test will
+ * fail. To be safe we loop the action in each test to create twice this many frames under
+ * normal conditions.
+ *
+ * <p>At least 100 frams should be chosen so there will be enough frame for the 90th, 95th, and
+ * 98th percentile measurements are significant.
*
* @see <a href="http://go/janktesthelper-best-practices">Jank Test Helper Best Practices</a>
*/
private static final int EXPECTED_FRAMES = 100;
+
private static final int WARM_UP_CHANNEL_ZAPPING_COUNT = 2;
@Override
@@ -46,11 +46,10 @@ public class ChannelZappingJankTest extends LiveChannelsTestCase {
Utils.pressKeysForChannelNumber(STARTING_CHANNEL, mDevice);
}
- @JankTest(expectedFrames = EXPECTED_FRAMES,
- beforeTest = "warmChannelZapping")
+ @JankTest(expectedFrames = EXPECTED_FRAMES, beforeTest = "warmChannelZapping")
@GfxMonitor(processName = Utils.LIVE_CHANNELS_PROCESS_NAME)
public void testChannelZapping() {
- int frameCountForOneChannelZapping = 40; // measured by hand
+ int frameCountForOneChannelZapping = 40; // measured by hand
int repeat = EXPECTED_FRAMES * 2 / frameCountForOneChannelZapping;
for (int i = 0; i < repeat; i++) {
mDevice.pressDPadUp();
diff --git a/tests/jank/src/com/android/tv/tests/jank/LiveChannelsTestCase.java b/tests/jank/src/com/android/tv/tests/jank/LiveChannelsTestCase.java
index 6de01036..507e9dd5 100644
--- a/tests/jank/src/com/android/tv/tests/jank/LiveChannelsTestCase.java
+++ b/tests/jank/src/com/android/tv/tests/jank/LiveChannelsTestCase.java
@@ -18,12 +18,9 @@ package com.android.tv.tests.jank;
import android.content.res.Resources;
import android.support.test.jank.JankTestBase;
import android.support.test.uiautomator.UiDevice;
-
import com.android.tv.testing.uihelper.LiveChannelsUiDeviceHelper;
-/**
- * Base test case for LiveChannel jank tests.
- */
+/** Base test case for LiveChannel jank tests. */
abstract class LiveChannelsTestCase extends JankTestBase {
protected UiDevice mDevice;
protected Resources mTargetResources;
@@ -34,8 +31,9 @@ abstract class LiveChannelsTestCase extends JankTestBase {
super.setUp();
mDevice = UiDevice.getInstance(getInstrumentation());
mTargetResources = getInstrumentation().getTargetContext().getResources();
- mLiveChannelsHelper = new LiveChannelsUiDeviceHelper(mDevice, mTargetResources,
- getInstrumentation().getContext());
+ mLiveChannelsHelper =
+ new LiveChannelsUiDeviceHelper(
+ mDevice, mTargetResources, getInstrumentation().getContext());
mLiveChannelsHelper.assertAppStarted();
}
diff --git a/tests/jank/src/com/android/tv/tests/jank/MenuJankTest.java b/tests/jank/src/com/android/tv/tests/jank/MenuJankTest.java
index 411a0bb9..ea80eb3d 100644
--- a/tests/jank/src/com/android/tv/tests/jank/MenuJankTest.java
+++ b/tests/jank/src/com/android/tv/tests/jank/MenuJankTest.java
@@ -18,26 +18,25 @@ package com.android.tv.tests.jank;
import android.support.test.filters.MediumTest;
import android.support.test.jank.GfxMonitor;
import android.support.test.jank.JankTest;
-
import com.android.tv.testing.uihelper.MenuHelper;
-/**
- * Jank tests for the program guide.
- */
+/** Jank tests for the program guide. */
@MediumTest
public class MenuJankTest extends LiveChannelsTestCase {
private static final String STARTING_CHANNEL = "1";
/**
- * The minimum number of frames expected during each jank test.
- * If there is less the test will fail. To be safe we loop the action in each test to create
- * twice this many frames under normal conditions.
+ * The minimum number of frames expected during each jank test. If there is less the test will
+ * fail. To be safe we loop the action in each test to create twice this many frames under
+ * normal conditions.
+ *
* <p>200 is chosen so there will be enough frame for the 90th, 95th, and 98th percentile
* measurements are significant.
*
* @see <a href="http://go/janktesthelper-best-practices">Jank Test Helper Best Practices</a>
*/
private static final int EXPECTED_FRAMES = 200;
+
protected MenuHelper mMenuHelper;
@Override
@@ -47,8 +46,7 @@ public class MenuJankTest extends LiveChannelsTestCase {
Utils.pressKeysForChannelNumber(STARTING_CHANNEL, mDevice);
}
- @JankTest(expectedFrames = EXPECTED_FRAMES,
- beforeTest = "fillTheMenuRowWithPreviousChannels")
+ @JankTest(expectedFrames = EXPECTED_FRAMES, beforeTest = "fillTheMenuRowWithPreviousChannels")
@GfxMonitor(processName = Utils.LIVE_CHANNELS_PROCESS_NAME)
public void testShowMenu() {
int frames = 40; // measured by hand.
diff --git a/tests/jank/src/com/android/tv/tests/jank/ProgramGuideJankTest.java b/tests/jank/src/com/android/tv/tests/jank/ProgramGuideJankTest.java
index d8860dd7..57d38ba9 100644
--- a/tests/jank/src/com/android/tv/tests/jank/ProgramGuideJankTest.java
+++ b/tests/jank/src/com/android/tv/tests/jank/ProgramGuideJankTest.java
@@ -21,23 +21,21 @@ import android.support.test.filters.MediumTest;
import android.support.test.jank.GfxMonitor;
import android.support.test.jank.JankTest;
import android.support.test.uiautomator.Until;
-
import com.android.tv.R;
import com.android.tv.testing.uihelper.ByResource;
import com.android.tv.testing.uihelper.Constants;
import com.android.tv.testing.uihelper.MenuHelper;
-/**
- * Jank tests for the program guide.
- */
+/** Jank tests for the program guide. */
@MediumTest
public class ProgramGuideJankTest extends LiveChannelsTestCase {
private static final String STARTING_CHANNEL = "13";
/**
- * The minimum number of frames expected during each jank test.
- * If there is less the test will fail. To be safe we loop the action in each test to create
- * twice this many frames under normal conditions.
+ * The minimum number of frames expected during each jank test. If there is less the test will
+ * fail. To be safe we loop the action in each test to create twice this many frames under
+ * normal conditions.
+ *
* <p>200 is chosen so there will be enough frame for the 90th, 95th, and 98th percentile
* measurements are significant.
*
@@ -54,8 +52,7 @@ public class ProgramGuideJankTest extends LiveChannelsTestCase {
Utils.pressKeysForChannelNumber(STARTING_CHANNEL, mDevice);
}
- @JankTest(expectedFrames = EXPECTED_FRAMES,
- beforeTest = "warmProgramGuide")
+ @JankTest(expectedFrames = EXPECTED_FRAMES, beforeTest = "warmProgramGuide")
@GfxMonitor(processName = Utils.LIVE_CHANNELS_PROCESS_NAME)
public void testShowClearProgramGuide() {
int frames = 53; // measured by hand
@@ -66,24 +63,28 @@ public class ProgramGuideJankTest extends LiveChannelsTestCase {
}
}
- @JankTest(expectedFrames = EXPECTED_FRAMES,
- beforeLoop = "showAndFocusProgramGuide",
- afterLoop = "clearProgramGuide")
+ @JankTest(
+ expectedFrames = EXPECTED_FRAMES,
+ beforeLoop = "showAndFocusProgramGuide",
+ afterLoop = "clearProgramGuide"
+ )
@GfxMonitor(processName = Utils.LIVE_CHANNELS_PROCESS_NAME)
public void testScrollDown() {
- int frames = 20; // measured by hand
+ int frames = 20; // measured by hand
int repeat = EXPECTED_FRAMES * 2 / frames;
for (int i = 0; i < repeat; i++) {
mDevice.pressDPadDown();
}
}
- @JankTest(expectedFrames = EXPECTED_FRAMES,
- beforeLoop = "showAndFocusProgramGuide",
- afterLoop = "clearProgramGuide")
+ @JankTest(
+ expectedFrames = EXPECTED_FRAMES,
+ beforeLoop = "showAndFocusProgramGuide",
+ afterLoop = "clearProgramGuide"
+ )
@GfxMonitor(processName = Utils.LIVE_CHANNELS_PROCESS_NAME)
public void testScrollRight() {
- int frames = 30; // measured by hand
+ int frames = 30; // measured by hand
int repeat = EXPECTED_FRAMES * 2 / frames;
for (int i = 0; i < repeat; i++) {
mDevice.pressDPadRight();
@@ -92,8 +93,8 @@ public class ProgramGuideJankTest extends LiveChannelsTestCase {
private void selectProgramGuideMenuItem() {
mMenuHelper.showMenu();
- mMenuHelper.assertNavigateToMenuItem(R.string.menu_title_channels,
- R.string.channels_item_program_guide);
+ mMenuHelper.assertNavigateToMenuItem(
+ R.string.menu_title_channels, R.string.channels_item_program_guide);
mDevice.waitForIdle();
}
diff --git a/tests/jank/src/com/android/tv/tests/jank/Utils.java b/tests/jank/src/com/android/tv/tests/jank/Utils.java
index cd1f7eff..4ad0f643 100644
--- a/tests/jank/src/com/android/tv/tests/jank/Utils.java
+++ b/tests/jank/src/com/android/tv/tests/jank/Utils.java
@@ -15,19 +15,16 @@
*/
package com.android.tv.tests.jank;
-import com.android.tv.testing.uihelper.UiDeviceUtils;
-
import android.support.test.uiautomator.UiDevice;
+import com.android.tv.testing.uihelper.UiDeviceUtils;
public final class Utils {
/** Live TV process name */
public static final String LIVE_CHANNELS_PROCESS_NAME = "com.android.tv";
- private Utils() { }
+ private Utils() {}
- /**
- * Presses channel number to tune to {@code channel}.
- */
+ /** Presses channel number to tune to {@code channel}. */
public static void pressKeysForChannelNumber(String channel, UiDevice uiDevice) {
UiDeviceUtils.pressKeys(uiDevice, channel);
uiDevice.pressDPadCenter();
diff --git a/tests/tunerscripts/measure-tuning-time.awk b/tests/tunerscripts/measure-tuning-time.awk
new file mode 100644
index 00000000..e7febcf1
--- /dev/null
+++ b/tests/tunerscripts/measure-tuning-time.awk
@@ -0,0 +1,32 @@
+# Awk script to measure tuning time statistics from logcat dump
+
+BEGIN {
+ n = 0;
+ sum = 0;
+}
+
+# Collect tuning time with "Video available in <time> ms" message
+/Video available in/ {
+ n++;
+ tune_time = $11;
+ sum += tune_time;
+ if (n == 1) {
+ min = tune_time;
+ max = tune_time;
+ } else {
+ if (tune_time < min) {
+ min = tune_time
+ }
+ if (tune_time > max) {
+ max = tune_time
+ }
+ }
+}
+
+END {
+ average = sum / n;
+ print "Average tune time", average, "ms";
+ print "Minimum tune time", min, "ms";
+ print "Maximum tune time", max, "ms";
+}
+
diff --git a/tests/tunerscripts/usbtuner-test.sh b/tests/tunerscripts/usbtuner-test.sh
new file mode 100755
index 00000000..b8c15841
--- /dev/null
+++ b/tests/tunerscripts/usbtuner-test.sh
@@ -0,0 +1,159 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 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.
+#
+# usage: usbtuner-test.sh <test_case> [channel]
+#
+# To test repeated channel change, run:
+#
+# ./usbtuner-test.sh <1 or 3>
+#
+# To test watching a fixed channel, run:
+#
+# ./usbtuner-test.sh 2
+#
+# Case 2 uses the last-viewed channel by TV app. Give a channel number
+# as a 2nd parameter if you want to use the channel for testing, like below:
+#
+# ./usbtuner-test.sh 2 6-1
+#
+# The script assumes that:
+# 1) Browsing by keydown event circulates among the USB input channels only
+# 2) When started, TV app should tune to one of the channels provided by the USB input
+#
+# The test result is logged in the doc: https://goo.gl/MsPBf7
+
+function start_tv {
+ disable_analytics_report
+ adb shell am force-stop com.android.tv
+ adb shell am start -n com.android.tv/.MainActivity > /dev/null
+ sleep 5
+}
+
+function log_begin {
+ adb shell dumpsys meminfo -d --package com.android.tv.tuner > meminfo-begin.txt
+}
+
+function tune {
+ adb shell input text $1
+ adb shell input keyevent KEYCODE_DPAD_CENTER
+ sleep 5 # Wait enough for tuning
+}
+
+function browse {
+ for i in {1..50}; do
+ adb shell input keyevent DPAD_DOWN
+ sleep 10 # Tune and watch the channel for a while
+ done;
+}
+
+function browse_heavily {
+ for i in {1..60}; do
+ echo "$(date '+%x %X') ======== Test #$i of 60 ========"
+ clear_logcat
+ for j in {1..60}; do
+ adb shell input keyevent DPAD_DOWN
+ sleep $(( $RANDOM % 3 )) # Sleep for 0 - 2 seconds
+ done;
+ measure_tuning_time
+ done;
+}
+
+function clear_logcat {
+ adb logcat -c
+}
+
+function measure_tuning_time {
+ timeout 1 adb logcat -s TvInputSessionImpl | awk -f $(dirname $0)/measure-tuning-time.awk
+}
+
+function log_end {
+ adb shell dumpsys meminfo -d --package com.android.tv.tuner > meminfo-end.txt
+}
+
+function stop_tv {
+ # Stop TV by running other app (Settings)
+ adb shell am start com.android.tv.settings/com.android.tv.settings.MainSettings
+ restore_analytics_setting
+}
+
+function output {
+ echo "Cut and paste this"
+ sed -n 33,46p meminfo-begin.txt | cut -f 2 -d ":" -s | awk '{print $1}'
+ sed -n 33,46p meminfo-end.txt | cut -f 2 -d ":" -s | awk '{print $1}'
+}
+
+function disable_analytics_report {
+ tracker=$(adb shell getprop tv_use_tracker | tr -d '[[:space:]]')
+ adb shell setprop tv_use_tracker false
+}
+
+function restore_analytics_setting {
+ if [ "${tracker}" == "" ]; then
+ adb shell setprop tv_use_tracker ""
+ else
+ adb shell setprop tv_use_tracker ${tracker}
+ fi
+}
+
+function control_c {
+ restore_analytics_setting
+ echo "Exiting..."
+ exit 1
+}
+
+# Entry point
+
+trap control_c SIGINT
+
+case "$1" in
+ 1)
+ echo "Runing test 1"
+ start_tv
+ log_begin
+ clear_logcat
+ browse # Repeat channel change for about 10 minutes
+ measure_tuning_time
+ log_end
+ stop_tv
+ output
+ ;;
+ 2)
+ echo "Runing test 2"
+ start_tv
+ log_begin
+ if [ "$2" != "" ]; then
+ tune $2
+ fi
+ sleep 600 # 10 minutes
+ log_end
+ stop_tv
+ output
+ ;;
+ 3)
+ echo "Runing test 3"
+ start_tv
+ log_begin
+ browse_heavily # Repeat channel change for about 3 hours
+ log_end
+ stop_tv
+ output
+ ;;
+ *)
+ echo "usage: usbtuner-test.sh <1|2|3> [channel]"
+ exit 1
+ ;;
+esac
+
diff --git a/tests/unit/Android.mk b/tests/unit/Android.mk
index 3632fe94..a425bcfe 100644
--- a/tests/unit/Android.mk
+++ b/tests/unit/Android.mk
@@ -12,6 +12,11 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
mockito-target \
tv-test-common \
+LOCAL_JAVA_LIBRARIES := \
+ android.test.runner.stubs \
+ android.test.base.stubs \
+ android.test.mock.stubs \
+
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../common/res
@@ -20,7 +25,7 @@ LOCAL_PACKAGE_NAME := TVUnitTests
LOCAL_INSTRUMENTATION_FOR := LiveTv
LOCAL_SDK_VERSION := system_current
-LOCAL_MIN_SDK_VERSION := 23 # M
+LOCAL_PROGUARD_ENABLED := disabled
include $(BUILD_PACKAGE)
diff --git a/tests/unit/AndroidManifest.xml b/tests/unit/AndroidManifest.xml
index d073f8ac..9134a1c1 100644
--- a/tests/unit/AndroidManifest.xml
+++ b/tests/unit/AndroidManifest.xml
@@ -18,7 +18,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.tv.tests" >
- <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="23" />
+ <uses-sdk android:targetSdkVersion="26" android:minSdkVersion="23" />
<instrumentation
android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/unit/src/com/android/tv/CurrentPositionMediatorTest.java b/tests/unit/src/com/android/tv/CurrentPositionMediatorTest.java
index f2917181..abadde31 100644
--- a/tests/unit/src/com/android/tv/CurrentPositionMediatorTest.java
+++ b/tests/unit/src/com/android/tv/CurrentPositionMediatorTest.java
@@ -18,16 +18,18 @@ package com.android.tv;
import static com.android.tv.TimeShiftManager.INVALID_TIME;
import static com.android.tv.TimeShiftManager.REQUEST_TIMEOUT_MS;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotSame;
+import static com.google.common.truth.Truth.assertWithMessage;
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.MediumTest;
-
+import android.support.test.runner.AndroidJUnit4;
+import com.android.tv.testing.activities.BaseMainActivityTestCase;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
@MediumTest
+@RunWith(AndroidJUnit4.class)
public class CurrentPositionMediatorTest extends BaseMainActivityTestCase {
private TimeShiftManager.CurrentPositionMediator mMediator;
@@ -51,8 +53,12 @@ public class CurrentPositionMediatorTest extends BaseMainActivityTestCase {
public void testOnSeekRequested() {
long seekToTimeMs = System.currentTimeMillis() - REQUEST_TIMEOUT_MS * 3;
mMediator.onSeekRequested(seekToTimeMs);
- assertNotSame("Seek request time", INVALID_TIME, mMediator.mSeekRequestTimeMs);
- assertEquals("Current position", seekToTimeMs, mMediator.mCurrentPositionMs);
+ assertWithMessage("Seek request time")
+ .that(mMediator.mSeekRequestTimeMs)
+ .isNotSameAs(INVALID_TIME);
+ assertWithMessage("Current position")
+ .that(mMediator.mCurrentPositionMs)
+ .isEqualTo(seekToTimeMs);
}
@UiThreadTest
@@ -62,9 +68,15 @@ public class CurrentPositionMediatorTest extends BaseMainActivityTestCase {
long newCurrentTimeMs = seekToTimeMs + REQUEST_TIMEOUT_MS;
mMediator.onSeekRequested(seekToTimeMs);
mMediator.onCurrentPositionChanged(newCurrentTimeMs);
- assertNotSame("Seek request time", INVALID_TIME, mMediator.mSeekRequestTimeMs);
- assertNotSame("Current position", seekToTimeMs, mMediator.mCurrentPositionMs);
- assertNotSame("Current position", newCurrentTimeMs, mMediator.mCurrentPositionMs);
+ assertWithMessage("Seek request time")
+ .that(mMediator.mSeekRequestTimeMs)
+ .isNotSameAs(INVALID_TIME);
+ assertWithMessage("Current position")
+ .that(mMediator.mCurrentPositionMs)
+ .isNotSameAs(seekToTimeMs);
+ assertWithMessage("Current position")
+ .that(mMediator.mCurrentPositionMs)
+ .isNotSameAs(newCurrentTimeMs);
}
@UiThreadTest
@@ -77,9 +89,13 @@ public class CurrentPositionMediatorTest extends BaseMainActivityTestCase {
assertCurrentPositionMediator(INVALID_TIME, newCurrentTimeMs);
}
- private void assertCurrentPositionMediator(long expectedSeekRequestTimeMs,
- long expectedCurrentPositionMs) {
- assertEquals("Seek request time", expectedSeekRequestTimeMs, mMediator.mSeekRequestTimeMs);
- assertEquals("Current position", expectedCurrentPositionMs, mMediator.mCurrentPositionMs);
+ private void assertCurrentPositionMediator(
+ long expectedSeekRequestTimeMs, long expectedCurrentPositionMs) {
+ assertWithMessage("Seek request time")
+ .that(mMediator.mSeekRequestTimeMs)
+ .isEqualTo(expectedSeekRequestTimeMs);
+ assertWithMessage("Current position")
+ .that(mMediator.mCurrentPositionMs)
+ .isEqualTo(expectedCurrentPositionMs);
}
}
diff --git a/tests/unit/src/com/android/tv/FeaturesTest.java b/tests/unit/src/com/android/tv/FeaturesTest.java
index 9d61e757..e19f4b7c 100644
--- a/tests/unit/src/com/android/tv/FeaturesTest.java
+++ b/tests/unit/src/com/android/tv/FeaturesTest.java
@@ -16,21 +16,21 @@
package com.android.tv;
-import static org.junit.Assert.assertFalse;
+import static com.google.common.truth.Truth.assertThat;
import android.support.test.filters.SmallTest;
-
+import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
+import org.junit.runner.RunWith;
-/**
- * Test for features.
- */
+/** Test for features. */
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class FeaturesTest {
@Test
public void testPropertyFeatureKeyLength() {
// This forces the class to be loaded and verifies all PropertyFeature key lengths.
// If any keys are too long the test will fail to load.
- assertFalse(Features.TEST_FEATURE.isEnabled(null));
+ assertThat(TvFeatures.TEST_FEATURE.isEnabled(null)).isFalse();
}
}
diff --git a/tests/unit/src/com/android/tv/MainActivityTest.java b/tests/unit/src/com/android/tv/MainActivityTest.java
index 15805032..c5df21a9 100644
--- a/tests/unit/src/com/android/tv/MainActivityTest.java
+++ b/tests/unit/src/com/android/tv/MainActivityTest.java
@@ -16,31 +16,30 @@
package com.android.tv;
import static android.support.test.InstrumentationRegistry.getInstrumentation;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
import android.view.View;
import android.widget.TextView;
-
-import com.android.tv.data.Channel;
+import com.android.tv.data.api.Channel;
+import com.android.tv.testing.activities.BaseMainActivityTestCase;
import com.android.tv.testing.testinput.TvTestInputConstants;
import com.android.tv.ui.ChannelBannerView;
-
-import org.junit.Test;
-
import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
-/**
- * Tests for {@link MainActivity}.
- */
+/** Tests for {@link MainActivity}. */
@MediumTest
+@RunWith(AndroidJUnit4.class)
public class MainActivityTest extends BaseMainActivityTestCase {
@Test
public void testInitialConditions() {
waitUntilChannelLoadingFinish();
List<Channel> channelList = mActivity.getChannelDataManager().getChannelList();
- assertTrue("Expected at least one channel", channelList.size() > 0);
+ assertWithMessage("Expected at least one channel").that(channelList.size() > 0).isTrue();
}
@Test
@@ -61,17 +60,19 @@ public class MainActivityTest extends BaseMainActivityTestCase {
private void showProgramGuide() {
// Run on UI thread so views can be modified
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mActivity.getOverlayManager().showProgramGuide();
- }
- });
+ getInstrumentation()
+ .runOnMainSync(
+ new Runnable() {
+ @Override
+ public void run() {
+ mActivity.getOverlayManager().showProgramGuide();
+ }
+ });
}
private void assertChannelName(String displayName) {
TextView channelNameView = (TextView) mActivity.findViewById(R.id.channel_name);
- assertEquals("Channel Name", displayName, channelNameView.getText());
+ assertWithMessage("Channel Name").that(channelNameView.getText()).isEqualTo(displayName);
}
private void assertProgramGuide(boolean isShown) {
@@ -83,12 +84,13 @@ public class MainActivityTest extends BaseMainActivityTestCase {
return (ChannelBannerView) v;
}
- private View assertExpectedBannerSceneClassShown(Class<ChannelBannerView> expectedClass,
- boolean expectedShown) {
- View v = assertViewIsShown(expectedClass.getSimpleName(), R.id.scene_transition_common,
- expectedShown);
+ private View assertExpectedBannerSceneClassShown(
+ Class<ChannelBannerView> expectedClass, boolean expectedShown) {
+ View v =
+ assertViewIsShown(
+ expectedClass.getSimpleName(), R.id.scene_transition_common, expectedShown);
if (v != null) {
- assertEquals(expectedClass, v.getClass());
+ assertThat(v.getClass()).isEqualTo(expectedClass);
}
return v;
}
@@ -102,7 +104,7 @@ public class MainActivityTest extends BaseMainActivityTestCase {
return null;
}
}
- assertEquals(viewName + " shown", expected, view.isShown());
+ assertWithMessage(viewName + " shown").that(view.isShown()).isEqualTo(expected);
return view;
}
}
diff --git a/tests/unit/src/com/android/tv/TimeShiftManagerTest.java b/tests/unit/src/com/android/tv/TimeShiftManagerTest.java
index 052b5d19..cb523045 100644
--- a/tests/unit/src/com/android/tv/TimeShiftManagerTest.java
+++ b/tests/unit/src/com/android/tv/TimeShiftManagerTest.java
@@ -22,14 +22,17 @@ import static com.android.tv.TimeShiftManager.TIME_SHIFT_ACTION_ID_JUMP_TO_PREVI
import static com.android.tv.TimeShiftManager.TIME_SHIFT_ACTION_ID_PAUSE;
import static com.android.tv.TimeShiftManager.TIME_SHIFT_ACTION_ID_PLAY;
import static com.android.tv.TimeShiftManager.TIME_SHIFT_ACTION_ID_REWIND;
-import static org.junit.Assert.assertEquals;
+import static com.google.common.truth.Truth.assertWithMessage;
import android.support.test.filters.MediumTest;
-
+import android.support.test.runner.AndroidJUnit4;
+import com.android.tv.testing.activities.BaseMainActivityTestCase;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
@MediumTest
+@RunWith(AndroidJUnit4.class)
public class TimeShiftManagerTest extends BaseMainActivityTestCase {
private TimeShiftManager mTimeShiftManager;
@@ -85,19 +88,30 @@ public class TimeShiftManagerTest extends BaseMainActivityTestCase {
mTimeShiftManager.enableAction(TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT, enabled);
}
- private void assertActionState(boolean playEnabled, boolean pauseEnabled, boolean rewindEnabled,
- boolean fastForwardEnabled, boolean jumpToPreviousEnabled, boolean jumpToNextEnabled) {
- assertEquals("Play Action", playEnabled,
- mTimeShiftManager.isActionEnabled(TIME_SHIFT_ACTION_ID_PLAY));
- assertEquals("Pause Action", pauseEnabled,
- mTimeShiftManager.isActionEnabled(TIME_SHIFT_ACTION_ID_PAUSE));
- assertEquals("Rewind Action", rewindEnabled,
- mTimeShiftManager.isActionEnabled(TIME_SHIFT_ACTION_ID_REWIND));
- assertEquals("Fast Forward Action", fastForwardEnabled,
- mTimeShiftManager.isActionEnabled(TIME_SHIFT_ACTION_ID_FAST_FORWARD));
- assertEquals("Jump To Previous Action", jumpToPreviousEnabled,
- mTimeShiftManager.isActionEnabled(TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS));
- assertEquals("Jump To Next Action", jumpToNextEnabled,
- mTimeShiftManager.isActionEnabled(TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT));
+ private void assertActionState(
+ boolean playEnabled,
+ boolean pauseEnabled,
+ boolean rewindEnabled,
+ boolean fastForwardEnabled,
+ boolean jumpToPreviousEnabled,
+ boolean jumpToNextEnabled) {
+ assertWithMessage("Play Action")
+ .that(mTimeShiftManager.isActionEnabled(TIME_SHIFT_ACTION_ID_PLAY))
+ .isEqualTo(playEnabled);
+ assertWithMessage("Pause Action")
+ .that(mTimeShiftManager.isActionEnabled(TIME_SHIFT_ACTION_ID_PAUSE))
+ .isEqualTo(pauseEnabled);
+ assertWithMessage("Rewind Action")
+ .that(mTimeShiftManager.isActionEnabled(TIME_SHIFT_ACTION_ID_REWIND))
+ .isEqualTo(rewindEnabled);
+ assertWithMessage("Fast Forward Action")
+ .that(mTimeShiftManager.isActionEnabled(TIME_SHIFT_ACTION_ID_FAST_FORWARD))
+ .isEqualTo(fastForwardEnabled);
+ assertWithMessage("Jump To Previous Action")
+ .that(mTimeShiftManager.isActionEnabled(TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS))
+ .isEqualTo(jumpToPreviousEnabled);
+ assertWithMessage("Jump To Next Action")
+ .that(mTimeShiftManager.isActionEnabled(TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT))
+ .isEqualTo(jumpToNextEnabled);
}
}
diff --git a/tests/unit/src/com/android/tv/data/ChannelDataManagerTest.java b/tests/unit/src/com/android/tv/data/ChannelDataManagerTest.java
index 7a4a4982..96c1f7a1 100644
--- a/tests/unit/src/com/android/tv/data/ChannelDataManagerTest.java
+++ b/tests/unit/src/com/android/tv/data/ChannelDataManagerTest.java
@@ -18,9 +18,8 @@ package com.android.tv.data;
import static android.support.test.InstrumentationRegistry.getInstrumentation;
import static android.support.test.InstrumentationRegistry.getTargetContext;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import android.content.ContentProvider;
import android.content.ContentUris;
@@ -31,7 +30,9 @@ import android.database.Cursor;
import android.media.tv.TvContract;
import android.media.tv.TvContract.Channels;
import android.net.Uri;
+import android.os.AsyncTask;
import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
import android.test.MoreAsserts;
import android.test.mock.MockContentProvider;
import android.test.mock.MockContentResolver;
@@ -39,30 +40,30 @@ import android.test.mock.MockCursor;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
-
-import com.android.tv.testing.ChannelInfo;
-import com.android.tv.testing.Constants;
+import com.android.tv.data.api.Channel;
+import com.android.tv.testing.constants.Constants;
+import com.android.tv.testing.data.ChannelInfo;
import com.android.tv.util.TvInputManagerHelper;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Matchers;
-import org.mockito.Mockito;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Matchers;
+import org.mockito.Mockito;
/**
* Test for {@link ChannelDataManager}
*
- * A test method may include tests for multiple methods to minimize the DB access.
- * Note that all the methods of {@link ChannelDataManager} should be called from the UI thread.
+ * <p>A test method may include tests for multiple methods to minimize the DB access. Note that all
+ * the methods of {@link ChannelDataManager} should be called from the UI thread.
*/
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class ChannelDataManagerTest {
private static final boolean DEBUG = false;
private static final String TAG = "ChannelDataManagerTest";
@@ -80,73 +81,89 @@ public class ChannelDataManagerTest {
@Before
public void setUp() {
- assertTrue("More than 2 channels to test", Constants.UNIT_TEST_CHANNEL_COUNT > 2);
+ assertWithMessage("More than 2 channels to test")
+ .that(Constants.UNIT_TEST_CHANNEL_COUNT > 2)
+ .isTrue();
mContentProvider = new FakeContentProvider(getTargetContext());
mContentResolver = new FakeContentResolver();
mContentResolver.addProvider(TvContract.AUTHORITY, mContentProvider);
mListener = new TestChannelDataManagerListener();
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- TvInputManagerHelper mockHelper = Mockito.mock(TvInputManagerHelper.class);
- Mockito.when(mockHelper.hasTvInputInfo(Matchers.anyString())).thenReturn(true);
- mChannelDataManager = new ChannelDataManager(getTargetContext(), mockHelper,
- mContentResolver);
- mChannelDataManager.addListener(mListener);
- }
- });
+ getInstrumentation()
+ .runOnMainSync(
+ new Runnable() {
+ @Override
+ public void run() {
+ TvInputManagerHelper mockHelper =
+ Mockito.mock(TvInputManagerHelper.class);
+ Mockito.when(mockHelper.hasTvInputInfo(Matchers.anyString()))
+ .thenReturn(true);
+ mChannelDataManager =
+ new ChannelDataManager(
+ getTargetContext(),
+ mockHelper,
+ AsyncTask.SERIAL_EXECUTOR,
+ mContentResolver);
+ mChannelDataManager.addListener(mListener);
+ }
+ });
}
@After
public void tearDown() {
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mChannelDataManager.stop();
- }
- });
+ getInstrumentation()
+ .runOnMainSync(
+ new Runnable() {
+ @Override
+ public void run() {
+ mChannelDataManager.stop();
+ }
+ });
}
private void startAndWaitForComplete() throws InterruptedException {
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mChannelDataManager.start();
- }
- });
- assertTrue(mListener.loadFinishedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS));
+ getInstrumentation()
+ .runOnMainSync(
+ new Runnable() {
+ @Override
+ public void run() {
+ mChannelDataManager.start();
+ }
+ });
+ assertThat(mListener.loadFinishedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS))
+ .isTrue();
}
private void restart() throws InterruptedException {
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mChannelDataManager.stop();
- mListener.reset();
- }
- });
+ getInstrumentation()
+ .runOnMainSync(
+ new Runnable() {
+ @Override
+ public void run() {
+ mChannelDataManager.stop();
+ mListener.reset();
+ }
+ });
startAndWaitForComplete();
}
@Test
public void testIsDbLoadFinished() throws InterruptedException {
startAndWaitForComplete();
- assertTrue(mChannelDataManager.isDbLoadFinished());
+ assertThat(mChannelDataManager.isDbLoadFinished()).isTrue();
}
/**
- * Test for following methods
- * - {@link ChannelDataManager#getChannelCount}
- * - {@link ChannelDataManager#getChannelList}
- * - {@link ChannelDataManager#getChannel}
+ * Test for following methods - {@link ChannelDataManager#getChannelCount} - {@link
+ * ChannelDataManager#getChannelList} - {@link ChannelDataManager#getChannel}
*/
@Test
public void testGetChannels() throws InterruptedException {
startAndWaitForComplete();
// Test {@link ChannelDataManager#getChannelCount}
- assertEquals(Constants.UNIT_TEST_CHANNEL_COUNT, mChannelDataManager.getChannelCount());
+ assertThat(mChannelDataManager.getChannelCount())
+ .isEqualTo(Constants.UNIT_TEST_CHANNEL_COUNT);
// Test {@link ChannelDataManager#getChannelList}
List<ChannelInfo> channelInfoList = new ArrayList<>();
@@ -157,36 +174,32 @@ public class ChannelDataManagerTest {
for (Channel channel : channelList) {
boolean found = false;
for (ChannelInfo channelInfo : channelInfoList) {
- if (TextUtils.equals(channelInfo.name, channel.getDisplayName())
- && TextUtils.equals(channelInfo.name, channel.getDisplayName())) {
+ if (TextUtils.equals(channelInfo.name, channel.getDisplayName())) {
found = true;
channelInfoList.remove(channelInfo);
break;
}
}
- assertTrue("Cannot find (" + channel + ")", found);
+ assertWithMessage("Cannot find (" + channel + ")").that(found).isTrue();
}
// Test {@link ChannelDataManager#getChannelIndex()}
for (Channel channel : channelList) {
- assertEquals(channel, mChannelDataManager.getChannel(channel.getId()));
+ assertThat(mChannelDataManager.getChannel(channel.getId())).isEqualTo(channel);
}
}
- /**
- * Test for {@link ChannelDataManager#getChannelCount} when no channel is available.
- */
+ /** Test for {@link ChannelDataManager#getChannelCount} when no channel is available. */
@Test
public void testGetChannels_noChannels() throws InterruptedException {
mContentProvider.clear();
startAndWaitForComplete();
- assertEquals(0, mChannelDataManager.getChannelCount());
+ assertThat(mChannelDataManager.getChannelCount()).isEqualTo(0);
}
/**
- * Test for following methods and channel listener with notifying change.
- * - {@link ChannelDataManager#updateBrowsable}
- * - {@link ChannelDataManager#applyUpdatedValuesToDb}
+ * Test for following methods and channel listener with notifying change. - {@link
+ * ChannelDataManager#updateBrowsable} - {@link ChannelDataManager#applyUpdatedValuesToDb}
*/
@Test
public void testBrowsable() throws InterruptedException {
@@ -197,9 +210,9 @@ public class ChannelDataManagerTest {
List<Channel> browsableChannelList = mChannelDataManager.getBrowsableChannelList();
for (Channel browsableChannel : browsableChannelList) {
boolean found = channelList.remove(browsableChannel);
- assertTrue("Cannot find (" + browsableChannel + ")", found);
+ assertWithMessage("Cannot find (" + browsableChannel + ")").that(found).isTrue();
}
- assertEquals(0, channelList.size());
+ assertThat(channelList).isEmpty();
// Prepare for next tests.
channelList = mChannelDataManager.getChannelList();
@@ -210,8 +223,8 @@ public class ChannelDataManagerTest {
// Test {@link ChannelDataManager#updateBrowsable} & notification.
mChannelDataManager.updateBrowsable(channel1.getId(), false, false);
- assertTrue(mListener.channelBrowsableChangedCalled);
- assertFalse(mChannelDataManager.getBrowsableChannelList().contains(channel1));
+ assertThat(mListener.channelBrowsableChangedCalled).isTrue();
+ assertThat(mChannelDataManager.getBrowsableChannelList()).doesNotContain(channel1);
MoreAsserts.assertContentsInAnyOrder(channelListener.updatedChannels, channel1);
channelListener.reset();
@@ -221,14 +234,13 @@ public class ChannelDataManagerTest {
mChannelDataManager.applyUpdatedValuesToDb();
restart();
browsableChannelList = mChannelDataManager.getBrowsableChannelList();
- assertEquals(Constants.UNIT_TEST_CHANNEL_COUNT - 1, browsableChannelList.size());
- assertFalse(browsableChannelList.contains(channel1));
+ assertThat(browsableChannelList).hasSize(Constants.UNIT_TEST_CHANNEL_COUNT - 1);
+ assertThat(browsableChannelList).doesNotContain(channel1);
}
/**
- * Test for following methods and channel listener without notifying change.
- * - {@link ChannelDataManager#updateBrowsable}
- * - {@link ChannelDataManager#applyUpdatedValuesToDb}
+ * Test for following methods and channel listener without notifying change. - {@link
+ * ChannelDataManager#updateBrowsable} - {@link ChannelDataManager#applyUpdatedValuesToDb}
*/
@Test
public void testBrowsable_skipNotification() throws InterruptedException {
@@ -247,10 +259,10 @@ public class ChannelDataManagerTest {
mChannelDataManager.updateBrowsable(channel1.getId(), false, true);
mChannelDataManager.updateBrowsable(channel2.getId(), false, true);
mChannelDataManager.updateBrowsable(channel1.getId(), true, true);
- assertFalse(mListener.channelBrowsableChangedCalled);
+ assertThat(mListener.channelBrowsableChangedCalled).isFalse();
List<Channel> browsableChannelList = mChannelDataManager.getBrowsableChannelList();
- assertTrue(browsableChannelList.contains(channel1));
- assertFalse(browsableChannelList.contains(channel2));
+ assertThat(browsableChannelList).contains(channel1);
+ assertThat(browsableChannelList).doesNotContain(channel2);
// Test {@link ChannelDataManager#applyUpdatedValuesToDb}
// Disable the update notification to avoid the unwanted call of "onLoadFinished".
@@ -258,14 +270,13 @@ public class ChannelDataManagerTest {
mChannelDataManager.applyUpdatedValuesToDb();
restart();
browsableChannelList = mChannelDataManager.getBrowsableChannelList();
- assertEquals(Constants.UNIT_TEST_CHANNEL_COUNT - 1, browsableChannelList.size());
- assertFalse(browsableChannelList.contains(channel2));
+ assertThat(browsableChannelList).hasSize(Constants.UNIT_TEST_CHANNEL_COUNT - 1);
+ assertThat(browsableChannelList).doesNotContain(channel2);
}
/**
- * Test for following methods and channel listener.
- * - {@link ChannelDataManager#updateLocked}
- * - {@link ChannelDataManager#applyUpdatedValuesToDb}
+ * Test for following methods and channel listener. - {@link ChannelDataManager#updateLocked} -
+ * {@link ChannelDataManager#applyUpdatedValuesToDb}
*/
@Test
public void testLocked() throws InterruptedException {
@@ -274,7 +285,7 @@ public class ChannelDataManagerTest {
// Test if all channels aren't locked at the first time.
List<Channel> channelList = mChannelDataManager.getChannelList();
for (Channel channel : channelList) {
- assertFalse(channel + " is locked", channel.isLocked());
+ assertWithMessage(channel + " is locked").that(channel.isLocked()).isFalse();
}
// Prepare for next tests.
@@ -282,22 +293,20 @@ public class ChannelDataManagerTest {
// Test {@link ChannelDataManager#updateLocked}
mChannelDataManager.updateLocked(channel.getId(), true);
- assertTrue(mChannelDataManager.getChannel(channel.getId()).isLocked());
+ assertThat(mChannelDataManager.getChannel(channel.getId()).isLocked()).isTrue();
// 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());
+ assertThat(mChannelDataManager.getChannel(channel.getId()).isLocked()).isTrue();
// Cleanup
mChannelDataManager.updateLocked(channel.getId(), false);
}
- /**
- * Test ChannelDataManager when channels in TvContract are updated, removed, or added.
- */
+ /** Test ChannelDataManager when channels in TvContract are updated, removed, or added. */
@Test
public void testChannelListChanged() throws InterruptedException {
startAndWaitForComplete();
@@ -308,9 +317,10 @@ public class ChannelDataManagerTest {
ChannelInfo testChannelInfo = ChannelInfo.create(getTargetContext(), (int) testChannelId);
testChannelId = Constants.UNIT_TEST_CHANNEL_COUNT + 1;
mContentProvider.simulateInsert(testChannelInfo);
- assertTrue(
- mListener.channelListUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS));
- assertEquals(Constants.UNIT_TEST_CHANNEL_COUNT + 1, mChannelDataManager.getChannelCount());
+ assertThat(mListener.channelListUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS))
+ .isTrue();
+ assertThat(mChannelDataManager.getChannelCount())
+ .isEqualTo(Constants.UNIT_TEST_CHANNEL_COUNT + 1);
// Test channel update
mListener.reset();
@@ -319,39 +329,45 @@ public class ChannelDataManagerTest {
mChannelDataManager.addChannelListener(testChannelId, channelListener);
String newName = testChannelInfo.name + "_test";
mContentProvider.simulateUpdate(testChannelId, newName);
- assertTrue(
- mListener.channelListUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS));
- assertTrue(
- channelListener.channelChangedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS));
- assertEquals(0, channelListener.removedChannels.size());
- assertEquals(1, channelListener.updatedChannels.size());
+ assertThat(mListener.channelListUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS))
+ .isTrue();
+ assertThat(
+ channelListener.channelChangedLatch.await(
+ WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS))
+ .isTrue();
+ assertThat(channelListener.removedChannels).isEmpty();
+ assertThat(channelListener.updatedChannels).hasSize(1);
Channel updatedChannel = channelListener.updatedChannels.get(0);
- assertEquals(testChannelId, updatedChannel.getId());
- assertEquals(testChannelInfo.number, updatedChannel.getDisplayNumber());
- assertEquals(newName, updatedChannel.getDisplayName());
- assertEquals(Constants.UNIT_TEST_CHANNEL_COUNT + 1,
- mChannelDataManager.getChannelCount());
+ assertThat(updatedChannel.getId()).isEqualTo(testChannelId);
+ assertThat(updatedChannel.getDisplayNumber()).isEqualTo(testChannelInfo.number);
+ assertThat(updatedChannel.getDisplayName()).isEqualTo(newName);
+ assertThat(mChannelDataManager.getChannelCount())
+ .isEqualTo(Constants.UNIT_TEST_CHANNEL_COUNT + 1);
// Test channel remove.
mListener.reset();
channelListener.reset();
mContentProvider.simulateDelete(testChannelId);
- assertTrue(
- mListener.channelListUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS));
- assertTrue(
- channelListener.channelChangedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS));
- assertEquals(1, channelListener.removedChannels.size());
- assertEquals(0, channelListener.updatedChannels.size());
+ assertThat(mListener.channelListUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS))
+ .isTrue();
+ assertThat(
+ channelListener.channelChangedLatch.await(
+ WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS))
+ .isTrue();
+ assertThat(channelListener.removedChannels).hasSize(1);
+ assertThat(channelListener.updatedChannels).isEmpty();
Channel removedChannel = channelListener.removedChannels.get(0);
- assertEquals(newName, removedChannel.getDisplayName());
- assertEquals(testChannelInfo.number, removedChannel.getDisplayNumber());
- assertEquals(Constants.UNIT_TEST_CHANNEL_COUNT, mChannelDataManager.getChannelCount());
+ assertThat(removedChannel.getDisplayName()).isEqualTo(newName);
+ assertThat(removedChannel.getDisplayNumber()).isEqualTo(testChannelInfo.number);
+ assertThat(mChannelDataManager.getChannelCount())
+ .isEqualTo(Constants.UNIT_TEST_CHANNEL_COUNT);
}
- private class ChannelInfoWrapper {
+ private static class ChannelInfoWrapper {
public ChannelInfo channelInfo;
public boolean browsable;
public boolean locked;
+
public ChannelInfoWrapper(ChannelInfo channelInfo) {
this.channelInfo = channelInfo;
browsable = true;
@@ -366,8 +382,14 @@ public class ChannelDataManagerTest {
public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
super.notifyChange(uri, observer, syncToNetwork);
if (DEBUG) {
- Log.d(TAG, "onChanged(uri=" + uri + ", observer=" + observer + ") - Notification "
- + (mNotifyDisabled ? "disabled" : "enabled"));
+ Log.d(
+ TAG,
+ "onChanged(uri="
+ + uri
+ + ", observer="
+ + observer
+ + ") - Notification "
+ + (mNotifyDisabled ? "disabled" : "enabled"));
}
if (mNotifyDisabled) {
return;
@@ -390,19 +412,23 @@ public class ChannelDataManagerTest {
public FakeContentProvider(Context context) {
super(context);
for (int i = 1; i <= Constants.UNIT_TEST_CHANNEL_COUNT; i++) {
- mChannelInfoList.put(i,
- new ChannelInfoWrapper(ChannelInfo.create(getTargetContext(), i)));
+ mChannelInfoList.put(
+ i, new ChannelInfoWrapper(ChannelInfo.create(getTargetContext(), i)));
}
}
/**
- * Implementation of {@link ContentProvider#query}.
- * This assumes that {@link ChannelDataManager} queries channels
- * with empty {@code selection}. (i.e. channels are always queries for all)
+ * Implementation of {@link ContentProvider#query}. This assumes that {@link
+ * ChannelDataManager} queries channels with empty {@code selection}. (i.e. channels are
+ * always queries for all)
*/
@Override
- public Cursor query(Uri uri, String[] projection, String selection, String[]
- selectionArgs, String sortOrder) {
+ public Cursor query(
+ Uri uri,
+ String[] projection,
+ String selection,
+ String[] selectionArgs,
+ String sortOrder) {
if (DEBUG) {
Log.d(TAG, "dump query");
Log.d(TAG, " uri=" + uri);
@@ -414,9 +440,8 @@ public class ChannelDataManagerTest {
}
/**
- * Implementation of {@link ContentProvider#update}.
- * This assumes that {@link ChannelDataManager} update channels
- * only for changing browsable and locked.
+ * Implementation of {@link ContentProvider#update}. This assumes that {@link
+ * ChannelDataManager} update channels only for changing browsable and locked.
*/
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
@@ -434,8 +459,9 @@ public class ChannelDataManagerTest {
}
} else {
// See {@link Utils#buildSelectionForIds} for the syntax.
- String selectionForId = selection.substring(
- selection.indexOf("(") + 1, selection.lastIndexOf(")"));
+ String selectionForId =
+ selection.substring(
+ selection.indexOf("(") + 1, selection.lastIndexOf(")"));
String[] ids = selectionForId.split(", ");
if (ids != null) {
for (String id : ids) {
@@ -476,27 +502,25 @@ public class ChannelDataManagerTest {
}
/**
- * Simulates channel data insert.
- * This assigns original network ID (the same with channel number) to channel ID.
+ * Simulates channel data insert. This assigns original network ID (the same with channel
+ * number) to channel ID.
*/
public void simulateInsert(ChannelInfo testChannelInfo) {
long channelId = testChannelInfo.originalNetworkId;
- mChannelInfoList.put((int) channelId, new ChannelInfoWrapper(
- ChannelInfo.create(getTargetContext(), (int) channelId)));
+ mChannelInfoList.put(
+ (int) channelId,
+ new ChannelInfoWrapper(
+ ChannelInfo.create(getTargetContext(), (int) channelId)));
mContentResolver.notifyChange(TvContract.buildChannelUri(channelId), null);
}
- /**
- * Simulates channel data delete.
- */
+ /** Simulates channel data delete. */
public void simulateDelete(long channelId) {
mChannelInfoList.remove((int) channelId);
mContentResolver.notifyChange(TvContract.buildChannelUri(channelId), null);
}
- /**
- * Simulates channel data update.
- */
+ /** Simulates channel data update. */
public void simulateUpdate(long channelId, String newName) {
ChannelInfoWrapper channel = mChannelInfoList.get((int) channelId);
ChannelInfo.Builder builder = new ChannelInfo.Builder(channel.channelInfo);
@@ -506,8 +530,9 @@ public class ChannelDataManagerTest {
}
private void assertChannelUri(Uri uri) {
- assertTrue("Uri(" + uri + ") isn't channel uri",
- uri.toString().startsWith(Channels.CONTENT_URI.toString()));
+ assertWithMessage("Uri(" + uri + ") isn't channel uri")
+ .that(uri.toString().startsWith(Channels.CONTENT_URI.toString()))
+ .isTrue();
}
public void clear() {
@@ -528,20 +553,21 @@ public class ChannelDataManagerTest {
}
private class FakeCursor extends MockCursor {
- private final String[] ALL_COLUMNS = {
- Channels._ID,
- Channels.COLUMN_DISPLAY_NAME,
- Channels.COLUMN_DISPLAY_NUMBER,
- Channels.COLUMN_INPUT_ID,
- Channels.COLUMN_VIDEO_FORMAT,
- Channels.COLUMN_ORIGINAL_NETWORK_ID,
- COLUMN_BROWSABLE,
- COLUMN_LOCKED};
+ private final String[] allColumns = {
+ Channels._ID,
+ Channels.COLUMN_DISPLAY_NAME,
+ Channels.COLUMN_DISPLAY_NUMBER,
+ Channels.COLUMN_INPUT_ID,
+ Channels.COLUMN_VIDEO_FORMAT,
+ Channels.COLUMN_ORIGINAL_NETWORK_ID,
+ COLUMN_BROWSABLE,
+ COLUMN_LOCKED
+ };
private final String[] mColumns;
private int mPosition;
public FakeCursor(String[] columns) {
- mColumns = (columns == null) ? ALL_COLUMNS : columns;
+ mColumns = (columns == null) ? allColumns : columns;
mPosition = -1;
}
@@ -566,6 +592,7 @@ public class ChannelDataManagerTest {
switch (columnName) {
case Channels._ID:
return mContentProvider.keyAt(mPosition);
+ default: // fall out
}
if (DEBUG) {
Log.d(TAG, "Column (" + columnName + ") is ignored in getLong()");
@@ -586,6 +613,7 @@ public class ChannelDataManagerTest {
return DUMMY_INPUT_ID;
case Channels.COLUMN_VIDEO_FORMAT:
return channel.channelInfo.getVideoFormat();
+ default: // fall out
}
if (DEBUG) {
Log.d(TAG, "Column (" + columnName + ") is ignored in getString()");
@@ -604,6 +632,7 @@ public class ChannelDataManagerTest {
return channel.browsable ? 1 : 0;
case COLUMN_LOCKED:
return channel.locked ? 1 : 0;
+ default: // fall out
}
if (DEBUG) {
Log.d(TAG, "Column (" + columnName + ") is ignored in getInt()");
@@ -627,7 +656,7 @@ public class ChannelDataManagerTest {
}
}
- private class TestChannelDataManagerListener implements ChannelDataManager.Listener {
+ private static class TestChannelDataManagerListener implements ChannelDataManager.Listener {
public CountDownLatch loadFinishedLatch = new CountDownLatch(1);
public CountDownLatch channelListUpdatedLatch = new CountDownLatch(1);
public boolean channelBrowsableChangedCalled;
@@ -654,7 +683,7 @@ public class ChannelDataManagerTest {
}
}
- private class TestChannelDataManagerChannelListener
+ private static class TestChannelDataManagerChannelListener
implements ChannelDataManager.ChannelListener {
public CountDownLatch channelChangedLatch = new CountDownLatch(1);
public final List<Channel> removedChannels = new ArrayList<>();
diff --git a/tests/unit/src/com/android/tv/data/ChannelImplTest.java b/tests/unit/src/com/android/tv/data/ChannelImplTest.java
new file mode 100644
index 00000000..b791a7e4
--- /dev/null
+++ b/tests/unit/src/com/android/tv/data/ChannelImplTest.java
@@ -0,0 +1,381 @@
+/*
+ * 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 static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import com.android.tv.data.api.Channel;
+import com.android.tv.testing.ComparatorTester;
+import com.android.tv.util.TvInputManagerHelper;
+import java.util.Comparator;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Matchers;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/** Tests for {@link ChannelImpl}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ChannelImplTest {
+ // Used for testing TV inputs with invalid input package. This could happen when a TV input is
+ // uninstalled while drawing an app link card.
+ private static final String INVALID_TV_INPUT_PACKAGE_NAME = "com.android.tv.invalid_tv_input";
+ // Used for testing TV inputs defined inside of Live TV.
+ private static final String LIVE_CHANNELS_PACKAGE_NAME = "com.android.tv";
+ // Used for testing a TV input which doesn't have its leanback launcher activity.
+ private static final String NONE_LEANBACK_TV_INPUT_PACKAGE_NAME =
+ "com.android.tv.none_leanback_tv_input";
+ // Used for testing a TV input which has its leanback launcher activity.
+ private static final String LEANBACK_TV_INPUT_PACKAGE_NAME = "com.android.tv.leanback_tv_input";
+ private static final String TEST_APP_LINK_TEXT = "test_app_link_text";
+ private static final String PARTNER_INPUT_ID = "partner";
+ private static final ActivityInfo TEST_ACTIVITY_INFO = new ActivityInfo();
+
+ private Context mMockContext;
+ private Intent mInvalidIntent;
+ private Intent mValidIntent;
+
+ @Before
+ public void setUp() throws NameNotFoundException {
+ mInvalidIntent = new Intent(Intent.ACTION_VIEW);
+ mInvalidIntent.setComponent(new ComponentName(INVALID_TV_INPUT_PACKAGE_NAME, ".test"));
+ mValidIntent = new Intent(Intent.ACTION_VIEW);
+ mValidIntent.setComponent(new ComponentName(LEANBACK_TV_INPUT_PACKAGE_NAME, ".test"));
+ Intent liveChannelsIntent = new Intent(Intent.ACTION_VIEW);
+ liveChannelsIntent.setComponent(
+ new ComponentName(LIVE_CHANNELS_PACKAGE_NAME, ".MainActivity"));
+ Intent leanbackTvInputIntent = new Intent(Intent.ACTION_VIEW);
+ leanbackTvInputIntent.setComponent(
+ new ComponentName(LEANBACK_TV_INPUT_PACKAGE_NAME, ".test"));
+
+ PackageManager mockPackageManager = Mockito.mock(PackageManager.class);
+ Mockito.when(
+ mockPackageManager.getLeanbackLaunchIntentForPackage(
+ INVALID_TV_INPUT_PACKAGE_NAME))
+ .thenReturn(null);
+ Mockito.when(
+ mockPackageManager.getLeanbackLaunchIntentForPackage(
+ LIVE_CHANNELS_PACKAGE_NAME))
+ .thenReturn(liveChannelsIntent);
+ Mockito.when(
+ mockPackageManager.getLeanbackLaunchIntentForPackage(
+ NONE_LEANBACK_TV_INPUT_PACKAGE_NAME))
+ .thenReturn(null);
+ Mockito.when(
+ mockPackageManager.getLeanbackLaunchIntentForPackage(
+ LEANBACK_TV_INPUT_PACKAGE_NAME))
+ .thenReturn(leanbackTvInputIntent);
+
+ // Channel.getAppLinkIntent() calls initAppLinkTypeAndIntent() which calls
+ // Intent.resolveActivityInfo() which calls PackageManager.getActivityInfo().
+ Mockito.doAnswer(
+ new Answer<ActivityInfo>() {
+ @Override
+ public ActivityInfo answer(InvocationOnMock invocation) {
+ // We only check the package name, since the class name can be
+ // changed
+ // when an intent is changed to an uri and created from the uri.
+ // (ex, ".className" -> "packageName.className")
+ return mValidIntent
+ .getComponent()
+ .getPackageName()
+ .equals(
+ ((ComponentName)
+ invocation
+ .getArguments()[0])
+ .getPackageName())
+ ? TEST_ACTIVITY_INFO
+ : null;
+ }
+ })
+ .when(mockPackageManager)
+ .getActivityInfo(Mockito.<ComponentName>any(), Mockito.anyInt());
+
+ mMockContext = Mockito.mock(Context.class);
+ Mockito.when(mMockContext.getApplicationContext()).thenReturn(mMockContext);
+ Mockito.when(mMockContext.getPackageName()).thenReturn(LIVE_CHANNELS_PACKAGE_NAME);
+ Mockito.when(mMockContext.getPackageManager()).thenReturn(mockPackageManager);
+ }
+
+ @Test
+ public void testGetAppLinkType_NoText_NoIntent() {
+ assertAppLinkType(Channel.APP_LINK_TYPE_NONE, INVALID_TV_INPUT_PACKAGE_NAME, null, null);
+ assertAppLinkType(Channel.APP_LINK_TYPE_NONE, LIVE_CHANNELS_PACKAGE_NAME, null, null);
+ assertAppLinkType(
+ Channel.APP_LINK_TYPE_NONE, NONE_LEANBACK_TV_INPUT_PACKAGE_NAME, null, null);
+ assertAppLinkType(Channel.APP_LINK_TYPE_APP, LEANBACK_TV_INPUT_PACKAGE_NAME, null, null);
+ }
+
+ @Test
+ public void testGetAppLinkType_NoText_InvalidIntent() {
+ assertAppLinkType(
+ Channel.APP_LINK_TYPE_NONE, INVALID_TV_INPUT_PACKAGE_NAME, null, mInvalidIntent);
+ assertAppLinkType(
+ Channel.APP_LINK_TYPE_NONE, LIVE_CHANNELS_PACKAGE_NAME, null, mInvalidIntent);
+ assertAppLinkType(
+ Channel.APP_LINK_TYPE_NONE,
+ NONE_LEANBACK_TV_INPUT_PACKAGE_NAME,
+ null,
+ mInvalidIntent);
+ assertAppLinkType(
+ Channel.APP_LINK_TYPE_APP, LEANBACK_TV_INPUT_PACKAGE_NAME, null, mInvalidIntent);
+ }
+
+ @Test
+ public void testGetAppLinkType_NoText_ValidIntent() {
+ assertAppLinkType(
+ Channel.APP_LINK_TYPE_NONE, INVALID_TV_INPUT_PACKAGE_NAME, null, mValidIntent);
+ assertAppLinkType(
+ Channel.APP_LINK_TYPE_NONE, LIVE_CHANNELS_PACKAGE_NAME, null, mValidIntent);
+ assertAppLinkType(
+ Channel.APP_LINK_TYPE_NONE,
+ NONE_LEANBACK_TV_INPUT_PACKAGE_NAME,
+ null,
+ mValidIntent);
+ assertAppLinkType(
+ Channel.APP_LINK_TYPE_APP, LEANBACK_TV_INPUT_PACKAGE_NAME, null, mValidIntent);
+ }
+
+ @Test
+ public void testGetAppLinkType_HasText_NoIntent() {
+ assertAppLinkType(
+ Channel.APP_LINK_TYPE_NONE,
+ INVALID_TV_INPUT_PACKAGE_NAME,
+ TEST_APP_LINK_TEXT,
+ null);
+ assertAppLinkType(
+ Channel.APP_LINK_TYPE_NONE, LIVE_CHANNELS_PACKAGE_NAME, TEST_APP_LINK_TEXT, null);
+ assertAppLinkType(
+ Channel.APP_LINK_TYPE_NONE,
+ NONE_LEANBACK_TV_INPUT_PACKAGE_NAME,
+ TEST_APP_LINK_TEXT,
+ null);
+ assertAppLinkType(
+ Channel.APP_LINK_TYPE_APP,
+ LEANBACK_TV_INPUT_PACKAGE_NAME,
+ TEST_APP_LINK_TEXT,
+ null);
+ }
+
+ @Test
+ public void testGetAppLinkType_HasText_InvalidIntent() {
+ assertAppLinkType(
+ Channel.APP_LINK_TYPE_NONE,
+ INVALID_TV_INPUT_PACKAGE_NAME,
+ TEST_APP_LINK_TEXT,
+ mInvalidIntent);
+ assertAppLinkType(
+ Channel.APP_LINK_TYPE_NONE,
+ LIVE_CHANNELS_PACKAGE_NAME,
+ TEST_APP_LINK_TEXT,
+ mInvalidIntent);
+ assertAppLinkType(
+ Channel.APP_LINK_TYPE_NONE,
+ NONE_LEANBACK_TV_INPUT_PACKAGE_NAME,
+ TEST_APP_LINK_TEXT,
+ mInvalidIntent);
+ assertAppLinkType(
+ Channel.APP_LINK_TYPE_APP,
+ LEANBACK_TV_INPUT_PACKAGE_NAME,
+ TEST_APP_LINK_TEXT,
+ mInvalidIntent);
+ }
+
+ @Test
+ public void testGetAppLinkType_HasText_ValidIntent() {
+ assertAppLinkType(
+ Channel.APP_LINK_TYPE_CHANNEL,
+ INVALID_TV_INPUT_PACKAGE_NAME,
+ TEST_APP_LINK_TEXT,
+ mValidIntent);
+ assertAppLinkType(
+ Channel.APP_LINK_TYPE_CHANNEL,
+ LIVE_CHANNELS_PACKAGE_NAME,
+ TEST_APP_LINK_TEXT,
+ mValidIntent);
+ assertAppLinkType(
+ Channel.APP_LINK_TYPE_CHANNEL,
+ NONE_LEANBACK_TV_INPUT_PACKAGE_NAME,
+ TEST_APP_LINK_TEXT,
+ mValidIntent);
+ assertAppLinkType(
+ Channel.APP_LINK_TYPE_CHANNEL,
+ LEANBACK_TV_INPUT_PACKAGE_NAME,
+ TEST_APP_LINK_TEXT,
+ mValidIntent);
+ }
+
+ private void assertAppLinkType(
+ int expectedType, String inputPackageName, String appLinkText, Intent appLinkIntent) {
+ // In ChannelImpl, Intent.URI_INTENT_SCHEME is used to parse the URI. So the same flag
+ // should be
+ // used when the URI is created.
+ ChannelImpl testChannel =
+ new ChannelImpl.Builder()
+ .setPackageName(inputPackageName)
+ .setAppLinkText(appLinkText)
+ .setAppLinkIntentUri(
+ appLinkIntent == null
+ ? null
+ : appLinkIntent.toUri(Intent.URI_INTENT_SCHEME))
+ .build();
+ assertWithMessage("Unexpected app-link type for for " + testChannel)
+ .that(testChannel.getAppLinkType(mMockContext))
+ .isEqualTo(expectedType);
+ }
+
+ @Test
+ public void testComparator() {
+ TvInputManagerHelper manager = Mockito.mock(TvInputManagerHelper.class);
+ Mockito.when(manager.isPartnerInput(Matchers.anyString()))
+ .thenAnswer(
+ new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ String inputId = (String) invocation.getArguments()[0];
+ return PARTNER_INPUT_ID.equals(inputId);
+ }
+ });
+ Comparator<Channel> comparator = new TestChannelComparator(manager);
+ ComparatorTester<Channel> comparatorTester = ComparatorTester.withoutEqualsTest(comparator);
+ comparatorTester.addComparableGroup(
+ new ChannelImpl.Builder().setInputId(PARTNER_INPUT_ID).build());
+ comparatorTester.addComparableGroup(new ChannelImpl.Builder().setInputId("1").build());
+ comparatorTester.addComparableGroup(
+ new ChannelImpl.Builder().setInputId("1").setDisplayNumber("2").build());
+ comparatorTester.addComparableGroup(
+ new ChannelImpl.Builder().setInputId("2").setDisplayNumber("1.0").build());
+
+ // display name does not affect comparator
+ comparatorTester.addComparableGroup(
+ new ChannelImpl.Builder()
+ .setInputId("2")
+ .setDisplayNumber("1.62")
+ .setDisplayName("test1")
+ .build(),
+ new ChannelImpl.Builder()
+ .setInputId("2")
+ .setDisplayNumber("1.62")
+ .setDisplayName("test2")
+ .build(),
+ new ChannelImpl.Builder()
+ .setInputId("2")
+ .setDisplayNumber("1.62")
+ .setDisplayName("test3")
+ .build());
+ comparatorTester.addComparableGroup(
+ new ChannelImpl.Builder().setInputId("2").setDisplayNumber("2.0").build());
+ // Numeric display number sorting
+ comparatorTester.addComparableGroup(
+ new ChannelImpl.Builder().setInputId("2").setDisplayNumber("12.2").build());
+ comparatorTester.test();
+ }
+
+ /**
+ * Test Input Label handled by {@link ChannelImpl.DefaultComparator}.
+ *
+ * <p>Sort partner inputs first, then sort by input label, then by input id. See <a
+ * href="http://b/23031603">b/23031603</a>.
+ */
+ @Test
+ public void testComparatorLabel() {
+ TvInputManagerHelper manager = Mockito.mock(TvInputManagerHelper.class);
+ Mockito.when(manager.isPartnerInput(Matchers.anyString()))
+ .thenAnswer(
+ new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ String inputId = (String) invocation.getArguments()[0];
+ return PARTNER_INPUT_ID.equals(inputId);
+ }
+ });
+ Comparator<Channel> comparator = new ChannelComparatorWithDescriptionAsLabel(manager);
+ ComparatorTester<Channel> comparatorTester = ComparatorTester.withoutEqualsTest(comparator);
+
+ comparatorTester.addComparableGroup(
+ new ChannelImpl.Builder().setInputId(PARTNER_INPUT_ID).setDescription("A").build());
+
+ // The description is used as a label for this test.
+ comparatorTester.addComparableGroup(
+ new ChannelImpl.Builder().setDescription("A").setInputId("1").build());
+ comparatorTester.addComparableGroup(
+ new ChannelImpl.Builder().setDescription("A").setInputId("2").build());
+ comparatorTester.addComparableGroup(
+ new ChannelImpl.Builder().setDescription("B").setInputId("1").build());
+
+ comparatorTester.test();
+ }
+
+ @Test
+ public void testNormalizeChannelNumber() {
+ assertNormalizedDisplayNumber(null, null);
+ assertNormalizedDisplayNumber("", "");
+ assertNormalizedDisplayNumber("1", "1");
+ assertNormalizedDisplayNumber("abcde", "abcde");
+ assertNormalizedDisplayNumber("1-1", "1-1");
+ assertNormalizedDisplayNumber("1.1", "1-1");
+ assertNormalizedDisplayNumber("1 1", "1-1");
+ assertNormalizedDisplayNumber("1\u058a1", "1-1");
+ assertNormalizedDisplayNumber("1\u05be1", "1-1");
+ assertNormalizedDisplayNumber("1\u14001", "1-1");
+ assertNormalizedDisplayNumber("1\u18061", "1-1");
+ assertNormalizedDisplayNumber("1\u20101", "1-1");
+ assertNormalizedDisplayNumber("1\u20111", "1-1");
+ assertNormalizedDisplayNumber("1\u20121", "1-1");
+ assertNormalizedDisplayNumber("1\u20131", "1-1");
+ assertNormalizedDisplayNumber("1\u20141", "1-1");
+ }
+
+ private void assertNormalizedDisplayNumber(String displayNumber, String normalized) {
+ assertThat(ChannelImpl.normalizeDisplayNumber(displayNumber)).isEqualTo(normalized);
+ }
+
+ private static final class TestChannelComparator extends ChannelImpl.DefaultComparator {
+ public TestChannelComparator(TvInputManagerHelper manager) {
+ super(null, manager);
+ }
+
+ @Override
+ public String getInputLabelForChannel(Channel channel) {
+ return channel.getInputId();
+ }
+ }
+
+ private static final class ChannelComparatorWithDescriptionAsLabel
+ extends ChannelImpl.DefaultComparator {
+ public ChannelComparatorWithDescriptionAsLabel(TvInputManagerHelper manager) {
+ super(null, manager);
+ }
+
+ @Override
+ public String getInputLabelForChannel(Channel channel) {
+ return channel.getDescription();
+ }
+ }
+}
diff --git a/tests/unit/src/com/android/tv/data/ChannelNumberTest.java b/tests/unit/src/com/android/tv/data/ChannelNumberTest.java
deleted file mode 100644
index 827dcdbd..00000000
--- a/tests/unit/src/com/android/tv/data/ChannelNumberTest.java
+++ /dev/null
@@ -1,97 +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.data;
-
-import static com.android.tv.data.ChannelNumber.parseChannelNumber;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
-import android.support.test.filters.SmallTest;
-
-import com.android.tv.testing.ComparableTester;
-
-import org.junit.Test;
-
-/**
- * Tests for {@link ChannelNumber}.
- */
-@SmallTest
-public class ChannelNumberTest {
- /**
- * Test method for {@link ChannelNumber#ChannelNumber()}.
- */
- @Test
- public void testChannelNumber() {
- assertChannelEquals(new ChannelNumber(), "", false, "");
- }
-
- /**
- * Test method for
- * {@link com.android.tv.data.ChannelNumber#parseChannelNumber(java.lang.String)}.
- */
- @Test
- public void testParseChannelNumber() {
- assertNull(parseChannelNumber(""));
- assertNull(parseChannelNumber("-"));
- assertNull(parseChannelNumber("abcd12"));
- assertNull(parseChannelNumber("12abcd"));
- assertNull(parseChannelNumber("-12"));
- assertChannelEquals(parseChannelNumber("1"), "1", false, "");
- assertChannelEquals(parseChannelNumber("1234-4321"), "1234", true, "4321");
- assertChannelEquals(parseChannelNumber("3-4"), "3", true, "4");
- assertChannelEquals(parseChannelNumber("5-6"), "5", true, "6");
- }
-
- /**
- * Test method for {@link ChannelNumber#compareTo(com.android.tv.data.ChannelNumber)}.
- */
- @Test
- public void testCompareTo() {
- new ComparableTester<ChannelNumber>()
- .addEquivalentGroup(parseChannelNumber("1"), parseChannelNumber("1"))
- .addEquivalentGroup(parseChannelNumber("2"))
- .addEquivalentGroup(parseChannelNumber("2-1"))
- .addEquivalentGroup(parseChannelNumber("2-2"))
- .addEquivalentGroup(parseChannelNumber("2-10"))
- .addEquivalentGroup(parseChannelNumber("3"))
- .addEquivalentGroup(parseChannelNumber("4"), parseChannelNumber("4-0"))
- .addEquivalentGroup(parseChannelNumber("10"))
- .addEquivalentGroup(parseChannelNumber("100"))
- .test();
- }
-
- /**
- * Test method for {@link ChannelNumber#compare(java.lang.String, java.lang.String)}.
- */
- @Test
- public void testCompare() {
- // Only need to test nulls, the reset is tested by testCompareTo
- assertEquals("compareTo(null,null)", 0, ChannelNumber.compare(null, null));
- assertEquals("compareTo(1,1)", 0, ChannelNumber.compare("1", "1"));
- assertEquals("compareTo(null,1)<0", true, ChannelNumber.compare(null, "1") < 0);
- assertEquals("compareTo(mal-formatted,1)<0", true, ChannelNumber.compare("abcd", "1") < 0);
- assertEquals("compareTo(mal-formatted,1)<0", true, ChannelNumber.compare(".4", "1") < 0);
- assertEquals("compareTo(1,null)>0", true, ChannelNumber.compare("1", null) > 0);
- }
-
- private void assertChannelEquals(ChannelNumber actual, String expectedMajor,
- boolean expectedHasDelimiter, String expectedMinor) {
- assertEquals(actual + " major", actual.majorNumber, expectedMajor);
- assertEquals(actual + " hasDelimiter", actual.hasDelimiter, expectedHasDelimiter);
- assertEquals(actual + " minor", actual.minorNumber, expectedMinor);
- }
-
-}
diff --git a/tests/unit/src/com/android/tv/data/ChannelTest.java b/tests/unit/src/com/android/tv/data/ChannelTest.java
deleted file mode 100644
index 69fcb858..00000000
--- a/tests/unit/src/com/android/tv/data/ChannelTest.java
+++ /dev/null
@@ -1,312 +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.data;
-
-import static org.junit.Assert.assertEquals;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.support.test.filters.SmallTest;
-
-import com.android.tv.testing.ComparatorTester;
-import com.android.tv.util.TvInputManagerHelper;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Matchers;
-import org.mockito.Mockito;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import java.util.Comparator;
-
-/**
- * Tests for {@link Channel}.
- */
-@SmallTest
-public class ChannelTest {
- // Used for testing TV inputs with invalid input package. This could happen when a TV input is
- // uninstalled while drawing an app link card.
- private static final String INVALID_TV_INPUT_PACKAGE_NAME =
- "com.android.tv.invalid_tv_input";
- // Used for testing TV inputs defined inside of Live TV.
- private static final String LIVE_CHANNELS_PACKAGE_NAME = "com.android.tv";
- // Used for testing a TV input which doesn't have its leanback launcher activity.
- private static final String NONE_LEANBACK_TV_INPUT_PACKAGE_NAME =
- "com.android.tv.none_leanback_tv_input";
- // Used for testing a TV input which has its leanback launcher activity.
- private static final String LEANBACK_TV_INPUT_PACKAGE_NAME =
- "com.android.tv.leanback_tv_input";
- private static final String TEST_APP_LINK_TEXT = "test_app_link_text";
- private static final String PARTNER_INPUT_ID = "partner";
- private static final ActivityInfo TEST_ACTIVITY_INFO = new ActivityInfo();
-
- private Context mMockContext;
- private Intent mInvalidIntent;
- private Intent mValidIntent;
-
- @Before
- public void setUp() throws NameNotFoundException {
- mInvalidIntent = new Intent(Intent.ACTION_VIEW);
- mInvalidIntent.setComponent(new ComponentName(INVALID_TV_INPUT_PACKAGE_NAME, ".test"));
- mValidIntent = new Intent(Intent.ACTION_VIEW);
- mValidIntent.setComponent(new ComponentName(LEANBACK_TV_INPUT_PACKAGE_NAME, ".test"));
- Intent liveChannelsIntent = new Intent(Intent.ACTION_VIEW);
- liveChannelsIntent.setComponent(
- new ComponentName(LIVE_CHANNELS_PACKAGE_NAME, ".MainActivity"));
- Intent leanbackTvInputIntent = new Intent(Intent.ACTION_VIEW);
- leanbackTvInputIntent.setComponent(
- new ComponentName(LEANBACK_TV_INPUT_PACKAGE_NAME, ".test"));
-
- PackageManager mockPackageManager = Mockito.mock(PackageManager.class);
- Mockito.when(mockPackageManager.getLeanbackLaunchIntentForPackage(
- INVALID_TV_INPUT_PACKAGE_NAME)).thenReturn(null);
- Mockito.when(mockPackageManager.getLeanbackLaunchIntentForPackage(
- LIVE_CHANNELS_PACKAGE_NAME)).thenReturn(liveChannelsIntent);
- Mockito.when(mockPackageManager.getLeanbackLaunchIntentForPackage(
- NONE_LEANBACK_TV_INPUT_PACKAGE_NAME)).thenReturn(null);
- Mockito.when(mockPackageManager.getLeanbackLaunchIntentForPackage(
- LEANBACK_TV_INPUT_PACKAGE_NAME)).thenReturn(leanbackTvInputIntent);
-
- // Channel.getAppLinkIntent() calls initAppLinkTypeAndIntent() which calls
- // Intent.resolveActivityInfo() which calls PackageManager.getActivityInfo().
- Mockito.doAnswer(new Answer<ActivityInfo>() {
- @Override
- public ActivityInfo answer(InvocationOnMock invocation) {
- // We only check the package name, since the class name can be changed
- // when an intent is changed to an uri and created from the uri.
- // (ex, ".className" -> "packageName.className")
- return mValidIntent.getComponent().getPackageName().equals(
- ((ComponentName)invocation.getArguments()[0]).getPackageName())
- ? TEST_ACTIVITY_INFO : null;
- }
- }).when(mockPackageManager).getActivityInfo(Mockito.<ComponentName>any(), Mockito.anyInt());
-
- mMockContext = Mockito.mock(Context.class);
- Mockito.when(mMockContext.getApplicationContext()).thenReturn(mMockContext);
- Mockito.when(mMockContext.getPackageName()).thenReturn(LIVE_CHANNELS_PACKAGE_NAME);
- Mockito.when(mMockContext.getPackageManager()).thenReturn(mockPackageManager);
- }
-
- @Test
- public void testGetAppLinkType_NoText_NoIntent() {
- assertAppLinkType(Channel.APP_LINK_TYPE_NONE, INVALID_TV_INPUT_PACKAGE_NAME, null, null);
- assertAppLinkType(Channel.APP_LINK_TYPE_NONE, LIVE_CHANNELS_PACKAGE_NAME, null, null);
- assertAppLinkType(Channel.APP_LINK_TYPE_NONE, NONE_LEANBACK_TV_INPUT_PACKAGE_NAME, null,
- null);
- assertAppLinkType(Channel.APP_LINK_TYPE_APP, LEANBACK_TV_INPUT_PACKAGE_NAME, null, null);
- }
-
- @Test
- public void testGetAppLinkType_NoText_InvalidIntent() {
- assertAppLinkType(Channel.APP_LINK_TYPE_NONE, INVALID_TV_INPUT_PACKAGE_NAME, null,
- mInvalidIntent);
- assertAppLinkType(Channel.APP_LINK_TYPE_NONE, LIVE_CHANNELS_PACKAGE_NAME, null,
- mInvalidIntent);
- assertAppLinkType(Channel.APP_LINK_TYPE_NONE, NONE_LEANBACK_TV_INPUT_PACKAGE_NAME, null,
- mInvalidIntent);
- assertAppLinkType(Channel.APP_LINK_TYPE_APP, LEANBACK_TV_INPUT_PACKAGE_NAME, null,
- mInvalidIntent);
- }
-
- @Test
- public void testGetAppLinkType_NoText_ValidIntent() {
- assertAppLinkType(Channel.APP_LINK_TYPE_NONE, INVALID_TV_INPUT_PACKAGE_NAME, null,
- mValidIntent);
- assertAppLinkType(Channel.APP_LINK_TYPE_NONE, LIVE_CHANNELS_PACKAGE_NAME, null,
- mValidIntent);
- assertAppLinkType(Channel.APP_LINK_TYPE_NONE, NONE_LEANBACK_TV_INPUT_PACKAGE_NAME, null,
- mValidIntent);
- assertAppLinkType(Channel.APP_LINK_TYPE_APP, LEANBACK_TV_INPUT_PACKAGE_NAME, null,
- mValidIntent);
- }
-
- @Test
- public void testGetAppLinkType_HasText_NoIntent() {
- assertAppLinkType(Channel.APP_LINK_TYPE_NONE, INVALID_TV_INPUT_PACKAGE_NAME,
- TEST_APP_LINK_TEXT, null);
- assertAppLinkType(Channel.APP_LINK_TYPE_NONE, LIVE_CHANNELS_PACKAGE_NAME,
- TEST_APP_LINK_TEXT, null);
- assertAppLinkType(Channel.APP_LINK_TYPE_NONE, NONE_LEANBACK_TV_INPUT_PACKAGE_NAME,
- TEST_APP_LINK_TEXT, null);
- assertAppLinkType(Channel.APP_LINK_TYPE_APP, LEANBACK_TV_INPUT_PACKAGE_NAME,
- TEST_APP_LINK_TEXT, null);
- }
-
- @Test
- public void testGetAppLinkType_HasText_InvalidIntent() {
- assertAppLinkType(Channel.APP_LINK_TYPE_NONE, INVALID_TV_INPUT_PACKAGE_NAME,
- TEST_APP_LINK_TEXT, mInvalidIntent);
- assertAppLinkType(Channel.APP_LINK_TYPE_NONE, LIVE_CHANNELS_PACKAGE_NAME,
- TEST_APP_LINK_TEXT, mInvalidIntent);
- assertAppLinkType(Channel.APP_LINK_TYPE_NONE, NONE_LEANBACK_TV_INPUT_PACKAGE_NAME,
- TEST_APP_LINK_TEXT, mInvalidIntent);
- assertAppLinkType(Channel.APP_LINK_TYPE_APP, LEANBACK_TV_INPUT_PACKAGE_NAME,
- TEST_APP_LINK_TEXT, mInvalidIntent);
- }
-
- @Test
- public void testGetAppLinkType_HasText_ValidIntent() {
- assertAppLinkType(Channel.APP_LINK_TYPE_CHANNEL, INVALID_TV_INPUT_PACKAGE_NAME,
- TEST_APP_LINK_TEXT, mValidIntent);
- assertAppLinkType(Channel.APP_LINK_TYPE_CHANNEL, LIVE_CHANNELS_PACKAGE_NAME,
- TEST_APP_LINK_TEXT, mValidIntent);
- assertAppLinkType(Channel.APP_LINK_TYPE_CHANNEL, NONE_LEANBACK_TV_INPUT_PACKAGE_NAME,
- TEST_APP_LINK_TEXT, mValidIntent);
- assertAppLinkType(Channel.APP_LINK_TYPE_CHANNEL, LEANBACK_TV_INPUT_PACKAGE_NAME,
- TEST_APP_LINK_TEXT, mValidIntent);
- }
-
- private void assertAppLinkType(int expectedType, String inputPackageName, String appLinkText,
- Intent appLinkIntent) {
- // In Channel, Intent.URI_INTENT_SCHEME is used to parse the URI. So the same flag should be
- // used when the URI is created.
- Channel testChannel = new Channel.Builder()
- .setPackageName(inputPackageName)
- .setAppLinkText(appLinkText)
- .setAppLinkIntentUri(appLinkIntent == null ? null : appLinkIntent.toUri(
- Intent.URI_INTENT_SCHEME))
- .build();
- assertEquals("Unexpected app-link type for for " + testChannel,
- expectedType, testChannel.getAppLinkType(mMockContext));
- }
-
- @Test
- public void testComparator() {
-
- TvInputManagerHelper manager = Mockito.mock(TvInputManagerHelper.class);
- Mockito.when(manager.isPartnerInput(Matchers.anyString())).thenAnswer(
- new Answer<Boolean>() {
- @Override
- public Boolean answer(InvocationOnMock invocation) throws Throwable {
- String inputId = (String) invocation.getArguments()[0];
- return PARTNER_INPUT_ID.equals(inputId);
- }
- });
- Comparator<Channel> comparator = new TestChannelComparator(manager);
- ComparatorTester<Channel> comparatorTester =
- ComparatorTester.withoutEqualsTest(comparator);
- comparatorTester.addComparableGroup(
- new Channel.Builder().setInputId(PARTNER_INPUT_ID).build());
- comparatorTester.addComparableGroup(
- new Channel.Builder().setInputId("1").build());
- comparatorTester.addComparableGroup(
- new Channel.Builder().setInputId("1").setDisplayNumber("2").build());
- comparatorTester.addComparableGroup(
- new Channel.Builder().setInputId("2").setDisplayNumber("1.0").build());
-
- // display name does not affect comparator
- comparatorTester.addComparableGroup(
- new Channel.Builder().setInputId("2").setDisplayNumber("1.62")
- .setDisplayName("test1").build(),
- new Channel.Builder().setInputId("2").setDisplayNumber("1.62")
- .setDisplayName("test2").build(),
- new Channel.Builder().setInputId("2").setDisplayNumber("1.62")
- .setDisplayName("test3").build());
- comparatorTester.addComparableGroup(
- new Channel.Builder().setInputId("2").setDisplayNumber("2.0").build());
- // Numeric display number sorting
- comparatorTester.addComparableGroup(
- new Channel.Builder().setInputId("2").setDisplayNumber("12.2").build());
- comparatorTester.test();
- }
-
- /**
- * Test Input Label handled by {@link com.android.tv.data.Channel.DefaultComparator}.
- *
- * <p>Sort partner inputs first, then sort by input label, then by input id.
- * See <a href="http://b/23031603">b/23031603</a>.
- */
- @Test
- public void testComparatorLabel() {
- TvInputManagerHelper manager = Mockito.mock(TvInputManagerHelper.class);
- Mockito.when(manager.isPartnerInput(Matchers.anyString())).thenAnswer(
- new Answer<Boolean>() {
- @Override
- public Boolean answer(InvocationOnMock invocation) throws Throwable {
- String inputId = (String) invocation.getArguments()[0];
- return PARTNER_INPUT_ID.equals(inputId);
- }
- });
- Comparator<Channel> comparator = new ChannelComparatorWithDescriptionAsLabel(manager);
- ComparatorTester<Channel> comparatorTester =
- ComparatorTester.withoutEqualsTest(comparator);
-
- comparatorTester.addComparableGroup(
- new Channel.Builder().setInputId(PARTNER_INPUT_ID).setDescription("A").build());
-
- // The description is used as a label for this test.
- comparatorTester.addComparableGroup(
- new Channel.Builder().setDescription("A").setInputId("1").build());
- comparatorTester.addComparableGroup(
- new Channel.Builder().setDescription("A").setInputId("2").build());
- comparatorTester.addComparableGroup(
- new Channel.Builder().setDescription("B").setInputId("1").build());
-
- comparatorTester.test();
- }
-
- @Test
- public void testNormalizeChannelNumber() {
- assertNormalizedDisplayNumber(null, null);
- assertNormalizedDisplayNumber("", "");
- assertNormalizedDisplayNumber("1", "1");
- assertNormalizedDisplayNumber("abcde", "abcde");
- assertNormalizedDisplayNumber("1-1", "1-1");
- assertNormalizedDisplayNumber("1.1", "1-1");
- assertNormalizedDisplayNumber("1 1", "1-1");
- assertNormalizedDisplayNumber("1\u058a1", "1-1");
- assertNormalizedDisplayNumber("1\u05be1", "1-1");
- assertNormalizedDisplayNumber("1\u14001", "1-1");
- assertNormalizedDisplayNumber("1\u18061", "1-1");
- assertNormalizedDisplayNumber("1\u20101", "1-1");
- assertNormalizedDisplayNumber("1\u20111", "1-1");
- assertNormalizedDisplayNumber("1\u20121", "1-1");
- assertNormalizedDisplayNumber("1\u20131", "1-1");
- assertNormalizedDisplayNumber("1\u20141", "1-1");
- }
-
- private void assertNormalizedDisplayNumber(String displayNumber, String normalized) {
- assertEquals(normalized, Channel.normalizeDisplayNumber(displayNumber));
- }
-
- private class TestChannelComparator extends Channel.DefaultComparator {
- public TestChannelComparator(TvInputManagerHelper manager) {
- super(null, manager);
- }
-
- @Override
- public String getInputLabelForChannel(Channel channel) {
- return channel.getInputId();
- }
- }
-
- private static class ChannelComparatorWithDescriptionAsLabel extends Channel.DefaultComparator {
- public ChannelComparatorWithDescriptionAsLabel(TvInputManagerHelper manager) {
- super(null, manager);
- }
-
- @Override
- public String getInputLabelForChannel(Channel channel) {
- return channel.getDescription();
- }
- }
-}
diff --git a/tests/unit/src/com/android/tv/data/GenreItemTest.java b/tests/unit/src/com/android/tv/data/GenreItemTest.java
deleted file mode 100644
index fdbcb599..00000000
--- a/tests/unit/src/com/android/tv/data/GenreItemTest.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.data;
-
-import static android.support.test.InstrumentationRegistry.getTargetContext;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import android.media.tv.TvContract.Programs.Genres;
-import android.os.Build;
-import android.support.test.filters.SmallTest;
-
-import org.junit.Test;
-
-/**
- * Tests for {@link Channel}.
- */
-@SmallTest
-public class GenreItemTest {
- private static final String INVALID_GENRE = "INVALID GENRE";
-
- @Test
- public void testGetLabels() {
- // Checks if no exception is thrown.
- GenreItems.getLabels(getTargetContext());
- }
-
- @Test
- 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));
- }
- }
-
- @Test
- 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);
- }
-
- @Test
- 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/ProgramDataManagerTest.java b/tests/unit/src/com/android/tv/data/ProgramDataManagerTest.java
deleted file mode 100644
index 5457051f..00000000
--- a/tests/unit/src/com/android/tv/data/ProgramDataManagerTest.java
+++ /dev/null
@@ -1,542 +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.data;
-
-import static android.support.test.InstrumentationRegistry.getTargetContext;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import android.content.Context;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.media.tv.TvContract;
-import android.net.Uri;
-import android.os.HandlerThread;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.test.mock.MockContentProvider;
-import android.test.mock.MockContentResolver;
-import android.test.mock.MockCursor;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.tv.testing.Constants;
-import com.android.tv.testing.FakeClock;
-import com.android.tv.testing.ProgramInfo;
-import com.android.tv.util.Utils;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Test for {@link com.android.tv.data.ProgramDataManager}
- */
-@SmallTest
-public class ProgramDataManagerTest {
- private static final boolean DEBUG = false;
- private static final String TAG = "ProgramDataManagerTest";
-
- // Wait time for expected success.
- private static final long WAIT_TIME_OUT_MS = 1000L;
- // Wait time for expected failure.
- private static final long FAILURE_TIME_OUT_MS = 300L;
-
- // TODO: Use TvContract constants, once they become public.
- private static final String PARAM_CHANNEL = "channel";
- private static final String PARAM_START_TIME = "start_time";
- private static final String PARAM_END_TIME = "end_time";
-
- private ProgramDataManager mProgramDataManager;
- private FakeClock mClock;
- private HandlerThread mHandlerThread;
- private TestProgramDataManagerListener mListener;
- private FakeContentResolver mContentResolver;
- private FakeContentProvider mContentProvider;
-
- @Before
- public void setUp() {
- mClock = FakeClock.createWithCurrentTime();
- mListener = new TestProgramDataManagerListener();
- mContentProvider = new FakeContentProvider(getTargetContext());
- mContentResolver = new FakeContentResolver();
- mContentResolver.addProvider(TvContract.AUTHORITY, mContentProvider);
- mHandlerThread = new HandlerThread(TAG);
- mHandlerThread.start();
- mProgramDataManager = new ProgramDataManager(
- mContentResolver, mClock, mHandlerThread.getLooper());
- mProgramDataManager.setPrefetchEnabled(true);
- mProgramDataManager.addListener(mListener);
- }
-
- @After
- public void tearDown() {
- mHandlerThread.quitSafely();
- mProgramDataManager.stop();
- }
-
- private void startAndWaitForComplete() throws InterruptedException {
- mProgramDataManager.start();
- assertTrue(mListener.programUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS));
- }
-
- /**
- * Test for {@link ProgramInfo#getIndex} and {@link ProgramInfo#getStartTimeMs}.
- */
- @Test
- public void testProgramUtils() {
- ProgramInfo stub = ProgramInfo.create();
- for (long channelId = 1; channelId < Constants.UNIT_TEST_CHANNEL_COUNT; channelId++) {
- int index = stub.getIndex(mClock.currentTimeMillis(), channelId);
- long startTimeMs = stub.getStartTimeMs(index, channelId);
- ProgramInfo programAt = stub.build(InstrumentationRegistry.getContext(), index);
- assertTrue(startTimeMs <= mClock.currentTimeMillis());
- assertTrue(mClock.currentTimeMillis() < startTimeMs + programAt.durationMs);
- }
- }
-
- /**
- * Test for following methods.
- *
- * <p>
- * {@link ProgramDataManager#getCurrentProgram(long)},
- * {@link ProgramDataManager#getPrograms(long, long)},
- * {@link ProgramDataManager#setPrefetchTimeRange(long)}.
- * </p>
- */
- @Test
- public void testGetPrograms() throws InterruptedException {
- // Initial setup to test {@link ProgramDataManager#setPrefetchTimeRange(long)}.
- long preventSnapDelayMs = ProgramDataManager.PROGRAM_GUIDE_SNAP_TIME_MS * 2;
- long prefetchTimeRangeStartMs = System.currentTimeMillis() + preventSnapDelayMs;
- mClock.setCurrentTimeMillis(prefetchTimeRangeStartMs + preventSnapDelayMs);
- mProgramDataManager.setPrefetchTimeRange(prefetchTimeRangeStartMs);
-
- startAndWaitForComplete();
-
- for (long channelId = 1; channelId <= Constants.UNIT_TEST_CHANNEL_COUNT; channelId++) {
- Program currentProgram = mProgramDataManager.getCurrentProgram(channelId);
- // Test {@link ProgramDataManager#getCurrentProgram(long)}.
- assertTrue(currentProgram.getStartTimeUtcMillis() <= mClock.currentTimeMillis()
- && mClock.currentTimeMillis() <= currentProgram.getEndTimeUtcMillis());
-
- // Test {@link ProgramDataManager#getPrograms(long)}.
- // Case #1: Normal case
- List<Program> programs =
- mProgramDataManager.getPrograms(channelId, mClock.currentTimeMillis());
- ProgramInfo stub = ProgramInfo.create();
- int index = stub.getIndex(mClock.currentTimeMillis(), channelId);
- for (Program program : programs) {
- ProgramInfo programInfoAt = stub.build(InstrumentationRegistry.getContext(), index);
- long startTimeMs = stub.getStartTimeMs(index, channelId);
- assertProgramEquals(startTimeMs, programInfoAt, program);
- index++;
- }
- // Case #2: Corner cases where there's a program that starts at the start of the range.
- long startTimeMs = programs.get(0).getStartTimeUtcMillis();
- programs = mProgramDataManager.getPrograms(channelId, startTimeMs);
- assertEquals(startTimeMs, programs.get(0).getStartTimeUtcMillis());
-
- // Test {@link ProgramDataManager#setPrefetchTimeRange(long)}.
- programs = mProgramDataManager.getPrograms(channelId,
- prefetchTimeRangeStartMs - TimeUnit.HOURS.toMillis(1));
- for (Program program : programs) {
- assertTrue(program.getEndTimeUtcMillis() >= prefetchTimeRangeStartMs);
- }
- }
- }
-
- /**
- * Test for following methods.
- *
- * <p>
- * {@link ProgramDataManager#addOnCurrentProgramUpdatedListener},
- * {@link ProgramDataManager#removeOnCurrentProgramUpdatedListener}.
- * </p>
- */
- @Test
- public void testCurrentProgramListener() throws InterruptedException {
- final long testChannelId = 1;
- ProgramInfo stub = ProgramInfo.create();
- int index = stub.getIndex(mClock.currentTimeMillis(), testChannelId);
- // Set current time to few seconds before the current program ends,
- // so we can see if callback is called as expected.
- long nextProgramStartTimeMs = stub.getStartTimeMs(index + 1, testChannelId);
- ProgramInfo nextProgramInfo = stub.build(InstrumentationRegistry.getContext(), index + 1);
- mClock.setCurrentTimeMillis(nextProgramStartTimeMs - (WAIT_TIME_OUT_MS / 2));
-
- startAndWaitForComplete();
- // Note that changing current time doesn't affect the current program
- // because current program is updated after waiting for the program's duration.
- // See {@link ProgramDataManager#updateCurrentProgram}.
- mClock.setCurrentTimeMillis(mClock.currentTimeMillis() + WAIT_TIME_OUT_MS);
- TestProgramDataManagerOnCurrentProgramUpdatedListener listener =
- new TestProgramDataManagerOnCurrentProgramUpdatedListener();
- mProgramDataManager.addOnCurrentProgramUpdatedListener(testChannelId, listener);
- assertTrue(
- listener.currentProgramUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS));
- assertEquals(testChannelId, listener.updatedChannelId);
- Program currentProgram = mProgramDataManager.getCurrentProgram(testChannelId);
- assertProgramEquals(nextProgramStartTimeMs, nextProgramInfo, currentProgram);
- assertEquals(listener.updatedProgram, currentProgram);
- }
-
- /**
- * Test if program data is refreshed after the program insertion.
- */
- @Test
- public void testContentProviderUpdate() throws InterruptedException {
- final long testChannelId = 1;
- startAndWaitForComplete();
- // Force program data manager to update program data whenever it's changes.
- mProgramDataManager.setProgramPrefetchUpdateWait(0);
- mListener.reset();
- List<Program> programList =
- mProgramDataManager.getPrograms(testChannelId, mClock.currentTimeMillis());
- assertNotNull(programList);
- long lastProgramEndTime = programList.get(programList.size() - 1).getEndTimeUtcMillis();
- // Make change in content provider
- mContentProvider.simulateAppend(testChannelId);
- assertTrue(mListener.programUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS));
- programList = mProgramDataManager.getPrograms(testChannelId, mClock.currentTimeMillis());
- assertTrue(
- lastProgramEndTime < programList.get(programList.size() - 1).getEndTimeUtcMillis());
- }
-
- /**
- * Test for {@link ProgramDataManager#setPauseProgramUpdate(boolean)}.
- */
- @Test
- public void testSetPauseProgramUpdate() throws InterruptedException {
- final long testChannelId = 1;
- startAndWaitForComplete();
- // Force program data manager to update program data whenever it's changes.
- mProgramDataManager.setProgramPrefetchUpdateWait(0);
- mListener.reset();
- mProgramDataManager.setPauseProgramUpdate(true);
- mContentProvider.simulateAppend(testChannelId);
- assertFalse(mListener.programUpdatedLatch.await(FAILURE_TIME_OUT_MS,
- TimeUnit.MILLISECONDS));
- }
-
- public static void assertProgramEquals(long expectedStartTime, ProgramInfo expectedInfo,
- Program actualProgram) {
- assertEquals("title", expectedInfo.title, actualProgram.getTitle());
- assertEquals("episode", expectedInfo.episode, actualProgram.getEpisodeTitle());
- assertEquals("description", expectedInfo.description, actualProgram.getDescription());
- assertEquals("startTime", expectedStartTime, actualProgram.getStartTimeUtcMillis());
- assertEquals("endTime", expectedStartTime + expectedInfo.durationMs,
- actualProgram.getEndTimeUtcMillis());
- }
-
- private class FakeContentResolver extends MockContentResolver {
- @Override
- public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
- super.notifyChange(uri, observer, syncToNetwork);
- if (DEBUG) {
- Log.d(TAG, "onChanged(uri=" + uri + ")");
- }
- if (observer != null) {
- observer.dispatchChange(false, uri);
- } else {
- mProgramDataManager.getContentObserver().dispatchChange(false, uri);
- }
- }
- }
-
- private static class ProgramInfoWrapper {
- private final int index;
- private final long startTimeMs;
- private final ProgramInfo programInfo;
-
- public ProgramInfoWrapper(int index, long startTimeMs, ProgramInfo programInfo) {
- this.index = index;
- this.startTimeMs = startTimeMs;
- this.programInfo = programInfo;
- }
- }
-
- // This implements the minimal methods in content resolver
- // and detailed assumptions are written in each method.
- private class FakeContentProvider extends MockContentProvider {
- private final SparseArray<List<ProgramInfoWrapper>> mProgramInfoList = new SparseArray<>();
-
- /**
- * Constructor for FakeContentProvider
- * <p>
- * This initializes program info assuming that
- * channel IDs are 1, 2, 3, ... {@link Constants#UNIT_TEST_CHANNEL_COUNT}.
- * </p>
- */
- public FakeContentProvider(Context context) {
- super(context);
- long startTimeMs = Utils.floorTime(
- mClock.currentTimeMillis() - ProgramDataManager.PROGRAM_GUIDE_SNAP_TIME_MS,
- ProgramDataManager.PROGRAM_GUIDE_SNAP_TIME_MS);
- long endTimeMs = startTimeMs + (ProgramDataManager.PROGRAM_GUIDE_MAX_TIME_RANGE / 2);
- for (int i = 1; i <= Constants.UNIT_TEST_CHANNEL_COUNT; i++) {
- List<ProgramInfoWrapper> programInfoList = new ArrayList<>();
- ProgramInfo stub = ProgramInfo.create();
- int index = stub.getIndex(startTimeMs, i);
- long programStartTimeMs = stub.getStartTimeMs(index, i);
- while (programStartTimeMs < endTimeMs) {
- ProgramInfo programAt = stub.build(InstrumentationRegistry.getContext(), index);
- programInfoList.add(
- new ProgramInfoWrapper(index, programStartTimeMs, programAt));
- index++;
- programStartTimeMs += programAt.durationMs;
- }
- mProgramInfoList.put(i, programInfoList);
- }
- }
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection,
- String[] selectionArgs, String sortOrder) {
- if (DEBUG) {
- Log.d(TAG, "dump query");
- Log.d(TAG, " uri=" + uri);
- Log.d(TAG, " projection=" + Arrays.toString(projection));
- Log.d(TAG, " selection=" + selection);
- }
- long startTimeMs = Long.parseLong(uri.getQueryParameter(PARAM_START_TIME));
- long endTimeMs = Long.parseLong(uri.getQueryParameter(PARAM_END_TIME));
- if (startTimeMs == 0 || endTimeMs == 0) {
- throw new UnsupportedOperationException();
- }
- assertProgramUri(uri);
- long channelId;
- try {
- channelId = Long.parseLong(uri.getQueryParameter(PARAM_CHANNEL));
- } catch (NumberFormatException e) {
- channelId = -1;
- }
- return new FakeCursor(projection, channelId, startTimeMs, endTimeMs);
- }
-
- /**
- * Simulate program data appends at the end of the existing programs.
- * This appends programs until the maximum program query range
- * ({@link ProgramDataManager#PROGRAM_GUIDE_MAX_TIME_RANGE})
- * where we started with the inserting half of it.
- */
- public void simulateAppend(long channelId) {
- long endTimeMs =
- mClock.currentTimeMillis() + ProgramDataManager.PROGRAM_GUIDE_MAX_TIME_RANGE;
- List<ProgramInfoWrapper> programList = mProgramInfoList.get((int) channelId);
- if (mProgramInfoList == null) {
- return;
- }
- ProgramInfo stub = ProgramInfo.create();
- ProgramInfoWrapper last = programList.get(programList.size() - 1);
- while (last.startTimeMs < endTimeMs) {
- ProgramInfo nextProgramInfo = stub.build(InstrumentationRegistry.getContext(),
- last.index + 1);
- ProgramInfoWrapper next = new ProgramInfoWrapper(last.index + 1,
- last.startTimeMs + last.programInfo.durationMs, nextProgramInfo);
- programList.add(next);
- last = next;
- }
- mContentResolver.notifyChange(TvContract.Programs.CONTENT_URI, null);
- }
-
- private void assertProgramUri(Uri uri) {
- assertTrue("Uri(" + uri + ") isn't channel uri",
- uri.toString().startsWith(TvContract.Programs.CONTENT_URI.toString()));
- }
-
- public ProgramInfoWrapper get(long channelId, int position) {
- List<ProgramInfoWrapper> programList = mProgramInfoList.get((int) channelId);
- if (programList == null || position >= programList.size()) {
- return null;
- }
- return programList.get(position);
- }
- }
-
- private class FakeCursor extends MockCursor {
- private final String[] ALL_COLUMNS = {
- TvContract.Programs.COLUMN_CHANNEL_ID,
- TvContract.Programs.COLUMN_TITLE,
- TvContract.Programs.COLUMN_SHORT_DESCRIPTION,
- TvContract.Programs.COLUMN_EPISODE_TITLE,
- TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS,
- TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS};
- private final String[] mColumns;
- private final boolean mIsQueryForSingleChannel;
- private final long mStartTimeMs;
- private final long mEndTimeMs;
- private final int mCount;
- private long mChannelId;
- private int mProgramPosition;
- private ProgramInfoWrapper mCurrentProgram;
-
- /**
- * Constructor
- * @param columns the same as projection passed from {@link FakeContentProvider#query}.
- * Can be null for query all.
- * @param channelId channel ID to query programs belongs to the specified channel.
- * Can be negative to indicate all channels.
- * @param startTimeMs start of the time range to query programs.
- * @param endTimeMs end of the time range to query programs.
- */
- public FakeCursor(String[] columns, long channelId, long startTimeMs, long endTimeMs) {
- mColumns = (columns == null) ? ALL_COLUMNS : columns;
- mIsQueryForSingleChannel = (channelId > 0);
- mChannelId = channelId;
- mProgramPosition = -1;
- mStartTimeMs = startTimeMs;
- mEndTimeMs = endTimeMs;
- int count = 0;
- while (moveToNext()) {
- count++;
- }
- mCount = count;
- // Rewind channel Id and program index.
- mChannelId = channelId;
- mProgramPosition = -1;
- if (DEBUG) {
- Log.d(TAG, "FakeCursor(columns=" + Arrays.toString(columns)
- + ", channelId=" + channelId + ", startTimeMs=" + startTimeMs
- + ", endTimeMs=" + endTimeMs + ") has mCount=" + mCount);
- }
- }
-
- @Override
- public String getColumnName(int columnIndex) {
- return mColumns[columnIndex];
- }
-
- @Override
- public int getColumnIndex(String columnName) {
- for (int i = 0; i < mColumns.length; i++) {
- if (mColumns[i].equalsIgnoreCase(columnName)) {
- return i;
- }
- }
- return -1;
- }
-
- @Override
- public int getInt(int columnIndex) {
- if (DEBUG) {
- Log.d(TAG, "Column (" + getColumnName(columnIndex) + ") is ignored in getInt()");
- }
- return 0;
- }
-
- @Override
- public long getLong(int columnIndex) {
- String columnName = getColumnName(columnIndex);
- switch (columnName) {
- case TvContract.Programs.COLUMN_CHANNEL_ID:
- return mChannelId;
- case TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS:
- return mCurrentProgram.startTimeMs;
- case TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS:
- return mCurrentProgram.startTimeMs + mCurrentProgram.programInfo.durationMs;
- }
- if (DEBUG) {
- Log.d(TAG, "Column (" + columnName + ") is ignored in getLong()");
- }
- return 0;
- }
-
- @Override
- public String getString(int columnIndex) {
- String columnName = getColumnName(columnIndex);
- switch (columnName) {
- case TvContract.Programs.COLUMN_TITLE:
- return mCurrentProgram.programInfo.title;
- case TvContract.Programs.COLUMN_SHORT_DESCRIPTION:
- return mCurrentProgram.programInfo.description;
- case TvContract.Programs.COLUMN_EPISODE_TITLE:
- return mCurrentProgram.programInfo.episode;
- }
- if (DEBUG) {
- Log.d(TAG, "Column (" + columnName + ") is ignored in getString()");
- }
- return null;
- }
-
- @Override
- public int getCount() {
- return mCount;
- }
-
- @Override
- public boolean moveToNext() {
- while (true) {
- ProgramInfoWrapper program = mContentProvider.get(mChannelId, ++mProgramPosition);
- if (program == null || program.startTimeMs >= mEndTimeMs) {
- if (mIsQueryForSingleChannel) {
- return false;
- } else {
- if (++mChannelId > Constants.UNIT_TEST_CHANNEL_COUNT) {
- return false;
- }
- mProgramPosition = -1;
- }
- } else if (program.startTimeMs + program.programInfo.durationMs >= mStartTimeMs) {
- mCurrentProgram = program;
- break;
- }
- }
- return true;
- }
-
- @Override
- public void close() {
- // No-op.
- }
- }
-
- private class TestProgramDataManagerListener implements ProgramDataManager.Listener {
- public CountDownLatch programUpdatedLatch = new CountDownLatch(1);
-
- @Override
- public void onProgramUpdated() {
- programUpdatedLatch.countDown();
- }
-
- public void reset() {
- programUpdatedLatch = new CountDownLatch(1);
- }
- }
-
- private class TestProgramDataManagerOnCurrentProgramUpdatedListener implements
- OnCurrentProgramUpdatedListener {
- public final CountDownLatch currentProgramUpdatedLatch = new CountDownLatch(1);
- public long updatedChannelId = -1;
- public Program updatedProgram = null;
-
- @Override
- public void onCurrentProgramUpdated(long channelId, Program program) {
- updatedChannelId = channelId;
- updatedProgram = program;
- currentProgramUpdatedLatch.countDown();
- }
- }
-}
diff --git a/tests/unit/src/com/android/tv/data/ProgramTest.java b/tests/unit/src/com/android/tv/data/ProgramTest.java
deleted file mode 100644
index 1d1f6c10..00000000
--- a/tests/unit/src/com/android/tv/data/ProgramTest.java
+++ /dev/null
@@ -1,182 +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.data;
-
-import static android.media.tv.TvContract.Programs.Genres.COMEDY;
-import static android.media.tv.TvContract.Programs.Genres.FAMILY_KIDS;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import android.media.tv.TvContentRating;
-import android.media.tv.TvContract.Programs.Genres;
-import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-
-import com.android.tv.data.Program.CriticScore;
-
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Tests for {@link Program}.
- */
-@SmallTest
-public class ProgramTest {
- private static final int NOT_FOUND_GENRE = 987;
-
- private static final int FAMILY_GENRE_ID = GenreItems.getId(FAMILY_KIDS);
-
- private static final int COMEDY_GENRE_ID = GenreItems.getId(COMEDY);
-
- @Test
- public void testBuild() {
- Program program = new Program.Builder().build();
- assertEquals("isValid", false, program.isValid());
- }
-
- @Test
- public void testNoGenres() {
- Program program = new Program.Builder()
- .setCanonicalGenres("")
- .build();
- assertNullCanonicalGenres(program);
- assertHasGenre(program, NOT_FOUND_GENRE, false);
- assertHasGenre(program, FAMILY_GENRE_ID, false);
- assertHasGenre(program, COMEDY_GENRE_ID, false);
- assertHasGenre(program, GenreItems.ID_ALL_CHANNELS, true);
- }
-
- @Test
- public void testFamilyGenre() {
- Program program = new Program.Builder()
- .setCanonicalGenres(FAMILY_KIDS)
- .build();
- assertCanonicalGenres(program, FAMILY_KIDS);
- assertHasGenre(program, NOT_FOUND_GENRE, false);
- assertHasGenre(program, FAMILY_GENRE_ID, true);
- assertHasGenre(program, COMEDY_GENRE_ID, false);
- assertHasGenre(program, GenreItems.ID_ALL_CHANNELS, true);
- }
-
- @Test
- public void testFamilyComedyGenre() {
- Program program = new Program.Builder()
- .setCanonicalGenres(FAMILY_KIDS + ", " + COMEDY)
- .build();
- assertCanonicalGenres(program, FAMILY_KIDS, COMEDY);
- assertHasGenre(program, NOT_FOUND_GENRE, false);
- assertHasGenre(program, FAMILY_GENRE_ID, true);
- assertHasGenre(program, COMEDY_GENRE_ID, true);
- assertHasGenre(program, GenreItems.ID_ALL_CHANNELS, true);
- }
-
- @Test
- public void testOtherGenre() {
- Program program = new Program.Builder()
- .setCanonicalGenres("other")
- .build();
- assertCanonicalGenres(program);
- assertHasGenre(program, NOT_FOUND_GENRE, false);
- assertHasGenre(program, FAMILY_GENRE_ID, false);
- assertHasGenre(program, COMEDY_GENRE_ID, false);
- assertHasGenre(program, GenreItems.ID_ALL_CHANNELS, true);
- }
-
- @Test
- public void testParcelable() {
- List<CriticScore> criticScores = new ArrayList<>();
- criticScores.add(new CriticScore("1", "2", "3"));
- criticScores.add(new CriticScore("4", "5", "6"));
- TvContentRating[] ratings = new TvContentRating[2];
- ratings[0] = TvContentRating.unflattenFromString("1/2/3");
- ratings[1] = TvContentRating.unflattenFromString("4/5/6");
- Program p = new Program.Builder()
- .setId(1)
- .setPackageName("2")
- .setChannelId(3)
- .setTitle("4")
- .setSeriesId("5")
- .setEpisodeTitle("6")
- .setSeasonNumber("7")
- .setSeasonTitle("8")
- .setEpisodeNumber("9")
- .setStartTimeUtcMillis(10)
- .setEndTimeUtcMillis(11)
- .setDescription("12")
- .setLongDescription("12-long")
- .setVideoWidth(13)
- .setVideoHeight(14)
- .setCriticScores(criticScores)
- .setPosterArtUri("15")
- .setThumbnailUri("16")
- .setCanonicalGenres(Genres.encode(Genres.SPORTS, Genres.SHOPPING))
- .setContentRatings(ratings)
- .setRecordingProhibited(true)
- .build();
- Parcel p1 = Parcel.obtain();
- Parcel p2 = Parcel.obtain();
- try {
- p.writeToParcel(p1, 0);
- byte[] bytes = p1.marshall();
- p2.unmarshall(bytes, 0, bytes.length);
- p2.setDataPosition(0);
- Program r2 = Program.fromParcel(p2);
- assertEquals(p, r2);
- } finally {
- p1.recycle();
- p2.recycle();
- }
- }
-
- @Test
- public void testParcelableWithCriticScore() {
- Program program = new Program.Builder()
- .setTitle("MyTitle")
- .addCriticScore(new CriticScore(
- "default source",
- "5/10",
- "https://testurl/testimage.jpg"))
- .build();
- Parcel parcel = Parcel.obtain();
- program.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
- Program programFromParcel = Program.CREATOR.createFromParcel(parcel);
-
- assertNotNull(programFromParcel.getCriticScores());
- assertEquals(programFromParcel.getCriticScores().get(0).source, "default source");
- assertEquals(programFromParcel.getCriticScores().get(0).score, "5/10");
- assertEquals(programFromParcel.getCriticScores().get(0).logoUrl,
- "https://testurl/testimage.jpg");
- }
-
- private static void assertNullCanonicalGenres(Program program) {
- String[] actual = program.getCanonicalGenres();
- assertNull("Expected null canonical genres but was " + Arrays.toString(actual), actual);
- }
-
- private static void assertCanonicalGenres(Program program, String... expected) {
- assertEquals("canonical genres", Arrays.asList(expected),
- Arrays.asList(program.getCanonicalGenres()));
- }
-
- private static void assertHasGenre(Program program, int genreId, boolean expected) {
- assertEquals("hasGenre(" + genreId + ")", expected, program.hasGenre(genreId));
- }
-}
diff --git a/tests/unit/src/com/android/tv/data/TvInputNewComparatorTest.java b/tests/unit/src/com/android/tv/data/TvInputNewComparatorTest.java
index b4682dd9..8e892cce 100644
--- a/tests/unit/src/com/android/tv/data/TvInputNewComparatorTest.java
+++ b/tests/unit/src/com/android/tv/data/TvInputNewComparatorTest.java
@@ -19,26 +19,24 @@ package com.android.tv.data;
import android.content.pm.ResolveInfo;
import android.media.tv.TvInputInfo;
import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
import android.util.Pair;
-
import com.android.tv.testing.ComparatorTester;
+import com.android.tv.testing.utils.TestUtils;
import com.android.tv.util.SetupUtils;
-import com.android.tv.util.TestUtils;
import com.android.tv.util.TvInputManagerHelper;
-
+import java.util.Comparator;
+import java.util.LinkedHashMap;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Matchers;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
-import java.util.Comparator;
-import java.util.LinkedHashMap;
-
-/**
- * Test for {@link TvInputNewComparator}
- */
+/** Test for {@link TvInputNewComparator} */
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class TvInputNewComparatorTest {
@Test
public void testComparator() throws Exception {
@@ -51,45 +49,48 @@ public class TvInputNewComparatorTest {
inputIdToNewInput.put("3_old_input", new Pair<>(false, true));
SetupUtils setupUtils = Mockito.mock(SetupUtils.class);
- Mockito.when(setupUtils.isNewInput(Matchers.anyString())).thenAnswer(
- new Answer<Boolean>() {
- @Override
- public Boolean answer(InvocationOnMock invocation) throws Throwable {
- String inputId = (String) invocation.getArguments()[0];
- return inputIdToNewInput.get(inputId).first;
- }
- }
- );
- Mockito.when(setupUtils.isSetupDone(Matchers.anyString())).thenAnswer(
- new Answer<Boolean>() {
- @Override
- public Boolean answer(InvocationOnMock invocation) throws Throwable {
- String inputId = (String) invocation.getArguments()[0];
- return inputIdToNewInput.get(inputId).second;
- }
- }
- );
+ Mockito.when(setupUtils.isNewInput(Matchers.anyString()))
+ .thenAnswer(
+ new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ String inputId = (String) invocation.getArguments()[0];
+ return inputIdToNewInput.get(inputId).first;
+ }
+ });
+ Mockito.when(setupUtils.isSetupDone(Matchers.anyString()))
+ .thenAnswer(
+ new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ String inputId = (String) invocation.getArguments()[0];
+ return inputIdToNewInput.get(inputId).second;
+ }
+ });
TvInputManagerHelper inputManager = Mockito.mock(TvInputManagerHelper.class);
- Mockito.when(inputManager.getDefaultTvInputInfoComparator()).thenReturn(
- new Comparator<TvInputInfo>() {
- @Override
- public int compare(TvInputInfo lhs, TvInputInfo rhs) {
- return lhs.getId().compareTo(rhs.getId());
- }
- }
- );
+ Mockito.when(inputManager.getDefaultTvInputInfoComparator())
+ .thenReturn(
+ new Comparator<TvInputInfo>() {
+ @Override
+ public int compare(TvInputInfo lhs, TvInputInfo rhs) {
+ return lhs.getId().compareTo(rhs.getId());
+ }
+ });
TvInputNewComparator comparator = new TvInputNewComparator(setupUtils, inputManager);
ComparatorTester<TvInputInfo> comparatorTester =
ComparatorTester.withoutEqualsTest(comparator);
ResolveInfo resolveInfo = TestUtils.createResolveInfo("test", "test");
for (String id : inputIdToNewInput.keySet()) {
// Put mock resolveInfo to prevent NPE in {@link TvInputInfo#toString}
- TvInputInfo info1 = TestUtils.createTvInputInfo(
- resolveInfo, id, "test1", TvInputInfo.TYPE_TUNER, false);
- TvInputInfo info2 = TestUtils.createTvInputInfo(
- resolveInfo, id, "test2", TvInputInfo.TYPE_DISPLAY_PORT, true);
- TvInputInfo info3 = TestUtils.createTvInputInfo(
- resolveInfo, id, "test", TvInputInfo.TYPE_HDMI, true);
+ TvInputInfo info1 =
+ TestUtils.createTvInputInfo(
+ resolveInfo, id, "test1", TvInputInfo.TYPE_TUNER, false);
+ TvInputInfo info2 =
+ TestUtils.createTvInputInfo(
+ resolveInfo, id, "test2", TvInputInfo.TYPE_DISPLAY_PORT, true);
+ TvInputInfo info3 =
+ TestUtils.createTvInputInfo(
+ resolveInfo, id, "test", TvInputInfo.TYPE_HDMI, true);
comparatorTester.addComparableGroup(info1, info2, info3);
}
comparatorTester.test();
diff --git a/tests/unit/src/com/android/tv/data/WatchedHistoryManagerTest.java b/tests/unit/src/com/android/tv/data/WatchedHistoryManagerTest.java
index 7eea1be7..43bfde09 100644
--- a/tests/unit/src/com/android/tv/data/WatchedHistoryManagerTest.java
+++ b/tests/unit/src/com/android/tv/data/WatchedHistoryManagerTest.java
@@ -17,26 +17,24 @@
package com.android.tv.data;
import static android.support.test.InstrumentationRegistry.getTargetContext;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
import android.os.Looper;
import android.support.test.filters.MediumTest;
-
+import android.support.test.runner.AndroidJUnit4;
import com.android.tv.data.WatchedHistoryManager.WatchedRecord;
-
+import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
-
-import java.util.concurrent.TimeUnit;
+import org.junit.runner.RunWith;
/**
* Test for {@link com.android.tv.data.WatchedHistoryManagerTest}
- * <p>
- * This is a medium test because it load files which accessing SharedPreferences.
+ *
+ * <p>This is a medium test because it load files which accessing SharedPreferences.
*/
@MediumTest
+@RunWith(AndroidJUnit4.class)
public class WatchedHistoryManagerTest {
// Wait time for expected success.
private static final int MAX_HISTORY_SIZE = 100;
@@ -56,13 +54,13 @@ public class WatchedHistoryManagerTest {
private void startAndWaitForComplete() throws InterruptedException {
mWatchedHistoryManager.start();
- assertTrue(mListener.mLoadFinished);
+ assertThat(mListener.mLoadFinished).isTrue();
}
@Test
public void testIsLoaded() throws InterruptedException {
startAndWaitForComplete();
- assertTrue(mWatchedHistoryManager.isLoaded());
+ assertThat(mWatchedHistoryManager.isLoaded()).isTrue();
}
@Test
@@ -71,16 +69,16 @@ public class WatchedHistoryManagerTest {
long fakeId = 100000000;
long time = System.currentTimeMillis();
long duration = TimeUnit.MINUTES.toMillis(10);
- Channel channel = new Channel.Builder().setId(fakeId).build();
+ ChannelImpl channel = new ChannelImpl.Builder().setId(fakeId).build();
mWatchedHistoryManager.logChannelViewStop(channel, time, duration);
WatchedRecord record = mWatchedHistoryManager.getRecord(0);
WatchedRecord recordFromSharedPreferences =
mWatchedHistoryManager.getRecordFromSharedPreferences(0);
- assertEquals(record.channelId, fakeId);
- assertEquals(record.watchedStartTime, time - duration);
- assertEquals(record.duration, duration);
- assertEquals(record, recordFromSharedPreferences);
+ assertThat(fakeId).isEqualTo(record.channelId);
+ assertThat(time - duration).isEqualTo(record.watchedStartTime);
+ assertThat(duration).isEqualTo(record.duration);
+ assertThat(recordFromSharedPreferences).isEqualTo(record);
}
@Test
@@ -92,28 +90,28 @@ public class WatchedHistoryManagerTest {
int size = MAX_HISTORY_SIZE * 2;
for (int i = 0; i < size; ++i) {
- Channel channel = new Channel.Builder().setId(startChannelId + i).build();
+ ChannelImpl channel = new ChannelImpl.Builder().setId(startChannelId + i).build();
mWatchedHistoryManager.logChannelViewStop(channel, time + duration * i, duration);
}
for (int i = 0; i < MAX_HISTORY_SIZE; ++i) {
WatchedRecord record = mWatchedHistoryManager.getRecord(i);
WatchedRecord recordFromSharedPreferences =
mWatchedHistoryManager.getRecordFromSharedPreferences(i);
- assertEquals(record, recordFromSharedPreferences);
- assertEquals(record.channelId, startChannelId + size - 1 - i);
+ assertThat(recordFromSharedPreferences).isEqualTo(record);
+ assertThat(startChannelId + size - 1 - i).isEqualTo(record.channelId);
}
// Since the WatchedHistory is a circular queue, the value for 0 and maxHistorySize
// are same.
- assertEquals(mWatchedHistoryManager.getRecordFromSharedPreferences(0),
- mWatchedHistoryManager.getRecordFromSharedPreferences(MAX_HISTORY_SIZE));
+ assertThat(mWatchedHistoryManager.getRecordFromSharedPreferences(MAX_HISTORY_SIZE))
+ .isEqualTo(mWatchedHistoryManager.getRecordFromSharedPreferences(0));
}
@Test
public void testWatchedRecordEquals() {
- assertTrue(new WatchedRecord(1, 2, 3).equals(new WatchedRecord(1, 2, 3)));
- assertFalse(new WatchedRecord(1, 2, 3).equals(new WatchedRecord(1, 2, 4)));
- assertFalse(new WatchedRecord(1, 2, 3).equals(new WatchedRecord(1, 4, 3)));
- assertFalse(new WatchedRecord(1, 2, 3).equals(new WatchedRecord(4, 2, 3)));
+ assertThat(new WatchedRecord(1, 2, 3).equals(new WatchedRecord(1, 2, 3))).isTrue();
+ assertThat(new WatchedRecord(1, 2, 3).equals(new WatchedRecord(1, 2, 4))).isFalse();
+ assertThat(new WatchedRecord(1, 2, 3).equals(new WatchedRecord(1, 4, 3))).isFalse();
+ assertThat(new WatchedRecord(1, 2, 3).equals(new WatchedRecord(4, 2, 3))).isFalse();
}
@Test
@@ -122,12 +120,13 @@ public class WatchedHistoryManagerTest {
long time = System.currentTimeMillis();
long duration = TimeUnit.MINUTES.toMillis(10);
WatchedRecord record = new WatchedRecord(fakeId, time, duration);
- WatchedRecord sameRecord = mWatchedHistoryManager.decode(
- mWatchedHistoryManager.encode(record));
- assertEquals(record, sameRecord);
+ WatchedRecord sameRecord =
+ mWatchedHistoryManager.decode(mWatchedHistoryManager.encode(record));
+ assertThat(sameRecord).isEqualTo(record);
}
- private class TestWatchedHistoryManagerListener implements WatchedHistoryManager.Listener {
+ private static final class TestWatchedHistoryManagerListener
+ implements WatchedHistoryManager.Listener {
boolean mLoadFinished;
@Override
@@ -136,6 +135,6 @@ public class WatchedHistoryManagerTest {
}
@Override
- public void onNewRecordAdded(WatchedRecord watchedRecord) { }
+ public void onNewRecordAdded(WatchedRecord watchedRecord) {}
}
}
diff --git a/tests/unit/src/com/android/tv/dvr/BaseDvrDataManagerTest.java b/tests/unit/src/com/android/tv/dvr/BaseDvrDataManagerTest.java
deleted file mode 100644
index 5f0ae15c..00000000
--- a/tests/unit/src/com/android/tv/dvr/BaseDvrDataManagerTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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 static android.support.test.InstrumentationRegistry.getContext;
-
-import android.os.Build;
-import android.support.annotation.NonNull;
-import android.support.test.filters.SdkSuppress;
-import android.support.test.filters.SmallTest;
-import android.test.MoreAsserts;
-
-import com.android.tv.common.feature.CommonFeatures;
-import com.android.tv.common.feature.TestableFeature;
-import com.android.tv.dvr.data.ScheduledRecording;
-import com.android.tv.testing.FakeClock;
-import com.android.tv.testing.dvr.RecordingTestUtils;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-/** Tests for {@link BaseDvrDataManager} using {@link DvrDataManagerInMemoryImpl}. */
-@SmallTest
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
-public class BaseDvrDataManagerTest {
- private static final String INPUT_ID = "input_id";
- private static final int CHANNEL_ID = 273;
-
- private final TestableFeature mDvrFeature = CommonFeatures.DVR;
- private DvrDataManagerInMemoryImpl mDvrDataManager;
- private FakeClock mFakeClock;
-
- @Before
- public void setUp() {
- mDvrFeature.enableForTest();
- mFakeClock = FakeClock.createWithCurrentTime();
- mDvrDataManager = new DvrDataManagerInMemoryImpl(getContext(), mFakeClock);
- }
-
- @After
- public void tearDown() {
- mDvrFeature.resetForTests();
- }
-
- @Test
- public void testGetNonStartedScheduledRecordings() {
- ScheduledRecording recording = mDvrDataManager
- .addScheduledRecordingInternal(createNewScheduledRecordingStartingNow());
- List<ScheduledRecording> result = mDvrDataManager.getNonStartedScheduledRecordings();
- MoreAsserts.assertContentsInAnyOrder(result, recording);
- }
-
- @Test
- 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(
- ScheduledRecording.ID_NOT_SET,
- INPUT_ID, 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
deleted file mode 100644
index 9771a2e5..00000000
--- a/tests/unit/src/com/android/tv/dvr/DvrDataManagerImplTest.java
+++ /dev/null
@@ -1,76 +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;
-
-import static org.junit.Assert.assertEquals;
-
-import android.os.Build;
-import android.support.test.filters.SdkSuppress;
-import android.support.test.filters.SmallTest;
-
-import com.android.tv.dvr.data.ScheduledRecording;
-import com.android.tv.testing.dvr.RecordingTestUtils;
-
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/** Tests for {@link DvrDataManagerImpl} */
-@SmallTest
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
-public class DvrDataManagerImplTest {
- private static final String INPUT_ID = "input_id";
- private static final int CHANNEL_ID = 273;
-
- @Test
- public void testGetNextScheduledStartTimeAfter() {
- long id = 1;
- List<ScheduledRecording> scheduledRecordings = new ArrayList<>();
- assertNextStartTime(scheduledRecordings, 0L, DvrDataManager.NEXT_START_TIME_NOT_FOUND);
- scheduledRecordings.add(RecordingTestUtils
- .createTestRecordingWithIdAndPeriod(id++, INPUT_ID, CHANNEL_ID, 10L, 20L));
- assertNextStartTime(scheduledRecordings, 9L, 10L);
- assertNextStartTime(scheduledRecordings, 10L, DvrDataManager.NEXT_START_TIME_NOT_FOUND);
- scheduledRecordings.add(RecordingTestUtils
- .createTestRecordingWithIdAndPeriod(id++, INPUT_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++, INPUT_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++, INPUT_ID, CHANNEL_ID, 10L, 20L));
- scheduledRecordings.add(RecordingTestUtils
- .createTestRecordingWithIdAndPeriod(id++, INPUT_ID, CHANNEL_ID, 10L, 20L));
- scheduledRecordings.add(RecordingTestUtils
- .createTestRecordingWithIdAndPeriod(id++, INPUT_ID, CHANNEL_ID, 10L, 20L));
- assertNextStartTime(scheduledRecordings, 9L, 10L);
- assertNextStartTime(scheduledRecordings, 10L, DvrDataManager.NEXT_START_TIME_NOT_FOUND);
- }
-
- private void assertNextStartTime(List<ScheduledRecording> scheduledRecordings, long startTime,
- long expected) {
- assertEquals("getNextScheduledStartTimeAfter()", expected,
- DvrDataManagerImpl.getNextStartTimeAfter(scheduledRecordings, startTime));
- }
-} \ No newline at end of file
diff --git a/tests/unit/src/com/android/tv/dvr/DvrScheduleManagerTest.java b/tests/unit/src/com/android/tv/dvr/DvrScheduleManagerTest.java
deleted file mode 100644
index 1c77aa0e..00000000
--- a/tests/unit/src/com/android/tv/dvr/DvrScheduleManagerTest.java
+++ /dev/null
@@ -1,693 +0,0 @@
-/*
- * 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 static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-import android.os.Build;
-import android.support.test.filters.SdkSuppress;
-import android.support.test.filters.SmallTest;
-import android.test.MoreAsserts;
-import android.util.Range;
-
-import com.android.tv.dvr.DvrScheduleManager.ConflictInfo;
-import com.android.tv.dvr.data.ScheduledRecording;
-import com.android.tv.testing.dvr.RecordingTestUtils;
-
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-/** Tests for {@link DvrScheduleManager} */
-@SmallTest
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
-public class DvrScheduleManagerTest {
- private static final String INPUT_ID = "input_id";
-
- @Test
- public void testGetConflictingSchedules_emptySchedule() {
- List<ScheduledRecording> schedules = new ArrayList<>();
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 1));
- }
-
- @Test
- public void testGetConflictingSchedules_noConflict() {
- long priority = 0;
- long channelId = 0;
- List<ScheduledRecording> schedules = new ArrayList<>();
-
- schedules.add(RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(++channelId,
- ++priority, 0L, 200L));
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 1));
-
- schedules.add(RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(++channelId,
- ++priority, 0L, 100L));
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 2));
-
- schedules.add(RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(++channelId,
- ++priority, 100L, 200L));
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 2));
-
- schedules.add(RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(++channelId,
- ++priority, 0L, 100L));
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 3));
-
- schedules.add(RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(++channelId,
- ++priority, 100L, 200L));
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 3));
- }
-
- @Test
- public void testGetConflictingSchedules_noTuner() {
- long priority = 0;
- long channelId = 0;
- List<ScheduledRecording> schedules = new ArrayList<>();
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 0));
-
- schedules.add(RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(++channelId,
- ++priority, 0L, 200L));
- assertEquals(schedules, DvrScheduleManager.getConflictingSchedules(schedules, 0));
- schedules.add(0, RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(++channelId,
- ++priority, 0L, 100L));
- assertEquals(schedules, DvrScheduleManager.getConflictingSchedules(schedules, 0));
- }
-
- @Test
- public void testGetConflictingSchedules_conflict() {
- long priority = 0;
- long channelId = 0;
- List<ScheduledRecording> schedules = new ArrayList<>();
-
- ScheduledRecording r1 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 200L);
- schedules.add(r1);
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 1));
-
- ScheduledRecording r2 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 100L);
- schedules.add(r2);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1),
- r1);
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 2));
-
- ScheduledRecording r3 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 100L, 200L);
- schedules.add(r3);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1),
- r1);
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 2));
-
- ScheduledRecording r4 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 100L);
- schedules.add(r4);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1),
- r2, r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 2),
- r1);
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 3));
-
- ScheduledRecording r5 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 100L, 200L);
- schedules.add(r5);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1),
- r3, r2, r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 2),
- r1);
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 3));
-
- ScheduledRecording r6 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 10L, 90L);
- schedules.add(r6);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1),
- r4, r3, r2, r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 2),
- r2, r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 3),
- r1);
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 4));
-
- ScheduledRecording r7 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 110L, 190L);
- schedules.add(r7);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1),
- r5, r4, r3, r2, r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 2),
- r3, r2, r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 3),
- r1);
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 4));
-
- ScheduledRecording r8 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 50L, 150L);
- schedules.add(r8);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1),
- r7, r6, r5, r4, r3, r2, r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 2),
- r5, r4, r3, r2, r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 3),
- r3, r2, r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 4),
- r1);
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 5));
- }
-
- @Test
- public void testGetConflictingSchedules_conflict2() {
- // The case when there is a long schedule.
- long priority = 0;
- long channelId = 0;
- List<ScheduledRecording> schedules = new ArrayList<>();
-
- ScheduledRecording r1 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 1000L);
- schedules.add(r1);
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 1));
-
- ScheduledRecording r2 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 100L);
- schedules.add(r2);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1),
- r1);
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 2));
-
- ScheduledRecording r3 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 100L, 200L);
- schedules.add(r3);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1),
- r1);
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 2));
- }
-
- @Test
- public void testGetConflictingSchedules_reverseOrder() {
- long priority = 0;
- long channelId = 0;
- List<ScheduledRecording> schedules = new ArrayList<>();
-
- ScheduledRecording r1 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 200L);
- schedules.add(0, r1);
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 1));
-
- ScheduledRecording r2 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 100L);
- schedules.add(0, r2);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1),
- r1);
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 2));
-
- ScheduledRecording r3 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 100L, 200L);
- schedules.add(0, r3);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1),
- r1);
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 2));
-
- ScheduledRecording r4 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 100L);
- schedules.add(0, r4);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1),
- r2, r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 2),
- r1);
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 3));
-
- ScheduledRecording r5 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 100L, 200L);
- schedules.add(0, r5);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1),
- r3, r2, r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 2),
- r1);
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 3));
-
- ScheduledRecording r6 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 10L, 90L);
- schedules.add(0, r6);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1),
- r4, r3, r2, r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 2),
- r2, r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 3),
- r1);
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 4));
-
- ScheduledRecording r7 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 110L, 190L);
- schedules.add(0, r7);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1),
- r5, r4, r3, r2, r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 2),
- r3, r2, r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 3),
- r1);
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 4));
-
- ScheduledRecording r8 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 50L, 150L);
- schedules.add(0, r8);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1),
- r7, r6, r5, r4, r3, r2, r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 2),
- r5, r4, r3, r2, r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 3),
- r3, r2, r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 4),
- r1);
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 5));
- }
-
- @Test
- public void testGetConflictingSchedules_period1() {
- long priority = 0;
- long channelId = 0;
- List<ScheduledRecording> schedules = new ArrayList<>();
-
- ScheduledRecording r1 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 200L);
- schedules.add(r1);
- ScheduledRecording r2 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 100L);
- schedules.add(r2);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1,
- Collections.singletonList(new Range<>(10L, 20L))), r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1,
- Collections.singletonList(new Range<>(110L, 120L))), r1);
- }
-
- @Test
- public void testGetConflictingSchedules_period2() {
- long priority = 0;
- long channelId = 0;
- List<ScheduledRecording> schedules = new ArrayList<>();
-
- ScheduledRecording r1 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 200L);
- schedules.add(r1);
- ScheduledRecording r2 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 100L, 200L);
- schedules.add(r2);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1,
- Collections.singletonList(new Range<>(10L, 20L))), r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1,
- Collections.singletonList(new Range<>(110L, 120L))), r1);
- }
-
- @Test
- public void testGetConflictingSchedules_period3() {
- long priority = 0;
- long channelId = 0;
- List<ScheduledRecording> schedules = new ArrayList<>();
-
- ScheduledRecording r1 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 100L);
- schedules.add(r1);
- ScheduledRecording r2 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 100L, 200L);
- schedules.add(r2);
- ScheduledRecording r3 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 100L);
- schedules.add(r3);
- ScheduledRecording r4 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 100L, 200L);
- schedules.add(r4);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1,
- Collections.singletonList(new Range<>(10L, 20L))), r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1,
- Collections.singletonList(new Range<>(110L, 120L))), r2);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1,
- Collections.singletonList(new Range<>(50L, 150L))), r2, r1);
- List<Range<Long>> ranges = new ArrayList<>();
- ranges.add(new Range<>(10L, 20L));
- ranges.add(new Range<>(110L, 120L));
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1,
- ranges), r2, r1);
- }
-
- @Test
- public void testGetConflictingSchedules_addSchedules1() {
- long priority = 0;
- long channelId = 0;
- List<ScheduledRecording> schedules = new ArrayList<>();
-
- ScheduledRecording r1 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 200L);
- schedules.add(r1);
- ScheduledRecording r2 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 100L);
- schedules.add(r2);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(
- Collections.singletonList(
- ScheduledRecording.builder(INPUT_ID, ++channelId, 10L, 20L)
- .setPriority(++priority).build()),
- schedules, 1), r2, r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(
- Collections.singletonList(
- ScheduledRecording.builder(INPUT_ID, ++channelId, 110L, 120L)
- .setPriority(++priority).build()),
- schedules, 1), r1);
- }
-
- @Test
- public void testGetConflictingSchedules_addSchedules2() {
- long priority = 0;
- long channelId = 0;
- List<ScheduledRecording> schedules = new ArrayList<>();
-
- ScheduledRecording r1 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 200L);
- schedules.add(r1);
- ScheduledRecording r2 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 100L, 200L);
- schedules.add(r2);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(
- Collections.singletonList(
- ScheduledRecording.builder(INPUT_ID, ++channelId, 10L, 20L)
- .setPriority(++priority).build()),
- schedules, 1), r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(
- Collections.singletonList(
- ScheduledRecording.builder(INPUT_ID, ++channelId, 110L, 120L)
- .setPriority(++priority).build()),
- schedules, 1), r2, r1);
- }
-
- @Test
- public void testGetConflictingSchedules_addLowestPriority() {
- long priority = 0;
- long channelId = 0;
- List<ScheduledRecording> schedules = new ArrayList<>();
-
- ScheduledRecording r1 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 400L);
- schedules.add(r1);
- ScheduledRecording r2 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 100L, 200L);
- schedules.add(r2);
- // Returning r1 even though r1 has the higher priority than the new one. That's because r1
- // starts at 0 and stops at 100, and the new one will be recorded successfully.
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(
- Collections.singletonList(
- ScheduledRecording.builder(INPUT_ID, ++channelId, 200L, 300L)
- .setPriority(0).build()),
- schedules, 1), r1);
- }
-
- @Test
- public void testGetConflictingSchedules_sameChannel() {
- long priority = 0;
- long channelId = 1;
- List<ScheduledRecording> schedules = new ArrayList<>();
- schedules.add(RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(channelId,
- ++priority, 0L, 200L));
- schedules.add(RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(channelId,
- ++priority, 0L, 200L));
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedules(schedules, 3));
- }
-
- @Test
- public void testGetConflictingSchedule_startEarlyAndFail() {
- long priority = 0;
- long channelId = 0;
- List<ScheduledRecording> schedules = new ArrayList<>();
- ScheduledRecording r1 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 200L, 300L);
- schedules.add(r1);
- ScheduledRecording r2 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 400L);
- schedules.add(r2);
- ScheduledRecording r3 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 100L, 200L);
- schedules.add(r3);
- // r2 starts recording and fails when r3 starts. r1 is recorded successfully.
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1),
- r2);
- }
-
- @Test
- public void testGetConflictingSchedule_startLate() {
- long priority = 0;
- long channelId = 0;
- List<ScheduledRecording> schedules = new ArrayList<>();
- ScheduledRecording r1 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 200L, 400L);
- schedules.add(r1);
- ScheduledRecording r2 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 100L, 300L);
- schedules.add(r2);
- ScheduledRecording r3 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 200L);
- schedules.add(r3);
- // r2 and r1 are clipped.
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedules(schedules, 1),
- r2, r1);
- }
-
- @Test
- public void testGetConflictingSchedulesForTune_canTune() {
- // Can tune to the recorded channel if tuner count is 1.
- long priority = 0;
- long channelId = 1;
- List<ScheduledRecording> schedules = new ArrayList<>();
- schedules.add(RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(channelId,
- ++priority, 0L, 200L));
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedulesForTune(INPUT_ID,
- channelId, 0L, priority + 1, schedules, 1));
- }
-
- @Test
- public void testGetConflictingSchedulesForTune_cannotTune() {
- // Can't tune to a channel if other channel is recording and tuner count is 1.
- long priority = 0;
- long channelId = 1;
- List<ScheduledRecording> schedules = new ArrayList<>();
- schedules.add(RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(channelId,
- ++priority, 0L, 200L));
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedulesForTune(
- INPUT_ID, channelId + 1, 0L, priority + 1, schedules, 1), schedules.get(0));
- }
-
- @Test
- public void testGetConflictingSchedulesForWatching_otherChannels() {
- // The other channels are to be recorded.
- long priority = 0;
- long channelToWatch = 1;
- long channelId = 1;
- List<ScheduledRecording> schedules = new ArrayList<>();
- ScheduledRecording r1 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 200L);
- schedules.add(r1);
- ScheduledRecording r2 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 200L);
- schedules.add(r2);
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedulesForWatching(
- INPUT_ID, channelToWatch, 0L, ++priority, schedules, 3));
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedulesForWatching(
- INPUT_ID, channelToWatch, 0L, ++priority, schedules, 2), r1);
- }
-
- @Test
- public void testGetConflictingSchedulesForWatching_sameChannel1() {
- long priority = 0;
- long channelToWatch = 1;
- long channelId = 1;
- List<ScheduledRecording> schedules = new ArrayList<>();
- ScheduledRecording r1 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- channelToWatch, ++priority, 0L, 200L);
- schedules.add(r1);
- ScheduledRecording r2 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 200L);
- schedules.add(r2);
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedulesForWatching(
- INPUT_ID, channelToWatch, 0L, ++priority, schedules, 2));
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedulesForWatching(
- INPUT_ID, channelToWatch, 0L, ++priority, schedules, 1), r2);
- }
-
- @Test
- public void testGetConflictingSchedulesForWatching_sameChannel2() {
- long priority = 0;
- long channelToWatch = 1;
- long channelId = 1;
- List<ScheduledRecording> schedules = new ArrayList<>();
- ScheduledRecording r1 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 200L);
- schedules.add(r1);
- ScheduledRecording r2 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- channelToWatch, ++priority, 0L, 200L);
- schedules.add(r2);
- MoreAsserts.assertEmpty(DvrScheduleManager.getConflictingSchedulesForWatching(
- INPUT_ID, channelToWatch, 0L, ++priority, schedules, 2));
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedulesForWatching(
- INPUT_ID, channelToWatch, 0L, ++priority, schedules, 1), r1);
- }
-
- @Test
- public void testGetConflictingSchedulesForWatching_sameChannelConflict1() {
- long priority = 0;
- long channelToWatch = 1;
- long channelId = 1;
- List<ScheduledRecording> schedules = new ArrayList<>();
- ScheduledRecording r1 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 200L);
- schedules.add(r1);
- ScheduledRecording r2 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- channelToWatch, ++priority, 0L, 200L);
- schedules.add(r2);
- ScheduledRecording r3 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- channelToWatch, ++priority, 0L, 200L);
- schedules.add(r3);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedulesForWatching(
- INPUT_ID, channelToWatch, 0L, ++priority, schedules, 3), r2);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedulesForWatching(
- INPUT_ID, channelToWatch, 0L, ++priority, schedules, 2), r2);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedulesForWatching(
- INPUT_ID, channelToWatch, 0L, ++priority, schedules, 1), r2, r1);
- }
-
- @Test
- public void testGetConflictingSchedulesForWatching_sameChannelConflict2() {
- long priority = 0;
- long channelToWatch = 1;
- long channelId = 1;
- List<ScheduledRecording> schedules = new ArrayList<>();
- ScheduledRecording r1 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- channelToWatch, ++priority, 0L, 200L);
- schedules.add(r1);
- ScheduledRecording r2 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- channelToWatch, ++priority, 0L, 200L);
- schedules.add(r2);
- ScheduledRecording r3 = RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(
- ++channelId, ++priority, 0L, 200L);
- schedules.add(r3);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedulesForWatching(
- INPUT_ID, channelToWatch, 0L, ++priority, schedules, 3), r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedulesForWatching(
- INPUT_ID, channelToWatch, 0L, ++priority, schedules, 2), r1);
- MoreAsserts.assertContentsInOrder(DvrScheduleManager.getConflictingSchedulesForWatching(
- INPUT_ID, channelToWatch, 0L, ++priority, schedules, 1), r3, r1);
- }
-
- @Test
- public void testPartiallyConflictingSchedules() {
- long priority = 100;
- long channelId = 0;
- List<ScheduledRecording> schedules = new ArrayList<>(Arrays.asList(
- RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(++channelId,
- --priority, 0L, 400L),
- RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(++channelId,
- --priority, 0L, 200L),
- RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(++channelId,
- --priority, 200L, 500L),
- RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(++channelId,
- --priority, 400L, 600L),
- RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(++channelId,
- --priority, 700L, 800L),
- RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(++channelId,
- --priority, 600L, 900L),
- RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(++channelId,
- --priority, 800L, 900L),
- RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(++channelId,
- --priority, 800L, 900L),
- RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(++channelId,
- --priority, 750L, 850L),
- RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(++channelId,
- --priority, 300L, 450L),
- RecordingTestUtils.createTestRecordingWithPriorityAndPeriod(++channelId,
- --priority, 50L, 900L)
- ));
- List<ConflictInfo> conflicts = DvrScheduleManager.getConflictingSchedulesInfo(schedules, 1);
-
- assertNotInList(schedules.get(0), conflicts);
- assertFullConflict(schedules.get(1), conflicts);
- assertPartialConflict(schedules.get(2), conflicts);
- assertPartialConflict(schedules.get(3), conflicts);
- assertNotInList(schedules.get(4), conflicts);
- assertPartialConflict(schedules.get(5), conflicts);
- assertNotInList(schedules.get(6), conflicts);
- assertFullConflict(schedules.get(7), conflicts);
- assertFullConflict(schedules.get(8), conflicts);
- assertFullConflict(schedules.get(9), conflicts);
- assertFullConflict(schedules.get(10), conflicts);
-
- conflicts = DvrScheduleManager.getConflictingSchedulesInfo(schedules, 2);
-
- assertNotInList(schedules.get(0), conflicts);
- assertNotInList(schedules.get(1), conflicts);
- assertNotInList(schedules.get(2), conflicts);
- assertNotInList(schedules.get(3), conflicts);
- assertNotInList(schedules.get(4), conflicts);
- assertNotInList(schedules.get(5), conflicts);
- assertNotInList(schedules.get(6), conflicts);
- assertFullConflict(schedules.get(7), conflicts);
- assertFullConflict(schedules.get(8), conflicts);
- assertFullConflict(schedules.get(9), conflicts);
- assertPartialConflict(schedules.get(10), conflicts);
-
- conflicts = DvrScheduleManager.getConflictingSchedulesInfo(schedules, 3);
-
- assertNotInList(schedules.get(0), conflicts);
- assertNotInList(schedules.get(1), conflicts);
- assertNotInList(schedules.get(2), conflicts);
- assertNotInList(schedules.get(3), conflicts);
- assertNotInList(schedules.get(4), conflicts);
- assertNotInList(schedules.get(5), conflicts);
- assertNotInList(schedules.get(6), conflicts);
- assertNotInList(schedules.get(7), conflicts);
- assertPartialConflict(schedules.get(8), conflicts);
- assertNotInList(schedules.get(9), conflicts);
- assertPartialConflict(schedules.get(10), conflicts);
- }
-
- private void assertNotInList(ScheduledRecording schedule, List<ConflictInfo> conflicts) {
- for (ConflictInfo conflictInfo : conflicts) {
- if (conflictInfo.schedule.equals(schedule)) {
- fail(schedule + " conflicts with others.");
- }
- }
- }
-
- private void assertPartialConflict(ScheduledRecording schedule, List<ConflictInfo> conflicts) {
- for (ConflictInfo conflictInfo : conflicts) {
- if (conflictInfo.schedule.equals(schedule)) {
- if (conflictInfo.partialConflict) {
- return;
- } else {
- fail(schedule + " fully conflicts with others.");
- }
- }
- }
- fail(schedule + " doesn't conflict");
- }
-
- private void assertFullConflict(ScheduledRecording schedule, List<ConflictInfo> conflicts) {
- for (ConflictInfo conflictInfo : conflicts) {
- if (conflictInfo.schedule.equals(schedule)) {
- if (!conflictInfo.partialConflict) {
- return;
- } else {
- fail(schedule + " partially conflicts with others.");
- }
- }
- }
- fail(schedule + " doesn't conflict");
- }
-} \ No newline at end of file
diff --git a/tests/unit/src/com/android/tv/dvr/ScheduledRecordingTest.java b/tests/unit/src/com/android/tv/dvr/ScheduledRecordingTest.java
deleted file mode 100644
index b98af603..00000000
--- a/tests/unit/src/com/android/tv/dvr/ScheduledRecordingTest.java
+++ /dev/null
@@ -1,117 +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;
-
-import static com.android.tv.testing.dvr.RecordingTestUtils.createTestRecordingWithIdAndPeriod;
-import static com.android.tv.testing.dvr.RecordingTestUtils.normalizePriority;
-import static junit.framework.TestCase.assertEquals;
-
-import android.os.Build;
-import android.support.test.filters.SdkSuppress;
-import android.support.test.filters.SmallTest;
-import android.test.MoreAsserts;
-import android.util.Range;
-
-import com.android.tv.data.Channel;
-import com.android.tv.data.Program;
-import com.android.tv.dvr.data.ScheduledRecording;
-import com.android.tv.testing.dvr.RecordingTestUtils;
-
-import org.junit.Test;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-/** Tests for {@link ScheduledRecordingTest} */
-@SmallTest
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
-public class ScheduledRecordingTest {
- private static final String INPUT_ID = "input_id";
- private static final int CHANNEL_ID = 273;
-
- @Test
- public void testIsOverLapping() {
- ScheduledRecording r = createTestRecordingWithIdAndPeriod(1, INPUT_ID, CHANNEL_ID,
- 10L, 20L);
- assertOverLapping(false, 1L, 9L, r);
-
- assertOverLapping(true, 1L, 20L, r);
- assertOverLapping(false, 1L, 10L, r);
- assertOverLapping(true, 10L, 19L, r);
- assertOverLapping(true, 10L, 20L, r);
- assertOverLapping(true, 11L, 20L, r);
- assertOverLapping(true, 11L, 21L, r);
- assertOverLapping(false, 20L, 21L, r);
-
- assertOverLapping(false, 21L, 29L, r);
- }
-
- @Test
- public void testBuildProgram() {
- Channel c = new Channel.Builder().build();
- Program p = new Program.Builder().build();
- ScheduledRecording actual = ScheduledRecording.builder(INPUT_ID, p)
- .setChannelId(c.getId()).build();
- assertEquals("type", ScheduledRecording.TYPE_PROGRAM, actual.getType());
- }
-
- @Test
- public void testBuildTime() {
- ScheduledRecording actual = createTestRecordingWithIdAndPeriod(1, INPUT_ID, CHANNEL_ID,
- 10L, 20L);
- assertEquals("type", ScheduledRecording.TYPE_TIMED, actual.getType());
- }
-
- @Test
- public void testBuildFrom() {
- ScheduledRecording expected = createTestRecordingWithIdAndPeriod(1, INPUT_ID, CHANNEL_ID,
- 10L, 20L);
- ScheduledRecording actual = ScheduledRecording.buildFrom(expected).build();
- RecordingTestUtils.assertRecordingEquals(expected, actual);
- }
-
- @Test
- public void testBuild_priority() {
- ScheduledRecording a = normalizePriority(
- createTestRecordingWithIdAndPeriod(1, INPUT_ID, CHANNEL_ID, 10L, 20L));
- ScheduledRecording b = normalizePriority(
- createTestRecordingWithIdAndPeriod(2, INPUT_ID, CHANNEL_ID, 10L, 20L));
- ScheduledRecording c = normalizePriority(
- createTestRecordingWithIdAndPeriod(3, INPUT_ID, CHANNEL_ID, 10L, 20L));
-
- // default priority
- MoreAsserts.assertContentsInOrder(sortByPriority(c, b, a), a, b, c);
-
- // make A preferred over B
- a = ScheduledRecording.buildFrom(a).setPriority(b.getPriority() + 2).build();
- MoreAsserts.assertContentsInOrder(sortByPriority(a, b, c), b, c, a);
- }
-
- 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, ScheduledRecording r) {
- assertEquals("isOverlapping(Range(" + lower + "," + upper + "), recording " + r, expected,
- r.isOverLapping(new Range<>(lower, upper)));
- }
-}
diff --git a/tests/unit/src/com/android/tv/dvr/data/SeriesRecordingTest.java b/tests/unit/src/com/android/tv/dvr/data/SeriesRecordingTest.java
deleted file mode 100644
index 790b2ee8..00000000
--- a/tests/unit/src/com/android/tv/dvr/data/SeriesRecordingTest.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * 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.data;
-
-import static org.junit.Assert.assertEquals;
-
-import android.os.Build;
-import android.os.Parcel;
-import android.support.test.filters.SdkSuppress;
-import android.support.test.filters.SmallTest;
-
-import com.android.tv.data.Program;
-
-import org.junit.Test;
-
-/**
- * Tests for {@link SeriesRecording}.
- */
-@SmallTest
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
-public class SeriesRecordingTest {
- private static final String PROGRAM_TITLE = "MyProgram";
- private static final long CHANNEL_ID = 123;
- private static final long OTHER_CHANNEL_ID = 321;
- private static final String SERIES_ID = "SERIES_ID";
- private static final String OTHER_SERIES_ID = "OTHER_SERIES_ID";
-
- private final SeriesRecording mBaseSeriesRecording = new SeriesRecording.Builder()
- .setTitle(PROGRAM_TITLE).setChannelId(CHANNEL_ID).setSeriesId(SERIES_ID).build();
- private final SeriesRecording mSeriesRecordingSeason2 = SeriesRecording
- .buildFrom(mBaseSeriesRecording).setStartFromSeason(2).build();
- private final SeriesRecording mSeriesRecordingSeason2Episode5 = SeriesRecording
- .buildFrom(mSeriesRecordingSeason2).setStartFromEpisode(5).build();
- private final Program mBaseProgram = new Program.Builder().setTitle(PROGRAM_TITLE)
- .setChannelId(CHANNEL_ID).setSeriesId(SERIES_ID).build();
-
- @Test
- public void testParcelable() {
- SeriesRecording r1 = new SeriesRecording.Builder()
- .setId(1)
- .setChannelId(2)
- .setPriority(3)
- .setTitle("4")
- .setDescription("5")
- .setLongDescription("5-long")
- .setSeriesId("6")
- .setStartFromEpisode(7)
- .setStartFromSeason(8)
- .setChannelOption(SeriesRecording.OPTION_CHANNEL_ALL)
- .setCanonicalGenreIds(new int[] {9, 10})
- .setPosterUri("11")
- .setPhotoUri("12")
- .build();
- Parcel p1 = Parcel.obtain();
- Parcel p2 = Parcel.obtain();
- try {
- r1.writeToParcel(p1, 0);
- byte[] bytes = p1.marshall();
- p2.unmarshall(bytes, 0, bytes.length);
- p2.setDataPosition(0);
- SeriesRecording r2 = SeriesRecording.fromParcel(p2);
- assertEquals(r1, r2);
- } finally {
- p1.recycle();
- p2.recycle();
- }
- }
-
- @Test
- public void testDoesProgramMatch_simpleMatch() {
- assertDoesProgramMatch(mBaseProgram, mBaseSeriesRecording, true);
- }
-
- @Test
- public void testDoesProgramMatch_differentSeriesId() {
- Program program = new Program.Builder(mBaseProgram).setSeriesId(OTHER_SERIES_ID).build();
- assertDoesProgramMatch(program, mBaseSeriesRecording, false);
- }
-
- @Test
- public void testDoesProgramMatch_differentChannel() {
- Program program = new Program.Builder(mBaseProgram).setChannelId(OTHER_CHANNEL_ID).build();
- assertDoesProgramMatch(program, mBaseSeriesRecording, false);
- }
-
- @Test
- public void testDoesProgramMatch_startFromSeason2() {
- Program program = mBaseProgram;
- assertDoesProgramMatch(program, mSeriesRecordingSeason2, true);
- program = new Program.Builder(program).setSeasonNumber("1").build();
- assertDoesProgramMatch(program, mSeriesRecordingSeason2, false);
- program = new Program.Builder(program).setSeasonNumber("2").build();
- assertDoesProgramMatch(program, mSeriesRecordingSeason2, true);
- program = new Program.Builder(program).setSeasonNumber("3").build();
- assertDoesProgramMatch(program, mSeriesRecordingSeason2, true);
- }
-
- @Test
- public void testDoesProgramMatch_startFromSeason2episode5() {
- Program program = mBaseProgram;
- assertDoesProgramMatch(program, mSeriesRecordingSeason2Episode5, true);
- program = new Program.Builder(program).setSeasonNumber("2").build();
- assertDoesProgramMatch(program, mSeriesRecordingSeason2Episode5, true);
- program = new Program.Builder(program).setEpisodeNumber("4").build();
- assertDoesProgramMatch(program, mSeriesRecordingSeason2Episode5, false);
- program = new Program.Builder(program).setEpisodeNumber("5").build();
- assertDoesProgramMatch(program, mSeriesRecordingSeason2Episode5, true);
- program = new Program.Builder(program).setEpisodeNumber("6").build();
- assertDoesProgramMatch(program, mSeriesRecordingSeason2Episode5, true);
- program = new Program.Builder(program).setSeasonNumber("3").setEpisodeNumber("1").build();
- assertDoesProgramMatch(program, mSeriesRecordingSeason2Episode5, true);
- }
-
- private void assertDoesProgramMatch(Program p, SeriesRecording seriesRecording,
- boolean expected) {
- assertEquals(seriesRecording + " doesProgramMatch " + p, expected,
- seriesRecording.matchProgram(p));
- }
-}
diff --git a/tests/unit/src/com/android/tv/dvr/provider/DvrDbSyncTest.java b/tests/unit/src/com/android/tv/dvr/provider/DvrDbSyncTest.java
deleted file mode 100644
index 94f88a51..00000000
--- a/tests/unit/src/com/android/tv/dvr/provider/DvrDbSyncTest.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * 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.provider;
-
-import static android.support.test.InstrumentationRegistry.getContext;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.os.Build;
-import android.support.test.filters.SdkSuppress;
-import android.support.test.filters.SmallTest;
-
-import com.android.tv.data.ChannelDataManager;
-import com.android.tv.data.Program;
-import com.android.tv.dvr.DvrDataManagerImpl;
-import com.android.tv.dvr.DvrManager;
-import com.android.tv.dvr.data.ScheduledRecording;
-import com.android.tv.dvr.data.SeriesRecording;
-import com.android.tv.dvr.recorder.SeriesRecordingScheduler;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link com.android.tv.dvr.DvrScheduleManager}
- */
-@SmallTest
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
-public class DvrDbSyncTest {
- private static final String INPUT_ID = "input_id";
- private static final long BASE_PROGRAM_ID = 1;
- private static final long BASE_START_TIME_MS = 0;
- private static final long BASE_END_TIME_MS = 1;
- private static final String BASE_SEASON_NUMBER = "2";
- private static final String BASE_EPISODE_NUMBER = "3";
- private static final Program BASE_PROGRAM = new Program.Builder().setId(BASE_PROGRAM_ID)
- .setStartTimeUtcMillis(BASE_START_TIME_MS).setEndTimeUtcMillis(BASE_END_TIME_MS)
- .build();
- private static final Program BASE_SERIES_PROGRAM = new Program.Builder().setId(BASE_PROGRAM_ID)
- .setStartTimeUtcMillis(BASE_START_TIME_MS).setEndTimeUtcMillis(BASE_END_TIME_MS)
- .setSeasonNumber(BASE_SEASON_NUMBER).setEpisodeNumber(BASE_EPISODE_NUMBER).build();
- private static final ScheduledRecording BASE_SCHEDULE =
- ScheduledRecording.builder(INPUT_ID, BASE_PROGRAM).build();
- private static final ScheduledRecording BASE_SERIES_SCHEDULE =
- ScheduledRecording.builder(INPUT_ID, BASE_SERIES_PROGRAM).build();
-
- private DvrDbSync mDbSync;
- @Mock private DvrManager mDvrManager;
- @Mock private DvrDataManagerImpl mDataManager;
- @Mock private ChannelDataManager mChannelDataManager;
- @Mock private SeriesRecordingScheduler mSeriesRecordingScheduler;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- when(mChannelDataManager.isDbLoadFinished()).thenReturn(true);
- when(mDvrManager.addSeriesRecording(anyObject(), anyObject(), anyInt()))
- .thenReturn(SeriesRecording.builder(INPUT_ID, BASE_PROGRAM).build());
- mDbSync = new DvrDbSync(getContext(), mDataManager, mChannelDataManager,
- mDvrManager, mSeriesRecordingScheduler);
- }
-
- @Test
- public void testHandleUpdateProgram_null() {
- addSchedule(BASE_PROGRAM_ID, BASE_SCHEDULE);
- mDbSync.handleUpdateProgram(null, BASE_PROGRAM_ID);
- verify(mDataManager).removeScheduledRecording(BASE_SCHEDULE);
- }
-
- @Test
- public void testHandleUpdateProgram_changeTimeNotStarted() {
- addSchedule(BASE_PROGRAM_ID, BASE_SCHEDULE);
- long startTimeMs = BASE_START_TIME_MS + 1;
- long endTimeMs = BASE_END_TIME_MS + 1;
- Program program = new Program.Builder(BASE_PROGRAM).setStartTimeUtcMillis(startTimeMs)
- .setEndTimeUtcMillis(endTimeMs).build();
- mDbSync.handleUpdateProgram(program, BASE_PROGRAM_ID);
- assertUpdateScheduleCalled(program);
- }
-
- @Test
- public void testHandleUpdateProgram_changeTimeInProgressNotCalled() {
- addSchedule(BASE_PROGRAM_ID, ScheduledRecording.buildFrom(BASE_SCHEDULE)
- .setState(ScheduledRecording.STATE_RECORDING_IN_PROGRESS).build());
- long startTimeMs = BASE_START_TIME_MS + 1;
- Program program = new Program.Builder(BASE_PROGRAM).setStartTimeUtcMillis(startTimeMs)
- .build();
- mDbSync.handleUpdateProgram(program, BASE_PROGRAM_ID);
- verify(mDataManager, never()).updateScheduledRecording(anyObject());
- }
-
- @Test
- public void testHandleUpdateProgram_changeSeason() {
- addSchedule(BASE_PROGRAM_ID, BASE_SERIES_SCHEDULE);
- String seasonNumber = BASE_SEASON_NUMBER + "1";
- String episodeNumber = BASE_EPISODE_NUMBER + "1";
- Program program = new Program.Builder(BASE_SERIES_PROGRAM).setSeasonNumber(seasonNumber)
- .setEpisodeNumber(episodeNumber).build();
- mDbSync.handleUpdateProgram(program, BASE_PROGRAM_ID);
- assertUpdateScheduleCalled(program);
- }
-
- @Test
- public void testHandleUpdateProgram_finished() {
- addSchedule(BASE_PROGRAM_ID, ScheduledRecording.buildFrom(BASE_SERIES_SCHEDULE)
- .setState(ScheduledRecording.STATE_RECORDING_FINISHED).build());
- String seasonNumber = BASE_SEASON_NUMBER + "1";
- String episodeNumber = BASE_EPISODE_NUMBER + "1";
- Program program = new Program.Builder(BASE_SERIES_PROGRAM).setSeasonNumber(seasonNumber)
- .setEpisodeNumber(episodeNumber).build();
- mDbSync.handleUpdateProgram(program, BASE_PROGRAM_ID);
- verify(mDataManager, never()).updateScheduledRecording(anyObject());
- }
-
- private void addSchedule(long programId, ScheduledRecording schedule) {
- when(mDataManager.getScheduledRecordingForProgramId(programId)).thenReturn(schedule);
- }
-
- private void assertUpdateScheduleCalled(Program program) {
- verify(mDataManager).updateScheduledRecording(
- eq(ScheduledRecording.builder(INPUT_ID, program).build()));
- }
-}
diff --git a/tests/unit/src/com/android/tv/dvr/provider/EpisodicProgramLoadTaskTest.java b/tests/unit/src/com/android/tv/dvr/provider/EpisodicProgramLoadTaskTest.java
deleted file mode 100644
index 216d4d5b..00000000
--- a/tests/unit/src/com/android/tv/dvr/provider/EpisodicProgramLoadTaskTest.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.provider;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.os.Build;
-import android.support.test.filters.SdkSuppress;
-import android.support.test.filters.SmallTest;
-
-import com.android.tv.dvr.data.SeasonEpisodeNumber;
-
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Tests for {@link EpisodicProgramLoadTask}
- */
-@SmallTest
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
-public class EpisodicProgramLoadTaskTest {
- private static final long SERIES_RECORDING_ID1 = 1;
- private static final long SERIES_RECORDING_ID2 = 2;
- private static final String SEASON_NUMBER1 = "SEASON NUMBER1";
- private static final String SEASON_NUMBER2 = "SEASON NUMBER2";
- private static final String EPISODE_NUMBER1 = "EPISODE NUMBER1";
- private static final String EPISODE_NUMBER2 = "EPISODE NUMBER2";
-
- @Test
- public void testEpisodeAlreadyScheduled_true() {
- List<SeasonEpisodeNumber> seasonEpisodeNumbers = new ArrayList<>();
- SeasonEpisodeNumber seasonEpisodeNumber = new SeasonEpisodeNumber(
- SERIES_RECORDING_ID1, SEASON_NUMBER1, EPISODE_NUMBER1);
- seasonEpisodeNumbers.add(seasonEpisodeNumber);
- assertTrue(seasonEpisodeNumbers.contains(
- new SeasonEpisodeNumber(SERIES_RECORDING_ID1, SEASON_NUMBER1, EPISODE_NUMBER1)));
- }
-
- @Test
- public void testEpisodeAlreadyScheduled_false() {
- List<SeasonEpisodeNumber> seasonEpisodeNumbers = new ArrayList<>();
- SeasonEpisodeNumber seasonEpisodeNumber = new SeasonEpisodeNumber(
- SERIES_RECORDING_ID1, SEASON_NUMBER1, EPISODE_NUMBER1);
- seasonEpisodeNumbers.add(seasonEpisodeNumber);
- assertFalse(seasonEpisodeNumbers.contains(
- new SeasonEpisodeNumber(SERIES_RECORDING_ID2, SEASON_NUMBER1, EPISODE_NUMBER1)));
- assertFalse(seasonEpisodeNumbers.contains(
- new SeasonEpisodeNumber(SERIES_RECORDING_ID1, SEASON_NUMBER2, EPISODE_NUMBER1)));
- assertFalse(seasonEpisodeNumbers.contains(
- new SeasonEpisodeNumber(SERIES_RECORDING_ID1, SEASON_NUMBER1, EPISODE_NUMBER2)));
- }
-
- @Test
- public void testEpisodeAlreadyScheduled_null() {
- List<SeasonEpisodeNumber> seasonEpisodeNumbers = new ArrayList<>();
- SeasonEpisodeNumber seasonEpisodeNumber = new SeasonEpisodeNumber(
- SERIES_RECORDING_ID1, SEASON_NUMBER1, EPISODE_NUMBER1);
- seasonEpisodeNumbers.add(seasonEpisodeNumber);
- assertFalse(seasonEpisodeNumbers.contains(
- new SeasonEpisodeNumber(SERIES_RECORDING_ID1, null, EPISODE_NUMBER1)));
- assertFalse(seasonEpisodeNumbers.contains(
- new SeasonEpisodeNumber(SERIES_RECORDING_ID1, SEASON_NUMBER1, null)));
- assertFalse(seasonEpisodeNumbers.contains(
- new SeasonEpisodeNumber(SERIES_RECORDING_ID1, null, null)));
- }
-} \ No newline at end of file
diff --git a/tests/unit/src/com/android/tv/dvr/recorder/DvrRecordingServiceTest.java b/tests/unit/src/com/android/tv/dvr/recorder/DvrRecordingServiceTest.java
index 8f7dcaf2..d510da32 100644
--- a/tests/unit/src/com/android/tv/dvr/recorder/DvrRecordingServiceTest.java
+++ b/tests/unit/src/com/android/tv/dvr/recorder/DvrRecordingServiceTest.java
@@ -16,24 +16,18 @@
package com.android.tv.dvr.recorder;
-import static org.mockito.Mockito.verify;
+import static com.google.common.truth.Truth.assertThat;
import android.content.Intent;
import android.os.Build;
import android.support.test.filters.SdkSuppress;
import android.support.test.filters.SmallTest;
import android.test.ServiceTestCase;
-
import com.android.tv.common.feature.CommonFeatures;
import com.android.tv.common.feature.TestableFeature;
-
-import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-/**
- * Tests for {@link DvrRecordingService}.
- */
+/** Tests for {@link DvrRecordingService}. */
@SmallTest
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
public class DvrRecordingServiceTest
@@ -61,13 +55,13 @@ public class DvrRecordingServiceTest
public void testStartService_null() throws Exception {
// Not recording
startService(null);
- assertFalse(getService().mInForeground);
+ assertThat(getService().mInForeground).isFalse();
// Recording
getService().startRecording();
startService(null);
- assertTrue(getService().mInForeground);
- assertTrue(getService().mIsRecording);
+ assertThat(getService().mInForeground).isTrue();
+ assertThat(getService().mIsRecording).isTrue();
getService().reset();
}
@@ -77,38 +71,38 @@ public class DvrRecordingServiceTest
// Not recording
startService(intent);
- assertTrue(getService().mInForeground);
- assertFalse(getService().mForegroundForUpcomingRecording);
+ assertThat(getService().mInForeground).isTrue();
+ assertThat(getService().mForegroundForUpcomingRecording).isFalse();
getService().stopForegroundIfNotRecordingInternal();
- assertFalse(getService().mInForeground);
+ assertThat(getService().mInForeground).isFalse();
// Recording, ended quickly
getService().startRecording();
startService(intent);
- assertTrue(getService().mInForeground);
- assertTrue(getService().mForegroundForUpcomingRecording);
- assertTrue(getService().mIsRecording);
+ assertThat(getService().mInForeground).isTrue();
+ assertThat(getService().mForegroundForUpcomingRecording).isTrue();
+ assertThat(getService().mIsRecording).isTrue();
getService().stopRecording();
- assertFalse(getService().mInForeground);
- assertFalse(getService().mIsRecording);
+ assertThat(getService().mInForeground).isFalse();
+ assertThat(getService().mIsRecording).isFalse();
getService().stopForegroundIfNotRecordingInternal();
- assertFalse(getService().mInForeground);
- assertFalse(getService().mIsRecording);
+ assertThat(getService().mInForeground).isFalse();
+ assertThat(getService().mIsRecording).isFalse();
getService().reset();
// Recording, ended later
getService().startRecording();
startService(intent);
- assertTrue(getService().mInForeground);
- assertTrue(getService().mForegroundForUpcomingRecording);
- assertTrue(getService().mIsRecording);
+ assertThat(getService().mInForeground).isTrue();
+ assertThat(getService().mForegroundForUpcomingRecording).isTrue();
+ assertThat(getService().mIsRecording).isTrue();
getService().stopForegroundIfNotRecordingInternal();
- assertTrue(getService().mInForeground);
- assertTrue(getService().mForegroundForUpcomingRecording);
- assertTrue(getService().mIsRecording);
+ assertThat(getService().mInForeground).isTrue();
+ assertThat(getService().mForegroundForUpcomingRecording).isTrue();
+ assertThat(getService().mIsRecording).isTrue();
getService().stopRecording();
- assertFalse(getService().mInForeground);
- assertFalse(getService().mIsRecording);
+ assertThat(getService().mInForeground).isFalse();
+ assertThat(getService().mIsRecording).isFalse();
getService().reset();
}
@@ -118,38 +112,39 @@ public class DvrRecordingServiceTest
// Not recording
startService(intent);
- assertTrue(getService().mInForeground);
- assertTrue(getService().mForegroundForUpcomingRecording);
- assertFalse(getService().mIsRecording);
+ assertThat(getService().mInForeground).isTrue();
+ assertThat(getService().mForegroundForUpcomingRecording).isTrue();
+ assertThat(getService().mIsRecording).isFalse();
getService().startRecording();
- assertTrue(getService().mInForeground);
- assertTrue(getService().mForegroundForUpcomingRecording);
- assertTrue(getService().mIsRecording);
+ assertThat(getService().mInForeground).isTrue();
+ assertThat(getService().mForegroundForUpcomingRecording).isTrue();
+ assertThat(getService().mIsRecording).isTrue();
getService().stopRecording();
- assertFalse(getService().mInForeground);
- assertFalse(getService().mIsRecording);
+ assertThat(getService().mInForeground).isFalse();
+ assertThat(getService().mIsRecording).isFalse();
getService().reset();
// Recording
getService().startRecording();
startService(intent);
- assertTrue(getService().mInForeground);
- assertTrue(getService().mForegroundForUpcomingRecording);
- assertTrue(getService().mIsRecording);
+ assertThat(getService().mInForeground).isTrue();
+ assertThat(getService().mForegroundForUpcomingRecording).isTrue();
+ assertThat(getService().mIsRecording).isTrue();
getService().startRecording();
- assertTrue(getService().mInForeground);
- assertTrue(getService().mForegroundForUpcomingRecording);
- assertTrue(getService().mIsRecording);
+ assertThat(getService().mInForeground).isTrue();
+ assertThat(getService().mForegroundForUpcomingRecording).isTrue();
+ assertThat(getService().mIsRecording).isTrue();
getService().stopRecording();
- assertTrue(getService().mInForeground);
- assertTrue(getService().mForegroundForUpcomingRecording);
- assertTrue(getService().mIsRecording);
+ assertThat(getService().mInForeground).isTrue();
+ assertThat(getService().mForegroundForUpcomingRecording).isTrue();
+ assertThat(getService().mIsRecording).isTrue();
getService().stopRecording();
- assertFalse(getService().mInForeground);
- assertFalse(getService().mIsRecording);
+ assertThat(getService().mInForeground).isFalse();
+ assertThat(getService().mIsRecording).isFalse();
getService().reset();
}
+ /** Mock {@link DvrRecordingService}. */
public static class MockDvrRecordingService extends DvrRecordingService {
private int mRecordingCount = 0;
private boolean mInForeground;
@@ -180,4 +175,4 @@ public class DvrRecordingServiceTest
mIsRecording = false;
}
}
-} \ No newline at end of file
+}
diff --git a/tests/unit/src/com/android/tv/dvr/recorder/InputTaskSchedulerTest.java b/tests/unit/src/com/android/tv/dvr/recorder/InputTaskSchedulerTest.java
deleted file mode 100644
index e5c27e2c..00000000
--- a/tests/unit/src/com/android/tv/dvr/recorder/InputTaskSchedulerTest.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * 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.recorder;
-
-import static android.support.test.InstrumentationRegistry.getContext;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.AlarmManager;
-import android.media.tv.TvInputInfo;
-import android.os.Build;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.support.test.filters.SdkSuppress;
-import android.support.test.filters.SmallTest;
-
-import com.android.tv.InputSessionManager;
-import com.android.tv.data.Channel;
-import com.android.tv.data.ChannelDataManager;
-import com.android.tv.dvr.DvrManager;
-import com.android.tv.dvr.WritableDvrDataManager;
-import com.android.tv.dvr.data.ScheduledRecording;
-import com.android.tv.dvr.recorder.InputTaskScheduler.RecordingTaskFactory;
-import com.android.tv.testing.FakeClock;
-import com.android.tv.testing.dvr.RecordingTestUtils;
-import com.android.tv.util.Clock;
-import com.android.tv.util.TestUtils;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Tests for {@link InputTaskScheduler}.
- */
-@SmallTest
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
-public class InputTaskSchedulerTest {
- private static final String INPUT_ID = "input_id";
- private static final int CHANNEL_ID = 1;
- private static final long LISTENER_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(1);
- private static final int TUNER_COUNT_ONE = 1;
- private static final int TUNER_COUNT_TWO = 2;
- private static final long LOW_PRIORITY = 1;
- private static final long HIGH_PRIORITY = 2;
-
- private FakeClock mFakeClock;
- private InputTaskScheduler mScheduler;
- @Mock private DvrManager mDvrManager;
- @Mock private WritableDvrDataManager mDataManager;
- @Mock private InputSessionManager mSessionManager;
- @Mock private AlarmManager mMockAlarmManager;
- @Mock private ChannelDataManager mChannelDataManager;
- private List<RecordingTask> mRecordingTasks;
-
- @Before
- public void setUp() throws Exception {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- mRecordingTasks = new ArrayList();
- MockitoAnnotations.initMocks(this);
- mFakeClock = FakeClock.createWithCurrentTime();
- TvInputInfo input = createTvInputInfo(TUNER_COUNT_ONE);
- mScheduler = new InputTaskScheduler(getContext(), input, Looper.myLooper(),
- mChannelDataManager, mDvrManager, mDataManager, mSessionManager, mFakeClock,
- new RecordingTaskFactory() {
- @Override
- public RecordingTask createRecordingTask(ScheduledRecording scheduledRecording,
- Channel channel, DvrManager dvrManager,
- InputSessionManager sessionManager, WritableDvrDataManager dataManager,
- Clock clock) {
- RecordingTask task = mock(RecordingTask.class);
- when(task.getPriority()).thenReturn(scheduledRecording.getPriority());
- when(task.getEndTimeMs()).thenReturn(scheduledRecording.getEndTimeMs());
- mRecordingTasks.add(task);
- return task;
- }
- });
- }
-
- @Test
- public void testAddSchedule_past() {
- ScheduledRecording r = RecordingTestUtils.createTestRecordingWithPeriod(INPUT_ID,
- CHANNEL_ID, 0L, 1L);
- when(mDataManager.getScheduledRecording(anyLong())).thenReturn(r);
- mScheduler.handleAddSchedule(r);
- mScheduler.handleBuildSchedule();
- verify(mDataManager, timeout((int) LISTENER_TIMEOUT_MS).times(1))
- .changeState(any(ScheduledRecording.class),
- eq(ScheduledRecording.STATE_RECORDING_FAILED));
- }
-
- @Test
- public void testAddSchedule_start() {
- mScheduler.handleAddSchedule(RecordingTestUtils.createTestRecordingWithPeriod(INPUT_ID,
- CHANNEL_ID, mFakeClock.currentTimeMillis(),
- mFakeClock.currentTimeMillis() + TimeUnit.HOURS.toMillis(1)));
- mScheduler.handleBuildSchedule();
- verify(mRecordingTasks.get(0), timeout((int) LISTENER_TIMEOUT_MS).times(1)).start();
- }
-
- @Test
- public void testAddSchedule_consecutiveNoStop() {
- long startTimeMs = mFakeClock.currentTimeMillis();
- long endTimeMs = startTimeMs + TimeUnit.SECONDS.toMillis(1);
- long id = 0;
- mScheduler.handleAddSchedule(
- RecordingTestUtils.createTestRecordingWithIdAndPriorityAndPeriod(++id, CHANNEL_ID,
- LOW_PRIORITY, startTimeMs, endTimeMs));
- mScheduler.handleBuildSchedule();
- startTimeMs = endTimeMs;
- endTimeMs = startTimeMs + TimeUnit.SECONDS.toMillis(1);
- mScheduler.handleAddSchedule(
- RecordingTestUtils.createTestRecordingWithIdAndPriorityAndPeriod(++id, CHANNEL_ID,
- HIGH_PRIORITY, startTimeMs, endTimeMs));
- mScheduler.handleBuildSchedule();
- verify(mRecordingTasks.get(0), timeout((int) LISTENER_TIMEOUT_MS).times(1)).start();
- // The first schedule should not be stopped because the second one should wait for the end
- // of the first schedule.
- SystemClock.sleep(LISTENER_TIMEOUT_MS);
- verify(mRecordingTasks.get(0), never()).stop();
- }
-
- @Test
- public void testAddSchedule_consecutiveNoFail() {
- long startTimeMs = mFakeClock.currentTimeMillis();
- long endTimeMs = startTimeMs + TimeUnit.SECONDS.toMillis(1);
- long id = 0;
- when(mDataManager.getScheduledRecording(anyLong())).thenReturn(ScheduledRecording
- .builder(INPUT_ID, CHANNEL_ID, 0L, 0L).build());
- mScheduler.handleAddSchedule(
- RecordingTestUtils.createTestRecordingWithIdAndPriorityAndPeriod(++id, CHANNEL_ID,
- HIGH_PRIORITY, startTimeMs, endTimeMs));
- mScheduler.handleBuildSchedule();
- startTimeMs = endTimeMs;
- endTimeMs = startTimeMs + TimeUnit.SECONDS.toMillis(1);
- mScheduler.handleAddSchedule(
- RecordingTestUtils.createTestRecordingWithIdAndPriorityAndPeriod(++id, CHANNEL_ID,
- LOW_PRIORITY, startTimeMs, endTimeMs));
- mScheduler.handleBuildSchedule();
- verify(mRecordingTasks.get(0), timeout((int) LISTENER_TIMEOUT_MS).times(1)).start();
- SystemClock.sleep(LISTENER_TIMEOUT_MS);
- verify(mRecordingTasks.get(0), never()).stop();
- // The second schedule should not fail because it can starts after the first one finishes.
- SystemClock.sleep(LISTENER_TIMEOUT_MS);
- verify(mDataManager, never())
- .changeState(any(ScheduledRecording.class),
- eq(ScheduledRecording.STATE_RECORDING_FAILED));
- }
-
- @Test
- public void testAddSchedule_consecutiveUseLessSession() throws Exception {
- TvInputInfo input = createTvInputInfo(TUNER_COUNT_TWO);
- mScheduler.updateTvInputInfo(input);
- long startTimeMs = mFakeClock.currentTimeMillis();
- long endTimeMs = startTimeMs + TimeUnit.SECONDS.toMillis(1);
- long id = 0;
- mScheduler.handleAddSchedule(
- RecordingTestUtils.createTestRecordingWithIdAndPriorityAndPeriod(++id, CHANNEL_ID,
- LOW_PRIORITY, startTimeMs, endTimeMs));
- mScheduler.handleBuildSchedule();
- startTimeMs = endTimeMs;
- endTimeMs = startTimeMs + TimeUnit.SECONDS.toMillis(1);
- mScheduler.handleAddSchedule(
- RecordingTestUtils.createTestRecordingWithIdAndPriorityAndPeriod(++id, CHANNEL_ID,
- HIGH_PRIORITY, startTimeMs, endTimeMs));
- mScheduler.handleBuildSchedule();
- verify(mRecordingTasks.get(0), timeout((int) LISTENER_TIMEOUT_MS).times(1)).start();
- SystemClock.sleep(LISTENER_TIMEOUT_MS);
- verify(mRecordingTasks.get(0), never()).stop();
- // The second schedule should wait until the first one finishes rather than creating a new
- // session even though there are available tuners.
- assertTrue(mRecordingTasks.size() == 1);
- }
-
- @Test
- public void testUpdateSchedule_noCancel() {
- ScheduledRecording r = RecordingTestUtils.createTestRecordingWithPeriod(INPUT_ID,
- CHANNEL_ID, mFakeClock.currentTimeMillis(),
- mFakeClock.currentTimeMillis() + TimeUnit.HOURS.toMillis(1));
- mScheduler.handleAddSchedule(r);
- mScheduler.handleBuildSchedule();
- mScheduler.handleUpdateSchedule(r);
- SystemClock.sleep(LISTENER_TIMEOUT_MS);
- verify(mRecordingTasks.get(0), never()).cancel();
- }
-
- @Test
- public void testUpdateSchedule_cancel() {
- ScheduledRecording r = RecordingTestUtils.createTestRecordingWithPeriod(INPUT_ID,
- CHANNEL_ID, mFakeClock.currentTimeMillis(),
- mFakeClock.currentTimeMillis() + TimeUnit.HOURS.toMillis(2));
- mScheduler.handleAddSchedule(r);
- mScheduler.handleBuildSchedule();
- mScheduler.handleUpdateSchedule(ScheduledRecording.buildFrom(r)
- .setStartTimeMs(mFakeClock.currentTimeMillis() + TimeUnit.HOURS.toMillis(1))
- .build());
- verify(mRecordingTasks.get(0), timeout((int) LISTENER_TIMEOUT_MS).times(1)).cancel();
- }
-
- private TvInputInfo createTvInputInfo(int tunerCount) throws Exception {
- return TestUtils.createTvInputInfo(null, null, null, 0, false, true, tunerCount);
- }
-}
diff --git a/tests/unit/src/com/android/tv/dvr/recorder/RecordingTaskTest.java b/tests/unit/src/com/android/tv/dvr/recorder/RecordingTaskTest.java
deleted file mode 100644
index 37561a42..00000000
--- a/tests/unit/src/com/android/tv/dvr/recorder/RecordingTaskTest.java
+++ /dev/null
@@ -1,149 +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.recorder;
-
-import static android.support.test.InstrumentationRegistry.getContext;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.support.test.filters.SdkSuppress;
-import android.support.test.filters.SmallTest;
-
-import com.android.tv.InputSessionManager;
-import com.android.tv.InputSessionManager.RecordingSession;
-import com.android.tv.common.feature.CommonFeatures;
-import com.android.tv.common.feature.TestableFeature;
-import com.android.tv.data.Channel;
-import com.android.tv.dvr.DvrDataManagerInMemoryImpl;
-import com.android.tv.dvr.DvrManager;
-import com.android.tv.dvr.data.ScheduledRecording;
-import com.android.tv.dvr.recorder.RecordingTask.State;
-import com.android.tv.testing.FakeClock;
-import com.android.tv.testing.dvr.RecordingTestUtils;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * Tests for {@link RecordingTask}.
- */
-@SmallTest
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
-public class RecordingTaskTest {
- private static final long DURATION = TimeUnit.MINUTES.toMillis(30);
- private static final long START_OFFSET_MS = RecordingScheduler.MS_TO_WAKE_BEFORE_START;
- private static final String INPUT_ID = "input_id";
- private static final int CHANNEL_ID = 273;
-
- private FakeClock mFakeClock;
- private DvrDataManagerInMemoryImpl mDataManager;
- @Mock Handler mMockHandler;
- @Mock DvrManager mDvrManager;
- @Mock InputSessionManager mMockSessionManager;
- @Mock RecordingSession mMockRecordingSession;
- private final TestableFeature mDvrFeature = CommonFeatures.DVR;
-
- @Before
- public void setUp() {
- mDvrFeature.enableForTest();
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- MockitoAnnotations.initMocks(this);
- mFakeClock = FakeClock.createWithCurrentTime();
- mDataManager = new DvrDataManagerInMemoryImpl(getContext(), mFakeClock);
- }
-
- @After
- public void tearDown() {
- mDvrFeature.resetForTests();
- }
-
- @Test
- public void testHandle_init() {
- Channel channel = createTestChannel();
- ScheduledRecording r = createRecording(channel);
- RecordingTask task = createRecordingTask(r, channel);
- String inputId = channel.getInputId();
- when(mMockSessionManager.createRecordingSession(eq(inputId), anyString(), eq(task),
- eq(mMockHandler), anyLong())).thenReturn(mMockRecordingSession);
- when(mMockHandler.sendMessageAtTime(anyObject(), anyLong())).thenReturn(true);
- assertTrue(task.handleMessage(createMessage(RecordingTask.MSG_INITIALIZE)));
- assertEquals(State.CONNECTION_PENDING, task.getState());
- verify(mMockSessionManager).createRecordingSession(eq(inputId), anyString(), eq(task),
- eq(mMockHandler), anyLong());
- verify(mMockRecordingSession).tune(eq(inputId), eq(channel.getUri()));
- verifyNoMoreInteractions(mMockHandler, mMockRecordingSession, mMockSessionManager);
- }
-
- private static Channel createTestChannel() {
- return new Channel.Builder().setInputId(INPUT_ID).setId(CHANNEL_ID)
- .setDisplayName("Test Ch " + CHANNEL_ID).build();
- }
-
- @Test
- public void testOnConnected() {
- Channel channel = createTestChannel();
- ScheduledRecording r = createRecording(channel);
- mDataManager.addScheduledRecording(r);
- RecordingTask task = createRecordingTask(r, channel);
- String inputId = channel.getInputId();
- when(mMockSessionManager.createRecordingSession(eq(inputId), anyString(), eq(task),
- eq(mMockHandler), anyLong())).thenReturn(mMockRecordingSession);
- when(mMockHandler.sendMessageAtTime(anyObject(), anyLong())).thenReturn(true);
- task.handleMessage(createMessage(RecordingTask.MSG_INITIALIZE));
- task.onTuned(channel.getUri());
- assertEquals(State.CONNECTED, task.getState());
- }
-
- private ScheduledRecording createRecording(Channel c) {
- long startTime = mFakeClock.currentTimeMillis() + START_OFFSET_MS;
- long endTime = startTime + DURATION;
- return RecordingTestUtils.createTestRecordingWithPeriod(c.getInputId(), c.getId(),
- startTime, endTime);
- }
-
- private RecordingTask createRecordingTask(ScheduledRecording r, Channel channel) {
- RecordingTask recordingTask = new RecordingTask(getContext(), r, channel, mDvrManager,
- mMockSessionManager, mDataManager, mFakeClock);
- recordingTask.setHandler(mMockHandler);
- return recordingTask;
- }
-
- private Message createMessage(int what) {
- Message msg = new Message();
- msg.setTarget(mMockHandler);
- msg.what = what;
- return msg;
- }
-} \ No newline at end of file
diff --git a/tests/unit/src/com/android/tv/dvr/recorder/ScheduledProgramReaperTest.java b/tests/unit/src/com/android/tv/dvr/recorder/ScheduledProgramReaperTest.java
deleted file mode 100644
index ca72e13f..00000000
--- a/tests/unit/src/com/android/tv/dvr/recorder/ScheduledProgramReaperTest.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * 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.recorder;
-
-import static android.support.test.InstrumentationRegistry.getContext;
-import static org.junit.Assert.assertTrue;
-
-import android.os.Build;
-import android.support.test.filters.SdkSuppress;
-import android.support.test.filters.SmallTest;
-import android.test.MoreAsserts;
-
-import com.android.tv.common.feature.CommonFeatures;
-import com.android.tv.common.feature.TestableFeature;
-import com.android.tv.dvr.DvrDataManagerInMemoryImpl;
-import com.android.tv.dvr.DvrManager;
-import com.android.tv.dvr.data.ScheduledRecording;
-import com.android.tv.testing.FakeClock;
-import com.android.tv.testing.dvr.RecordingTestUtils;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * Tests for {@link ScheduledProgramReaper}.
- */
-@SmallTest
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
-public class ScheduledProgramReaperTest {
- private static final String INPUT_ID = "input_id";
- private static final int CHANNEL_ID = 273;
- private static final long DURATION = TimeUnit.HOURS.toMillis(1);
-
- private ScheduledProgramReaper mReaper;
- private FakeClock mFakeClock;
- private DvrDataManagerInMemoryImpl mDvrDataManager;
- @Mock private DvrManager mDvrManager;
- private final TestableFeature mDvrFeature = CommonFeatures.DVR;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mDvrFeature.enableForTest();
- mFakeClock = FakeClock.createWithTimeOne();
- mDvrDataManager = new DvrDataManagerInMemoryImpl(getContext(), mFakeClock);
- mReaper = new ScheduledProgramReaper(mDvrDataManager, mFakeClock);
- }
-
- @After
- public void tearDown() {
- mDvrFeature.resetForTests();
- }
-
- @Test
- public void testRun_noRecordings() {
- assertTrue(mDvrDataManager.getAllScheduledRecordings().isEmpty());
- mReaper.run();
- assertTrue(mDvrDataManager.getAllScheduledRecordings().isEmpty());
- }
-
- @Test
- public void testRun_oneRecordingsTomorrow() {
- ScheduledRecording recording = addNewScheduledRecordingForTomorrow();
- MoreAsserts
- .assertContentsInAnyOrder(mDvrDataManager.getAllScheduledRecordings(), recording);
- mReaper.run();
- MoreAsserts
- .assertContentsInAnyOrder(mDvrDataManager.getAllScheduledRecordings(), recording);
- }
-
- @Test
- public void testRun_oneRecordingsStarted() {
- ScheduledRecording recording = addNewScheduledRecordingForTomorrow();
- MoreAsserts
- .assertContentsInAnyOrder(mDvrDataManager.getAllScheduledRecordings(), recording);
- mFakeClock.increment(TimeUnit.DAYS);
- mReaper.run();
- MoreAsserts
- .assertContentsInAnyOrder(mDvrDataManager.getAllScheduledRecordings(), recording);
- }
-
- @Test
- 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);
- }
-
- @Test
- 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();
- assertTrue("Recordings after reaper at " + com.android.tv.util.Utils
- .toIsoDateTimeString(mFakeClock.currentTimeMillis()),
- mDvrDataManager.getAllScheduledRecordings().isEmpty());
- }
-
- private ScheduledRecording addNewScheduledRecordingForTomorrow() {
- long startTime = mFakeClock.currentTimeMillis() + TimeUnit.DAYS.toMillis(1);
- ScheduledRecording recording = RecordingTestUtils.createTestRecordingWithPeriod(INPUT_ID,
- CHANNEL_ID, startTime, startTime + DURATION);
- return mDvrDataManager.addScheduledRecordingInternal(
- ScheduledRecording.buildFrom(recording)
- .setState(ScheduledRecording.STATE_RECORDING_FINISHED).build());
- }
-}
diff --git a/tests/unit/src/com/android/tv/dvr/recorder/SchedulerTest.java b/tests/unit/src/com/android/tv/dvr/recorder/SchedulerTest.java
deleted file mode 100644
index a5154729..00000000
--- a/tests/unit/src/com/android/tv/dvr/recorder/SchedulerTest.java
+++ /dev/null
@@ -1,125 +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.recorder;
-
-import static android.support.test.InstrumentationRegistry.getTargetContext;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.verify;
-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.support.test.filters.SmallTest;
-
-import com.android.tv.InputSessionManager;
-import com.android.tv.common.feature.CommonFeatures;
-import com.android.tv.common.feature.TestableFeature;
-import com.android.tv.data.ChannelDataManager;
-import com.android.tv.dvr.DvrDataManagerInMemoryImpl;
-import com.android.tv.dvr.DvrManager;
-import com.android.tv.dvr.data.ScheduledRecording;
-import com.android.tv.testing.FakeClock;
-import com.android.tv.testing.dvr.RecordingTestUtils;
-import com.android.tv.util.TvInputManagerHelper;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * Tests for {@link RecordingScheduler}.
- */
-@SmallTest
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
-public class SchedulerTest {
- private static final String INPUT_ID = "input_id";
- private static final int CHANNEL_ID = 273;
-
- private FakeClock mFakeClock;
- private DvrDataManagerInMemoryImpl mDataManager;
- private RecordingScheduler mScheduler;
- @Mock DvrManager mDvrManager;
- @Mock InputSessionManager mSessionManager;
- @Mock AlarmManager mMockAlarmManager;
- @Mock ChannelDataManager mChannelDataManager;
- @Mock TvInputManagerHelper mInputManager;
- private final TestableFeature mDvrFeature = CommonFeatures.DVR;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mDvrFeature.enableForTest();
- mFakeClock = FakeClock.createWithCurrentTime();
- mDataManager = new DvrDataManagerInMemoryImpl(getTargetContext(), mFakeClock);
- Mockito.when(mChannelDataManager.isDbLoadFinished()).thenReturn(true);
- mScheduler = new RecordingScheduler(Looper.myLooper(), mDvrManager, mSessionManager, mDataManager,
- mChannelDataManager, mInputManager, getTargetContext(), mFakeClock,
- mMockAlarmManager);
- }
-
- @After
- public void tearDown() {
- mDvrFeature.resetForTests();
- }
-
- @Test
- public void testUpdate_none() {
- mScheduler.updateAndStartServiceIfNeeded();
- verifyZeroInteractions(mMockAlarmManager);
- }
-
- @Test
- public void testUpdate_nextIn12Hours() {
- long now = mFakeClock.currentTimeMillis();
- long startTime = now + TimeUnit.HOURS.toMillis(12);
- ScheduledRecording r = RecordingTestUtils
- .createTestRecordingWithPeriod(INPUT_ID, CHANNEL_ID, startTime,
- startTime + TimeUnit.HOURS.toMillis(1));
- mDataManager.addScheduledRecording(r);
- verify(mMockAlarmManager).setExactAndAllowWhileIdle(
- eq(AlarmManager.RTC_WAKEUP),
- eq(startTime - RecordingScheduler.MS_TO_WAKE_BEFORE_START),
- any(PendingIntent.class));
- Mockito.reset(mMockAlarmManager);
- mScheduler.updateAndStartServiceIfNeeded();
- verify(mMockAlarmManager).setExactAndAllowWhileIdle(
- eq(AlarmManager.RTC_WAKEUP),
- eq(startTime - RecordingScheduler.MS_TO_WAKE_BEFORE_START),
- any(PendingIntent.class));
- }
-
- @Test
- public void testStartsWithin() {
- long now = mFakeClock.currentTimeMillis();
- long startTime = now + 3;
- ScheduledRecording r = RecordingTestUtils
- .createTestRecordingWithPeriod(INPUT_ID, CHANNEL_ID, startTime, startTime + 100);
- assertFalse(mScheduler.startsWithin(r, 2));
- assertTrue(mScheduler.startsWithin(r, 3));
- }
-} \ No newline at end of file
diff --git a/tests/unit/src/com/android/tv/dvr/recorder/SeriesRecordingSchedulerTest.java b/tests/unit/src/com/android/tv/dvr/recorder/SeriesRecordingSchedulerTest.java
deleted file mode 100644
index 16fa1baf..00000000
--- a/tests/unit/src/com/android/tv/dvr/recorder/SeriesRecordingSchedulerTest.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * 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.recorder;
-
-import static android.support.test.InstrumentationRegistry.getContext;
-
-import android.os.Build;
-import android.support.test.filters.SdkSuppress;
-import android.support.test.filters.SmallTest;
-import android.test.MoreAsserts;
-import android.util.LongSparseArray;
-
-import com.android.tv.common.feature.CommonFeatures;
-import com.android.tv.common.feature.TestableFeature;
-import com.android.tv.data.Program;
-import com.android.tv.dvr.DvrDataManagerInMemoryImpl;
-import com.android.tv.dvr.data.SeriesRecording;
-import com.android.tv.testing.FakeClock;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Tests for {@link SeriesRecordingScheduler}
- */
-@SmallTest
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
-public class SeriesRecordingSchedulerTest {
- private static final String PROGRAM_TITLE = "MyProgram";
- private static final long CHANNEL_ID = 123;
- private static final long SERIES_RECORDING_ID1 = 1;
- private static final String SERIES_ID = "SERIES_ID";
- private static final String SEASON_NUMBER1 = "SEASON NUMBER1";
- private static final String SEASON_NUMBER2 = "SEASON NUMBER2";
- private static final String EPISODE_NUMBER1 = "EPISODE NUMBER1";
- private static final String EPISODE_NUMBER2 = "EPISODE NUMBER2";
-
- private final SeriesRecording mBaseSeriesRecording = new SeriesRecording.Builder()
- .setTitle(PROGRAM_TITLE).setChannelId(CHANNEL_ID).setSeriesId(SERIES_ID).build();
- private final Program mBaseProgram = new Program.Builder().setTitle(PROGRAM_TITLE)
- .setChannelId(CHANNEL_ID).setSeriesId(SERIES_ID).build();
- private final TestableFeature mDvrFeature = CommonFeatures.DVR;
-
- private DvrDataManagerInMemoryImpl mDataManager;
-
- @Before
- public void setUp() {
- mDvrFeature.enableForTest();
- FakeClock fakeClock = FakeClock.createWithCurrentTime();
- mDataManager = new DvrDataManagerInMemoryImpl(getContext(), fakeClock);
- }
-
- @After
- public void tearDown() {
- mDvrFeature.resetForTests();
- }
-
- @Test
- public void testPickOneProgramPerEpisode_onePerEpisode() {
- SeriesRecording seriesRecording = SeriesRecording.buildFrom(mBaseSeriesRecording)
- .setId(SERIES_RECORDING_ID1).build();
- mDataManager.addSeriesRecording(seriesRecording);
- List<Program> programs = new ArrayList<>();
- Program program1 = new Program.Builder(mBaseProgram).setSeasonNumber(SEASON_NUMBER1)
- .setEpisodeNumber(EPISODE_NUMBER1).build();
- programs.add(program1);
- Program program2 = new Program.Builder(mBaseProgram).setSeasonNumber(SEASON_NUMBER2)
- .setEpisodeNumber(EPISODE_NUMBER2).build();
- programs.add(program2);
- LongSparseArray<List<Program>> result = SeriesRecordingScheduler.pickOneProgramPerEpisode(
- mDataManager, Collections.singletonList(seriesRecording), programs);
- MoreAsserts.assertContentsInAnyOrder(result.get(SERIES_RECORDING_ID1), program1, program2);
- }
-
- @Test
- public void testPickOneProgramPerEpisode_manyPerEpisode() {
- SeriesRecording seriesRecording = SeriesRecording.buildFrom(mBaseSeriesRecording)
- .setId(SERIES_RECORDING_ID1).build();
- mDataManager.addSeriesRecording(seriesRecording);
- List<Program> programs = new ArrayList<>();
- Program program1 = new Program.Builder(mBaseProgram).setSeasonNumber(SEASON_NUMBER1)
- .setEpisodeNumber(EPISODE_NUMBER1).setStartTimeUtcMillis(0).build();
- programs.add(program1);
- Program program2 = new Program.Builder(program1).setStartTimeUtcMillis(1).build();
- programs.add(program2);
- Program program3 = new Program.Builder(mBaseProgram).setSeasonNumber(SEASON_NUMBER2)
- .setEpisodeNumber(EPISODE_NUMBER2).build();
- programs.add(program3);
- Program program4 = new Program.Builder(program1).setStartTimeUtcMillis(1).build();
- programs.add(program4);
- LongSparseArray<List<Program>> result = SeriesRecordingScheduler.pickOneProgramPerEpisode(
- mDataManager, Collections.singletonList(seriesRecording), programs);
- MoreAsserts.assertContentsInAnyOrder(result.get(SERIES_RECORDING_ID1), program1, program3);
- }
-
- @Test
- public void testPickOneProgramPerEpisode_nullEpisode() {
- SeriesRecording seriesRecording = SeriesRecording.buildFrom(mBaseSeriesRecording)
- .setId(SERIES_RECORDING_ID1).build();
- mDataManager.addSeriesRecording(seriesRecording);
- List<Program> programs = new ArrayList<>();
- Program program1 = new Program.Builder(mBaseProgram).setStartTimeUtcMillis(0).build();
- programs.add(program1);
- Program program2 = new Program.Builder(mBaseProgram).setStartTimeUtcMillis(1).build();
- programs.add(program2);
- LongSparseArray<List<Program>> result = SeriesRecordingScheduler.pickOneProgramPerEpisode(
- mDataManager, Collections.singletonList(seriesRecording), programs);
- MoreAsserts.assertContentsInAnyOrder(result.get(SERIES_RECORDING_ID1), program1, program2);
- }
-}
diff --git a/tests/unit/src/com/android/tv/dvr/ui/SortedArrayAdapterTest.java b/tests/unit/src/com/android/tv/dvr/ui/SortedArrayAdapterTest.java
deleted file mode 100644
index 5667ee6b..00000000
--- a/tests/unit/src/com/android/tv/dvr/ui/SortedArrayAdapterTest.java
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * 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 org.junit.Before;
-import org.junit.Test;
-
-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, "c");
- public static final TestData P2 = TestData.create(2, "b");
- public static final TestData P3 = TestData.create(3, "a");
- public static final TestData EXTRA = TestData.create(4, "k");
- private TestSortedArrayAdapter mAdapter;
-
- @Before
- public void setUp() {
- mAdapter = new TestSortedArrayAdapter(Integer.MAX_VALUE, null);
- }
-
- @Test
- public void testContents_empty() {
- assertEmpty();
- }
-
- @Test
- public void testAdd_one() {
- mAdapter.add(P1);
- assertNotEmpty();
- assertContentsInOrder(mAdapter, P1);
- }
-
- @Test
- public void testAdd_two() {
- mAdapter.add(P1);
- mAdapter.add(P2);
- assertNotEmpty();
- assertContentsInOrder(mAdapter, P2, P1);
- }
-
- @Test
- public void testSetInitialItems_two() {
- mAdapter.setInitialItems(Arrays.asList(P1, P2));
- assertNotEmpty();
- assertContentsInOrder(mAdapter, P2, P1);
- }
-
- @Test
- public void testMaxInitialCount() {
- mAdapter = new TestSortedArrayAdapter(1, null);
- mAdapter.setInitialItems(Arrays.asList(P1, P2));
- assertNotEmpty();
- assertEquals(mAdapter.size(), 1);
- assertEquals(mAdapter.get(0), P2);
- }
-
- @Test
- public void testExtraItem() {
- mAdapter = new TestSortedArrayAdapter(Integer.MAX_VALUE, EXTRA);
- mAdapter.setInitialItems(Arrays.asList(P1, P2));
- assertNotEmpty();
- assertEquals(mAdapter.size(), 3);
- assertEquals(mAdapter.get(0), P2);
- assertEquals(mAdapter.get(2), EXTRA);
- mAdapter.remove(P2);
- mAdapter.remove(P1);
- assertEquals(mAdapter.size(), 1);
- assertEquals(mAdapter.get(0), EXTRA);
- }
-
- @Test
- public void testExtraItemWithMaxCount() {
- mAdapter = new TestSortedArrayAdapter(1, EXTRA);
- mAdapter.setInitialItems(Arrays.asList(P1, P2));
- assertNotEmpty();
- assertEquals(mAdapter.size(), 2);
- assertEquals(mAdapter.get(0), P2);
- assertEquals(mAdapter.get(1), EXTRA);
- mAdapter.remove(P2);
- assertEquals(mAdapter.size(), 1);
- assertEquals(mAdapter.get(0), EXTRA);
- }
-
- @Test
- 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();
- mAdapter.add(P1);
- mAdapter.add(P2);
- mAdapter.add(P3);
- assertContentsInOrder(mAdapter, P3, P2, P1);
- mAdapter.removeItems(0, 2);
- assertContentsInOrder(mAdapter, P1);
- mAdapter.add(P2);
- mAdapter.add(P3);
- mAdapter.addExtraItem(EXTRA);
- assertContentsInOrder(mAdapter, P3, P2, P1, EXTRA);
- mAdapter.removeItems(1, 1);
- assertContentsInOrder(mAdapter, P3, P1, EXTRA);
- mAdapter.removeItems(1, 2);
- assertContentsInOrder(mAdapter, P3);
- mAdapter.addExtraItem(EXTRA);
- mAdapter.addExtraItem(P2);
- mAdapter.add(P1);
- assertContentsInOrder(mAdapter, P3, P1, EXTRA, P2);
- mAdapter.removeItems(1, 2);
- assertContentsInOrder(mAdapter, P3, P2);
- mAdapter.add(P1);
- assertContentsInOrder(mAdapter, P3, P1, P2);
- }
-
- @Test
- public void testReplace() {
- mAdapter.add(P1);
- mAdapter.add(P2);
- assertNotEmpty();
- assertContentsInOrder(mAdapter, P2, P1);
- mAdapter.replace(1, P3);
- assertContentsInOrder(mAdapter, P3, P2);
- mAdapter.replace(0, P1);
- assertContentsInOrder(mAdapter, P2, P1);
- mAdapter.addExtraItem(EXTRA);
- assertContentsInOrder(mAdapter, P2, P1, EXTRA);
- mAdapter.replace(2, P3);
- assertContentsInOrder(mAdapter, P2, P1, P3);
- }
-
- @Test
- 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);
- }
-
- @Test
- public void testChange_new() {
- mAdapter.change(P1);
- assertNotEmpty();
- assertContentsInOrder(mAdapter, P1);
- }
-
- private void assertEmpty() {
- assertEquals("empty", true, mAdapter.isEmpty());
- }
-
- 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(int maxInitialCount, Object extra) {
- super(new ClassPresenterSelector(), TEXT_COMPARATOR, maxInitialCount);
- if (extra != null) {
- addExtraItem((TestData) extra);
- }
- }
-
- @Override
- protected long getId(TestData item) {
- return item.mId;
- }
- }
-} \ No newline at end of file
diff --git a/tests/unit/src/com/android/tv/experiments/ExperimentsTest.java b/tests/unit/src/com/android/tv/experiments/ExperimentsTest.java
deleted file mode 100644
index 3f827ce1..00000000
--- a/tests/unit/src/com/android/tv/experiments/ExperimentsTest.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.experiments;
-
-import static org.junit.Assert.assertEquals;
-
-import android.support.test.filters.SmallTest;
-
-import com.android.tv.common.BuildConfig;
-
-import junit.framework.Assert;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Tests for {@link Experiments}.
- */
-@SmallTest
-public class ExperimentsTest {
- @Before
- public void setUp() {
- ExperimentFlag.initForTest();
- }
-
-
- @Test
- public void testEngOnlyDefault() {
- assertEquals("ENABLE_DEVELOPER_FEATURES", Boolean.valueOf(BuildConfig.ENG),
- Experiments.ENABLE_DEVELOPER_FEATURES.get());
- }
-
-
-}
diff --git a/tests/unit/src/com/android/tv/menu/MenuTest.java b/tests/unit/src/com/android/tv/menu/MenuTest.java
index e8cfdbef..028a185d 100644
--- a/tests/unit/src/com/android/tv/menu/MenuTest.java
+++ b/tests/unit/src/com/android/tv/menu/MenuTest.java
@@ -16,24 +16,23 @@
package com.android.tv.menu;
import static android.support.test.InstrumentationRegistry.getTargetContext;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertWithMessage;
import android.support.test.filters.SmallTest;
-
+import android.support.test.runner.AndroidJUnit4;
import com.android.tv.menu.Menu.OnMenuVisibilityChangeListener;
-
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Matchers;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
-/**
- * Tests for {@link Menu}.
- */
+/** Tests for {@link Menu}. */
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class MenuTest {
private Menu mMenu;
private IMenuView mMenuView;
@@ -50,26 +49,27 @@ public class MenuTest {
mMenu.disableAnimationForTest();
}
+ @Ignore("b/73727914")
@Test
public void testScheduleHide() {
mMenu.show(Menu.REASON_NONE);
setMenuVisible(true);
- assertTrue("Hide is not scheduled", mMenu.isHideScheduled());
+ assertWithMessage("Hide is not scheduled").that(mMenu.isHideScheduled()).isTrue();
mMenu.hide(false);
setMenuVisible(false);
- assertFalse("Hide is scheduled", mMenu.isHideScheduled());
+ assertWithMessage("Hide is scheduled").that(mMenu.isHideScheduled()).isFalse();
mMenu.setKeepVisible(true);
mMenu.show(Menu.REASON_NONE);
setMenuVisible(true);
- assertFalse("Hide is scheduled", mMenu.isHideScheduled());
+ assertWithMessage("Hide is scheduled").that(mMenu.isHideScheduled()).isFalse();
mMenu.setKeepVisible(false);
- assertTrue("Hide is not scheduled", mMenu.isHideScheduled());
+ assertWithMessage("Hide is not scheduled").that(mMenu.isHideScheduled()).isTrue();
mMenu.setKeepVisible(true);
- assertFalse("Hide is scheduled", mMenu.isHideScheduled());
+ assertWithMessage("Hide is scheduled").that(mMenu.isHideScheduled()).isFalse();
mMenu.hide(false);
setMenuVisible(false);
- assertFalse("Hide is scheduled", mMenu.isHideScheduled());
+ assertWithMessage("Hide is scheduled").that(mMenu.isHideScheduled()).isFalse();
}
@Test
@@ -83,8 +83,11 @@ public class MenuTest {
Mockito.verify(mVisibilityChangeListener, Mockito.never())
.onMenuVisibilityChange(Matchers.eq(false));
// IMenuView.show should be called with the same parameter.
- Mockito.verify(mMenuView).onShow(Matchers.eq(Menu.REASON_NONE),
- Matchers.isNull(String.class), Matchers.isNull(Runnable.class));
+ Mockito.verify(mMenuView)
+ .onShow(
+ Matchers.eq(Menu.REASON_NONE),
+ Matchers.isNull(String.class),
+ Matchers.isNull(Runnable.class));
mMenu.hide(true);
setMenuVisible(false);
// Listener should be called with "false" argument.
@@ -104,8 +107,11 @@ public class MenuTest {
Mockito.verify(mVisibilityChangeListener, Mockito.never())
.onMenuVisibilityChange(Matchers.eq(false));
// IMenuView.show should be called with the same parameter.
- Mockito.verify(mMenuView).onShow(Matchers.eq(Menu.REASON_GUIDE),
- Matchers.eq(ChannelsRow.ID), Matchers.isNull(Runnable.class));
+ Mockito.verify(mMenuView)
+ .onShow(
+ Matchers.eq(Menu.REASON_GUIDE),
+ Matchers.eq(ChannelsRow.ID),
+ Matchers.isNull(Runnable.class));
mMenu.hide(false);
setMenuVisible(false);
// Listener should be called with "false" argument.
@@ -125,8 +131,11 @@ public class MenuTest {
Mockito.verify(mVisibilityChangeListener, Mockito.never())
.onMenuVisibilityChange(Matchers.eq(false));
// IMenuView.show should be called with the same parameter.
- Mockito.verify(mMenuView).onShow(Matchers.eq(Menu.REASON_PLAY_CONTROLS_FAST_FORWARD),
- Matchers.eq(PlayControlsRow.ID), Matchers.isNull(Runnable.class));
+ Mockito.verify(mMenuView)
+ .onShow(
+ Matchers.eq(Menu.REASON_PLAY_CONTROLS_FAST_FORWARD),
+ Matchers.eq(PlayControlsRow.ID),
+ Matchers.isNull(Runnable.class));
mMenu.hide(false);
setMenuVisible(false);
// Listener should be called with "false" argument.
@@ -136,11 +145,13 @@ public class MenuTest {
}
private void setMenuVisible(final boolean visible) {
- Mockito.when(mMenuView.isVisible()).thenAnswer(new Answer<Boolean>() {
- @Override
- public Boolean answer(InvocationOnMock invocation) throws Throwable {
- return visible;
- }
- });
+ Mockito.when(mMenuView.isVisible())
+ .thenAnswer(
+ new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ return visible;
+ }
+ });
}
}
diff --git a/tests/unit/src/com/android/tv/menu/TvOptionsRowAdapterTest.java b/tests/unit/src/com/android/tv/menu/TvOptionsRowAdapterTest.java
index 49ba8514..0f815a7a 100644
--- a/tests/unit/src/com/android/tv/menu/TvOptionsRowAdapterTest.java
+++ b/tests/unit/src/com/android/tv/menu/TvOptionsRowAdapterTest.java
@@ -16,30 +16,28 @@
package com.android.tv.menu;
import static android.support.test.InstrumentationRegistry.getInstrumentation;
-import static org.junit.Assert.assertEquals;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.fail;
import android.media.tv.TvTrackInfo;
import android.os.SystemClock;
import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
import android.text.TextUtils;
-
-import com.android.tv.BaseMainActivityTestCase;
-import com.android.tv.testing.Constants;
+import com.android.tv.testing.activities.BaseMainActivityTestCase;
+import com.android.tv.testing.constants.Constants;
import com.android.tv.testing.testinput.ChannelState;
import com.android.tv.testing.testinput.ChannelStateData;
import com.android.tv.testing.testinput.TvTestInputConstants;
-
-import org.junit.Before;
-import org.junit.Test;
-
import java.util.Collections;
import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
-/**
- * Tests for {@link TvOptionsRowAdapter}.
- */
+/** Tests for {@link TvOptionsRowAdapter}. */
@MediumTest
+@RunWith(AndroidJUnit4.class)
public class TvOptionsRowAdapterTest extends BaseMainActivityTestCase {
private static final int WAIT_TRACK_EVENT_TIMEOUT_MS = 300;
public static final int TRACK_CHECK_INTERVAL_MS = 10;
@@ -56,12 +54,14 @@ public class TvOptionsRowAdapterTest extends BaseMainActivityTestCase {
waitUntilAudioTracksHaveSize(1);
waitUntilAudioTrackSelected(ChannelState.DEFAULT.getSelectedAudioTrackId());
// update should be called on the main thread to avoid the multi-thread problem.
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mTvOptionsRowAdapter.update();
- }
- });
+ getInstrumentation()
+ .runOnMainSync(
+ new Runnable() {
+ @Override
+ public void run() {
+ mTvOptionsRowAdapter.update();
+ }
+ });
}
@Test
@@ -73,9 +73,10 @@ public class TvOptionsRowAdapterTest extends BaseMainActivityTestCase {
waitUntilAudioTrackSelected(Constants.EN_STEREO_AUDIO_TRACK.getId());
boolean result = mTvOptionsRowAdapter.updateMultiAudioAction();
- assertEquals("update Action had change", true, result);
- assertEquals("Multi Audio enabled", true,
- MenuAction.SELECT_AUDIO_LANGUAGE_ACTION.isEnabled());
+ assertWithMessage("update Action had change").that(result).isTrue();
+ assertWithMessage("Multi Audio enabled")
+ .that(MenuAction.SELECT_AUDIO_LANGUAGE_ACTION.isEnabled())
+ .isTrue();
}
@Test
@@ -90,9 +91,10 @@ public class TvOptionsRowAdapterTest extends BaseMainActivityTestCase {
waitUntilAudioTrackSelected(Constants.GENERIC_AUDIO_TRACK.getId());
boolean result = mTvOptionsRowAdapter.updateMultiAudioAction();
- assertEquals("update Action had change", true, result);
- assertEquals("Multi Audio enabled", false,
- MenuAction.SELECT_AUDIO_LANGUAGE_ACTION.isEnabled());
+ assertWithMessage("update Action had change").that(result).isTrue();
+ assertWithMessage("Multi Audio enabled")
+ .that(MenuAction.SELECT_AUDIO_LANGUAGE_ACTION.isEnabled())
+ .isFalse();
}
@Test
@@ -108,9 +110,10 @@ public class TvOptionsRowAdapterTest extends BaseMainActivityTestCase {
waitUntilVideoTrackSelected(data.mSelectedVideoTrackId);
boolean result = mTvOptionsRowAdapter.updateMultiAudioAction();
- assertEquals("update Action had change", true, result);
- assertEquals("Multi Audio enabled", false,
- MenuAction.SELECT_AUDIO_LANGUAGE_ACTION.isEnabled());
+ assertWithMessage("update Action had change").that(result).isTrue();
+ assertWithMessage("Multi Audio enabled")
+ .that(MenuAction.SELECT_AUDIO_LANGUAGE_ACTION.isEnabled())
+ .isFalse();
}
private void waitUntilAudioTracksHaveSize(int expected) {
@@ -135,8 +138,13 @@ public class TvOptionsRowAdapterTest extends BaseMainActivityTestCase {
}
SystemClock.sleep(TRACK_CHECK_INTERVAL_MS);
}
- fail("Waited for " + WAIT_TRACK_EVENT_TIMEOUT_MS + " milliseconds for track size to be "
- + expected + " but was " + size);
+ fail(
+ "Waited for "
+ + WAIT_TRACK_EVENT_TIMEOUT_MS
+ + " milliseconds for track size to be "
+ + expected
+ + " but was "
+ + size);
}
private void waitUntilAudioTrackSelected(String trackId) {
@@ -158,7 +166,12 @@ public class TvOptionsRowAdapterTest extends BaseMainActivityTestCase {
}
SystemClock.sleep(TRACK_CHECK_INTERVAL_MS);
}
- fail("Waited for " + WAIT_TRACK_EVENT_TIMEOUT_MS + " milliseconds for track ID to be "
- + trackId + " but was " + selectedTrackId);
+ fail(
+ "Waited for "
+ + WAIT_TRACK_EVENT_TIMEOUT_MS
+ + " milliseconds for track ID to be "
+ + trackId
+ + " but was "
+ + selectedTrackId);
}
}
diff --git a/tests/unit/src/com/android/tv/recommendation/ChannelRecordTest.java b/tests/unit/src/com/android/tv/recommendation/ChannelRecordTest.java
index db765109..e63bdc3a 100644
--- a/tests/unit/src/com/android/tv/recommendation/ChannelRecordTest.java
+++ b/tests/unit/src/com/android/tv/recommendation/ChannelRecordTest.java
@@ -17,22 +17,20 @@
package com.android.tv.recommendation;
import static android.support.test.InstrumentationRegistry.getContext;
-import static org.junit.Assert.assertEquals;
+import static com.google.common.truth.Truth.assertThat;
import android.support.test.filters.SmallTest;
-
-import com.android.tv.testing.Utils;
-
-import org.junit.Before;
-import org.junit.Test;
-
+import android.support.test.runner.AndroidJUnit4;
+import com.android.tv.testing.utils.Utils;
import java.util.Random;
import java.util.concurrent.TimeUnit;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
-/**
- * Unit tests for {@link ChannelRecord}.
- */
+/** Unit tests for {@link ChannelRecord}. */
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class ChannelRecordTest {
private static final int CHANNEL_RECORD_MAX_HISTORY_SIZE = ChannelRecord.MAX_HISTORY_SIZE;
@@ -49,14 +47,14 @@ public class ChannelRecordTest {
@Test
public void testGetLastWatchEndTime_noHistory() {
- assertEquals(0, mChannelRecord.getLastWatchEndTimeMs());
+ assertThat(mChannelRecord.getLastWatchEndTimeMs()).isEqualTo(0);
}
@Test
public void testGetLastWatchEndTime_oneHistory() {
addWatchLog();
- assertEquals(mLatestWatchEndTimeMs, mChannelRecord.getLastWatchEndTimeMs());
+ assertThat(mChannelRecord.getLastWatchEndTimeMs()).isEqualTo(mLatestWatchEndTimeMs);
}
@Test
@@ -65,7 +63,7 @@ public class ChannelRecordTest {
addWatchLog();
}
- assertEquals(mLatestWatchEndTimeMs, mChannelRecord.getLastWatchEndTimeMs());
+ assertThat(mChannelRecord.getLastWatchEndTimeMs()).isEqualTo(mLatestWatchEndTimeMs);
}
@Test
@@ -74,19 +72,19 @@ public class ChannelRecordTest {
addWatchLog();
}
- assertEquals(mLatestWatchEndTimeMs, mChannelRecord.getLastWatchEndTimeMs());
+ assertThat(mChannelRecord.getLastWatchEndTimeMs()).isEqualTo(mLatestWatchEndTimeMs);
}
@Test
public void testGetTotalWatchDuration_noHistory() {
- assertEquals(0, mChannelRecord.getTotalWatchDurationMs());
+ assertThat(mChannelRecord.getTotalWatchDurationMs()).isEqualTo(0);
}
@Test
public void testGetTotalWatchDuration_oneHistory() {
long durationMs = addWatchLog();
- assertEquals(durationMs, mChannelRecord.getTotalWatchDurationMs());
+ assertThat(mChannelRecord.getTotalWatchDurationMs()).isEqualTo(durationMs);
}
@Test
@@ -97,7 +95,7 @@ public class ChannelRecordTest {
totalWatchTimeMs += durationMs;
}
- assertEquals(totalWatchTimeMs, mChannelRecord.getTotalWatchDurationMs());
+ assertThat(mChannelRecord.getTotalWatchDurationMs()).isEqualTo(totalWatchTimeMs);
}
@Test
@@ -112,8 +110,9 @@ public class ChannelRecordTest {
}
}
- // Only latest CHANNEL_RECORD_MAX_HISTORY_SIZE logs are remained.
- assertEquals(totalWatchTimeMs - firstDurationMs, mChannelRecord.getTotalWatchDurationMs());
+ // Only latest CHANNEL_RECORD_MAX_HISTORY_SIZE logs are remained.
+ assertThat(mChannelRecord.getTotalWatchDurationMs())
+ .isEqualTo(totalWatchTimeMs - firstDurationMs);
}
/**
@@ -126,8 +125,9 @@ public class ChannelRecordTest {
mLatestWatchEndTimeMs += TimeUnit.SECONDS.toMillis(mRandom.nextInt(60) + 1);
long durationMs = TimeUnit.SECONDS.toMillis(mRandom.nextInt(60) + 1);
- mChannelRecord.logWatchHistory(new WatchedProgram(null,
- mLatestWatchEndTimeMs, mLatestWatchEndTimeMs + durationMs));
+ mChannelRecord.logWatchHistory(
+ new WatchedProgram(
+ null, mLatestWatchEndTimeMs, mLatestWatchEndTimeMs + durationMs));
mLatestWatchEndTimeMs += durationMs;
return durationMs;
diff --git a/tests/unit/src/com/android/tv/recommendation/EvaluatorTestCase.java b/tests/unit/src/com/android/tv/recommendation/EvaluatorTestCase.java
index 853fb245..f62a5e05 100644
--- a/tests/unit/src/com/android/tv/recommendation/EvaluatorTestCase.java
+++ b/tests/unit/src/com/android/tv/recommendation/EvaluatorTestCase.java
@@ -20,19 +20,15 @@ import static android.support.test.InstrumentationRegistry.getContext;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import com.android.tv.data.Channel;
+import com.android.tv.data.api.Channel;
import com.android.tv.recommendation.RecommendationUtils.ChannelRecordSortedMapHelper;
import com.android.tv.recommendation.Recommender.Evaluator;
-import com.android.tv.testing.Utils;
-
-import org.junit.Before;
-
+import com.android.tv.testing.utils.Utils;
import java.util.ArrayList;
import java.util.List;
+import org.junit.Before;
-/**
- * Base test case for Recommendation Evaluator Unit tests.
- */
+/** Base test case for Recommendation Evaluator Unit tests. */
public abstract class EvaluatorTestCase<T extends Evaluator> {
private static final long INVALID_CHANNEL_ID = -1;
@@ -46,8 +42,8 @@ public abstract class EvaluatorTestCase<T extends Evaluator> {
@Before
public void setUp() {
mChannelRecordSortedMap = new ChannelRecordSortedMapHelper(getContext());
- mDataManager = RecommendationUtils
- .createMockRecommendationDataManager(mChannelRecordSortedMap);
+ mDataManager =
+ RecommendationUtils.createMockRecommendationDataManager(mChannelRecordSortedMap);
Recommender mRecommender = new FakeRecommender();
mEvaluator = createEvaluator();
mEvaluator.setRecommender(mRecommender);
@@ -55,9 +51,7 @@ public abstract class EvaluatorTestCase<T extends Evaluator> {
mChannelRecordSortedMap.resetRandom(Utils.createTestRandom());
}
- /**
- * Each evaluator test has to create Evaluator in {@code mEvaluator}.
- */
+ /** Each evaluator test has to create Evaluator in {@code mEvaluator}. */
public abstract T createEvaluator();
public void addChannels(int numberOfChannels) {
@@ -68,15 +62,16 @@ public abstract class EvaluatorTestCase<T extends Evaluator> {
return mChannelRecordSortedMap.addChannel();
}
- public void addRandomWatchLogs(long watchStartTimeMs, long watchEndTimeMs,
- long maxWatchDurationMs) {
- assertTrue(mChannelRecordSortedMap.addRandomWatchLogs(watchStartTimeMs, watchEndTimeMs,
- maxWatchDurationMs));
+ public void addRandomWatchLogs(
+ long watchStartTimeMs, long watchEndTimeMs, long maxWatchDurationMs) {
+ assertTrue(
+ mChannelRecordSortedMap.addRandomWatchLogs(
+ watchStartTimeMs, watchEndTimeMs, maxWatchDurationMs));
}
public void addWatchLog(long channelId, long watchStartTimeMs, long durationTimeMs) {
- assertTrue(mChannelRecordSortedMap.addWatchLog(channelId, watchStartTimeMs,
- durationTimeMs));
+ assertTrue(
+ mChannelRecordSortedMap.addWatchLog(channelId, watchStartTimeMs, durationTimeMs));
}
public List<Long> getChannelIdListSorted() {
@@ -86,31 +81,29 @@ public abstract class EvaluatorTestCase<T extends Evaluator> {
public long getLatestWatchEndTimeMs() {
long latestWatchEndTimeMs = 0;
for (ChannelRecord channelRecord : mChannelRecordSortedMap.values()) {
- latestWatchEndTimeMs = Math.max(latestWatchEndTimeMs,
- channelRecord.getLastWatchEndTimeMs());
+ latestWatchEndTimeMs =
+ Math.max(latestWatchEndTimeMs, channelRecord.getLastWatchEndTimeMs());
}
return latestWatchEndTimeMs;
}
- /**
- * Check whether scores of each channels are valid.
- */
+ /** Check whether scores of each channels are valid. */
protected void assertChannelScoresValid() {
- assertEqualScores(Evaluator.NOT_RECOMMENDED,
- mEvaluator.evaluateChannel(INVALID_CHANNEL_ID));
- assertEqualScores(Evaluator.NOT_RECOMMENDED,
+ assertEqualScores(
+ Evaluator.NOT_RECOMMENDED, mEvaluator.evaluateChannel(INVALID_CHANNEL_ID));
+ assertEqualScores(
+ Evaluator.NOT_RECOMMENDED,
mEvaluator.evaluateChannel(mChannelRecordSortedMap.size()));
for (long channelId : mChannelRecordSortedMap.keySet()) {
double score = mEvaluator.evaluateChannel(channelId);
- assertTrue("Channel " + channelId + " score of " + score + "is not valid",
+ assertTrue(
+ "Channel " + channelId + " score of " + score + "is not valid",
score == Evaluator.NOT_RECOMMENDED || (0.0 <= score && score <= 1.0));
}
}
- /**
- * Notify that loading channels and watch logs are finished.
- */
+ /** Notify that loading channels and watch logs are finished. */
protected void notifyChannelAndWatchLogLoaded() {
mEvaluator.onChannelRecordListChanged(new ArrayList<>(mChannelRecordSortedMap.values()));
}
@@ -125,15 +118,16 @@ public abstract class EvaluatorTestCase<T extends Evaluator> {
private class FakeRecommender extends Recommender {
public FakeRecommender() {
- super(new Recommender.Listener() {
- @Override
- public void onRecommenderReady() {
- }
-
- @Override
- public void onRecommendationChanged() {
- }
- }, true, mDataManager);
+ super(
+ new Recommender.Listener() {
+ @Override
+ public void onRecommenderReady() {}
+
+ @Override
+ public void onRecommendationChanged() {}
+ },
+ true,
+ mDataManager);
}
@Override
diff --git a/tests/unit/src/com/android/tv/recommendation/FavoriteChannelEvaluatorTest.java b/tests/unit/src/com/android/tv/recommendation/FavoriteChannelEvaluatorTest.java
index ac701af9..e14320f0 100644
--- a/tests/unit/src/com/android/tv/recommendation/FavoriteChannelEvaluatorTest.java
+++ b/tests/unit/src/com/android/tv/recommendation/FavoriteChannelEvaluatorTest.java
@@ -16,19 +16,18 @@
package com.android.tv.recommendation;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
import android.support.test.filters.SmallTest;
-
-import org.junit.Test;
-
+import android.support.test.runner.AndroidJUnit4;
import java.util.List;
import java.util.concurrent.TimeUnit;
+import org.junit.Test;
+import org.junit.runner.RunWith;
-/**
- * Unit tests for {@link FavoriteChannelEvaluator}.
- */
+/** Unit tests for {@link FavoriteChannelEvaluator}. */
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class FavoriteChannelEvaluatorTest extends EvaluatorTestCase<FavoriteChannelEvaluator> {
private static final int DEFAULT_NUMBER_OF_CHANNELS = 4;
private static final long DEFAULT_WATCH_START_TIME_MS =
@@ -47,14 +46,16 @@ public class FavoriteChannelEvaluatorTest extends EvaluatorTestCase<FavoriteChan
long channelId = addChannel().getId();
notifyChannelAndWatchLogLoaded();
- assertEqualScores(Recommender.Evaluator.NOT_RECOMMENDED,
- mEvaluator.evaluateChannel(channelId));
+ assertEqualScores(
+ Recommender.Evaluator.NOT_RECOMMENDED, mEvaluator.evaluateChannel(channelId));
}
@Test
public void testOneChannelWithRandomWatchLogs() {
addChannel();
- addRandomWatchLogs(DEFAULT_WATCH_START_TIME_MS, DEFAULT_WATCH_END_TIME_MS,
+ addRandomWatchLogs(
+ DEFAULT_WATCH_START_TIME_MS,
+ DEFAULT_WATCH_END_TIME_MS,
DEFAULT_MAX_WATCH_DURATION_MS);
notifyChannelAndWatchLogLoaded();
@@ -68,15 +69,17 @@ public class FavoriteChannelEvaluatorTest extends EvaluatorTestCase<FavoriteChan
List<Long> channelIdList = getChannelIdListSorted();
for (long channelId : channelIdList) {
- assertEqualScores(Recommender.Evaluator.NOT_RECOMMENDED,
- mEvaluator.evaluateChannel(channelId));
+ assertEqualScores(
+ Recommender.Evaluator.NOT_RECOMMENDED, mEvaluator.evaluateChannel(channelId));
}
}
@Test
public void testMultiChannelsWithRandomWatchLogs() {
addChannels(DEFAULT_NUMBER_OF_CHANNELS);
- addRandomWatchLogs(DEFAULT_WATCH_START_TIME_MS, DEFAULT_WATCH_END_TIME_MS,
+ addRandomWatchLogs(
+ DEFAULT_WATCH_START_TIME_MS,
+ DEFAULT_WATCH_END_TIME_MS,
DEFAULT_MAX_WATCH_DURATION_MS);
notifyChannelAndWatchLogLoaded();
@@ -103,7 +106,7 @@ public class FavoriteChannelEvaluatorTest extends EvaluatorTestCase<FavoriteChan
double previousScore = Recommender.Evaluator.NOT_RECOMMENDED;
for (long channelId : channelIdList) {
double score = mEvaluator.evaluateChannel(channelId);
- assertTrue(previousScore <= score);
+ assertThat(previousScore).isAtMost(score);
previousScore = score;
}
}
@@ -112,40 +115,54 @@ public class FavoriteChannelEvaluatorTest extends EvaluatorTestCase<FavoriteChan
public void testTwoChannelsWithSameWatchDuration() {
long channelOne = addChannel().getId();
long channelTwo = addChannel().getId();
- addWatchLog(channelOne, System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1),
+ addWatchLog(
+ channelOne,
+ System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1),
TimeUnit.MINUTES.toMillis(30));
- addWatchLog(channelTwo, System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(30),
+ addWatchLog(
+ channelTwo,
+ System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(30),
TimeUnit.MINUTES.toMillis(30));
notifyChannelAndWatchLogLoaded();
- assertTrue(mEvaluator.evaluateChannel(channelOne) ==
- mEvaluator.evaluateChannel(channelTwo));
+ assertThat(mEvaluator.evaluateChannel(channelOne) == mEvaluator.evaluateChannel(channelTwo))
+ .isTrue();
}
@Test
public void testTwoChannelsWithDifferentWatchDuration() {
long channelOne = addChannel().getId();
long channelTwo = addChannel().getId();
- addWatchLog(channelOne, System.currentTimeMillis() - TimeUnit.HOURS.toMillis(3),
+ addWatchLog(
+ channelOne,
+ System.currentTimeMillis() - TimeUnit.HOURS.toMillis(3),
TimeUnit.MINUTES.toMillis(30));
- addWatchLog(channelTwo, System.currentTimeMillis() - TimeUnit.HOURS.toMillis(2),
+ addWatchLog(
+ channelTwo,
+ System.currentTimeMillis() - TimeUnit.HOURS.toMillis(2),
TimeUnit.HOURS.toMillis(1));
notifyChannelAndWatchLogLoaded();
- // Channel two was watched longer than channel one, so it's score is bigger.
- assertTrue(mEvaluator.evaluateChannel(channelOne) < mEvaluator.evaluateChannel(channelTwo));
+ // Channel two was watched longer than channel one, so it's score is bigger.
+ assertThat(mEvaluator.evaluateChannel(channelOne))
+ .isLessThan(mEvaluator.evaluateChannel(channelTwo));
- addWatchLog(channelOne, System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1),
+ addWatchLog(
+ channelOne,
+ System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1),
TimeUnit.HOURS.toMillis(1));
- // Now, channel one was watched longer than channel two, so it's score is bigger.
- assertTrue(mEvaluator.evaluateChannel(channelOne) > mEvaluator.evaluateChannel(channelTwo));
+ // Now, channel one was watched longer than channel two, so it's score is bigger.
+ assertThat(mEvaluator.evaluateChannel(channelOne))
+ .isGreaterThan(mEvaluator.evaluateChannel(channelTwo));
}
@Test
public void testScoreIncreasesWithNewWatchLog() {
long channelId = addChannel().getId();
- addRandomWatchLogs(DEFAULT_WATCH_START_TIME_MS, DEFAULT_WATCH_END_TIME_MS,
+ addRandomWatchLogs(
+ DEFAULT_WATCH_START_TIME_MS,
+ DEFAULT_WATCH_END_TIME_MS,
DEFAULT_MAX_WATCH_DURATION_MS);
notifyChannelAndWatchLogLoaded();
@@ -154,7 +171,7 @@ public class FavoriteChannelEvaluatorTest extends EvaluatorTestCase<FavoriteChan
addWatchLog(channelId, latestWatchEndTimeMs, TimeUnit.MINUTES.toMillis(10));
- // Score must be increased because total watch duration of the channel increases.
- assertTrue(previousScore <= mEvaluator.evaluateChannel(channelId));
+ // Score must be increased because total watch duration of the channel increases.
+ assertThat(previousScore).isAtMost(mEvaluator.evaluateChannel(channelId));
}
}
diff --git a/tests/unit/src/com/android/tv/recommendation/RecentChannelEvaluatorTest.java b/tests/unit/src/com/android/tv/recommendation/RecentChannelEvaluatorTest.java
index 8f092238..f8d6b220 100644
--- a/tests/unit/src/com/android/tv/recommendation/RecentChannelEvaluatorTest.java
+++ b/tests/unit/src/com/android/tv/recommendation/RecentChannelEvaluatorTest.java
@@ -16,21 +16,20 @@
package com.android.tv.recommendation;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
import android.support.test.filters.SmallTest;
-
-import org.junit.Test;
-
+import android.support.test.runner.AndroidJUnit4;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
+import org.junit.Test;
+import org.junit.runner.RunWith;
-/**
- * Unit tests for {@link RecentChannelEvaluator}.
- */
+/** Unit tests for {@link RecentChannelEvaluator}. */
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class RecentChannelEvaluatorTest extends EvaluatorTestCase<RecentChannelEvaluator> {
private static final int DEFAULT_NUMBER_OF_CHANNELS = 4;
private static final long DEFAULT_WATCH_START_TIME_MS =
@@ -49,14 +48,16 @@ public class RecentChannelEvaluatorTest extends EvaluatorTestCase<RecentChannelE
long channelId = addChannel().getId();
notifyChannelAndWatchLogLoaded();
- assertEqualScores(Recommender.Evaluator.NOT_RECOMMENDED,
- mEvaluator.evaluateChannel(channelId));
+ assertEqualScores(
+ Recommender.Evaluator.NOT_RECOMMENDED, mEvaluator.evaluateChannel(channelId));
}
@Test
public void testOneChannelWithRandomWatchLogs() {
addChannel();
- addRandomWatchLogs(DEFAULT_WATCH_START_TIME_MS, DEFAULT_WATCH_END_TIME_MS,
+ addRandomWatchLogs(
+ DEFAULT_WATCH_START_TIME_MS,
+ DEFAULT_WATCH_END_TIME_MS,
DEFAULT_MAX_WATCH_DURATION_MS);
notifyChannelAndWatchLogLoaded();
@@ -70,15 +71,17 @@ public class RecentChannelEvaluatorTest extends EvaluatorTestCase<RecentChannelE
List<Long> channelIdList = getChannelIdListSorted();
for (long channelId : channelIdList) {
- assertEqualScores(Recommender.Evaluator.NOT_RECOMMENDED,
- mEvaluator.evaluateChannel(channelId));
+ assertEqualScores(
+ Recommender.Evaluator.NOT_RECOMMENDED, mEvaluator.evaluateChannel(channelId));
}
}
@Test
public void testMultiChannelsWithRandomWatchLogs() {
addChannels(DEFAULT_NUMBER_OF_CHANNELS);
- addRandomWatchLogs(DEFAULT_WATCH_START_TIME_MS, DEFAULT_WATCH_END_TIME_MS,
+ addRandomWatchLogs(
+ DEFAULT_WATCH_START_TIME_MS,
+ DEFAULT_WATCH_END_TIME_MS,
DEFAULT_MAX_WATCH_DURATION_MS);
notifyChannelAndWatchLogLoaded();
@@ -103,7 +106,7 @@ public class RecentChannelEvaluatorTest extends EvaluatorTestCase<RecentChannelE
double previousScore = Recommender.Evaluator.NOT_RECOMMENDED;
for (long channelId : channelIdList) {
double score = mEvaluator.evaluateChannel(channelId);
- assertTrue(previousScore <= score);
+ assertThat(previousScore).isAtMost(score);
previousScore = score;
}
}
@@ -111,7 +114,9 @@ public class RecentChannelEvaluatorTest extends EvaluatorTestCase<RecentChannelE
@Test
public void testScoreIncreasesWithNewWatchLog() {
addChannels(DEFAULT_NUMBER_OF_CHANNELS);
- addRandomWatchLogs(DEFAULT_WATCH_START_TIME_MS, DEFAULT_WATCH_END_TIME_MS,
+ addRandomWatchLogs(
+ DEFAULT_WATCH_START_TIME_MS,
+ DEFAULT_WATCH_END_TIME_MS,
DEFAULT_MAX_WATCH_DURATION_MS);
notifyChannelAndWatchLogLoaded();
@@ -124,15 +129,17 @@ public class RecentChannelEvaluatorTest extends EvaluatorTestCase<RecentChannelE
addWatchLog(channelId, latestWatchEndTimeMs, durationMs);
latestWatchEndTimeMs += durationMs;
- // Score must be increased because recentness of the log increases.
- assertTrue(previousScore <= mEvaluator.evaluateChannel(channelId));
+ // Score must be increased because recentness of the log increases.
+ assertThat(previousScore).isAtMost(mEvaluator.evaluateChannel(channelId));
}
}
@Test
public void testScoreDecreasesWithIncrementOfWatchedLogUpdatedTime() {
addChannels(DEFAULT_NUMBER_OF_CHANNELS);
- addRandomWatchLogs(DEFAULT_WATCH_START_TIME_MS, DEFAULT_WATCH_END_TIME_MS,
+ addRandomWatchLogs(
+ DEFAULT_WATCH_START_TIME_MS,
+ DEFAULT_WATCH_END_TIME_MS,
DEFAULT_MAX_WATCH_DURATION_MS);
notifyChannelAndWatchLogLoaded();
@@ -148,8 +155,8 @@ public class RecentChannelEvaluatorTest extends EvaluatorTestCase<RecentChannelE
addWatchLog(newChannelId, latestWatchedEndTimeMs, TimeUnit.MINUTES.toMillis(10));
for (long channelId : channelIdList) {
- // Score must be decreased because LastWatchLogUpdateTime increases by new log.
- assertTrue(mEvaluator.evaluateChannel(channelId) <= scores.get(channelId));
+ // Score must be decreased because LastWatchLogUpdateTime increases by new log.
+ assertThat(mEvaluator.evaluateChannel(channelId)).isAtMost(scores.get(channelId));
}
}
}
diff --git a/tests/unit/src/com/android/tv/recommendation/RecommendationUtils.java b/tests/unit/src/com/android/tv/recommendation/RecommendationUtils.java
index b00ed16b..b929a0ae 100644
--- a/tests/unit/src/com/android/tv/recommendation/RecommendationUtils.java
+++ b/tests/unit/src/com/android/tv/recommendation/RecommendationUtils.java
@@ -17,50 +17,57 @@
package com.android.tv.recommendation;
import android.content.Context;
-
-import com.android.tv.data.Channel;
-import com.android.tv.testing.Utils;
-
-import org.mockito.Matchers;
-import org.mockito.Mockito;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
+import com.android.tv.data.ChannelImpl;
+import com.android.tv.data.api.Channel;
+import com.android.tv.testing.utils.Utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
+import org.mockito.Matchers;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
public class RecommendationUtils {
private static final long INVALID_CHANNEL_ID = -1;
- /**
- * Create a mock RecommendationDataManager backed by a {@link ChannelRecordSortedMapHelper}.
- */
+ /** Create a mock RecommendationDataManager backed by a {@link ChannelRecordSortedMapHelper}. */
public static RecommendationDataManager createMockRecommendationDataManager(
final ChannelRecordSortedMapHelper channelRecordSortedMap) {
RecommendationDataManager dataManager = Mockito.mock(RecommendationDataManager.class);
- Mockito.doAnswer(new Answer<Integer>() {
- @Override
- public Integer answer(InvocationOnMock invocation) throws Throwable {
- return channelRecordSortedMap.size();
- }
- }).when(dataManager).getChannelRecordCount();
- Mockito.doAnswer(new Answer<Collection<ChannelRecord>>() {
- @Override
- public Collection<ChannelRecord> answer(InvocationOnMock invocation) throws Throwable {
- return channelRecordSortedMap.values();
- }
- }).when(dataManager).getChannelRecords();
- Mockito.doAnswer(new Answer<ChannelRecord>() {
- @Override
- public ChannelRecord answer(InvocationOnMock invocation) throws Throwable {
- long channelId = (long) invocation.getArguments()[0];
- return channelRecordSortedMap.get(channelId);
- }
- }).when(dataManager).getChannelRecord(Matchers.anyLong());
+ Mockito.doAnswer(
+ new Answer<Integer>() {
+ @Override
+ public Integer answer(InvocationOnMock invocation) throws Throwable {
+ return channelRecordSortedMap.size();
+ }
+ })
+ .when(dataManager)
+ .getChannelRecordCount();
+ Mockito.doAnswer(
+ new Answer<Collection<ChannelRecord>>() {
+ @Override
+ public Collection<ChannelRecord> answer(InvocationOnMock invocation)
+ throws Throwable {
+ return channelRecordSortedMap.values();
+ }
+ })
+ .when(dataManager)
+ .getChannelRecords();
+ Mockito.doAnswer(
+ new Answer<ChannelRecord>() {
+ @Override
+ public ChannelRecord answer(InvocationOnMock invocation)
+ throws Throwable {
+ long channelId = (long) invocation.getArguments()[0];
+ return channelRecordSortedMap.get(channelId);
+ }
+ })
+ .when(dataManager)
+ .getChannelRecord(Matchers.anyLong());
return dataManager;
}
@@ -82,9 +89,9 @@ public class RecommendationUtils {
}
/**
- * Add new {@code numberOfChannels} channels by adding channel record to
- * {@code channelRecordMap} with no history.
- * This action corresponds to loading channels in the RecommendationDataManger.
+ * Add new {@code numberOfChannels} channels by adding channel record to {@code
+ * channelRecordMap} with no history. This action corresponds to loading channels in the
+ * RecommendationDataManger.
*/
public void addChannels(int numberOfChannels) {
for (int i = 0; i < numberOfChannels; ++i) {
@@ -100,21 +107,21 @@ public class RecommendationUtils {
*/
public Channel addChannel() {
long channelId = size();
- Channel channel = new Channel.Builder().setId(channelId).build();
+ ChannelImpl channel = new ChannelImpl.Builder().setId(channelId).build();
ChannelRecord channelRecord = new ChannelRecord(mContext, channel, false);
put(channelId, channelRecord);
return channel;
}
/**
- * Add the watch logs which its durationTime is under {@code maxWatchDurationMs}.
- * Add until latest watch end time becomes bigger than {@code watchEndTimeMs},
- * starting from {@code watchStartTimeMs}.
+ * Add the watch logs which its durationTime is under {@code maxWatchDurationMs}. Add until
+ * latest watch end time becomes bigger than {@code watchEndTimeMs}, starting from {@code
+ * watchStartTimeMs}.
*
* @return true if adding watch log success, otherwise false.
*/
- public boolean addRandomWatchLogs(long watchStartTimeMs, long watchEndTimeMs,
- long maxWatchDurationMs) {
+ public boolean addRandomWatchLogs(
+ long watchStartTimeMs, long watchEndTimeMs, long maxWatchDurationMs) {
long latestWatchEndTimeMs = watchStartTimeMs;
long previousChannelId = INVALID_CHANNEL_ID;
List<Long> channelIdList = new ArrayList<>(keySet());
@@ -143,13 +150,13 @@ public class RecommendationUtils {
*/
public boolean addWatchLog(long channelId, long watchStartTimeMs, long durationTimeMs) {
ChannelRecord channelRecord = get(channelId);
- if (channelRecord == null ||
- watchStartTimeMs + durationTimeMs > System.currentTimeMillis()) {
+ if (channelRecord == null
+ || watchStartTimeMs + durationTimeMs > System.currentTimeMillis()) {
return false;
}
- channelRecord.logWatchHistory(new WatchedProgram(null, watchStartTimeMs,
- watchStartTimeMs + durationTimeMs));
+ channelRecord.logWatchHistory(
+ new WatchedProgram(null, watchStartTimeMs, watchStartTimeMs + durationTimeMs));
if (mRecommender != null) {
mRecommender.onNewWatchLog(channelRecord);
}
diff --git a/tests/unit/src/com/android/tv/recommendation/RecommenderTest.java b/tests/unit/src/com/android/tv/recommendation/RecommenderTest.java
index 85524a82..812a3eb1 100644
--- a/tests/unit/src/com/android/tv/recommendation/RecommenderTest.java
+++ b/tests/unit/src/com/android/tv/recommendation/RecommenderTest.java
@@ -17,20 +17,14 @@
package com.android.tv.recommendation;
import static android.support.test.InstrumentationRegistry.getContext;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
import android.test.MoreAsserts;
-
-import com.android.tv.data.Channel;
+import com.android.tv.data.api.Channel;
import com.android.tv.recommendation.RecommendationUtils.ChannelRecordSortedMapHelper;
-import com.android.tv.testing.Utils;
-
-import org.junit.Before;
-import org.junit.Test;
-
+import com.android.tv.testing.utils.Utils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -39,8 +33,12 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class RecommenderTest {
private static final int DEFAULT_NUMBER_OF_CHANNELS = 5;
private static final long DEFAULT_WATCH_START_TIME_MS =
@@ -49,24 +47,27 @@ public class RecommenderTest {
System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1);
private static final long DEFAULT_MAX_WATCH_DURATION_MS = TimeUnit.HOURS.toMillis(1);
- private final Comparator<Channel> CHANNEL_SORT_KEY_COMPARATOR = new Comparator<Channel>() {
- @Override
- public int compare(Channel lhs, Channel rhs) {
- return mRecommender.getChannelSortKey(lhs.getId())
- .compareTo(mRecommender.getChannelSortKey(rhs.getId()));
- }
- };
- private final Runnable START_DATAMANAGER_RUNNABLE_ADD_FOUR_CHANNELS = new Runnable() {
- @Override
- public void run() {
- // Add 4 channels in ChannelRecordMap for testing. Store the added channels to
- // mChannels_1 ~ mChannels_4. They are sorted by channel id in increasing order.
- mChannel_1 = mChannelRecordSortedMap.addChannel();
- mChannel_2 = mChannelRecordSortedMap.addChannel();
- mChannel_3 = mChannelRecordSortedMap.addChannel();
- mChannel_4 = mChannelRecordSortedMap.addChannel();
- }
- };
+ private final Comparator<Channel> mChannelSortKeyComparator =
+ new Comparator<Channel>() {
+ @Override
+ public int compare(Channel lhs, Channel rhs) {
+ return mRecommender
+ .getChannelSortKey(lhs.getId())
+ .compareTo(mRecommender.getChannelSortKey(rhs.getId()));
+ }
+ };
+ private final Runnable mStartDatamanagerRunnableAddFourChannels =
+ new Runnable() {
+ @Override
+ public void run() {
+ // Add 4 channels in ChannelRecordMap for testing. Store the added channels to
+ // mChannels_1 ~ mChannels_4. They are sorted by channel id in increasing order.
+ mChannel_1 = mChannelRecordSortedMap.addChannel();
+ mChannel_2 = mChannelRecordSortedMap.addChannel();
+ mChannel_3 = mChannelRecordSortedMap.addChannel();
+ mChannel_4 = mChannelRecordSortedMap.addChannel();
+ }
+ };
private RecommendationDataManager mDataManager;
private Recommender mRecommender;
@@ -82,133 +83,133 @@ public class RecommenderTest {
@Before
public void setUp() {
mChannelRecordSortedMap = new ChannelRecordSortedMapHelper(getContext());
- mDataManager = RecommendationUtils
- .createMockRecommendationDataManager(mChannelRecordSortedMap);
+ mDataManager =
+ RecommendationUtils.createMockRecommendationDataManager(mChannelRecordSortedMap);
mChannelRecordSortedMap.resetRandom(Utils.createTestRandom());
}
@Test
public void testRecommendChannels_includeRecommendedOnly_allChannelsHaveNoScore() {
- createRecommender(true, START_DATAMANAGER_RUNNABLE_ADD_FOUR_CHANNELS);
-
- // Recommender doesn't recommend any channels because all channels are not recommended.
- assertEquals(0, mRecommender.recommendChannels().size());
- assertEquals(0, mRecommender.recommendChannels(-5).size());
- assertEquals(0, mRecommender.recommendChannels(0).size());
- assertEquals(0, mRecommender.recommendChannels(3).size());
- assertEquals(0, mRecommender.recommendChannels(4).size());
- assertEquals(0, mRecommender.recommendChannels(5).size());
+ createRecommender(true, mStartDatamanagerRunnableAddFourChannels);
+
+ // Recommender doesn't recommend any channels because all channels are not recommended.
+ assertThat(mRecommender.recommendChannels()).isEmpty();
+ assertThat(mRecommender.recommendChannels(-5)).isEmpty();
+ assertThat(mRecommender.recommendChannels(0)).isEmpty();
+ assertThat(mRecommender.recommendChannels(3)).isEmpty();
+ assertThat(mRecommender.recommendChannels(4)).isEmpty();
+ assertThat(mRecommender.recommendChannels(5)).isEmpty();
}
@Test
public void testRecommendChannels_notIncludeRecommendedOnly_allChannelsHaveNoScore() {
- createRecommender(false, START_DATAMANAGER_RUNNABLE_ADD_FOUR_CHANNELS);
-
- // Recommender recommends every channel because it recommends not-recommended channels too.
- assertEquals(4, mRecommender.recommendChannels().size());
- assertEquals(0, mRecommender.recommendChannels(-5).size());
- assertEquals(0, mRecommender.recommendChannels(0).size());
- assertEquals(3, mRecommender.recommendChannels(3).size());
- assertEquals(4, mRecommender.recommendChannels(4).size());
- assertEquals(4, mRecommender.recommendChannels(5).size());
+ createRecommender(false, mStartDatamanagerRunnableAddFourChannels);
+
+ // Recommender recommends every channel because it recommends not-recommended channels too.
+ assertThat(mRecommender.recommendChannels()).hasSize(4);
+ assertThat(mRecommender.recommendChannels(-5)).isEmpty();
+ assertThat(mRecommender.recommendChannels(0)).isEmpty();
+ assertThat(mRecommender.recommendChannels(3)).hasSize(3);
+ assertThat(mRecommender.recommendChannels(4)).hasSize(4);
+ assertThat(mRecommender.recommendChannels(5)).hasSize(4);
}
@Test
public void testRecommendChannels_includeRecommendedOnly_allChannelsHaveScore() {
- createRecommender(true, START_DATAMANAGER_RUNNABLE_ADD_FOUR_CHANNELS);
+ createRecommender(true, mStartDatamanagerRunnableAddFourChannels);
setChannelScores_scoreIncreasesAsChannelIdIncreases();
// recommendChannels must be sorted by score in decreasing order.
// (i.e. sorted by channel ID in decreasing order in this case)
- MoreAsserts.assertContentsInOrder(mRecommender.recommendChannels(),
- mChannel_4, mChannel_3, mChannel_2, mChannel_1);
- assertEquals(0, mRecommender.recommendChannels(-5).size());
- assertEquals(0, mRecommender.recommendChannels(0).size());
- MoreAsserts.assertContentsInOrder(mRecommender.recommendChannels(3),
- mChannel_4, mChannel_3, mChannel_2);
- MoreAsserts.assertContentsInOrder(mRecommender.recommendChannels(4),
- mChannel_4, mChannel_3, mChannel_2, mChannel_1);
- MoreAsserts.assertContentsInOrder(mRecommender.recommendChannels(5),
- mChannel_4, mChannel_3, mChannel_2, mChannel_1);
+ MoreAsserts.assertContentsInOrder(
+ mRecommender.recommendChannels(), mChannel_4, mChannel_3, mChannel_2, mChannel_1);
+ assertThat(mRecommender.recommendChannels(-5)).isEmpty();
+ assertThat(mRecommender.recommendChannels(0)).isEmpty();
+ MoreAsserts.assertContentsInOrder(
+ mRecommender.recommendChannels(3), mChannel_4, mChannel_3, mChannel_2);
+ MoreAsserts.assertContentsInOrder(
+ mRecommender.recommendChannels(4), mChannel_4, mChannel_3, mChannel_2, mChannel_1);
+ MoreAsserts.assertContentsInOrder(
+ mRecommender.recommendChannels(5), mChannel_4, mChannel_3, mChannel_2, mChannel_1);
}
@Test
public void testRecommendChannels_notIncludeRecommendedOnly_allChannelsHaveScore() {
- createRecommender(false, START_DATAMANAGER_RUNNABLE_ADD_FOUR_CHANNELS);
+ createRecommender(false, mStartDatamanagerRunnableAddFourChannels);
setChannelScores_scoreIncreasesAsChannelIdIncreases();
// recommendChannels must be sorted by score in decreasing order.
// (i.e. sorted by channel ID in decreasing order in this case)
- MoreAsserts.assertContentsInOrder(mRecommender.recommendChannels(),
- mChannel_4, mChannel_3, mChannel_2, mChannel_1);
- assertEquals(0, mRecommender.recommendChannels(-5).size());
- assertEquals(0, mRecommender.recommendChannels(0).size());
- MoreAsserts.assertContentsInOrder(mRecommender.recommendChannels(3),
- mChannel_4, mChannel_3, mChannel_2);
- MoreAsserts.assertContentsInOrder(mRecommender.recommendChannels(4),
- mChannel_4, mChannel_3, mChannel_2, mChannel_1);
- MoreAsserts.assertContentsInOrder(mRecommender.recommendChannels(5),
- mChannel_4, mChannel_3, mChannel_2, mChannel_1);
+ MoreAsserts.assertContentsInOrder(
+ mRecommender.recommendChannels(), mChannel_4, mChannel_3, mChannel_2, mChannel_1);
+ assertThat(mRecommender.recommendChannels(-5)).isEmpty();
+ assertThat(mRecommender.recommendChannels(0)).isEmpty();
+ MoreAsserts.assertContentsInOrder(
+ mRecommender.recommendChannels(3), mChannel_4, mChannel_3, mChannel_2);
+ MoreAsserts.assertContentsInOrder(
+ mRecommender.recommendChannels(4), mChannel_4, mChannel_3, mChannel_2, mChannel_1);
+ MoreAsserts.assertContentsInOrder(
+ mRecommender.recommendChannels(5), mChannel_4, mChannel_3, mChannel_2, mChannel_1);
}
@Test
public void testRecommendChannels_includeRecommendedOnly_fewChannelsHaveScore() {
- createRecommender(true, START_DATAMANAGER_RUNNABLE_ADD_FOUR_CHANNELS);
+ createRecommender(true, mStartDatamanagerRunnableAddFourChannels);
mEvaluator.setChannelScore(mChannel_1.getId(), 1.0);
mEvaluator.setChannelScore(mChannel_2.getId(), 1.0);
// Only two channels are recommended because recommender doesn't recommend other channels.
- MoreAsserts.assertContentsInAnyOrder(mRecommender.recommendChannels(),
- mChannel_1, mChannel_2);
- assertEquals(0, mRecommender.recommendChannels(-5).size());
- assertEquals(0, mRecommender.recommendChannels(0).size());
- MoreAsserts.assertContentsInAnyOrder(mRecommender.recommendChannels(3),
- mChannel_1, mChannel_2);
- MoreAsserts.assertContentsInAnyOrder(mRecommender.recommendChannels(4),
- mChannel_1, mChannel_2);
- MoreAsserts.assertContentsInAnyOrder(mRecommender.recommendChannels(5),
- mChannel_1, mChannel_2);
+ MoreAsserts.assertContentsInAnyOrder(
+ mRecommender.recommendChannels(), mChannel_1, mChannel_2);
+ assertThat(mRecommender.recommendChannels(-5)).isEmpty();
+ assertThat(mRecommender.recommendChannels(0)).isEmpty();
+ MoreAsserts.assertContentsInAnyOrder(
+ mRecommender.recommendChannels(3), mChannel_1, mChannel_2);
+ MoreAsserts.assertContentsInAnyOrder(
+ mRecommender.recommendChannels(4), mChannel_1, mChannel_2);
+ MoreAsserts.assertContentsInAnyOrder(
+ mRecommender.recommendChannels(5), mChannel_1, mChannel_2);
}
@Test
public void testRecommendChannels_notIncludeRecommendedOnly_fewChannelsHaveScore() {
- createRecommender(false, START_DATAMANAGER_RUNNABLE_ADD_FOUR_CHANNELS);
+ createRecommender(false, mStartDatamanagerRunnableAddFourChannels);
mEvaluator.setChannelScore(mChannel_1.getId(), 1.0);
mEvaluator.setChannelScore(mChannel_2.getId(), 1.0);
- assertEquals(4, mRecommender.recommendChannels().size());
- MoreAsserts.assertContentsInAnyOrder(mRecommender.recommendChannels().subList(0, 2),
- mChannel_1, mChannel_2);
+ assertThat(mRecommender.recommendChannels()).hasSize(4);
+ MoreAsserts.assertContentsInAnyOrder(
+ mRecommender.recommendChannels().subList(0, 2), mChannel_1, mChannel_2);
- assertEquals(0, mRecommender.recommendChannels(-5).size());
- assertEquals(0, mRecommender.recommendChannels(0).size());
+ assertThat(mRecommender.recommendChannels(-5)).isEmpty();
+ assertThat(mRecommender.recommendChannels(0)).isEmpty();
- assertEquals(3, mRecommender.recommendChannels(3).size());
- MoreAsserts.assertContentsInAnyOrder(mRecommender.recommendChannels(3).subList(0, 2),
- mChannel_1, mChannel_2);
+ assertThat(mRecommender.recommendChannels(3)).hasSize(3);
+ MoreAsserts.assertContentsInAnyOrder(
+ mRecommender.recommendChannels(3).subList(0, 2), mChannel_1, mChannel_2);
- assertEquals(4, mRecommender.recommendChannels(4).size());
- MoreAsserts.assertContentsInAnyOrder(mRecommender.recommendChannels(4).subList(0, 2),
- mChannel_1, mChannel_2);
+ assertThat(mRecommender.recommendChannels(4)).hasSize(4);
+ MoreAsserts.assertContentsInAnyOrder(
+ mRecommender.recommendChannels(4).subList(0, 2), mChannel_1, mChannel_2);
- assertEquals(4, mRecommender.recommendChannels(5).size());
- MoreAsserts.assertContentsInAnyOrder(mRecommender.recommendChannels(5).subList(0, 2),
- mChannel_1, mChannel_2);
+ assertThat(mRecommender.recommendChannels(5)).hasSize(4);
+ MoreAsserts.assertContentsInAnyOrder(
+ mRecommender.recommendChannels(5).subList(0, 2), mChannel_1, mChannel_2);
}
@Test
public void testGetChannelSortKey_recommendAllChannels() {
- createRecommender(true, START_DATAMANAGER_RUNNABLE_ADD_FOUR_CHANNELS);
+ createRecommender(true, mStartDatamanagerRunnableAddFourChannels);
setChannelScores_scoreIncreasesAsChannelIdIncreases();
List<Channel> expectedChannelList = mRecommender.recommendChannels();
List<Channel> channelList = Arrays.asList(mChannel_1, mChannel_2, mChannel_3, mChannel_4);
- Collections.sort(channelList, CHANNEL_SORT_KEY_COMPARATOR);
+ Collections.sort(channelList, mChannelSortKeyComparator);
// Recommended channel list and channel list sorted by sort key must be the same.
MoreAsserts.assertContentsInOrder(channelList, expectedChannelList.toArray());
@@ -218,17 +219,17 @@ public class RecommenderTest {
@Test
public void testGetChannelSortKey_recommendFewChannels() {
// Test with recommending 3 channels.
- createRecommender(true, START_DATAMANAGER_RUNNABLE_ADD_FOUR_CHANNELS);
+ createRecommender(true, mStartDatamanagerRunnableAddFourChannels);
setChannelScores_scoreIncreasesAsChannelIdIncreases();
List<Channel> expectedChannelList = mRecommender.recommendChannels(3);
- // A channel which is not recommended by the recommender has to get an invalid sort key.
- assertEquals(Recommender.INVALID_CHANNEL_SORT_KEY,
- mRecommender.getChannelSortKey(mChannel_1.getId()));
+ // A channel which is not recommended by the recommender has to get an invalid sort key.
+ assertThat(mRecommender.getChannelSortKey(mChannel_1.getId()))
+ .isEqualTo(Recommender.INVALID_CHANNEL_SORT_KEY);
List<Channel> channelList = Arrays.asList(mChannel_2, mChannel_3, mChannel_4);
- Collections.sort(channelList, CHANNEL_SORT_KEY_COMPARATOR);
+ Collections.sort(channelList, mChannelSortKeyComparator);
MoreAsserts.assertContentsInOrder(channelList, expectedChannelList.toArray());
assertSortKeyNotInvalid(channelList);
@@ -236,10 +237,10 @@ public class RecommenderTest {
@Test
public void testListener_onRecommendationChanged() {
- createRecommender(true, START_DATAMANAGER_RUNNABLE_ADD_FOUR_CHANNELS);
- // FakeEvaluator doesn't recommend a channel with empty watch log. As every channel
- // doesn't have a watch log, nothing is recommended and recommendation isn't changed.
- assertFalse(mOnRecommendationChanged);
+ createRecommender(true, mStartDatamanagerRunnableAddFourChannels);
+ // FakeEvaluator doesn't recommend a channel with empty watch log. As every channel
+ // doesn't have a watch log, nothing is recommended and recommendation isn't changed.
+ assertThat(mOnRecommendationChanged).isFalse();
// Set lastRecommendationUpdatedTimeUtcMs to check recommendation changed because,
// recommender has a minimum recommendation update period.
@@ -248,51 +249,63 @@ public class RecommenderTest {
long latestWatchEndTimeMs = DEFAULT_WATCH_START_TIME_MS;
for (long channelId : mChannelRecordSortedMap.keySet()) {
mEvaluator.setChannelScore(channelId, 1.0);
- // Add a log to recalculate the recommendation score.
- assertTrue(mChannelRecordSortedMap.addWatchLog(channelId, latestWatchEndTimeMs,
- TimeUnit.MINUTES.toMillis(10)));
+ // Add a log to recalculate the recommendation score.
+ assertThat(
+ mChannelRecordSortedMap.addWatchLog(
+ channelId, latestWatchEndTimeMs, TimeUnit.MINUTES.toMillis(10)))
+ .isTrue();
latestWatchEndTimeMs += TimeUnit.MINUTES.toMillis(10);
}
- // onRecommendationChanged must be called, because recommend channels are not empty,
- // by setting score to each channel.
- assertTrue(mOnRecommendationChanged);
+ // onRecommendationChanged must be called, because recommend channels are not empty,
+ // by setting score to each channel.
+ assertThat(mOnRecommendationChanged).isTrue();
}
@Test
public void testListener_onRecommenderReady() {
- createRecommender(true, new Runnable() {
- @Override
- public void run() {
- mChannelRecordSortedMap.addChannels(DEFAULT_NUMBER_OF_CHANNELS);
- mChannelRecordSortedMap.addRandomWatchLogs(DEFAULT_WATCH_START_TIME_MS,
- DEFAULT_WATCH_END_TIME_MS, DEFAULT_MAX_WATCH_DURATION_MS);
- }
- });
-
- // After loading channels and watch logs are finished, recommender must be available to use.
- assertTrue(mOnRecommenderReady);
+ createRecommender(
+ true,
+ new Runnable() {
+ @Override
+ public void run() {
+ mChannelRecordSortedMap.addChannels(DEFAULT_NUMBER_OF_CHANNELS);
+ mChannelRecordSortedMap.addRandomWatchLogs(
+ DEFAULT_WATCH_START_TIME_MS,
+ DEFAULT_WATCH_END_TIME_MS,
+ DEFAULT_MAX_WATCH_DURATION_MS);
+ }
+ });
+
+ // After loading channels and watch logs are finished, recommender must be available to use.
+ assertThat(mOnRecommenderReady).isTrue();
}
private void assertSortKeyNotInvalid(List<Channel> channelList) {
for (Channel channel : channelList) {
- MoreAsserts.assertNotEqual(Recommender.INVALID_CHANNEL_SORT_KEY,
+ MoreAsserts.assertNotEqual(
+ Recommender.INVALID_CHANNEL_SORT_KEY,
mRecommender.getChannelSortKey(channel.getId()));
}
}
- private void createRecommender(boolean includeRecommendedOnly,
- Runnable startDataManagerRunnable) {
- mRecommender = new Recommender(new Recommender.Listener() {
- @Override
- public void onRecommenderReady() {
- mOnRecommenderReady = true;
- }
- @Override
- public void onRecommendationChanged() {
- mOnRecommendationChanged = true;
- }
- }, includeRecommendedOnly, mDataManager);
+ private void createRecommender(
+ boolean includeRecommendedOnly, Runnable startDataManagerRunnable) {
+ mRecommender =
+ new Recommender(
+ new Recommender.Listener() {
+ @Override
+ public void onRecommenderReady() {
+ mOnRecommenderReady = true;
+ }
+
+ @Override
+ public void onRecommendationChanged() {
+ mOnRecommendationChanged = true;
+ }
+ },
+ includeRecommendedOnly,
+ mDataManager);
mEvaluator = new FakeEvaluator();
mRecommender.registerEvaluator(mEvaluator);
diff --git a/tests/unit/src/com/android/tv/recommendation/RoutineWatchEvaluatorTest.java b/tests/unit/src/com/android/tv/recommendation/RoutineWatchEvaluatorTest.java
index 7b8e256d..39e6e9c5 100644
--- a/tests/unit/src/com/android/tv/recommendation/RoutineWatchEvaluatorTest.java
+++ b/tests/unit/src/com/android/tv/recommendation/RoutineWatchEvaluatorTest.java
@@ -16,23 +16,22 @@
package com.android.tv.recommendation;
+import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import android.support.test.filters.SmallTest;
-import android.test.MoreAsserts;
-
+import android.support.test.runner.AndroidJUnit4;
import com.android.tv.data.Program;
import com.android.tv.recommendation.RoutineWatchEvaluator.ProgramTime;
-
-import org.junit.Test;
-
-import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
-import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+/** Tests for {@link RoutineWatchEvaluator}. */
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class RoutineWatchEvaluatorTest extends EvaluatorTestCase<RoutineWatchEvaluator> {
private static class ScoredItem implements Comparable<ScoredItem> {
private final String mBase;
@@ -67,13 +66,23 @@ public class RoutineWatchEvaluatorTest extends EvaluatorTestCase<RoutineWatchEva
@Test
public void testSplitTextToWords() {
- assertSplitTextToWords("");
- assertSplitTextToWords("Google", "Google");
- assertSplitTextToWords("The Big Bang Theory", "The", "Big", "Bang", "Theory");
- assertSplitTextToWords("Hello, world!", "Hello", "world");
- assertSplitTextToWords("Adam's Rib", "Adam's", "Rib");
- assertSplitTextToWords("G.I. Joe", "G.I", "Joe");
- assertSplitTextToWords("A.I.", "A.I");
+ assertThat(RoutineWatchEvaluator.splitTextToWords("")).containsExactly().inOrder();
+ assertThat(RoutineWatchEvaluator.splitTextToWords("Google"))
+ .containsExactly("Google")
+ .inOrder();
+ assertThat(RoutineWatchEvaluator.splitTextToWords("The Big Bang Theory"))
+ .containsExactly("The", "Big", "Bang", "Theory")
+ .inOrder();
+ assertThat(RoutineWatchEvaluator.splitTextToWords("Hello, world!"))
+ .containsExactly("Hello", "world")
+ .inOrder();
+ assertThat(RoutineWatchEvaluator.splitTextToWords("Adam's Rib"))
+ .containsExactly("Adam's", "Rib")
+ .inOrder();
+ assertThat(RoutineWatchEvaluator.splitTextToWords("G.I. Joe"))
+ .containsExactly("G.I", "Joe")
+ .inOrder();
+ assertThat(RoutineWatchEvaluator.splitTextToWords("A.I.")).containsExactly("A.I").inOrder();
}
@Test
@@ -102,7 +111,6 @@ public class RoutineWatchEvaluatorTest extends EvaluatorTestCase<RoutineWatchEva
assertEqualScores(0.0, RoutineWatchEvaluator.calculateTitleMatchScore(" ", "foo"));
}
-
@Test
public void testCalculateTitleMatchScore_null() {
assertEqualScores(0.0, RoutineWatchEvaluator.calculateTitleMatchScore(null, null));
@@ -113,11 +121,15 @@ public class RoutineWatchEvaluatorTest extends EvaluatorTestCase<RoutineWatchEva
@Test
public void testCalculateTitleMatchScore_longerMatchIsBetter() {
String base = "foo bar baz";
- assertInOrder(
- score(base, ""),
- score(base, "bar"),
- score(base, "foo bar"),
- score(base, "foo bar baz"));
+ assertThat(
+ new ScoredItem[] {
+ score(base, ""),
+ score(base, "bar"),
+ score(base, "foo bar"),
+ score(base, "foo bar baz")
+ })
+ .asList()
+ .isOrdered();
}
@Test
@@ -128,116 +140,154 @@ public class RoutineWatchEvaluatorTest extends EvaluatorTestCase<RoutineWatchEva
int tomorrowDayOfWeek = (todayDayOfWeek % 7) + 1;
// Today 00:00 - 01:00.
- ProgramTime programTimeToday0000_0100 = ProgramTime.createFromProgram(
- createDummyProgram(todayAtHourMin(0, 0), TimeUnit.HOURS.toMillis(1)));
- assertProgramTime(todayDayOfWeek, hourMinuteToSec(0, 0), hourMinuteToSec(1, 0),
- programTimeToday0000_0100);
+ ProgramTime programTimeToday0000to0100 =
+ ProgramTime.createFromProgram(
+ createDummyProgram(todayAtHourMin(0, 0), TimeUnit.HOURS.toMillis(1)));
+ assertProgramTime(
+ todayDayOfWeek,
+ hourMinuteToSec(0, 0),
+ hourMinuteToSec(1, 0),
+ programTimeToday0000to0100);
// Today 23:30 - 24:30.
- ProgramTime programTimeToday2330_2430 = ProgramTime.createFromProgram(
- createDummyProgram(todayAtHourMin(23, 30), TimeUnit.HOURS.toMillis(1)));
- assertProgramTime(todayDayOfWeek, hourMinuteToSec(23, 30), hourMinuteToSec(24, 30),
- programTimeToday2330_2430);
+ ProgramTime programTimeToday2330to2430 =
+ ProgramTime.createFromProgram(
+ createDummyProgram(todayAtHourMin(23, 30), TimeUnit.HOURS.toMillis(1)));
+ assertProgramTime(
+ todayDayOfWeek,
+ hourMinuteToSec(23, 30),
+ hourMinuteToSec(24, 30),
+ programTimeToday2330to2430);
// Tomorrow 00:00 - 01:00.
- ProgramTime programTimeTomorrow0000_0100 = ProgramTime.createFromProgram(
- createDummyProgram(tomorrowAtHourMin(0, 0), TimeUnit.HOURS.toMillis(1)));
- assertProgramTime(tomorrowDayOfWeek, hourMinuteToSec(0, 0), hourMinuteToSec(1, 0),
- programTimeTomorrow0000_0100);
+ ProgramTime programTimeTomorrow0000to0100 =
+ ProgramTime.createFromProgram(
+ createDummyProgram(tomorrowAtHourMin(0, 0), TimeUnit.HOURS.toMillis(1)));
+ assertProgramTime(
+ tomorrowDayOfWeek,
+ hourMinuteToSec(0, 0),
+ hourMinuteToSec(1, 0),
+ programTimeTomorrow0000to0100);
// Tomorrow 23:30 - 24:30.
- ProgramTime programTimeTomorrow2330_2430 = ProgramTime.createFromProgram(
- createDummyProgram(tomorrowAtHourMin(23, 30), TimeUnit.HOURS.toMillis(1)));
- assertProgramTime(tomorrowDayOfWeek, hourMinuteToSec(23, 30), hourMinuteToSec(24, 30),
- programTimeTomorrow2330_2430);
+ ProgramTime programTimeTomorrow2330to2430 =
+ ProgramTime.createFromProgram(
+ createDummyProgram(tomorrowAtHourMin(23, 30), TimeUnit.HOURS.toMillis(1)));
+ assertProgramTime(
+ tomorrowDayOfWeek,
+ hourMinuteToSec(23, 30),
+ hourMinuteToSec(24, 30),
+ programTimeTomorrow2330to2430);
// Today 18:00 - Tomorrow 12:00.
- ProgramTime programTimeToday1800_3600 = ProgramTime.createFromProgram(
- createDummyProgram(todayAtHourMin(18, 0), TimeUnit.HOURS.toMillis(18)));
+ ProgramTime programTimeToday1800to3600 =
+ ProgramTime.createFromProgram(
+ createDummyProgram(todayAtHourMin(18, 0), TimeUnit.HOURS.toMillis(18)));
// Maximum duration of ProgramTime is 12 hours.
// So, this program looks like it ends at Tomorrow 06:00 (30:00).
- assertProgramTime(todayDayOfWeek, hourMinuteToSec(18, 0), hourMinuteToSec(30, 0),
- programTimeToday1800_3600);
+ assertProgramTime(
+ todayDayOfWeek,
+ hourMinuteToSec(18, 0),
+ hourMinuteToSec(30, 0),
+ programTimeToday1800to3600);
}
@Test
public void testCalculateOverlappedIntervalScore() {
// Today 21:00 - 24:00.
- ProgramTime programTimeToday2100_2400 = ProgramTime.createFromProgram(
- createDummyProgram(todayAtHourMin(21, 0), TimeUnit.HOURS.toMillis(3)));
+ ProgramTime programTimeToday2100to2400 =
+ ProgramTime.createFromProgram(
+ createDummyProgram(todayAtHourMin(21, 0), TimeUnit.HOURS.toMillis(3)));
// Today 22:00 - 01:00.
- ProgramTime programTimeToday2200_0100 = ProgramTime.createFromProgram(
- createDummyProgram(todayAtHourMin(22, 0), TimeUnit.HOURS.toMillis(3)));
+ ProgramTime programTimeToday2200to0100 =
+ ProgramTime.createFromProgram(
+ createDummyProgram(todayAtHourMin(22, 0), TimeUnit.HOURS.toMillis(3)));
// Tomorrow 00:00 - 03:00.
- ProgramTime programTimeTomorrow0000_0300 = ProgramTime.createFromProgram(
- createDummyProgram(tomorrowAtHourMin(0, 0), TimeUnit.HOURS.toMillis(3)));
+ ProgramTime programTimeTomorrow0000to0300 =
+ ProgramTime.createFromProgram(
+ createDummyProgram(tomorrowAtHourMin(0, 0), TimeUnit.HOURS.toMillis(3)));
// Tomorrow 20:00 - Tomorrow 23:00.
- ProgramTime programTimeTomorrow2000_2300 = ProgramTime.createFromProgram(
- createDummyProgram(tomorrowAtHourMin(20, 0), TimeUnit.HOURS.toMillis(3)));
+ ProgramTime programTimeTomorrow2000to2300 =
+ ProgramTime.createFromProgram(
+ createDummyProgram(tomorrowAtHourMin(20, 0), TimeUnit.HOURS.toMillis(3)));
// 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, programTimeToday2100to2400, programTimeToday2200to0100);
+ assertOverlappedIntervalScore(
+ 0, false, programTimeToday2100to2400, programTimeTomorrow0000to0300);
+ assertOverlappedIntervalScore(
+ 2 * oneHourInSec, false, programTimeToday2100to2400, programTimeTomorrow2000to2300);
+ assertOverlappedIntervalScore(
+ oneHourInSec, true, programTimeToday2200to0100, programTimeTomorrow0000to0300);
+ assertOverlappedIntervalScore(
+ oneHourInSec, false, programTimeToday2200to0100, programTimeTomorrow2000to2300);
+ assertOverlappedIntervalScore(
+ 0, false, programTimeTomorrow0000to0300, programTimeTomorrow2000to2300);
}
@Test
public void testGetTimeOfDayInSec() {
// Time was set as 00:00:00. So, getTimeOfDay must returns 0 (= 0 * 60 * 60 + 0 * 60 + 0).
- assertEquals("TimeOfDayInSec", hourMinuteToSec(0, 0),
+ assertEquals(
+ "TimeOfDayInSec",
+ hourMinuteToSec(0, 0),
RoutineWatchEvaluator.getTimeOfDayInSec(todayAtHourMin(0, 0)));
// Time was set as 23:59:59. So, getTimeOfDay must returns 23 * 60 + 60 + 59 * 60 + 59.
- assertEquals("TimeOfDayInSec", hourMinuteSecondToSec(23, 59, 59),
+ assertEquals(
+ "TimeOfDayInSec",
+ hourMinuteSecondToSec(23, 59, 59),
RoutineWatchEvaluator.getTimeOfDayInSec(todayAtHourMinSec(23, 59, 59)));
}
- private void assertSplitTextToWords(String text, String... words) {
- List<String> wordList = RoutineWatchEvaluator.splitTextToWords(text);
- 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,
+ assertEquals(
+ "MaximumMatchedWordSequenceLength",
+ expectedLength,
RoutineWatchEvaluator.calculateMaximumMatchedWordSequenceLength(
wordList1, wordList2));
- assertEquals("MaximumMatchedWordSequenceLength", expectedLength,
+ assertEquals(
+ "MaximumMatchedWordSequenceLength",
+ expectedLength,
RoutineWatchEvaluator.calculateMaximumMatchedWordSequenceLength(
wordList2, wordList1));
}
- private void assertProgramTime(int expectedWeekDay, int expectedStartTimeOfDayInSec,
- int expectedEndTimeOfDayInSec, ProgramTime actualProgramTime) {
+ private void assertProgramTime(
+ int expectedWeekDay,
+ int expectedStartTimeOfDayInSec,
+ int expectedEndTimeOfDayInSec,
+ ProgramTime actualProgramTime) {
assertEquals("Weekday", expectedWeekDay, actualProgramTime.weekDay);
- assertEquals("StartTimeOfDayInSec", expectedStartTimeOfDayInSec,
+ assertEquals(
+ "StartTimeOfDayInSec",
+ expectedStartTimeOfDayInSec,
actualProgramTime.startTimeOfDayInSec);
- assertEquals("EndTimeOfDayInSec", expectedEndTimeOfDayInSec,
+ assertEquals(
+ "EndTimeOfDayInSec",
+ expectedEndTimeOfDayInSec,
actualProgramTime.endTimeOfDayInSec);
}
- private void assertOverlappedIntervalScore(int expectedSeconds, boolean overlappedOnSameDay,
- ProgramTime t1, ProgramTime t2) {
+ private void assertOverlappedIntervalScore(
+ int expectedSeconds, boolean overlappedOnSameDay, ProgramTime t1, ProgramTime t2) {
double score = expectedSeconds;
if (!overlappedOnSameDay) {
score *= RoutineWatchEvaluator.MULTIPLIER_FOR_UNMATCHED_DAY_OF_WEEK;
}
// Two tests for testing commutative law.
- assertEqualScores("OverlappedIntervalScore", score,
+ assertEqualScores(
+ "OverlappedIntervalScore",
+ score,
RoutineWatchEvaluator.calculateOverlappedIntervalScore(t1, t2));
- assertEqualScores("OverlappedIntervalScore", score,
+ assertEqualScores(
+ "OverlappedIntervalScore",
+ score,
RoutineWatchEvaluator.calculateOverlappedIntervalScore(t2, t1));
}
@@ -270,12 +320,9 @@ 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();
- }
-
- private static <T> void assertInOrder(T... items) {
- TreeSet<T> copy = new TreeSet<>(Arrays.asList(items));
- MoreAsserts.assertContentsInOrder(copy, items);
+ return new Program.Builder()
+ .setStartTimeUtcMillis(startTimeMs)
+ .setEndTimeUtcMillis(startTimeMs + programDurationMs)
+ .build();
}
}
diff --git a/tests/unit/src/com/android/tv/search/LocalSearchProviderTest.java b/tests/unit/src/com/android/tv/search/LocalSearchProviderTest.java
deleted file mode 100644
index b0d342c6..00000000
--- a/tests/unit/src/com/android/tv/search/LocalSearchProviderTest.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2017 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.search;
-
-import static android.support.test.InstrumentationRegistry.getTargetContext;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.verify;
-
-import android.app.SearchManager;
-import android.database.Cursor;
-import android.net.Uri;
-import android.support.test.filters.SmallTest;
-import android.test.ProviderTestCase2;
-
-import com.android.tv.ApplicationSingletons;
-import com.android.tv.TvApplication;
-import com.android.tv.perf.PerformanceMonitor;
-import com.android.tv.util.MockApplicationSingletons;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/** Unit test for {@link LocalSearchProvider}. */
-@SmallTest
-public class LocalSearchProviderTest extends ProviderTestCase2<LocalSearchProvider> {
- private static final String AUTHORITY = "com.android.tv.search";
- private static final String KEYWORD = "keyword";
- private static final Uri BASE_SEARCH_URI = Uri.parse("content://" + AUTHORITY + "/"
- + SearchManager.SUGGEST_URI_PATH_QUERY + "/" + KEYWORD);
- private static final Uri WRONG_SERACH_URI = Uri.parse("content://" + AUTHORITY + "/wrong_path/"
- + KEYWORD);
-
- private ApplicationSingletons mOldAppSingletons;
- MockApplicationSingletons mMockAppSingletons;
- @Mock PerformanceMonitor mMockPerformanceMointor;
- @Mock SearchInterface mMockSearchInterface;
-
- public LocalSearchProviderTest() {
- super(LocalSearchProvider.class, AUTHORITY);
- }
-
- @Before
- @Override
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- setContext(getTargetContext());
- mOldAppSingletons = TvApplication.sAppSingletons;
- mMockAppSingletons = new MockApplicationSingletons(getTargetContext());
- mMockAppSingletons.setPerformanceMonitor(mMockPerformanceMointor);
- TvApplication.sAppSingletons = mMockAppSingletons;
- super.setUp();
- getProvider().setSearchInterface(mMockSearchInterface);
- }
-
- @After
- @Override
- public void tearDown() throws Exception {
- TvApplication.sAppSingletons = mOldAppSingletons;
- super.tearDown();
- }
-
- @Test
- public void testQuery_normalUri() {
- verifyQueryWithArguments(null, null);
- verifyQueryWithArguments(1, null);
- verifyQueryWithArguments(null, 1);
- verifyQueryWithArguments(1, 1);
- }
-
- @Test
- public void testQuery_invalidUri() {
- try (Cursor c = getProvider().query(WRONG_SERACH_URI, null, null, null, null)) {
- fail("Query with invalid URI should fail.");
- } catch (IllegalArgumentException e) {
- // Success.
- }
- }
-
- @Test
- public void testQuery_invalidLimit() {
- verifyQueryWithArguments(-1, null);
- }
-
- @Test
- public void testQuery_invalidAction() {
- verifyQueryWithArguments(null, SearchInterface.ACTION_TYPE_START - 1);
- verifyQueryWithArguments(null, SearchInterface.ACTION_TYPE_END + 1);
- }
-
- private void verifyQueryWithArguments(Integer limit, Integer action) {
- Uri uri = BASE_SEARCH_URI;
- if (limit != null || action != null) {
- Uri.Builder builder = uri.buildUpon();
- if (limit != null) {
- builder.appendQueryParameter(SearchManager.SUGGEST_PARAMETER_LIMIT,
- limit.toString());
- }
- if (action != null) {
- builder.appendQueryParameter(LocalSearchProvider.SUGGEST_PARAMETER_ACTION,
- action.toString());
- }
- uri = builder.build();
- }
- try (Cursor c = getProvider().query(uri, null, null, null, null)) {
- // Do nothing.
- }
- int expectedLimit = limit == null || limit <= 0 ?
- LocalSearchProvider.DEFAULT_SEARCH_LIMIT : limit;
- int expectedAction = (action == null || action < SearchInterface.ACTION_TYPE_START
- || action > SearchInterface.ACTION_TYPE_END) ?
- LocalSearchProvider.DEFAULT_SEARCH_ACTION : action;
- verify(mMockSearchInterface).search(KEYWORD, expectedLimit, expectedAction);
- clearInvocations(mMockSearchInterface);
- }
-}
diff --git a/tests/unit/src/com/android/tv/tests/TvActivityTest.java b/tests/unit/src/com/android/tv/tests/TvActivityTest.java
deleted file mode 100644
index aa33f770..00000000
--- a/tests/unit/src/com/android/tv/tests/TvActivityTest.java
+++ /dev/null
@@ -1,43 +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.tests;
-
-import static android.support.test.InstrumentationRegistry.getTargetContext;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import android.support.test.filters.MediumTest;
-import android.support.test.rule.ActivityTestRule;
-
-import com.android.tv.TvActivity;
-import com.android.tv.testing.Utils;
-
-import org.junit.Rule;
-import org.junit.Test;
-
-@MediumTest
-public class TvActivityTest {
- @Rule
- public ActivityTestRule<TvActivity> mActivityTestRule =
- new ActivityTestRule<>(TvActivity.class, false, false);
-
- @Test
- public void testLifeCycle() {
- assertTrue("TvActivity should be enabled.", Utils.isTvActivityEnabled(getTargetContext()));
- assertNotNull(mActivityTestRule.launchActivity(null));
- }
-}
diff --git a/tests/unit/src/com/android/tv/util/MockApplicationSingletons.java b/tests/unit/src/com/android/tv/util/MockTvSingletons.java
index 4cfc7f8a..6de1eb3e 100644
--- a/tests/unit/src/com/android/tv/util/MockApplicationSingletons.java
+++ b/tests/unit/src/com/android/tv/util/MockTvSingletons.java
@@ -17,34 +17,41 @@
package com.android.tv.util;
import android.content.Context;
-
-import com.android.tv.ApplicationSingletons;
+import android.content.Intent;
import com.android.tv.InputSessionManager;
import com.android.tv.MainActivityWrapper;
import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.analytics.Analytics;
import com.android.tv.analytics.Tracker;
-import com.android.tv.config.RemoteConfig;
+import com.android.tv.common.config.api.RemoteConfig;
+import com.android.tv.common.experiments.ExperimentLoader;
+import com.android.tv.common.recording.RecordingStorageStatusManager;
+import com.android.tv.common.util.Clock;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.PreviewDataManager;
import com.android.tv.data.ProgramDataManager;
+import com.android.tv.data.epg.EpgFetcher;
+import com.android.tv.data.epg.EpgReader;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.DvrScheduleManager;
-import com.android.tv.dvr.DvrStorageStatusManager;
import com.android.tv.dvr.DvrWatchedPositionManager;
import com.android.tv.dvr.recorder.RecordingScheduler;
import com.android.tv.perf.PerformanceMonitor;
+import com.android.tv.testing.FakeClock;
+import com.android.tv.tuner.TunerInputController;
+import java.util.concurrent.Executor;
+import javax.inject.Provider;
-/**
- * Mock {@link ApplicationSingletons} class.
- */
-public class MockApplicationSingletons implements ApplicationSingletons {
- private final TvApplication mApp;
+/** Mock {@link TvSingletons} class. */
+public class MockTvSingletons implements TvSingletons {
+ public final FakeClock fakeClock = FakeClock.createWithCurrentTime();
+ private final TvApplication mApp;
private PerformanceMonitor mPerformanceMonitor;
- public MockApplicationSingletons(Context context) {
+ public MockTvSingletons(Context context) {
mApp = (TvApplication) context.getApplicationContext();
}
@@ -54,6 +61,9 @@ public class MockApplicationSingletons implements ApplicationSingletons {
}
@Override
+ public void handleInputCountChanged() {}
+
+ @Override
public ChannelDataManager getChannelDataManager() {
return mApp.getChannelDataManager();
}
@@ -84,8 +94,13 @@ public class MockApplicationSingletons implements ApplicationSingletons {
}
@Override
- public DvrStorageStatusManager getDvrStorageStatusManager() {
- return mApp.getDvrStorageStatusManager();
+ public Clock getClock() {
+ return fakeClock;
+ }
+
+ @Override
+ public RecordingStorageStatusManager getRecordingStorageStatusManager() {
+ return mApp.getRecordingStorageStatusManager();
}
@Override
@@ -124,12 +139,37 @@ public class MockApplicationSingletons implements ApplicationSingletons {
}
@Override
+ public Provider<EpgReader> providesEpgReader() {
+ return mApp.providesEpgReader();
+ }
+
+ @Override
+ public EpgFetcher getEpgFetcher() {
+ return mApp.getEpgFetcher();
+ }
+
+ @Override
+ public SetupUtils getSetupUtils() {
+ return mApp.getSetupUtils();
+ }
+
+ @Override
+ public TunerInputController getTunerInputController() {
+ return mApp.getTunerInputController();
+ }
+
+ @Override
+ public ExperimentLoader getExperimentLoader() {
+ return mApp.getExperimentLoader();
+ }
+
+ @Override
public MainActivityWrapper getMainActivityWrapper() {
return mApp.getMainActivityWrapper();
}
@Override
- public AccountHelper getAccountHelper() {
+ public com.android.tv.util.account.AccountHelper getAccountHelper() {
return mApp.getAccountHelper();
}
@@ -139,6 +179,11 @@ public class MockApplicationSingletons implements ApplicationSingletons {
}
@Override
+ public Intent getTunerSetupIntent(Context context) {
+ return mApp.getTunerSetupIntent(context);
+ }
+
+ @Override
public boolean isRunningInMainProcess() {
return mApp.isRunningInMainProcess();
}
@@ -151,4 +196,14 @@ public class MockApplicationSingletons implements ApplicationSingletons {
public void setPerformanceMonitor(PerformanceMonitor performanceMonitor) {
mPerformanceMonitor = performanceMonitor;
}
+
+ @Override
+ public String getEmbeddedTunerInputId() {
+ return "com.android.tv/.tuner.tvinput.LiveTvTunerTvInputService";
+ }
+
+ @Override
+ public Executor getDbExecutor() {
+ return mApp.getDbExecutor();
+ }
}
diff --git a/tests/unit/src/com/android/tv/util/MultiLongSparseArrayTest.java b/tests/unit/src/com/android/tv/util/MultiLongSparseArrayTest.java
deleted file mode 100644
index 7335f207..00000000
--- a/tests/unit/src/com/android/tv/util/MultiLongSparseArrayTest.java
+++ /dev/null
@@ -1,108 +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.util;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertSame;
-
-import android.support.test.filters.SmallTest;
-import android.test.MoreAsserts;
-
-import org.junit.Test;
-
-import java.util.Collections;
-
-/**
- * Tests for {@link MultiLongSparseArray}.
- */
-@SmallTest
-public class MultiLongSparseArrayTest {
- @Test
- public void testEmpty() {
- MultiLongSparseArray<String> sparseArray = new MultiLongSparseArray<>();
- assertSame(Collections.EMPTY_SET, sparseArray.get(0));
- }
-
- @Test
- public void testOneElement() {
- MultiLongSparseArray<String> sparseArray = new MultiLongSparseArray<>();
- sparseArray.put(0, "foo");
- MoreAsserts.assertContentsInAnyOrder(sparseArray.get(0), "foo");
- }
-
- @Test
- public void testTwoElements() {
- MultiLongSparseArray<String> sparseArray = new MultiLongSparseArray<>();
- sparseArray.put(0, "foo");
- sparseArray.put(0, "bar");
- MoreAsserts.assertContentsInAnyOrder(sparseArray.get(0), "foo", "bar");
- }
-
-
- @Test
- public void testClearEmptyCache() {
- MultiLongSparseArray<String> sparseArray = new MultiLongSparseArray<>();
- sparseArray.clearEmptyCache();
- assertEquals(0, sparseArray.getEmptyCacheSize());
- sparseArray.put(0, "foo");
- sparseArray.remove(0, "foo");
- assertEquals(1, sparseArray.getEmptyCacheSize());
- sparseArray.clearEmptyCache();
- assertEquals(0, sparseArray.getEmptyCacheSize());
- }
-
- @Test
- public void testMaxEmptyCacheSize() {
- MultiLongSparseArray<String> sparseArray = new MultiLongSparseArray<>();
- sparseArray.clearEmptyCache();
- assertEquals(0, sparseArray.getEmptyCacheSize());
- for (int i = 0; i <= MultiLongSparseArray.DEFAULT_MAX_EMPTIES_KEPT + 2; i++) {
- sparseArray.put(i, "foo");
- }
- for (int i = 0; i <= MultiLongSparseArray.DEFAULT_MAX_EMPTIES_KEPT + 2; i++) {
- sparseArray.remove(i, "foo");
- }
- assertEquals(MultiLongSparseArray.DEFAULT_MAX_EMPTIES_KEPT,
- sparseArray.getEmptyCacheSize());
- sparseArray.clearEmptyCache();
- assertEquals(0, sparseArray.getEmptyCacheSize());
- }
-
- @Test
- public void testReuseEmptySets() {
- MultiLongSparseArray<String> sparseArray = new MultiLongSparseArray<>();
- sparseArray.clearEmptyCache();
- assertEquals(0, sparseArray.getEmptyCacheSize());
- // create a bunch of sets
- for (int i = 0; i <= MultiLongSparseArray.DEFAULT_MAX_EMPTIES_KEPT + 2; i++) {
- sparseArray.put(i, "foo");
- }
- // remove them so they are all put in the cache.
- for (int i = 0; i <= MultiLongSparseArray.DEFAULT_MAX_EMPTIES_KEPT + 2; i++) {
- sparseArray.remove(i, "foo");
- }
- assertEquals(MultiLongSparseArray.DEFAULT_MAX_EMPTIES_KEPT,
- sparseArray.getEmptyCacheSize());
-
- // now create elements, that use the cached empty sets.
- for (int i = 0; i < MultiLongSparseArray.DEFAULT_MAX_EMPTIES_KEPT; i++) {
- sparseArray.put(10 + i, "bar");
- assertEquals(MultiLongSparseArray.DEFAULT_MAX_EMPTIES_KEPT - i - 1,
- sparseArray.getEmptyCacheSize());
- }
- }
-}
diff --git a/tests/unit/src/com/android/tv/util/ScaledBitmapInfoTest.java b/tests/unit/src/com/android/tv/util/ScaledBitmapInfoTest.java
deleted file mode 100644
index 2714e2e9..00000000
--- a/tests/unit/src/com/android/tv/util/ScaledBitmapInfoTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package com.android.tv.util;
-
-import static org.junit.Assert.assertEquals;
-
-import android.graphics.Bitmap;
-import android.support.test.filters.SmallTest;
-
-import com.android.tv.util.BitmapUtils.ScaledBitmapInfo;
-
-import org.junit.Test;
-
-/**
- * Tests for {@link ScaledBitmapInfo}.
- */
-@SmallTest
-public class ScaledBitmapInfoTest {
- private static final Bitmap B80x100 = Bitmap.createBitmap(80, 100, Bitmap.Config.RGB_565);
- private static final Bitmap B960x1440 = Bitmap.createBitmap(960, 1440, Bitmap.Config.RGB_565);
-
- @Test
- public void testSize_B100x100to50x50() {
- ScaledBitmapInfo actual = BitmapUtils.createScaledBitmapInfo("B80x100", B80x100, 50, 50);
- assertScaledBitmapSize(2, 40, 50, actual);
- }
-
- @Test
- public void testNeedsToReload_B100x100to50x50() {
- ScaledBitmapInfo actual = BitmapUtils.createScaledBitmapInfo("B80x100", B80x100, 50, 50);
- assertNeedsToReload(false, actual, 25, 25);
- assertNeedsToReload(false, actual, 50, 50);
- assertNeedsToReload(false, actual, 99, 99);
- assertNeedsToReload(true, actual, 100, 100);
- assertNeedsToReload(true, actual, 101, 101);
- }
-
- /**
- * Reproduces <a href="http://b/20488453">b/20488453</a>.
- */
- @Test
- public void testBug20488453() {
- ScaledBitmapInfo actual = BitmapUtils
- .createScaledBitmapInfo("B960x1440", B960x1440, 284, 160);
- assertScaledBitmapSize(8, 107, 160, actual);
- assertNeedsToReload(false, actual, 284, 160);
- }
-
- private static void assertNeedsToReload(boolean expected, ScaledBitmapInfo scaledBitmap,
- int reqWidth, int reqHeight) {
- assertEquals(scaledBitmap.id + " needToReload(" + reqWidth + "," + reqHeight + ")",
- expected, scaledBitmap.needToReload(reqWidth, reqHeight));
- }
-
- private static void assertScaledBitmapSize(int expectedInSampleSize, int expectedWidth,
- int expectedHeight, ScaledBitmapInfo actual) {
- assertEquals(actual.id + " inSampleSize", expectedInSampleSize, actual.inSampleSize);
- assertEquals(actual.id + " width", expectedWidth, actual.bitmap.getWidth());
- assertEquals(actual.id + " height", expectedHeight, actual.bitmap.getHeight());
- }
-}
diff --git a/tests/unit/src/com/android/tv/util/TestUtils.java b/tests/unit/src/com/android/tv/util/TestUtils.java
deleted file mode 100644
index d200733d..00000000
--- a/tests/unit/src/com/android/tv/util/TestUtils.java
+++ /dev/null
@@ -1,115 +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.util;
-
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.graphics.drawable.Icon;
-import android.hardware.hdmi.HdmiDeviceInfo;
-import android.media.tv.TvInputInfo;
-import android.os.Build;
-import android.os.Bundle;
-
-import java.lang.reflect.Constructor;
-
-/**
- * A class that includes convenience methods for testing.
- */
-public class TestUtils {
- /**
- * Creates a {@link TvInputInfo}.
- */
- public static TvInputInfo createTvInputInfo(ResolveInfo service, String id, String parentId,
- int type, boolean isHardwareInput) throws Exception {
- return createTvInputInfo(service, id, parentId, type, isHardwareInput, false, 0);
- }
-
- /**
- * Creates a {@link TvInputInfo}.
- * <p>
- * If this is called on MNC, {@code canRecord} and {@code tunerCount} are ignored.
- */
- public static TvInputInfo createTvInputInfo(ResolveInfo service, String id, String parentId,
- int type, boolean isHardwareInput, boolean canRecord, int tunerCount) throws Exception {
- // Create a mock TvInputInfo by using private constructor
- // Note that mockito doesn't support mock/spy on final object.
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- return createTvInputInfoForO(service, id, parentId, type, isHardwareInput, canRecord,
- tunerCount);
-
- } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- return createTvInputInfoForNyc(service, id, parentId, type, isHardwareInput, canRecord,
- tunerCount);
- }
- return createTvInputInfoForMnc(service, id, parentId, type, isHardwareInput);
- }
-
- /**
- * private TvInputInfo(ResolveInfo service, String id, int type, boolean isHardwareInput,
- * CharSequence label, int labelResId, Icon icon, Icon iconStandby, Icon iconDisconnected,
- * String setupActivity, boolean canRecord, int tunerCount, HdmiDeviceInfo hdmiDeviceInfo,
- * boolean isConnectedToHdmiSwitch, String parentId, Bundle extras) {
- */
- private static TvInputInfo createTvInputInfoForO(ResolveInfo service, String id,
- String parentId, int type, boolean isHardwareInput, boolean canRecord, int tunerCount)
- throws Exception {
- Constructor<TvInputInfo> constructor = TvInputInfo.class.getDeclaredConstructor(
- ResolveInfo.class, String.class, int.class, boolean.class, CharSequence.class,
- int.class, Icon.class, Icon.class, Icon.class, String.class, boolean.class,
- int.class, HdmiDeviceInfo.class, boolean.class, String.class, Bundle.class);
- constructor.setAccessible(true);
- return constructor.newInstance(service, id, type, isHardwareInput, null, 0, null, null,
- null, null, canRecord, tunerCount, null, false, parentId, null);
- }
-
- /**
- * private TvInputInfo(ResolveInfo service, String id, int type, boolean isHardwareInput,
- * CharSequence label, int labelResId, Icon icon, Icon iconStandby, Icon iconDisconnected,
- * String setupActivity, String settingsActivity, boolean canRecord, int tunerCount,
- * HdmiDeviceInfo hdmiDeviceInfo, boolean isConnectedToHdmiSwitch, String parentId,
- * Bundle extras) {
- */
- private static TvInputInfo createTvInputInfoForNyc(ResolveInfo service, String id,
- String parentId, int type, boolean isHardwareInput, boolean canRecord, int tunerCount)
- throws Exception {
- Constructor<TvInputInfo> constructor = TvInputInfo.class.getDeclaredConstructor(
- ResolveInfo.class, String.class, int.class, boolean.class, CharSequence.class,
- int.class, Icon.class, Icon.class, Icon.class, String.class, String.class,
- boolean.class, int.class, HdmiDeviceInfo.class, boolean.class, String.class,
- Bundle.class);
- constructor.setAccessible(true);
- return constructor.newInstance(service, id, type, isHardwareInput, null, 0, null, null,
- null, null, null, canRecord, tunerCount, null, false, parentId, null);
- }
-
- private static TvInputInfo createTvInputInfoForMnc(ResolveInfo service, String id,
- String parentId, int type, boolean isHardwareInput) throws Exception {
- Constructor<TvInputInfo> constructor = TvInputInfo.class.getDeclaredConstructor(
- ResolveInfo.class, String.class, String.class, int.class, boolean.class);
- constructor.setAccessible(true);
- return constructor.newInstance(service, id, parentId, type, isHardwareInput);
- }
-
- public static ResolveInfo createResolveInfo(String packageName, String name) {
- ResolveInfo resolveInfo = new ResolveInfo();
- resolveInfo.serviceInfo = new ServiceInfo();
- resolveInfo.serviceInfo.packageName = packageName;
- resolveInfo.serviceInfo.name = name;
- resolveInfo.serviceInfo.metaData = new Bundle();
- return resolveInfo;
- }
-}
diff --git a/tests/unit/src/com/android/tv/util/TvInputManagerHelperTest.java b/tests/unit/src/com/android/tv/util/TvInputManagerHelperTest.java
index 404ee5d3..6dfed64a 100644
--- a/tests/unit/src/com/android/tv/util/TvInputManagerHelperTest.java
+++ b/tests/unit/src/com/android/tv/util/TvInputManagerHelperTest.java
@@ -21,22 +21,21 @@ import static android.support.test.InstrumentationRegistry.getContext;
import android.content.pm.ResolveInfo;
import android.media.tv.TvInputInfo;
import android.support.test.filters.SmallTest;
-
+import android.support.test.runner.AndroidJUnit4;
import com.android.tv.testing.ComparatorTester;
-
+import com.android.tv.testing.utils.TestUtils;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * Test for {@link TvInputManagerHelper}
- */
+/** Test for {@link TvInputManagerHelper} */
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class TvInputManagerHelperTest {
final HashMap<String, TvInputInfoWrapper> TEST_INPUT_MAP = new HashMap<>();
@@ -45,18 +44,51 @@ public class TvInputManagerHelperTest {
ResolveInfo resolveInfo = TestUtils.createResolveInfo("test", "test");
List<TvInputInfo> inputs = new ArrayList<>();
- inputs.add(createTvInputInfo(resolveInfo, "2_partner_input", null, 0, false,
- "2_partner_input", null, true));
- inputs.add(createTvInputInfo(resolveInfo, "3_partner_input", null, 0, false,
- "3_partner_input", null, true));
- inputs.add(createTvInputInfo(resolveInfo, "1_3rd_party_input", null, 0, false,
- "1_3rd_party_input", null, false));
- inputs.add(createTvInputInfo(resolveInfo, "4_3rd_party_input", null, 0, false,
- "4_3rd_party_input", null, false));
+ inputs.add(
+ createTvInputInfo(
+ resolveInfo,
+ "2_partner_input",
+ null,
+ 0,
+ false,
+ "2_partner_input",
+ null,
+ true));
+ inputs.add(
+ createTvInputInfo(
+ resolveInfo,
+ "3_partner_input",
+ null,
+ 0,
+ false,
+ "3_partner_input",
+ null,
+ true));
+ inputs.add(
+ createTvInputInfo(
+ resolveInfo,
+ "1_3rd_party_input",
+ null,
+ 0,
+ false,
+ "1_3rd_party_input",
+ null,
+ false));
+ inputs.add(
+ createTvInputInfo(
+ resolveInfo,
+ "4_3rd_party_input",
+ null,
+ 0,
+ false,
+ "4_3rd_party_input",
+ null,
+ false));
TvInputManagerHelper manager = createMockTvInputManager();
- ComparatorTester<TvInputInfo> comparatorTester = ComparatorTester.withoutEqualsTest(
+ ComparatorTester<TvInputInfo> comparatorTester =
+ ComparatorTester.withoutEqualsTest(
new TvInputManagerHelper.InputComparatorInternal(manager));
for (TvInputInfo input : inputs) {
comparatorTester.addComparableGroup(input);
@@ -68,20 +100,54 @@ public class TvInputManagerHelperTest {
public void testHardwareInputComparatorHdmi() {
ResolveInfo resolveInfo = TestUtils.createResolveInfo("test", "test");
- TvInputInfo hdmi1 = createTvInputInfo(resolveInfo, "HDMI1", null, TvInputInfo.TYPE_HDMI,
- true, "HDMI1", null, false);
- TvInputInfo hdmi2 = createTvInputInfo(resolveInfo, "HDMI2", null, TvInputInfo.TYPE_HDMI,
- true, "HDMI2", "DVD", false);
- TvInputInfo hdmi3 = createTvInputInfo(resolveInfo, "HDMI3", null, TvInputInfo.TYPE_HDMI,
- true, "HDMI3", "Cable", false);
- TvInputInfo hdmi4 = createTvInputInfo(resolveInfo, "HDMI4", null, TvInputInfo.TYPE_HDMI,
- true, "HDMI4", null, false);
+ TvInputInfo hdmi1 =
+ createTvInputInfo(
+ resolveInfo,
+ "HDMI1",
+ null,
+ TvInputInfo.TYPE_HDMI,
+ true,
+ "HDMI1",
+ null,
+ false);
+ TvInputInfo hdmi2 =
+ createTvInputInfo(
+ resolveInfo,
+ "HDMI2",
+ null,
+ TvInputInfo.TYPE_HDMI,
+ true,
+ "HDMI2",
+ "DVD",
+ false);
+ TvInputInfo hdmi3 =
+ createTvInputInfo(
+ resolveInfo,
+ "HDMI3",
+ null,
+ TvInputInfo.TYPE_HDMI,
+ true,
+ "HDMI3",
+ "Cable",
+ false);
+ TvInputInfo hdmi4 =
+ createTvInputInfo(
+ resolveInfo,
+ "HDMI4",
+ null,
+ TvInputInfo.TYPE_HDMI,
+ true,
+ "HDMI4",
+ null,
+ false);
TvInputManagerHelper manager = createMockTvInputManager();
- ComparatorTester<TvInputInfo> comparatorTester = ComparatorTester.withoutEqualsTest(
+ ComparatorTester<TvInputInfo> comparatorTester =
+ ComparatorTester.withoutEqualsTest(
new TvInputManagerHelper.HardwareInputComparator(getContext(), manager));
- comparatorTester.addComparableGroup(hdmi3)
+ comparatorTester
+ .addComparableGroup(hdmi3)
.addComparableGroup(hdmi2)
.addComparableGroup(hdmi1)
.addComparableGroup(hdmi4)
@@ -92,61 +158,111 @@ public class TvInputManagerHelperTest {
public void testHardwareInputComparatorCec() {
ResolveInfo resolveInfo = TestUtils.createResolveInfo("test", "test");
- TvInputInfo hdmi1 = createTvInputInfo(resolveInfo, "HDMI1", null, TvInputInfo.TYPE_HDMI,
- true, "HDMI1", null, false);
- TvInputInfo hdmi2 = createTvInputInfo(resolveInfo, "HDMI2", null, TvInputInfo.TYPE_HDMI,
- true, "HDMI2", null, false);
+ TvInputInfo hdmi1 =
+ createTvInputInfo(
+ resolveInfo,
+ "HDMI1",
+ null,
+ TvInputInfo.TYPE_HDMI,
+ true,
+ "HDMI1",
+ null,
+ false);
+ TvInputInfo hdmi2 =
+ createTvInputInfo(
+ resolveInfo,
+ "HDMI2",
+ null,
+ TvInputInfo.TYPE_HDMI,
+ true,
+ "HDMI2",
+ null,
+ false);
- TvInputInfo cec1 = createTvInputInfo(resolveInfo, "2_cec", "HDMI1", TvInputInfo.TYPE_HDMI,
- true, "2_cec", null, false);
- TvInputInfo cec2 = createTvInputInfo(resolveInfo, "1_cec", "HDMI2", TvInputInfo.TYPE_HDMI,
- true, "1_cec", null, false);
+ TvInputInfo cec1 =
+ createTvInputInfo(
+ resolveInfo,
+ "2_cec",
+ "HDMI1",
+ TvInputInfo.TYPE_HDMI,
+ true,
+ "2_cec",
+ null,
+ false);
+ TvInputInfo cec2 =
+ createTvInputInfo(
+ resolveInfo,
+ "1_cec",
+ "HDMI2",
+ TvInputInfo.TYPE_HDMI,
+ true,
+ "1_cec",
+ null,
+ false);
TvInputManagerHelper manager = createMockTvInputManager();
- ComparatorTester<TvInputInfo> comparatorTester = ComparatorTester.withoutEqualsTest(
+ ComparatorTester<TvInputInfo> comparatorTester =
+ ComparatorTester.withoutEqualsTest(
new TvInputManagerHelper.HardwareInputComparator(getContext(), manager));
- comparatorTester.addComparableGroup(cec1)
- .addComparableGroup(cec2)
- .test();
+ comparatorTester.addComparableGroup(cec1).addComparableGroup(cec2).test();
}
private TvInputManagerHelper createMockTvInputManager() {
TvInputManagerHelper manager = Mockito.mock(TvInputManagerHelper.class);
- Mockito.doAnswer(new Answer<Boolean>() {
- @Override
- public Boolean answer(InvocationOnMock invocation) throws Throwable {
- TvInputInfo info = (TvInputInfo) invocation.getArguments()[0];
- return TEST_INPUT_MAP.get(info.getId()).mIsPartnerInput;
- }
- }).when(manager).isPartnerInput(Mockito.<TvInputInfo>any());
- Mockito.doAnswer(new Answer<String>() {
- @Override
- public String answer(InvocationOnMock invocation) throws Throwable {
- TvInputInfo info = (TvInputInfo) invocation.getArguments()[0];
- return TEST_INPUT_MAP.get(info.getId()).mLabel;
- }
- }).when(manager).loadLabel(Mockito.<TvInputInfo>any());
- Mockito.doAnswer(new Answer<String>() {
- @Override
- public String answer(InvocationOnMock invocation) throws Throwable {
- TvInputInfo info = (TvInputInfo) invocation.getArguments()[0];
- return TEST_INPUT_MAP.get(info.getId()).mCustomLabel;
- }
- }).when(manager).loadCustomLabel(Mockito.<TvInputInfo>any());
- Mockito.doAnswer(new Answer<TvInputInfo>() {
- @Override
- public TvInputInfo answer(InvocationOnMock invocation) throws Throwable {
- String inputId = (String) invocation.getArguments()[0];
- TvInputInfoWrapper inputWrapper = TEST_INPUT_MAP.get(inputId);
- return inputWrapper == null ? null : inputWrapper.mInput;
- }
- }).when(manager).getTvInputInfo(Mockito.<String>any());
+ Mockito.doAnswer(
+ new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ TvInputInfo info = (TvInputInfo) invocation.getArguments()[0];
+ return TEST_INPUT_MAP.get(info.getId()).mIsPartnerInput;
+ }
+ })
+ .when(manager)
+ .isPartnerInput(Mockito.<TvInputInfo>any());
+ Mockito.doAnswer(
+ new Answer<String>() {
+ @Override
+ public String answer(InvocationOnMock invocation) throws Throwable {
+ TvInputInfo info = (TvInputInfo) invocation.getArguments()[0];
+ return TEST_INPUT_MAP.get(info.getId()).mLabel;
+ }
+ })
+ .when(manager)
+ .loadLabel(Mockito.<TvInputInfo>any());
+ Mockito.doAnswer(
+ new Answer<String>() {
+ @Override
+ public String answer(InvocationOnMock invocation) throws Throwable {
+ TvInputInfo info = (TvInputInfo) invocation.getArguments()[0];
+ return TEST_INPUT_MAP.get(info.getId()).mCustomLabel;
+ }
+ })
+ .when(manager)
+ .loadCustomLabel(Mockito.<TvInputInfo>any());
+ Mockito.doAnswer(
+ new Answer<TvInputInfo>() {
+ @Override
+ public TvInputInfo answer(InvocationOnMock invocation)
+ throws Throwable {
+ String inputId = (String) invocation.getArguments()[0];
+ TvInputInfoWrapper inputWrapper = TEST_INPUT_MAP.get(inputId);
+ return inputWrapper == null ? null : inputWrapper.mInput;
+ }
+ })
+ .when(manager)
+ .getTvInputInfo(Mockito.<String>any());
return manager;
}
- private TvInputInfo createTvInputInfo(ResolveInfo service, String id,
- String parentId, int type, boolean isHardwareInput, String label, String customLabel,
+ private TvInputInfo createTvInputInfo(
+ ResolveInfo service,
+ String id,
+ String parentId,
+ int type,
+ boolean isHardwareInput,
+ String label,
+ String customLabel,
boolean isPartnerInput) {
TvInputInfoWrapper inputWrapper = new TvInputInfoWrapper();
try {
diff --git a/tests/unit/src/com/android/tv/util/TvTrackInfoUtilsTest.java b/tests/unit/src/com/android/tv/util/TvTrackInfoUtilsTest.java
index 4512bb7d..d84a90d4 100644
--- a/tests/unit/src/com/android/tv/util/TvTrackInfoUtilsTest.java
+++ b/tests/unit/src/com/android/tv/util/TvTrackInfoUtilsTest.java
@@ -16,24 +16,22 @@
package com.android.tv.util;
import static com.android.tv.util.TvTrackInfoUtils.getBestTrackInfo;
-import static org.junit.Assert.assertEquals;
+import static com.google.common.truth.Truth.assertWithMessage;
import android.media.tv.TvTrackInfo;
import android.support.test.filters.SmallTest;
-
+import android.support.test.runner.AndroidJUnit4;
import com.android.tv.testing.ComparatorTester;
-
-import org.junit.Test;
-
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
-/**
- * Tests for {@link com.android.tv.util.TvTrackInfoUtils}.
- */
+/** Tests for {@link com.android.tv.util.TvTrackInfoUtils}. */
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class TvTrackInfoUtilsTest {
private static final String UN_MATCHED_ID = "no matching ID";
@@ -54,57 +52,57 @@ public class TvTrackInfoUtilsTest {
.build();
}
- private static final List<TvTrackInfo> ALL = Arrays.asList(INFO_1_EN_1, INFO_2_EN_5,
- INFO_3_FR_8, INFO_4_NULL_2, INFO_5_NULL_6);
- private static final List<TvTrackInfo> NULL_LANGUAGE_TRACKS = Arrays.asList(INFO_4_NULL_2,
- INFO_5_NULL_6);
+ private static final List<TvTrackInfo> ALL =
+ Arrays.asList(INFO_1_EN_1, INFO_2_EN_5, INFO_3_FR_8, INFO_4_NULL_2, INFO_5_NULL_6);
+ private static final List<TvTrackInfo> NULL_LANGUAGE_TRACKS =
+ Arrays.asList(INFO_4_NULL_2, INFO_5_NULL_6);
@Test
public void testGetBestTrackInfo_empty() {
TvTrackInfo result = getBestTrackInfo(Collections.emptyList(), UN_MATCHED_ID, "en", 1);
- assertEquals("best track ", null, result);
+ assertWithMessage("best track ").that(result).isEqualTo(null);
}
@Test
public void testGetBestTrackInfo_exactMatch() {
TvTrackInfo result = getBestTrackInfo(ALL, "1", "en", 1);
- assertEquals("best track ", INFO_1_EN_1, result);
+ assertWithMessage("best track ").that(result).isEqualTo(INFO_1_EN_1);
}
@Test
public void testGetBestTrackInfo_langAndChannelCountMatch() {
TvTrackInfo result = getBestTrackInfo(ALL, UN_MATCHED_ID, "en", 5);
- assertEquals("best track ", INFO_2_EN_5, result);
+ assertWithMessage("best track ").that(result).isEqualTo(INFO_2_EN_5);
}
@Test
public void testGetBestTrackInfo_languageOnlyMatch() {
TvTrackInfo result = getBestTrackInfo(ALL, UN_MATCHED_ID, "fr", 1);
- assertEquals("best track ", INFO_3_FR_8, result);
+ assertWithMessage("best track ").that(result).isEqualTo(INFO_3_FR_8);
}
@Test
public void testGetBestTrackInfo_channelCountOnlyMatchWithNullLanguage() {
TvTrackInfo result = getBestTrackInfo(ALL, UN_MATCHED_ID, null, 8);
- assertEquals("best track ", INFO_3_FR_8, result);
+ assertWithMessage("best track ").that(result).isEqualTo(INFO_3_FR_8);
}
@Test
public void testGetBestTrackInfo_noMatches() {
TvTrackInfo result = getBestTrackInfo(ALL, UN_MATCHED_ID, "kr", 1);
- assertEquals("best track ", INFO_1_EN_1, result);
+ assertWithMessage("best track ").that(result).isEqualTo(INFO_1_EN_1);
}
@Test
public void testGetBestTrackInfo_noMatchesWithNullLanguage() {
TvTrackInfo result = getBestTrackInfo(ALL, UN_MATCHED_ID, null, 0);
- assertEquals("best track ", INFO_1_EN_1, result);
+ assertWithMessage("best track ").that(result).isEqualTo(INFO_1_EN_1);
}
@Test
public void testGetBestTrackInfo_channelCountAndIdMatch() {
TvTrackInfo result = getBestTrackInfo(NULL_LANGUAGE_TRACKS, "5", null, 6);
- assertEquals("best track ", INFO_5_NULL_6, result);
+ assertWithMessage("best track ").that(result).isEqualTo(INFO_5_NULL_6);
}
@Test
@@ -112,15 +110,17 @@ public class TvTrackInfoUtilsTest {
Comparator<TvTrackInfo> comparator = TvTrackInfoUtils.createComparator("1", "en", 1);
ComparatorTester.withoutEqualsTest(comparator)
// lang not match
- .addComparableGroup(create("1", "kr", 1), create("2", "kr", 2),
+ .addComparableGroup(
+ create("1", "kr", 1),
+ create("2", "kr", 2),
create("1", "ja", 1),
create("1", "ch", 1))
- // lang match not count match
- .addComparableGroup(create("2", "en", 2), create("3", "en", 3),
- create("1", "en", 2))
- // lang and count match
+ // lang match not count match
+ .addComparableGroup(
+ create("2", "en", 2), create("3", "en", 3), create("1", "en", 2))
+ // lang and count match
.addComparableGroup(create("2", "en", 1), create("3", "en", 1))
- // all match
+ // all match
.addComparableGroup(create("1", "en", 1), create("1", "en", 1))
.test();
}
diff --git a/tests/unit/src/com/android/tv/util/UtilsTest_GetDurationString.java b/tests/unit/src/com/android/tv/util/UtilsTest_GetDurationString.java
deleted file mode 100644
index e61802f5..00000000
--- a/tests/unit/src/com/android/tv/util/UtilsTest_GetDurationString.java
+++ /dev/null
@@ -1,273 +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.util;
-
-import static android.support.test.InstrumentationRegistry.getContext;
-import static org.junit.Assert.assertEquals;
-
-import android.support.test.filters.SmallTest;
-import android.text.format.DateUtils;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.Calendar;
-import java.util.GregorianCalendar;
-import java.util.Locale;
-
-/**
- * Tests for {@link com.android.tv.util.Utils#getDurationString}.
- * <p/>
- * This test uses deprecated flags {@link DateUtils#FORMAT_12HOUR} and
- * {@link DateUtils#FORMAT_24HOUR} to run this test independent to system's 12/24h format.
- * Note that changing system setting requires permission android.permission.WRITE_SETTINGS
- * and it should be defined in TV app, not this test.
- */
-@SmallTest
-public class UtilsTest_GetDurationString {
- // TODO: Mock Context so we can specify current time and locale for test.
- private Locale mLocale;
- private static final long DATE_THIS_YEAR_2_1_MS = getFebOfThisYearInMillis(1, 0, 0);
-
- // All possible list for a parameter to test parameter independent result.
- private static final boolean[] PARAM_USE_SHORT_FORMAT = {false, true};
-
- @Before
- public void setUp() {
- // Set locale to US
- mLocale = Locale.getDefault();
- Locale.setDefault(Locale.US);
- }
-
- @After
- public void tearDown() {
- // Revive system locale.
- Locale.setDefault(mLocale);
- }
-
- /**
- * Return time in millis assuming that whose year is this year and month is Jan.
- */
- private static long getJanOfThisYearInMillis(int date, int hour, int minutes) {
- return new GregorianCalendar(getThisYear(), Calendar.JANUARY, date, hour, minutes)
- .getTimeInMillis();
- }
-
- private static long getJanOfThisYearInMillis(int date, int hour) {
- return getJanOfThisYearInMillis(date, hour, 0);
- }
-
- /**
- * Return time in millis assuming that whose year is this year and month is Feb.
- */
- private static long getFebOfThisYearInMillis(int date, int hour, int minutes) {
- return new GregorianCalendar(getThisYear(), Calendar.FEBRUARY, date, hour, minutes)
- .getTimeInMillis();
- }
-
- private static long getFebOfThisYearInMillis(int date, int hour) {
- return getFebOfThisYearInMillis(date, hour, 0);
- }
-
- private static int getThisYear() {
- return new GregorianCalendar().get(GregorianCalendar.YEAR);
- }
-
- @Test
- public void testSameDateAndTime() {
- assertEquals("3:00 AM", Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(1, 3), getFebOfThisYearInMillis(1, 3), false,
- DateUtils.FORMAT_12HOUR));
- assertEquals("03:00", Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(1, 3), getFebOfThisYearInMillis(1, 3), false,
- DateUtils.FORMAT_24HOUR));
- }
-
- @Test
- public void testDurationWithinToday() {
- assertEquals("12:00 – 3:00 AM",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS, DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(1, 3), false,
- DateUtils.FORMAT_12HOUR));
- assertEquals("00:00 – 03:00",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS, DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(1, 3), false,
- DateUtils.FORMAT_24HOUR));
- }
-
- @Test
- public void testDurationFromYesterdayToToday() {
- assertEquals("Jan 31, 3:00 AM – Feb 1, 4:00 AM",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getJanOfThisYearInMillis(31, 3), getFebOfThisYearInMillis(1, 4), false,
- DateUtils.FORMAT_12HOUR));
- assertEquals("Jan 31, 03:00 – Feb 1, 04:00",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getJanOfThisYearInMillis(31, 3), getFebOfThisYearInMillis(1, 4), false,
- DateUtils.FORMAT_24HOUR));
- assertEquals("1/31, 11:30 PM – 12:30 AM",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getJanOfThisYearInMillis(31, 23, 30), getFebOfThisYearInMillis(1, 0, 30),
- true, DateUtils.FORMAT_12HOUR));
- assertEquals("1/31, 23:30 – 00:30",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getJanOfThisYearInMillis(31, 23, 30), getFebOfThisYearInMillis(1, 0, 30),
- true, DateUtils.FORMAT_24HOUR));
- }
-
- @Test
- public void testDurationFromTodayToTomorrow() {
- assertEquals("Feb 1, 3:00 AM – Feb 2, 4:00 AM",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(1, 3), getFebOfThisYearInMillis(2, 4), false,
- DateUtils.FORMAT_12HOUR));
- assertEquals("Feb 1, 03:00 – Feb 2, 04:00",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(1, 3), getFebOfThisYearInMillis(2, 4), false,
- DateUtils.FORMAT_24HOUR));
- assertEquals("2/1, 3:00 AM – 2/2, 4:00 AM",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(1, 3), getFebOfThisYearInMillis(2, 4), true,
- DateUtils.FORMAT_12HOUR));
- assertEquals("2/1, 03:00 – 2/2, 04:00",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(1, 3), getFebOfThisYearInMillis(2, 4), true,
- DateUtils.FORMAT_24HOUR));
-
- assertEquals("Feb 1, 11:30 PM – Feb 2, 12:30 AM",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(1, 23, 30), getFebOfThisYearInMillis(2, 0, 30),
- false,
- DateUtils.FORMAT_12HOUR));
- assertEquals("Feb 1, 23:30 – Feb 2, 00:30",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(1, 23, 30), getFebOfThisYearInMillis(2, 0, 30),
- false,
- DateUtils.FORMAT_24HOUR));
- assertEquals("11:30 PM – 12:30 AM",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(1, 23, 30), getFebOfThisYearInMillis(2, 0, 30),
- true,
- DateUtils.FORMAT_12HOUR));
- assertEquals("23:30 – 00:30", Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(1, 23, 30), getFebOfThisYearInMillis(2, 0, 30),
- true,
- DateUtils.FORMAT_24HOUR));
- }
-
- @Test
- public void testDurationWithinTomorrow() {
- assertEquals("Feb 2, 2:00 – 4:00 AM",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(2, 2), getFebOfThisYearInMillis(2, 4), false,
- DateUtils.FORMAT_12HOUR));
- assertEquals("Feb 2, 02:00 – 04:00",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(2, 2), getFebOfThisYearInMillis(2, 4), false,
- DateUtils.FORMAT_24HOUR));
- assertEquals("2/2, 2:00 – 4:00 AM",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(2, 2), getFebOfThisYearInMillis(2, 4), true,
- DateUtils.FORMAT_12HOUR));
- assertEquals("2/2, 02:00 – 04:00",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(2, 2), getFebOfThisYearInMillis(2, 4), true,
- DateUtils.FORMAT_24HOUR));
- }
-
- @Test
- public void testStartOfDay() {
- assertEquals("12:00 – 1:00 AM",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS, DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(1, 1), false,
- DateUtils.FORMAT_12HOUR));
- assertEquals("00:00 – 01:00",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS, DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(1, 1), false,
- DateUtils.FORMAT_24HOUR));
-
- assertEquals("Feb 2, 12:00 – 1:00 AM",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(2, 0), getFebOfThisYearInMillis(2, 1), false,
- DateUtils.FORMAT_12HOUR));
- assertEquals("Feb 2, 00:00 – 01:00",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(2, 0), getFebOfThisYearInMillis(2, 1), false,
- DateUtils.FORMAT_24HOUR));
- assertEquals("2/2, 12:00 – 1:00 AM",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(2, 0), getFebOfThisYearInMillis(2, 1), true,
- DateUtils.FORMAT_12HOUR));
- assertEquals("2/2, 00:00 – 01:00",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(2, 0), getFebOfThisYearInMillis(2, 1), true,
- DateUtils.FORMAT_24HOUR));
- }
-
- @Test
- public void testEndOfDay() {
- for (boolean useShortFormat : PARAM_USE_SHORT_FORMAT) {
- assertEquals("11:00 PM – 12:00 AM",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(1, 23), getFebOfThisYearInMillis(2, 0),
- useShortFormat,
- DateUtils.FORMAT_12HOUR));
- assertEquals("23:00 – 00:00",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(1, 23), getFebOfThisYearInMillis(2, 0),
- useShortFormat,
- DateUtils.FORMAT_24HOUR));
- }
-
- assertEquals("Feb 2, 11:00 PM – 12:00 AM",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(2, 23), getFebOfThisYearInMillis(3, 0), false,
- DateUtils.FORMAT_12HOUR));
- assertEquals("Feb 2, 23:00 – 00:00",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(2, 23), getFebOfThisYearInMillis(3, 0), false,
- DateUtils.FORMAT_24HOUR));
- assertEquals("2/2, 11:00 PM – 12:00 AM",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(2, 23), getFebOfThisYearInMillis(3, 0), true,
- DateUtils.FORMAT_12HOUR));
- assertEquals("2/2, 23:00 – 00:00",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(2, 23), getFebOfThisYearInMillis(3, 0), true,
- DateUtils.FORMAT_24HOUR));
- assertEquals("2/2, 12:00 AM – 2/3, 12:00 AM",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(2, 0), getFebOfThisYearInMillis(3, 0), true,
- DateUtils.FORMAT_12HOUR));
- assertEquals("2/2, 00:00 – 2/3, 00:00",
- Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- getFebOfThisYearInMillis(2, 0), getFebOfThisYearInMillis(3, 0), true,
- DateUtils.FORMAT_24HOUR));
- }
-
- @Test
- public void testMidnight() {
- for (boolean useShortFormat : PARAM_USE_SHORT_FORMAT) {
- assertEquals("12:00 AM", Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- DATE_THIS_YEAR_2_1_MS, DATE_THIS_YEAR_2_1_MS, useShortFormat,
- DateUtils.FORMAT_12HOUR));
- assertEquals("00:00", Utils.getDurationString(getContext(), DATE_THIS_YEAR_2_1_MS,
- DATE_THIS_YEAR_2_1_MS, DATE_THIS_YEAR_2_1_MS, useShortFormat,
- DateUtils.FORMAT_24HOUR));
- }
- }
-}
diff --git a/tests/unit/src/com/android/tv/util/UtilsTest_GetMultiAudioString.java b/tests/unit/src/com/android/tv/util/UtilsTest_GetMultiAudioString.java
deleted file mode 100644
index 1e75342b..00000000
--- a/tests/unit/src/com/android/tv/util/UtilsTest_GetMultiAudioString.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.util;
-
-import static android.support.test.InstrumentationRegistry.getTargetContext;
-import static org.junit.Assert.assertEquals;
-
-import android.content.Context;
-import android.media.tv.TvTrackInfo;
-import android.support.test.filters.SmallTest;
-
-import org.junit.Test;
-
-/**
- * Tests for {@link com.android.tv.util.Utils#getMultiAudioString}.
- */
-@SmallTest
-public class UtilsTest_GetMultiAudioString {
- private static final String TRACK_ID = "test_track_id";
- private static final int AUDIO_SAMPLE_RATE = 48000;
-
- @Test
- public void testAudioTrackLanguage() {
- Context context = getTargetContext();
- assertEquals("Korean",
- Utils.getMultiAudioString(context, createAudioTrackInfo("kor"), false));
- assertEquals("English",
- Utils.getMultiAudioString(context, createAudioTrackInfo("eng"), false));
- assertEquals("Unknown language",
- Utils.getMultiAudioString(context, createAudioTrackInfo(null), false));
- assertEquals("Unknown language",
- Utils.getMultiAudioString(context, createAudioTrackInfo(""), false));
- assertEquals("abc", Utils.getMultiAudioString(context, createAudioTrackInfo("abc"), false));
- }
-
- @Test
- public void testAudioTrackCount() {
- Context context = getTargetContext();
- assertEquals("English",
- Utils.getMultiAudioString(context, createAudioTrackInfo("eng", -1), false));
- assertEquals("English",
- Utils.getMultiAudioString(context, createAudioTrackInfo("eng", 0), false));
- assertEquals("English (mono)",
- Utils.getMultiAudioString(context, createAudioTrackInfo("eng", 1), false));
- assertEquals("English (stereo)",
- Utils.getMultiAudioString(context, createAudioTrackInfo("eng", 2), false));
- assertEquals("English (3 channels)",
- Utils.getMultiAudioString(context, createAudioTrackInfo("eng", 3), false));
- assertEquals("English (4 channels)",
- Utils.getMultiAudioString(context, createAudioTrackInfo("eng", 4), false));
- assertEquals("English (5 channels)",
- Utils.getMultiAudioString(context, createAudioTrackInfo("eng", 5), false));
- assertEquals("English (5.1 surround)",
- Utils.getMultiAudioString(context, createAudioTrackInfo("eng", 6), false));
- assertEquals("English (7 channels)",
- Utils.getMultiAudioString(context, createAudioTrackInfo("eng", 7), false));
- assertEquals("English (7.1 surround)",
- Utils.getMultiAudioString(context, createAudioTrackInfo("eng", 8), false));
- }
-
- @Test
- public void testShowSampleRate() {
- assertEquals("Korean (48kHz)",
- Utils.getMultiAudioString(getTargetContext(),
- createAudioTrackInfo("kor", 0), true));
- assertEquals("Korean (7.1 surround, 48kHz)",
- Utils.getMultiAudioString(getTargetContext(),
- createAudioTrackInfo("kor", 8), true));
- }
-
- private static TvTrackInfo createAudioTrackInfo(String language) {
- return createAudioTrackInfo(language, 0);
- }
-
- private static TvTrackInfo createAudioTrackInfo(String language, int channelCount) {
- return new TvTrackInfo.Builder(TvTrackInfo.TYPE_AUDIO, TRACK_ID)
- .setLanguage(language).setAudioChannelCount(channelCount)
- .setAudioSampleRate(AUDIO_SAMPLE_RATE).build();
- }
-}
diff --git a/tests/unit/src/com/android/tv/util/UtilsTest_IsInGivenDay.java b/tests/unit/src/com/android/tv/util/UtilsTest_IsInGivenDay.java
deleted file mode 100644
index 2b43abc1..00000000
--- a/tests/unit/src/com/android/tv/util/UtilsTest_IsInGivenDay.java
+++ /dev/null
@@ -1,68 +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.util;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.support.test.filters.SmallTest;
-
-import org.junit.Test;
-
-import java.util.Calendar;
-import java.util.GregorianCalendar;
-import java.util.TimeZone;
-
-/**
- * Tests for {@link com.android.tv.util.Utils#isInGivenDay}.
- */
-@SmallTest
-public class UtilsTest_IsInGivenDay {
- @Test
- public void testIsInGivenDay() {
- assertTrue(Utils.isInGivenDay(
- new GregorianCalendar(2015, Calendar.JANUARY, 1).getTimeInMillis(),
- new GregorianCalendar(2015, Calendar.JANUARY, 1, 0, 30).getTimeInMillis()));
- }
-
- @Test
- public void testIsNotInGivenDay() {
- assertFalse(Utils.isInGivenDay(
- new GregorianCalendar(2015, Calendar.JANUARY, 1).getTimeInMillis(),
- new GregorianCalendar(2015, Calendar.JANUARY, 2).getTimeInMillis()));
- }
-
- @Test
- public void testIfTimeZoneApplied() {
- TimeZone timeZone = TimeZone.getDefault();
-
- TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul"));
-
- // 2015.01.01 00:00 in KST = 2014.12.31 15:00 in UTC
- long date2015StartMs =
- new GregorianCalendar(2015, Calendar.JANUARY, 1).getTimeInMillis();
-
- // 2015.01.01 10:00 in KST = 2015.01.01 01:00 in UTC
- long date2015Start10AMMs =
- new GregorianCalendar(2015, Calendar.JANUARY, 1, 10, 0).getTimeInMillis();
-
- // Those two times aren't in the same day in UTC, but they are in KST.
- assertTrue(Utils.isInGivenDay(date2015StartMs, date2015Start10AMMs));
-
- TimeZone.setDefault(timeZone);
- }
-}
diff --git a/tests/unit/src/com/android/tv/util/ImageCacheTest.java b/tests/unit/src/com/android/tv/util/images/ImageCacheTest.java
index a76194b8..b7715c4a 100644
--- a/tests/unit/src/com/android/tv/util/ImageCacheTest.java
+++ b/tests/unit/src/com/android/tv/util/images/ImageCacheTest.java
@@ -14,23 +14,22 @@
* limitations under the License.
*/
-package com.android.tv.util;
+package com.android.tv.util.images;
-import static com.android.tv.util.BitmapUtils.createScaledBitmapInfo;
-import static org.junit.Assert.assertSame;
+import static com.android.tv.util.images.BitmapUtils.createScaledBitmapInfo;
+import static com.google.common.truth.Truth.assertWithMessage;
import android.graphics.Bitmap;
import android.support.test.filters.MediumTest;
-
-import com.android.tv.util.BitmapUtils.ScaledBitmapInfo;
-
+import android.support.test.runner.AndroidJUnit4;
+import com.android.tv.util.images.BitmapUtils.ScaledBitmapInfo;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
-/**
- * Tests for {@link ImageCache}.
- */
+/** Tests for {@link ImageCache}. */
@MediumTest
+@RunWith(AndroidJUnit4.class)
public class ImageCacheTest {
private static final Bitmap ORIG = Bitmap.createBitmap(100, 100, Bitmap.Config.RGB_565);
@@ -47,34 +46,34 @@ public class ImageCacheTest {
mImageCache = ImageCache.newInstance(0.1f);
}
- //TODO: Empty the cache in the setup. Try using @VisibleForTesting
+ // TODO: Empty the cache in the setup. Try using @VisibleForTesting
@Test
public void testPutIfLarger_smaller() throws Exception {
- mImageCache.putIfNeeded( INFO_50);
- assertSame("before", INFO_50, mImageCache.get(KEY));
+ mImageCache.putIfNeeded(INFO_50);
+ assertWithMessage("before").that(mImageCache.get(KEY)).isSameAs(INFO_50);
- mImageCache.putIfNeeded( INFO_25);
- assertSame("after", INFO_50, mImageCache.get(KEY));
+ mImageCache.putIfNeeded(INFO_25);
+ assertWithMessage("after").that(mImageCache.get(KEY)).isSameAs(INFO_50);
}
@Test
public void testPutIfLarger_larger() throws Exception {
- mImageCache.putIfNeeded( INFO_50);
- assertSame("before", INFO_50, mImageCache.get(KEY));
+ mImageCache.putIfNeeded(INFO_50);
+ assertWithMessage("before").that(mImageCache.get(KEY)).isSameAs(INFO_50);
mImageCache.putIfNeeded(INFO_100);
- assertSame("after", INFO_100, mImageCache.get(KEY));
+ assertWithMessage("after").that(mImageCache.get(KEY)).isSameAs(INFO_100);
}
@Test
public void testPutIfLarger_alreadyMax() throws Exception {
- mImageCache.putIfNeeded( INFO_100);
- assertSame("before", INFO_100, mImageCache.get(KEY));
+ mImageCache.putIfNeeded(INFO_100);
+ assertWithMessage("before").that(mImageCache.get(KEY)).isSameAs(INFO_100);
- mImageCache.putIfNeeded( INFO_200);
- assertSame("after", INFO_100, mImageCache.get(KEY));
+ mImageCache.putIfNeeded(INFO_200);
+ assertWithMessage("after").that(mImageCache.get(KEY)).isSameAs(INFO_100);
}
-} \ No newline at end of file
+}
diff --git a/tests/unit/src/com/android/tv/util/images/ScaledBitmapInfoTest.java b/tests/unit/src/com/android/tv/util/images/ScaledBitmapInfoTest.java
new file mode 100644
index 00000000..005775b6
--- /dev/null
+++ b/tests/unit/src/com/android/tv/util/images/ScaledBitmapInfoTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2017 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.images;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.graphics.Bitmap;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import com.android.tv.util.images.BitmapUtils.ScaledBitmapInfo;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for {@link ScaledBitmapInfo}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ScaledBitmapInfoTest {
+ private static final Bitmap B80x100 = Bitmap.createBitmap(80, 100, Bitmap.Config.RGB_565);
+ private static final Bitmap B960x1440 = Bitmap.createBitmap(960, 1440, Bitmap.Config.RGB_565);
+
+ @Test
+ public void testSize_B100x100to50x50() {
+ ScaledBitmapInfo actual = BitmapUtils.createScaledBitmapInfo("B80x100", B80x100, 50, 50);
+ assertScaledBitmapSize(2, 40, 50, actual);
+ }
+
+ @Test
+ public void testNeedsToReload_B100x100to50x50() {
+ ScaledBitmapInfo actual = BitmapUtils.createScaledBitmapInfo("B80x100", B80x100, 50, 50);
+ assertNeedsToReload(false, actual, 25, 25);
+ assertNeedsToReload(false, actual, 50, 50);
+ assertNeedsToReload(false, actual, 99, 99);
+ assertNeedsToReload(true, actual, 100, 100);
+ assertNeedsToReload(true, actual, 101, 101);
+ }
+
+ /** Reproduces <a href="http://b/20488453">b/20488453</a>. */
+ @Test
+ public void testBug20488453() {
+ ScaledBitmapInfo actual =
+ BitmapUtils.createScaledBitmapInfo("B960x1440", B960x1440, 284, 160);
+ assertScaledBitmapSize(8, 107, 160, actual);
+ assertNeedsToReload(false, actual, 284, 160);
+ }
+
+ private static void assertNeedsToReload(
+ boolean expected, ScaledBitmapInfo scaledBitmap, int reqWidth, int reqHeight) {
+ assertWithMessage(scaledBitmap.id + " needToReload(" + reqWidth + "," + reqHeight + ")")
+ .that(scaledBitmap.needToReload(reqWidth, reqHeight))
+ .isEqualTo(expected);
+ }
+
+ private static void assertScaledBitmapSize(
+ int expectedInSampleSize,
+ int expectedWidth,
+ int expectedHeight,
+ ScaledBitmapInfo actual) {
+ assertWithMessage(actual.id + " inSampleSize")
+ .that(actual.inSampleSize)
+ .isEqualTo(expectedInSampleSize);
+ assertWithMessage(actual.id + " width").that(actual.bitmap.getWidth()).isEqualTo(expectedWidth);
+ assertWithMessage(actual.id + " height")
+ .that(actual.bitmap.getHeight())
+ .isEqualTo(expectedHeight);
+ }
+}
diff --git a/tuner/Android.mk b/tuner/Android.mk
new file mode 100644
index 00000000..ce17a3ce
--- /dev/null
+++ b/tuner/Android.mk
@@ -0,0 +1,42 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# Include all java and proto files.
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ $(call all-proto-files-under, proto)
+
+
+LOCAL_MODULE := live-tv-tuner
+LOCAL_MODULE_CLASS := STATIC_JAVA_LIBRARIES
+LOCAL_MODULE_TAGS := optional
+LOCAL_SDK_VERSION := system_current
+
+LOCAL_USE_AAPT2 := true
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := nano
+LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/proto/
+LOCAL_PROTO_JAVA_OUTPUT_PARAMS := enum_style=java
+
+LOCAL_RESOURCE_DIR := \
+ $(LOCAL_PATH)/res \
+
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations \
+ lib-exoplayer \
+ lib-exoplayer-v2-core \
+
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+ android-support-compat \
+ android-support-core-ui \
+ android-support-tv-provider \
+ android-support-v7-palette \
+ android-support-v7-recyclerview \
+ android-support-v17-leanback \
+ android-support-tv-provider \
+ tv-common \
+
+include $(LOCAL_PATH)/buildconfig.mk
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
diff --git a/tuner/AndroidManifest.xml b/tuner/AndroidManifest.xml
new file mode 100644
index 00000000..af80f692
--- /dev/null
+++ b/tuner/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 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
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tv.tuner"
+ android:versionCode="1">
+ <uses-sdk android:targetSdkVersion="26" android:minSdkVersion="21"/>
+ <application />
+</manifest>
diff --git a/tuner/BuildConfig.java.in b/tuner/BuildConfig.java.in
new file mode 100644
index 00000000..85967fad
--- /dev/null
+++ b/tuner/BuildConfig.java.in
@@ -0,0 +1,8 @@
+/* This file is auto generated. Do not modify. */
+package com.android.tv.tuner;
+
+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/tuner/SampleDvbTuner/AndroidManifest.xml b/tuner/SampleDvbTuner/AndroidManifest.xml
new file mode 100755
index 00000000..740989ad
--- /dev/null
+++ b/tuner/SampleDvbTuner/AndroidManifest.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tv.tuner.sample.dvb" >
+
+ <uses-sdk
+ android:minSdkVersion="23"
+ android:targetSdkVersion="26" />
+
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.READ_CONTENT_RATING_SYSTEMS" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_TV_LISTINGS" />
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+ <uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" />
+ <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
+
+ <!-- Permissions/feature for USB tuner -->
+ <uses-permission android:name="android.permission.DVB_DEVICE" />
+
+ <uses-feature
+ android:name="android.hardware.usb.host"
+ android:required="false" />
+
+ <!-- Limit only for Android TV -->
+ <uses-feature
+ android:name="android.software.leanback"
+ android:required="true" />
+ <uses-feature
+ android:name="android.software.live_tv"
+ android:required="true" />
+ <uses-feature
+ android:name="android.hardware.touchscreen"
+ android:required="false" />
+
+ <application
+ android:name="com.android.tv.tuner.sample.dvb.app.SampleDvbTuner"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/sample_dvb_tuner_app_name" >
+ <activity
+ android:name="com.google.android.gms.common.api.GoogleApiActivity"
+ android:exported="false"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar" />
+
+ <activity
+ android:name="com.android.tv.tuner.sample.dvb.setup.SampleDvbTunerSetupActivity"
+ android:configChanges="keyboard|keyboardHidden"
+ android:exported="true"
+ android:label="@string/sample_dvb_tuner_app_name"
+ android:launchMode="singleInstance"
+ android:theme="@style/Theme.Setup.GuidedStep" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
+
+ <service
+ android:name="com.android.tv.tuner.sample.dvb.tvinput.SampleDvbTunerTvInputService"
+ android:label="@string/sample_dvb_tuner_app_name"
+ android:permission="android.permission.BIND_TV_INPUT"
+ android:process="com.google.android.tv.tuner.sample.dvb.tvinput" >
+ <intent-filter>
+ <action android:name="android.media.tv.TvInputService" />
+ </intent-filter>
+
+ <meta-data
+ android:name="android.media.tv.input"
+ android:resource="@xml/sample_dvb_tvinputservice" />
+ </service>
+ <service
+ android:name="com.android.tv.tuner.tvinput.TunerStorageCleanUpService"
+ android:exported="false"
+ android:permission="android.permission.BIND_JOB_SERVICE"
+ android:process="com.android.tv.tuner" />
+ </application>
+
+</manifest> \ No newline at end of file
diff --git a/tuner/SampleDvbTuner/ResourceManifest.xml b/tuner/SampleDvbTuner/ResourceManifest.xml
new file mode 100644
index 00000000..e13f9584
--- /dev/null
+++ b/tuner/SampleDvbTuner/ResourceManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tv.tuner.sample.dvb" xmlns:tools="http://schemas.android.com/tools">
+
+ <uses-sdk android:targetSdkVersion="26" android:minSdkVersion="23"/>
+
+ <!-- Only used for resources-->
+</manifest>
diff --git a/tuner/SampleDvbTuner/res/mipmap-hdpi/ic_launcher.png b/tuner/SampleDvbTuner/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..b5c51706
--- /dev/null
+++ b/tuner/SampleDvbTuner/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/tuner/SampleDvbTuner/res/mipmap-mdpi/ic_launcher.png b/tuner/SampleDvbTuner/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..66297216
--- /dev/null
+++ b/tuner/SampleDvbTuner/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/tuner/SampleDvbTuner/res/mipmap-xhdpi/ic_launcher.png b/tuner/SampleDvbTuner/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..f259d1c9
--- /dev/null
+++ b/tuner/SampleDvbTuner/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tuner/SampleDvbTuner/res/mipmap-xxhdpi/ic_launcher.png b/tuner/SampleDvbTuner/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..421cd08b
--- /dev/null
+++ b/tuner/SampleDvbTuner/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tuner/SampleDvbTuner/res/mipmap-xxxhdpi/ic_launcher.png b/tuner/SampleDvbTuner/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000..91be3220
--- /dev/null
+++ b/tuner/SampleDvbTuner/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/tuner/SampleDvbTuner/res/values/strings.xml b/tuner/SampleDvbTuner/res/values/strings.xml
new file mode 100644
index 00000000..04b9e1bf
--- /dev/null
+++ b/tuner/SampleDvbTuner/res/values/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Name of this application. It appears in TV app UI, as one of available TV inputs. -->
+ <string name="sample_dvb_tuner_app_name" translatable="false">Sample DVB Tuner</string>
+</resources> \ No newline at end of file
diff --git a/tuner/SampleDvbTuner/res/xml/sample_dvb_tvinputservice.xml b/tuner/SampleDvbTuner/res/xml/sample_dvb_tvinputservice.xml
new file mode 100644
index 00000000..f0e4a636
--- /dev/null
+++ b/tuner/SampleDvbTuner/res/xml/sample_dvb_tvinputservice.xml
@@ -0,0 +1,39 @@
+<?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.
+ -->
+
+<!--
+/**
+ * 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.
+ */
+-->
+
+<tv-input xmlns:android="http://schemas.android.com/apk/res/android"
+ android:setupActivity="com.android.tv.tuner.sample.dvb.setup.SampleDvbTunerSetupActivity"
+ android:canRecord="true"
+ android:tunerCount="1" />
diff --git a/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/AndroidManifest.xml b/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/AndroidManifest.xml
new file mode 100644
index 00000000..adb8e309
--- /dev/null
+++ b/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 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.
+ -->
+
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tv.tuner.sample.dvb" xmlns:tools="http://schemas.android.com/tools">
+
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.READ_CONTENT_RATING_SYSTEMS" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_TV_LISTINGS" />
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+ <uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" />
+ <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
+
+ <!-- Permissions/feature for USB tuner -->
+ <uses-permission android:name="android.permission.DVB_DEVICE" />
+ <uses-feature android:name="android.hardware.usb.host" android:required="false" />
+
+ <!-- Limit only for Android TV -->
+ <uses-feature android:name="android.software.leanback" android:required="true" />
+ <uses-feature android:name="android.software.live_tv" android:required="true" />
+ <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
+ <uses-sdk android:targetSdkVersion="26" android:minSdkVersion="23"/>
+ <application />
+</manifest>
diff --git a/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/README.md b/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/README.md
new file mode 100644
index 00000000..d2c63114
--- /dev/null
+++ b/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/README.md
@@ -0,0 +1,37 @@
+# SampleDvbTuner
+
+SampleDvbTuner is the reference DVB Tuner. Partners should copy these files to
+their own directory and modify as needed.
+
+## Prerequisites
+
+* A DVB Tuner
+ * A Nexus player with a USB Tuner attached will work.
+* system privileged app
+ * The DVB_DEVICE permission requires the app to be a privileged system app
+
+## First install
+
+#### Root
+
+```bash
+adb root
+adb remount
+```
+
+### modify privapp-permissions-atv.xml
+
+Edit system/etc/permissions/privapp-permissions-atv.xml
+
+```xml
+<privapp-permissions package="com.android.tv.tuner.sample.dvb">
+ <permission name="android.permission.DVB_DEVICE"/>
+</privapp-permissions>
+```
+
+### Push to system/priv-app
+
+```bash
+adb shell mkdir /system/priv-app/SampleDvbTuner
+adb push <path to apk> /system/priv-app/SampleDvbTuner/SampleDvbTuner.apk
+```
diff --git a/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/app/SampleDvbTuner.java b/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/app/SampleDvbTuner.java
new file mode 100644
index 00000000..15e90437
--- /dev/null
+++ b/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/app/SampleDvbTuner.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 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.tuner.sample.dvb.app;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.media.tv.TvContract;
+import com.android.tv.common.BaseApplication;
+import com.android.tv.common.actions.InputSetupActionUtils;
+import com.android.tv.common.config.DefaultConfigManager;
+import com.android.tv.common.config.api.RemoteConfig;
+import com.android.tv.common.util.CommonUtils;
+import com.android.tv.tuner.sample.dvb.tvinput.SampleDvbTunerTvInputService;
+import com.android.tv.tuner.setup.LiveTvTunerSetupActivity;
+
+/** The top level application for Sample DVB Tuner. */
+public class SampleDvbTuner extends BaseApplication {
+ private String mEmbeddedInputId;
+ private RemoteConfig mRemoteConfig;
+
+ @Override
+ public Intent getTunerSetupIntent(Context context) {
+ // Make an intent to launch the setup activity of TV tuner input.
+ Intent intent =
+ CommonUtils.createSetupIntent(
+ new Intent(context, LiveTvTunerSetupActivity.class), mEmbeddedInputId);
+ intent.putExtra(InputSetupActionUtils.EXTRA_INPUT_ID, mEmbeddedInputId);
+ return intent;
+ }
+
+ @Override
+ public synchronized String getEmbeddedTunerInputId() {
+ if (mEmbeddedInputId == null) {
+ mEmbeddedInputId =
+ TvContract.buildInputId(
+ new ComponentName(this, SampleDvbTunerTvInputService.class));
+ }
+ return mEmbeddedInputId;
+ }
+
+ @Override
+ public RemoteConfig getRemoteConfig() {
+ if (mRemoteConfig == null) {
+ // No need to synchronize this, it does not hurt to create two and throw one away.
+ mRemoteConfig = DefaultConfigManager.createInstance(this).getRemoteConfig();
+ }
+ return mRemoteConfig;
+ }
+}
diff --git a/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/setup/SampleDvbTunerSetupActivity.java b/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/setup/SampleDvbTunerSetupActivity.java
new file mode 100644
index 00000000..54b3a9e7
--- /dev/null
+++ b/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/setup/SampleDvbTunerSetupActivity.java
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2018 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.tuner.sample.dvb.setup;
+
+import android.app.FragmentManager;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.media.tv.TvInputInfo;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+import android.view.KeyEvent;
+import com.android.tv.common.BaseApplication;
+import com.android.tv.common.feature.CommonFeatures;
+import com.android.tv.common.ui.setup.SetupFragment;
+import com.android.tv.common.ui.setup.SetupMultiPaneFragment;
+import com.android.tv.common.util.PostalCodeUtils;
+import com.android.tv.tuner.TunerHal;
+import com.android.tv.tuner.sample.dvb.R;
+import com.android.tv.tuner.setup.BaseTunerSetupActivity;
+import com.android.tv.tuner.setup.ConnectionTypeFragment;
+import com.android.tv.tuner.setup.LineupFragment;
+import com.android.tv.tuner.setup.PostalCodeFragment;
+import com.android.tv.tuner.setup.ScanFragment;
+import com.android.tv.tuner.setup.ScanResultFragment;
+import com.android.tv.tuner.setup.WelcomeFragment;
+import com.google.android.tv.partner.support.EpgContract;
+import com.google.android.tv.partner.support.EpgInput;
+import com.google.android.tv.partner.support.EpgInputs;
+import com.google.android.tv.partner.support.Lineup;
+import com.google.android.tv.partner.support.Lineups;
+import com.google.android.tv.partner.support.TunerSetupUtils;
+import java.util.ArrayList;
+import java.util.List;
+
+/** An activity that serves Live TV tuner setup process. */
+public class SampleDvbTunerSetupActivity extends BaseTunerSetupActivity {
+ private static final String TAG = "SampleDvbTunerSetupActivity";
+ private static final boolean DEBUG = false;
+
+ private static final int FETCH_LINEUP_TIMEOUT_MS = 10000; // 10 seconds
+ private static final int FETCH_LINEUP_RETRY_TIMEOUT_MS = 20000; // 20 seconds
+ private static final String OTAD_PREFIX = "OTAD";
+ private static final String STRING_BROADCAST_DIGITAL = "Broadcast Digital";
+
+ private LineupFragment currentLineupFragment;
+
+ private List<String> channelNumbers;
+ private List<Lineup> lineups;
+ private Lineup selectedLineup;
+ private List<Pair<Lineup, Integer>> lineupMatchCountPair;
+ private FetchLineupTask fetchLineupTask;
+ private EpgInput epgInput;
+ private String postalCode;
+ private final Handler handler = new Handler();
+ private final Runnable cancelFetchLineupTaskRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ cancelFetchLineup();
+ }
+ };
+ private String embeddedInputId;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ if (DEBUG) {
+ Log.d(TAG, "onCreate");
+ }
+ embeddedInputId = BaseApplication.getSingletons(this).getEmbeddedTunerInputId();
+ new QueryEpgInputTask(embeddedInputId).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void executeGetTunerTypeAndCountAsyncTask() {
+ new AsyncTask<Void, Void, Integer>() {
+ @Override
+ protected Integer doInBackground(Void... arg0) {
+ return TunerHal.getTunerTypeAndCount(SampleDvbTunerSetupActivity.this).first;
+ }
+
+ @Override
+ protected void onPostExecute(Integer result) {
+ if (!SampleDvbTunerSetupActivity.this.isDestroyed()) {
+ mTunerType = result;
+ if (result == null) {
+ finish();
+ } else if (!mActivityStopped) {
+ showInitialFragment();
+ } else {
+ mPendingShowInitialFragment = true;
+ }
+ }
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+
+ @Override
+ protected boolean executeAction(String category, int actionId, Bundle params) {
+ switch (category) {
+ case WelcomeFragment.ACTION_CATEGORY:
+ switch (actionId) {
+ case SetupMultiPaneFragment.ACTION_DONE:
+ super.executeAction(category, actionId, params);
+ break;
+ default:
+ String postalCode = PostalCodeUtils.getLastPostalCode(this);
+ if (mNeedToShowPostalCodeFragment
+ || (CommonFeatures.ENABLE_CLOUD_EPG_REGION.isEnabled(
+ getApplicationContext())
+ && TextUtils.isEmpty(postalCode))) {
+ // We cannot get postal code automatically. Postal code input fragment
+ // should always be shown even if users have input some valid postal
+ // code in this activity before.
+ mNeedToShowPostalCodeFragment = true;
+ showPostalCodeFragment();
+ } else {
+ lineups = null;
+ selectedLineup = null;
+ this.postalCode = postalCode;
+ restartFetchLineupTask();
+ showConnectionTypeFragment();
+ }
+ break;
+ }
+ return true;
+ case PostalCodeFragment.ACTION_CATEGORY:
+ lineups = null;
+ selectedLineup = null;
+ switch (actionId) {
+ case SetupMultiPaneFragment.ACTION_DONE:
+ String postalCode = params.getString(PostalCodeFragment.KEY_POSTAL_CODE);
+ if (postalCode != null) {
+ this.postalCode = postalCode;
+ restartFetchLineupTask();
+ }
+ // fall through
+ case SetupMultiPaneFragment.ACTION_SKIP:
+ showConnectionTypeFragment();
+ break;
+ default: // fall out
+ }
+ return true;
+ case ConnectionTypeFragment.ACTION_CATEGORY:
+ channelNumbers = null;
+ lineupMatchCountPair = null;
+ return super.executeAction(category, actionId, params);
+ case ScanFragment.ACTION_CATEGORY:
+ switch (actionId) {
+ case ScanFragment.ACTION_CANCEL:
+ getFragmentManager().popBackStack();
+ return true;
+ case ScanFragment.ACTION_FINISH:
+ clearTunerHal();
+ channelNumbers =
+ params.getStringArrayList(ScanFragment.KEY_CHANNEL_NUMBERS);
+ selectedLineup = null;
+ if (CommonFeatures.ENABLE_CLOUD_EPG_REGION.isEnabled(
+ getApplicationContext())
+ && channelNumbers != null
+ && !channelNumbers.isEmpty()
+ && !TextUtils.isEmpty(this.postalCode)) {
+ showLineupFragment();
+ } else {
+ showScanResultFragment();
+ }
+ return true;
+ default: // fall out
+ }
+ break;
+ case LineupFragment.ACTION_CATEGORY:
+ switch (actionId) {
+ case LineupFragment.ACTION_SKIP:
+ selectedLineup = null;
+ currentLineupFragment = null;
+ showScanResultFragment();
+ break;
+ case LineupFragment.ACTION_ID_RETRY:
+ currentLineupFragment.onRetry();
+ restartFetchLineupTask();
+ handler.postDelayed(
+ cancelFetchLineupTaskRunnable, FETCH_LINEUP_RETRY_TIMEOUT_MS);
+ break;
+ default:
+ if (actionId >= 0 && actionId < lineupMatchCountPair.size()) {
+ if (DEBUG) {
+ if (selectedLineup != null) {
+ Log.d(
+ TAG,
+ "Lineup " + selectedLineup.getName() + " is selected.");
+ }
+ }
+ selectedLineup = lineupMatchCountPair.get(actionId).first;
+ }
+ currentLineupFragment = null;
+ showScanResultFragment();
+ break;
+ }
+ return true;
+ case ScanResultFragment.ACTION_CATEGORY:
+ switch (actionId) {
+ case SetupMultiPaneFragment.ACTION_DONE:
+ new SampleDvbTunerSetupActivity.InsertOrModifyEpgInputTask(
+ selectedLineup, embeddedInputId)
+ .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ break;
+ default:
+ // scan again
+ if (lineups == null || lineups.isEmpty()) {
+ lineups = null;
+ restartFetchLineupTask();
+ }
+ super.executeAction(category, actionId, params);
+ break;
+ }
+ return true;
+ default: // fall out
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ FragmentManager manager = getFragmentManager();
+ int count = manager.getBackStackEntryCount();
+ if (count > 0) {
+ String lastTag = manager.getBackStackEntryAt(count - 1).getName();
+ if (LineupFragment.class.getCanonicalName().equals(lastTag) && count >= 2) {
+ // Pops fragment including ScanFragment.
+ manager.popBackStack(
+ manager.getBackStackEntryAt(count - 2).getName(),
+ FragmentManager.POP_BACK_STACK_INCLUSIVE);
+ return true;
+ }
+ if (ScanResultFragment.class.getCanonicalName().equals(lastTag) && count >= 2) {
+ String secondLastTag = manager.getBackStackEntryAt(count - 2).getName();
+ if (ScanFragment.class.getCanonicalName().equals(secondLastTag)) {
+ // Pops fragment including ScanFragment.
+ manager.popBackStack(
+ secondLastTag, FragmentManager.POP_BACK_STACK_INCLUSIVE);
+ return true;
+ }
+ if (LineupFragment.class.getCanonicalName().equals(secondLastTag)) {
+ currentLineupFragment =
+ (LineupFragment) manager.findFragmentByTag(secondLastTag);
+ if (lineups == null || lineups.isEmpty()) {
+ lineups = null;
+ restartFetchLineupTask();
+ }
+ }
+ } else if (ScanFragment.class.getCanonicalName().equals(lastTag)) {
+ mLastScanFragment.finishScan(true);
+ return true;
+ }
+ }
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
+ private void showLineupFragment() {
+ if (lineupMatchCountPair == null && lineups != null) {
+ lineupMatchCountPair = TunerSetupUtils.lineupChannelMatchCount(lineups, channelNumbers);
+ }
+ currentLineupFragment = new LineupFragment();
+ currentLineupFragment.setArguments(getArgsForLineupFragment());
+ currentLineupFragment.setShortDistance(
+ SetupFragment.FRAGMENT_ENTER_TRANSITION | SetupFragment.FRAGMENT_RETURN_TRANSITION);
+ handler.removeCallbacksAndMessages(null);
+ showFragment(currentLineupFragment, true);
+ handler.postDelayed(cancelFetchLineupTaskRunnable, FETCH_LINEUP_TIMEOUT_MS);
+ }
+
+ private Bundle getArgsForLineupFragment() {
+ Bundle args = new Bundle();
+ if (lineupMatchCountPair == null) {
+ return args;
+ }
+ ArrayList<String> lineupNames = new ArrayList<>(lineupMatchCountPair.size());
+ ArrayList<Integer> matchNumbers = new ArrayList<>(lineupMatchCountPair.size());
+ int defaultLineupIndex = 0;
+ for (Pair<Lineup, Integer> pair : lineupMatchCountPair) {
+ Lineup lineup = pair.first;
+ String name;
+ if (!TextUtils.isEmpty(lineup.getName())) {
+ name = lineup.getName();
+ } else {
+ name = lineup.getId();
+ }
+ if (name.equals(OTAD_PREFIX + postalCode) || name.equals(STRING_BROADCAST_DIGITAL)) {
+ // rename OTA / antenna lineups
+ name = getString(R.string.ut_lineup_name_antenna);
+ }
+ lineupNames.add(name);
+ matchNumbers.add(pair.second);
+ if (epgInput != null && TextUtils.equals(lineup.getId(), epgInput.getLineupId())) {
+ // The last index is the current one.
+ defaultLineupIndex = lineupNames.size() - 1;
+ }
+ }
+ args.putStringArrayList(LineupFragment.KEY_LINEUP_NAMES, lineupNames);
+ args.putIntegerArrayList(LineupFragment.KEY_MATCH_NUMBERS, matchNumbers);
+ args.putInt(LineupFragment.KEY_DEFAULT_LINEUP, defaultLineupIndex);
+ return args;
+ }
+
+ private void cancelFetchLineup() {
+ if (fetchLineupTask == null) {
+ return;
+ }
+ AsyncTask.Status status = fetchLineupTask.getStatus();
+ if (status == AsyncTask.Status.RUNNING || status == AsyncTask.Status.PENDING) {
+ fetchLineupTask.cancel(true);
+ fetchLineupTask = null;
+ if (currentLineupFragment != null) {
+ currentLineupFragment.onLineupNotFound();
+ }
+ }
+ }
+
+ private void restartFetchLineupTask() {
+ if (!CommonFeatures.ENABLE_CLOUD_EPG_REGION.isEnabled(getApplicationContext())
+ || TextUtils.isEmpty(postalCode)) {
+ return;
+ }
+ if (fetchLineupTask != null) {
+ fetchLineupTask.cancel(true);
+ }
+ handler.removeCallbacksAndMessages(null);
+ fetchLineupTask = new FetchLineupTask(getContentResolver(), postalCode);
+ fetchLineupTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+
+ private class FetchLineupTask extends AsyncTask<Void, Void, List<Lineup>> {
+ private final ContentResolver contentResolver;
+ private final String postalCode;
+
+ FetchLineupTask(ContentResolver contentResolver, String postalCode) {
+ this.contentResolver = contentResolver;
+ this.postalCode = postalCode;
+ }
+
+ @Override
+ protected List<Lineup> doInBackground(Void... args) {
+ if (contentResolver == null || TextUtils.isEmpty(postalCode)) {
+ return new ArrayList<>();
+ }
+ return new ArrayList<>(Lineups.query(contentResolver, postalCode));
+ }
+
+ @Override
+ protected void onPostExecute(List<Lineup> lineups) {
+ if (DEBUG) {
+ if (lineups != null) {
+ Log.d(TAG, "FetchLineupTask fetched " + lineups.size() + " lineups");
+ } else {
+ Log.d(TAG, "FetchLineupTask returned null");
+ }
+ }
+ SampleDvbTunerSetupActivity.this.lineups = lineups;
+ if (currentLineupFragment != null) {
+ if (lineups == null || lineups.isEmpty()) {
+ currentLineupFragment.onLineupNotFound();
+ } else {
+ lineupMatchCountPair =
+ TunerSetupUtils.lineupChannelMatchCount(
+ SampleDvbTunerSetupActivity.this.lineups, channelNumbers);
+ currentLineupFragment.onLineupFound(getArgsForLineupFragment());
+ }
+ }
+ }
+ }
+
+ private class InsertOrModifyEpgInputTask extends AsyncTask<Void, Void, Void> {
+ private final Lineup lineup;
+ private final String inputId;
+
+ InsertOrModifyEpgInputTask(@Nullable Lineup lineup, String inputId) {
+ this.lineup = lineup;
+ this.inputId = inputId;
+ }
+
+ @Override
+ protected Void doInBackground(Void... args) {
+ if (lineup == null
+ || (SampleDvbTunerSetupActivity.this.epgInput != null
+ && TextUtils.equals(
+ lineup.getId(),
+ SampleDvbTunerSetupActivity.this.epgInput.getLineupId()))) {
+ return null;
+ }
+ ContentValues values = new ContentValues();
+ values.put(EpgContract.EpgInputs.COLUMN_INPUT_ID, inputId);
+ values.put(EpgContract.EpgInputs.COLUMN_LINEUP_ID, lineup.getId());
+
+ ContentResolver contentResolver = getContentResolver();
+ if (SampleDvbTunerSetupActivity.this.epgInput != null) {
+ values.put(
+ EpgContract.EpgInputs.COLUMN_ID,
+ SampleDvbTunerSetupActivity.this.epgInput.getId());
+ EpgInputs.update(contentResolver, EpgInput.createEpgChannel(values));
+ return null;
+ }
+ EpgInput epgInput = EpgInputs.queryEpgInput(contentResolver, inputId);
+ if (epgInput == null) {
+ contentResolver.insert(EpgContract.EpgInputs.CONTENT_URI, values);
+ } else {
+ values.put(EpgContract.EpgInputs.COLUMN_ID, epgInput.getId());
+ EpgInputs.update(contentResolver, EpgInput.createEpgChannel(values));
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ Intent data = new Intent();
+ data.putExtra(TvInputInfo.EXTRA_INPUT_ID, inputId);
+ data.putExtra(EpgContract.EXTRA_USE_CLOUD_EPG, true);
+ setResult(RESULT_OK, data);
+ finish();
+ }
+ }
+
+ private class QueryEpgInputTask extends AsyncTask<Void, Void, EpgInput> {
+ private final String inputId;
+
+ QueryEpgInputTask(String inputId) {
+ this.inputId = inputId;
+ }
+
+ @Override
+ protected EpgInput doInBackground(Void... args) {
+ ContentResolver contentResolver = getContentResolver();
+ return EpgInputs.queryEpgInput(contentResolver, inputId);
+ }
+
+ @Override
+ protected void onPostExecute(EpgInput result) {
+ epgInput = result;
+ }
+ }
+}
diff --git a/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/tvinput/SampleDvbTunerTvInputService.java b/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/tvinput/SampleDvbTunerTvInputService.java
new file mode 100644
index 00000000..ae15affc
--- /dev/null
+++ b/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/tvinput/SampleDvbTunerTvInputService.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 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.tuner.sample.dvb.tvinput;
+
+import com.android.tv.tuner.tvinput.BaseTunerTvInputService;
+
+/** Sample DVB Tuner {@link android.media.tv.TvInputService}. */
+public class SampleDvbTunerTvInputService extends BaseTunerTvInputService {}
diff --git a/tuner/buildconfig.mk b/tuner/buildconfig.mk
new file mode 100644
index 00000000..cece7f2b
--- /dev/null
+++ b/tuner/buildconfig.mk
@@ -0,0 +1,39 @@
+#
+# 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.
+#
+
+# Emulate gradles BuildConfig.java
+
+ifeq "$(TARGET_BUILD_VARIANT)" "eng"
+ BC_DEBUG_STATUS := true
+else ifeq "$(TARGET_BUILD_VARIANT)" "userdebug"
+ BC_DEBUG_STATUS := true
+else
+ BC_DEBUG_STATUS := false
+endif
+
+ifeq "$(TARGET_BUILD_VARIANT)" "eng"
+ BC_ENG_STATUS := true
+else
+ BC_ENG_STATUS := false
+endif
+
+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/proto/channel.proto b/tuner/proto/channel.proto
index b4e67e07..1f994522 100644
--- a/proto/channel.proto
+++ b/tuner/proto/channel.proto
@@ -49,6 +49,13 @@ message TunerChannelProto {
optional AtscServiceType service_type = 22 [default = SERVICE_TYPE_ATSC_DIGITAL_TELEVISION];
optional bool recording_prohibited = 23;
optional string video_format = 24;
+ /**
+ * The flag indicating whether this TV channel is locked or not.
+ * This is primarily used for alternative parental control to prevent unauthorized users from
+ * watching the current channel regardless of the content rating
+ * @see <a href="https://developer.android.com/reference/android/media/tv/TvContract.Channels.html#COLUMN_LOCKED">link</a>
+ */
+ optional bool locked = 25;
}
// Enum describing the types of tuner.
diff --git a/proto/track.proto b/tuner/proto/track.proto
index fe60fed5..fe60fed5 100644
--- a/proto/track.proto
+++ b/tuner/proto/track.proto
diff --git a/usbtuner-res/drawable-xhdpi/recommendation_antenna.png b/tuner/res/drawable-xhdpi/recommendation_antenna.png
index c4710bcc..c4710bcc 100644
--- a/usbtuner-res/drawable-xhdpi/recommendation_antenna.png
+++ b/tuner/res/drawable-xhdpi/recommendation_antenna.png
Binary files differ
diff --git a/usbtuner-res/drawable-xhdpi/usb_antenna.png b/tuner/res/drawable-xhdpi/usb_antenna.png
index ca5b2d72..ca5b2d72 100644
--- a/usbtuner-res/drawable-xhdpi/usb_antenna.png
+++ b/tuner/res/drawable-xhdpi/usb_antenna.png
Binary files differ
diff --git a/usbtuner-res/drawable/ut_scan_progress.xml b/tuner/res/drawable/ut_scan_progress.xml
index f4daf6c8..f4daf6c8 100644
--- a/usbtuner-res/drawable/ut_scan_progress.xml
+++ b/tuner/res/drawable/ut_scan_progress.xml
diff --git a/res/layout/guided_action_editable.xml b/tuner/res/layout/guided_action_editable.xml
index 84f56f8d..84f56f8d 100644
--- a/res/layout/guided_action_editable.xml
+++ b/tuner/res/layout/guided_action_editable.xml
diff --git a/usbtuner-res/layout/ut_channel_list.xml b/tuner/res/layout/ut_channel_list.xml
index 430234f8..430234f8 100644
--- a/usbtuner-res/layout/ut_channel_list.xml
+++ b/tuner/res/layout/ut_channel_list.xml
diff --git a/usbtuner-res/layout/ut_channel_scan.xml b/tuner/res/layout/ut_channel_scan.xml
index 415ac929..415ac929 100644
--- a/usbtuner-res/layout/ut_channel_scan.xml
+++ b/tuner/res/layout/ut_channel_scan.xml
diff --git a/usbtuner-res/layout/ut_overlay_view.xml b/tuner/res/layout/ut_overlay_view.xml
index 1ee2e340..1ee2e340 100644
--- a/usbtuner-res/layout/ut_overlay_view.xml
+++ b/tuner/res/layout/ut_overlay_view.xml
diff --git a/usbtuner-res/raw/ut_euro_dvbt_all b/tuner/res/raw/ut_euro_dvbt_all
index 101ee3eb..101ee3eb 100644
--- a/usbtuner-res/raw/ut_euro_dvbt_all
+++ b/tuner/res/raw/ut_euro_dvbt_all
diff --git a/usbtuner-res/raw/ut_kr_all b/tuner/res/raw/ut_kr_all
index 9c4d7e64..9c4d7e64 100644
--- a/usbtuner-res/raw/ut_kr_all
+++ b/tuner/res/raw/ut_kr_all
diff --git a/usbtuner-res/raw/ut_kr_atsc_center_frequencies_8vsb b/tuner/res/raw/ut_kr_atsc_center_frequencies_8vsb
index 14678d3a..14678d3a 100644
--- a/usbtuner-res/raw/ut_kr_atsc_center_frequencies_8vsb
+++ b/tuner/res/raw/ut_kr_atsc_center_frequencies_8vsb
diff --git a/usbtuner-res/raw/ut_kr_cable_standard_center_frequencies_qam256 b/tuner/res/raw/ut_kr_cable_standard_center_frequencies_qam256
index 258a179a..258a179a 100644
--- a/usbtuner-res/raw/ut_kr_cable_standard_center_frequencies_qam256
+++ b/tuner/res/raw/ut_kr_cable_standard_center_frequencies_qam256
diff --git a/usbtuner-res/raw/ut_kr_dev_cj_cable_center_frequencies_qam256 b/tuner/res/raw/ut_kr_dev_cj_cable_center_frequencies_qam256
index c52eeeea..c52eeeea 100644
--- a/usbtuner-res/raw/ut_kr_dev_cj_cable_center_frequencies_qam256
+++ b/tuner/res/raw/ut_kr_dev_cj_cable_center_frequencies_qam256
diff --git a/usbtuner-res/raw/ut_us_all b/tuner/res/raw/ut_us_all
index a5bd846a..a5bd846a 100644
--- a/usbtuner-res/raw/ut_us_all
+++ b/tuner/res/raw/ut_us_all
diff --git a/usbtuner-res/raw/ut_us_atsc_center_frequencies_8vsb b/tuner/res/raw/ut_us_atsc_center_frequencies_8vsb
index 245a68a8..245a68a8 100644
--- a/usbtuner-res/raw/ut_us_atsc_center_frequencies_8vsb
+++ b/tuner/res/raw/ut_us_atsc_center_frequencies_8vsb
diff --git a/usbtuner-res/raw/ut_us_cable_standard_center_frequencies_qam256 b/tuner/res/raw/ut_us_cable_standard_center_frequencies_qam256
index f42a5141..f42a5141 100644
--- a/usbtuner-res/raw/ut_us_cable_standard_center_frequencies_qam256
+++ b/tuner/res/raw/ut_us_cable_standard_center_frequencies_qam256
diff --git a/usbtuner-res/values/attrs.xml b/tuner/res/values/attrs.xml
index 4249ed2f..4249ed2f 100644
--- a/usbtuner-res/values/attrs.xml
+++ b/tuner/res/values/attrs.xml
diff --git a/usbtuner-res/values/colors.xml b/tuner/res/values/colors.xml
index 873ecc8e..873ecc8e 100644
--- a/usbtuner-res/values/colors.xml
+++ b/tuner/res/values/colors.xml
diff --git a/usbtuner-res/values/dimens.xml b/tuner/res/values/dimens.xml
index 1fe39f6c..1fe39f6c 100644
--- a/usbtuner-res/values/dimens.xml
+++ b/tuner/res/values/dimens.xml
diff --git a/usbtuner-res/values/integers.xml b/tuner/res/values/integers.xml
index 65d20c67..65d20c67 100644
--- a/usbtuner-res/values/integers.xml
+++ b/tuner/res/values/integers.xml
diff --git a/usbtuner-res/values/strings.xml b/tuner/res/values/strings.xml
index dd393c3e..58d7214c 100644
--- a/usbtuner-res/values/strings.xml
+++ b/tuner/res/values/strings.xml
@@ -127,18 +127,36 @@
</plurals>
<!-- Title for a button which will stop channel scanning process -->
<string name="ut_stop_channel_scan">STOP CHANNEL SCAN</string>
+
+ <!-- Title for lineup selection menu when TV lineups are found -->
+ <string name="ut_lineup_title_lineups_found" >Select a TV lineup</string>
+ <!-- Description for lineup selection menu when TV lineups are found.-->
+ <string name="ut_lineup_description_lineups_found" >Choose how you receive your TV signal.</string>
+ <!-- Title for lineup selection menu when no TV lineups is found -->
+ <string name="ut_lineup_title_lineups_not_found" >No TV lineups found</string>
+ <!-- Description for lineup selection menu when no TV lineups is found -->
+ <string name="ut_lineup_description_lineups_not_found" >Please make sure your device is connected to the internet and the zip code / postal code is correct.</string>
+ <!-- Title for lineup selection menu when fetching lineups -->
+ <string name="ut_lineup_title_fetching_lineups">Fetching TV lineups…</string>
+ <!-- Description for lineup selection menu when fetching lineups -->
+ <string name="ut_lineup_description_fetching_lineups">This may take a few minutes. Please wait…</string>
+ <!-- Description for a button when no channels match -->
+ <string name="ut_lineup_no_channels_matched">No channels match</string>
+ <!-- Number of matched channels for lineup selection menu when lineups were found -->
+ <plurals name="ut_lineup_channels_matched">
+ <item quantity="one"><xliff:g id="channels_matched_one">%1$d</xliff:g> channel matches</item>
+ <item quantity="other"><xliff:g id="channels_matched_other">%1$d</xliff:g> channels match</item>
+ </plurals>
+ <!-- A button to select a specific TV lineup of channels for the channels received by a "Television Antenna" -->
+ <string name="ut_lineup_name_antenna">Antenna</string>
<!-- Title for channel scanning result menu when channels were found -->
<plurals name="ut_result_found_title">
<item quantity="one">%1$d channel found</item>
<item quantity="other">%1$d channels found</item>
</plurals>
- <!-- Description for channel scanning result menu when channels were found -->
- <plurals name="ut_result_found_description">
- <item quantity="one">Nice! %1$d channel was found during the channel scan.
- If this doesn’t seem right, try adjusting the antenna position and scan again.</item>
- <item quantity="other">Nice! %1$d channels were found during the channel scan.
- If this doesn’t seem right, try adjusting the antenna position and scan again.</item>
- </plurals>
+ <!-- Channel description for channel scanning result menu when channels were found "scan again" should match the button in string ut_result_found_choices-->
+ <string name="ut_result_found_description">If that seems incorrect, try adjusting your antenna and scan again.</string>
+ <!-- Rescan description for channel scanning result menu when channels were found -->
<!-- Menu items for channel scanning result menu when channels were found -->
<string-array name="ut_result_found_choices">
<item>Done</item>
@@ -169,8 +187,8 @@
</string-array>
<!-- Notification of USB channel tuner setup strings. -->
- <!-- Name of notification channel which is for tuner setup. -->
- <string name="ut_setup_notification_channel_name" translatable="false">USB tuner set up</string>
+ <!-- Name of notification channel which is for tuner setup. "USB Tuner" is a TV Tuner that is connected via a USB connection.-->
+ <string name="ut_setup_notification_channel_name" >USB Tuner setup</string>
<!-- Content title of the notification to launch the setup application of
USB tuner TV input for scanning channels. -->
<string name="ut_setup_notification_content_title">Scan for TV channels</string>
@@ -188,4 +206,14 @@
<string name="msg_usb_tuner_disconnected">USB TV tuner disconnected.</string>
<!-- Message when Network tuner device is unplugged or disconnected. [CHAR LIMIT=NONE] -->
<string name="msg_network_tuner_disconnected">Network tuner disconnected.</string>
+
+ <!-- Title of postal/zip code input guided step fragment [CHAR LIMIT=30] -->
+ <string name="postal_code_guidance_title">Enter your ZIP Code.</string>
+ <!-- Description of postal/zip code input guided step fragment [CHAR LIMIT=NONE] -->
+ <string name="postal_code_guidance_description">Live TV app will use the ZIP Code to provide a complete program guide for the TV channels.</string>
+ <!-- Description of postal/zip code input edit text view to prompt users entering ZIP Code [CHAR LIMIT=30] -->
+ <string name="postal_code_action_description">Enter your ZIP Code</string>
+ <!-- Warning message shown in description field of postal/zip code input edit text view when user enters an invalid ZIP Code and presses Done [CHAR LIMIT=30] -->
+ <string name="postal_code_invalid_warning">Invalid ZIP Code</string>
+
</resources>
diff --git a/usbtuner-res/xml/ut_tvinputservice.xml b/tuner/res/xml/ut_tvinputservice.xml
index 9b7fdbc2..a2f45a91 100644
--- a/usbtuner-res/xml/ut_tvinputservice.xml
+++ b/tuner/res/xml/ut_tvinputservice.xml
@@ -34,6 +34,6 @@
-->
<tv-input xmlns:android="http://schemas.android.com/apk/res/android"
- android:setupActivity="com.android.tv.tuner.setup.TunerSetupActivity"
+ android:setupActivity="com.android.tv.tuner.setup.LiveTvTunerSetupActivity"
android:canRecord="true"
android:tunerCount="1" />
diff --git a/src/com/android/tv/tuner/ChannelScanFileParser.java b/tuner/src/com/android/tv/tuner/ChannelScanFileParser.java
index a255de3e..d2ed6c38 100644
--- a/src/com/android/tv/tuner/ChannelScanFileParser.java
+++ b/tuner/src/com/android/tv/tuner/ChannelScanFileParser.java
@@ -17,9 +17,7 @@
package com.android.tv.tuner;
import android.util.Log;
-
import com.android.tv.tuner.data.nano.Channel;
-
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
@@ -27,9 +25,7 @@ import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
-/**
- * Parses plain text formatted scan files, which contain the list of channels.
- */
+/** Parses plain text formatted scan files, which contain the list of channels. */
public class ChannelScanFileParser {
private static final String TAG = "ChannelScanFileParser";
@@ -40,22 +36,30 @@ public class ChannelScanFileParser {
public final String filename;
/**
* Radio frequency (channel) number specified at
- * https://en.wikipedia.org/wiki/North_American_television_frequencies
- * This can be {@code null} for cases like cable signal.
+ * https://en.wikipedia.org/wiki/North_American_television_frequencies This can be {@code
+ * null} for cases like cable signal.
*/
public final Integer radioFrequencyNumber;
- public static ScanChannel forTuner(int frequency, String modulation,
- Integer radioFrequencyNumber) {
- return new ScanChannel(Channel.TYPE_TUNER, frequency, modulation, null,
+ public static ScanChannel forTuner(
+ int frequency, String modulation, Integer radioFrequencyNumber) {
+ return new ScanChannel(
+ Channel.TunerType.TYPE_TUNER,
+ frequency,
+ modulation,
+ null,
radioFrequencyNumber);
}
public static ScanChannel forFile(int frequency, String filename) {
- return new ScanChannel(Channel.TYPE_FILE, frequency, "file:", filename, null);
+ return new ScanChannel(Channel.TunerType.TYPE_FILE, frequency, "file:", filename, null);
}
- private ScanChannel(int type, int frequency, String modulation, String filename,
+ private ScanChannel(
+ int type,
+ int frequency,
+ String modulation,
+ String filename,
Integer radioFrequencyNumber) {
this.type = type;
this.frequency = frequency;
@@ -68,9 +72,9 @@ public class ChannelScanFileParser {
/**
* Parses a given scan file and returns the list of {@link ScanChannel} objects.
*
- * @param is {@link InputStream} of a scan file. Each line matches one channel.
- * The line format of the scan file is as follows:<br>
- * "A &lt;frequency&gt; &lt;modulation&gt;".
+ * @param is {@link InputStream} of a scan file. Each line matches one channel. The line format
+ * of the scan file is as follows:<br>
+ * "A &lt;frequency&gt; &lt;modulation&gt;".
* @return a list of {@link ScanChannel} objects parsed
*/
public static List<ScanChannel> parseScanFile(InputStream is) {
@@ -90,8 +94,11 @@ public class ChannelScanFileParser {
if (tokens.length != 3 && tokens.length != 4) {
continue;
}
- scanChannelList.add(ScanChannel.forTuner(Integer.parseInt(tokens[1]), tokens[2],
- tokens.length == 4 ? Integer.parseInt(tokens[3]) : null));
+ scanChannelList.add(
+ ScanChannel.forTuner(
+ Integer.parseInt(tokens[1]),
+ tokens[2],
+ tokens.length == 4 ? Integer.parseInt(tokens[3]) : null));
}
} catch (IOException e) {
Log.e(TAG, "error on parseScanFile()", e);
diff --git a/src/com/android/tv/tuner/DvbDeviceAccessor.java b/tuner/src/com/android/tv/tuner/DvbDeviceAccessor.java
index 4f5d8ee4..217433d2 100644
--- a/src/com/android/tv/tuner/DvbDeviceAccessor.java
+++ b/tuner/src/com/android/tv/tuner/DvbDeviceAccessor.java
@@ -22,10 +22,7 @@ import android.os.ParcelFileDescriptor;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.util.Log;
-
import com.android.tv.common.recording.RecordingCapability;
-import com.android.tv.tuner.tvinput.TunerTvInputService;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationTargetException;
@@ -35,15 +32,14 @@ import java.util.Collections;
import java.util.List;
import java.util.Locale;
-/**
- * Provides with the file descriptors to access DVB device.
- */
+/** Provides with the file descriptors to access DVB device. */
public class DvbDeviceAccessor {
private static final String TAG = "DvbDeviceAccessor";
@IntDef({DVB_DEVICE_DEMUX, DVB_DEVICE_DVR, DVB_DEVICE_FRONTEND})
@Retention(RetentionPolicy.SOURCE)
public @interface DvbDevice {}
+
public static final int DVB_DEVICE_DEMUX = 0; // TvInputManager.DVB_DEVICE_DEMUX;
public static final int DVB_DEVICE_DVR = 1; // TvInputManager.DVB_DEVICE_DVR;
public static final int DVB_DEVICE_FRONTEND = 2; // TvInputManager.DVB_DEVICE_FRONTEND;
@@ -59,8 +55,9 @@ public class DvbDeviceAccessor {
Class dvbDeviceInfoClass = Class.forName("android.media.tv.DvbDeviceInfo");
sGetDvbDeviceListMethod = tvInputManagerClass.getDeclaredMethod("getDvbDeviceList");
sGetDvbDeviceListMethod.setAccessible(true);
- sOpenDvbDeviceMethod = tvInputManagerClass.getDeclaredMethod(
- "openDvbDevice", dvbDeviceInfoClass, Integer.TYPE);
+ sOpenDvbDeviceMethod =
+ tvInputManagerClass.getDeclaredMethod(
+ "openDvbDevice", dvbDeviceInfoClass, Integer.TYPE);
sOpenDvbDeviceMethod.setAccessible(true);
} catch (ClassNotFoundException e) {
Log.e(TAG, "Couldn't find class", e);
@@ -90,9 +87,7 @@ public class DvbDeviceAccessor {
return null;
}
- /**
- * Returns the number of currently connected DVB devices.
- */
+ /** Returns the number of currently connected DVB devices. */
public int getNumOfDvbDevices() {
List<DvbDeviceInfoWrapper> dvbDeviceList = getDvbDeviceList();
return dvbDeviceList == null ? 0 : dvbDeviceList.size();
@@ -110,11 +105,12 @@ public class DvbDeviceAccessor {
return false;
}
- public ParcelFileDescriptor openDvbDevice(DvbDeviceInfoWrapper deviceInfo,
- @DvbDevice int device) {
+ public ParcelFileDescriptor openDvbDevice(
+ DvbDeviceInfoWrapper deviceInfo, @DvbDevice int device) {
try {
- return (ParcelFileDescriptor) sOpenDvbDeviceMethod.invoke(
- mTvInputManager, deviceInfo.getDvbDeviceInfo(), device);
+ return (ParcelFileDescriptor)
+ sOpenDvbDeviceMethod.invoke(
+ mTvInputManager, deviceInfo.getDvbDeviceInfo(), device);
} catch (IllegalAccessException e) {
Log.e(TAG, "Couldn't access", e);
} catch (InvocationTargetException e) {
@@ -125,6 +121,7 @@ public class DvbDeviceAccessor {
/**
* Returns the current recording capability for USB tuner.
+ *
* @param inputId the input id to use.
*/
public RecordingCapability getRecordingCapability(String inputId) {
@@ -215,7 +212,9 @@ public class DvbDeviceAccessor {
@Override
public String toString() {
- return String.format(Locale.US, "DvbDeviceInfo {adapterId: %d, deviceId: %d}",
+ return String.format(
+ Locale.US,
+ "DvbDeviceInfo {adapterId: %d, deviceId: %d}",
getAdapterId(),
getDeviceId());
}
diff --git a/src/com/android/tv/tuner/DvbTunerHal.java b/tuner/src/com/android/tv/tuner/DvbTunerHal.java
index ea977230..4375fc32 100644
--- a/src/com/android/tv/tuner/DvbTunerHal.java
+++ b/tuner/src/com/android/tv/tuner/DvbTunerHal.java
@@ -20,14 +20,11 @@ import android.content.Context;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import com.android.tv.tuner.DvbDeviceAccessor.DvbDeviceInfoWrapper;
-
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
-/**
- * A class to handle a hardware Linux DVB API supported tuner device.
- */
+/** A class to handle a hardware Linux DVB API supported tuner device. */
public class DvbTunerHal extends TunerHal {
private static final Object sLock = new Object();
@@ -37,7 +34,7 @@ public class DvbTunerHal extends TunerHal {
private final DvbDeviceAccessor mDvbDeviceAccessor;
private DvbDeviceInfoWrapper mDvbDeviceInfo;
- protected DvbTunerHal(Context context) {
+ public DvbTunerHal(Context context) {
super(context);
mDvbDeviceAccessor = new DvbDeviceAccessor(context);
}
@@ -133,8 +130,9 @@ public class DvbTunerHal extends TunerHal {
@Override
protected int openDvbFrontEndFd() {
if (mDvbDeviceInfo != null) {
- ParcelFileDescriptor descriptor = mDvbDeviceAccessor.openDvbDevice(
- mDvbDeviceInfo, DvbDeviceAccessor.DVB_DEVICE_FRONTEND);
+ ParcelFileDescriptor descriptor =
+ mDvbDeviceAccessor.openDvbDevice(
+ mDvbDeviceInfo, DvbDeviceAccessor.DVB_DEVICE_FRONTEND);
if (descriptor != null) {
return descriptor.detachFd();
}
@@ -145,8 +143,9 @@ public class DvbTunerHal extends TunerHal {
@Override
protected int openDvbDemuxFd() {
if (mDvbDeviceInfo != null) {
- ParcelFileDescriptor descriptor = mDvbDeviceAccessor.openDvbDevice(
- mDvbDeviceInfo, DvbDeviceAccessor.DVB_DEVICE_DEMUX);
+ ParcelFileDescriptor descriptor =
+ mDvbDeviceAccessor.openDvbDevice(
+ mDvbDeviceInfo, DvbDeviceAccessor.DVB_DEVICE_DEMUX);
if (descriptor != null) {
return descriptor.detachFd();
}
@@ -157,8 +156,9 @@ public class DvbTunerHal extends TunerHal {
@Override
protected int openDvbDvrFd() {
if (mDvbDeviceInfo != null) {
- ParcelFileDescriptor descriptor = mDvbDeviceAccessor.openDvbDevice(
- mDvbDeviceInfo, DvbDeviceAccessor.DVB_DEVICE_DVR);
+ ParcelFileDescriptor descriptor =
+ mDvbDeviceAccessor.openDvbDevice(
+ mDvbDeviceInfo, DvbDeviceAccessor.DVB_DEVICE_DVR);
if (descriptor != null) {
return descriptor.detachFd();
}
@@ -166,9 +166,7 @@ public class DvbTunerHal extends TunerHal {
return -1;
}
- /**
- * Gets the number of USB tuner devices currently present.
- */
+ /** Gets the number of USB tuner devices currently present. */
public static int getNumberOfDevices(Context context) {
try {
return (new DvbDeviceAccessor(context)).getNumOfDvbDevices();
diff --git a/tuner/src/com/android/tv/tuner/TunerFeatures.java b/tuner/src/com/android/tv/tuner/TunerFeatures.java
new file mode 100644
index 00000000..e682e636
--- /dev/null
+++ b/tuner/src/com/android/tv/tuner/TunerFeatures.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2017 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.tuner;
+
+import static com.android.tv.common.feature.FeatureUtils.OFF;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.Log;
+import com.android.tv.common.BaseApplication;
+import com.android.tv.common.config.api.RemoteConfig;
+import com.android.tv.common.feature.CommonFeatures;
+import com.android.tv.common.feature.Feature;
+import com.android.tv.common.feature.Model;
+import com.android.tv.common.feature.PropertyFeature;
+import com.android.tv.common.util.CommonUtils;
+import com.android.tv.common.util.LocationUtils;
+import java.util.Locale;
+
+/**
+ * List of {@link Feature} for Tuner.
+ *
+ * <p>Remove the {@code Feature} once it is launched.
+ */
+public class TunerFeatures extends CommonFeatures {
+ private static final String TAG = "TunerFeatures";
+ private static final boolean DEBUG = false;
+
+ /** Use network tuner if it is available and there is no other tuner types. */
+ public static final Feature NETWORK_TUNER =
+ new Feature() {
+ @Override
+ public boolean isEnabled(Context context) {
+ if (!TUNER.isEnabled(context)) {
+ return false;
+ }
+ if (CommonUtils.isDeveloper()) {
+ // Network tuner will be enabled for developers.
+ return true;
+ }
+ return Locale.US
+ .getCountry()
+ .equalsIgnoreCase(LocationUtils.getCurrentCountry(context));
+ }
+ };
+
+ /**
+ * USE_SW_CODEC_FOR_SD
+ *
+ * <p>Prefer software based codec for SD channels.
+ */
+ public static final Feature USE_SW_CODEC_FOR_SD =
+ PropertyFeature.create(
+ "use_sw_codec_for_sd",
+ false
+ );
+
+ /** Use AC3 software decode. */
+ public static final Feature AC3_SOFTWARE_DECODE =
+ new Feature() {
+ private final String[] SUPPORTED_REGIONS = {};
+
+ private Boolean mEnabled;
+
+ @Override
+ public boolean isEnabled(Context context) {
+ if (mEnabled == null) {
+ if (mEnabled == null) {
+ // We will not cache the result of fallback solution.
+ String country = LocationUtils.getCurrentCountry(context);
+ for (int i = 0; i < SUPPORTED_REGIONS.length; ++i) {
+ if (SUPPORTED_REGIONS[i].equalsIgnoreCase(country)) {
+ return true;
+ }
+ }
+ if (DEBUG) Log.d(TAG, "AC3 flag false after country check");
+ return false;
+ }
+ }
+ if (DEBUG) Log.d(TAG, "AC3 flag " + mEnabled);
+ return mEnabled;
+ }
+ };
+
+ /** Enable Dvb parsers and listeners. */
+ public static final Feature ENABLE_FILE_DVB = OFF;
+
+ private TunerFeatures() {}
+}
diff --git a/src/com/android/tv/tuner/TunerHal.java b/tuner/src/com/android/tv/tuner/TunerHal.java
index 1176cdf0..5801406b 100644
--- a/src/com/android/tv/tuner/TunerHal.java
+++ b/tuner/src/com/android/tv/tuner/TunerHal.java
@@ -22,40 +22,48 @@ import android.support.annotation.StringDef;
import android.support.annotation.WorkerThread;
import android.util.Log;
import android.util.Pair;
+import com.android.tv.common.BuildConfig;
+import com.android.tv.common.customization.CustomizationManager;
-import com.android.tv.Features;
-import com.android.tv.customization.TvCustomizationManager;
+import com.android.tv.common.annotation.UsedByNative;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
-/**
- * A base class to handle a hardware tuner device.
- */
+/** A base class to handle a hardware tuner device. */
public abstract class TunerHal implements AutoCloseable {
protected static final String TAG = "TunerHal";
protected static final boolean DEBUG = false;
- @IntDef({ FILTER_TYPE_OTHER, FILTER_TYPE_AUDIO, FILTER_TYPE_VIDEO, FILTER_TYPE_PCR })
+ @IntDef({FILTER_TYPE_OTHER, FILTER_TYPE_AUDIO, FILTER_TYPE_VIDEO, FILTER_TYPE_PCR})
@Retention(RetentionPolicy.SOURCE)
public @interface FilterType {}
+
public static final int FILTER_TYPE_OTHER = 0;
public static final int FILTER_TYPE_AUDIO = 1;
public static final int FILTER_TYPE_VIDEO = 2;
public static final int FILTER_TYPE_PCR = 3;
- @StringDef({ MODULATION_8VSB, MODULATION_QAM256 })
+ @StringDef({MODULATION_8VSB, MODULATION_QAM256})
@Retention(RetentionPolicy.SOURCE)
public @interface ModulationType {}
+
public static final String MODULATION_8VSB = "8VSB";
public static final String MODULATION_QAM256 = "QAM256";
- @IntDef({ DELIVERY_SYSTEM_UNDEFINED, DELIVERY_SYSTEM_ATSC, DELIVERY_SYSTEM_DVBC,
- DELIVERY_SYSTEM_DVBS, DELIVERY_SYSTEM_DVBS2, DELIVERY_SYSTEM_DVBT,
- DELIVERY_SYSTEM_DVBT2 })
+ @IntDef({
+ DELIVERY_SYSTEM_UNDEFINED,
+ DELIVERY_SYSTEM_ATSC,
+ DELIVERY_SYSTEM_DVBC,
+ DELIVERY_SYSTEM_DVBS,
+ DELIVERY_SYSTEM_DVBS2,
+ DELIVERY_SYSTEM_DVBT,
+ DELIVERY_SYSTEM_DVBT2
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface DeliverySystemType {}
+
public static final int DELIVERY_SYSTEM_UNDEFINED = 0;
public static final int DELIVERY_SYSTEM_ATSC = 1;
public static final int DELIVERY_SYSTEM_DVBC = 2;
@@ -64,9 +72,10 @@ public abstract class TunerHal implements AutoCloseable {
public static final int DELIVERY_SYSTEM_DVBT = 5;
public static final int DELIVERY_SYSTEM_DVBT2 = 6;
- @IntDef({ TUNER_TYPE_BUILT_IN, TUNER_TYPE_USB, TUNER_TYPE_NETWORK })
+ @IntDef({TUNER_TYPE_BUILT_IN, TUNER_TYPE_USB, TUNER_TYPE_NETWORK})
@Retention(RetentionPolicy.SOURCE)
public @interface TunerType {}
+
public static final int TUNER_TYPE_BUILT_IN = 1;
public static final int TUNER_TYPE_USB = 2;
public static final int TUNER_TYPE_NETWORK = 3;
@@ -77,12 +86,13 @@ public abstract class TunerHal implements AutoCloseable {
protected static final int PID_DVB_EIT = 0x0012;
protected static final int DEFAULT_VSB_TUNE_TIMEOUT_MS = 2000;
protected static final int DEFAULT_QAM_TUNE_TIMEOUT_MS = 4000; // Some device takes time for
- // QAM256 tuning.
+ // QAM256 tuning.
@IntDef({
- BUILT_IN_TUNER_TYPE_LINUX_DVB
+ BUILT_IN_TUNER_TYPE_LINUX_DVB
})
@Retention(RetentionPolicy.SOURCE)
private @interface BuiltInTunerType {}
+
private static final int BUILT_IN_TUNER_TYPE_LINUX_DVB = 1;
private static Integer sBuiltInTunerType;
@@ -93,16 +103,19 @@ public abstract class TunerHal implements AutoCloseable {
private String mModulation;
static {
- System.loadLibrary("tunertvinput_jni");
+ if (!BuildConfig.NO_JNI_TEST) {
+ System.loadLibrary("tunertvinput_jni");
+ }
}
/**
* Creates a TunerHal instance.
+ *
* @param context context for creating the TunerHal instance
* @return the TunerHal instance
*/
@WorkerThread
- public synchronized static TunerHal createInstance(Context context) {
+ public static synchronized TunerHal createInstance(Context context) {
TunerHal tunerHal = null;
if (DvbTunerHal.getNumberOfDevices(context) > 0) {
if (DEBUG) Log.d(TAG, "Use DvbTunerHal");
@@ -111,9 +124,7 @@ public abstract class TunerHal implements AutoCloseable {
return tunerHal != null && tunerHal.openFirstAvailable() ? tunerHal : null;
}
- /**
- * Gets the number of tuner devices currently present.
- */
+ /** Gets the number of tuner devices currently present. */
@WorkerThread
public static Pair<Integer, Integer> getTunerTypeAndCount(Context context) {
if (useBuiltInTuner(context)) {
@@ -129,9 +140,7 @@ public abstract class TunerHal implements AutoCloseable {
return new Pair<>(null, 0);
}
- /**
- * Check a delivery system is for DVB or not.
- */
+ /** Check a delivery system is for DVB or not. */
public static boolean isDvbDeliverySystem(@DeliverySystemType int deliverySystemType) {
return deliverySystemType == DELIVERY_SYSTEM_DVBC
|| deliverySystemType == DELIVERY_SYSTEM_DVBS
@@ -144,14 +153,14 @@ public abstract class TunerHal implements AutoCloseable {
* Returns if tuner input service would use built-in tuners instead of USB tuners or network
* tuners.
*/
- static boolean useBuiltInTuner(Context context) {
+ public static boolean useBuiltInTuner(Context context) {
return getBuiltInTunerType(context) != 0;
}
private static @BuiltInTunerType int getBuiltInTunerType(Context context) {
if (sBuiltInTunerType == null) {
sBuiltInTunerType = 0;
- if (TvCustomizationManager.hasLinuxDvbBuiltInTuner(context)
+ if (CustomizationManager.hasLinuxDvbBuiltInTuner(context)
&& DvbTunerHal.getNumberOfDevices(context) > 0) {
sBuiltInTunerType = BUILT_IN_TUNER_TYPE_LINUX_DVB;
}
@@ -176,8 +185,8 @@ public abstract class TunerHal implements AutoCloseable {
}
/**
- * Returns {@code true} if this tuner HAL can be reused to save tuning time between channels
- * of the same frequency.
+ * Returns {@code true} if this tuner HAL can be reused to save tuning time between channels of
+ * the same frequency.
*/
public boolean isReusable() {
return true;
@@ -208,12 +217,12 @@ public abstract class TunerHal implements AutoCloseable {
*
* @param frequency a frequency of the channel to tune to
* @param modulation a modulation method of the channel to tune to
- * @param channelNumber channel number when channel number is already known. Some tuner HAL
- * may use channelNumber instead of frequency for tune.
+ * @param channelNumber channel number when channel number is already known. Some tuner HAL may
+ * use channelNumber instead of frequency for tune.
* @return {@code true} if the operation was successful, {@code false} otherwise
*/
- public synchronized boolean tune(int frequency, @ModulationType String modulation,
- String channelNumber) {
+ public synchronized boolean tune(
+ int frequency, @ModulationType String modulation, String channelNumber) {
if (!isDeviceOpen()) {
Log.e(TAG, "There's no available device");
return false;
@@ -235,8 +244,10 @@ public abstract class TunerHal implements AutoCloseable {
mIsStreaming = true;
return true;
}
- int timeout_ms = modulation.equals(MODULATION_8VSB) ? DEFAULT_VSB_TUNE_TIMEOUT_MS
- : DEFAULT_QAM_TUNE_TIMEOUT_MS;
+ int timeout_ms =
+ modulation.equals(MODULATION_8VSB)
+ ? DEFAULT_VSB_TUNE_TIMEOUT_MS
+ : DEFAULT_QAM_TUNE_TIMEOUT_MS;
if (nativeTune(getDeviceId(), frequency, modulation, timeout_ms)) {
addPidFilter(PID_PAT, FILTER_TYPE_OTHER);
addPidFilter(PID_ATSC_SI_BASE, FILTER_TYPE_OTHER);
@@ -252,8 +263,8 @@ public abstract class TunerHal implements AutoCloseable {
return false;
}
- protected native boolean nativeTune(long deviceId, int frequency,
- @ModulationType String modulation, int timeout_ms);
+ protected native boolean nativeTune(
+ long deviceId, int frequency, @ModulationType String modulation, int timeout_ms);
/**
* Sets a pid filter. This should be set after setting a channel.
@@ -275,8 +286,11 @@ public abstract class TunerHal implements AutoCloseable {
}
protected native void nativeAddPidFilter(long deviceId, int pid, @FilterType int filterType);
+
protected native void nativeCloseAllPidFilters(long deviceId);
+
protected native void nativeSetHasPendingTune(long deviceId, boolean hasPendingTune);
+
protected native int nativeGetDeliverySystemType(long deviceId);
/**
@@ -306,15 +320,15 @@ public abstract class TunerHal implements AutoCloseable {
protected native void nativeStopTune(long deviceId);
/**
- * This method must be called after {@link TunerHal#tune} and before
- * {@link TunerHal#stopTune}. Writes at most maxSize TS frames in a buffer
- * provided by the user. The frames employ MPEG encoding.
+ * This method must be called after {@link TunerHal#tune} and before {@link TunerHal#stopTune}.
+ * Writes at most maxSize TS frames in a buffer provided by the user. The frames employ MPEG
+ * encoding.
*
* @param javaBuffer a buffer to write the video data in
* @param javaBufferSize the max amount of bytes to write in this buffer. Usually this number
- * should be equal to the length of the buffer.
+ * should be equal to the length of the buffer.
* @return the amount of bytes written in the buffer. Note that this value could be 0 if no new
- * frames have been obtained since the last call.
+ * frames have been obtained since the last call.
*/
public synchronized int readTsStream(byte[] javaBuffer, int javaBufferSize) {
if (isDeviceOpen()) {
@@ -330,6 +344,7 @@ public abstract class TunerHal implements AutoCloseable {
* Opens Linux DVB frontend device. This method is called from native JNI and used only for
* DvbTunerHal.
*/
+ @UsedByNative("DvbManager.cpp")
protected int openDvbFrontEndFd() {
return -1;
}
@@ -338,6 +353,7 @@ public abstract class TunerHal implements AutoCloseable {
* Opens Linux DVB demux device. This method is called from native JNI and used only for
* DvbTunerHal.
*/
+ @UsedByNative("DvbManager.cpp")
protected int openDvbDemuxFd() {
return -1;
}
@@ -346,6 +362,7 @@ public abstract class TunerHal implements AutoCloseable {
* Opens Linux DVB dvr device. This method is called from native JNI and used only for
* DvbTunerHal.
*/
+ @UsedByNative("DvbManager.cpp")
protected int openDvbDvrFd() {
return -1;
}
diff --git a/tuner/src/com/android/tv/tuner/TunerPreferences.java b/tuner/src/com/android/tv/tuner/TunerPreferences.java
new file mode 100644
index 00000000..7b45b997
--- /dev/null
+++ b/tuner/src/com/android/tv/tuner/TunerPreferences.java
@@ -0,0 +1,100 @@
+/*
+ * 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.tuner;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import com.android.tv.common.CommonConstants;
+import com.android.tv.common.CommonPreferences;
+import com.android.tv.common.SoftPreconditions;
+
+/** A helper class for the tuner preferences. */
+public class TunerPreferences extends CommonPreferences {
+ private static final String TAG = "TunerPreferences";
+
+ 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";
+ private static final String PREFS_KEY_TRICKPLAY_EXPIRED_MS = "trickplay_expired_ms";
+
+ private static final String SHARED_PREFS_NAME =
+ CommonConstants.BASE_PACKAGE + ".tuner.preferences";
+
+ public static final int CHANNEL_DATA_VERSION_NOT_SET = -1;
+
+ protected static SharedPreferences getSharedPreferences(Context context) {
+ return context.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE);
+ }
+
+ public static synchronized int getChannelDataVersion(Context context) {
+ SoftPreconditions.checkState(sInitialized);
+ return getSharedPreferences(context)
+ .getInt(
+ TunerPreferences.PREFS_KEY_CHANNEL_DATA_VERSION,
+ CHANNEL_DATA_VERSION_NOT_SET);
+ }
+
+ public static synchronized void setChannelDataVersion(Context context, int version) {
+ SoftPreconditions.checkState(sInitialized);
+ getSharedPreferences(context)
+ .edit()
+ .putInt(TunerPreferences.PREFS_KEY_CHANNEL_DATA_VERSION, version)
+ .apply();
+ }
+
+ public static synchronized int getScannedChannelCount(Context context) {
+ SoftPreconditions.checkState(sInitialized);
+ return getSharedPreferences(context)
+ .getInt(TunerPreferences.PREFS_KEY_SCANNED_CHANNEL_COUNT, 0);
+ }
+
+ public static synchronized void setScannedChannelCount(Context context, int channelCount) {
+ SoftPreconditions.checkState(sInitialized);
+ getSharedPreferences(context)
+ .edit()
+ .putInt(TunerPreferences.PREFS_KEY_SCANNED_CHANNEL_COUNT, channelCount)
+ .apply();
+ }
+
+ public static synchronized boolean isScanDone(Context context) {
+ SoftPreconditions.checkState(sInitialized);
+ return getSharedPreferences(context)
+ .getBoolean(TunerPreferences.PREFS_KEY_SCAN_DONE, false);
+ }
+
+ public static synchronized void setScanDone(Context context) {
+ SoftPreconditions.checkState(sInitialized);
+ getSharedPreferences(context)
+ .edit()
+ .putBoolean(TunerPreferences.PREFS_KEY_SCAN_DONE, true)
+ .apply();
+ }
+
+ public static synchronized long getTrickplayExpiredMs(Context context) {
+ SoftPreconditions.checkState(sInitialized);
+ return getSharedPreferences(context)
+ .getLong(TunerPreferences.PREFS_KEY_TRICKPLAY_EXPIRED_MS, 0);
+ }
+
+ public static synchronized void setTrickplayExpiredMs(Context context, long timeMs) {
+ SoftPreconditions.checkState(sInitialized);
+ getSharedPreferences(context)
+ .edit()
+ .putLong(TunerPreferences.PREFS_KEY_TRICKPLAY_EXPIRED_MS, timeMs)
+ .apply();
+ }
+}
diff --git a/src/com/android/tv/tuner/cc/CaptionLayout.java b/tuner/src/com/android/tv/tuner/cc/CaptionLayout.java
index a88538df..eb9ad463 100644
--- a/src/com/android/tv/tuner/cc/CaptionLayout.java
+++ b/tuner/src/com/android/tv/tuner/cc/CaptionLayout.java
@@ -18,13 +18,12 @@ package com.android.tv.tuner.cc;
import android.content.Context;
import android.util.AttributeSet;
-
import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
import com.android.tv.tuner.layout.ScaledLayout;
/**
- * Layout containing the safe title area that helps the closed captions look more prominent.
- * This is required by CEA-708B.
+ * Layout containing the safe title area that helps the closed captions look more prominent. This is
+ * required by CEA-708B.
*/
public class CaptionLayout extends ScaledLayout {
// The safe title area has 10% margins of the screen.
@@ -47,13 +46,15 @@ public class CaptionLayout extends ScaledLayout {
public CaptionLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mSafeTitleAreaLayout = new ScaledLayout(context);
- addView(mSafeTitleAreaLayout, new ScaledLayoutParams(
- SAFE_TITLE_AREA_SCALE_START_X, SAFE_TITLE_AREA_SCALE_END_X,
- SAFE_TITLE_AREA_SCALE_START_Y, SAFE_TITLE_AREA_SCALE_END_Y));
+ addView(
+ mSafeTitleAreaLayout,
+ new ScaledLayoutParams(
+ SAFE_TITLE_AREA_SCALE_START_X, SAFE_TITLE_AREA_SCALE_END_X,
+ SAFE_TITLE_AREA_SCALE_START_Y, SAFE_TITLE_AREA_SCALE_END_Y));
}
- public void addOrUpdateViewToSafeTitleArea(CaptionWindowLayout captionWindowLayout,
- ScaledLayoutParams scaledLayoutParams) {
+ public void addOrUpdateViewToSafeTitleArea(
+ CaptionWindowLayout captionWindowLayout, ScaledLayoutParams scaledLayoutParams) {
int index = mSafeTitleAreaLayout.indexOfChild(captionWindowLayout);
if (index < 0) {
mSafeTitleAreaLayout.addView(captionWindowLayout, scaledLayoutParams);
diff --git a/src/com/android/tv/tuner/cc/CaptionTrackRenderer.java b/tuner/src/com/android/tv/tuner/cc/CaptionTrackRenderer.java
index 24a0f354..84033240 100644
--- a/src/com/android/tv/tuner/cc/CaptionTrackRenderer.java
+++ b/tuner/src/com/android/tv/tuner/cc/CaptionTrackRenderer.java
@@ -20,7 +20,6 @@ import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
-
import com.android.tv.tuner.data.Cea708Data.CaptionEvent;
import com.android.tv.tuner.data.Cea708Data.CaptionPenAttr;
import com.android.tv.tuner.data.Cea708Data.CaptionPenColor;
@@ -28,13 +27,9 @@ import com.android.tv.tuner.data.Cea708Data.CaptionPenLocation;
import com.android.tv.tuner.data.Cea708Data.CaptionWindow;
import com.android.tv.tuner.data.Cea708Data.CaptionWindowAttr;
import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
-
import java.util.ArrayList;
-import java.util.concurrent.TimeUnit;
-/**
- * Decodes and renders CEA-708.
- */
+/** Decodes and renders CEA-708. */
public class CaptionTrackRenderer implements Handler.Callback {
// TODO: Remaining works
// CaptionTrackRenderer does not support the full spec of CEA-708. The remaining works are
@@ -50,7 +45,7 @@ public class CaptionTrackRenderer implements Handler.Callback {
private static final String TAG = "CaptionTrackRenderer";
private static final boolean DEBUG = false;
- private static final long DELAY_IN_MILLIS = TimeUnit.MILLISECONDS.toMillis(100);
+ private static final long DELAY_IN_MILLIS = 100 /* milliseconds */;
// According to CEA-708B, there can exist up to 8 caption windows.
private static final int CAPTION_WINDOWS_MAX = 8;
@@ -291,8 +286,8 @@ public class CaptionTrackRenderer implements Handler.Callback {
return;
}
mIsDelayed = true;
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_DELAY_CANCEL),
- tenthsOfSeconds * DELAY_IN_MILLIS);
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(MSG_DELAY_CANCEL), tenthsOfSeconds * DELAY_IN_MILLIS);
}
private void delayCancel() {
@@ -318,8 +313,8 @@ public class CaptionTrackRenderer implements Handler.Callback {
if (mCurrentWindowLayout != null) {
mCurrentWindowLayout.sendBuffer(buffer);
mHandler.removeMessages(MSG_CAPTION_CLEAR);
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CAPTION_CLEAR),
- CAPTION_CLEAR_INTERVAL_MS);
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(MSG_CAPTION_CLEAR), CAPTION_CLEAR_INTERVAL_MS);
}
}
diff --git a/src/com/android/tv/tuner/cc/CaptionWindowLayout.java b/tuner/src/com/android/tv/tuner/cc/CaptionWindowLayout.java
index 6f42b506..13c6ff47 100644
--- a/src/com/android/tv/tuner/cc/CaptionWindowLayout.java
+++ b/tuner/src/com/android/tv/tuner/cc/CaptionWindowLayout.java
@@ -39,15 +39,13 @@ import android.view.accessibility.CaptioningManager;
import android.view.accessibility.CaptioningManager.CaptionStyle;
import android.view.accessibility.CaptioningManager.CaptioningChangeListener;
import android.widget.RelativeLayout;
-
-import com.google.android.exoplayer.text.CaptionStyleCompat;
-import com.google.android.exoplayer.text.SubtitleView;
import com.android.tv.tuner.data.Cea708Data.CaptionPenAttr;
import com.android.tv.tuner.data.Cea708Data.CaptionPenColor;
import com.android.tv.tuner.data.Cea708Data.CaptionWindow;
import com.android.tv.tuner.data.Cea708Data.CaptionWindowAttr;
+import com.android.tv.tuner.exoplayer.text.SubtitleView;
import com.android.tv.tuner.layout.ScaledLayout;
-
+import com.google.android.exoplayer.text.CaptionStyleCompat;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
@@ -55,8 +53,8 @@ import java.util.Arrays;
import java.util.List;
/**
- * Layout which renders a caption window of CEA-708B. It contains a {@link SubtitleView} that
- * takes care of displaying the actual cc text.
+ * Layout which renders a caption window of CEA-708B. It contains a {@link SubtitleView} that takes
+ * care of displaying the actual cc text.
*/
public class CaptionWindowLayout extends RelativeLayout implements View.OnLayoutChangeListener {
private static final String TAG = "CaptionWindowLayout";
@@ -136,8 +134,9 @@ public class CaptionWindowLayout extends RelativeLayout implements View.OnLayout
// Add a subtitle view to the layout.
mSubtitleView = new SubtitleView(context);
- LayoutParams params = new RelativeLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ LayoutParams params =
+ new RelativeLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
addView(mSubtitleView, params);
// Set the system wide cc preferences to the subtitle view.
@@ -241,12 +240,13 @@ public class CaptionWindowLayout extends RelativeLayout implements View.OnLayout
/**
* This method places the window on a given CaptionLayout along with the anchor of the window.
- * <p>
- * According to CEA-708B, the anchor id indicates the gravity of the window as the follows.
+ *
+ * <p>According to CEA-708B, the anchor id indicates the gravity of the window as the follows.
* For example, A value 7 of a anchor id says that a window is align with its parent bottom and
* is located at the center horizontally of its parent.
- * </p>
+ *
* <h4>Anchor id and the gravity of a window</h4>
+ *
* <table>
* <tr>
* <th>GRAVITY</th>
@@ -273,28 +273,29 @@ public class CaptionWindowLayout extends RelativeLayout implements View.OnLayout
* <td>8</td>
* </tr>
* </table>
- * <p>
- * In order to handle the gravity of a window, there are two steps. First, set the size of the
- * window. Since the window will be positioned at {@link ScaledLayout}, the size factors are
+ *
+ * <p>In order to handle the gravity of a window, there are two steps. First, set the size of
+ * the window. Since the window will be positioned at {@link ScaledLayout}, the size factors are
* determined in a ratio. Second, set the gravity of the window. {@link CaptionWindowLayout} is
* inherited from {@link RelativeLayout}. Hence, we could set the gravity of its child view,
* {@link SubtitleView}.
- * </p>
- * <p>
- * The gravity of the window is also related to its size. When it should be pushed to a one of
- * the end of the window, like LEFT, RIGHT, TOP or BOTTOM, the anchor point should be a boundary
- * of the window. When it should be pushed in the horizontal/vertical center of its container,
- * the horizontal/vertical center point of the window should be the same as the anchor point.
- * </p>
+ *
+ * <p>The gravity of the window is also related to its size. When it should be pushed to a one
+ * of the end of the window, like LEFT, RIGHT, TOP or BOTTOM, the anchor point should be a
+ * boundary of the window. When it should be pushed in the horizontal/vertical center of its
+ * container, the horizontal/vertical center point of the window should be the same as the
+ * anchor point.
*
* @param captionLayout a given {@link CaptionLayout}, which contains a safe title area
* @param captionWindow a given {@link CaptionWindow}, which stores the construction info of the
- * window
+ * window
*/
public void initWindow(CaptionLayout captionLayout, CaptionWindow captionWindow) {
if (DEBUG) {
- Log.d(TAG, "initWindow with "
- + (captionLayout != null ? captionLayout.getCaptionTrack() : null));
+ Log.d(
+ TAG,
+ "initWindow with "
+ + (captionLayout != null ? captionLayout.getCaptionTrack() : null));
}
if (mCaptionLayout != captionLayout) {
if (mCaptionLayout != null) {
@@ -306,23 +307,35 @@ public class CaptionWindowLayout extends RelativeLayout implements View.OnLayout
}
// Both anchor vertical and horizontal indicates the position cell number of the window.
- float scaleRow = (float) captionWindow.anchorVertical / (captionWindow.relativePositioning
- ? ANCHOR_RELATIVE_POSITIONING_MAX : ANCHOR_VERTICAL_MAX);
- float scaleCol = (float) captionWindow.anchorHorizontal /
- (captionWindow.relativePositioning ? ANCHOR_RELATIVE_POSITIONING_MAX
- : (isWideAspectRatio()
- ? ANCHOR_HORIZONTAL_16_9_MAX : ANCHOR_HORIZONTAL_4_3_MAX));
+ float scaleRow =
+ (float) captionWindow.anchorVertical
+ / (captionWindow.relativePositioning
+ ? ANCHOR_RELATIVE_POSITIONING_MAX
+ : ANCHOR_VERTICAL_MAX);
+ float scaleCol =
+ (float) captionWindow.anchorHorizontal
+ / (captionWindow.relativePositioning
+ ? ANCHOR_RELATIVE_POSITIONING_MAX
+ : (isWideAspectRatio()
+ ? ANCHOR_HORIZONTAL_16_9_MAX
+ : ANCHOR_HORIZONTAL_4_3_MAX));
// The range of scaleRow/Col need to be verified to be in [0, 1].
// Otherwise a {@link RuntimeException} will be raised in {@link ScaledLayout}.
if (scaleRow < 0 || scaleRow > 1) {
- Log.i(TAG, "The vertical position of the anchor point should be at the range of 0 and 1"
- + " but " + scaleRow);
+ Log.i(
+ TAG,
+ "The vertical position of the anchor point should be at the range of 0 and 1"
+ + " but "
+ + scaleRow);
scaleRow = Math.max(0, Math.min(scaleRow, 1));
}
if (scaleCol < 0 || scaleCol > 1) {
- Log.i(TAG, "The horizontal position of the anchor point should be at the range of 0 and"
- + " 1 but " + scaleCol);
+ Log.i(
+ TAG,
+ "The horizontal position of the anchor point should be at the range of 0 and"
+ + " 1 but "
+ + scaleCol);
scaleCol = Math.max(0, Math.min(scaleCol, 1));
}
int gravity = Gravity.CENTER;
@@ -356,8 +369,10 @@ public class CaptionWindowLayout extends RelativeLayout implements View.OnLayout
paint.setTypeface(mCaptionStyleCompat.typeface);
paint.setTextSize(mTextSize);
float maxWindowWidth = paint.measureText(widestTextBuilder.toString());
- float halfMaxWidthScale = mCaptionLayout.getWidth() > 0
- ? maxWindowWidth / 2.0f / (mCaptionLayout.getWidth() * 0.8f) : 0.0f;
+ float halfMaxWidthScale =
+ mCaptionLayout.getWidth() > 0
+ ? maxWindowWidth / 2.0f / (mCaptionLayout.getWidth() * 0.8f)
+ : 0.0f;
if (halfMaxWidthScale > 0f && halfMaxWidthScale < scaleCol) {
// Calculate the expected max window size based on the column count of the
// caption window multiplied by average alphabets char width, then align the
@@ -403,8 +418,10 @@ public class CaptionWindowLayout extends RelativeLayout implements View.OnLayout
scaleEndRow = scaleRow;
break;
}
- mCaptionLayout.addOrUpdateViewToSafeTitleArea(this, new ScaledLayout
- .ScaledLayoutParams(scaleStartRow, scaleEndRow, scaleStartCol, scaleEndCol));
+ mCaptionLayout.addOrUpdateViewToSafeTitleArea(
+ this,
+ new ScaledLayout.ScaledLayoutParams(
+ scaleStartRow, scaleEndRow, scaleStartCol, scaleEndCol));
setCaptionWindowId(captionWindow.id);
setRowLimit(captionWindow.rowCount);
setGravity(gravity);
@@ -420,8 +437,16 @@ public class CaptionWindowLayout extends RelativeLayout implements View.OnLayout
}
@Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
- int oldTop, int oldRight, int oldBottom) {
+ public void onLayoutChange(
+ View v,
+ int left,
+ int top,
+ int right,
+ int bottom,
+ int oldLeft,
+ int oldTop,
+ int oldRight,
+ int oldBottom) {
int width = right - left;
int height = bottom - top;
if (width != mLastCaptionLayoutWidth || height != mLastCaptionLayoutHeight) {
@@ -432,13 +457,15 @@ public class CaptionWindowLayout extends RelativeLayout implements View.OnLayout
}
private boolean isKoreanLanguageTrack() {
- return mCaptionLayout != null && mCaptionLayout.getCaptionTrack() != null
+ return mCaptionLayout != null
+ && mCaptionLayout.getCaptionTrack() != null
&& mCaptionLayout.getCaptionTrack().language != null
&& "KOR".compareToIgnoreCase(mCaptionLayout.getCaptionTrack().language) == 0;
}
private boolean isWideAspectRatio() {
- return mCaptionLayout != null && mCaptionLayout.getCaptionTrack() != null
+ return mCaptionLayout != null
+ && mCaptionLayout.getCaptionTrack() != null
&& mCaptionLayout.getCaptionTrack().wideAspectRatio;
}
@@ -451,7 +478,7 @@ public class CaptionWindowLayout extends RelativeLayout implements View.OnLayout
Charset latin1 = Charset.forName("ISO-8859-1");
float widestCharWidth = 0f;
for (int i = 0; i < 256; ++i) {
- String ch = new String(new byte[]{(byte) i}, latin1);
+ String ch = new String(new byte[] {(byte) i}, latin1);
float charWidth = paint.measureText(ch);
if (widestCharWidth < charWidth) {
widestCharWidth = charWidth;
@@ -548,7 +575,10 @@ public class CaptionWindowLayout extends RelativeLayout implements View.OnLayout
int length = mBuilder.length();
mBuilder.append(text);
for (CharacterStyle characterStyle : mCharacterStyles) {
- mBuilder.setSpan(characterStyle, length, mBuilder.length(),
+ mBuilder.setSpan(
+ characterStyle,
+ length,
+ mBuilder.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
@@ -557,8 +587,8 @@ public class CaptionWindowLayout extends RelativeLayout implements View.OnLayout
// Truncate text not to exceed the row limit.
// Plus one here since the range of the rows is [0, mRowLimit].
int startRow = Math.max(0, lines.length - (mRowLimit + 1));
- String truncatedText = TextUtils.join("\n", Arrays.copyOfRange(
- lines, startRow, lines.length));
+ String truncatedText =
+ TextUtils.join("\n", Arrays.copyOfRange(lines, startRow, lines.length));
mBuilder.delete(0, mBuilder.length() - truncatedText.length());
mCurrentTextRow = lines.length - startRow - 1;
diff --git a/src/com/android/tv/tuner/cc/Cea708Parser.java b/tuner/src/com/android/tv/tuner/cc/Cea708Parser.java
index d0f6cf11..4e080276 100644
--- a/src/com/android/tv/tuner/cc/Cea708Parser.java
+++ b/tuner/src/com/android/tv/tuner/cc/Cea708Parser.java
@@ -20,7 +20,6 @@ import android.os.SystemClock;
import android.support.annotation.IntDef;
import android.util.Log;
import android.util.SparseIntArray;
-
import com.android.tv.tuner.data.Cea708Data;
import com.android.tv.tuner.data.Cea708Data.CaptionColor;
import com.android.tv.tuner.data.Cea708Data.CaptionEvent;
@@ -31,7 +30,6 @@ import com.android.tv.tuner.data.Cea708Data.CaptionWindow;
import com.android.tv.tuner.data.Cea708Data.CaptionWindowAttr;
import com.android.tv.tuner.data.Cea708Data.CcPacket;
import com.android.tv.tuner.util.ByteArrayBuffer;
-
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -43,9 +41,9 @@ import java.util.TreeSet;
/**
* A class for parsing CEA-708, which is the standard for closed captioning for ATSC DTV.
*
- * <p>ATSC DTV closed caption data are carried on picture user data of video streams.
- * This class starts to parse from picture user data payload, so extraction process of user_data
- * from video streams is up to outside of this code.
+ * <p>ATSC DTV closed caption data are carried on picture user data of video streams. This class
+ * starts to parse from picture user data payload, so extraction process of user_data from video
+ * streams is up to outside of this code.
*
* <p>There are 4 steps to decode user_data to provide closed caption services.
*
@@ -67,45 +65,47 @@ import java.util.TreeSet;
*
* <p>A DTVCC packet consists of multiple service blocks. Each service block represents a caption
* track and has a service number, which ranges from 1 to 63, that denotes caption track identity.
- * In here, we listen at most one chosen caption track by {@link #mListenServiceNumber}.
- * Otherwise, just skip the other service blocks.
+ * In here, we listen at most one chosen caption track by {@link #mListenServiceNumber}. Otherwise,
+ * just skip the other service blocks.
*
- * <h3>Step 4. Interpreting Service Block Data ({@link #parseServiceBlockData}, {@code parseXX},
- * and {@link #parseExt1} methods)</h3>
+ * <h3>Step 4. Interpreting Service Block Data ({@link #parseServiceBlockData}, {@code parseXX}, and
+ * {@link #parseExt1} methods)</h3>
*
* <p>Service block data is actual caption stream. it looks similar to telnet. It uses most parts of
- * ASCII table and consists of specially defined commands and some ASCII control codes which work
- * in a behavior slightly different from their original purpose. ASCII control codes and caption
+ * ASCII table and consists of specially defined commands and some ASCII control codes which work in
+ * a behavior slightly different from their original purpose. ASCII control codes and caption
* commands are explicit instructions that control the state of a closed caption service and the
* other ASCII and text codes are implicit instructions that send their characters to buffer.
*
* <p>There are 4 main code groups and 4 extended code groups. Both the range of code groups are the
* same as the range of a byte.
*
- * <p>4 main code groups: C0, C1, G0, G1
- * <br>4 extended code groups: C2, C3, G2, G3
+ * <p>4 main code groups: C0, C1, G0, G1 <br>
+ * 4 extended code groups: C2, C3, G2, G3
*
* <p>Each code group has its own handle method. For example, {@link #parseC0} handles C0 code group
* and so on. And {@link #parseServiceBlockData} method maps a stream on the main code groups while
* {@link #parseExt1} method maps on the extended code groups.
*
* <p>The main code groups:
+ *
* <ul>
- * <li>C0 - contains modified ASCII control codes. It is not intended by CEA-708 but Korea TTA
- * standard for ATSC CC uses P16 character heavily, which is unclear entity in CEA-708 doc,
- * even for the alphanumeric characters instead of ASCII characters.</li>
- * <li>C1 - contains the caption commands. There are 3 categories of a caption command.</li>
- * <ul>
- * <li>Window commands: The window commands control a caption window which is addressable area being
- * with in the Safe title area. (CWX, CLW, DSW, HDW, TGW, DLW, SWA, DFX)</li>
- * <li>Pen commands: Th pen commands control text style and location. (SPA, SPC, SPL)</li>
- * <li>Job commands: The job commands make a delay and recover from the delay. (DLY, DLC, RST)</li>
- * </ul>
- * <li>G0 - same as printable ASCII character set except music note character.</li>
- * <li>G1 - same as ISO 8859-1 Latin 1 character set.</li>
+ * <li>C0 - contains modified ASCII control codes. It is not intended by CEA-708 but Korea TTA
+ * standard for ATSC CC uses P16 character heavily, which is unclear entity in CEA-708 doc,
+ * even for the alphanumeric characters instead of ASCII characters.
+ * <li>C1 - contains the caption commands. There are 3 categories of a caption command.
+ * <ul>
+ * <li>Window commands: The window commands control a caption window which is addressable
+ * area being with in the Safe title area. (CWX, CLW, DSW, HDW, TGW, DLW, SWA, DFX)
+ * <li>Pen commands: Th pen commands control text style and location. (SPA, SPC, SPL)
+ * <li>Job commands: The job commands make a delay and recover from the delay. (DLY, DLC,
+ * RST)
+ * </ul>
+ * <li>G0 - same as printable ASCII character set except music note character.
+ * <li>G1 - same as ISO 8859-1 Latin 1 character set.
* </ul>
- * <p>Most of the extended code groups are being skipped.
*
+ * <p>Most of the extended code groups are being skipped.
*/
public class Cea708Parser {
private static final String TAG = "Cea708Parser";
@@ -113,8 +113,8 @@ public class Cea708Parser {
// According to CEA-708B, the maximum value of closed caption bandwidth is 9600bps.
private static final int MAX_ALLOCATED_SIZE = 9600 / 8;
- private static final String MUSIC_NOTE_CHAR = new String(
- "\u266B".getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
+ private static final String MUSIC_NOTE_CHAR =
+ new String("\u266B".getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
// The following values are denoting the type of closed caption data.
// See CEA-708B section 4.4.1.
@@ -143,44 +143,58 @@ public class Cea708Parser {
private boolean mFirstServiceNumberDiscovered;
// Assign a dummy listener in order to avoid null checks.
- private OnCea708ParserListener mListener = new OnCea708ParserListener() {
- @Override
- public void emitEvent(CaptionEvent event) {
- // do nothing
- }
+ private OnCea708ParserListener mListener =
+ new OnCea708ParserListener() {
+ @Override
+ public void emitEvent(CaptionEvent event) {
+ // do nothing
+ }
- @Override
- public void discoverServiceNumber(int serviceNumber) {
- // do nothing
- }
- };
+ @Override
+ public void discoverServiceNumber(int serviceNumber) {
+ // do nothing
+ }
+ };
/**
- * {@link Cea708Parser} emits caption event of three different types.
- * {@link OnCea708ParserListener#emitEvent} is invoked with the parameter
- * {@link CaptionEvent} to pass all the results to an observer of the decoding process.
- *
- * <p>{@link CaptionEvent#type} determines the type of the result and
- * {@link CaptionEvent#obj} contains the output value of a caption event.
- * The observer must do the casting to the corresponding type.
- *
- * <ul><li>{@code CAPTION_EMIT_TYPE_BUFFER}: Passes a caption text buffer to a observer.
- * {@code obj} must be of {@link String}.</li>
+ * {@link Cea708Parser} emits caption event of three different types. {@link
+ * OnCea708ParserListener#emitEvent} is invoked with the parameter {@link CaptionEvent} to pass
+ * all the results to an observer of the decoding process.
*
- * <li>{@code CAPTION_EMIT_TYPE_CONTROL}: Passes a caption character control code to a observer.
- * {@code obj} must be of {@link Character}.</li>
+ * <p>{@link CaptionEvent#type} determines the type of the result and {@link CaptionEvent#obj}
+ * contains the output value of a caption event. The observer must do the casting to the
+ * corresponding type.
*
- * <li>{@code CAPTION_EMIT_TYPE_CLEAR_COMMAND}: Passes a clear command to a observer.
- * {@code obj} must be {@code NULL}.</li></ul>
+ * <ul>
+ * <li>{@code CAPTION_EMIT_TYPE_BUFFER}: Passes a caption text buffer to a observer. {@code
+ * obj} must be of {@link String}.
+ * <li>{@code CAPTION_EMIT_TYPE_CONTROL}: Passes a caption character control code to a
+ * observer. {@code obj} must be of {@link Character}.
+ * <li>{@code CAPTION_EMIT_TYPE_CLEAR_COMMAND}: Passes a clear command to a observer. {@code
+ * obj} must be {@code NULL}.
+ * </ul>
*/
- @IntDef({CAPTION_EMIT_TYPE_BUFFER, CAPTION_EMIT_TYPE_CONTROL, CAPTION_EMIT_TYPE_COMMAND_CWX,
- CAPTION_EMIT_TYPE_COMMAND_CLW, CAPTION_EMIT_TYPE_COMMAND_DSW, CAPTION_EMIT_TYPE_COMMAND_HDW,
- CAPTION_EMIT_TYPE_COMMAND_TGW, CAPTION_EMIT_TYPE_COMMAND_DLW, CAPTION_EMIT_TYPE_COMMAND_DLY,
- CAPTION_EMIT_TYPE_COMMAND_DLC, CAPTION_EMIT_TYPE_COMMAND_RST, CAPTION_EMIT_TYPE_COMMAND_SPA,
- CAPTION_EMIT_TYPE_COMMAND_SPC, CAPTION_EMIT_TYPE_COMMAND_SPL, CAPTION_EMIT_TYPE_COMMAND_SWA,
- CAPTION_EMIT_TYPE_COMMAND_DFX})
+ @IntDef({
+ CAPTION_EMIT_TYPE_BUFFER,
+ CAPTION_EMIT_TYPE_CONTROL,
+ CAPTION_EMIT_TYPE_COMMAND_CWX,
+ CAPTION_EMIT_TYPE_COMMAND_CLW,
+ CAPTION_EMIT_TYPE_COMMAND_DSW,
+ CAPTION_EMIT_TYPE_COMMAND_HDW,
+ CAPTION_EMIT_TYPE_COMMAND_TGW,
+ CAPTION_EMIT_TYPE_COMMAND_DLW,
+ CAPTION_EMIT_TYPE_COMMAND_DLY,
+ CAPTION_EMIT_TYPE_COMMAND_DLC,
+ CAPTION_EMIT_TYPE_COMMAND_RST,
+ CAPTION_EMIT_TYPE_COMMAND_SPA,
+ CAPTION_EMIT_TYPE_COMMAND_SPC,
+ CAPTION_EMIT_TYPE_COMMAND_SPL,
+ CAPTION_EMIT_TYPE_COMMAND_SWA,
+ CAPTION_EMIT_TYPE_COMMAND_DFX
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface CaptionEmitType {}
+
public static final int CAPTION_EMIT_TYPE_BUFFER = 1;
public static final int CAPTION_EMIT_TYPE_CONTROL = 2;
public static final int CAPTION_EMIT_TYPE_COMMAND_CWX = 3;
@@ -200,6 +214,7 @@ public class Cea708Parser {
public interface OnCea708ParserListener {
void emitEvent(CaptionEvent event);
+
void discoverServiceNumber(int serviceNumber);
}
@@ -337,7 +352,8 @@ public class Cea708Parser {
// bytes sent with the same service number during a discovery period.
// The viewer in most TV sets chooses between CC1, CC2, CC3, CC4 to view different
// language captions. Therefore, only CC1, CC2, CC3, CC4 are allowed to be reported.
- if (blockSize > 0 && serviceNumber >= DISCOVERY_CC_SERVICE_NUMBER_START
+ if (blockSize > 0
+ && serviceNumber >= DISCOVERY_CC_SERVICE_NUMBER_START
&& serviceNumber <= DISCOVERY_CC_SERVICE_NUMBER_END) {
mDiscoveredNumBytes.put(
serviceNumber, blockSize + mDiscoveredNumBytes.get(serviceNumber, 0));
@@ -411,9 +427,7 @@ public class Cea708Parser {
if (data[pos] == 0) {
mBuffer.append((char) data[pos + 1]);
} else {
- String value = new String(
- Arrays.copyOfRange(data, pos, pos + 2),
- "EUC-KR");
+ String value = new String(Arrays.copyOfRange(data, pos, pos + 2), "EUC-KR");
mBuffer.append(value);
}
} catch (UnsupportedEncodingException e) {
@@ -466,203 +480,265 @@ public class Cea708Parser {
case Cea708Data.CODE_C1_CW4:
case Cea708Data.CODE_C1_CW5:
case Cea708Data.CODE_C1_CW6:
- case Cea708Data.CODE_C1_CW7: {
- // SetCurrentWindow0-7
- int windowId = mCommand - Cea708Data.CODE_C1_CW0;
- emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_CWX, windowId));
- if (DEBUG) {
- Log.d(TAG, String.format("CaptionCommand CWX windowId: %d", windowId));
+ case Cea708Data.CODE_C1_CW7:
+ {
+ // SetCurrentWindow0-7
+ int windowId = mCommand - Cea708Data.CODE_C1_CW0;
+ emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_CWX, windowId));
+ if (DEBUG) {
+ Log.d(TAG, String.format("CaptionCommand CWX windowId: %d", windowId));
+ }
+ break;
}
- break;
- }
- case Cea708Data.CODE_C1_CLW: {
- // ClearWindows
- int windowBitmap = data[pos] & 0xff;
- ++pos;
- emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_CLW, windowBitmap));
- if (DEBUG) {
- Log.d(TAG, String.format("CaptionCommand CLW windowBitmap: %d", windowBitmap));
+ case Cea708Data.CODE_C1_CLW:
+ {
+ // ClearWindows
+ int windowBitmap = data[pos] & 0xff;
+ ++pos;
+ emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_CLW, windowBitmap));
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ String.format("CaptionCommand CLW windowBitmap: %d", windowBitmap));
+ }
+ break;
}
- break;
- }
- case Cea708Data.CODE_C1_DSW: {
- // DisplayWindows
- int windowBitmap = data[pos] & 0xff;
- ++pos;
- emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_DSW, windowBitmap));
- if (DEBUG) {
- Log.d(TAG, String.format("CaptionCommand DSW windowBitmap: %d", windowBitmap));
+ case Cea708Data.CODE_C1_DSW:
+ {
+ // DisplayWindows
+ int windowBitmap = data[pos] & 0xff;
+ ++pos;
+ emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_DSW, windowBitmap));
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ String.format("CaptionCommand DSW windowBitmap: %d", windowBitmap));
+ }
+ break;
}
- break;
- }
- case Cea708Data.CODE_C1_HDW: {
- // HideWindows
- int windowBitmap = data[pos] & 0xff;
- ++pos;
- emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_HDW, windowBitmap));
- if (DEBUG) {
- Log.d(TAG, String.format("CaptionCommand HDW windowBitmap: %d", windowBitmap));
+ case Cea708Data.CODE_C1_HDW:
+ {
+ // HideWindows
+ int windowBitmap = data[pos] & 0xff;
+ ++pos;
+ emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_HDW, windowBitmap));
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ String.format("CaptionCommand HDW windowBitmap: %d", windowBitmap));
+ }
+ break;
}
- break;
- }
- case Cea708Data.CODE_C1_TGW: {
- // ToggleWindows
- int windowBitmap = data[pos] & 0xff;
- ++pos;
- emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_TGW, windowBitmap));
- if (DEBUG) {
- Log.d(TAG, String.format("CaptionCommand TGW windowBitmap: %d", windowBitmap));
+ case Cea708Data.CODE_C1_TGW:
+ {
+ // ToggleWindows
+ int windowBitmap = data[pos] & 0xff;
+ ++pos;
+ emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_TGW, windowBitmap));
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ String.format("CaptionCommand TGW windowBitmap: %d", windowBitmap));
+ }
+ break;
}
- break;
- }
- case Cea708Data.CODE_C1_DLW: {
- // DeleteWindows
- int windowBitmap = data[pos] & 0xff;
- ++pos;
- emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_DLW, windowBitmap));
- if (DEBUG) {
- Log.d(TAG, String.format("CaptionCommand DLW windowBitmap: %d", windowBitmap));
+ case Cea708Data.CODE_C1_DLW:
+ {
+ // DeleteWindows
+ int windowBitmap = data[pos] & 0xff;
+ ++pos;
+ emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_DLW, windowBitmap));
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ String.format("CaptionCommand DLW windowBitmap: %d", windowBitmap));
+ }
+ break;
}
- break;
- }
- case Cea708Data.CODE_C1_DLY: {
- // Delay
- int tenthsOfSeconds = data[pos] & 0xff;
- ++pos;
- emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_DLY, tenthsOfSeconds));
- if (DEBUG) {
- Log.d(TAG, String.format("CaptionCommand DLY %d tenths of seconds",
- tenthsOfSeconds));
+ case Cea708Data.CODE_C1_DLY:
+ {
+ // Delay
+ int tenthsOfSeconds = data[pos] & 0xff;
+ ++pos;
+ emitCaptionEvent(
+ new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_DLY, tenthsOfSeconds));
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ String.format(
+ "CaptionCommand DLY %d tenths of seconds",
+ tenthsOfSeconds));
+ }
+ break;
}
- break;
- }
- case Cea708Data.CODE_C1_DLC: {
- // DelayCancel
- emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_DLC, null));
- if (DEBUG) {
- Log.d(TAG, "CaptionCommand DLC");
+ case Cea708Data.CODE_C1_DLC:
+ {
+ // DelayCancel
+ emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_DLC, null));
+ if (DEBUG) {
+ Log.d(TAG, "CaptionCommand DLC");
+ }
+ break;
}
- break;
- }
- case Cea708Data.CODE_C1_RST: {
- // Reset
- emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_RST, null));
- if (DEBUG) {
- Log.d(TAG, "CaptionCommand RST");
+ case Cea708Data.CODE_C1_RST:
+ {
+ // Reset
+ emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_RST, null));
+ if (DEBUG) {
+ Log.d(TAG, "CaptionCommand RST");
+ }
+ break;
}
- break;
- }
- case Cea708Data.CODE_C1_SPA: {
- // SetPenAttributes
- int textTag = (data[pos] & 0xf0) >> 4;
- int penSize = data[pos] & 0x03;
- int penOffset = (data[pos] & 0x0c) >> 2;
- boolean italic = (data[pos + 1] & 0x80) != 0;
- boolean underline = (data[pos + 1] & 0x40) != 0;
- int edgeType = (data[pos + 1] & 0x38) >> 3;
- int fontTag = data[pos + 1] & 0x7;
- pos += 2;
- emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_SPA,
- new CaptionPenAttr(penSize, penOffset, textTag, fontTag, edgeType,
- underline, italic)));
- if (DEBUG) {
- Log.d(TAG, String.format(
- "CaptionCommand SPA penSize: %d, penOffset: %d, textTag: %d, "
- + "fontTag: %d, edgeType: %d, underline: %s, italic: %s",
- penSize, penOffset, textTag, fontTag, edgeType, underline, italic));
+ case Cea708Data.CODE_C1_SPA:
+ {
+ // SetPenAttributes
+ int textTag = (data[pos] & 0xf0) >> 4;
+ int penSize = data[pos] & 0x03;
+ int penOffset = (data[pos] & 0x0c) >> 2;
+ boolean italic = (data[pos + 1] & 0x80) != 0;
+ boolean underline = (data[pos + 1] & 0x40) != 0;
+ int edgeType = (data[pos + 1] & 0x38) >> 3;
+ int fontTag = data[pos + 1] & 0x7;
+ pos += 2;
+ emitCaptionEvent(
+ new CaptionEvent(
+ CAPTION_EMIT_TYPE_COMMAND_SPA,
+ new CaptionPenAttr(
+ penSize, penOffset, textTag, fontTag, edgeType,
+ underline, italic)));
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ String.format(
+ "CaptionCommand SPA penSize: %d, penOffset: %d, textTag: %d, "
+ + "fontTag: %d, edgeType: %d, underline: %s, italic: %s",
+ penSize, penOffset, textTag, fontTag, edgeType, underline,
+ italic));
+ }
+ break;
}
- break;
- }
- case Cea708Data.CODE_C1_SPC: {
- // SetPenColor
- int opacity = (data[pos] & 0xc0) >> 6;
- int red = (data[pos] & 0x30) >> 4;
- int green = (data[pos] & 0x0c) >> 2;
- int blue = data[pos] & 0x03;
- CaptionColor foregroundColor = new CaptionColor(opacity, red, green, blue);
- ++pos;
- opacity = (data[pos] & 0xc0) >> 6;
- red = (data[pos] & 0x30) >> 4;
- green = (data[pos] & 0x0c) >> 2;
- blue = data[pos] & 0x03;
- CaptionColor backgroundColor = new CaptionColor(opacity, red, green, blue);
- ++pos;
- red = (data[pos] & 0x30) >> 4;
- green = (data[pos] & 0x0c) >> 2;
- blue = data[pos] & 0x03;
- CaptionColor edgeColor = new CaptionColor(
- CaptionColor.OPACITY_SOLID, red, green, blue);
- ++pos;
- emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_SPC,
- new CaptionPenColor(foregroundColor, backgroundColor, edgeColor)));
- if (DEBUG) {
- Log.d(TAG, String.format(
- "CaptionCommand SPC foregroundColor %s backgroundColor %s edgeColor %s",
- foregroundColor, backgroundColor, edgeColor));
+ case Cea708Data.CODE_C1_SPC:
+ {
+ // SetPenColor
+ int opacity = (data[pos] & 0xc0) >> 6;
+ int red = (data[pos] & 0x30) >> 4;
+ int green = (data[pos] & 0x0c) >> 2;
+ int blue = data[pos] & 0x03;
+ CaptionColor foregroundColor = new CaptionColor(opacity, red, green, blue);
+ ++pos;
+ opacity = (data[pos] & 0xc0) >> 6;
+ red = (data[pos] & 0x30) >> 4;
+ green = (data[pos] & 0x0c) >> 2;
+ blue = data[pos] & 0x03;
+ CaptionColor backgroundColor = new CaptionColor(opacity, red, green, blue);
+ ++pos;
+ red = (data[pos] & 0x30) >> 4;
+ green = (data[pos] & 0x0c) >> 2;
+ blue = data[pos] & 0x03;
+ CaptionColor edgeColor =
+ new CaptionColor(CaptionColor.OPACITY_SOLID, red, green, blue);
+ ++pos;
+ emitCaptionEvent(
+ new CaptionEvent(
+ CAPTION_EMIT_TYPE_COMMAND_SPC,
+ new CaptionPenColor(
+ foregroundColor, backgroundColor, edgeColor)));
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ String.format(
+ "CaptionCommand SPC foregroundColor %s backgroundColor %s edgeColor %s",
+ foregroundColor, backgroundColor, edgeColor));
+ }
+ break;
}
- break;
- }
- case Cea708Data.CODE_C1_SPL: {
- // SetPenLocation
- // column is normally 0-31 for 4:3 formats, and 0-41 for 16:9 formats
- int row = data[pos] & 0x0f;
- int column = data[pos + 1] & 0x3f;
- pos += 2;
- emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_SPL,
- new CaptionPenLocation(row, column)));
- if (DEBUG) {
- Log.d(TAG, String.format("CaptionCommand SPL row: %d, column: %d",
- row, column));
+ case Cea708Data.CODE_C1_SPL:
+ {
+ // SetPenLocation
+ // column is normally 0-31 for 4:3 formats, and 0-41 for 16:9 formats
+ int row = data[pos] & 0x0f;
+ int column = data[pos + 1] & 0x3f;
+ pos += 2;
+ emitCaptionEvent(
+ new CaptionEvent(
+ CAPTION_EMIT_TYPE_COMMAND_SPL,
+ new CaptionPenLocation(row, column)));
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ String.format(
+ "CaptionCommand SPL row: %d, column: %d", row, column));
+ }
+ break;
}
- break;
- }
- case Cea708Data.CODE_C1_SWA: {
- // SetWindowAttributes
- int opacity = (data[pos] & 0xc0) >> 6;
- int red = (data[pos] & 0x30) >> 4;
- int green = (data[pos] & 0x0c) >> 2;
- int blue = data[pos] & 0x03;
- CaptionColor fillColor = new CaptionColor(opacity, red, green, blue);
- int borderType = (data[pos + 1] & 0xc0) >> 6 | (data[pos + 2] & 0x80) >> 5;
- red = (data[pos + 1] & 0x30) >> 4;
- green = (data[pos + 1] & 0x0c) >> 2;
- blue = data[pos + 1] & 0x03;
- CaptionColor borderColor = new CaptionColor(
- CaptionColor.OPACITY_SOLID, red, green, blue);
- boolean wordWrap = (data[pos + 2] & 0x40) != 0;
- int printDirection = (data[pos + 2] & 0x30) >> 4;
- int scrollDirection = (data[pos + 2] & 0x0c) >> 2;
- int justify = (data[pos + 2] & 0x03);
- int effectSpeed = (data[pos + 3] & 0xf0) >> 4;
- int effectDirection = (data[pos + 3] & 0x0c) >> 2;
- int displayEffect = data[pos + 3] & 0x3;
- pos += 4;
- emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_SWA,
- new CaptionWindowAttr(fillColor, borderColor, borderType, wordWrap,
- printDirection, scrollDirection, justify,
- effectDirection, effectSpeed, displayEffect)));
- if (DEBUG) {
- Log.d(TAG, String.format(
- "CaptionCommand SWA fillColor: %s, borderColor: %s, borderType: %d"
- + "wordWrap: %s, printDirection: %d, scrollDirection: %d, "
- + "justify: %s, effectDirection: %d, effectSpeed: %d, "
- + "displayEffect: %d",
- fillColor, borderColor, borderType, wordWrap, printDirection,
- scrollDirection, justify, effectDirection, effectSpeed, displayEffect));
+ case Cea708Data.CODE_C1_SWA:
+ {
+ // SetWindowAttributes
+ int opacity = (data[pos] & 0xc0) >> 6;
+ int red = (data[pos] & 0x30) >> 4;
+ int green = (data[pos] & 0x0c) >> 2;
+ int blue = data[pos] & 0x03;
+ CaptionColor fillColor = new CaptionColor(opacity, red, green, blue);
+ int borderType = (data[pos + 1] & 0xc0) >> 6 | (data[pos + 2] & 0x80) >> 5;
+ red = (data[pos + 1] & 0x30) >> 4;
+ green = (data[pos + 1] & 0x0c) >> 2;
+ blue = data[pos + 1] & 0x03;
+ CaptionColor borderColor =
+ new CaptionColor(CaptionColor.OPACITY_SOLID, red, green, blue);
+ boolean wordWrap = (data[pos + 2] & 0x40) != 0;
+ int printDirection = (data[pos + 2] & 0x30) >> 4;
+ int scrollDirection = (data[pos + 2] & 0x0c) >> 2;
+ int justify = (data[pos + 2] & 0x03);
+ int effectSpeed = (data[pos + 3] & 0xf0) >> 4;
+ int effectDirection = (data[pos + 3] & 0x0c) >> 2;
+ int displayEffect = data[pos + 3] & 0x3;
+ pos += 4;
+ emitCaptionEvent(
+ new CaptionEvent(
+ CAPTION_EMIT_TYPE_COMMAND_SWA,
+ new CaptionWindowAttr(
+ fillColor,
+ borderColor,
+ borderType,
+ wordWrap,
+ printDirection,
+ scrollDirection,
+ justify,
+ effectDirection,
+ effectSpeed,
+ displayEffect)));
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ String.format(
+ "CaptionCommand SWA fillColor: %s, borderColor: %s, borderType: %d"
+ + "wordWrap: %s, printDirection: %d, scrollDirection: %d, "
+ + "justify: %s, effectDirection: %d, effectSpeed: %d, "
+ + "displayEffect: %d",
+ fillColor,
+ borderColor,
+ borderType,
+ wordWrap,
+ printDirection,
+ scrollDirection,
+ justify,
+ effectDirection,
+ effectSpeed,
+ displayEffect));
+ }
+ break;
}
- break;
- }
case Cea708Data.CODE_C1_DF0:
case Cea708Data.CODE_C1_DF1:
@@ -671,39 +747,65 @@ public class Cea708Parser {
case Cea708Data.CODE_C1_DF4:
case Cea708Data.CODE_C1_DF5:
case Cea708Data.CODE_C1_DF6:
- case Cea708Data.CODE_C1_DF7: {
- // DefineWindow0-7
- int windowId = mCommand - Cea708Data.CODE_C1_DF0;
- boolean visible = (data[pos] & 0x20) != 0;
- boolean rowLock = (data[pos] & 0x10) != 0;
- boolean columnLock = (data[pos] & 0x08) != 0;
- int priority = data[pos] & 0x07;
- boolean relativePositioning = (data[pos + 1] & 0x80) != 0;
- int anchorVertical = data[pos + 1] & 0x7f;
- int anchorHorizontal = data[pos + 2] & 0xff;
- int anchorId = (data[pos + 3] & 0xf0) >> 4;
- int rowCount = data[pos + 3] & 0x0f;
- int columnCount = data[pos + 4] & 0x3f;
- int windowStyle = (data[pos + 5] & 0x38) >> 3;
- int penStyle = data[pos + 5] & 0x07;
- pos += 6;
- emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_DFX,
- new CaptionWindow(windowId, visible, rowLock, columnLock, priority,
- relativePositioning, anchorVertical, anchorHorizontal, anchorId,
- rowCount, columnCount, penStyle, windowStyle)));
- if (DEBUG) {
- Log.d(TAG, String.format(
- "CaptionCommand DFx windowId: %d, priority: %d, columnLock: %s, "
- + "rowLock: %s, visible: %s, anchorVertical: %d, "
- + "relativePositioning: %s, anchorHorizontal: %d, "
- + "rowCount: %d, anchorId: %d, columnCount: %d, penStyle: %d, "
- + "windowStyle: %d",
- windowId, priority, columnLock, rowLock, visible, anchorVertical,
- relativePositioning, anchorHorizontal, rowCount, anchorId, columnCount,
- penStyle, windowStyle));
+ case Cea708Data.CODE_C1_DF7:
+ {
+ // DefineWindow0-7
+ int windowId = mCommand - Cea708Data.CODE_C1_DF0;
+ boolean visible = (data[pos] & 0x20) != 0;
+ boolean rowLock = (data[pos] & 0x10) != 0;
+ boolean columnLock = (data[pos] & 0x08) != 0;
+ int priority = data[pos] & 0x07;
+ boolean relativePositioning = (data[pos + 1] & 0x80) != 0;
+ int anchorVertical = data[pos + 1] & 0x7f;
+ int anchorHorizontal = data[pos + 2] & 0xff;
+ int anchorId = (data[pos + 3] & 0xf0) >> 4;
+ int rowCount = data[pos + 3] & 0x0f;
+ int columnCount = data[pos + 4] & 0x3f;
+ int windowStyle = (data[pos + 5] & 0x38) >> 3;
+ int penStyle = data[pos + 5] & 0x07;
+ pos += 6;
+ emitCaptionEvent(
+ new CaptionEvent(
+ CAPTION_EMIT_TYPE_COMMAND_DFX,
+ new CaptionWindow(
+ windowId,
+ visible,
+ rowLock,
+ columnLock,
+ priority,
+ relativePositioning,
+ anchorVertical,
+ anchorHorizontal,
+ anchorId,
+ rowCount,
+ columnCount,
+ penStyle,
+ windowStyle)));
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ String.format(
+ "CaptionCommand DFx windowId: %d, priority: %d, columnLock: %s, "
+ + "rowLock: %s, visible: %s, anchorVertical: %d, "
+ + "relativePositioning: %s, anchorHorizontal: %d, "
+ + "rowCount: %d, anchorId: %d, columnCount: %d, penStyle: %d, "
+ + "windowStyle: %d",
+ windowId,
+ priority,
+ columnLock,
+ rowLock,
+ visible,
+ anchorVertical,
+ relativePositioning,
+ anchorHorizontal,
+ rowCount,
+ anchorId,
+ columnCount,
+ penStyle,
+ windowStyle));
+ }
+ break;
}
- break;
- }
default:
break;
@@ -748,7 +850,7 @@ public class Cea708Parser {
pos = parseG2(data, pos);
} else if (mCommand >= Cea708Data.CODE_G3_RANGE_START
&& mCommand <= Cea708Data.CODE_G3_RANGE_END) {
- pos = parseG3(data ,pos);
+ pos = parseG3(data, pos);
}
return pos;
}
diff --git a/src/com/android/tv/tuner/data/Cea708Data.java b/tuner/src/com/android/tv/tuner/data/Cea708Data.java
index 6350d63c..73a90181 100644
--- a/src/com/android/tv/tuner/data/Cea708Data.java
+++ b/tuner/src/com/android/tv/tuner/data/Cea708Data.java
@@ -16,18 +16,14 @@
package com.android.tv.tuner.data;
-import com.android.tv.tuner.cc.Cea708Parser;
-
import android.graphics.Color;
import android.support.annotation.NonNull;
+import com.android.tv.tuner.cc.Cea708Parser;
-/**
- * Collection of CEA-708 structures.
- */
+/** Collection of CEA-708 structures. */
public class Cea708Data {
- private Cea708Data() {
- }
+ private Cea708Data() {}
// According to CEA-708B, the range of valid service number is between 1 and 63.
public static final int EMPTY_SERVICE_NUMBER = 0;
@@ -134,17 +130,15 @@ public class Cea708Data {
}
}
- /**
- * CEA-708B-specific color.
- */
+ /** CEA-708B-specific color. */
public static class CaptionColor {
public static final int OPACITY_SOLID = 0;
public static final int OPACITY_FLASH = 1;
public static final int OPACITY_TRANSLUCENT = 2;
public static final int OPACITY_TRANSPARENT = 3;
- private static final int[] COLOR_MAP = new int[] { 0x00, 0x0f, 0xf0, 0xff };
- private static final int[] OPACITY_MAP = new int[] { 0xff, 0xfe, 0x80, 0x00 };
+ private static final int[] COLOR_MAP = new int[] {0x00, 0x0f, 0xf0, 0xff};
+ private static final int[] OPACITY_MAP = new int[] {0xff, 0xfe, 0x80, 0x00};
public final int opacity;
public final int red;
@@ -164,9 +158,7 @@ public class Cea708Data {
}
}
- /**
- * Caption event generated by {@link Cea708Parser}.
- */
+ /** Caption event generated by {@link Cea708Parser}. */
public static class CaptionEvent {
@Cea708Parser.CaptionEmitType public final int type;
public final Object obj;
@@ -177,9 +169,7 @@ public class Cea708Data {
}
}
- /**
- * Pen style information.
- */
+ /** Pen style information. */
public static class CaptionPenAttr {
// Pen sizes
public static final int PEN_SIZE_SMALL = 0;
@@ -199,8 +189,14 @@ public class Cea708Data {
public final boolean underline;
public final boolean italic;
- public CaptionPenAttr(int penSize, int penOffset, int textTag, int fontTag, int edgeType,
- boolean underline, boolean italic) {
+ public CaptionPenAttr(
+ int penSize,
+ int penOffset,
+ int textTag,
+ int fontTag,
+ int edgeType,
+ boolean underline,
+ boolean italic) {
this.penSize = penSize;
this.penOffset = penOffset;
this.textTag = textTag;
@@ -220,7 +216,9 @@ public class Cea708Data {
public final CaptionColor backgroundColor;
public final CaptionColor edgeColor;
- public CaptionPenColor(CaptionColor foregroundColor, CaptionColor backgroundColor,
+ public CaptionPenColor(
+ CaptionColor foregroundColor,
+ CaptionColor backgroundColor,
CaptionColor edgeColor) {
this.foregroundColor = foregroundColor;
this.backgroundColor = backgroundColor;
@@ -228,9 +226,7 @@ public class Cea708Data {
}
}
- /**
- * Location information of a pen.
- */
+ /** Location information of a pen. */
public static class CaptionPenLocation {
public final int row;
public final int column;
@@ -241,9 +237,7 @@ public class Cea708Data {
}
}
- /**
- * Attributes of a caption window, which is defined in CEA-708B.
- */
+ /** Attributes of a caption window, which is defined in CEA-708B. */
public static class CaptionWindowAttr {
public static final int JUSTIFY_LEFT = 0;
public static final int JUSTIFY_CENTER = 2;
@@ -263,10 +257,17 @@ public class Cea708Data {
public final int effectSpeed;
public final int displayEffect;
- public CaptionWindowAttr(CaptionColor fillColor, CaptionColor borderColor, int borderType,
- boolean wordWrap, int printDirection, int scrollDirection, int justify,
+ public CaptionWindowAttr(
+ CaptionColor fillColor,
+ CaptionColor borderColor,
+ int borderType,
+ boolean wordWrap,
+ int printDirection,
+ int scrollDirection,
+ int justify,
int effectDirection,
- int effectSpeed, int displayEffect) {
+ int effectSpeed,
+ int displayEffect) {
this.fillColor = fillColor;
this.borderColor = borderColor;
this.borderType = borderType;
@@ -280,9 +281,7 @@ public class Cea708Data {
}
}
- /**
- * Construction information of the caption window of CEA-708B.
- */
+ /** Construction information of the caption window of CEA-708B. */
public static class CaptionWindow {
public final int id;
public final boolean visible;
@@ -298,10 +297,20 @@ public class Cea708Data {
public final int penStyle;
public final int windowStyle;
- public CaptionWindow(int id, boolean visible,
- boolean rowLock, boolean columnLock, int priority, boolean relativePositioning,
- int anchorVertical, int anchorHorizontal, int anchorId,
- int rowCount, int columnCount, int penStyle, int windowStyle) {
+ public CaptionWindow(
+ int id,
+ boolean visible,
+ boolean rowLock,
+ boolean columnLock,
+ int priority,
+ boolean relativePositioning,
+ int anchorVertical,
+ int anchorHorizontal,
+ int anchorId,
+ int rowCount,
+ int columnCount,
+ int penStyle,
+ int windowStyle) {
this.id = id;
this.visible = visible;
this.rowLock = rowLock;
diff --git a/src/com/android/tv/tuner/data/PsiData.java b/tuner/src/com/android/tv/tuner/data/PsiData.java
index 67700c6a..9b7c2e2c 100644
--- a/src/com/android/tv/tuner/data/PsiData.java
+++ b/tuner/src/com/android/tv/tuner/data/PsiData.java
@@ -14,21 +14,16 @@
* limitations under the License.
*/
-
package com.android.tv.tuner.data;
import com.android.tv.tuner.data.nano.Track.AtscAudioTrack;
import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
-
import java.util.List;
-/**
- * Collection of MPEG PSI table items.
- */
+/** Collection of MPEG PSI table items. */
public class PsiData {
- private PsiData() {
- }
+ private PsiData() {}
public static class PatItem {
private final int mProgramNo;
@@ -61,8 +56,11 @@ public class PsiData {
private final List<AtscAudioTrack> mAudioTracks;
private final List<AtscCaptionTrack> mCaptionTracks;
- public PmtItem(int streamType, int esPid,
- List<AtscAudioTrack> audioTracks, List<AtscCaptionTrack> captionTracks) {
+ public PmtItem(
+ int streamType,
+ int esPid,
+ List<AtscAudioTrack> audioTracks,
+ List<AtscCaptionTrack> captionTracks) {
mStreamType = streamType;
mEsPid = esPid;
mAudioTracks = audioTracks;
@@ -87,7 +85,8 @@ public class PsiData {
@Override
public String toString() {
- return String.format("Stream Type: %x ES Pid: %x AudioTracks: %s CaptionTracks: %s",
+ return String.format(
+ "Stream Type: %x ES Pid: %x AudioTracks: %s CaptionTracks: %s",
mStreamType, mEsPid, mAudioTracks, mCaptionTracks);
}
}
diff --git a/src/com/android/tv/tuner/data/PsipData.java b/tuner/src/com/android/tv/tuner/data/PsipData.java
index 8f98e67c..239009dc 100644
--- a/src/com/android/tv/tuner/data/PsipData.java
+++ b/tuner/src/com/android/tv/tuner/data/PsipData.java
@@ -19,25 +19,20 @@ package com.android.tv.tuner.data;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.text.format.DateUtils;
-
+import com.android.tv.common.util.StringUtils;
import com.android.tv.tuner.data.nano.Track.AtscAudioTrack;
import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
import com.android.tv.tuner.ts.SectionParser;
import com.android.tv.tuner.util.ConvertUtils;
-import com.android.tv.util.StringUtils;
-
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
-/**
- * Collection of ATSC PSIP table items.
- */
+/** Collection of ATSC PSIP table items. */
public class PsipData {
- private PsipData() {
- }
+ private PsipData() {}
public static class PsipSection {
private final int mTableId;
@@ -56,7 +51,10 @@ public class PsipData {
return new PsipSection(tableId, tableIdExtension, sectionNumber, currentNextIndicator);
}
- private PsipSection(int tableId, int tableIdExtension, int sectionNumber,
+ private PsipSection(
+ int tableId,
+ int tableIdExtension,
+ int sectionNumber,
boolean currentNextIndicator) {
mTableId = tableId;
mTableIdExtension = tableIdExtension;
@@ -104,29 +102,21 @@ public class PsipData {
}
}
- /**
- * {@link TvTracksInterface} for serving the audio and caption tracks.
- */
+ /** {@link TvTracksInterface} for serving the audio and caption tracks. */
public interface TvTracksInterface {
- /**
- * Set the flag that tells the caption tracks have been found in this section container.
- */
+ /** Set the flag that tells the caption tracks have been found in this section container. */
void setHasCaptionTrack();
/**
- * Returns whether or not the caption tracks have been found in this section container.
- * If true, zero caption track will be interpreted as a clearance of the caption tracks.
+ * Returns whether or not the caption tracks have been found in this section container. If
+ * true, zero caption track will be interpreted as a clearance of the caption tracks.
*/
boolean hasCaptionTrack();
- /**
- * Returns the audio tracks received.
- */
+ /** Returns the audio tracks received. */
List<AtscAudioTrack> getAudioTracks();
- /**
- * Returns the caption tracks received.
- */
+ /** Returns the caption tracks received. */
List<AtscCaptionTrack> getCaptionTracks();
}
@@ -165,8 +155,15 @@ public class PsipData {
private final int mSourceId;
private String mDescription;
- public VctItem(String shortName, String longName, int serviceType, int channelTsid,
- int programNumber, int majorChannelNumber, int minorChannelNumber, int sourceId) {
+ public VctItem(
+ String shortName,
+ String longName,
+ int serviceType,
+ int channelTsid,
+ int programNumber,
+ int majorChannelNumber,
+ int minorChannelNumber,
+ int sourceId) {
mShortName = shortName;
mLongName = longName;
mServiceType = serviceType;
@@ -211,11 +208,18 @@ public class PsipData {
@Override
public String toString() {
- return String
- .format(Locale.US, "ShortName: %s LongName: %s ServiceType: %d ChannelTsid: %x "
+ return String.format(
+ Locale.US,
+ "ShortName: %s LongName: %s ServiceType: %d ChannelTsid: %x "
+ "ProgramNumber:%d %d-%d SourceId: %x",
- mShortName, mLongName, mServiceType, mChannelTsid,
- mProgramNumber, mMajorChannelNumber, mMinorChannelNumber, mSourceId);
+ mShortName,
+ mLongName,
+ mServiceType,
+ mChannelTsid,
+ mProgramNumber,
+ mMajorChannelNumber,
+ mMinorChannelNumber,
+ mSourceId);
}
public void setDescription(String description) {
@@ -234,8 +238,12 @@ public class PsipData {
private final int mServiceId;
private final int mOriginalNetWorkId;
- public SdtItem(String serviceName, String serviceProviderName, int serviceType,
- int serviceId, int originalNetWorkId) {
+ public SdtItem(
+ String serviceName,
+ String serviceProviderName,
+ int serviceType,
+ int serviceId,
+ int originalNetWorkId) {
mServiceName = serviceName;
mServiceProviderName = serviceProviderName;
mServiceType = serviceType;
@@ -265,15 +273,14 @@ public class PsipData {
@Override
public String toString() {
- return String.format("ServiceName: %s ServiceProviderName:%s ServiceType:%d "
+ return String.format(
+ "ServiceName: %s ServiceProviderName:%s ServiceType:%d "
+ "OriginalNetworkId:%d",
mServiceName, mServiceProviderName, mServiceType, mOriginalNetWorkId);
}
}
- /**
- * A base class for descriptors of Ts packets.
- */
+ /** A base class for descriptors of Ts packets. */
public abstract static class TsDescriptor {
public abstract int getTag();
}
@@ -374,10 +381,22 @@ public class PsipData {
private final String mLanguage;
private final String mLanguage2;
- public Ac3AudioDescriptor(byte sampleRateCode, byte bsid, byte bitRateCode,
- byte surroundMode, byte bsmod, int numChannels, boolean fullSvc, byte langCod,
- byte langCod2, byte mainId, byte priority, byte asvcflags, String text,
- String language, String language2) {
+ public Ac3AudioDescriptor(
+ byte sampleRateCode,
+ byte bsid,
+ byte bitRateCode,
+ byte surroundMode,
+ byte bsmod,
+ int numChannels,
+ boolean fullSvc,
+ byte langCod,
+ byte langCod2,
+ byte mainId,
+ byte priority,
+ byte asvcflags,
+ String text,
+ String language,
+ String language2) {
mSampleRateCode = sampleRateCode;
mBsid = bsid;
mBitRateCode = bitRateCode;
@@ -475,13 +494,27 @@ public class PsipData {
@Override
public String toString() {
- return String.format(Locale.US,
+ return String.format(
+ Locale.US,
"AC3 audio stream sampleRateCode: %d, bsid: %d, bitRateCode: %d, "
- + "surroundMode: %d, bsmod: %d, numChannels: %d, fullSvc: %s, langCod: %d, "
- + "langCod2: %d, mainId: %d, priority: %d, avcflags: %d, text: %s, language: %s"
- + ", language2: %s", mSampleRateCode, mBsid, mBitRateCode, mSurroundMode,
- mBsmod, mNumChannels, mFullSvc, mLangCod, mLangCod2, mMainId, mPriority,
- mAsvcflags, mText, mLanguage, mLanguage2);
+ + "surroundMode: %d, bsmod: %d, numChannels: %d, fullSvc: %s, langCod: %d, "
+ + "langCod2: %d, mainId: %d, priority: %d, avcflags: %d, text: %s, language: %s"
+ + ", language2: %s",
+ mSampleRateCode,
+ mBsid,
+ mBitRateCode,
+ mSurroundMode,
+ mBsmod,
+ mNumChannels,
+ mFullSvc,
+ mLangCod,
+ mLangCod2,
+ mMainId,
+ mPriority,
+ mAsvcflags,
+ mText,
+ mLanguage,
+ mLanguage2);
}
}
@@ -540,7 +573,8 @@ public class PsipData {
return String.format(
"Service descriptor, service type: %d, "
+ "service provider name: %s, "
- + "service name: %s", mServiceType, mServiceProviderName, mServiceName);
+ + "service name: %s",
+ mServiceType, mServiceProviderName, mServiceName);
}
}
@@ -566,8 +600,9 @@ public class PsipData {
@Override
public String toString() {
- return String.format("ShortEvent Descriptor, language:%s, event name: %s, "
- + "text:%s", mLanguage, mEventName, mText);
+ return String.format(
+ "ShortEvent Descriptor, language:%s, event name: %s, " + "text:%s",
+ mLanguage, mEventName, mText);
}
}
@@ -652,9 +687,17 @@ public class PsipData {
private final String mBroadcastGenre;
private final String mCanonicalGenre;
- public EitItem(long programId, int eventId, String titleText, long startTime,
- int lengthInSecond, String contentRating, List<AtscAudioTrack> audioTracks,
- List<AtscCaptionTrack> captionTracks, String broadcastGenre, String canonicalGenre,
+ public EitItem(
+ long programId,
+ int eventId,
+ String titleText,
+ long startTime,
+ int lengthInSecond,
+ String contentRating,
+ List<AtscAudioTrack> audioTracks,
+ List<AtscCaptionTrack> captionTracks,
+ String broadcastGenre,
+ String canonicalGenre,
String description) {
mProgramId = programId;
mEventId = eventId;
@@ -702,8 +745,8 @@ public class PsipData {
}
public long getEndTimeUtcMillis() {
- return ConvertUtils.convertGPSTimeToUnixEpoch(
- mStartTime + mLengthInSecond) * DateUtils.SECOND_IN_MILLIS;
+ return ConvertUtils.convertGPSTimeToUnixEpoch(mStartTime + mLengthInSecond)
+ * DateUtils.SECOND_IN_MILLIS;
}
public String getContentRating() {
@@ -797,14 +840,22 @@ public class PsipData {
@Override
public String toString() {
- return String.format(Locale.US,
+ return String.format(
+ Locale.US,
"EitItem programId: %d, eventId: %d, title: %s, startTime: %10d, "
+ "length: %6d, rating: %s, audio tracks: %d, caption tracks: %d, "
+ "genres (broadcast: %s, canonical: %s), description: %s",
- mProgramId, mEventId, mTitleText, mStartTime, mLengthInSecond, mContentRating,
+ mProgramId,
+ mEventId,
+ mTitleText,
+ mStartTime,
+ mLengthInSecond,
+ mContentRating,
mAudioTracks != null ? mAudioTracks.size() : 0,
mCaptionTracks != null ? mCaptionTracks.size() : 0,
- mBroadcastGenre, mCanonicalGenre, mDescription);
+ mBroadcastGenre,
+ mCanonicalGenre,
+ mDescription);
}
}
diff --git a/src/com/android/tv/tuner/data/TunerChannel.java b/tuner/src/com/android/tv/tuner/data/TunerChannel.java
index 1cf514c1..d20c343b 100644
--- a/src/com/android/tv/tuner/data/TunerChannel.java
+++ b/tuner/src/com/android/tv/tuner/data/TunerChannel.java
@@ -16,17 +16,16 @@
package com.android.tv.tuner.data;
+import android.database.Cursor;
import android.support.annotation.NonNull;
import android.util.Log;
-
+import com.android.tv.common.util.StringUtils;
import com.android.tv.tuner.data.nano.Channel;
import com.android.tv.tuner.data.nano.Channel.TunerChannelProto;
import com.android.tv.tuner.data.nano.Track.AtscAudioTrack;
import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
import com.android.tv.tuner.util.Ints;
-import com.android.tv.util.StringUtils;
import com.google.protobuf.nano.MessageNano;
-
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -34,31 +33,29 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
-/**
- * A class that represents a single channel accessible through a tuner.
- */
+/** A class that represents a single channel accessible through a tuner. */
public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracksInterface {
private static final String TAG = "TunerChannel";
- /**
- * Channel number separator between major number and minor number.
- */
+ /** Channel number separator between major number and minor number. */
public static final char CHANNEL_NUMBER_SEPARATOR = '-';
// See ATSC Code Points Registry.
- private static final String[] ATSC_SERVICE_TYPE_NAMES = new String[] {
- "ATSC Reserved",
- "Analog television channels",
- "ATSC_digital_television",
- "ATSC_audio",
- "ATSC_data_only_service",
- "Software Download",
- "Unassociated/Small Screen Service",
- "Parameterized Service",
- "ATSC NRT Service",
- "Extended Parameterized Service" };
+ private static final String[] ATSC_SERVICE_TYPE_NAMES =
+ new String[] {
+ "ATSC Reserved",
+ "Analog television channels",
+ "ATSC_digital_television",
+ "ATSC_audio",
+ "ATSC_data_only_service",
+ "Software Download",
+ "Unassociated/Small Screen Service",
+ "Parameterized Service",
+ "ATSC NRT Service",
+ "Extended Parameterized Service"
+ };
private static final String ATSC_SERVICE_TYPE_NAME_RESERVED =
- ATSC_SERVICE_TYPE_NAMES[Channel.SERVICE_TYPE_ATSC_RESERVED];
+ ATSC_SERVICE_TYPE_NAMES[Channel.AtscServiceType.SERVICE_TYPE_ATSC_RESERVED];
public static final int INVALID_FREQUENCY = -1;
@@ -71,8 +68,8 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks
// @GuardedBy(this) Writing operations and toByteArray will be guarded. b/34197766
private final TunerChannelProto mProto;
- private TunerChannel(PsipData.VctItem channel, int programNumber,
- List<PsiData.PmtItem> pmtItems, int type) {
+ private TunerChannel(
+ PsipData.VctItem channel, int programNumber, List<PsiData.PmtItem> pmtItems, int type) {
mProto = new TunerChannelProto();
if (channel == null) {
mProto.shortName = "";
@@ -107,31 +104,33 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks
List<Integer> audioStreamTypes = new ArrayList<>();
for (PsiData.PmtItem pmt : pmtItems) {
switch (pmt.getStreamType()) {
- // MPEG ES stream video types
- case Channel.MPEG1:
- case Channel.MPEG2:
- case Channel.H263:
- case Channel.H264:
- case Channel.H265:
+ // MPEG ES stream video types
+ case Channel.VideoStreamType.MPEG1:
+ case Channel.VideoStreamType.MPEG2:
+ case Channel.VideoStreamType.H263:
+ case Channel.VideoStreamType.H264:
+ case Channel.VideoStreamType.H265:
mProto.videoPid = pmt.getEsPid();
mProto.videoStreamType = pmt.getStreamType();
break;
- // MPEG ES stream audio types
- case Channel.MPEG1AUDIO:
- case Channel.MPEG2AUDIO:
- case Channel.MPEG2AACAUDIO:
- case Channel.MPEG4LATMAACAUDIO:
- case Channel.A52AC3AUDIO:
- case Channel.EAC3AUDIO:
+ // MPEG ES stream audio types
+ case Channel.AudioStreamType.MPEG1AUDIO:
+ case Channel.AudioStreamType.MPEG2AUDIO:
+ case Channel.AudioStreamType.MPEG2AACAUDIO:
+ case Channel.AudioStreamType.MPEG4LATMAACAUDIO:
+ case Channel.AudioStreamType.A52AC3AUDIO:
+ case Channel.AudioStreamType.EAC3AUDIO:
audioPids.add(pmt.getEsPid());
audioStreamTypes.add(pmt.getStreamType());
break;
- // Non MPEG ES stream types
+ // Non MPEG ES stream types
case 0x100: // PmtItem.ES_PID_PCR:
mProto.pcrPid = pmt.getEsPid();
break;
+ default:
+ // fall out
}
}
mProto.audioPids = Ints.toArray(audioPids);
@@ -139,8 +138,8 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks
mProto.audioTrackIndex = (audioPids.size() > 0) ? 0 : -1;
}
- private TunerChannel(int programNumber, int type, PsipData.SdtItem channel,
- List<PsiData.PmtItem> pmtItems) {
+ private TunerChannel(
+ int programNumber, int type, PsipData.SdtItem channel, List<PsiData.PmtItem> pmtItems) {
mProto = new TunerChannelProto();
mProto.tsid = 0;
mProto.virtualMajor = 0;
@@ -156,25 +155,19 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks
initProto(pmtItems, type);
}
- /**
- * Initialize tuner channel with VCT items and PMT items.
- */
+ /** Initialize tuner channel with VCT items and PMT items. */
public TunerChannel(PsipData.VctItem channel, List<PsiData.PmtItem> pmtItems) {
- this(channel, 0, pmtItems, Channel.TYPE_TUNER);
+ this(channel, 0, pmtItems, Channel.TunerType.TYPE_TUNER);
}
- /**
- * Initialize tuner channel with program number and PMT items.
- */
+ /** Initialize tuner channel with program number and PMT items. */
public TunerChannel(int programNumber, List<PsiData.PmtItem> pmtItems) {
- this(null, programNumber, pmtItems, Channel.TYPE_TUNER);
+ this(null, programNumber, pmtItems, Channel.TunerType.TYPE_TUNER);
}
- /**
- * Initialize tuner channel with SDT items and PMT items.
- */
+ /** Initialize tuner channel with SDT items and PMT items. */
public TunerChannel(PsipData.SdtItem channel, List<PsiData.PmtItem> pmtItems) {
- this(0, Channel.TYPE_TUNER, channel, pmtItems);
+ this(0, Channel.TunerType.TYPE_TUNER, channel, pmtItems);
}
private TunerChannel(TunerChannelProto tunerChannelProto) {
@@ -182,39 +175,49 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks
}
public static TunerChannel forFile(PsipData.VctItem channel, List<PsiData.PmtItem> pmtItems) {
- return new TunerChannel(channel, 0, pmtItems, Channel.TYPE_FILE);
+ return new TunerChannel(channel, 0, pmtItems, Channel.TunerType.TYPE_FILE);
}
public static TunerChannel forDvbFile(
PsipData.SdtItem channel, List<PsiData.PmtItem> pmtItems) {
- return new TunerChannel(0, Channel.TYPE_FILE, channel, pmtItems);
+ return new TunerChannel(0, Channel.TunerType.TYPE_FILE, channel, pmtItems);
}
/**
* Create a TunerChannel object suitable for network tuners
+ *
* @param major Channel number major
* @param minor Channel number minor
* @param programNumber Program number
* @param shortName Short name
* @param recordingProhibited Recording prohibition info
- * @param videoFormat Video format. Should be {@code null} or one of the followings:
- * {@link android.media.tv.TvContract.Channels#VIDEO_FORMAT_240P},
- * {@link android.media.tv.TvContract.Channels#VIDEO_FORMAT_360P},
- * {@link android.media.tv.TvContract.Channels#VIDEO_FORMAT_480I},
- * {@link android.media.tv.TvContract.Channels#VIDEO_FORMAT_480P},
- * {@link android.media.tv.TvContract.Channels#VIDEO_FORMAT_576I},
- * {@link android.media.tv.TvContract.Channels#VIDEO_FORMAT_576P},
- * {@link android.media.tv.TvContract.Channels#VIDEO_FORMAT_720P},
- * {@link android.media.tv.TvContract.Channels#VIDEO_FORMAT_1080I},
- * {@link android.media.tv.TvContract.Channels#VIDEO_FORMAT_1080P},
- * {@link android.media.tv.TvContract.Channels#VIDEO_FORMAT_2160P},
- * {@link android.media.tv.TvContract.Channels#VIDEO_FORMAT_4320P}
+ * @param videoFormat Video format. Should be {@code null} or one of the followings: {@link
+ * android.media.tv.TvContract.Channels#VIDEO_FORMAT_240P}, {@link
+ * android.media.tv.TvContract.Channels#VIDEO_FORMAT_360P}, {@link
+ * android.media.tv.TvContract.Channels#VIDEO_FORMAT_480I}, {@link
+ * android.media.tv.TvContract.Channels#VIDEO_FORMAT_480P}, {@link
+ * android.media.tv.TvContract.Channels#VIDEO_FORMAT_576I}, {@link
+ * android.media.tv.TvContract.Channels#VIDEO_FORMAT_576P}, {@link
+ * android.media.tv.TvContract.Channels#VIDEO_FORMAT_720P}, {@link
+ * android.media.tv.TvContract.Channels#VIDEO_FORMAT_1080I}, {@link
+ * android.media.tv.TvContract.Channels#VIDEO_FORMAT_1080P}, {@link
+ * android.media.tv.TvContract.Channels#VIDEO_FORMAT_2160P}, {@link
+ * android.media.tv.TvContract.Channels#VIDEO_FORMAT_4320P}
* @return a TunerChannel object
*/
- public static TunerChannel forNetwork(int major, int minor, int programNumber,
- String shortName, boolean recordingProhibited, String videoFormat) {
- TunerChannel tunerChannel = new TunerChannel(
- null, programNumber, Collections.EMPTY_LIST, Channel.TYPE_NETWORK);
+ public static TunerChannel forNetwork(
+ int major,
+ int minor,
+ int programNumber,
+ String shortName,
+ boolean recordingProhibited,
+ String videoFormat) {
+ TunerChannel tunerChannel =
+ new TunerChannel(
+ null,
+ programNumber,
+ Collections.EMPTY_LIST,
+ Channel.TunerType.TYPE_NETWORK);
tunerChannel.setVirtualMajor(major);
tunerChannel.setVirtualMinor(minor);
tunerChannel.setShortName(shortName);
@@ -277,7 +280,7 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks
return mProto.videoPid;
}
- synchronized public void setVideoPid(int videoPid) {
+ public synchronized void setVideoPid(int videoPid) {
mProto.videoPid = videoPid;
}
@@ -303,7 +306,7 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks
return Ints.asList(mProto.audioPids);
}
- synchronized public void setAudioPids(List<Integer> audioPids) {
+ public synchronized void setAudioPids(List<Integer> audioPids) {
mProto.audioPids = Ints.toArray(audioPids);
}
@@ -311,7 +314,7 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks
return Ints.asList(mProto.audioStreamTypes);
}
- synchronized public void setAudioStreamTypes(List<Integer> audioStreamTypes) {
+ public synchronized void setAudioStreamTypes(List<Integer> audioStreamTypes) {
mProto.audioStreamTypes = Ints.toArray(audioStreamTypes);
}
@@ -323,7 +326,7 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks
return mProto.type;
}
- synchronized public void setFilepath(String filepath) {
+ public synchronized void setFilepath(String filepath) {
mProto.filepath = filepath == null ? "" : filepath;
}
@@ -331,23 +334,23 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks
return mProto.filepath;
}
- synchronized public void setVirtualMajor(int virtualMajor) {
+ public synchronized void setVirtualMajor(int virtualMajor) {
mProto.virtualMajor = virtualMajor;
}
- synchronized public void setVirtualMinor(int virtualMinor) {
+ public synchronized void setVirtualMinor(int virtualMinor) {
mProto.virtualMinor = virtualMinor;
}
- synchronized public void setShortName(String shortName) {
+ public synchronized void setShortName(String shortName) {
mProto.shortName = shortName == null ? "" : shortName;
}
- synchronized public void setFrequency(int frequency) {
+ public synchronized void setFrequency(int frequency) {
mProto.frequency = frequency;
}
- synchronized public void setModulation(String modulation) {
+ public synchronized void setModulation(String modulation) {
mProto.modulation = modulation == null ? "" : modulation;
}
@@ -363,18 +366,34 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks
return mProto.channelId;
}
- synchronized public void setChannelId(long channelId) {
+ public synchronized void setChannelId(long channelId) {
mProto.channelId = channelId;
}
+ /**
+ * The flag indicating whether this TV channel is locked or not. This is primarily used for
+ * alternative parental control to prevent unauthorized users from watching the current channel
+ * regardless of the content rating
+ *
+ * @see <a
+ * href="https://developer.android.com/reference/android/media/tv/TvContract.Channels.html#COLUMN_LOCKED">link</a>
+ */
+ public boolean isLocked() {
+ return mProto.locked;
+ }
+
+ public synchronized void setLocked(boolean locked) {
+ mProto.locked = locked;
+ }
+
public String getDisplayNumber() {
return getDisplayNumber(true);
}
public String getDisplayNumber(boolean ignoreZeroMinorNumber) {
if (mProto.virtualMajor != 0 && (mProto.virtualMinor != 0 || !ignoreZeroMinorNumber)) {
- return String.format("%d%c%d", mProto.virtualMajor, CHANNEL_NUMBER_SEPARATOR,
- mProto.virtualMinor);
+ return String.format(
+ "%d%c%d", mProto.virtualMajor, CHANNEL_NUMBER_SEPARATOR, mProto.virtualMinor);
} else if (mProto.virtualMajor != 0) {
return Integer.toString(mProto.virtualMajor);
} else {
@@ -387,7 +406,7 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks
}
@Override
- synchronized public void setHasCaptionTrack() {
+ public synchronized void setHasCaptionTrack() {
mProto.hasCaptionTrack = true;
}
@@ -401,7 +420,7 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks
return Collections.unmodifiableList(Arrays.asList(mProto.audioTracks));
}
- synchronized public void setAudioTracks(List<AtscAudioTrack> audioTracks) {
+ public synchronized void setAudioTracks(List<AtscAudioTrack> audioTracks) {
mProto.audioTracks = audioTracks.toArray(new AtscAudioTrack[audioTracks.size()]);
}
@@ -410,11 +429,11 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks
return Collections.unmodifiableList(Arrays.asList(mProto.captionTracks));
}
- synchronized public void setCaptionTracks(List<AtscCaptionTrack> captionTracks) {
+ public synchronized void setCaptionTracks(List<AtscCaptionTrack> captionTracks) {
mProto.captionTracks = captionTracks.toArray(new AtscCaptionTrack[captionTracks.size()]);
}
- synchronized public void selectAudioTrack(int index) {
+ public synchronized void selectAudioTrack(int index) {
if (0 <= index && index < mProto.audioPids.length) {
mProto.audioTrackIndex = index;
} else {
@@ -422,7 +441,7 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks
}
}
- synchronized public void setRecordingProhibited(boolean recordingProhibited) {
+ public synchronized void setRecordingProhibited(boolean recordingProhibited) {
mProto.recordingProhibited = recordingProhibited;
}
@@ -430,7 +449,7 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks
return mProto.recordingProhibited;
}
- synchronized public void setVideoFormat(String videoFormat) {
+ public synchronized void setVideoFormat(String videoFormat) {
mProto.videoFormat = videoFormat == null ? "" : videoFormat;
}
@@ -441,15 +460,23 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks
@Override
public String toString() {
switch (mProto.type) {
- case Channel.TYPE_FILE:
- return String.format("{%d-%d %s} Filepath: %s, ProgramNumber %d",
- mProto.virtualMajor, mProto.virtualMinor, mProto.shortName,
- mProto.filepath, mProto.programNumber);
- //case Channel.TYPE_TUNER:
+ case Channel.TunerType.TYPE_FILE:
+ return String.format(
+ "{%d-%d %s} Filepath: %s, ProgramNumber %d",
+ mProto.virtualMajor,
+ mProto.virtualMinor,
+ mProto.shortName,
+ mProto.filepath,
+ mProto.programNumber);
+ // case Channel.TunerType.TYPE_TUNER:
default:
- return String.format("{%d-%d %s} Frequency: %d, ProgramNumber %d",
- mProto.virtualMajor, mProto.virtualMinor, mProto.shortName,
- mProto.frequency, mProto.programNumber);
+ return String.format(
+ "{%d-%d %s} Frequency: %d, ProgramNumber %d",
+ mProto.virtualMajor,
+ mProto.virtualMinor,
+ mProto.shortName,
+ mProto.frequency,
+ mProto.programNumber);
}
}
@@ -486,12 +513,14 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks
}
// Serialization
- synchronized public byte[] toByteArray() {
+ public synchronized byte[] toByteArray() {
try {
return MessageNano.toByteArray(mProto);
} catch (Exception e) {
// Retry toByteArray. b/34197766
- Log.w(TAG, "TunerChannel or its variables are modified in multiple thread without lock",
+ Log.w(
+ TAG,
+ "TunerChannel or its variables are modified in multiple thread without lock",
e);
return MessageNano.toByteArray(mProto);
}
@@ -508,4 +537,16 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks
return null;
}
}
+
+ public static TunerChannel fromCursor(Cursor cursor) {
+ long channelId = cursor.getLong(0);
+ boolean locked = cursor.getInt(1) > 0;
+ byte[] data = cursor.getBlob(2);
+ TunerChannel channel = TunerChannel.parseFrom(data);
+ if (channel != null) {
+ channel.setChannelId(channelId);
+ channel.setLocked(locked);
+ }
+ return channel;
+ }
}
diff --git a/src/com/android/tv/tuner/exoplayer/Cea708TextTrackRenderer.java b/tuner/src/com/android/tv/tuner/exoplayer/Cea708TextTrackRenderer.java
index 5f536708..1f48c45b 100644
--- a/src/com/android/tv/tuner/exoplayer/Cea708TextTrackRenderer.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/Cea708TextTrackRenderer.java
@@ -17,7 +17,8 @@
package com.android.tv.tuner.exoplayer;
import android.util.Log;
-
+import com.android.tv.tuner.cc.Cea708Parser;
+import com.android.tv.tuner.data.Cea708Data.CaptionEvent;
import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.MediaClock;
import com.google.android.exoplayer.MediaFormat;
@@ -26,16 +27,11 @@ import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.util.Assertions;
-import com.android.tv.tuner.cc.Cea708Parser;
-import com.android.tv.tuner.data.Cea708Data.CaptionEvent;
-
import java.io.IOException;
-/**
- * A {@link TrackRenderer} for CEA-708 textual subtitles.
- */
-public class Cea708TextTrackRenderer extends TrackRenderer implements
- Cea708Parser.OnCea708ParserListener {
+/** A {@link TrackRenderer} for CEA-708 textual subtitles. */
+public class Cea708TextTrackRenderer extends TrackRenderer
+ implements Cea708Parser.OnCea708ParserListener {
private static final String TAG = "Cea708TextTrackRenderer";
private static final boolean DEBUG = false;
@@ -59,7 +55,9 @@ public class Cea708TextTrackRenderer extends TrackRenderer implements
public interface CcListener {
void emitEvent(CaptionEvent captionEvent);
+
void clearCaption();
+
void discoverServiceNumber(int serviceNumber);
}
@@ -165,8 +163,9 @@ public class Cea708TextTrackRenderer extends TrackRenderer implements
}
private boolean processOutput() {
- return !mInputStreamEnded && mCea708Parser != null &&
- mCea708Parser.processClosedCaptions(mPresentationTimeUs);
+ return !mInputStreamEnded
+ && mCea708Parser != null
+ && mCea708Parser.processClosedCaptions(mPresentationTimeUs);
}
private boolean feedInputBuffer() throws IOException, ExoPlaybackException {
@@ -186,32 +185,36 @@ public class Cea708TextTrackRenderer extends TrackRenderer implements
}
mSampleHolder.data.clear();
mSampleHolder.size = 0;
- int result = mSource.readData(mTrackIndex, mPresentationTimeUs,
- mFormatHolder, mSampleHolder);
+ int result =
+ mSource.readData(mTrackIndex, mPresentationTimeUs, mFormatHolder, mSampleHolder);
switch (result) {
- case SampleSource.NOTHING_READ: {
- return false;
- }
- case SampleSource.FORMAT_READ: {
- if (DEBUG) {
- Log.i(TAG, "Format was read again");
+ case SampleSource.NOTHING_READ:
+ {
+ return false;
}
- return true;
- }
- case SampleSource.END_OF_STREAM: {
- if (DEBUG) {
- Log.i(TAG, "End of stream from SampleSource");
+ case SampleSource.FORMAT_READ:
+ {
+ if (DEBUG) {
+ Log.i(TAG, "Format was read again");
+ }
+ return true;
}
- mInputStreamEnded = true;
- return false;
- }
- case SampleSource.SAMPLE_READ: {
- mSampleHolder.data.flip();
- if (mCea708Parser != null && !mRenderingDisabled) {
- mCea708Parser.parseClosedCaption(mSampleHolder.data, mSampleHolder.timeUs);
+ case SampleSource.END_OF_STREAM:
+ {
+ if (DEBUG) {
+ Log.i(TAG, "End of stream from SampleSource");
+ }
+ mInputStreamEnded = true;
+ return false;
+ }
+ case SampleSource.SAMPLE_READ:
+ {
+ mSampleHolder.data.flip();
+ if (mCea708Parser != null && !mRenderingDisabled) {
+ mCea708Parser.parseClosedCaption(mSampleHolder.data, mSampleHolder.timeUs);
+ }
+ return true;
}
- return true;
- }
}
return false;
}
diff --git a/src/com/android/tv/tuner/exoplayer/ExoPlayerExtractorsFactory.java b/tuner/src/com/android/tv/tuner/exoplayer/ExoPlayerExtractorsFactory.java
index 0ab6d8c4..dc08c072 100644
--- a/src/com/android/tv/tuner/exoplayer/ExoPlayerExtractorsFactory.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/ExoPlayerExtractorsFactory.java
@@ -15,15 +15,11 @@
*/
package com.android.tv.tuner.exoplayer;
-import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorsFactory;
-import com.google.android.exoplayer2.extractor.TimestampAdjuster;
import com.google.android.exoplayer2.extractor.ts.DefaultTsPayloadReaderFactory;
import com.google.android.exoplayer2.extractor.ts.TsExtractor;
-
-import java.util.ArrayList;
-import java.util.List;
+import com.google.android.exoplayer2.util.TimestampAdjuster;
/**
* Extractor factory, mainly aim at create TsExtractor with FLAG_ALLOW_NON_IDR_KEYFRAMES flags for
@@ -34,8 +30,12 @@ public final class ExoPlayerExtractorsFactory implements ExtractorsFactory {
public Extractor[] createExtractors() {
// Only create TsExtractor since we only target MPEG2TS stream.
Extractor[] extractors = {
- new TsExtractor(new TimestampAdjuster(0), new DefaultTsPayloadReaderFactory(
- DefaultTsPayloadReaderFactory.FLAG_ALLOW_NON_IDR_KEYFRAMES), false) };
+ new TsExtractor(
+ TsExtractor.MODE_SINGLE_PMT,
+ new TimestampAdjuster(0),
+ new DefaultTsPayloadReaderFactory(
+ DefaultTsPayloadReaderFactory.FLAG_ALLOW_NON_IDR_KEYFRAMES))
+ };
return extractors;
}
}
diff --git a/src/com/android/tv/tuner/exoplayer/ExoPlayerSampleExtractor.java b/tuner/src/com/android/tv/tuner/exoplayer/ExoPlayerSampleExtractor.java
index 0b648400..e10a2991 100644
--- a/src/com/android/tv/tuner/exoplayer/ExoPlayerSampleExtractor.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/ExoPlayerSampleExtractor.java
@@ -23,8 +23,13 @@ import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
+import android.support.annotation.VisibleForTesting;
import android.util.Pair;
-
+import com.android.tv.tuner.exoplayer.audio.MpegTsDefaultAudioTrackRenderer;
+import com.android.tv.tuner.exoplayer.buffer.BufferManager;
+import com.android.tv.tuner.exoplayer.buffer.RecordingSampleBuffer;
+import com.android.tv.tuner.exoplayer.buffer.SimpleSampleBuffer;
+import com.android.tv.tuner.tvinput.PlaybackBufferListener;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.MediaFormatHolder;
import com.google.android.exoplayer.SampleHolder;
@@ -44,16 +49,9 @@ import com.google.android.exoplayer2.trackselection.FixedTrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.DefaultAllocator;
-import com.android.tv.tuner.exoplayer.audio.MpegTsDefaultAudioTrackRenderer;
-import com.android.tv.tuner.exoplayer.buffer.BufferManager;
-import com.android.tv.tuner.exoplayer.buffer.RecordingSampleBuffer;
-import com.android.tv.tuner.exoplayer.buffer.SimpleSampleBuffer;
-import com.android.tv.tuner.tvinput.PlaybackBufferListener;
-
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -82,69 +80,113 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
private boolean mVideoTrackMet;
private long mBaseSamplePts = Long.MIN_VALUE;
private HashMap<Integer, Long> mLastExtractedPositionUsMap = new HashMap<>();
- private final List<Pair<Integer, SampleHolder>> mPendingSamples = new LinkedList<>();
+ private final List<Pair<Integer, SampleHolder>> mPendingSamples = new ArrayList<>();
private OnCompletionListener mOnCompletionListener;
private Handler mOnCompletionListenerHandler;
private IOException mError;
- public ExoPlayerSampleExtractor(Uri uri, final DataSource source, BufferManager bufferManager,
- PlaybackBufferListener bufferListener, boolean isRecording) {
+ public ExoPlayerSampleExtractor(
+ Uri uri,
+ final DataSource source,
+ BufferManager bufferManager,
+ PlaybackBufferListener bufferListener,
+ boolean isRecording) {
+ this(
+ uri,
+ source,
+ bufferManager,
+ bufferListener,
+ isRecording,
+ Looper.myLooper(),
+ new HandlerThread("SourceReaderThread"));
+ }
+
+ @VisibleForTesting
+ public ExoPlayerSampleExtractor(
+ Uri uri,
+ DataSource source,
+ BufferManager bufferManager,
+ PlaybackBufferListener bufferListener,
+ boolean isRecording,
+ Looper workerLooper,
+ HandlerThread sourceReaderThread) {
// It'll be used as a timeshift file chunk name's prefix.
mId = System.currentTimeMillis();
- EventListener eventListener = new EventListener() {
- @Override
- public void onLoadError(IOException error) {
- mError = error;
- }
- };
-
- mSourceReaderThread = new HandlerThread("SourceReaderThread");
- mSourceReaderWorker = new SourceReaderWorker(new ExtractorMediaSource(uri,
- new com.google.android.exoplayer2.upstream.DataSource.Factory() {
+ EventListener eventListener =
+ new EventListener() {
@Override
- public com.google.android.exoplayer2.upstream.DataSource createDataSource() {
- // Returns an adapter implementation for ExoPlayer V2 DataSource interface.
- return new com.google.android.exoplayer2.upstream.DataSource() {
- @Override
- public long open(DataSpec dataSpec) throws IOException {
- return source.open(
- new com.google.android.exoplayer.upstream.DataSpec(
- dataSpec.uri, dataSpec.postBody,
- dataSpec.absoluteStreamPosition, dataSpec.position,
- dataSpec.length, dataSpec.key, dataSpec.flags));
- }
-
- @Override
- public int read(byte[] buffer, int offset, int readLength)
- throws IOException {
- return source.read(buffer, offset, readLength);
- }
-
- @Override
- public Uri getUri() {
- return null;
- }
-
- @Override
- public void close() throws IOException {
- source.close();
- }
- };
+ public void onLoadError(IOException error) {
+ mError = error;
}
- },
- new ExoPlayerExtractorsFactory(),
- // Do not create a handler if we not on a looper. e.g. test.
- Looper.myLooper() != null ? new Handler() : null, eventListener));
+ };
+
+ mSourceReaderThread = sourceReaderThread;
+ mSourceReaderWorker =
+ new SourceReaderWorker(
+ new ExtractorMediaSource(
+ uri,
+ new com.google.android.exoplayer2.upstream.DataSource.Factory() {
+ @Override
+ public com.google.android.exoplayer2.upstream.DataSource
+ createDataSource() {
+ // Returns an adapter implementation for ExoPlayer V2
+ // DataSource interface.
+ return new com.google.android.exoplayer2.upstream
+ .DataSource() {
+ @Override
+ public long open(DataSpec dataSpec) throws IOException {
+ return source.open(
+ new com.google.android.exoplayer.upstream
+ .DataSpec(
+ dataSpec.uri,
+ dataSpec.postBody,
+ dataSpec.absoluteStreamPosition,
+ dataSpec.position,
+ dataSpec.length,
+ dataSpec.key,
+ dataSpec.flags));
+ }
+
+ @Override
+ public int read(
+ byte[] buffer, int offset, int readLength)
+ throws IOException {
+ return source.read(buffer, offset, readLength);
+ }
+
+ @Override
+ public Uri getUri() {
+ return null;
+ }
+
+ @Override
+ public void close() throws IOException {
+ source.close();
+ }
+ };
+ }
+ },
+ new ExoPlayerExtractorsFactory(),
+ new Handler(workerLooper),
+ eventListener));
if (isRecording) {
- mSampleBuffer = new RecordingSampleBuffer(bufferManager, bufferListener, false,
- RecordingSampleBuffer.BUFFER_REASON_RECORDING);
+ mSampleBuffer =
+ new RecordingSampleBuffer(
+ bufferManager,
+ bufferListener,
+ false,
+ RecordingSampleBuffer.BUFFER_REASON_RECORDING);
} else {
if (bufferManager == null) {
mSampleBuffer = new SimpleSampleBuffer(bufferListener);
} else {
- mSampleBuffer = new RecordingSampleBuffer(bufferManager, bufferListener, true,
- RecordingSampleBuffer.BUFFER_REASON_LIVE_PLAYBACK);
+ mSampleBuffer =
+ new RecordingSampleBuffer(
+ bufferManager,
+ bufferListener,
+ true,
+ RecordingSampleBuffer.BUFFER_REASON_LIVE_PLAYBACK);
}
}
}
@@ -173,39 +215,64 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
public SourceReaderWorker(MediaSource sampleSource) {
mSampleSource = sampleSource;
- mSampleSource.prepareSource(null, false, new MediaSource.Listener() {
- @Override
- public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
- // Dynamic stream change is not supported yet. b/28169263
- // For now, this will cause EOS and playback reset.
- }
- });
- mDecoderInputBuffer = new DecoderInputBuffer(
- DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);
+ mSampleSource.prepareSource(
+ null,
+ false,
+ new MediaSource.Listener() {
+ @Override
+ public void onSourceInfoRefreshed(
+ MediaSource source, Timeline timeline, Object manifest) {
+ // Dynamic stream change is not supported yet. b/28169263
+ // For now, this will cause EOS and playback reset.
+ }
+ });
+ mDecoderInputBuffer =
+ new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);
mSampleHolder = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_NORMAL);
}
MediaFormat convertFormat(Format format) {
if (format.sampleMimeType.startsWith("audio/")) {
- return MediaFormat.createAudioFormat(format.id, format.sampleMimeType,
- format.bitrate, format.maxInputSize,
- com.google.android.exoplayer.C.UNKNOWN_TIME_US, format.channelCount,
- format.sampleRate, format.initializationData, format.language,
+ return MediaFormat.createAudioFormat(
+ format.id,
+ format.sampleMimeType,
+ format.bitrate,
+ format.maxInputSize,
+ com.google.android.exoplayer.C.UNKNOWN_TIME_US,
+ format.channelCount,
+ format.sampleRate,
+ format.initializationData,
+ format.language,
format.pcmEncoding);
} else if (format.sampleMimeType.startsWith("video/")) {
return MediaFormat.createVideoFormat(
- format.id, format.sampleMimeType, format.bitrate, format.maxInputSize,
- com.google.android.exoplayer.C.UNKNOWN_TIME_US, format.width, format.height,
- format.initializationData, format.rotationDegrees,
- format.pixelWidthHeightRatio, format.projectionData, format.stereoMode);
+ format.id,
+ format.sampleMimeType,
+ format.bitrate,
+ format.maxInputSize,
+ com.google.android.exoplayer.C.UNKNOWN_TIME_US,
+ format.width,
+ format.height,
+ format.initializationData,
+ format.rotationDegrees,
+ format.pixelWidthHeightRatio,
+ format.projectionData,
+ format.stereoMode,
+ null // colorInfo
+ );
} else if (format.sampleMimeType.endsWith("/cea-608")
|| format.sampleMimeType.startsWith("text/")) {
return MediaFormat.createTextFormat(
- format.id, format.sampleMimeType, format.bitrate,
- com.google.android.exoplayer.C.UNKNOWN_TIME_US, format.language);
+ format.id,
+ format.sampleMimeType,
+ format.bitrate,
+ com.google.android.exoplayer.C.UNKNOWN_TIME_US,
+ format.language);
} else {
return MediaFormat.createFormatForMimeType(
- format.id, format.sampleMimeType, format.bitrate,
+ format.id,
+ format.sampleMimeType,
+ format.bitrate,
com.google.android.exoplayer.C.UNKNOWN_TIME_US);
}
}
@@ -222,8 +289,8 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
for (int i = 0; i < selections.length; ++i) {
selections[i] = selectionFactory.createTrackSelection(trackGroupArray.get(i), 0);
}
- boolean retain[] = new boolean[trackGroupArray.length];
- boolean reset[] = new boolean[trackGroupArray.length];
+ boolean[] retain = new boolean[trackGroupArray.length];
+ boolean[] reset = new boolean[trackGroupArray.length];
mStreams = new SampleStream[trackGroupArray.length];
mMediaPeriod.selectTracks(selections, retain, mStreams, reset, 0);
if (mTrackFormats == null) {
@@ -273,9 +340,11 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
case MSG_PREPARE:
if (!mPrepareRequested) {
mPrepareRequested = true;
- mMediaPeriod = mSampleSource.createPeriod(0,
- new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE), 0);
- mMediaPeriod.prepare(this);
+ mMediaPeriod =
+ mSampleSource.createPeriod(
+ new MediaSource.MediaPeriodId(0),
+ new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE));
+ mMediaPeriod.prepare(this, 0);
try {
mMediaPeriod.maybeThrowPrepareError();
} catch (IOException e) {
@@ -288,8 +357,8 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
ConditionVariable conditionVariable = new ConditionVariable();
int trackCount = mStreams.length;
for (int i = 0; i < trackCount; ++i) {
- if (!mTrackMetEos[i] && C.RESULT_NOTHING_READ
- != fetchSample(i, mSampleHolder, conditionVariable)) {
+ if (!mTrackMetEos[i]
+ && C.RESULT_NOTHING_READ != fetchSample(i, conditionVariable)) {
if (mMetEos) {
// If mMetEos was on during fetchSample() due to an error,
// fetching from other tracks is not necessary.
@@ -303,8 +372,8 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
if (didSomething) {
mSourceReaderHandler.sendEmptyMessage(MSG_FETCH_SAMPLES);
} else {
- mSourceReaderHandler.sendEmptyMessageDelayed(MSG_FETCH_SAMPLES,
- RETRY_INTERVAL_MS);
+ mSourceReaderHandler.sendEmptyMessageDelayed(
+ MSG_FETCH_SAMPLES, RETRY_INTERVAL_MS);
}
} else {
notifyCompletionIfNeeded(false);
@@ -319,27 +388,31 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
cleanUp();
mSourceReaderHandler.removeCallbacksAndMessages(null);
return true;
+ default: // fall out
}
return false;
}
- private int fetchSample(int track, SampleHolder sample,
- ConditionVariable conditionVariable) {
+ private int fetchSample(int track, ConditionVariable conditionVariable) {
FormatHolder dummyFormatHolder = new FormatHolder();
mDecoderInputBuffer.clear();
- int ret = mStreams[track].readData(dummyFormatHolder, mDecoderInputBuffer);
+ int ret = mStreams[track].readData(dummyFormatHolder, mDecoderInputBuffer, false);
if (ret == C.RESULT_BUFFER_READ
// Double-check if the extractor provided the data to prevent NPE. b/33758354
&& mDecoderInputBuffer.data != null) {
if (mCurrentPosition < mDecoderInputBuffer.timeUs) {
mCurrentPosition = mDecoderInputBuffer.timeUs;
}
+ if (mMediaPeriod != null) {
+ mMediaPeriod.discardBuffer(mCurrentPosition, false);
+ }
try {
Long lastExtractedPositionUs = mLastExtractedPositionUsMap.get(track);
if (lastExtractedPositionUs == null) {
mLastExtractedPositionUsMap.put(track, mDecoderInputBuffer.timeUs);
} else {
- mLastExtractedPositionUsMap.put(track,
+ mLastExtractedPositionUsMap.put(
+ track,
Math.max(lastExtractedPositionUs, mDecoderInputBuffer.timeUs));
}
queueSample(track, conditionVariable);
@@ -373,10 +446,15 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_NORMAL);
mSampleHolder.flags =
(mDecoderInputBuffer.isKeyFrame()
- ? com.google.android.exoplayer.C.SAMPLE_FLAG_SYNC : 0)
- | (mDecoderInputBuffer.isDecodeOnly()
- ? com.google.android.exoplayer.C.SAMPLE_FLAG_DECODE_ONLY
- : 0);
+ ? com.google.android.exoplayer.C.SAMPLE_FLAG_SYNC
+ : 0)
+ | (mDecoderInputBuffer.isDecodeOnly()
+ ? com.google
+ .android
+ .exoplayer
+ .C
+ .SAMPLE_FLAG_DECODE_ONLY
+ : 0);
sample.timeUs = mDecoderInputBuffer.timeUs;
sample.size = mDecoderInputBuffer.data.position();
sample.ensureSpaceForWrite(sample.size);
@@ -399,8 +477,7 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
}
mPendingSamples.clear();
} else {
- if (mDecoderInputBuffer.timeUs < mBaseSamplePts
- && mVideoTrackIndex != index) {
+ if (mDecoderInputBuffer.timeUs < mBaseSamplePts && mVideoTrackIndex != index) {
return;
}
}
@@ -409,9 +486,11 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
mSampleHolder.clearData();
mSampleHolder.flags =
(mDecoderInputBuffer.isKeyFrame()
- ? com.google.android.exoplayer.C.SAMPLE_FLAG_SYNC : 0)
- | (mDecoderInputBuffer.isDecodeOnly()
- ? com.google.android.exoplayer.C.SAMPLE_FLAG_DECODE_ONLY : 0);
+ ? com.google.android.exoplayer.C.SAMPLE_FLAG_SYNC
+ : 0)
+ | (mDecoderInputBuffer.isDecodeOnly()
+ ? com.google.android.exoplayer.C.SAMPLE_FLAG_DECODE_ONLY
+ : 0);
mSampleHolder.timeUs = mDecoderInputBuffer.timeUs;
mSampleHolder.size = mDecoderInputBuffer.data.position();
mSampleHolder.ensureSpaceForWrite(mSampleHolder.size);
@@ -423,8 +502,8 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
mSampleBuffer.writeSample(index, mSampleHolder, conditionVariable);
// Checks whether the storage has enough bandwidth for recording samples.
- if (mSampleBuffer.isWriteSpeedSlow(mSampleHolder.size,
- SystemClock.elapsedRealtimeNanos() - writeStartTimeNs)) {
+ if (mSampleBuffer.isWriteSpeedSlow(
+ mSampleHolder.size, SystemClock.elapsedRealtimeNanos() - writeStartTimeNs)) {
mSampleBuffer.handleWriteSpeedSlow();
}
}
@@ -443,8 +522,8 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
public boolean prepare() throws IOException {
if (!mSourceReaderThread.isAlive()) {
mSourceReaderThread.start();
- mSourceReaderHandler = new Handler(mSourceReaderThread.getLooper(),
- mSourceReaderWorker);
+ mSourceReaderHandler =
+ new Handler(mSourceReaderThread.getLooper(), mSourceReaderWorker);
mSourceReaderHandler.sendEmptyMessage(SourceReaderWorker.MSG_PREPARE);
}
if (mExceptionOnPrepare != null) {
@@ -527,12 +606,13 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
final OnCompletionListener listener = mOnCompletionListener;
final long lastExtractedPositionUs = getLastExtractedPositionUs();
if (mOnCompletionListenerHandler != null && mOnCompletionListener != null) {
- mOnCompletionListenerHandler.post(new Runnable() {
- @Override
- public void run() {
- listener.onCompletion(result, lastExtractedPositionUs);
- }
- });
+ mOnCompletionListenerHandler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ listener.onCompletion(result, lastExtractedPositionUs);
+ }
+ });
}
}
}
diff --git a/src/com/android/tv/tuner/exoplayer/FileSampleExtractor.java b/tuner/src/com/android/tv/tuner/exoplayer/FileSampleExtractor.java
index b7e42a7c..e7224422 100644
--- a/src/com/android/tv/tuner/exoplayer/FileSampleExtractor.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/FileSampleExtractor.java
@@ -16,25 +16,23 @@
package com.android.tv.tuner.exoplayer;
+import android.os.Handler;
+import com.android.tv.tuner.exoplayer.buffer.BufferManager;
+import com.android.tv.tuner.exoplayer.buffer.RecordingSampleBuffer;
+import com.android.tv.tuner.tvinput.PlaybackBufferListener;
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.android.tv.tuner.exoplayer.buffer.BufferManager;
-import com.android.tv.tuner.exoplayer.buffer.RecordingSampleBuffer;
-import com.android.tv.tuner.tvinput.PlaybackBufferListener;
-
-import android.os.Handler;
-
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
- * A class that plays a recorded stream without using {@link android.media.MediaExtractor},
- * since all samples are extracted and stored to the permanent storage already.
+ * A class that plays a recorded stream without using {@link android.media.MediaExtractor}, since
+ * all samples are extracted and stored to the permanent storage already.
*/
-public class FileSampleExtractor implements SampleExtractor{
+public class FileSampleExtractor implements SampleExtractor {
private static final String TAG = "FileSampleExtractor";
private static final boolean DEBUG = false;
@@ -46,8 +44,7 @@ public class FileSampleExtractor implements SampleExtractor{
private final PlaybackBufferListener mBufferListener;
private BufferManager.SampleBuffer mSampleBuffer;
- public FileSampleExtractor(
- BufferManager bufferManager, PlaybackBufferListener bufferListener) {
+ public FileSampleExtractor(BufferManager bufferManager, PlaybackBufferListener bufferListener) {
mBufferManager = bufferManager;
mBufferListener = bufferListener;
mTrackCount = -1;
@@ -72,8 +69,12 @@ public class FileSampleExtractor implements SampleExtractor{
ids.add(trackFormat.trackId);
mTrackFormats.add(MediaFormatUtil.createMediaFormat(trackFormat.format));
}
- mSampleBuffer = new RecordingSampleBuffer(mBufferManager, mBufferListener, true,
- RecordingSampleBuffer.BUFFER_REASON_RECORDED_PLAYBACK);
+ mSampleBuffer =
+ new RecordingSampleBuffer(
+ mBufferManager,
+ mBufferListener,
+ true,
+ RecordingSampleBuffer.BUFFER_REASON_RECORDED_PLAYBACK);
mSampleBuffer.init(ids, mTrackFormats);
return true;
}
@@ -134,5 +135,5 @@ public class FileSampleExtractor implements SampleExtractor{
}
@Override
- public void setOnCompletionListener(OnCompletionListener listener, Handler handler) { }
+ public void setOnCompletionListener(OnCompletionListener listener, Handler handler) {}
}
diff --git a/src/com/android/tv/tuner/exoplayer/MpegTsPlayer.java b/tuner/src/com/android/tv/tuner/exoplayer/MpegTsPlayer.java
index 2694298a..a49cbfaf 100644
--- a/src/com/android/tv/tuner/exoplayer/MpegTsPlayer.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/MpegTsPlayer.java
@@ -23,7 +23,16 @@ import android.media.PlaybackParams;
import android.os.Handler;
import android.support.annotation.IntDef;
import android.view.Surface;
-
+import com.android.tv.common.SoftPreconditions;
+import com.android.tv.tuner.data.Cea708Data;
+import com.android.tv.tuner.data.Cea708Data.CaptionEvent;
+import com.android.tv.tuner.data.TunerChannel;
+import com.android.tv.tuner.exoplayer.audio.MpegTsDefaultAudioTrackRenderer;
+import com.android.tv.tuner.exoplayer.audio.MpegTsMediaCodecAudioTrackRenderer;
+import com.android.tv.tuner.source.TsDataSource;
+import com.android.tv.tuner.source.TsDataSourceManager;
+import com.android.tv.tuner.tvinput.EventDetector;
+import com.android.tv.tuner.tvinput.TunerDebug;
import com.google.android.exoplayer.DummyTrackRenderer;
import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.ExoPlayer;
@@ -35,17 +44,6 @@ 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.upstream.DataSource;
-import com.android.tv.common.SoftPreconditions;
-import com.android.tv.tuner.data.Cea708Data;
-import com.android.tv.tuner.data.Cea708Data.CaptionEvent;
-import com.android.tv.tuner.data.TunerChannel;
-import com.android.tv.tuner.exoplayer.audio.MpegTsDefaultAudioTrackRenderer;
-import com.android.tv.tuner.exoplayer.audio.MpegTsMediaCodecAudioTrackRenderer;
-import com.android.tv.tuner.source.TsDataSource;
-import com.android.tv.tuner.source.TsDataSourceManager;
-import com.android.tv.tuner.tvinput.EventDetector;
-import com.android.tv.tuner.tvinput.TunerDebug;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -57,52 +55,46 @@ public class MpegTsPlayer
MpegTsMediaCodecAudioTrackRenderer.Ac3EventListener {
private int mCaptionServiceNumber = Cea708Data.EMPTY_SERVICE_NUMBER;
- /**
- * Interface definition for building specific track renderers.
- */
+ /** Interface definition for building specific track renderers. */
public interface RendererBuilder {
- void buildRenderers(MpegTsPlayer mpegTsPlayer, DataSource dataSource,
- boolean hasSoftwareAudioDecoder, RendererBuilderCallback callback);
+ void buildRenderers(
+ MpegTsPlayer mpegTsPlayer,
+ DataSource dataSource,
+ boolean hasSoftwareAudioDecoder,
+ RendererBuilderCallback callback);
}
- /**
- * Interface definition for {@link RendererBuilder#buildRenderers} to notify the result.
- */
+ /** Interface definition for {@link RendererBuilder#buildRenderers} to notify the result. */
public interface RendererBuilderCallback {
void onRenderers(String[][] trackNames, TrackRenderer[] renderers);
+
void onRenderersError(Exception e);
}
- /**
- * Interface definition for a callback to be notified of changes in player state.
- */
+ /** Interface definition for a callback to be notified of changes in player state. */
public interface Listener {
void onStateChanged(boolean playWhenReady, int playbackState);
+
void onError(Exception e);
- void onVideoSizeChanged(int width, int height,
- float pixelWidthHeightRatio);
+
+ void onVideoSizeChanged(int width, int height, float pixelWidthHeightRatio);
+
void onDrawnToSurface(MpegTsPlayer player, Surface surface);
+
void onAudioUnplayable();
+
void onSmoothTrickplayForceStopped();
}
- /**
- * Interface definition for a callback to be notified of changes on video display.
- */
+ /** Interface definition for a callback to be notified of changes on video display. */
public interface VideoEventListener {
- /**
- * Notifies the caption event.
- */
+ /** Notifies the caption event. */
void onEmitCaptionEvent(CaptionEvent event);
- /**
- * Notifies clearing up whole closed caption event.
- */
+ /** Notifies clearing up whole closed caption event. */
void onClearCaptionEvent();
- /**
- * Notifies the discovered caption service number.
- */
+ /** Notifies the discovered caption service number. */
void onDiscoverCaptionServiceNumber(int serviceNumber);
}
@@ -113,14 +105,19 @@ public class MpegTsPlayer
@IntDef({TRACK_TYPE_VIDEO, TRACK_TYPE_AUDIO, TRACK_TYPE_TEXT})
@Retention(RetentionPolicy.SOURCE)
public @interface TrackType {}
+
public static final int TRACK_TYPE_VIDEO = 0;
public static final int TRACK_TYPE_AUDIO = 1;
public static final int TRACK_TYPE_TEXT = 2;
- @IntDef({RENDERER_BUILDING_STATE_IDLE, RENDERER_BUILDING_STATE_BUILDING,
- RENDERER_BUILDING_STATE_BUILT})
+ @IntDef({
+ RENDERER_BUILDING_STATE_IDLE,
+ RENDERER_BUILDING_STATE_BUILDING,
+ RENDERER_BUILDING_STATE_BUILT
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface RendererBuildingState {}
+
private static final int RENDERER_BUILDING_STATE_IDLE = 1;
private static final int RENDERER_BUILDING_STATE_BUILDING = 2;
private static final int RENDERER_BUILDING_STATE_BUILT = 3;
@@ -157,8 +154,11 @@ public class MpegTsPlayer
* @param capabilities the {@link AudioCapabilities} of the current device
* @param listener the listener for playback state changes
*/
- public MpegTsPlayer(RendererBuilder rendererBuilder, Handler handler,
- TsDataSourceManager sourceManager, AudioCapabilities capabilities,
+ public MpegTsPlayer(
+ RendererBuilder rendererBuilder,
+ Handler handler,
+ TsDataSourceManager sourceManager,
+ AudioCapabilities capabilities,
Listener listener) {
mRendererBuilder = rendererBuilder;
mPlayer = ExoPlayer.Factory.newInstance(RENDERER_COUNT, MIN_BUFFER_MS, MIN_REBUFFER_MS);
@@ -188,8 +188,10 @@ public class MpegTsPlayer
public void setCaptionServiceNumber(int captionServiceNumber) {
mCaptionServiceNumber = captionServiceNumber;
if (mTextRenderer != null) {
- mPlayer.sendMessage(mTextRenderer,
- Cea708TextTrackRenderer.MSG_SERVICE_NUMBER, mCaptionServiceNumber);
+ mPlayer.sendMessage(
+ mTextRenderer,
+ Cea708TextTrackRenderer.MSG_SERVICE_NUMBER,
+ mCaptionServiceNumber);
}
}
@@ -203,16 +205,12 @@ public class MpegTsPlayer
pushSurface(false);
}
- /**
- * Returns the current surface of the player.
- */
+ /** Returns the current surface of the player. */
public Surface getSurface() {
return mSurface;
}
- /**
- * Clears the surface and waits until the surface is being cleaned.
- */
+ /** Clears the surface and waits until the surface is being cleaned. */
public void blockingClearSurface() {
mSurface = null;
pushSurface(true);
@@ -220,13 +218,17 @@ public class MpegTsPlayer
/**
* Creates renderers and {@link DataSource} and initializes player.
+ *
* @param context a {@link Context} instance
* @param channel to play
* @param hasSoftwareAudioDecoder {@code true} if there is connected software decoder
* @param eventListener for program information which will be scanned from MPEG2-TS stream
* @return true when everything is created and initialized well, false otherwise
*/
- public boolean prepare(Context context, TunerChannel channel, boolean hasSoftwareAudioDecoder,
+ public boolean prepare(
+ Context context,
+ TunerChannel channel,
+ boolean hasSoftwareAudioDecoder,
EventDetector.EventListener eventListener) {
TsDataSource source = null;
if (channel != null) {
@@ -248,9 +250,7 @@ public class MpegTsPlayer
return true;
}
- /**
- * Returns {@link TsDataSource} which provides MPEG2-TS stream.
- */
+ /** Returns {@link TsDataSource} which provides MPEG2-TS stream. */
public TsDataSource getDataSource() {
return mDataSource;
}
@@ -288,18 +288,15 @@ public class MpegTsPlayer
/**
* Sets the player state to pause or play.
*
- * @param playWhenReady sets the player state to being ready to play when {@code true},
- * sets the player state to being paused when {@code false}
- *
+ * @param playWhenReady sets the player state to being ready to play when {@code true}, sets the
+ * player state to being paused when {@code false}
*/
public void setPlayWhenReady(boolean playWhenReady) {
mPlayer.setPlayWhenReady(playWhenReady);
stopSmoothTrickplay(false);
}
- /**
- * Returns true, if trickplay is supported.
- */
+ /** Returns true, if trickplay is supported. */
public boolean supportSmoothTrickPlay(float playbackSpeed) {
return playbackSpeed > MIN_SMOOTH_TRICKPLAY_SPEED
&& playbackSpeed < MAX_SMOOTH_TRICKPLAY_SPEED;
@@ -318,7 +315,8 @@ public class MpegTsPlayer
MpegTsDefaultAudioTrackRenderer.MSG_SET_PLAYBACK_SPEED,
playbackParams.getSpeed());
} else {
- mPlayer.sendMessage(mAudioRenderer,
+ mPlayer.sendMessage(
+ mAudioRenderer,
MediaCodecAudioTrackRenderer.MSG_SET_PLAYBACK_PARAMS,
playbackParams);
}
@@ -329,10 +327,12 @@ public class MpegTsPlayer
mTrickplayRunning = false;
if (mAudioRenderer instanceof MpegTsDefaultAudioTrackRenderer) {
mPlayer.sendMessage(
- mAudioRenderer, MpegTsDefaultAudioTrackRenderer.MSG_SET_PLAYBACK_SPEED,
+ mAudioRenderer,
+ MpegTsDefaultAudioTrackRenderer.MSG_SET_PLAYBACK_SPEED,
1.0f);
} else {
- mPlayer.sendMessage(mAudioRenderer,
+ mPlayer.sendMessage(
+ mAudioRenderer,
MediaCodecAudioTrackRenderer.MSG_SET_PLAYBACK_PARAMS,
new PlaybackParams().setSpeed(1.0f));
}
@@ -352,9 +352,7 @@ public class MpegTsPlayer
stopSmoothTrickplay(true);
}
- /**
- * Releases the player.
- */
+ /** Releases the player. */
public void release() {
if (mDataSource != null) {
mSourceManager.releaseDataSource(mDataSource);
@@ -370,57 +368,45 @@ public class MpegTsPlayer
mPlayer.release();
}
- /**
- * Returns the current status of the player.
- */
- public int getPlaybackState() {
+ /** Returns the current status of the player. */
+ public int getPlaybackState() {
if (mRendererBuildingState == RENDERER_BUILDING_STATE_BUILDING) {
return ExoPlayer.STATE_PREPARING;
}
return mPlayer.getPlaybackState();
}
- /**
- * Returns {@code true} when the player is prepared to play, {@code false} otherwise.
- */
- public boolean isPrepared() {
+ /** Returns {@code true} when the player is prepared to play, {@code false} otherwise. */
+ public boolean isPrepared() {
int state = getPlaybackState();
return state == ExoPlayer.STATE_READY || state == ExoPlayer.STATE_BUFFERING;
}
- /**
- * Returns {@code true} when the player is being ready to play, {@code false} otherwise.
- */
+ /** Returns {@code true} when the player is being ready to play, {@code false} otherwise. */
public boolean isPlaying() {
int state = getPlaybackState();
return (state == ExoPlayer.STATE_READY || state == ExoPlayer.STATE_BUFFERING)
&& mPlayer.getPlayWhenReady();
}
- /**
- * Returns {@code true} when the player is buffering, {@code false} otherwise.
- */
+ /** Returns {@code true} when the player is buffering, {@code false} otherwise. */
public boolean isBuffering() {
return getPlaybackState() == ExoPlayer.STATE_BUFFERING;
}
- /**
- * Returns the current position of the playback in milli seconds.
- */
+ /** Returns the current position of the playback in milli seconds. */
public long getCurrentPosition() {
return mPlayer.getCurrentPosition();
}
- /**
- * Returns the total duration of the playback.
- */
+ /** Returns the total duration of the playback. */
public long getDuration() {
return mPlayer.getDuration();
}
/**
- * Returns {@code true} when the player is being ready to play,
- * {@code false} when the player is paused.
+ * Returns {@code true} when the player is being ready to play, {@code false} when the player is
+ * paused.
*/
public boolean getPlayWhenReady() {
return mPlayer.getPlayWhenReady();
@@ -434,11 +420,11 @@ public class MpegTsPlayer
public void setVolume(float volume) {
mVolume = volume;
if (mAudioRenderer instanceof MpegTsDefaultAudioTrackRenderer) {
- mPlayer.sendMessage(mAudioRenderer, MpegTsDefaultAudioTrackRenderer.MSG_SET_VOLUME,
- volume);
+ mPlayer.sendMessage(
+ mAudioRenderer, MpegTsDefaultAudioTrackRenderer.MSG_SET_VOLUME, volume);
} else {
- mPlayer.sendMessage(mAudioRenderer, MediaCodecAudioTrackRenderer.MSG_SET_VOLUME,
- volume);
+ mPlayer.sendMessage(
+ mAudioRenderer, MediaCodecAudioTrackRenderer.MSG_SET_VOLUME, volume);
}
}
@@ -449,57 +435,49 @@ public class MpegTsPlayer
*/
public void setAudioTrackAndClosedCaption(boolean enable) {
if (mAudioRenderer instanceof MpegTsDefaultAudioTrackRenderer) {
- mPlayer.sendMessage(mAudioRenderer, MpegTsDefaultAudioTrackRenderer.MSG_SET_AUDIO_TRACK,
+ mPlayer.sendMessage(
+ mAudioRenderer,
+ MpegTsDefaultAudioTrackRenderer.MSG_SET_AUDIO_TRACK,
enable ? 1 : 0);
} else {
- mPlayer.sendMessage(mAudioRenderer, MediaCodecAudioTrackRenderer.MSG_SET_VOLUME,
+ mPlayer.sendMessage(
+ mAudioRenderer,
+ MediaCodecAudioTrackRenderer.MSG_SET_VOLUME,
enable ? mVolume : 0.0f);
}
- mPlayer.sendMessage(mTextRenderer, Cea708TextTrackRenderer.MSG_ENABLE_CLOSED_CAPTION,
- enable);
+ mPlayer.sendMessage(
+ mTextRenderer, Cea708TextTrackRenderer.MSG_ENABLE_CLOSED_CAPTION, enable);
}
- /**
- * Returns {@code true} when AC3 audio can be played, {@code false} otherwise.
- */
+ /** Returns {@code true} when AC3 audio can be played, {@code false} otherwise. */
public boolean isAc3Playable() {
return mAudioCapabilities != null
&& mAudioCapabilities.supportsEncoding(AudioFormat.ENCODING_AC3);
}
- /**
- * Notifies when the audio cannot be played by the current device.
- */
+ /** Notifies when the audio cannot be played by the current device. */
public void onAudioUnplayable() {
if (mListener != null) {
mListener.onAudioUnplayable();
}
}
- /**
- * Returns {@code true} if the player has any video track, {@code false} otherwise.
- */
+ /** Returns {@code true} if the player has any video track, {@code false} otherwise. */
public boolean hasVideo() {
return mPlayer.getTrackCount(TRACK_TYPE_VIDEO) > 0;
}
- /**
- * Returns {@code true} if the player has any audio trock, {@code false} otherwise.
- */
+ /** Returns {@code true} if the player has any audio trock, {@code false} otherwise. */
public boolean hasAudio() {
return mPlayer.getTrackCount(TRACK_TYPE_AUDIO) > 0;
}
- /**
- * Returns the number of tracks exposed by the specified renderer.
- */
+ /** Returns the number of tracks exposed by the specified renderer. */
public int getTrackCount(int rendererIndex) {
return mPlayer.getTrackCount(rendererIndex);
}
- /**
- * Selects a track for the specified renderer.
- */
+ /** Selects a track for the specified renderer. */
public void setSelectedTrack(int rendererIndex, int trackIndex) {
if (trackIndex >= getTrackCount(rendererIndex)) {
return;
@@ -511,8 +489,8 @@ public class MpegTsPlayer
* Returns the index of the currently selected track for the specified renderer.
*
* @param rendererIndex The index of the renderer.
- * @return The selected track. A negative value or a value greater than or equal to the renderer's
- * track count indicates that the renderer is disabled.
+ * @return The selected track. A negative value or a value greater than or equal to the
+ * renderer's track count indicates that the renderer is disabled.
*/
public int getSelectedTrack(int rendererIndex) {
return mPlayer.getSelectedTrack(rendererIndex);
@@ -529,9 +507,7 @@ public class MpegTsPlayer
return mPlayer.getTrackFormat(rendererIndex, trackIndex);
}
- /**
- * Gets the main handler of the player.
- */
+ /** Gets the main handler of the player. */
/* package */ Handler getMainHandler() {
return mMainHandler;
}
@@ -542,11 +518,11 @@ public class MpegTsPlayer
return;
}
mListener.onStateChanged(playWhenReady, state);
- if (state == ExoPlayer.STATE_READY && mPlayer.getTrackCount(TRACK_TYPE_VIDEO) > 0
+ if (state == ExoPlayer.STATE_READY
+ && mPlayer.getTrackCount(TRACK_TYPE_VIDEO) > 0
&& playWhenReady) {
MediaFormat format = mPlayer.getTrackFormat(TRACK_TYPE_VIDEO, 0);
- mListener.onVideoSizeChanged(format.width,
- format.height, format.pixelWidthHeightRatio);
+ mListener.onVideoSizeChanged(format.width, format.height, format.pixelWidthHeightRatio);
}
}
@@ -559,16 +535,16 @@ public class MpegTsPlayer
}
@Override
- public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
- float pixelWidthHeightRatio) {
+ public void onVideoSizeChanged(
+ int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
if (mListener != null) {
mListener.onVideoSizeChanged(width, height, pixelWidthHeightRatio);
}
}
@Override
- public void onDecoderInitialized(String decoderName, long elapsedRealtimeMs,
- long initializationDurationMs) {
+ public void onDecoderInitialized(
+ String decoderName, long elapsedRealtimeMs, long initializationDurationMs) {
// Do nothing.
}
@@ -590,8 +566,8 @@ public class MpegTsPlayer
}
@Override
- public void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs,
- long elapsedSinceLastFeedMs) {
+ public void onAudioTrackUnderrun(
+ int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {
// Do nothing.
}
@@ -693,4 +669,4 @@ public class MpegTsPlayer
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/tuner/exoplayer/MpegTsRendererBuilder.java b/tuner/src/com/android/tv/tuner/exoplayer/MpegTsRendererBuilder.java
index 006ccac2..774285e9 100644
--- a/src/com/android/tv/tuner/exoplayer/MpegTsRendererBuilder.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/MpegTsRendererBuilder.java
@@ -17,43 +17,45 @@
package com.android.tv.tuner.exoplayer;
import android.content.Context;
-
-import com.google.android.exoplayer.MediaCodecSelector;
-import com.google.android.exoplayer.SampleSource;
-import com.google.android.exoplayer.TrackRenderer;
-import com.google.android.exoplayer.upstream.DataSource;
-import com.android.tv.Features;
+import com.android.tv.tuner.TunerFeatures;
import com.android.tv.tuner.exoplayer.MpegTsPlayer.RendererBuilder;
import com.android.tv.tuner.exoplayer.MpegTsPlayer.RendererBuilderCallback;
import com.android.tv.tuner.exoplayer.audio.MpegTsDefaultAudioTrackRenderer;
import com.android.tv.tuner.exoplayer.buffer.BufferManager;
import com.android.tv.tuner.tvinput.PlaybackBufferListener;
+import com.google.android.exoplayer.MediaCodecSelector;
+import com.google.android.exoplayer.SampleSource;
+import com.google.android.exoplayer.TrackRenderer;
+import com.google.android.exoplayer.upstream.DataSource;
-/**
- * Builder for renderer objects for {@link MpegTsPlayer}.
- */
+/** Builder for renderer objects for {@link MpegTsPlayer}. */
public class MpegTsRendererBuilder implements RendererBuilder {
private final Context mContext;
private final BufferManager mBufferManager;
private final PlaybackBufferListener mBufferListener;
- public MpegTsRendererBuilder(Context context, BufferManager bufferManager,
- PlaybackBufferListener bufferListener) {
+ public MpegTsRendererBuilder(
+ Context context, BufferManager bufferManager, PlaybackBufferListener bufferListener) {
mContext = context;
mBufferManager = bufferManager;
mBufferListener = bufferListener;
}
@Override
- public void buildRenderers(MpegTsPlayer mpegTsPlayer, DataSource dataSource,
- boolean mHasSoftwareAudioDecoder, RendererBuilderCallback callback) {
+ public void buildRenderers(
+ MpegTsPlayer mpegTsPlayer,
+ DataSource dataSource,
+ boolean mHasSoftwareAudioDecoder,
+ RendererBuilderCallback callback) {
// Build the video and audio renderers.
- SampleExtractor extractor = dataSource == null ?
- new MpegTsSampleExtractor(mBufferManager, mBufferListener) :
- new MpegTsSampleExtractor(dataSource, mBufferManager, mBufferListener);
+ SampleExtractor extractor =
+ dataSource == null
+ ? new MpegTsSampleExtractor(mBufferManager, mBufferListener)
+ : new MpegTsSampleExtractor(dataSource, mBufferManager, mBufferListener);
SampleSource sampleSource = new MpegTsSampleSource(extractor);
- MpegTsVideoTrackRenderer videoRenderer = new MpegTsVideoTrackRenderer(mContext,
- sampleSource, mpegTsPlayer.getMainHandler(), mpegTsPlayer);
+ MpegTsVideoTrackRenderer videoRenderer =
+ new MpegTsVideoTrackRenderer(
+ mContext, sampleSource, mpegTsPlayer.getMainHandler(), mpegTsPlayer);
// TODO: Only using MpegTsDefaultAudioTrackRenderer for A/V sync issue. We will use
// {@link MpegTsMediaCodecAudioTrackRenderer} when we use ExoPlayer's extractor.
TrackRenderer audioRenderer =
@@ -63,7 +65,7 @@ public class MpegTsRendererBuilder implements RendererBuilder {
mpegTsPlayer.getMainHandler(),
mpegTsPlayer,
mHasSoftwareAudioDecoder,
- !Features.AC3_SOFTWARE_DECODE.isEnabled(mContext));
+ !TunerFeatures.AC3_SOFTWARE_DECODE.isEnabled(mContext));
Cea708TextTrackRenderer textRenderer = new Cea708TextTrackRenderer(sampleSource);
TrackRenderer[] renderers = new TrackRenderer[MpegTsPlayer.RENDERER_COUNT];
diff --git a/src/com/android/tv/tuner/exoplayer/MpegTsSampleExtractor.java b/tuner/src/com/android/tv/tuner/exoplayer/MpegTsSampleExtractor.java
index 7bf116c8..593b576e 100644
--- a/src/com/android/tv/tuner/exoplayer/MpegTsSampleExtractor.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/MpegTsSampleExtractor.java
@@ -18,26 +18,22 @@ package com.android.tv.tuner.exoplayer;
import android.net.Uri;
import android.os.Handler;
-
+import com.android.tv.tuner.exoplayer.buffer.BufferManager;
+import com.android.tv.tuner.exoplayer.buffer.SamplePool;
+import com.android.tv.tuner.tvinput.PlaybackBufferListener;
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.upstream.DataSource;
import com.google.android.exoplayer.util.MimeTypes;
-import com.android.tv.tuner.exoplayer.buffer.BufferManager;
-import com.android.tv.tuner.exoplayer.buffer.SamplePool;
-import com.android.tv.tuner.tvinput.PlaybackBufferListener;
-
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
-/**
- * Extracts samples from {@link DataSource} for MPEG-TS streams.
- */
+/** Extracts samples from {@link DataSource} for MPEG-TS streams. */
public final class MpegTsSampleExtractor implements SampleExtractor {
public static final String MIMETYPE_TEXT_CEA_708 = "text/cea-708";
@@ -66,13 +62,14 @@ public final class MpegTsSampleExtractor implements SampleExtractor {
*
* @param source the {@link DataSource} to extract from
* @param bufferManager the manager for reading & writing samples backed by physical storage
- * @param bufferListener the {@link PlaybackBufferListener}
- * to notify buffer storage status change
+ * @param bufferListener the {@link PlaybackBufferListener} to notify buffer storage status
+ * change
*/
- public MpegTsSampleExtractor(DataSource source, BufferManager bufferManager,
- PlaybackBufferListener bufferListener) {
- mSampleExtractor = new ExoPlayerSampleExtractor(Uri.EMPTY, source, bufferManager,
- bufferListener, false);
+ public MpegTsSampleExtractor(
+ DataSource source, BufferManager bufferManager, PlaybackBufferListener bufferListener) {
+ mSampleExtractor =
+ new ExoPlayerSampleExtractor(
+ Uri.EMPTY, source, bufferManager, bufferListener, false);
init();
}
@@ -80,11 +77,11 @@ public final class MpegTsSampleExtractor implements SampleExtractor {
* Creates MpegTsSampleExtractor for a recorded program.
*
* @param bufferManager the samples provider which is stored in physical storage
- * @param bufferListener the {@link PlaybackBufferListener}
- * to notify buffer storage status change
+ * @param bufferListener the {@link PlaybackBufferListener} to notify buffer storage status
+ * change
*/
- public MpegTsSampleExtractor(BufferManager bufferManager,
- PlaybackBufferListener bufferListener) {
+ public MpegTsSampleExtractor(
+ BufferManager bufferManager, PlaybackBufferListener bufferListener) {
mSampleExtractor = new FileSampleExtractor(bufferManager, bufferListener);
init();
}
@@ -98,7 +95,7 @@ public final class MpegTsSampleExtractor implements SampleExtractor {
@Override
public boolean prepare() throws IOException {
- if(!mSampleExtractor.prepare()) {
+ if (!mSampleExtractor.prepare()) {
return false;
}
List<MediaFormat> formats = mSampleExtractor.getTrackFormats();
@@ -124,8 +121,9 @@ public final class MpegTsSampleExtractor implements SampleExtractor {
mCea708TextTrackIndex = trackCount;
}
if (mCea708TextTrackIndex >= 0) {
- mTrackFormats.add(MediaFormat.createTextFormat(null, MIMETYPE_TEXT_CEA_708, 0,
- mTrackFormats.get(0).durationUs, ""));
+ mTrackFormats.add(
+ MediaFormat.createTextFormat(
+ null, MIMETYPE_TEXT_CEA_708, 0, mTrackFormats.get(0).durationUs, ""));
}
return true;
}
@@ -186,23 +184,27 @@ public final class MpegTsSampleExtractor implements SampleExtractor {
return SampleSource.SAMPLE_READ;
} else {
return mVideoTrackIndex < 0 || mReachedEos.get(mVideoTrackIndex)
- ? SampleSource.END_OF_STREAM : SampleSource.NOTHING_READ;
+ ? SampleSource.END_OF_STREAM
+ : SampleSource.NOTHING_READ;
}
}
int result = mSampleExtractor.readSample(track, sampleHolder);
switch (result) {
- case SampleSource.END_OF_STREAM: {
- mReachedEos.set(track, true);
- break;
- }
- case SampleSource.SAMPLE_READ: {
- if (mCea708TextTrackSelected && track == mVideoTrackIndex
- && sampleHolder.data != null) {
- mCcParser.mayParseClosedCaption(sampleHolder.data, sampleHolder.timeUs);
+ case SampleSource.END_OF_STREAM:
+ {
+ mReachedEos.set(track, true);
+ break;
+ }
+ case SampleSource.SAMPLE_READ:
+ {
+ if (mCea708TextTrackSelected
+ && track == mVideoTrackIndex
+ && sampleHolder.data != null) {
+ mCcParser.mayParseClosedCaption(sampleHolder.data, sampleHolder.timeUs);
+ }
+ break;
}
- break;
- }
}
return result;
}
@@ -221,7 +223,7 @@ public final class MpegTsSampleExtractor implements SampleExtractor {
}
@Override
- public void setOnCompletionListener(OnCompletionListener listener, Handler handler) { }
+ public void setOnCompletionListener(OnCompletionListener listener, Handler handler) {}
private abstract class CcParser {
// Interim buffer for reduce direct access to ByteBuffer which is expensive. Using
@@ -278,8 +280,12 @@ public final class MpegTsSampleExtractor implements SampleExtractor {
&& mBuffer[j + 6] == '9'
&& mBuffer[j + 7] == '4'
&& mBuffer[j + 8] == 3) {
- j = parseClosedCaption(buffer, i + j + PATTERN_LENGTH,
- presentationTimeUs) - i;
+ j =
+ parseClosedCaption(
+ buffer,
+ i + j + PATTERN_LENGTH,
+ presentationTimeUs)
+ - i;
} else {
j += PATTERN_LENGTH;
}
@@ -307,20 +313,24 @@ public final class MpegTsSampleExtractor implements SampleExtractor {
int j = 0;
while (j < size - PATTERN_LENGTH) {
// Find the start prefix code of a NAL Unit.
- if (mBuffer[j] == 0
- && mBuffer[j + 1] == 0
- && mBuffer[j + 2] == 1) {
+ if (mBuffer[j] == 0 && mBuffer[j + 1] == 0 && mBuffer[j + 2] == 1) {
int nalType = mBuffer[j + 3] & 0x1f;
int payloadType = mBuffer[j + 4] & 0xff;
// ATSC closed caption data embedded in H264 private user data has NAL type
// 6, payload type 4, and 'GA94' user identifier for ATSC.
- if (nalType == 6 && payloadType == 4 && mBuffer[j + 9] == 'G'
+ if (nalType == 6
+ && payloadType == 4
+ && mBuffer[j + 9] == 'G'
&& mBuffer[j + 10] == 'A'
&& mBuffer[j + 11] == '9'
&& mBuffer[j + 12] == '4') {
- j = parseClosedCaption(buffer, i + j + PATTERN_LENGTH,
- presentationTimeUs) - i;
+ j =
+ parseClosedCaption(
+ buffer,
+ i + j + PATTERN_LENGTH,
+ presentationTimeUs)
+ - i;
} else {
j += 7;
}
diff --git a/src/com/android/tv/tuner/exoplayer/MpegTsSampleSource.java b/tuner/src/com/android/tv/tuner/exoplayer/MpegTsSampleSource.java
index 6007b0be..3b5d1011 100644
--- a/src/com/android/tv/tuner/exoplayer/MpegTsSampleSource.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/MpegTsSampleSource.java
@@ -22,7 +22,6 @@ 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;
import java.util.ArrayList;
import java.util.List;
@@ -132,8 +131,8 @@ public final class MpegTsSampleSource implements SampleSource, SampleSourceReade
}
@Override
- public int readData(int track, long positionUs, MediaFormatHolder formatHolder,
- SampleHolder sampleHolder) {
+ public int readData(
+ int track, long positionUs, MediaFormatHolder formatHolder, SampleHolder sampleHolder) {
Assertions.checkState(mPrepared);
Assertions.checkState(mTrackStates.get(track) != TRACK_STATE_DISABLED);
if (mPendingDiscontinuities.get(track)) {
diff --git a/src/com/android/tv/tuner/exoplayer/MpegTsVideoTrackRenderer.java b/tuner/src/com/android/tv/tuner/exoplayer/MpegTsVideoTrackRenderer.java
index 19360c69..b136e235 100644
--- a/src/com/android/tv/tuner/exoplayer/MpegTsVideoTrackRenderer.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/MpegTsVideoTrackRenderer.java
@@ -1,10 +1,25 @@
+/*
+ * Copyright (C) 2017 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.tuner.exoplayer;
import android.content.Context;
import android.media.MediaCodec;
import android.os.Handler;
import android.util.Log;
-
+import com.android.tv.tuner.TunerFeatures;
import com.google.android.exoplayer.DecoderInfo;
import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.MediaCodecSelector;
@@ -13,13 +28,9 @@ 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;
-
import java.lang.reflect.Field;
-/**
- * MPEG-2 TS video track renderer
- */
+/** MPEG-2 TS video track renderer */
public class MpegTsVideoTrackRenderer extends MediaCodecVideoTrackRenderer {
private static final String TAG = "MpegTsVideoTrackRender";
@@ -37,42 +48,54 @@ public class MpegTsVideoTrackRenderer extends MediaCodecVideoTrackRenderer {
static {
// Remove the reflection below once b/31223646 is resolved.
try {
- sRenderedFirstFrameField = MediaCodecVideoTrackRenderer.class.getDeclaredField(
- "renderedFirstFrame");
+ sRenderedFirstFrameField =
+ MediaCodecVideoTrackRenderer.class.getDeclaredField("renderedFirstFrame");
sRenderedFirstFrameField.setAccessible(true);
} catch (NoSuchFieldException e) {
// Null-checking for {@code sRenderedFirstFrameField} will do the error handling.
}
}
- public MpegTsVideoTrackRenderer(Context context, SampleSource source, Handler handler,
+ 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);
+ super(
+ context,
+ source,
+ MediaCodecSelector.DEFAULT,
+ MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT,
+ VIDEO_PLAYBACK_DEADLINE_IN_MS,
+ handler,
+ listener,
+ DROPPED_FRAMES_NOTIFICATION_THRESHOLD);
+ mIsSwCodecEnabled = TunerFeatures.USE_SW_CODEC_FOR_SD.isEnabled(context);
}
@Override
- protected DecoderInfo getDecoderInfo(MediaCodecSelector codecSelector, String mimeType,
- boolean requiresSecureDecoder) throws MediaCodecUtil.DecoderQueryException {
+ protected DecoderInfo getDecoderInfo(
+ MediaCodecSelector codecSelector, String mimeType, boolean requiresSecureDecoder)
+ throws MediaCodecUtil.DecoderQueryException {
try {
if (mIsSwCodecEnabled && mCodecIsSwPreferred) {
- DecoderInfo swCodec = MediaSoftwareCodecUtil.getSoftwareDecoderInfo(
- mimeType, requiresSecureDecoder);
+ DecoderInfo swCodec =
+ MediaSoftwareCodecUtil.getSoftwareDecoderInfo(
+ mimeType, requiresSecureDecoder);
if (swCodec != null) {
return swCodec;
}
}
} catch (MediaSoftwareCodecUtil.DecoderQueryException e) {
}
- return super.getDecoderInfo(codecSelector, mimeType,requiresSecureDecoder);
+ 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;
+ mCodecIsSwPreferred =
+ MIMETYPE_MPEG2.equalsIgnoreCase(holder.format.mimeType)
+ && holder.format.height < MIN_HD_HEIGHT;
super.onInputFormatChanged(holder);
}
@@ -93,8 +116,10 @@ public class MpegTsVideoTrackRenderer extends MediaCodecVideoTrackRenderer {
try {
sRenderedFirstFrameField.setBoolean(this, renderedFirstFrame);
} catch (IllegalAccessException e) {
- Log.w(TAG, "renderedFirstFrame is not accessible. Playback may start with a frozen"
- +" picture.");
+ Log.w(
+ TAG,
+ "renderedFirstFrame is not accessible. Playback may start with a frozen"
+ + " picture.");
}
}
}
diff --git a/src/com/android/tv/tuner/exoplayer/SampleExtractor.java b/tuner/src/com/android/tv/tuner/exoplayer/SampleExtractor.java
index 543588c7..256aea92 100644
--- a/src/com/android/tv/tuner/exoplayer/SampleExtractor.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/SampleExtractor.java
@@ -16,25 +16,23 @@
package com.android.tv.tuner.exoplayer;
import android.os.Handler;
-
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.TrackRenderer;
-
import java.io.IOException;
import java.util.List;
/**
* 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 #getTrackFormats} and {@link #getTrackMediaFormat}.
+ * <p>Call {@link #prepare} until it returns {@code true}, then access track metadata via {@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
- * data or seeking. Initially, all tracks are deselected.
+ * by calling {@link #deselectTrack}. It is safe to select/deselect tracks after reading sample data
+ * or seeking. Initially, all tracks are deselected.
*
* <p>Call {@link #release()} when the extractor is no longer needed to free resources.
*/
@@ -49,11 +47,11 @@ public interface SampleExtractor {
void maybeThrowError() throws IOException;
/**
- * Prepares the extractor for reading track metadata and samples.
- *
- * @return whether the source is ready; if {@code false}, this method must be called again.
- * @throws IOException thrown if the source can't be read
- */
+ * Prepares the extractor for reading track metadata and samples.
+ *
+ * @return whether the source is ready; if {@code false}, this method must be called again.
+ * @throws IOException thrown if the source can't be read
+ */
boolean prepare() throws IOException;
/** Returns track information about all tracks that can be selected. */
@@ -66,42 +64,42 @@ public interface SampleExtractor {
void deselectTrack(int index);
/**
- * Returns an estimate of the position up to which data is buffered.
- *
- * <p>This method should not be called until after the extractor has been successfully prepared.
- *
- * @return an estimate of the absolute position in microseconds up to which data is buffered,
- * or {@link TrackRenderer#END_OF_TRACK_US} if data is buffered to the end of the stream, or
- * {@link TrackRenderer#UNKNOWN_TIME_US} if no estimate is available.
- */
+ * Returns an estimate of the position up to which data is buffered.
+ *
+ * <p>This method should not be called until after the extractor has been successfully prepared.
+ *
+ * @return an estimate of the absolute position in microseconds up to which data is buffered, or
+ * {@link TrackRenderer#END_OF_TRACK_US} if data is buffered to the end of the stream, or
+ * {@link TrackRenderer#UNKNOWN_TIME_US} if no estimate is available.
+ */
long getBufferedPositionUs();
/**
- * Seeks to the specified time in microseconds.
- *
- * <p>This method should not be called until after the extractor has been successfully prepared.
- *
- * @param positionUs the seek position in microseconds
- */
+ * Seeks to the specified time in microseconds.
+ *
+ * <p>This method should not be called until after the extractor has been successfully prepared.
+ *
+ * @param positionUs the seek position in microseconds
+ */
void seekTo(long positionUs);
/** Stores the {@link MediaFormat} of {@code track}. */
void getTrackMediaFormat(int track, MediaFormatHolder outMediaFormatHolder);
/**
- * Reads the next sample in the track at index {@code track} into {@code sampleHolder}, returning
- * {@link SampleSource#SAMPLE_READ} if it is available.
- *
- * <p>Advances to the next sample if a sample was read.
- *
- * @param track the index of the track from which to read a sample
- * @param sampleHolder the holder for read sample data, if {@link SampleSource#SAMPLE_READ} is
- * returned
- * @return {@link SampleSource#SAMPLE_READ} if a sample was read into {@code sampleHolder}, or
- * {@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.
- */
+ * Reads the next sample in the track at index {@code track} into {@code sampleHolder},
+ * returning {@link SampleSource#SAMPLE_READ} if it is available.
+ *
+ * <p>Advances to the next sample if a sample was read.
+ *
+ * @param track the index of the track from which to read a sample
+ * @param sampleHolder the holder for read sample data, if {@link SampleSource#SAMPLE_READ} is
+ * returned
+ * @return {@link SampleSource#SAMPLE_READ} if a sample was read into {@code sampleHolder}, or
+ * {@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.
+ */
int readSample(int track, SampleHolder sampleHolder);
/** Releases resources associated with this extractor. */
@@ -118,17 +116,14 @@ public interface SampleExtractor {
*/
void setOnCompletionListener(OnCompletionListener listener, Handler handler);
- /**
- * The listener for SampleExtractor being completed.
- */
+ /** The listener for SampleExtractor being completed. */
interface OnCompletionListener {
/**
* Called when sample extraction is completed.
*
- * @param result {@code true} when the extractor is finished without an error,
- * {@code false} otherwise (storage error, weak signal, being reached at EoS
- * prematurely, etc.)
+ * @param result {@code true} when the extractor is finished without an error, {@code false}
+ * otherwise (storage error, weak signal, being reached at EoS prematurely, etc.)
* @param lastExtractedPositionUs the last extracted position when extractor is completed
*/
void onCompletion(boolean result, long lastExtractedPositionUs);
diff --git a/src/com/android/tv/tuner/exoplayer/audio/AudioClock.java b/tuner/src/com/android/tv/tuner/exoplayer/audio/AudioClock.java
index 5666c5b9..13eabc3a 100644
--- a/src/com/android/tv/tuner/exoplayer/audio/AudioClock.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/audio/AudioClock.java
@@ -16,37 +16,32 @@
package com.android.tv.tuner.exoplayer.audio;
-import com.android.tv.common.SoftPreconditions;
-
import android.os.SystemClock;
+import com.android.tv.common.SoftPreconditions;
/**
* Copy of {@link com.google.android.exoplayer.MediaClock}.
- * <p>
- * A simple clock for tracking the progression of media time. The clock can be started, stopped and
- * its time can be set and retrieved. When started, this clock is based on
- * {@link SystemClock#elapsedRealtime()}.
+ *
+ * <p>A simple clock for tracking the progression of media time. The clock can be started, stopped
+ * and its time can be set and retrieved. When started, this clock is based on {@link
+ * SystemClock#elapsedRealtime()}.
*/
/* package */ class AudioClock {
private boolean mStarted;
- /**
- * The media time when the clock was last set or stopped.
- */
+ /** The media time when the clock was last set or stopped. */
private long mPositionUs;
/**
- * The difference between {@link SystemClock#elapsedRealtime()} and {@link #mPositionUs}
- * when the clock was last set or mStarted.
+ * The difference between {@link SystemClock#elapsedRealtime()} and {@link #mPositionUs} when
+ * the clock was last set or mStarted.
*/
private long mDeltaUs;
private float mPlaybackSpeed = 1.0f;
private long mDeltaUpdatedTimeUs;
- /**
- * Starts the clock. Does nothing if the clock is already started.
- */
+ /** Starts the clock. Does nothing if the clock is already started. */
public void start() {
if (!mStarted) {
mStarted = true;
@@ -55,9 +50,7 @@ import android.os.SystemClock;
}
}
- /**
- * Stops the clock. Does nothing if the clock is already stopped.
- */
+ /** Stops the clock. Does nothing if the clock is already stopped. */
public void stop() {
if (mStarted) {
mPositionUs = elapsedRealtimeMinus(mDeltaUs);
@@ -65,25 +58,21 @@ import android.os.SystemClock;
}
}
- /**
- * @param timeUs The position to set in microseconds.
- */
+ /** @param timeUs The position to set in microseconds. */
public void setPositionUs(long timeUs) {
this.mPositionUs = timeUs;
mDeltaUs = elapsedRealtimeMinus(timeUs);
mDeltaUpdatedTimeUs = SystemClock.elapsedRealtime() * 1000;
}
- /**
- * @return The current position in microseconds.
- */
+ /** @return The current position in microseconds. */
public long getPositionUs() {
if (!mStarted) {
return mPositionUs;
}
if (mPlaybackSpeed != 1.0f) {
- long elapsedTimeFromPlaybackSpeedChanged = SystemClock.elapsedRealtime() * 1000
- - mDeltaUpdatedTimeUs;
+ long elapsedTimeFromPlaybackSpeedChanged =
+ SystemClock.elapsedRealtime() * 1000 - mDeltaUpdatedTimeUs;
return elapsedRealtimeMinus(mDeltaUs)
+ (long) ((mPlaybackSpeed - 1.0f) * elapsedTimeFromPlaybackSpeedChanged);
} else {
@@ -91,9 +80,7 @@ import android.os.SystemClock;
}
}
- /**
- * Sets playback speed. {@code speed} should be positive.
- */
+ /** Sets playback speed. {@code speed} should be positive. */
public void setPlaybackSpeed(float speed) {
SoftPreconditions.checkState(speed > 0);
mDeltaUs = elapsedRealtimeMinus(getPositionUs());
diff --git a/src/com/android/tv/tuner/exoplayer/audio/AudioDecoder.java b/tuner/src/com/android/tv/tuner/exoplayer/audio/AudioDecoder.java
index e581092a..64fe1344 100644
--- a/src/com/android/tv/tuner/exoplayer/audio/AudioDecoder.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/audio/AudioDecoder.java
@@ -19,7 +19,6 @@ package com.android.tv.tuner.exoplayer.audio;
import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.SampleHolder;
-
import java.nio.ByteBuffer;
/** A base class for audio decoders. */
@@ -42,7 +41,7 @@ public abstract class AudioDecoder {
* Clear previous decode state if any. Prepares to decode samples of the specified encoding.
* This method should be called before using decode.
*
- * @param mime audio encoding
+ * @param mimeType audio encoding
*/
public abstract void resetDecoderState(String mimeType);
diff --git a/src/com/android/tv/tuner/exoplayer/audio/AudioTrackMonitor.java b/tuner/src/com/android/tv/tuner/exoplayer/audio/AudioTrackMonitor.java
index ec616b13..28389017 100644
--- a/src/com/android/tv/tuner/exoplayer/audio/AudioTrackMonitor.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/audio/AudioTrackMonitor.java
@@ -19,15 +19,12 @@ package com.android.tv.tuner.exoplayer.audio;
import android.os.SystemClock;
import android.util.Log;
import android.util.Pair;
-
import com.google.android.exoplayer.util.MimeTypes;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
-/**
- * Monitors the rendering position of {@link AudioTrack}.
- */
+/** Monitors the rendering position of {@link AudioTrack}. */
public class AudioTrackMonitor {
private static final String TAG = "AudioTrackMonitor";
private static final boolean DEBUG = false;
@@ -98,30 +95,44 @@ public class AudioTrackMonitor {
/**
* Logs if interested events are present.
- * <p>
- * Periodic logging is not enabled in release mode in order to avoid verbose logging.
+ *
+ * <p>Periodic logging is not enabled in release mode in order to avoid verbose logging.
*/
public void maybeLog() {
long now = SystemClock.elapsedRealtime();
if (mExpireMs != 0 && now >= mExpireMs) {
if (DEBUG) {
- long unitDuration = mIsMp2 ? MpegTsDefaultAudioTrackRenderer.MP2_SAMPLE_DURATION_US
- : MpegTsDefaultAudioTrackRenderer.AC3_SAMPLE_DURATION_US;
+ long unitDuration =
+ mIsMp2
+ ? MpegTsDefaultAudioTrackRenderer.MP2_SAMPLE_DURATION_US
+ : MpegTsDefaultAudioTrackRenderer.AC3_SAMPLE_DURATION_US;
long sampleDuration = (mTotalCount - 1) * unitDuration / 1000;
long totalDuration = now - mStartMs;
StringBuilder ptsBuilder = new StringBuilder();
- ptsBuilder.append("PTS received ").append(mSampleCount).append(", ")
- .append(totalDuration - sampleDuration).append(' ');
+ ptsBuilder
+ .append("PTS received ")
+ .append(mSampleCount)
+ .append(", ")
+ .append(totalDuration - sampleDuration)
+ .append(' ');
for (Pair<Long, Integer> pair : mPtsList) {
- ptsBuilder.append('[').append(pair.first).append(':').append(pair.second)
+ ptsBuilder
+ .append('[')
+ .append(pair.first)
+ .append(':')
+ .append(pair.second)
.append("], ");
}
Log.d(TAG, ptsBuilder.toString());
}
if (DEBUG || mCurSampleSize.size() > 1) {
- Log.d(TAG, "PTS received sample size: "
- + String.valueOf(mSampleSize) + mCurSampleSize + mHeader);
+ Log.d(
+ TAG,
+ "PTS received sample size: "
+ + String.valueOf(mSampleSize)
+ + mCurSampleSize
+ + mHeader);
}
flush();
}
diff --git a/src/com/android/tv/tuner/exoplayer/audio/AudioTrackWrapper.java b/tuner/src/com/android/tv/tuner/exoplayer/audio/AudioTrackWrapper.java
index 953c9fc4..7446c923 100644
--- a/src/com/android/tv/tuner/exoplayer/audio/AudioTrackWrapper.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/audio/AudioTrackWrapper.java
@@ -17,16 +17,14 @@
package com.android.tv.tuner.exoplayer.audio;
import android.media.MediaFormat;
-
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.audio.AudioTrack;
-
import java.nio.ByteBuffer;
/**
- * {@link AudioTrack} wrapper class for trickplay operations including FF/RW.
- * FF/RW trickplay operations do not need framework {@link AudioTrack}.
- * This wrapper class will do nothing in disabled status for those operations.
+ * {@link AudioTrack} wrapper class for trickplay operations including FF/RW. FF/RW trickplay
+ * operations do not need framework {@link AudioTrack}. This wrapper class will do nothing in
+ * disabled status for those operations.
*/
public class AudioTrackWrapper {
private static final int PCM16_FRAME_BYTES = 2;
@@ -57,7 +55,7 @@ public class AudioTrackWrapper {
resetSessionId();
}
- public void release() {
+ public void release() {
if (mAudioSessionID != AudioTrack.SESSION_ID_NOT_SET) {
mAudioTrack.release();
}
diff --git a/src/com/android/tv/tuner/exoplayer/audio/MediaCodecAudioDecoder.java b/tuner/src/com/android/tv/tuner/exoplayer/audio/MediaCodecAudioDecoder.java
index 72bc68b6..80f91ebd 100644
--- a/src/com/android/tv/tuner/exoplayer/audio/MediaCodecAudioDecoder.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/audio/MediaCodecAudioDecoder.java
@@ -18,7 +18,6 @@ package com.android.tv.tuner.exoplayer.audio;
import android.media.MediaCodec;
import android.util.Log;
-
import com.google.android.exoplayer.CodecCounters;
import com.google.android.exoplayer.DecoderInfo;
import com.google.android.exoplayer.ExoPlaybackException;
@@ -26,7 +25,6 @@ import com.google.android.exoplayer.MediaCodecSelector;
import com.google.android.exoplayer.MediaCodecUtil;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.SampleHolder;
-
import java.nio.ByteBuffer;
import java.util.ArrayList;
diff --git a/src/com/android/tv/tuner/exoplayer/audio/MpegTsDefaultAudioTrackRenderer.java b/tuner/src/com/android/tv/tuner/exoplayer/audio/MpegTsDefaultAudioTrackRenderer.java
index 77170419..944cfbcf 100644
--- a/src/com/android/tv/tuner/exoplayer/audio/MpegTsDefaultAudioTrackRenderer.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/audio/MpegTsDefaultAudioTrackRenderer.java
@@ -21,7 +21,7 @@ import android.os.Build;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
-
+import com.android.tv.tuner.tvinput.TunerDebug;
import com.google.android.exoplayer.CodecCounters;
import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.MediaClock;
@@ -34,9 +34,6 @@ import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.audio.AudioTrack;
import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.MimeTypes;
-import com.android.tv.tuner.exoplayer.ffmpeg.FfmpegDecoderClient;
-import com.android.tv.tuner.tvinput.TunerDebug;
-
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
@@ -65,21 +62,21 @@ public class MpegTsDefaultAudioTrackRenderer extends TrackRenderer implements Me
public static final long INITIAL_AUDIO_BUFFERING_TIME_US =
BUFFERED_SAMPLES_IN_AUDIOTRACK * AC3_SAMPLE_DURATION_US;
-
private static final String TAG = "MpegTsDefaultAudioTrac";
private static final boolean DEBUG = false;
/**
- * Interface definition for a callback to be notified of
- * {@link com.google.android.exoplayer.audio.AudioTrack} error.
+ * Interface definition for a callback to be notified of {@link
+ * com.google.android.exoplayer.audio.AudioTrack} error.
*/
public interface EventListener {
void onAudioTrackInitializationError(AudioTrack.InitializationException e);
+
void onAudioTrackWriteError(AudioTrack.WriteException e);
}
private static final int DEFAULT_INPUT_BUFFER_SIZE = 16384 * 2;
- private static final int DEFAULT_OUTPUT_BUFFER_SIZE = 1024*1024;
+ private static final int DEFAULT_OUTPUT_BUFFER_SIZE = 1024 * 1024;
private static final int MONITOR_DURATION_MS = 1000;
private static final int AC3_HEADER_BITRATE_OFFSET = 4;
private static final int MP2_HEADER_BITRATE_OFFSET = 2;
@@ -156,7 +153,8 @@ public class MpegTsDefaultAudioTrackRenderer extends TrackRenderer implements Me
mAudioClock = new AudioClock();
mTracksIndex = new ArrayList<>();
mAc3Passthrough = usePassthrough;
- mSoftwareDecoderAvailable = hasSoftwareAudioDecoder && FfmpegDecoderClient.isAvailable();
+ // TODO reimplement ffmpeg decoder check for google3
+ mSoftwareDecoderAvailable = false;
}
@Override
@@ -374,8 +372,8 @@ public class MpegTsDefaultAudioTrackRenderer extends TrackRenderer implements Me
}
private void readFormat() throws IOException, ExoPlaybackException {
- int result = mSource.readData(mTrackIndex, mCurrentPositionUs,
- mFormatHolder, mSampleHolder);
+ int result =
+ mSource.readData(mTrackIndex, mCurrentPositionUs, mFormatHolder, mSampleHolder);
if (result == SampleSource.FORMAT_READ) {
onInputFormatChanged(mFormatHolder);
}
@@ -394,8 +392,7 @@ public class MpegTsDefaultAudioTrackRenderer extends TrackRenderer implements Me
format.language);
}
- private void onInputFormatChanged(MediaFormatHolder formatHolder)
- throws ExoPlaybackException {
+ private void onInputFormatChanged(MediaFormatHolder formatHolder) throws ExoPlaybackException {
String mimeType = formatHolder.format.mimeType;
mUseFrameworkDecoder = MediaCodecAudioDecoder.supportMimeType(mSelector, mimeType);
if (mUseFrameworkDecoder) {
@@ -403,15 +400,11 @@ public class MpegTsDefaultAudioTrackRenderer extends TrackRenderer implements Me
mFormat = formatHolder.format;
mAudioDecoder.maybeInitDecoder(mFormat);
mSampleHolder = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_DISABLED);
- } else if (mSoftwareDecoderAvailable
- && (MimeTypes.AUDIO_MPEG_L2.equalsIgnoreCase(mimeType)
- || MimeTypes.AUDIO_AC3.equalsIgnoreCase(mimeType) && !mAc3Passthrough)) {
- releaseDecoder();
- mSampleHolder = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_DIRECT);
- mSampleHolder.ensureSpaceForWrite(DEFAULT_INPUT_BUFFER_SIZE);
- mAudioDecoder = FfmpegDecoderClient.getInstance();
- mDecodingMime = mimeType;
- mFormat = convertMediaFormatToRaw(formatHolder.format);
+ // TODO reimplement ffmeg for google3
+ // Here use else if
+ // (MimeTypes.AUDIO_MPEG_L2.equalsIgnoreCase(mimeType)
+ // || MimeTypes.AUDIO_AC3.equalsIgnoreCase(mimeType) && !mAc3Passthrough
+ // then set the audio decoder to ffmpeg
} else {
mSampleHolder = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_DIRECT);
mSampleHolder.ensureSpaceForWrite(DEFAULT_INPUT_BUFFER_SIZE);
@@ -470,64 +463,68 @@ public class MpegTsDefaultAudioTrackRenderer extends TrackRenderer implements Me
int result =
mSource.readData(mTrackIndex, mPresentationTimeUs, mFormatHolder, mSampleHolder);
switch (result) {
- case SampleSource.NOTHING_READ: {
- return false;
- }
- case SampleSource.FORMAT_READ: {
- Log.i(TAG, "Format was read again");
- onInputFormatChanged(mFormatHolder);
- return true;
- }
- case SampleSource.END_OF_STREAM: {
- Log.i(TAG, "End of stream from SampleSource");
- mInputStreamEnded = true;
- return false;
- }
- default: {
- if (mSampleHolder.size != mSampleSize
- && mFormatConfigured
- && !mUseFrameworkDecoder) {
- onSampleSizeChanged(mSampleHolder.size);
+ case SampleSource.NOTHING_READ:
+ {
+ return false;
}
- mSampleHolder.data.flip();
- if (!mUseFrameworkDecoder) {
- if (MimeTypes.AUDIO_MPEG_L2.equalsIgnoreCase(mDecodingMime)) {
- mMonitor.addPts(
- mSampleHolder.timeUs,
- mOutputBuffer.position(),
- mSampleHolder.data.get(MP2_HEADER_BITRATE_OFFSET)
- & MP2_HEADER_BITRATE_MASK);
- } else {
- mMonitor.addPts(
- mSampleHolder.timeUs,
- mOutputBuffer.position(),
- mSampleHolder.data.get(AC3_HEADER_BITRATE_OFFSET) & 0xff);
- }
+ case SampleSource.FORMAT_READ:
+ {
+ Log.i(TAG, "Format was read again");
+ onInputFormatChanged(mFormatHolder);
+ return true;
+ }
+ case SampleSource.END_OF_STREAM:
+ {
+ Log.i(TAG, "End of stream from SampleSource");
+ mInputStreamEnded = true;
+ return false;
}
- if (mAudioDecoder != null) {
- mAudioDecoder.decode(mSampleHolder);
- if (mUseFrameworkDecoder) {
- int outputIndex =
- ((MediaCodecAudioDecoder) mAudioDecoder).getOutputIndex();
- if (outputIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
- onOutputFormatChanged(mAudioDecoder.getOutputFormat());
- return true;
- } else if (outputIndex < 0) {
- return true;
+ default:
+ {
+ if (mSampleHolder.size != mSampleSize
+ && mFormatConfigured
+ && !mUseFrameworkDecoder) {
+ onSampleSizeChanged(mSampleHolder.size);
+ }
+ mSampleHolder.data.flip();
+ if (!mUseFrameworkDecoder) {
+ if (MimeTypes.AUDIO_MPEG_L2.equalsIgnoreCase(mDecodingMime)) {
+ mMonitor.addPts(
+ mSampleHolder.timeUs,
+ mOutputBuffer.position(),
+ mSampleHolder.data.get(MP2_HEADER_BITRATE_OFFSET)
+ & MP2_HEADER_BITRATE_MASK);
+ } else {
+ mMonitor.addPts(
+ mSampleHolder.timeUs,
+ mOutputBuffer.position(),
+ mSampleHolder.data.get(AC3_HEADER_BITRATE_OFFSET) & 0xff);
}
- if (((MediaCodecAudioDecoder) mAudioDecoder).maybeDecodeOnlyIndex()) {
- AUDIO_TRACK.handleDiscontinuity();
- return true;
+ }
+ if (mAudioDecoder != null) {
+ mAudioDecoder.decode(mSampleHolder);
+ if (mUseFrameworkDecoder) {
+ int outputIndex =
+ ((MediaCodecAudioDecoder) mAudioDecoder).getOutputIndex();
+ if (outputIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ onOutputFormatChanged(mAudioDecoder.getOutputFormat());
+ return true;
+ } else if (outputIndex < 0) {
+ return true;
+ }
+ if (((MediaCodecAudioDecoder) mAudioDecoder).maybeDecodeOnlyIndex()) {
+ AUDIO_TRACK.handleDiscontinuity();
+ return true;
+ }
}
+ ByteBuffer outputBuffer = mAudioDecoder.getDecodedSample();
+ long presentationTimeUs = mAudioDecoder.getDecodedTimeUs();
+ decodeDone(outputBuffer, presentationTimeUs);
+ } else {
+ decodeDone(mSampleHolder.data, mSampleHolder.timeUs);
}
- ByteBuffer outputBuffer = mAudioDecoder.getDecodedSample();
- long presentationTimeUs = mAudioDecoder.getDecodedTimeUs();
- decodeDone(outputBuffer, presentationTimeUs);
- } else {
- decodeDone(mSampleHolder.data, mSampleHolder.timeUs);
+ return true;
}
- return true;
- }
}
}
@@ -549,11 +546,11 @@ public class MpegTsDefaultAudioTrackRenderer extends TrackRenderer implements Me
try {
// To reduce discontinuity, interpolate presentation time.
if (MimeTypes.AUDIO_MPEG_L2.equalsIgnoreCase(mDecodingMime)) {
- mInterpolatedTimeUs = mPresentationTimeUs
- + mPresentationCount * MP2_SAMPLE_DURATION_US;
+ mInterpolatedTimeUs =
+ mPresentationTimeUs + mPresentationCount * MP2_SAMPLE_DURATION_US;
} else if (!mUseFrameworkDecoder) {
- mInterpolatedTimeUs = mPresentationTimeUs
- + mPresentationCount * AC3_SAMPLE_DURATION_US;
+ mInterpolatedTimeUs =
+ mPresentationTimeUs + mPresentationCount * AC3_SAMPLE_DURATION_US;
} else {
mInterpolatedTimeUs = mPresentationTimeUs;
}
@@ -588,7 +585,8 @@ public class MpegTsDefaultAudioTrackRenderer extends TrackRenderer implements Me
protected long getBufferedPositionUs() {
long pos = mSource.getBufferedPositionUs();
return pos == UNKNOWN_TIME_US || pos == END_OF_TRACK_US
- ? pos : Math.max(pos, getPositionUs());
+ ? pos
+ : Math.max(pos, getPositionUs());
}
@Override
@@ -607,15 +605,19 @@ public class MpegTsDefaultAudioTrackRenderer extends TrackRenderer implements Me
if (DEBUG) {
long oldPositionUs = Math.max(mCurrentPositionUs, 0);
long currentPositionUs = Math.max(mPresentationTimeUs, mCurrentPositionUs);
- Log.d(TAG, "Audio position is not set, diff in us: "
- + String.valueOf(currentPositionUs - oldPositionUs));
+ Log.d(
+ TAG,
+ "Audio position is not set, diff in us: "
+ + String.valueOf(currentPositionUs - oldPositionUs));
}
mCurrentPositionUs = Math.max(mPresentationTimeUs, mCurrentPositionUs);
} else {
if (mPreviousPositionUs
> audioTrackCurrentPositionUs + BACKWARD_AUDIO_TRACK_MOVE_THRESHOLD_US) {
- Log.e(TAG, "audio_position BACK JUMP: "
- + (mPreviousPositionUs - audioTrackCurrentPositionUs));
+ Log.e(
+ TAG,
+ "audio_position BACK JUMP: "
+ + (mPreviousPositionUs - audioTrackCurrentPositionUs));
mCurrentPositionUs = audioTrackCurrentPositionUs;
} else {
mCurrentPositionUs = Math.max(mCurrentPositionUs, audioTrackCurrentPositionUs);
@@ -660,24 +662,26 @@ public class MpegTsDefaultAudioTrackRenderer extends TrackRenderer implements Me
if (mEventHandler == null || mEventListener == null) {
return;
}
- mEventHandler.post(new Runnable() {
- @Override
- public void run() {
- mEventListener.onAudioTrackInitializationError(e);
- }
- });
+ mEventHandler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ mEventListener.onAudioTrackInitializationError(e);
+ }
+ });
}
private void notifyAudioTrackWriteError(final AudioTrack.WriteException e) {
if (mEventHandler == null || mEventListener == null) {
return;
}
- mEventHandler.post(new Runnable() {
- @Override
- public void run() {
- mEventListener.onAudioTrackWriteError(e);
- }
- });
+ mEventHandler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ mEventListener.onAudioTrackWriteError(e);
+ }
+ });
}
@Override
diff --git a/src/com/android/tv/tuner/exoplayer/audio/MpegTsMediaCodecAudioTrackRenderer.java b/tuner/src/com/android/tv/tuner/exoplayer/audio/MpegTsMediaCodecAudioTrackRenderer.java
index 142aa9b2..b382545f 100644
--- a/src/com/android/tv/tuner/exoplayer/audio/MpegTsMediaCodecAudioTrackRenderer.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/audio/MpegTsMediaCodecAudioTrackRenderer.java
@@ -17,7 +17,6 @@
package com.android.tv.tuner.exoplayer.audio;
import android.os.Handler;
-
import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
import com.google.android.exoplayer.MediaCodecSelector;
@@ -36,8 +35,8 @@ public class MpegTsMediaCodecAudioTrackRenderer extends MediaCodecAudioTrackRend
public interface Ac3EventListener extends EventListener {
/**
- * Invoked when a {@link android.media.PlaybackParams} set to an
- * {@link android.media.AudioTrack} is not valid.
+ * Invoked when a {@link android.media.PlaybackParams} set to an {@link
+ * android.media.AudioTrack} is not valid.
*
* @param e The corresponding exception.
*/
@@ -70,16 +69,17 @@ public class MpegTsMediaCodecAudioTrackRenderer extends MediaCodecAudioTrackRend
private void notifyAudioTrackSetPlaybackParamsError(final IllegalArgumentException e) {
if (eventHandler != null && mListener != null) {
- eventHandler.post(new Runnable() {
- @Override
- public void run() {
- mListener.onAudioTrackSetPlaybackParamsError(e);
- }
- });
+ eventHandler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ mListener.onAudioTrackSetPlaybackParamsError(e);
+ }
+ });
}
}
- static private boolean isAudioTrackSetPlaybackParamsError(IllegalArgumentException e) {
+ private static boolean isAudioTrackSetPlaybackParamsError(IllegalArgumentException e) {
if (e.getStackTrace() == null || e.getStackTrace().length < 1) {
return false;
}
@@ -91,4 +91,4 @@ public class MpegTsMediaCodecAudioTrackRenderer extends MediaCodecAudioTrackRend
}
return false;
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/tuner/exoplayer/buffer/BufferManager.java b/tuner/src/com/android/tv/tuner/exoplayer/buffer/BufferManager.java
index 112e9dc4..3e4ab103 100644
--- a/src/com/android/tv/tuner/exoplayer/buffer/BufferManager.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/buffer/BufferManager.java
@@ -23,12 +23,10 @@ import android.support.annotation.VisibleForTesting;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
-
-import com.google.android.exoplayer.SampleHolder;
import com.android.tv.common.SoftPreconditions;
+import com.android.tv.common.util.CommonUtils;
import com.android.tv.tuner.exoplayer.SampleExtractor;
-import com.android.tv.util.Utils;
-
+import com.google.android.exoplayer.SampleHolder;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -39,11 +37,12 @@ import java.util.Locale;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Manages {@link SampleChunk} objects.
- * <p>
- * The buffer manager can be disabled, while running, if the write throughput to the associated
+ *
+ * <p>The buffer manager can be disabled, while running, if the write throughput to the associated
* external storage is detected to be lower than a threshold {@code MINIMUM_DISK_WRITE_SPEED_MBPS}".
* This leads to restarting playback flow.
*/
@@ -53,10 +52,10 @@ public class BufferManager {
// 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
+ 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
+ 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
private final SampleChunk.SampleChunkCreator mSampleChunkCreator;
// Maps from track name to a map which maps from starting position to {@link SampleChunk}.
@@ -67,51 +66,49 @@ public class BufferManager {
private final StorageManager mStorageManager;
private long mBufferSize = 0;
private final EvictChunkQueueMap mPendingDelete = new EvictChunkQueueMap();
- private final SampleChunk.ChunkCallback mChunkCallback = new SampleChunk.ChunkCallback() {
- @Override
- public void onChunkWrite(SampleChunk chunk) {
- mBufferSize += chunk.getSize();
- }
+ private final SampleChunk.ChunkCallback mChunkCallback =
+ new SampleChunk.ChunkCallback() {
+ @Override
+ public void onChunkWrite(SampleChunk chunk) {
+ mBufferSize += chunk.getSize();
+ }
- @Override
- public void onChunkDelete(SampleChunk chunk) {
- mBufferSize -= chunk.getSize();
- }
- };
+ @Override
+ public void onChunkDelete(SampleChunk chunk) {
+ mBufferSize -= chunk.getSize();
+ }
+ };
private int mMinSampleSizeForSpeedCheck = MINIMUM_SAMPLE_SIZE_FOR_SPEED_CHECK;
private long mTotalWriteSize;
private long mTotalWriteTimeNs;
private float mWriteBandwidth = 0.0f;
- private volatile int mSpeedCheckCount;
+ private final AtomicInteger mSpeedCheckCount = new AtomicInteger();
public interface ChunkEvictedListener {
void onChunkEvicted(String id, long createdTimeMs);
}
- /**
- * Handles I/O
- * between BufferManager and {@link SampleExtractor}.
- */
+ /** Handles I/O between BufferManager and {@link SampleExtractor}. */
public interface SampleBuffer {
/**
* Initializes SampleBuffer.
+ *
* @param Ids track identifiers for storage read/write.
* @param mediaFormats meta-data for each track.
* @throws IOException
*/
- void init(@NonNull List<String> Ids,
- @NonNull List<com.google.android.exoplayer.MediaFormat> mediaFormats)
+ void init(
+ @NonNull List<String> Ids,
+ @NonNull List<com.google.android.exoplayer.MediaFormat> mediaFormats)
throws IOException;
- /**
- * Selects the track {@code index} for reading sample data.
- */
+ /** 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.
+ * Deselects the track at {@code index}, so that no more samples will be read from the
+ * track.
*/
void deselectTrack(int index);
@@ -126,71 +123,59 @@ public class BufferManager {
void writeSample(int index, SampleHolder sample, ConditionVariable conditionVariable)
throws IOException;
- /**
- * Checks whether storage write speed is slow.
- */
+ /** Checks whether storage write speed is slow. */
boolean isWriteSpeedSlow(int sampleSize, long writeDurationNs);
/**
* Handles when write speed is slow.
+ *
* @throws IOException
*/
void handleWriteSpeedSlow() throws IOException;
- /**
- * Sets the flag when EoS was reached.
- */
+ /** Sets the flag when EoS was reached. */
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}.
+ * 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.
- */
+ /** Seeks to the specified time in microseconds. */
void seekTo(long positionUs);
- /**
- * Returns an estimate of the position up to which data is buffered.
- */
+ /** Returns an estimate of the position up to which data is buffered. */
long getBufferedPositionUs();
- /**
- * Returns whether there is buffered data.
- */
+ /** Returns whether there is buffered data. */
boolean continueBuffering(long positionUs);
/**
* Cleans up and releases everything.
+ *
* @throws IOException
*/
void release() throws IOException;
}
- /**
- * A Track format which will be loaded and saved from the permanent storage for recordings.
- */
+ /** A Track format which will be loaded and saved from the permanent storage for recordings. */
public static class TrackFormat {
/**
- * The track id for the specified track. The track id will be used as a track identifier
- * for recordings.
+ * The track id for the specified track. The track id will be used as a track identifier for
+ * recordings.
*/
public final String trackId;
- /**
- * The {@link MediaFormat} for the specified track.
- */
+ /** The {@link MediaFormat} for the specified track. */
public final MediaFormat format;
/**
* Creates TrackFormat.
+ *
* @param trackId
* @param format
*/
@@ -200,29 +185,24 @@ public class BufferManager {
}
}
- /**
- * A Holder for a sample position which will be loaded from the index file for recordings.
- */
+ /** A Holder for a sample position which will be loaded from the index file for recordings. */
public static class PositionHolder {
/**
- * The current sample position in microseconds.
- * The position is identical to the PTS(presentation time stamp) of the sample.
+ * The current sample position in microseconds. The position is identical to the
+ * PTS(presentation time stamp) of the sample.
*/
public final long positionUs;
- /**
- * Base sample position for the current {@link SampleChunk}.
- */
+ /** Base sample position for the current {@link SampleChunk}. */
public final long basePositionUs;
- /**
- * The file offset for the current sample in the current {@link SampleChunk}.
- */
+ /** The file offset for the current sample in the current {@link SampleChunk}. */
public final int offset;
/**
* Creates a holder for a specific position in the recording.
+ *
* @param positionUs
* @param offset
*/
@@ -233,9 +213,7 @@ public class BufferManager {
}
}
- /**
- * Storage configuration and policy manager for {@link BufferManager}
- */
+ /** Storage configuration and policy manager for {@link BufferManager} */
public interface StorageManager {
/**
@@ -257,7 +235,7 @@ public class BufferManager {
*
* @param bufferSize the current total usage of Storage in bytes.
* @param pendingDelete the current storage usage which will be deleted in near future by
- * bytes
+ * bytes
* @return {@code true} if it reached pre-determined max size
*/
boolean reachedStorageMax(long bufferSize, long pendingDelete);
@@ -266,7 +244,7 @@ public class BufferManager {
* Informs whether the storage has enough remained space.
*
* @param pendingDelete the current storage usage which will be deleted in near future by
- * bytes
+ * bytes
* @return {@code true} if it has enough space
*/
boolean hasEnoughBuffer(long pendingDelete);
@@ -295,8 +273,7 @@ public class BufferManager {
* @param isAudio {@code true} if it is for audio track
* @throws IOException
*/
- void writeTrackInfoFiles(List<TrackFormat> formatList, boolean isAudio)
- throws IOException;
+ void writeTrackInfoFiles(List<TrackFormat> formatList, boolean isAudio) throws IOException;
/**
* Writes index file to storage.
@@ -356,8 +333,8 @@ public class BufferManager {
this(storageManager, new SampleChunk.SampleChunkCreator());
}
- public BufferManager(StorageManager storageManager,
- SampleChunk.SampleChunkCreator sampleChunkCreator) {
+ public BufferManager(
+ StorageManager storageManager, SampleChunk.SampleChunkCreator sampleChunkCreator) {
mStorageManager = storageManager;
mSampleChunkCreator = sampleChunkCreator;
}
@@ -380,14 +357,19 @@ public class BufferManager {
* @param id the name of the track
* @param positionUs current position to write a sample in micro seconds.
* @param samplePool {@link SamplePool} for the fast creation of samples.
- * @param currentChunk the current {@link SampleChunk} to write, {@code null} when to create
- * a new {@link SampleChunk}.
+ * @param currentChunk the current {@link SampleChunk} to write, {@code null} when to create a
+ * new {@link SampleChunk}.
* @param currentOffset the current offset to write.
* @return returns the created {@link SampleChunk}.
* @throws IOException
*/
- public SampleChunk createNewWriteFileIfNeeded(String id, long positionUs, SamplePool samplePool,
- SampleChunk currentChunk, int currentOffset) throws IOException {
+ public SampleChunk createNewWriteFileIfNeeded(
+ String id,
+ long positionUs,
+ SamplePool samplePool,
+ SampleChunk currentChunk,
+ int currentOffset)
+ throws IOException {
if (!maybeEvictChunk()) {
throw new IOException("Not enough storage space");
}
@@ -400,8 +382,9 @@ public class BufferManager {
}
if (currentChunk == null) {
File file = new File(mStorageManager.getBufferDir(), getFileName(id, positionUs));
- SampleChunk sampleChunk = mSampleChunkCreator
- .createSampleChunk(samplePool, file, positionUs, mChunkCallback);
+ SampleChunk sampleChunk =
+ mSampleChunkCreator.createSampleChunk(
+ samplePool, file, positionUs, mChunkCallback);
map.put(positionUs, new Pair(sampleChunk, 0));
return sampleChunk;
} else {
@@ -430,11 +413,16 @@ public class BufferManager {
}
SampleChunk chunk = null;
long basePositionUs = -1;
- for (PositionHolder position: keyPositions) {
+ for (PositionHolder position : keyPositions) {
if (position.basePositionUs != basePositionUs) {
- chunk = mSampleChunkCreator.loadSampleChunkFromFile(samplePool,
- mStorageManager.getBufferDir(), getFileName(trackId, position.positionUs),
- position.positionUs, mChunkCallback, chunk);
+ chunk =
+ mSampleChunkCreator.loadSampleChunkFromFile(
+ samplePool,
+ mStorageManager.getBufferDir(),
+ getFileName(trackId, position.positionUs),
+ position.positionUs,
+ mChunkCallback,
+ chunk);
basePositionUs = position.basePositionUs;
}
map.put(position.positionUs, new Pair(chunk, position.offset));
@@ -467,13 +455,13 @@ public class BufferManager {
* Evicts chunks which are ready to be evicted for the specified track
*
* @param id the specified track
- * @param earlierThanPositionUs the start position of the {@link SampleChunk}
- * should be earlier than
+ * @param earlierThanPositionUs the start position of the {@link SampleChunk} should be earlier
+ * than
*/
public void evictChunks(String id, long earlierThanPositionUs) {
SampleChunk chunk = null;
while ((chunk = mPendingDelete.poll(id, earlierThanPositionUs)) != null) {
- SampleChunk.IoState.release(chunk, !mStorageManager.isPersistent()) ;
+ SampleChunk.IoState.release(chunk, !mStorageManager.isPersistent());
}
}
@@ -518,11 +506,17 @@ public class BufferManager {
mPendingDelete.add(earliestChunkId, earliestChunk);
earliestChunkMap.remove(earliestChunk.getStartPositionUs());
if (DEBUG) {
- Log.d(TAG, String.format("bufferSize = %d; pendingDelete = %b; "
- + "earliestChunk size = %d; %s@%d (%s)",
- mBufferSize, pendingDelete, earliestChunk.getSize(), earliestChunkId,
- earliestChunk.getStartPositionUs(),
- Utils.toIsoDateTimeString(earliestChunk.getCreatedTimeMs())));
+ Log.d(
+ TAG,
+ String.format(
+ "bufferSize = %d; pendingDelete = %b; "
+ + "earliestChunk size = %d; %s@%d (%s)",
+ mBufferSize,
+ pendingDelete,
+ earliestChunk.getSize(),
+ earliestChunkId,
+ earliestChunk.getStartPositionUs(),
+ CommonUtils.toIsoDateTimeString(earliestChunk.getCreatedTimeMs())));
}
ChunkEvictedListener listener = mEvictListeners.get(earliestChunkId);
if (listener != null) {
@@ -593,9 +587,7 @@ public class BufferManager {
}
}
- /**
- * Releases all the resources.
- */
+ /** Releases all the resources. */
public void release() {
try {
mPendingDelete.release();
@@ -613,8 +605,8 @@ public class BufferManager {
} catch (ConcurrentModificationException | NullPointerException e) {
// TODO: remove this after it it confirmed that race condition issues are resolved.
// b/32492258, b/32373376
- SoftPreconditions.checkState(false, "Exception on BufferManager#release: ",
- e.toString());
+ SoftPreconditions.checkState(
+ false, "Exception on BufferManager#release: ", e.toString());
}
}
@@ -624,9 +616,7 @@ public class BufferManager {
mTotalWriteTimeNs = 0;
}
- /**
- * Adds a disk write sample size to calculate the average disk write bandwidth.
- */
+ /** Adds a disk write sample size to calculate the average disk write bandwidth. */
public void addWriteStat(long size, long timeNs) {
if (size >= mMinSampleSizeForSpeedCheck) {
mTotalWriteSize += size;
@@ -635,8 +625,8 @@ public class BufferManager {
}
/**
- * Returns if the average disk write bandwidth is slower than
- * threshold {@code MINIMUM_DISK_WRITE_SPEED_MBPS}.
+ * Returns if the average disk write bandwidth is slower than threshold {@code
+ * MINIMUM_DISK_WRITE_SPEED_MBPS}.
*/
public boolean isWriteSlow() {
if (mTotalWriteSize < MINIMUM_WRITE_SIZE_FOR_SPEED_CHECK) {
@@ -645,10 +635,10 @@ public class BufferManager {
// Checks write speed for only MAXIMUM_SPEED_CHECK_COUNT times to ignore outliers
// by temporary system overloading during the playback.
- if (mSpeedCheckCount > MAXIMUM_SPEED_CHECK_COUNT) {
+ if (mSpeedCheckCount.get() > MAXIMUM_SPEED_CHECK_COUNT) {
return false;
}
- mSpeedCheckCount++;
+ mSpeedCheckCount.incrementAndGet();
float megabytePerSecond = calculateWriteBandwidth();
resetWriteStat(megabytePerSecond);
if (DEBUG) {
@@ -658,8 +648,8 @@ public class BufferManager {
}
/**
- * Returns recent write bandwidth in MBps. If recent bandwidth is not available,
- * returns {float -1.0f}.
+ * Returns recent write bandwidth in MBps. If recent bandwidth is not available, returns {float
+ * -1.0f}.
*/
public float getWriteBandwidth() {
return mWriteBandwidth == 0.0f ? -1.0f : mWriteBandwidth;
@@ -673,16 +663,17 @@ public class BufferManager {
}
/**
- * Returns if {@link BufferManager} has checked the write speed,
- * which is suitable for Trickplay.
+ * Returns if {@link BufferManager} has checked the write speed, which is suitable for
+ * Trickplay.
*/
@VisibleForTesting
public boolean hasSpeedCheckDone() {
- return mSpeedCheckCount > 0;
+ return mSpeedCheckCount.get() > 0;
}
/**
* Sets minimum sample size for write speed check.
+ *
* @param sampleSize minimum sample size for write speed check.
*/
@VisibleForTesting
diff --git a/src/com/android/tv/tuner/exoplayer/buffer/DvrStorageManager.java b/tuner/src/com/android/tv/tuner/exoplayer/buffer/DvrStorageManager.java
index 6a09016c..2a58ffcf 100644
--- a/src/com/android/tv/tuner/exoplayer/buffer/DvrStorageManager.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/buffer/DvrStorageManager.java
@@ -19,10 +19,8 @@ package com.android.tv.tuner.exoplayer.buffer;
import android.media.MediaFormat;
import android.util.Log;
import android.util.Pair;
-
import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
import com.google.protobuf.nano.MessageNano;
-
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
@@ -36,9 +34,7 @@ import java.util.List;
import java.util.Map;
import java.util.SortedMap;
-/**
- * Manages DVR storage.
- */
+/** Manages DVR storage. */
public class DvrStorageManager implements BufferManager.StorageManager {
private static final String TAG = "DvrStorageManager";
@@ -118,7 +114,7 @@ public class DvrStorageManager implements BufferManager.StorageManager {
if (len <= 0) {
return null;
}
- byte [] strBytes = new byte[len];
+ byte[] strBytes = new byte[len];
in.readFully(strBytes);
return new String(strBytes, StandardCharsets.UTF_8);
}
@@ -147,7 +143,7 @@ public class DvrStorageManager implements BufferManager.StorageManager {
if (len <= 0) {
return null;
}
- byte [] bytes = new byte[len];
+ byte[] bytes = new byte[len];
in.readFully(bytes);
ByteBuffer buffer = ByteBuffer.allocate(len);
buffer.put(bytes);
@@ -170,8 +166,9 @@ public class DvrStorageManager implements BufferManager.StorageManager {
int index = 0;
boolean trackNotFound = false;
do {
- String fileName = (isAudio ? META_FILE_TYPE_AUDIO : META_FILE_TYPE_VIDEO)
- + ((index == 0) ? META_FILE_SUFFIX : (index + META_FILE_SUFFIX));
+ String fileName =
+ (isAudio ? META_FILE_TYPE_AUDIO : META_FILE_TYPE_VIDEO)
+ + ((index == 0) ? META_FILE_SUFFIX : (index + META_FILE_SUFFIX));
File file = new File(getBufferDir(), fileName);
try (DataInputStream in = new DataInputStream(new FileInputStream(file))) {
String name = readString(in);
@@ -195,7 +192,7 @@ public class DvrStorageManager implements BufferManager.StorageManager {
trackNotFound = true;
}
index++;
- } while(!trackNotFound);
+ } while (!trackNotFound);
return trackFormatList;
}
@@ -209,8 +206,9 @@ public class DvrStorageManager implements BufferManager.StorageManager {
int index = 0;
boolean trackNotFound = false;
do {
- String fileName = META_FILE_TYPE_CAPTION +
- ((index == 0) ? META_FILE_SUFFIX : (index + META_FILE_SUFFIX));
+ String fileName =
+ META_FILE_TYPE_CAPTION
+ + ((index == 0) ? META_FILE_SUFFIX : (index + META_FILE_SUFFIX));
File file = new File(getBufferDir(), fileName);
try (DataInputStream in = new DataInputStream(new FileInputStream(file))) {
byte[] data = new byte[(int) file.length()];
@@ -220,7 +218,7 @@ public class DvrStorageManager implements BufferManager.StorageManager {
trackNotFound = true;
}
index++;
- } while(!trackNotFound);
+ } while (!trackNotFound);
return tracks;
}
@@ -259,7 +257,7 @@ public class DvrStorageManager implements BufferManager.StorageManager {
if (file.exists()) {
return readNewIndexFile(file);
} else {
- return readOldIndexFile(new File(getBufferDir(),trackId + IDX_FILE_SUFFIX));
+ return readOldIndexFile(new File(getBufferDir(), trackId + IDX_FILE_SUFFIX));
}
}
@@ -291,7 +289,7 @@ public class DvrStorageManager implements BufferManager.StorageManager {
}
private void writeString(DataOutputStream out, String str) throws IOException {
- byte [] data = str.getBytes(StandardCharsets.UTF_8);
+ byte[] data = str.getBytes(StandardCharsets.UTF_8);
out.writeInt(data.length);
if (data.length > 0) {
out.write(data);
@@ -308,7 +306,7 @@ public class DvrStorageManager implements BufferManager.StorageManager {
}
private void writeByteBuffer(DataOutputStream out, ByteBuffer buffer) throws IOException {
- byte [] data = new byte[buffer.limit()];
+ byte[] data = new byte[buffer.limit()];
buffer.get(data);
buffer.flip();
out.writeInt(data.length);
@@ -331,10 +329,11 @@ public class DvrStorageManager implements BufferManager.StorageManager {
@Override
public void writeTrackInfoFiles(List<BufferManager.TrackFormat> formatList, boolean isAudio)
throws IOException {
- for (int i = 0; i < formatList.size() ; ++i) {
+ for (int i = 0; i < formatList.size(); ++i) {
BufferManager.TrackFormat trackFormat = formatList.get(i);
- String fileName = (isAudio ? META_FILE_TYPE_AUDIO : META_FILE_TYPE_VIDEO)
- + ((i == 0) ? META_FILE_SUFFIX : (i + META_FILE_SUFFIX));
+ String fileName =
+ (isAudio ? META_FILE_TYPE_AUDIO : META_FILE_TYPE_VIDEO)
+ + ((i == 0) ? META_FILE_SUFFIX : (i + META_FILE_SUFFIX));
File file = new File(getBufferDir(), fileName);
try (DataOutputStream out = new DataOutputStream(new FileOutputStream(file))) {
writeString(out, trackFormat.trackId);
@@ -365,8 +364,8 @@ public class DvrStorageManager implements BufferManager.StorageManager {
}
for (int i = 0; i < tracks.size(); i++) {
AtscCaptionTrack track = tracks.get(i);
- String fileName = META_FILE_TYPE_CAPTION +
- ((i == 0) ? META_FILE_SUFFIX : (i + META_FILE_SUFFIX));
+ String fileName =
+ META_FILE_TYPE_CAPTION + ((i == 0) ? META_FILE_SUFFIX : (i + META_FILE_SUFFIX));
File file = new File(getBufferDir(), fileName);
try (DataOutputStream out = new DataOutputStream(new FileOutputStream(file))) {
out.write(MessageNano.toByteArray(track));
@@ -379,7 +378,7 @@ public class DvrStorageManager implements BufferManager.StorageManager {
@Override
public void writeIndexFile(String trackName, SortedMap<Long, Pair<SampleChunk, Integer>> index)
throws IOException {
- File indexFile = new File(getBufferDir(), trackName + IDX_FILE_SUFFIX_V2);
+ File indexFile = new File(getBufferDir(), trackName + IDX_FILE_SUFFIX_V2);
try (DataOutputStream out = new DataOutputStream(new FileOutputStream(indexFile))) {
out.writeLong(index.size());
for (Map.Entry<Long, Pair<SampleChunk, Integer>> entry : index.entrySet()) {
diff --git a/src/com/android/tv/tuner/exoplayer/buffer/RecordingSampleBuffer.java b/tuner/src/com/android/tv/tuner/exoplayer/buffer/RecordingSampleBuffer.java
index af0c3f0d..ebf00f59 100644
--- a/src/com/android/tv/tuner/exoplayer/buffer/RecordingSampleBuffer.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/buffer/RecordingSampleBuffer.java
@@ -20,16 +20,14 @@ import android.os.ConditionVariable;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.util.Log;
-
+import com.android.tv.tuner.exoplayer.MpegTsPlayer;
+import com.android.tv.tuner.exoplayer.SampleExtractor;
+import com.android.tv.tuner.tvinput.PlaybackBufferListener;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.util.Assertions;
-import com.android.tv.tuner.exoplayer.MpegTsPlayer;
-import com.android.tv.tuner.tvinput.PlaybackBufferListener;
-import com.android.tv.tuner.exoplayer.SampleExtractor;
-
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -38,43 +36,33 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
/**
- * Handles I/O between {@link SampleExtractor} and
- * {@link BufferManager}.Reads & writes samples from/to {@link SampleChunk} which is backed
- * by physical storage.
+ * Handles I/O between {@link SampleExtractor} and {@link BufferManager}.Reads & writes samples
+ * from/to {@link SampleChunk} which is backed by physical storage.
*/
-public class RecordingSampleBuffer implements BufferManager.SampleBuffer,
- BufferManager.ChunkEvictedListener {
+public class RecordingSampleBuffer
+ implements BufferManager.SampleBuffer, BufferManager.ChunkEvictedListener {
private static final String TAG = "RecordingSampleBuffer";
@IntDef({BUFFER_REASON_LIVE_PLAYBACK, BUFFER_REASON_RECORDED_PLAYBACK, BUFFER_REASON_RECORDING})
@Retention(RetentionPolicy.SOURCE)
public @interface BufferReason {}
- /**
- * A buffer reason for live-stream playback.
- */
+ /** A buffer reason for live-stream playback. */
public static final int BUFFER_REASON_LIVE_PLAYBACK = 0;
- /**
- * A buffer reason for playback of a recorded program.
- */
+ /** A buffer reason for playback of a recorded program. */
public static final int BUFFER_REASON_RECORDED_PLAYBACK = 1;
- /**
- * A buffer reason for recording a program.
- */
+ /** A buffer reason for recording a program. */
public static final int BUFFER_REASON_RECORDING = 2;
- /**
- * The minimum duration to support seek in Trickplay.
- */
+ /** The minimum duration to support seek in Trickplay. */
static final long MIN_SEEK_DURATION_US = TimeUnit.MILLISECONDS.toMicros(500);
- /**
- * The duration of a {@link SampleChunk} for recordings.
- */
+ /** The duration of a {@link SampleChunk} for recordings. */
static final long RECORDING_CHUNK_DURATION_US = MIN_SEEK_DURATION_US * 1200; // 10 minutes
- private static final long BUFFER_WRITE_TIMEOUT_MS = 10 * 1000; // 10 seconds
+
+ private static final long BUFFER_WRITE_TIMEOUT_MS = 10 * 1000; // 10 seconds
private static final long BUFFER_NEEDED_US =
1000L * Math.max(MpegTsPlayer.MIN_BUFFER_MS, MpegTsPlayer.MIN_REBUFFER_MS);
@@ -97,28 +85,31 @@ public class RecordingSampleBuffer implements BufferManager.SampleBuffer,
private SampleChunkIoHelper mSampleChunkIoHelper;
private final SampleChunkIoHelper.IoCallback mIoCallback =
new SampleChunkIoHelper.IoCallback() {
- @Override
- public void onIoReachedEos() {
- mEos = true;
- }
+ @Override
+ public void onIoReachedEos() {
+ mEos = true;
+ }
- @Override
- public void onIoError() {
- mError = true;
- }
- };
+ @Override
+ public void onIoError() {
+ mError = true;
+ }
+ };
/**
- * Creates {@link BufferManager.SampleBuffer} with
- * cached I/O backed by physical storage (e.g. trickplay,recording,recorded-playback).
+ * Creates {@link BufferManager.SampleBuffer} with cached I/O backed by physical storage (e.g.
+ * trickplay,recording,recorded-playback).
*
* @param bufferManager the manager of {@link SampleChunk}
* @param bufferListener the listener for buffer I/O event
* @param enableTrickplay {@code true} when trickplay should be enabled
* @param bufferReason the reason for caching samples {@link RecordingSampleBuffer.BufferReason}
*/
- public RecordingSampleBuffer(BufferManager bufferManager, PlaybackBufferListener bufferListener,
- boolean enableTrickplay, @BufferReason int bufferReason) {
+ public RecordingSampleBuffer(
+ BufferManager bufferManager,
+ PlaybackBufferListener bufferListener,
+ boolean enableTrickplay,
+ @BufferReason int bufferReason) {
mBufferManager = bufferManager;
mBufferListener = bufferListener;
if (bufferListener != null) {
@@ -136,8 +127,9 @@ public class RecordingSampleBuffer implements BufferManager.SampleBuffer,
}
mTrackSelected = new boolean[mTrackCount];
mReadSampleQueues = new ArrayList<>();
- mSampleChunkIoHelper = new SampleChunkIoHelper(ids, mediaFormats, mBufferReason,
- mBufferManager, mSamplePool, mIoCallback);
+ mSampleChunkIoHelper =
+ new SampleChunkIoHelper(
+ ids, mediaFormats, mBufferReason, mBufferManager, mSamplePool, mIoCallback);
for (int i = 0; i < mTrackCount; ++i) {
mReadSampleQueues.add(i, new SampleQueue(mSamplePool));
}
@@ -186,13 +178,16 @@ public class RecordingSampleBuffer implements BufferManager.SampleBuffer,
}
@Override
- public void handleWriteSpeedSlow() throws IOException{
+ public void handleWriteSpeedSlow() throws IOException {
if (mBufferReason == BUFFER_REASON_RECORDING) {
// Recording does not need to stop because I/O speed is slow temporarily.
// If fixed size buffer of TsStreamer overflows, TsDataSource will reach EoS.
// Reaching EoS will stop recording eventually.
- Log.w(TAG, "Disk I/O speed is slow for recording temporarily: "
- + mBufferManager.getWriteBandwidth() + "MBps");
+ Log.w(
+ TAG,
+ "Disk I/O speed is slow for recording temporarily: "
+ + mBufferManager.getWriteBandwidth()
+ + "MBps");
return;
}
// Disables buffering samples afterwards, and notifies the disk speed is slow.
@@ -253,8 +248,7 @@ public class RecordingSampleBuffer implements BufferManager.SampleBuffer,
if (!mTrackSelected[i]) {
continue;
}
- Long lastQueuedSamplePositionUs =
- mReadSampleQueues.get(i).getLastQueuedPositionUs();
+ Long lastQueuedSamplePositionUs = mReadSampleQueues.get(i).getLastQueuedPositionUs();
if (lastQueuedSamplePositionUs == null) {
// No sample has been queued.
result = mLastBufferedPositionUs;
diff --git a/src/com/android/tv/tuner/exoplayer/buffer/SampleChunk.java b/tuner/src/com/android/tv/tuner/exoplayer/buffer/SampleChunk.java
index 04b5a071..bf77a6eb 100644
--- a/src/com/android/tv/tuner/exoplayer/buffer/SampleChunk.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/buffer/SampleChunk.java
@@ -19,18 +19,16 @@ package com.android.tv.tuner.exoplayer.buffer;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.util.Log;
-
import com.google.android.exoplayer.SampleHolder;
-
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
/**
- * {@link SampleChunk} stores samples into file and makes them available for read.
- * Stored file = { Header, Sample } * N
- * Header = sample size : int, sample flag : int, sample PTS in micro second : long
+ * {@link SampleChunk} stores samples into file and makes them available for read. Stored file = {
+ * Header, Sample } * N Header = sample size : int, sample flag : int, sample PTS in micro second :
+ * long
*/
public class SampleChunk {
private static final String TAG = "SampleChunk";
@@ -52,32 +50,25 @@ public class SampleChunk {
private boolean mIsReading;
private boolean mIsWriting;
- /**
- * A callback for chunks being committed to permanent storage.
- */
- public static abstract class ChunkCallback {
+ /** A callback for chunks being committed to permanent storage. */
+ public abstract static class ChunkCallback {
/**
* Notifies when writing a SampleChunk is completed.
*
* @param chunk SampleChunk which is written completely
*/
- public void onChunkWrite(SampleChunk chunk) {
-
- }
+ public void onChunkWrite(SampleChunk chunk) {}
/**
* Notifies when a SampleChunk is deleted.
*
* @param chunk SampleChunk which is deleted from storage
*/
- public void onChunkDelete(SampleChunk chunk) {
- }
+ public void onChunkDelete(SampleChunk chunk) {}
}
- /**
- * A class for SampleChunk creation.
- */
+ /** A class for SampleChunk creation. */
public static class SampleChunkCreator {
/**
@@ -88,15 +79,19 @@ public class SampleChunk {
* @param startPositionUs the start position of the earliest sample to be stored
* @param chunkCallback for total storage usage change notification
*/
- SampleChunk createSampleChunk(SamplePool samplePool, File file,
- long startPositionUs, ChunkCallback chunkCallback) {
- return new SampleChunk(samplePool, file, startPositionUs, System.currentTimeMillis(),
- chunkCallback);
+ @VisibleForTesting
+ public SampleChunk createSampleChunk(
+ SamplePool samplePool,
+ File file,
+ long startPositionUs,
+ ChunkCallback chunkCallback) {
+ return new SampleChunk(
+ samplePool, file, startPositionUs, System.currentTimeMillis(), chunkCallback);
}
/**
- * Returns a newly created SampleChunk which is backed by an existing file.
- * Created SampleChunk is read-only.
+ * Returns a newly created SampleChunk which is backed by an existing file. Created
+ * SampleChunk is read-only.
*
* @param samplePool sample allocator
* @param bufferDir the directory where the file to read is located
@@ -106,12 +101,16 @@ public class SampleChunk {
* @param prev the previous SampleChunk just before the newly created SampleChunk
* @throws IOException
*/
- SampleChunk loadSampleChunkFromFile(SamplePool samplePool, File bufferDir,
- String filename, long startPositionUs, ChunkCallback chunkCallback,
- SampleChunk prev) throws IOException {
+ SampleChunk loadSampleChunkFromFile(
+ SamplePool samplePool,
+ File bufferDir,
+ String filename,
+ long startPositionUs,
+ ChunkCallback chunkCallback,
+ SampleChunk prev)
+ throws IOException {
File file = new File(bufferDir, filename);
- SampleChunk chunk =
- new SampleChunk(samplePool, file, startPositionUs, chunkCallback);
+ SampleChunk chunk = new SampleChunk(samplePool, file, startPositionUs, chunkCallback);
if (prev != null) {
prev.mNextChunk = chunk;
}
@@ -120,10 +119,11 @@ public class SampleChunk {
}
/**
- * Handles I/O for SampleChunk.
- * Maintains current SampleChunk and the current offset for next I/O operation.
+ * Handles I/O for SampleChunk. Maintains current SampleChunk and the current offset for next
+ * I/O operation.
*/
- static class IoState {
+ @VisibleForTesting
+ public static class IoState {
private SampleChunk mChunk;
private long mCurrentOffset;
@@ -131,16 +131,12 @@ public class SampleChunk {
return chunk == mChunk && mCurrentOffset == offset;
}
- /**
- * Returns whether read I/O operation is finished.
- */
+ /** Returns whether read I/O operation is finished. */
boolean isReadFinished() {
return mChunk == null;
}
- /**
- * Returns the start position of the current SampleChunk
- */
+ /** Returns the start position of the current SampleChunk */
long getStartPositionUs() {
return mChunk == null ? 0 : mChunk.getStartPositionUs();
}
@@ -175,7 +171,7 @@ public class SampleChunk {
* @param chunk the new SampleChunk to write samples afterwards
* @throws IOException
*/
- void openWrite(SampleChunk chunk) throws IOException{
+ void openWrite(SampleChunk chunk) throws IOException {
if (mChunk != null) {
mChunk.closeWrite(chunk);
}
@@ -215,14 +211,16 @@ public class SampleChunk {
* Writes a sample.
*
* @param sample to write
- * @param nextChunk if this is {@code null} writes at the current SampleChunk,
- * otherwise close current SampleChunk and writes at this
+ * @param nextChunk if this is {@code null} writes at the current SampleChunk, otherwise
+ * close current SampleChunk and writes at this
* @throws IOException
*/
- void write(SampleHolder sample, SampleChunk nextChunk)
- throws IOException {
+ void write(SampleHolder sample, SampleChunk nextChunk) throws IOException {
+ if (mChunk == null) {
+ throw new IOException("mChunk should not be null");
+ }
if (nextChunk != null) {
- if (mChunk == null || mChunk.mNextChunk != null) {
+ if (mChunk.mNextChunk != null) {
throw new IllegalStateException("Requested write for wrong SampleChunk");
}
mChunk.closeWrite(nextChunk);
@@ -244,16 +242,12 @@ public class SampleChunk {
}
}
- /**
- * Returns the current SampleChunk for subsequent I/O operation.
- */
+ /** Returns the current SampleChunk for subsequent I/O operation. */
SampleChunk getChunk() {
return mChunk;
}
- /**
- * Returns the current offset of the current SampleChunk for subsequent I/O operation.
- */
+ /** Returns the current offset of the current SampleChunk for subsequent I/O operation. */
long getOffset() {
return mCurrentOffset;
}
@@ -262,8 +256,8 @@ public class SampleChunk {
* Releases SampleChunk. the SampleChunk will not be used anymore.
*
* @param chunk to release
- * @param delete {@code true} when the backed file needs to be deleted,
- * {@code false} otherwise.
+ * @param delete {@code true} when the backed file needs to be deleted, {@code false}
+ * otherwise.
*/
static void release(SampleChunk chunk, boolean delete) {
chunk.release(delete);
@@ -271,8 +265,12 @@ public class SampleChunk {
}
@VisibleForTesting
- protected SampleChunk(SamplePool samplePool, File file, long startPositionUs,
- long createdTimeMs, ChunkCallback chunkCallback) {
+ protected SampleChunk(
+ SamplePool samplePool,
+ File file,
+ long startPositionUs,
+ long createdTimeMs,
+ ChunkCallback chunkCallback) {
mStartPositionUs = startPositionUs;
mCreatedTimeMs = createdTimeMs;
mSamplePool = samplePool;
@@ -281,8 +279,9 @@ public class SampleChunk {
}
// Constructor of SampleChunk which is backed by the given existing file.
- private SampleChunk(SamplePool samplePool, File file, long startPositionUs,
- ChunkCallback chunkCallback) throws IOException {
+ private SampleChunk(
+ SamplePool samplePool, File file, long startPositionUs, ChunkCallback chunkCallback)
+ throws IOException {
mStartPositionUs = startPositionUs;
mCreatedTimeMs = mStartPositionUs / 1000;
mSamplePool = samplePool;
@@ -311,8 +310,8 @@ public class SampleChunk {
}
if (!mIsWriting) {
if (mIsReading) {
- throw new IllegalStateException("Write is requested for "
- + "an already opened SampleChunk");
+ throw new IllegalStateException(
+ "Write is requested for " + "an already opened SampleChunk");
}
mAccessFile = new RandomAccessFile(mFile, "rw");
mIsWriting = true;
@@ -331,15 +330,14 @@ public class SampleChunk {
}
}
- private void closeRead() throws IOException{
+ private void closeRead() throws IOException {
if (mIsReading) {
mIsReading = false;
CloseAccessFileIfNeeded();
}
}
- private void closeWrite(SampleChunk nextChunk)
- throws IOException {
+ private void closeWrite(SampleChunk nextChunk) throws IOException {
if (mIsWriting) {
mNextChunk = nextChunk;
mIsWriting = false;
@@ -374,16 +372,20 @@ public class SampleChunk {
sample.flags = mAccessFile.readInt();
sample.timeUs = mAccessFile.readLong();
sample.clearData();
- sample.data.put(mAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY,
- offset + SAMPLE_HEADER_LENGTH, sample.size));
+ sample.data.put(
+ mAccessFile
+ .getChannel()
+ .map(
+ FileChannel.MapMode.READ_ONLY,
+ offset + SAMPLE_HEADER_LENGTH,
+ sample.size));
offset += sample.size + SAMPLE_HEADER_LENGTH;
state.mCurrentOffset = offset;
return sample;
}
@VisibleForTesting
- protected void write(SampleHolder sample, IoState state)
- throws IOException {
+ protected void write(SampleHolder sample, IoState state) throws IOException {
if (mAccessFile == null || mNextChunk != null || !state.equals(this, mWriteOffset)) {
throw new IllegalStateException("Requested write for wrong SampleChunk");
}
@@ -414,23 +416,17 @@ public class SampleChunk {
}
}
- /**
- * Returns the start position.
- */
+ /** Returns the start position. */
public long getStartPositionUs() {
return mStartPositionUs;
}
- /**
- * Returns the creation time.
- */
+ /** Returns the creation time. */
public long getCreatedTimeMs() {
return mCreatedTimeMs;
}
- /**
- * Returns the current size.
- */
+ /** Returns the current size. */
public long getSize() {
return mWriteOffset;
}
diff --git a/src/com/android/tv/tuner/exoplayer/buffer/SampleChunkIoHelper.java b/tuner/src/com/android/tv/tuner/exoplayer/buffer/SampleChunkIoHelper.java
index ca97a91a..d95d0adb 100644
--- a/src/com/android/tv/tuner/exoplayer/buffer/SampleChunkIoHelper.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/buffer/SampleChunkIoHelper.java
@@ -24,13 +24,11 @@ import android.os.Message;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
-
+import com.android.tv.common.SoftPreconditions;
+import com.android.tv.tuner.exoplayer.buffer.RecordingSampleBuffer.BufferReason;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.util.MimeTypes;
-import com.android.tv.common.SoftPreconditions;
-import com.android.tv.tuner.exoplayer.buffer.RecordingSampleBuffer.BufferReason;
-
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
@@ -38,8 +36,8 @@ import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
- * Handles all {@link SampleChunk} I/O operations.
- * An I/O dedicated thread handles all I/O operations for synchronization.
+ * Handles all {@link SampleChunk} I/O operations. An I/O dedicated thread handles all I/O
+ * operations for synchronization.
*/
public class SampleChunkIoHelper implements Handler.Callback {
private static final String TAG = "SampleChunkIoHelper";
@@ -77,32 +75,27 @@ public class SampleChunkIoHelper implements Handler.Callback {
private boolean mErrorNotified;
private boolean mFinished;
- /**
- * A Callback for I/O events.
- */
- public static abstract class IoCallback {
+ /** A Callback for I/O events. */
+ public abstract static class IoCallback {
- /**
- * Called when there is no sample to read.
- */
- public void onIoReachedEos() {
- }
+ /** Called when there is no sample to read. */
+ public void onIoReachedEos() {}
- /**
- * Called when there is an irrecoverable error during I/O.
- */
- public void onIoError() {
- }
+ /** Called when there is an irrecoverable error during I/O. */
+ public void onIoError() {}
}
- private class IoParams {
+ private static class IoParams {
private final int index;
private final long positionUs;
private final SampleHolder sample;
private final ConditionVariable conditionVariable;
private final ConcurrentLinkedQueue<SampleHolder> readSampleBuffer;
- private IoParams(int index, long positionUs, SampleHolder sample,
+ private IoParams(
+ int index,
+ long positionUs,
+ SampleHolder sample,
ConditionVariable conditionVariable,
ConcurrentLinkedQueue<SampleHolder> readSampleBuffer) {
this.index = index;
@@ -123,8 +116,12 @@ public class SampleChunkIoHelper implements Handler.Callback {
* @param samplePool allocator for a sample
* @param ioCallback listeners for I/O events
*/
- public SampleChunkIoHelper(List<String> ids, List<MediaFormat> mediaFormats,
- @BufferReason int bufferReason, BufferManager bufferManager, SamplePool samplePool,
+ public SampleChunkIoHelper(
+ List<String> ids,
+ List<MediaFormat> mediaFormats,
+ @BufferReason int bufferReason,
+ BufferManager bufferManager,
+ SamplePool samplePool,
IoCallback ioCallback) {
mTrackCount = ids.size();
mIds = ids;
@@ -144,9 +141,9 @@ public class SampleChunkIoHelper implements Handler.Callback {
// Small chunk duration for live playback will give more fine grained storage usage
// and eviction handling for trickplay.
mSampleChunkDurationUs =
- bufferReason == RecordingSampleBuffer.BUFFER_REASON_LIVE_PLAYBACK ?
- RecordingSampleBuffer.MIN_SEEK_DURATION_US :
- RecordingSampleBuffer.RECORDING_CHUNK_DURATION_US;
+ bufferReason == RecordingSampleBuffer.BUFFER_REASON_LIVE_PLAYBACK
+ ? RecordingSampleBuffer.MIN_SEEK_DURATION_US
+ : RecordingSampleBuffer.RECORDING_CHUNK_DURATION_US;
for (int i = 0; i < mTrackCount; ++i) {
mWriteIndexEndPositionUs[i] = RecordingSampleBuffer.MIN_SEEK_DURATION_US;
mWriteChunkEndPositionUs[i] = mSampleChunkDurationUs;
@@ -196,8 +193,8 @@ public class SampleChunkIoHelper implements Handler.Callback {
* @param conditionVariable which will be wait until the write is finished
* @throws IOException
*/
- public void writeSample(int index, SampleHolder sample,
- ConditionVariable conditionVariable) throws IOException {
+ public void writeSample(int index, SampleHolder sample, ConditionVariable conditionVariable)
+ throws IOException {
if (mErrorNotified) {
throw new IOException("Storage I/O error happened");
}
@@ -228,15 +225,14 @@ public class SampleChunkIoHelper implements Handler.Callback {
mIoHandler.sendMessage(mIoHandler.obtainMessage(MSG_CLOSE_READ, index));
}
- /**
- * Notifies writes are finished.
- */
+ /** Notifies writes are finished. */
public void closeWrite() {
mIoHandler.sendEmptyMessage(MSG_CLOSE_WRITE);
}
/**
* Finishes I/O operations and releases all the resources.
+ *
* @throws IOException
*/
public void release() throws IOException {
@@ -320,8 +316,8 @@ public class SampleChunkIoHelper implements Handler.Callback {
Pair<SampleChunk, Integer> readPosition =
mBufferManager.getReadFile(mIds.get(index), params.positionUs);
if (readPosition == null) {
- String errorMessage = "Chunk ID:" + mIds.get(index) + " pos:" + params.positionUs
- + "is not found";
+ String errorMessage =
+ "Chunk ID:" + mIds.get(index) + " pos:" + params.positionUs + "is not found";
SoftPreconditions.checkNotNull(readPosition, TAG, errorMessage);
throw new IOException(errorMessage);
}
@@ -338,8 +334,8 @@ public class SampleChunkIoHelper implements Handler.Callback {
}
private void doOpenWrite(int index) throws IOException {
- SampleChunk chunk = mBufferManager.createNewWriteFileIfNeeded(mIds.get(index), 0,
- mSamplePool, null, 0);
+ SampleChunk chunk =
+ mBufferManager.createNewWriteFileIfNeeded(mIds.get(index), 0, mSamplePool, null, 0);
mWriteIoStates[index].openWrite(chunk);
}
@@ -378,8 +374,7 @@ public class SampleChunkIoHelper implements Handler.Callback {
// Read reached write but write is not finished yet --- wait a few moments to
// see if another sample is written.
mIoHandler.sendMessageDelayed(
- mIoHandler.obtainMessage(MSG_READ, index),
- READ_RESCHEDULING_DELAY_MS);
+ mIoHandler.obtainMessage(MSG_READ, index), READ_RESCHEDULING_DELAY_MS);
}
}
}
@@ -398,15 +393,21 @@ public class SampleChunkIoHelper implements Handler.Callback {
mBufferDurationUs = sample.timeUs;
}
if (sample.timeUs >= mWriteIndexEndPositionUs[index]) {
- SampleChunk currentChunk = sample.timeUs >= mWriteChunkEndPositionUs[index] ?
- null : mWriteIoStates[params.index].getChunk();
+ SampleChunk currentChunk =
+ sample.timeUs >= mWriteChunkEndPositionUs[index]
+ ? null
+ : mWriteIoStates[params.index].getChunk();
int currentOffset = (int) mWriteIoStates[params.index].getOffset();
- nextChunk = mBufferManager.createNewWriteFileIfNeeded(
- mIds.get(index), mWriteIndexEndPositionUs[index], mSamplePool,
- currentChunk, currentOffset);
+ nextChunk =
+ mBufferManager.createNewWriteFileIfNeeded(
+ mIds.get(index),
+ mWriteIndexEndPositionUs[index],
+ mSamplePool,
+ currentChunk,
+ currentOffset);
mWriteIndexEndPositionUs[index] =
- ((sample.timeUs / RecordingSampleBuffer.MIN_SEEK_DURATION_US) + 1) *
- RecordingSampleBuffer.MIN_SEEK_DURATION_US;
+ ((sample.timeUs / RecordingSampleBuffer.MIN_SEEK_DURATION_US) + 1)
+ * RecordingSampleBuffer.MIN_SEEK_DURATION_US;
if (nextChunk != null) {
mWriteChunkEndPositionUs[index] =
((sample.timeUs / mSampleChunkDurationUs) + 1)
@@ -449,13 +450,15 @@ public class SampleChunkIoHelper implements Handler.Callback {
}
long currentStartPositionUs = Long.MAX_VALUE;
for (int trackIndex : mSelectedTracks) {
- currentStartPositionUs = Math.min(currentStartPositionUs,
- mReadIoStates[trackIndex].getStartPositionUs());
+ currentStartPositionUs =
+ Math.min(
+ currentStartPositionUs, mReadIoStates[trackIndex].getStartPositionUs());
}
for (int i = 0; i < mTrackCount; ++i) {
- long evictEndPositionUs = Math.min(mBufferManager.getStartPositionUs(mIds.get(i)),
- currentStartPositionUs);
+ long evictEndPositionUs =
+ Math.min(
+ mBufferManager.getStartPositionUs(mIds.get(i)), currentStartPositionUs);
mBufferManager.evictChunks(mIds.get(i), evictEndPositionUs);
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/tuner/exoplayer/buffer/SamplePool.java b/tuner/src/com/android/tv/tuner/exoplayer/buffer/SamplePool.java
index bb048e85..b89a14db 100644
--- a/src/com/android/tv/tuner/exoplayer/buffer/SamplePool.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/buffer/SamplePool.java
@@ -17,18 +17,15 @@
package com.android.tv.tuner.exoplayer.buffer;
import com.google.android.exoplayer.SampleHolder;
-
import java.util.LinkedList;
-/**
- * Pool of samples to recycle ByteBuffers as much as possible.
- */
+/** Pool of samples to recycle ByteBuffers as much as possible. */
public class SamplePool {
private final LinkedList<SampleHolder> mSamplePool = new LinkedList<>();
/**
- * Acquires a sample with a buffer larger than size from the pool. Allocate new one or resize
- * an existing buffer if necessary.
+ * Acquires a sample with a buffer larger than size from the pool. Allocate new one or resize an
+ * existing buffer if necessary.
*/
public synchronized SampleHolder acquireSample(int size) {
if (mSamplePool.isEmpty()) {
@@ -40,8 +37,9 @@ public class SamplePool {
SampleHolder maxSample = mSamplePool.getFirst();
for (SampleHolder sample : mSamplePool) {
// Grab the smallest sufficient sample.
- if (sample.data.capacity() >= size && (smallestSufficientSample == null
- || smallestSufficientSample.data.capacity() > sample.data.capacity())) {
+ if (sample.data.capacity() >= size
+ && (smallestSufficientSample == null
+ || smallestSufficientSample.data.capacity() > sample.data.capacity())) {
smallestSufficientSample = sample;
}
@@ -61,9 +59,7 @@ public class SamplePool {
return sampleFromPool;
}
- /**
- * Releases the sample back to the pool.
- */
+ /** Releases the sample back to the pool. */
public synchronized void releaseSample(SampleHolder sample) {
sample.clearData();
mSamplePool.offerLast(sample);
diff --git a/src/com/android/tv/tuner/exoplayer/buffer/SampleQueue.java b/tuner/src/com/android/tv/tuner/exoplayer/buffer/SampleQueue.java
index 75eac5a2..e208f2c2 100644
--- a/src/com/android/tv/tuner/exoplayer/buffer/SampleQueue.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/buffer/SampleQueue.java
@@ -18,12 +18,9 @@ package com.android.tv.tuner.exoplayer.buffer;
import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource;
-
import java.util.LinkedList;
-/**
- * A sample queue which reads from the buffer and passes to player pipeline.
- */
+/** A sample queue which reads from the buffer and passes to player pipeline. */
public class SampleQueue {
private final LinkedList<SampleHolder> mQueue = new LinkedList<>();
private final SamplePool mSamplePool;
diff --git a/src/com/android/tv/tuner/exoplayer/buffer/SimpleSampleBuffer.java b/tuner/src/com/android/tv/tuner/exoplayer/buffer/SimpleSampleBuffer.java
index 159fde18..4c6260bf 100644
--- a/src/com/android/tv/tuner/exoplayer/buffer/SimpleSampleBuffer.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/buffer/SimpleSampleBuffer.java
@@ -17,23 +17,20 @@
package com.android.tv.tuner.exoplayer.buffer;
import android.os.ConditionVariable;
-
import android.support.annotation.NonNull;
-
+import com.android.tv.common.SoftPreconditions;
+import com.android.tv.tuner.exoplayer.SampleExtractor;
+import com.android.tv.tuner.tvinput.PlaybackBufferListener;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource;
-import com.android.tv.common.SoftPreconditions;
-import com.android.tv.tuner.tvinput.PlaybackBufferListener;
-import com.android.tv.tuner.exoplayer.SampleExtractor;
-
import java.io.IOException;
import java.util.List;
/**
- * Handles I/O for {@link SampleExtractor} when
- * physical storage based buffer is not used. Trickplay is disabled.
+ * Handles I/O for {@link SampleExtractor} when physical storage based buffer is not used. Trickplay
+ * is disabled.
*/
public class SimpleSampleBuffer implements BufferManager.SampleBuffer {
private final SamplePool mSamplePool = new SamplePool();
@@ -50,8 +47,8 @@ public class SimpleSampleBuffer implements BufferManager.SampleBuffer {
}
@Override
- public synchronized void init(@NonNull List<String> ids,
- @NonNull List<MediaFormat> mediaFormats) {
+ public synchronized void init(
+ @NonNull List<String> ids, @NonNull List<MediaFormat> mediaFormats) {
int trackCount = ids.size();
mPlayingSampleQueues = new SampleQueue[trackCount];
for (int i = 0; i < trackCount; i++) {
@@ -124,8 +121,8 @@ public class SimpleSampleBuffer implements BufferManager.SampleBuffer {
}
@Override
- public void writeSample(int index, SampleHolder sample,
- ConditionVariable conditionVariable) throws IOException {
+ 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;
diff --git a/src/com/android/tv/tuner/exoplayer/buffer/TrickplayStorageManager.java b/tuner/src/com/android/tv/tuner/exoplayer/buffer/TrickplayStorageManager.java
index 9fe921b8..b22b8af1 100644
--- a/src/com/android/tv/tuner/exoplayer/buffer/TrickplayStorageManager.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/buffer/TrickplayStorageManager.java
@@ -21,26 +21,21 @@ import android.os.AsyncTask;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.util.Pair;
-
import com.android.tv.common.SoftPreconditions;
-
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedMap;
-/**
- * Manages Trickplay storage.
- */
+/** Manages Trickplay storage. */
public class TrickplayStorageManager implements BufferManager.StorageManager {
// TODO: Support multi-sessions.
private static final String BUFFER_DIR = "timeshift";
// Copied from android.provider.Settings.Global (hidden fields)
- private static final String
- SYS_STORAGE_THRESHOLD_PERCENTAGE = "sys_storage_threshold_percentage";
- private static final String
- SYS_STORAGE_THRESHOLD_MAX_BYTES = "sys_storage_threshold_max_bytes";
+ private static final String SYS_STORAGE_THRESHOLD_PERCENTAGE =
+ "sys_storage_threshold_percentage";
+ private static final String SYS_STORAGE_THRESHOLD_MAX_BYTES = "sys_storage_threshold_max_bytes";
// Copied from android.os.StorageManager
private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10;
@@ -54,17 +49,22 @@ public class TrickplayStorageManager implements BufferManager.StorageManager {
private static void initParamsIfNeeded(Context context, @NonNull File path) {
// TODO: Support multi-sessions.
- SoftPreconditions.checkState(
- sBufferDir == null || sBufferDir.equals(path));
+ SoftPreconditions.checkState(sBufferDir == null || sBufferDir.equals(path));
if (path.equals(sBufferDir)) {
return;
}
sBufferDir = path;
- long lowPercentage = Settings.Global.getInt(context.getContentResolver(),
- SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE);
+ long lowPercentage =
+ Settings.Global.getInt(
+ context.getContentResolver(),
+ SYS_STORAGE_THRESHOLD_PERCENTAGE,
+ DEFAULT_THRESHOLD_PERCENTAGE);
long lowPercentageToBytes = path.getTotalSpace() * lowPercentage / 100;
- long maxLowBytes = Settings.Global.getLong(context.getContentResolver(),
- SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES);
+ long maxLowBytes =
+ Settings.Global.getLong(
+ context.getContentResolver(),
+ SYS_STORAGE_THRESHOLD_MAX_BYTES,
+ DEFAULT_THRESHOLD_MAX_BYTES);
sStorageBufferBytes = Math.min(lowPercentageToBytes, maxLowBytes);
}
@@ -80,28 +80,29 @@ public class TrickplayStorageManager implements BufferManager.StorageManager {
if (sLastCacheCleanUpTask != null) {
sLastCacheCleanUpTask.cancel(true);
}
- sLastCacheCleanUpTask = new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- if (isCancelled()) {
- return null;
- }
- File files[] = sBufferDir.listFiles();
- if (files == null || files.length == 0) {
- return null;
- }
- for (File file : files) {
- if (isCancelled()) {
- break;
- }
- long lastModified = file.lastModified();
- if (lastModified != 0 && lastModified < now) {
- file.delete();
+ sLastCacheCleanUpTask =
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ if (isCancelled()) {
+ return null;
+ }
+ File files[] = sBufferDir.listFiles();
+ if (files == null || files.length == 0) {
+ return null;
+ }
+ for (File file : files) {
+ if (isCancelled()) {
+ break;
+ }
+ long lastModified = file.lastModified();
+ if (lastModified != 0 && lastModified < now) {
+ file.delete();
+ }
+ }
+ return null;
}
- }
- return null;
- }
- };
+ };
sLastCacheCleanUpTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@@ -136,12 +137,9 @@ public class TrickplayStorageManager implements BufferManager.StorageManager {
}
@Override
- public void writeTrackInfoFiles(List<BufferManager.TrackFormat> formatList, boolean isAudio) {
- }
+ public void writeTrackInfoFiles(List<BufferManager.TrackFormat> formatList, boolean isAudio) {}
@Override
- public void writeIndexFile(String trackName,
- SortedMap<Long, Pair<SampleChunk, Integer>> index) {
- }
-
+ public void writeIndexFile(
+ String trackName, SortedMap<Long, Pair<SampleChunk, Integer>> index) {}
}
diff --git a/src/com/android/exoplayer/text/SubtitleView.java b/tuner/src/com/android/tv/tuner/exoplayer/text/SubtitleView.java
index 37926eda..91bee7a0 100644
--- a/src/com/android/exoplayer/text/SubtitleView.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/text/SubtitleView.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.google.android.exoplayer.text;
+package com.android.tv.tuner.exoplayer.text;
import android.annotation.TargetApi;
import android.content.Context;
@@ -32,27 +32,22 @@ import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
-
+import com.google.android.exoplayer.text.CaptionStyleCompat;
import com.google.android.exoplayer.util.Util;
-
import java.util.ArrayList;
+import java.util.Objects;
/**
- * Since this class does not exist in recent version of ExoPlayer and used by
- * {@link com.android.tv.tuner.cc.CaptionWindowLayout}, this class is copied from
- * older version of ExoPlayer.
- * A view for rendering a single caption.
+ * Since this class does not exist in recent version of ExoPlayer and used by {@link
+ * com.android.tv.tuner.cc.CaptionWindowLayout}, this class is copied from older version of
+ * ExoPlayer. A view for rendering a single caption.
*/
@Deprecated
public class SubtitleView extends View {
- /**
- * Ratio of inner padding to font size.
- */
+ /** Ratio of inner padding to font size. */
private static final float INNER_PADDING_RATIO = 0.125f;
- /**
- * Temporary rectangle used for computing line bounds.
- */
+ /** Temporary rectangle used for computing line bounds. */
private final RectF mLineBounds = new RectF();
// Styled dimensions.
@@ -93,8 +88,12 @@ public class SubtitleView extends View {
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};
+ 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);
@@ -189,7 +188,7 @@ public class SubtitleView extends View {
}
private void setTypeface(Typeface typeface) {
- if (mTextPaint.getTypeface() != typeface) {
+ if (Objects.equals(mTextPaint.getTypeface(), (typeface))) {
mTextPaint.setTypeface(typeface);
forceUpdate(true);
}
@@ -250,8 +249,9 @@ public class SubtitleView extends View {
mHasMeasurements = true;
mLastMeasuredWidth = maxWidth;
- mLayout = new StaticLayout(mText, mTextPaint, maxWidth, mAlignment,
- mSpacingMult, mSpacingAdd, true);
+ mLayout =
+ new StaticLayout(
+ mText, mTextPaint, maxWidth, mAlignment, mSpacingMult, mSpacingAdd, true);
return true;
}
@@ -320,5 +320,4 @@ public class SubtitleView extends View {
textPaint.setShadowLayer(0, 0, 0, 0);
c.restoreToCount(saveCount);
}
-
}
diff --git a/src/com/android/tv/tuner/layout/ScaledLayout.java b/tuner/src/com/android/tv/tuner/layout/ScaledLayout.java
index 379ea70e..dd92b641 100644
--- a/src/com/android/tv/tuner/layout/ScaledLayout.java
+++ b/tuner/src/com/android/tv/tuner/layout/ScaledLayout.java
@@ -26,28 +26,25 @@ import android.util.Log;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
-
import com.android.tv.tuner.R;
-
import java.util.Arrays;
import java.util.Comparator;
-/**
- * A layout that scales its children using the given percentage value.
- */
+/** A layout that scales its children using the given percentage value. */
public class ScaledLayout extends ViewGroup {
private static final String TAG = "ScaledLayout";
private static final boolean DEBUG = false;
- private static final Comparator<Rect> mRectTopLeftSorter = new Comparator<Rect>() {
- @Override
- public int compare(Rect lhs, Rect rhs) {
- if (lhs.top != rhs.top) {
- return lhs.top - rhs.top;
- } else {
- return lhs.left - rhs.left;
- }
- }
- };
+ private static final Comparator<Rect> mRectTopLeftSorter =
+ new Comparator<Rect>() {
+ @Override
+ public int compare(Rect lhs, Rect rhs) {
+ if (lhs.top != rhs.top) {
+ return lhs.top - rhs.top;
+ } else {
+ return lhs.left - rhs.left;
+ }
+ }
+ };
private Rect[] mRectArray;
private final int mMaxWidth;
@@ -64,8 +61,8 @@ public class ScaledLayout extends ViewGroup {
public ScaledLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
Point size = new Point();
- DisplayManager displayManager = (DisplayManager) getContext()
- .getSystemService(Context.DISPLAY_SERVICE);
+ DisplayManager displayManager =
+ (DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE);
Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
display.getRealSize(size);
mMaxWidth = size.x;
@@ -73,21 +70,19 @@ public class ScaledLayout extends ViewGroup {
}
/**
- * ScaledLayoutParams stores the four scale factors.
- * <br>
- * Vertical coordinate system: ({@code scaleStartRow} * 100) % ~ ({@code scaleEndRow} * 100) %
+ * ScaledLayoutParams stores the four scale factors. <br>
+ * Vertical coordinate system: ({@code scaleStartRow} * 100) % ~ ({@code scaleEndRow} * 100) %
* Horizontal coordinate system: ({@code scaleStartCol} * 100) % ~ ({@code scaleEndCol} * 100) %
* <br>
* In XML, for example,
- * <pre>
- * {@code
+ *
+ * <pre>{@code
* <View
* app:layout_scaleStartRow="0.1"
* app:layout_scaleEndRow="0.5"
* app:layout_scaleStartCol="0.4"
* app:layout_scaleEndCol="1" />
- * }
- * </pre>
+ * }</pre>
*/
public static class ScaledLayoutParams extends ViewGroup.LayoutParams {
public static final float SCALE_UNSPECIFIED = -1;
@@ -96,8 +91,8 @@ public class ScaledLayout extends ViewGroup {
public final float scaleStartCol;
public final float scaleEndCol;
- public ScaledLayoutParams(float scaleStartRow, float scaleEndRow,
- float scaleStartCol, float scaleEndCol) {
+ public ScaledLayoutParams(
+ float scaleStartRow, float scaleEndRow, float scaleStartCol, float scaleEndCol) {
super(MATCH_PARENT, MATCH_PARENT);
this.scaleStartRow = scaleStartRow;
this.scaleEndRow = scaleEndRow;
@@ -107,16 +102,19 @@ public class ScaledLayout extends ViewGroup {
public ScaledLayoutParams(Context context, AttributeSet attrs) {
super(MATCH_PARENT, MATCH_PARENT);
- TypedArray array =
- context.obtainStyledAttributes(attrs, R.styleable.utScaledLayout);
+ TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.utScaledLayout);
scaleStartRow =
- array.getFloat(R.styleable.utScaledLayout_layout_scaleStartRow, SCALE_UNSPECIFIED);
+ array.getFloat(
+ R.styleable.utScaledLayout_layout_scaleStartRow, SCALE_UNSPECIFIED);
scaleEndRow =
- array.getFloat(R.styleable.utScaledLayout_layout_scaleEndRow, SCALE_UNSPECIFIED);
+ array.getFloat(
+ R.styleable.utScaledLayout_layout_scaleEndRow, SCALE_UNSPECIFIED);
scaleStartCol =
- array.getFloat(R.styleable.utScaledLayout_layout_scaleStartCol, SCALE_UNSPECIFIED);
+ array.getFloat(
+ R.styleable.utScaledLayout_layout_scaleStartCol, SCALE_UNSPECIFIED);
scaleEndCol =
- array.getFloat(R.styleable.utScaledLayout_layout_scaleEndCol, SCALE_UNSPECIFIED);
+ array.getFloat(
+ R.styleable.utScaledLayout_layout_scaleEndCol, SCALE_UNSPECIFIED);
array.recycle();
}
}
@@ -155,31 +153,43 @@ public class ScaledLayout extends ViewGroup {
scaleStartCol = ((ScaledLayoutParams) params).scaleStartCol;
scaleEndCol = ((ScaledLayoutParams) params).scaleEndCol;
if (scaleStartRow < 0 || scaleStartRow > 1) {
- throw new RuntimeException("A child of ScaledLayout should have a range of "
- + "scaleStartRow between 0 and 1");
+ throw new RuntimeException(
+ "A child of ScaledLayout should have a range of "
+ + "scaleStartRow between 0 and 1");
}
if (scaleEndRow < scaleStartRow || scaleStartRow > 1) {
- throw new RuntimeException("A child of ScaledLayout should have a range of "
- + "scaleEndRow between scaleStartRow and 1");
+ throw new RuntimeException(
+ "A child of ScaledLayout should have a range of "
+ + "scaleEndRow between scaleStartRow and 1");
}
if (scaleEndCol < 0 || scaleEndCol > 1) {
- throw new RuntimeException("A child of ScaledLayout should have a range of "
- + "scaleStartCol between 0 and 1");
+ throw new RuntimeException(
+ "A child of ScaledLayout should have a range of "
+ + "scaleStartCol between 0 and 1");
}
if (scaleEndCol < scaleStartCol || scaleEndCol > 1) {
- throw new RuntimeException("A child of ScaledLayout should have a range of "
- + "scaleEndCol between scaleStartCol and 1");
+ throw new RuntimeException(
+ "A child of ScaledLayout should have a range of "
+ + "scaleEndCol between scaleStartCol and 1");
}
if (DEBUG) {
- Log.d(TAG, String.format("onMeasure child scaleStartRow: %f scaleEndRow: %f "
- + "scaleStartCol: %f scaleEndCol: %f",
- scaleStartRow, scaleEndRow, scaleStartCol, scaleEndCol));
+ Log.d(
+ TAG,
+ String.format(
+ "onMeasure child scaleStartRow: %f scaleEndRow: %f "
+ + "scaleStartCol: %f scaleEndCol: %f",
+ scaleStartRow, scaleEndRow, scaleStartCol, scaleEndCol));
}
- mRectArray[i] = new Rect((int) (scaleStartCol * width), (int) (scaleStartRow * height),
- (int) (scaleEndCol * width), (int) (scaleEndRow * height));
+ mRectArray[i] =
+ new Rect(
+ (int) (scaleStartCol * width),
+ (int) (scaleStartRow * height),
+ (int) (scaleEndCol * width),
+ (int) (scaleEndRow * height));
int scaleWidth = (int) (width * (scaleEndCol - scaleStartCol));
- int childWidthSpec = MeasureSpec.makeMeasureSpec(
- scaleWidth > mMaxWidth ? mMaxWidth : scaleWidth, MeasureSpec.EXACTLY);
+ int childWidthSpec =
+ MeasureSpec.makeMeasureSpec(
+ scaleWidth > mMaxWidth ? mMaxWidth : scaleWidth, MeasureSpec.EXACTLY);
int childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
child.measure(childWidthSpec, childHeightSpec);
@@ -201,8 +211,10 @@ public class ScaledLayout extends ViewGroup {
}
}
int scaleHeight = (int) (height * (scaleEndRow - scaleStartRow));
- childHeightSpec = MeasureSpec.makeMeasureSpec(
- scaleHeight > mMaxHeight ? mMaxHeight : scaleHeight, MeasureSpec.EXACTLY);
+ childHeightSpec =
+ MeasureSpec.makeMeasureSpec(
+ scaleHeight > mMaxHeight ? mMaxHeight : scaleHeight,
+ MeasureSpec.EXACTLY);
child.measure(childWidthSpec, childHeightSpec);
}
@@ -225,7 +237,8 @@ public class ScaledLayout extends ViewGroup {
for (int j = i + 1; j < visibleRectCount; ++j) {
if (Rect.intersects(visibleRectArray[i], visibleRectArray[j])) {
visibleRectGroup[j] = visibleRectGroup[i];
- visibleRectArray[j].set(visibleRectArray[j].left,
+ visibleRectArray[j].set(
+ visibleRectArray[j].left,
visibleRectArray[i].bottom,
visibleRectArray[j].right,
visibleRectArray[i].bottom + visibleRectArray[j].height());
@@ -239,7 +252,8 @@ public class ScaledLayout extends ViewGroup {
int overflowedHeight = visibleRectArray[i].bottom - height;
for (int j = 0; j <= i; ++j) {
if (visibleRectGroup[i] == visibleRectGroup[j]) {
- visibleRectArray[j].set(visibleRectArray[j].left,
+ visibleRectArray[j].set(
+ visibleRectArray[j].left,
visibleRectArray[j].top - overflowedHeight,
visibleRectArray[j].right,
visibleRectArray[j].bottom - overflowedHeight);
@@ -263,9 +277,11 @@ public class ScaledLayout extends ViewGroup {
int childBottom = paddingLeft + mRectArray[i].bottom;
int childRight = paddingTop + mRectArray[i].right;
if (DEBUG) {
- Log.d(TAG, String.format("layoutChild bottom: %d left: %d right: %d top: %d",
- childBottom, childLeft,
- childRight, childTop));
+ Log.d(
+ TAG,
+ String.format(
+ "layoutChild bottom: %d left: %d right: %d top: %d",
+ childBottom, childLeft, childRight, childTop));
}
child.layout(childLeft, childTop, childRight, childBottom);
}
diff --git a/src/com/android/tv/config/ConfigKeys.java b/tuner/src/com/android/tv/tuner/livetuner/LiveTvTunerTvInputService.java
index 7df033d2..f741fdb0 100644
--- a/src/com/android/tv/config/ConfigKeys.java
+++ b/tuner/src/com/android/tv/tuner/livetuner/LiveTvTunerTvInputService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2018 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,14 +14,9 @@
* limitations under the License.
*/
-package com.android.tv.config;
-
-/**
- * Static list of config keys.
- */
-public final class ConfigKeys {
+package com.android.tv.tuner.livetuner;
+import com.android.tv.tuner.tvinput.BaseTunerTvInputService;
- private ConfigKeys() {
- }
-}
+/** Live TV embedded tuner. */
+public class LiveTvTunerTvInputService extends BaseTunerTvInputService {}
diff --git a/src/com/android/tv/tuner/setup/TunerSetupActivity.java b/tuner/src/com/android/tv/tuner/setup/BaseTunerSetupActivity.java
index e9f3baa7..1be4e1c2 100644
--- a/src/com/android/tv/tuner/setup/TunerSetupActivity.java
+++ b/tuner/src/com/android/tv/tuner/setup/BaseTunerSetupActivity.java
@@ -17,19 +17,15 @@
package com.android.tv.tuner.setup;
import android.app.Fragment;
-import android.app.FragmentManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.media.tv.TvContract;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
@@ -40,49 +36,38 @@ import android.support.annotation.WorkerThread;
import android.support.v4.app.NotificationCompat;
import android.text.TextUtils;
import android.util.Log;
-import android.view.KeyEvent;
import android.widget.Toast;
-
-import com.android.tv.Features;
-import com.android.tv.TvApplication;
-import com.android.tv.common.AutoCloseableUtils;
+import com.android.tv.common.BaseApplication;
import com.android.tv.common.SoftPreconditions;
-import com.android.tv.common.TvCommonConstants;
-import com.android.tv.common.TvCommonUtils;
+import com.android.tv.common.experiments.Experiments;
+import com.android.tv.common.feature.CommonFeatures;
import com.android.tv.common.ui.setup.SetupActivity;
import com.android.tv.common.ui.setup.SetupFragment;
import com.android.tv.common.ui.setup.SetupMultiPaneFragment;
-import com.android.tv.experiments.Experiments;
+import com.android.tv.common.util.AutoCloseableUtils;
+import com.android.tv.common.util.PostalCodeUtils;
import com.android.tv.tuner.R;
import com.android.tv.tuner.TunerHal;
import com.android.tv.tuner.TunerPreferences;
-import com.android.tv.tuner.tvinput.TunerTvInputService;
-import com.android.tv.tuner.util.PostalCodeUtils;
-
import java.util.concurrent.Executor;
-/**
- * An activity that serves tuner setup process.
- */
-public class TunerSetupActivity extends SetupActivity {
- private static final String TAG = "TunerSetupActivity";
+/** The base setup activity class for tuner. */
+public class BaseTunerSetupActivity extends SetupActivity {
+ private static final String TAG = "BaseTunerSetupActivity";
private static final boolean DEBUG = false;
- /**
- * Key for passing tuner type to sub-fragments.
- */
+ /** Key for passing tuner type to sub-fragments. */
public static final String KEY_TUNER_TYPE = "TunerSetupActivity.tunerType";
// For the notification.
- private static final String TV_ACTIVITY_CLASS_NAME = "com.android.tv.TvActivity";
- private static final String TUNER_SET_UP_NOTIFICATION_CHANNEL_ID = "tuner_setup_channel";
- private static final String NOTIFY_TAG = "TunerSetup";
- private static final int NOTIFY_ID = 1000;
- private static final String TAG_DRAWABLE = "drawable";
- private static final String TAG_ICON = "ic_launcher_s";
- private static final int PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION = 1;
-
- private static final int CHANNEL_MAP_SCAN_FILE[] = {
+ protected static final String TUNER_SET_UP_NOTIFICATION_CHANNEL_ID = "tuner_setup_channel";
+ protected static final String NOTIFY_TAG = "TunerSetup";
+ protected static final int NOTIFY_ID = 1000;
+ protected static final String TAG_DRAWABLE = "drawable";
+ protected static final String TAG_ICON = "ic_launcher_s";
+ protected static final int PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION = 1;
+
+ protected static final int[] CHANNEL_MAP_SCAN_FILE = {
R.raw.ut_us_atsc_center_frequencies_8vsb,
R.raw.ut_us_cable_standard_center_frequencies_qam256,
R.raw.ut_us_all,
@@ -95,43 +80,33 @@ public class TunerSetupActivity extends SetupActivity {
R.raw.ut_euro_dvbt_all
};
- private ScanFragment mLastScanFragment;
- private Integer mTunerType;
+ protected ScanFragment mLastScanFragment;
+ protected Integer mTunerType;
+ protected boolean mNeedToShowPostalCodeFragment;
+ protected String mPreviousPostalCode;
+ protected boolean mActivityStopped;
+ protected boolean mPendingShowInitialFragment;
+
private TunerHalFactory mTunerHalFactory;
- private boolean mNeedToShowPostalCodeFragment;
- private String mPreviousPostalCode;
@Override
protected void onCreate(Bundle savedInstanceState) {
- if (DEBUG) Log.d(TAG, "onCreate");
- new AsyncTask<Void, Void, Integer>() {
- @Override
- protected Integer doInBackground(Void... arg0) {
- return TunerHal.getTunerTypeAndCount(TunerSetupActivity.this).first;
- }
-
- @Override
- protected void onPostExecute(Integer result) {
- if (!TunerSetupActivity.this.isDestroyed()) {
- mTunerType = result;
- if (result == null) {
- finish();
- } else {
- showInitialFragment();
- }
- }
- }
- }.execute();
- TvApplication.setCurrentRunningProcess(this, false);
+ if (DEBUG) {
+ Log.d(TAG, "onCreate");
+ }
+ mActivityStopped = false;
+ executeGetTunerTypeAndCountAsyncTask();
+ mTunerHalFactory =
+ new TunerHalFactory(getApplicationContext(), AsyncTask.THREAD_POOL_EXECUTOR);
super.onCreate(savedInstanceState);
// TODO: check {@link shouldShowRequestPermissionRationale}.
if (checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
// No need to check the request result.
- requestPermissions(new String[] {android.Manifest.permission.ACCESS_COARSE_LOCATION},
+ requestPermissions(
+ new String[] {android.Manifest.permission.ACCESS_COARSE_LOCATION},
PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION);
}
- mTunerHalFactory = new TunerHalFactory(getApplicationContext());
try {
// Updating postal code takes time, therefore we called it here for "warm-up".
mPreviousPostalCode = PostalCodeUtils.getLastPostalCode(this);
@@ -144,11 +119,30 @@ public class TunerSetupActivity extends SetupActivity {
}
}
+ protected void executeGetTunerTypeAndCountAsyncTask() {}
+
@Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
- @NonNull int[] grantResults) {
+ protected void onStop() {
+ mActivityStopped = true;
+ super.onStop();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mActivityStopped = false;
+ if (mPendingShowInitialFragment) {
+ showInitialFragment();
+ mPendingShowInitialFragment = false;
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(
+ int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION) {
- if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED
+ if (grantResults.length > 0
+ && grantResults[0] == PackageManager.PERMISSION_GRANTED
&& Experiments.CLOUD_EPG.get()) {
try {
// Updating postal code takes time, therefore we should update postal code
@@ -169,8 +163,9 @@ public class TunerSetupActivity extends SetupActivity {
Bundle args = new Bundle();
args.putInt(KEY_TUNER_TYPE, mTunerType);
fragment.setArguments(args);
- fragment.setShortDistance(SetupFragment.FRAGMENT_EXIT_TRANSITION
- | SetupFragment.FRAGMENT_REENTER_TRANSITION);
+ fragment.setShortDistance(
+ SetupFragment.FRAGMENT_EXIT_TRANSITION
+ | SetupFragment.FRAGMENT_REENTER_TRANSITION);
return fragment;
} else {
return null;
@@ -188,11 +183,11 @@ public class TunerSetupActivity extends SetupActivity {
finish();
break;
default:
+ String postalCode = PostalCodeUtils.getLastPostalCode(this);
if (mNeedToShowPostalCodeFragment
- || Features.ENABLE_CLOUD_EPG_REGION.isEnabled(
+ || (CommonFeatures.ENABLE_CLOUD_EPG_REGION.isEnabled(
getApplicationContext())
- && TextUtils.isEmpty(
- PostalCodeUtils.getLastPostalCode(this))) {
+ && TextUtils.isEmpty(postalCode))) {
// We cannot get postal code automatically. Postal code input fragment
// should always be shown even if users have input some valid postal
// code in this activity before.
@@ -205,22 +200,29 @@ public class TunerSetupActivity extends SetupActivity {
}
return true;
case PostalCodeFragment.ACTION_CATEGORY:
- if (actionId == SetupMultiPaneFragment.ACTION_DONE
- || actionId == SetupMultiPaneFragment.ACTION_SKIP) {
- showConnectionTypeFragment();
+ switch (actionId) {
+ case SetupMultiPaneFragment.ACTION_DONE:
+ // fall through
+ case SetupMultiPaneFragment.ACTION_SKIP:
+ showConnectionTypeFragment();
+ break;
+ default: // fall out
}
return true;
case ConnectionTypeFragment.ACTION_CATEGORY:
if (mTunerHalFactory.getOrCreate() == null) {
finish();
- Toast.makeText(getApplicationContext(),
- R.string.ut_channel_scan_tuner_unavailable,Toast.LENGTH_LONG).show();
+ Toast.makeText(
+ getApplicationContext(),
+ R.string.ut_channel_scan_tuner_unavailable,
+ Toast.LENGTH_LONG)
+ .show();
return true;
}
mLastScanFragment = new ScanFragment();
Bundle args1 = new Bundle();
- args1.putInt(ScanFragment.EXTRA_FOR_CHANNEL_SCAN_FILE,
- CHANNEL_MAP_SCAN_FILE[actionId]);
+ args1.putInt(
+ ScanFragment.EXTRA_FOR_CHANNEL_SCAN_FILE, CHANNEL_MAP_SCAN_FILE[actionId]);
args1.putInt(KEY_TUNER_TYPE, mTunerType);
mLastScanFragment.setArguments(args1);
showFragment(mLastScanFragment, true);
@@ -232,14 +234,9 @@ public class TunerSetupActivity extends SetupActivity {
return true;
case ScanFragment.ACTION_FINISH:
mTunerHalFactory.clear();
- SetupFragment fragment = new ScanResultFragment();
- Bundle args2 = new Bundle();
- args2.putInt(KEY_TUNER_TYPE, mTunerType);
- fragment.setArguments(args2);
- fragment.setShortDistance(SetupFragment.FRAGMENT_EXIT_TRANSITION
- | SetupFragment.FRAGMENT_REENTER_TRANSITION);
- showFragment(fragment, true);
+ showScanResultFragment();
return true;
+ default: // fall out
}
break;
case ScanResultFragment.ACTION_CATEGORY:
@@ -249,39 +246,21 @@ public class TunerSetupActivity extends SetupActivity {
finish();
break;
default:
+ // scan again
SetupFragment fragment = new ConnectionTypeFragment();
- fragment.setShortDistance(SetupFragment.FRAGMENT_ENTER_TRANSITION
- | SetupFragment.FRAGMENT_RETURN_TRANSITION);
+ fragment.setShortDistance(
+ SetupFragment.FRAGMENT_ENTER_TRANSITION
+ | SetupFragment.FRAGMENT_RETURN_TRANSITION);
showFragment(fragment, true);
break;
}
return true;
+ default: // fall out
}
return false;
}
@Override
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK) {
- FragmentManager manager = getFragmentManager();
- int count = manager.getBackStackEntryCount();
- if (count > 0) {
- String lastTag = manager.getBackStackEntryAt(count - 1).getName();
- if (ScanResultFragment.class.getCanonicalName().equals(lastTag) && count >= 2) {
- // Pops fragment including ScanFragment.
- manager.popBackStack(manager.getBackStackEntryAt(count - 2).getName(),
- FragmentManager.POP_BACK_STACK_INCLUSIVE);
- return true;
- } else if (ScanFragment.class.getCanonicalName().equals(lastTag)) {
- mLastScanFragment.finishScan(true);
- return true;
- }
- }
- }
- return super.onKeyUp(keyCode, event);
- }
-
- @Override
public void onDestroy() {
if (mPreviousPostalCode != null && PostalCodeUtils.getLastPostalCode(this) == null) {
PostalCodeUtils.setLastPostalCode(this, mPreviousPostalCode);
@@ -289,12 +268,61 @@ public class TunerSetupActivity extends SetupActivity {
super.onDestroy();
}
+ /** Gets the currently used tuner HAL. */
+ TunerHal getTunerHal() {
+ return mTunerHalFactory.getOrCreate();
+ }
+
+ /** Generates tuner HAL. */
+ void generateTunerHal() {
+ mTunerHalFactory.generate();
+ }
+
+ /** Clears the currently used tuner HAL. */
+ protected void clearTunerHal() {
+ mTunerHalFactory.clear();
+ }
+
+ protected void showPostalCodeFragment() {
+ SetupFragment fragment = new PostalCodeFragment();
+ fragment.setShortDistance(
+ SetupFragment.FRAGMENT_ENTER_TRANSITION | SetupFragment.FRAGMENT_RETURN_TRANSITION);
+ showFragment(fragment, true);
+ }
+
+ protected void showConnectionTypeFragment() {
+ SetupFragment fragment = new ConnectionTypeFragment();
+ fragment.setShortDistance(
+ SetupFragment.FRAGMENT_ENTER_TRANSITION | SetupFragment.FRAGMENT_RETURN_TRANSITION);
+ showFragment(fragment, true);
+ }
+
+ protected void showScanResultFragment() {
+ SetupFragment scanResultFragment = new ScanResultFragment();
+ Bundle args2 = new Bundle();
+ args2.putInt(KEY_TUNER_TYPE, mTunerType);
+ scanResultFragment.setShortDistance(
+ SetupFragment.FRAGMENT_EXIT_TRANSITION | SetupFragment.FRAGMENT_REENTER_TRANSITION);
+ showFragment(scanResultFragment, true);
+ }
+
+ /**
+ * Cancels the previously shown notification.
+ *
+ * @param context a {@link Context} instance
+ */
+ public static void cancelNotification(Context context) {
+ NotificationManager notificationManager =
+ (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ notificationManager.cancel(NOTIFY_TAG, NOTIFY_ID);
+ }
+
/**
* A callback to be invoked when the TvInputService is enabled or disabled.
*
* @param context a {@link Context} instance
- * @param enabled {@code true} for the {@link TunerTvInputService} to be enabled;
- * otherwise {@code false}
+ * @param enabled {@code true} for the {@link TunerTvInputService} to be enabled; otherwise
+ * {@code false}
*/
public static void onTvInputEnabled(Context context, boolean enabled, Integer tunerType) {
// Send a notification for tuner setup if there's no channels and the tuner TV input
@@ -310,59 +338,9 @@ public class TunerSetupActivity extends SetupActivity {
}
}
- /**
- * Returns a {@link Intent} to launch the tuner TV input service.
- *
- * @param context a {@link Context} instance
- */
- public static Intent createSetupActivity(Context context) {
- String inputId = TvContract.buildInputId(new ComponentName(context.getPackageName(),
- TunerTvInputService.class.getName()));
-
- // Make an intent to launch the setup activity of TV tuner input.
- Intent intent = TvCommonUtils.createSetupIntent(
- new Intent(context, TunerSetupActivity.class), inputId);
- intent.putExtra(TvCommonConstants.EXTRA_INPUT_ID, inputId);
- Intent tvActivityIntent = new Intent();
- tvActivityIntent.setComponent(new ComponentName(context, TV_ACTIVITY_CLASS_NAME));
- intent.putExtra(TvCommonConstants.EXTRA_ACTIVITY_AFTER_COMPLETION, tvActivityIntent);
- return intent;
- }
-
- /**
- * Gets the currently used tuner HAL.
- */
- TunerHal getTunerHal() {
- return mTunerHalFactory.getOrCreate();
- }
-
- /**
- * Generates tuner HAL.
- */
- void generateTunerHal() {
- mTunerHalFactory.generate();
- }
-
- /**
- * Clears the currently used tuner HAL.
- */
- void clearTunerHal() {
- mTunerHalFactory.clear();
- }
-
- /**
- * Returns a {@link PendingIntent} to launch the tuner TV input service.
- *
- * @param context a {@link Context} instance
- */
- private static PendingIntent createPendingIntentForSetupActivity(Context context) {
- return PendingIntent.getActivity(context, 0, createSetupActivity(context),
- PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
private static void sendNotification(Context context, Integer tunerType) {
- SoftPreconditions.checkState(tunerType != null, TAG,
- "tunerType is null when send notification");
+ SoftPreconditions.checkState(
+ tunerType != null, TAG, "tunerType is null when send notification");
if (tunerType == null) {
return;
}
@@ -379,94 +357,93 @@ public class TunerSetupActivity extends SetupActivity {
case TunerHal.TUNER_TYPE_NETWORK:
contentTextId = R.string.nt_setup_notification_content_text;
break;
+ default: // fall out
}
String contentText = resources.getString(contentTextId);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
sendNotificationInternal(context, contentTitle, contentText);
} else {
- Bitmap largeIcon = BitmapFactory.decodeResource(resources,
- R.drawable.recommendation_antenna);
+ Bitmap largeIcon =
+ BitmapFactory.decodeResource(resources, R.drawable.recommendation_antenna);
sendRecommendationCard(context, contentTitle, contentText, largeIcon);
}
}
+ private static void sendNotificationInternal(
+ Context context, String contentTitle, String contentText) {
+ NotificationManager notificationManager =
+ (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ notificationManager.createNotificationChannel(
+ new NotificationChannel(
+ TUNER_SET_UP_NOTIFICATION_CHANNEL_ID,
+ context.getResources()
+ .getString(R.string.ut_setup_notification_channel_name),
+ NotificationManager.IMPORTANCE_HIGH));
+ Notification notification =
+ new Notification.Builder(context, TUNER_SET_UP_NOTIFICATION_CHANNEL_ID)
+ .setContentTitle(contentTitle)
+ .setContentText(contentText)
+ .setSmallIcon(
+ context.getResources()
+ .getIdentifier(
+ TAG_ICON, TAG_DRAWABLE, context.getPackageName()))
+ .setContentIntent(createPendingIntentForSetupActivity(context))
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .extend(new Notification.TvExtender())
+ .build();
+ notificationManager.notify(NOTIFY_TAG, NOTIFY_ID, notification);
+ }
+
/**
* Sends the recommendation card to start the tuner TV input setup activity.
*
* @param context a {@link Context} instance
*/
- private static void sendRecommendationCard(Context context, String contentTitle,
- String contentText, Bitmap largeIcon) {
+ private static void sendRecommendationCard(
+ Context context, String contentTitle, String contentText, Bitmap largeIcon) {
// Build and send the notification.
- Notification notification = new NotificationCompat.BigPictureStyle(
- new NotificationCompat.Builder(context)
- .setAutoCancel(false)
- .setContentTitle(contentTitle)
- .setContentText(contentText)
- .setContentInfo(contentText)
- .setCategory(Notification.CATEGORY_RECOMMENDATION)
- .setLargeIcon(largeIcon)
- .setSmallIcon(context.getResources().getIdentifier(
- TAG_ICON, TAG_DRAWABLE, context.getPackageName()))
- .setContentIntent(createPendingIntentForSetupActivity(context)))
- .build();
- NotificationManager notificationManager = (NotificationManager) context
- .getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.notify(NOTIFY_TAG, NOTIFY_ID, notification);
- }
-
- private static void sendNotificationInternal(Context context, String contentTitle,
- String contentText) {
- NotificationManager notificationManager = (NotificationManager) context.getSystemService(
- Context.NOTIFICATION_SERVICE);
- notificationManager.createNotificationChannel(new NotificationChannel(
- TUNER_SET_UP_NOTIFICATION_CHANNEL_ID,
- context.getResources().getString(R.string.ut_setup_notification_channel_name),
- NotificationManager.IMPORTANCE_HIGH));
- Notification notification = new Notification.Builder(
- context, TUNER_SET_UP_NOTIFICATION_CHANNEL_ID)
- .setContentTitle(contentTitle)
- .setContentText(contentText)
- .setSmallIcon(context.getResources().getIdentifier(
- TAG_ICON, TAG_DRAWABLE, context.getPackageName()))
- .setContentIntent(createPendingIntentForSetupActivity(context))
- .setVisibility(Notification.VISIBILITY_PUBLIC)
- .extend(new Notification.TvExtender())
- .build();
+ Notification notification =
+ new NotificationCompat.BigPictureStyle(
+ new NotificationCompat.Builder(context)
+ .setAutoCancel(false)
+ .setContentTitle(contentTitle)
+ .setContentText(contentText)
+ .setContentInfo(contentText)
+ .setCategory(Notification.CATEGORY_RECOMMENDATION)
+ .setLargeIcon(largeIcon)
+ .setSmallIcon(
+ context.getResources()
+ .getIdentifier(
+ TAG_ICON,
+ TAG_DRAWABLE,
+ context.getPackageName()))
+ .setContentIntent(
+ createPendingIntentForSetupActivity(context)))
+ .build();
+ NotificationManager notificationManager =
+ (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFY_TAG, NOTIFY_ID, notification);
}
- private void showPostalCodeFragment() {
- SetupFragment fragment = new PostalCodeFragment();
- fragment.setShortDistance(SetupFragment.FRAGMENT_ENTER_TRANSITION
- | SetupFragment.FRAGMENT_RETURN_TRANSITION);
- showFragment(fragment, true);
- }
-
- private void showConnectionTypeFragment() {
- SetupFragment fragment = new ConnectionTypeFragment();
- fragment.setShortDistance(SetupFragment.FRAGMENT_ENTER_TRANSITION
- | SetupFragment.FRAGMENT_RETURN_TRANSITION);
- showFragment(fragment, true);
- }
-
/**
- * Cancels the previously shown notification.
+ * Returns a {@link PendingIntent} to launch the tuner TV input service.
*
* @param context a {@link Context} instance
*/
- public static void cancelNotification(Context context) {
- NotificationManager notificationManager = (NotificationManager) context
- .getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.cancel(NOTIFY_TAG, NOTIFY_ID);
+ private static PendingIntent createPendingIntentForSetupActivity(Context context) {
+ return PendingIntent.getActivity(
+ context,
+ 0,
+ BaseApplication.getSingletons(context).getTunerSetupIntent(context),
+ PendingIntent.FLAG_UPDATE_CURRENT);
}
+ /** A static factory for {@link TunerHal} instances * */
@VisibleForTesting
- static class TunerHalFactory {
+ protected static class TunerHalFactory {
private Context mContext;
- @VisibleForTesting
- TunerHal mTunerHal;
- private GenerateTunerHalTask mGenerateTunerHalTask;
+ @VisibleForTesting TunerHal mTunerHal;
+ private TunerHalFactory.GenerateTunerHalTask mGenerateTunerHalTask;
private final Executor mExecutor;
TunerHalFactory(Context context) {
@@ -497,20 +474,16 @@ public class TunerSetupActivity extends SetupActivity {
return mTunerHal;
}
- /**
- * Generates tuner hal for scanning with asynchronous tasks.
- */
+ /** Generates tuner hal for scanning with asynchronous tasks. */
@MainThread
void generate() {
if (mGenerateTunerHalTask == null && mTunerHal == null) {
- mGenerateTunerHalTask = new GenerateTunerHalTask();
+ mGenerateTunerHalTask = new TunerHalFactory.GenerateTunerHalTask();
mGenerateTunerHalTask.executeOnExecutor(mExecutor);
}
}
- /**
- * Clears the currently used tuner hal.
- */
+ /** Clears the currently used tuner hal. */
@MainThread
void clear() {
if (mGenerateTunerHalTask != null) {
@@ -540,4 +513,4 @@ public class TunerSetupActivity extends SetupActivity {
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/tuner/setup/ConnectionTypeFragment.java b/tuner/src/com/android/tv/tuner/setup/ConnectionTypeFragment.java
index e0e21a20..ebe4e41e 100644
--- a/src/com/android/tv/tuner/setup/ConnectionTypeFragment.java
+++ b/tuner/src/com/android/tv/tuner/setup/ConnectionTypeFragment.java
@@ -20,37 +20,33 @@ import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
import android.support.v17.leanback.widget.GuidedAction;
-
import com.android.tv.common.BuildConfig;
import com.android.tv.common.ui.setup.SetupGuidedStepFragment;
import com.android.tv.common.ui.setup.SetupMultiPaneFragment;
import com.android.tv.tuner.R;
-
import java.util.List;
import java.util.TimeZone;
-/**
- * A fragment for connection type selection.
- */
+/** A fragment for connection type selection. */
public class ConnectionTypeFragment extends SetupMultiPaneFragment {
public static final String ACTION_CATEGORY =
"com.android.tv.tuner.setup.ConnectionTypeFragment";
@Override
public void onCreate(Bundle savedInstanceState) {
- ((TunerSetupActivity) getActivity()).generateTunerHal();
+ ((BaseTunerSetupActivity) getActivity()).generateTunerHal();
super.onCreate(savedInstanceState);
}
@Override
public void onResume() {
- ((TunerSetupActivity) getActivity()).generateTunerHal();
+ ((BaseTunerSetupActivity) getActivity()).generateTunerHal();
super.onResume();
}
@Override
public void onDestroy() {
- ((TunerSetupActivity) getActivity()).clearTunerHal();
+ ((BaseTunerSetupActivity) getActivity()).clearTunerHal();
super.onDestroy();
}
@@ -69,27 +65,31 @@ public class ConnectionTypeFragment extends SetupMultiPaneFragment {
return false;
}
+ /** The content fragment of {@link ConnectionTypeFragment}. */
public static class ContentFragment extends SetupGuidedStepFragment {
@NonNull
@Override
public Guidance onCreateGuidance(Bundle savedInstanceState) {
- return new Guidance(getString(R.string.ut_connection_title),
+ return new Guidance(
+ getString(R.string.ut_connection_title),
getString(R.string.ut_connection_description),
- getString(R.string.ut_setup_breadcrumb), null);
+ getString(R.string.ut_setup_breadcrumb),
+ null);
}
@Override
- public void onCreateActions(@NonNull List<GuidedAction> actions,
- Bundle savedInstanceState) {
+ public void onCreateActions(
+ @NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
String[] choices = getResources().getStringArray(R.array.ut_connection_choices);
int length = choices.length - 1;
int startOffset = 0;
for (int i = 0; i < length; ++i) {
- actions.add(new GuidedAction.Builder(getActivity())
- .id(startOffset + i)
- .title(choices[i])
- .build());
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(startOffset + i)
+ .title(choices[i])
+ .build());
}
}
diff --git a/tuner/src/com/android/tv/tuner/setup/LineupFragment.java b/tuner/src/com/android/tv/tuner/setup/LineupFragment.java
new file mode 100644
index 00000000..41f755df
--- /dev/null
+++ b/tuner/src/com/android/tv/tuner/setup/LineupFragment.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2017 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.tuner.setup;
+
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
+import android.support.v17.leanback.widget.GuidedAction;
+import android.util.Log;
+import android.view.View;
+import com.android.tv.common.ui.setup.SetupGuidedStepFragment;
+import com.android.tv.common.ui.setup.SetupMultiPaneFragment;
+import com.android.tv.tuner.R;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Lineup Fragment shows available lineups and lets users select one of them. */
+public class LineupFragment extends SetupMultiPaneFragment {
+ public static final String TAG = "LineupFragment";
+ public static final boolean DEBUG = false;
+
+ public static final String ACTION_CATEGORY = "com.android.tv.tuner.setup.LineupFragment";
+ public static final String KEY_LINEUP_NAMES = "lineup_names";
+ public static final String KEY_MATCH_NUMBERS = "match_numbers";
+ public static final String KEY_DEFAULT_LINEUP = "default_lineup";
+ public static final String KEY_LINEUP_NOT_FOUND = "lineup_not_found";
+ public static final int ACTION_ID_RETRY = SetupMultiPaneFragment.MAX_SUBCLASSES_ID - 1;
+
+ private ContentFragment contentFragment;
+ private Bundle args;
+ private ArrayList<String> lineups;
+ private boolean lineupNotFound;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ if (savedInstanceState == null) {
+ lineups = getArguments().getStringArrayList(KEY_LINEUP_NAMES);
+ }
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected SetupGuidedStepFragment onCreateContentFragment() {
+ contentFragment = new LineupFragment.ContentFragment();
+ Bundle args = new Bundle();
+ Bundle arguments = this.args != null ? this.args : getArguments();
+ args.putStringArrayList(KEY_LINEUP_NAMES, lineups);
+ args.putIntegerArrayList(
+ KEY_MATCH_NUMBERS, arguments.getIntegerArrayList(KEY_MATCH_NUMBERS));
+ args.putInt(KEY_DEFAULT_LINEUP, arguments.getInt(KEY_DEFAULT_LINEUP));
+ args.putBoolean(KEY_LINEUP_NOT_FOUND, lineupNotFound);
+ contentFragment.setArguments(args);
+ return contentFragment;
+ }
+
+ @Override
+ protected String getActionCategory() {
+ return ACTION_CATEGORY;
+ }
+
+ public void onLineupFound(Bundle args) {
+ if (DEBUG) {
+ Log.d(TAG, "onLineupFound");
+ }
+ this.args = args;
+ lineups = args.getStringArrayList(KEY_LINEUP_NAMES);
+ lineupNotFound = false;
+ if (contentFragment != null) {
+ updateContentFragment();
+ }
+ }
+
+ public void onLineupNotFound() {
+ if (DEBUG) {
+ Log.d(TAG, "onLineupNotFound");
+ }
+ lineupNotFound = true;
+ if (contentFragment != null) {
+ updateContentFragment();
+ }
+ }
+
+ public void onRetry() {
+ if (DEBUG) {
+ Log.d(TAG, "onRetry");
+ }
+ if (contentFragment != null) {
+ lineupNotFound = false;
+ lineups = null;
+ updateContentFragment();
+ } else {
+ // onRetry() can be called only when retry button is clicked.
+ // This should never happen.
+ throw new RuntimeException(
+ "ContentFragment hasn't been created when onRetry() is called");
+ }
+ }
+
+ @Override
+ protected boolean needsDoneButton() {
+ return false;
+ }
+
+ private void updateContentFragment() {
+ contentFragment = (ContentFragment) onCreateContentFragment();
+ getChildFragmentManager()
+ .beginTransaction()
+ .replace(
+ com.android.tv.common.R.id.guided_step_fragment_container,
+ contentFragment,
+ SetupMultiPaneFragment.CONTENT_FRAGMENT_TAG)
+ .commit();
+ }
+
+ /** The content fragment of {@link LineupFragment}. */
+ public static class ContentFragment extends SetupGuidedStepFragment {
+
+ private ArrayList<String> lineups;
+ private ArrayList<Integer> matchNumbers;
+ private int defaultLineup;
+ private boolean lineupNotFound;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ if (savedInstanceState == null) {
+ lineups = getArguments().getStringArrayList(KEY_LINEUP_NAMES);
+ matchNumbers = getArguments().getIntegerArrayList(KEY_MATCH_NUMBERS);
+ defaultLineup = getArguments().getInt(KEY_DEFAULT_LINEUP);
+ this.lineupNotFound = getArguments().getBoolean(KEY_LINEUP_NOT_FOUND);
+ }
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ int position = findActionPositionById(defaultLineup);
+ if (position >= 0 && position < getActions().size()) {
+ setSelectedActionPosition(position);
+ }
+ }
+
+ @NonNull
+ @Override
+ public Guidance onCreateGuidance(Bundle savedInstanceState) {
+ if ((lineups != null && lineups.isEmpty()) || this.lineupNotFound) {
+ return new Guidance(
+ getString(R.string.ut_lineup_title_lineups_not_found),
+ getString(R.string.ut_lineup_description_lineups_not_found),
+ getString(R.string.ut_setup_breadcrumb),
+ null);
+ } else if (lineups == null) {
+ return new Guidance(
+ getString(R.string.ut_lineup_title_fetching_lineups),
+ getString(R.string.ut_lineup_description_fetching_lineups),
+ getString(R.string.ut_setup_breadcrumb),
+ null);
+ }
+ return new Guidance(
+ getString(R.string.ut_lineup_title_lineups_found),
+ getString(R.string.ut_lineup_description_lineups_found),
+ getString(R.string.ut_setup_breadcrumb),
+ null);
+ }
+
+ @Override
+ public void onCreateActions(
+ @NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
+ actions.addAll(buildActions());
+ }
+
+ @Override
+ protected String getActionCategory() {
+ return ACTION_CATEGORY;
+ }
+
+ private List<GuidedAction> buildActions() {
+ List<GuidedAction> actions = new ArrayList<>();
+
+ if ((lineups != null && lineups.isEmpty()) || this.lineupNotFound) {
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(ACTION_ID_RETRY)
+ .title(com.android.tv.common.R.string.action_text_retry)
+ .build());
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(ACTION_SKIP)
+ .title(com.android.tv.common.R.string.action_text_skip)
+ .build());
+ } else if (lineups == null) {
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(ACTION_SKIP)
+ .title(com.android.tv.common.R.string.action_text_skip)
+ .build());
+ } else {
+ Resources res = getResources();
+ for (int i = 0; i < lineups.size(); ++i) {
+ int matchNumber = matchNumbers.get(i);
+ String description =
+ matchNumber == 0
+ ? res.getString(R.string.ut_lineup_no_channels_matched)
+ : res.getQuantityString(
+ R.plurals.ut_lineup_channels_matched,
+ matchNumber,
+ matchNumber);
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(i)
+ .title(lineups.get(i))
+ .description(description)
+ .build());
+ }
+ }
+ return actions;
+ }
+ }
+}
diff --git a/tuner/src/com/android/tv/tuner/setup/LiveTvTunerSetupActivity.java b/tuner/src/com/android/tv/tuner/setup/LiveTvTunerSetupActivity.java
new file mode 100644
index 00000000..722de7c6
--- /dev/null
+++ b/tuner/src/com/android/tv/tuner/setup/LiveTvTunerSetupActivity.java
@@ -0,0 +1,75 @@
+/*
+ * 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.tuner.setup;
+
+import android.app.FragmentManager;
+import android.os.AsyncTask;
+import android.view.KeyEvent;
+import com.android.tv.tuner.TunerHal;
+
+/** An activity that serves tuner setup process. */
+public class LiveTvTunerSetupActivity extends BaseTunerSetupActivity {
+ private static final String TAG = "LiveTvTunerSetupActivity";
+
+ @Override
+ protected void executeGetTunerTypeAndCountAsyncTask() {
+ new AsyncTask<Void, Void, Integer>() {
+ @Override
+ protected Integer doInBackground(Void... arg0) {
+ return TunerHal.getTunerTypeAndCount(LiveTvTunerSetupActivity.this).first;
+ }
+
+ @Override
+ protected void onPostExecute(Integer result) {
+ if (!LiveTvTunerSetupActivity.this.isDestroyed()) {
+ mTunerType = result;
+ if (result == null) {
+ finish();
+ } else if (!mActivityStopped) {
+ showInitialFragment();
+ } else {
+ mPendingShowInitialFragment = true;
+ }
+ }
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ FragmentManager manager = getFragmentManager();
+ int count = manager.getBackStackEntryCount();
+ if (count > 0) {
+ String lastTag = manager.getBackStackEntryAt(count - 1).getName();
+ if (ScanResultFragment.class.getCanonicalName().equals(lastTag) && count >= 2) {
+ String secondLastTag = manager.getBackStackEntryAt(count - 2).getName();
+ if (ScanFragment.class.getCanonicalName().equals(secondLastTag)) {
+ // Pops fragment including ScanFragment.
+ manager.popBackStack(
+ secondLastTag, FragmentManager.POP_BACK_STACK_INCLUSIVE);
+ return true;
+ }
+ } else if (ScanFragment.class.getCanonicalName().equals(lastTag)) {
+ mLastScanFragment.finishScan(true);
+ return true;
+ }
+ }
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+}
diff --git a/src/com/android/tv/tuner/setup/PostalCodeFragment.java b/tuner/src/com/android/tv/tuner/setup/PostalCodeFragment.java
index 025b9193..f4b9f65e 100644
--- a/src/com/android/tv/tuner/setup/PostalCodeFragment.java
+++ b/tuner/src/com/android/tv/tuner/setup/PostalCodeFragment.java
@@ -25,19 +25,17 @@ import android.text.InputFilter;
import android.text.InputFilter.AllCaps;
import android.view.View;
import android.widget.TextView;
-import com.android.tv.R;
import com.android.tv.common.ui.setup.SetupGuidedStepFragment;
import com.android.tv.common.ui.setup.SetupMultiPaneFragment;
-import com.android.tv.tuner.util.PostalCodeUtils;
-import com.android.tv.util.LocationUtils;
+import com.android.tv.common.util.LocationUtils;
+import com.android.tv.common.util.PostalCodeUtils;
+import com.android.tv.tuner.R;
import java.util.List;
-/**
- * A fragment for initial screen.
- */
+/** A fragment for initial screen. */
public class PostalCodeFragment extends SetupMultiPaneFragment {
- public static final String ACTION_CATEGORY =
- "com.android.tv.tuner.setup.PostalCodeFragment";
+ public static final String ACTION_CATEGORY = "com.android.tv.tuner.setup.PostalCodeFragment";
+ public static final String KEY_POSTAL_CODE = "postal_code";
private static final int VIEW_TYPE_EDITABLE = 1;
@Override
@@ -72,12 +70,15 @@ public class PostalCodeFragment extends SetupMultiPaneFragment {
@Override
public void onClick(View view) {
CharSequence postalCode =
- ((ContentFragment) getContentFragment()).mEditAction.getTitle();
+ ((ContentFragment) getContentFragment())
+ .mEditedActionTitleView.getText();
String region = LocationUtils.getCurrentCountry(getContext());
if (postalCode != null && PostalCodeUtils.matches(postalCode, region)) {
- PostalCodeUtils.setLastPostalCode(
- getContext(), postalCode.toString());
- onActionClick(category, actionId);
+ String postalCodeString = postalCode.toString();
+ PostalCodeUtils.setLastPostalCode(getContext(), postalCodeString);
+ Bundle params = new Bundle();
+ params.putString(KEY_POSTAL_CODE, postalCodeString);
+ onActionClick(category, actionId, params);
} else {
ContentFragment contentFragment =
(ContentFragment) getContentFragment();
@@ -93,9 +94,11 @@ public class PostalCodeFragment extends SetupMultiPaneFragment {
}
}
+ /** The content fragment of {@link PostalCodeFragment}. */
public static class ContentFragment extends SetupGuidedStepFragment {
private GuidedAction mEditAction;
private View mEditedActionView;
+ private TextView mEditedActionTitleView;
private View mDoneActionView;
private boolean mProceed;
@@ -105,7 +108,7 @@ public class PostalCodeFragment extends SetupMultiPaneFragment {
if (mProceed) {
// "NEXT" in IME was just clicked, moves focus to Done button.
if (mDoneActionView == null) {
- mDoneActionView = getActivity().findViewById(R.id.button_done);
+ mDoneActionView = getDoneButton();
}
mDoneActionView.requestFocus();
mProceed = false;
@@ -114,11 +117,12 @@ public class PostalCodeFragment extends SetupMultiPaneFragment {
if (mEditedActionView == null) {
int maxLength = PostalCodeUtils.getRegionMaxLength(getContext());
mEditedActionView = getView().findViewById(R.id.guidedactions_editable);
- ((TextView) mEditedActionView.findViewById(R.id.guidedactions_item_title))
- .setFilters(
- new InputFilter[] {
- new InputFilter.LengthFilter(maxLength), new AllCaps()
- });
+ mEditedActionTitleView =
+ mEditedActionView.findViewById(R.id.guidedactions_item_title);
+ mEditedActionTitleView.setFilters(
+ new InputFilter[] {
+ new InputFilter.LengthFilter(maxLength), new AllCaps()
+ });
}
mEditedActionView.performClick();
}
@@ -141,11 +145,15 @@ public class PostalCodeFragment extends SetupMultiPaneFragment {
}
@Override
- public void onCreateActions(@NonNull List<GuidedAction> actions,
- Bundle savedInstanceState) {
+ public void onCreateActions(
+ @NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
String description = getString(R.string.postal_code_action_description);
- mEditAction = new GuidedAction.Builder(getActivity()).id(0).editable(true)
- .description(description).build();
+ mEditAction =
+ new GuidedAction.Builder(getActivity())
+ .id(0)
+ .editable(true)
+ .description(description)
+ .build();
actions.add(mEditAction);
}
@@ -175,4 +183,4 @@ public class PostalCodeFragment extends SetupMultiPaneFragment {
};
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/tuner/setup/ScanFragment.java b/tuner/src/com/android/tv/tuner/setup/ScanFragment.java
index b6936e38..3ac86e19 100644
--- a/src/com/android/tv/tuner/setup/ScanFragment.java
+++ b/tuner/src/com/android/tv/tuner/setup/ScanFragment.java
@@ -35,7 +35,6 @@ import android.widget.Button;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
-
import com.android.tv.common.SoftPreconditions;
import com.android.tv.common.ui.setup.SetupFragment;
import com.android.tv.tuner.ChannelScanFileParser;
@@ -45,22 +44,21 @@ import com.android.tv.tuner.TunerPreferences;
import com.android.tv.tuner.data.PsipData;
import com.android.tv.tuner.data.TunerChannel;
import com.android.tv.tuner.data.nano.Channel;
+
+
import com.android.tv.tuner.source.FileTsStreamer;
import com.android.tv.tuner.source.TsDataSource;
import com.android.tv.tuner.source.TsStreamer;
import com.android.tv.tuner.source.TunerTsStreamer;
import com.android.tv.tuner.tvinput.ChannelDataManager;
import com.android.tv.tuner.tvinput.EventDetector;
-
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-/**
- * A fragment for scanning channels.
- */
+/** A fragment for scanning channels. */
public class ScanFragment extends SetupFragment {
private static final String TAG = "ScanFragment";
private static final boolean DEBUG = false;
@@ -76,7 +74,7 @@ public class ScanFragment extends SetupFragment {
public static final int ACTION_FINISH = 2;
public static final String EXTRA_FOR_CHANNEL_SCAN_FILE = "scan_file_choice";
-
+ public static final String KEY_CHANNEL_NUMBERS = "channel_numbers";
private static final long CHANNEL_SCAN_SHOW_DELAY_MS = 10000;
private static final long CHANNEL_SCAN_PERIOD_MS = 4000;
private static final long SHOW_PROGRESS_DIALOG_DELAY_MS = 300;
@@ -93,11 +91,14 @@ public class ScanFragment extends SetupFragment {
private volatile boolean mChannelListVisible;
private Button mCancelButton;
+ private ArrayList<String> mChannelNumbers;
+
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (DEBUG) Log.d(TAG, "onCreateView");
View view = super.onCreateView(inflater, container, savedInstanceState);
+ mChannelNumbers = new ArrayList<>();
mChannelDataManager = new ChannelDataManager(getActivity());
mChannelDataManager.checkDataVersion(getActivity());
mAdapter = new ChannelAdapter();
@@ -112,14 +113,15 @@ public class ScanFragment extends SetupFragment {
progressHolder.setLayoutTransition(transition);
mChannelHolder = view.findViewById(R.id.channel_holder);
mCancelButton = (Button) view.findViewById(R.id.tune_cancel);
- mCancelButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- finishScan(false);
- }
- });
+ mCancelButton.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ finishScan(false);
+ }
+ });
Bundle args = getArguments();
- int tunerType = (args == null ? 0 : args.getInt(TunerSetupActivity.KEY_TUNER_TYPE, 0));
+ int tunerType = (args == null ? 0 : args.getInt(BaseTunerSetupActivity.KEY_TUNER_TYPE, 0));
// TODO: Handle the case when the fragment is restored.
startScan(args == null ? 0 : args.getInt(EXTRA_FOR_CHANNEL_SCAN_FILE, 0));
TextView scanTitleView = (TextView) view.findViewById(R.id.tune_title);
@@ -172,21 +174,24 @@ public class ScanFragment extends SetupFragment {
mChannelScanTask.cancelScan(cancel);
// Notifies a user of waiting to finish the scanning process.
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- if (mChannelScanTask != null) {
- mChannelScanTask.showFinishingProgressDialog();
- }
- }
- }, SHOW_PROGRESS_DIALOG_DELAY_MS);
+ new Handler()
+ .postDelayed(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mChannelScanTask != null) {
+ mChannelScanTask.showFinishingProgressDialog();
+ }
+ }
+ },
+ SHOW_PROGRESS_DIALOG_DELAY_MS);
// Hides the cancel button.
mCancelButton.setEnabled(false);
}
}
- private class ChannelAdapter extends BaseAdapter {
+ private static class ChannelAdapter extends BaseAdapter {
private final ArrayList<TunerChannel> mChannels;
public ChannelAdapter() {
@@ -223,8 +228,8 @@ public class ScanFragment extends SetupFragment {
final Context context = parent.getContext();
if (convertView == null) {
- LayoutInflater inflater = (LayoutInflater) context
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ LayoutInflater inflater =
+ (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.ut_channel_list, parent, false);
}
@@ -264,7 +269,7 @@ public class ScanFragment extends SetupFragment {
if (FAKE_MODE) {
mScanTsStreamer = new FakeTsStreamer(this);
} else {
- TunerHal hal = ((TunerSetupActivity) mActivity).getTunerHal();
+ TunerHal hal = ((BaseTunerSetupActivity) mActivity).getTunerHal();
if (hal == null) {
throw new RuntimeException("Failed to open a DVB device");
}
@@ -276,34 +281,44 @@ public class ScanFragment extends SetupFragment {
}
private void maybeSetChannelListVisible() {
- mActivity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- int channelsFound = mAdapter.getCount();
- if (!mChannelListVisible && channelsFound > 0) {
- String format = getResources().getQuantityString(
- R.plurals.ut_channel_scan_message, channelsFound, channelsFound);
- mScanningMessage.setText(String.format(format, channelsFound));
- mChannelHolder.setVisibility(View.VISIBLE);
- mChannelListVisible = true;
- }
- }
- });
+ mActivity.runOnUiThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ int channelsFound = mAdapter.getCount();
+ if (!mChannelListVisible && channelsFound > 0) {
+ String format =
+ getResources()
+ .getQuantityString(
+ R.plurals.ut_channel_scan_message,
+ channelsFound,
+ channelsFound);
+ mScanningMessage.setText(String.format(format, channelsFound));
+ mChannelHolder.setVisibility(View.VISIBLE);
+ mChannelListVisible = true;
+ }
+ }
+ });
}
private void addChannel(final TunerChannel channel) {
- mActivity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mAdapter.add(channel);
- if (mChannelListVisible) {
- int channelsFound = mAdapter.getCount();
- String format = getResources().getQuantityString(
- R.plurals.ut_channel_scan_message, channelsFound, channelsFound);
- mScanningMessage.setText(String.format(format, channelsFound));
- }
- }
- });
+ mActivity.runOnUiThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ mAdapter.add(channel);
+ if (mChannelListVisible) {
+ int channelsFound = mAdapter.getCount();
+ String format =
+ getResources()
+ .getQuantityString(
+ R.plurals.ut_channel_scan_message,
+ channelsFound,
+ channelsFound);
+ mScanningMessage.setText(String.format(format, channelsFound));
+ }
+ }
+ });
}
@Override
@@ -312,8 +327,9 @@ public class ScanFragment extends SetupFragment {
if (SCAN_LOCAL_STREAMS) {
FileTsStreamer.addLocalStreamFiles(mScanChannelList);
}
- mScanChannelList.addAll(ChannelScanFileParser.parseScanFile(
- getResources().openRawResource(mChannelMapId)));
+ mScanChannelList.addAll(
+ ChannelScanFileParser.parseScanFile(
+ getResources().openRawResource(mChannelMapId)));
scanChannels();
return null;
}
@@ -362,11 +378,13 @@ public class ScanFragment extends SetupFragment {
try {
mLatch.await(CHANNEL_SCAN_PERIOD_MS, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
- Log.e(TAG, "The current thread is interrupted during scanChannels(). " +
- "The TS stream is stopped earlier than expected.", e);
+ Log.e(
+ TAG,
+ "The current thread is interrupted during scanChannels(). "
+ + "The TS stream is stopped earlier than expected.",
+ e);
}
streamer.stopStream();
-
addChannelsWithoutVct(scanChannel);
if (System.currentTimeMillis() > startMs + CHANNEL_SCAN_SHOW_DELAY_MS
&& !mChannelListVisible) {
@@ -385,21 +403,23 @@ public class ScanFragment extends SetupFragment {
if (DEBUG) Log.i(TAG, "Channel scan ended");
}
-
private void addChannelsWithoutVct(ChannelScanFileParser.ScanChannel scanChannel) {
if (scanChannel.radioFrequencyNumber == null
|| !(mScanTsStreamer instanceof TunerTsStreamer)) {
return;
}
- for (TunerChannel tunerChannel
- : ((TunerTsStreamer) mScanTsStreamer).getMalFormedChannels()) {
+ for (TunerChannel tunerChannel :
+ ((TunerTsStreamer) mScanTsStreamer).getMalFormedChannels()) {
if ((tunerChannel.getVideoPid() != TunerChannel.INVALID_PID)
&& (tunerChannel.getAudioPid() != TunerChannel.INVALID_PID)) {
tunerChannel.setFrequency(scanChannel.frequency);
tunerChannel.setModulation(scanChannel.modulation);
- tunerChannel.setShortName(String.format(Locale.US, VCTLESS_CHANNEL_NAME_FORMAT,
- scanChannel.radioFrequencyNumber,
- tunerChannel.getProgramNumber()));
+ tunerChannel.setShortName(
+ String.format(
+ Locale.US,
+ VCTLESS_CHANNEL_NAME_FORMAT,
+ scanChannel.radioFrequencyNumber,
+ tunerChannel.getProgramNumber()));
tunerChannel.setVirtualMajor(scanChannel.radioFrequencyNumber);
tunerChannel.setVirtualMinor(tunerChannel.getProgramNumber());
onChannelDetected(tunerChannel, true);
@@ -409,9 +429,9 @@ public class ScanFragment extends SetupFragment {
private TsStreamer getStreamer(int type) {
switch (type) {
- case Channel.TYPE_TUNER:
+ case Channel.TunerType.TYPE_TUNER:
return mScanTsStreamer;
- case Channel.TYPE_FILE:
+ case Channel.TunerType.TYPE_FILE:
return mFileTsStreamer;
default:
return null;
@@ -440,14 +460,16 @@ public class ScanFragment extends SetupFragment {
// No video-only channel has been found.
addChannel(channel);
mChannelDataManager.notifyChannelDetected(channel, channelArrivedAtFirstTime);
+ mChannelNumbers.add(channel.getDisplayNumber());
}
}
public void showFinishingProgressDialog() {
// Show a progress dialog to wait for the scanning process if it's not done yet.
if (!mIsFinished && mFinishingProgressDialog == null) {
- mFinishingProgressDialog = ProgressDialog.show(mActivity, "",
- getString(R.string.ut_setup_cancel), true, false);
+ mFinishingProgressDialog =
+ ProgressDialog.show(
+ mActivity, "", getString(R.string.ut_setup_cancel), true, false);
}
}
@@ -456,10 +478,11 @@ public class ScanFragment extends SetupFragment {
mChannelDataManager.setCurrentVersion(mActivity);
mChannelDataManager.releaseSafely();
mIsFinished = true;
- TunerPreferences.setScannedChannelCount(mActivity.getApplicationContext(),
+ TunerPreferences.setScannedChannelCount(
+ mActivity.getApplicationContext(),
mChannelDataManager.getScannedChannelCount());
// Cancel a previously shown notification.
- TunerSetupActivity.cancelNotification(mActivity.getApplicationContext());
+ BaseTunerSetupActivity.cancelNotification(mActivity.getApplicationContext());
// Mark scan as done
TunerPreferences.setScanDone(mActivity.getApplicationContext());
// finishing will be done manually.
@@ -469,7 +492,13 @@ public class ScanFragment extends SetupFragment {
// If the fragment is not resumed, the next fragment (scan result page) can't be
// displayed. In that case, just close the activity.
if (isResumed()) {
- onActionClick(ACTION_CATEGORY, mIsCanceled ? ACTION_CANCEL : ACTION_FINISH);
+ if (mIsCanceled) {
+ onActionClick(ACTION_CATEGORY, ACTION_CANCEL);
+ } else {
+ Bundle params = new Bundle();
+ params.putStringArrayList(KEY_CHANNEL_NUMBERS, mChannelNumbers);
+ onActionClick(ACTION_CATEGORY, ACTION_FINISH, params);
+ }
} else if (getActivity() != null) {
getActivity().finish();
}
@@ -492,17 +521,19 @@ public class ScanFragment extends SetupFragment {
}
final String displayNumber = Integer.toString(mProgramNumber);
final String name = "Channel-" + mProgramNumber;
- mEventListener.onChannelDetected(new TunerChannel(mProgramNumber, new ArrayList<>()) {
- @Override
- public String getDisplayNumber() {
- return displayNumber;
- }
-
- @Override
- public String getName() {
- return name;
- }
- }, true);
+ mEventListener.onChannelDetected(
+ new TunerChannel(mProgramNumber, new ArrayList<>()) {
+ @Override
+ public String getDisplayNumber() {
+ return displayNumber;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+ },
+ true);
return true;
}
@@ -512,8 +543,7 @@ public class ScanFragment extends SetupFragment {
}
@Override
- public void stopStream() {
- }
+ public void stopStream() {}
@Override
public TsDataSource createDataSource() {
diff --git a/src/com/android/tv/tuner/setup/ScanResultFragment.java b/tuner/src/com/android/tv/tuner/setup/ScanResultFragment.java
index 3b8cd823..480bf081 100644
--- a/src/com/android/tv/tuner/setup/ScanResultFragment.java
+++ b/tuner/src/com/android/tv/tuner/setup/ScanResultFragment.java
@@ -22,26 +22,23 @@ import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
import android.support.v17.leanback.widget.GuidedAction;
-
import com.android.tv.common.ui.setup.SetupGuidedStepFragment;
import com.android.tv.common.ui.setup.SetupMultiPaneFragment;
import com.android.tv.tuner.R;
import com.android.tv.tuner.TunerHal;
import com.android.tv.tuner.TunerPreferences;
-import com.android.tv.tuner.util.TunerInputInfoUtils;
-
import java.util.List;
-/**
- * A fragment for initial screen.
- */
+/** A fragment for initial screen. */
public class ScanResultFragment extends SetupMultiPaneFragment {
- public static final String ACTION_CATEGORY =
- "com.android.tv.tuner.setup.ScanResultFragment";
+ public static final String ACTION_CATEGORY = "com.android.tv.tuner.setup.ScanResultFragment";
@Override
protected SetupGuidedStepFragment onCreateContentFragment() {
- return new ContentFragment();
+ Bundle args = new Bundle();
+ ContentFragment fragment = new ContentFragment();
+ fragment.setArguments(args);
+ return fragment;
}
@Override
@@ -54,6 +51,7 @@ public class ScanResultFragment extends SetupMultiPaneFragment {
return false;
}
+ /** The content fragment of {@link ScanResultFragment}. */
public static class ContentFragment extends SetupGuidedStepFragment {
private int mChannelCountOnPreference;
@@ -71,15 +69,18 @@ public class ScanResultFragment extends SetupMultiPaneFragment {
String breadcrumb;
if (mChannelCountOnPreference > 0) {
Resources res = getResources();
- title = res.getQuantityString(R.plurals.ut_result_found_title,
- mChannelCountOnPreference, mChannelCountOnPreference);
- description = res.getQuantityString(R.plurals.ut_result_found_description,
- mChannelCountOnPreference, mChannelCountOnPreference);
+ title =
+ res.getQuantityString(
+ R.plurals.ut_result_found_title,
+ mChannelCountOnPreference,
+ mChannelCountOnPreference);
+ description = res.getString(R.string.ut_result_found_description);
+
breadcrumb = null;
} else {
Bundle args = getArguments();
int tunerType =
- (args == null ? 0 : args.getInt(TunerSetupActivity.KEY_TUNER_TYPE, 0));
+ (args == null ? 0 : args.getInt(BaseTunerSetupActivity.KEY_TUNER_TYPE, 0));
title = getString(R.string.ut_result_not_found_title);
switch (tunerType) {
case TunerHal.TUNER_TYPE_USB:
@@ -97,8 +98,8 @@ public class ScanResultFragment extends SetupMultiPaneFragment {
}
@Override
- public void onCreateActions(@NonNull List<GuidedAction> actions,
- Bundle savedInstanceState) {
+ public void onCreateActions(
+ @NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
String[] choices;
int doneActionIndex;
if (mChannelCountOnPreference > 0) {
@@ -110,11 +111,17 @@ public class ScanResultFragment extends SetupMultiPaneFragment {
}
for (int i = 0; i < choices.length; ++i) {
if (i == doneActionIndex) {
- actions.add(new GuidedAction.Builder(getActivity()).id(ACTION_DONE)
- .title(choices[i]).build());
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(ACTION_DONE)
+ .title(choices[i])
+ .build());
} else {
- actions.add(new GuidedAction.Builder(getActivity()).id(i).title(choices[i])
- .build());
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(i)
+ .title(choices[i])
+ .build());
}
}
}
diff --git a/src/com/android/tv/tuner/setup/WelcomeFragment.java b/tuner/src/com/android/tv/tuner/setup/WelcomeFragment.java
index feae1ec9..788ba918 100644
--- a/src/com/android/tv/tuner/setup/WelcomeFragment.java
+++ b/tuner/src/com/android/tv/tuner/setup/WelcomeFragment.java
@@ -28,12 +28,9 @@ import com.android.tv.tuner.TunerHal;
import com.android.tv.tuner.TunerPreferences;
import java.util.List;
-/**
- * A fragment for initial screen.
- */
+/** A fragment for initial screen. */
public class WelcomeFragment extends SetupMultiPaneFragment {
- public static final String ACTION_CATEGORY =
- "com.android.tv.tuner.setup.WelcomeFragment";
+ public static final String ACTION_CATEGORY = "com.android.tv.tuner.setup.WelcomeFragment";
@Override
protected SetupGuidedStepFragment onCreateContentFragment() {
@@ -52,6 +49,7 @@ public class WelcomeFragment extends SetupMultiPaneFragment {
return false;
}
+ /** The content fragment of {@link WelcomeFragment}. */
public static class ContentFragment extends SetupGuidedStepFragment {
private int mChannelCountOnPreference;
@@ -67,8 +65,11 @@ public class WelcomeFragment extends SetupMultiPaneFragment {
public Guidance onCreateGuidance(Bundle savedInstanceState) {
String title;
String description;
- int tunerType = getArguments().getInt(TunerSetupActivity.KEY_TUNER_TYPE,
- TunerHal.TUNER_TYPE_BUILT_IN);
+ int tunerType =
+ getArguments()
+ .getInt(
+ BaseTunerSetupActivity.KEY_TUNER_TYPE,
+ TunerHal.TUNER_TYPE_BUILT_IN);
if (mChannelCountOnPreference == 0) {
switch (tunerType) {
case TunerHal.TUNER_TYPE_USB:
@@ -100,16 +101,23 @@ public class WelcomeFragment extends SetupMultiPaneFragment {
}
@Override
- public void onCreateActions(@NonNull List<GuidedAction> actions,
- Bundle savedInstanceState) {
- String[] choices = getResources().getStringArray(mChannelCountOnPreference == 0
- ? R.array.ut_setup_new_choices : R.array.ut_setup_again_choices);
+ public void onCreateActions(
+ @NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
+ String[] choices =
+ getResources()
+ .getStringArray(
+ mChannelCountOnPreference == 0
+ ? R.array.ut_setup_new_choices
+ : R.array.ut_setup_again_choices);
for (int i = 0; i < choices.length - 1; ++i) {
- actions.add(new GuidedAction.Builder(getActivity()).id(i).title(choices[i])
- .build());
+ actions.add(
+ new GuidedAction.Builder(getActivity()).id(i).title(choices[i]).build());
}
- actions.add(new GuidedAction.Builder(getActivity()).id(ACTION_DONE)
- .title(choices[choices.length - 1]).build());
+ actions.add(
+ new GuidedAction.Builder(getActivity())
+ .id(ACTION_DONE)
+ .title(choices[choices.length - 1])
+ .build());
}
@Override
diff --git a/src/com/android/tv/tuner/source/FileTsStreamer.java b/tuner/src/com/android/tv/tuner/source/FileTsStreamer.java
index f17dd46b..38a59b3d 100644
--- a/src/com/android/tv/tuner/source/FileTsStreamer.java
+++ b/tuner/src/com/android/tv/tuner/source/FileTsStreamer.java
@@ -20,17 +20,15 @@ import android.content.Context;
import android.os.Environment;
import android.util.Log;
import android.util.SparseBooleanArray;
-
-import com.google.android.exoplayer.C;
-import com.google.android.exoplayer.upstream.DataSpec;
-import com.android.tv.Features;
import com.android.tv.common.SoftPreconditions;
import com.android.tv.tuner.ChannelScanFileParser.ScanChannel;
+import com.android.tv.tuner.TunerFeatures;
import com.android.tv.tuner.data.TunerChannel;
import com.android.tv.tuner.ts.TsParser;
import com.android.tv.tuner.tvinput.EventDetector;
import com.android.tv.tuner.tvinput.FileSourceEventDetector;
-
+import com.google.android.exoplayer.C;
+import com.google.android.exoplayer.upstream.DataSpec;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -105,13 +103,16 @@ public class FileTsStreamer implements TsStreamer {
}
@Override
- public void close() {
- }
+ public void close() {}
@Override
public int read(byte[] buffer, int offset, int readLength) throws IOException {
- int ret = mTsStreamer.readAt(mStartBufferedPosition + mLastReadPosition.get(), buffer,
- offset, readLength);
+ int ret =
+ mTsStreamer.readAt(
+ mStartBufferedPosition + mLastReadPosition.get(),
+ buffer,
+ offset,
+ readLength);
if (ret > 0) {
mLastReadPosition.addAndGet(ret);
}
@@ -121,12 +122,13 @@ public class FileTsStreamer implements TsStreamer {
/**
* Creates {@link TsStreamer} for scanning & playing MPEG-2 TS file.
+ *
* @param eventListener the listener for channel & program information
*/
public FileTsStreamer(EventDetector.EventListener eventListener, Context context) {
mEventDetector =
new FileSourceEventDetector(
- eventListener, Features.ENABLE_FILE_DVB.isEnabled(context));
+ eventListener, TunerFeatures.ENABLE_FILE_DVB.isEnabled(context));
mContext = context;
}
@@ -140,7 +142,7 @@ public class FileTsStreamer implements TsStreamer {
mEventDetector.start(mSource, FileSourceEventDetector.ALL_PROGRAM_NUMBERS);
mSource.addPidFilter(TsParser.PAT_PID);
mSource.addPidFilter(TsParser.ATSC_SI_BASE_PID);
- if (Features.ENABLE_FILE_DVB.isEnabled(mContext)) {
+ if (TunerFeatures.ENABLE_FILE_DVB.isEnabled(mContext)) {
mSource.addPidFilter(TsParser.DVB_EIT_PID);
mSource.addPidFilter(TsParser.DVB_SDT_PID);
}
@@ -172,7 +174,7 @@ public class FileTsStreamer implements TsStreamer {
mSource.addPidFilter(channel.getPcrPid());
mSource.addPidFilter(TsParser.PAT_PID);
mSource.addPidFilter(TsParser.ATSC_SI_BASE_PID);
- if (Features.ENABLE_FILE_DVB.isEnabled(mContext)) {
+ if (TunerFeatures.ENABLE_FILE_DVB.isEnabled(mContext)) {
mSource.addPidFilter(TsParser.DVB_EIT_PID);
mSource.addPidFilter(TsParser.DVB_SDT_PID);
}
@@ -216,6 +218,7 @@ public class FileTsStreamer implements TsStreamer {
/**
* Returns the current buffered position from the file.
+ *
* @return the current buffered position
*/
public long getBufferedPosition() {
@@ -224,9 +227,7 @@ public class FileTsStreamer implements TsStreamer {
}
}
- /**
- * Provides MPEG-2 transport stream from a local file. Stream can be filtered by PID.
- */
+ /** Provides MPEG-2 transport stream from a local file. Stream can be filtered by PID. */
public static class StreamProvider {
private final String mFilepath;
private final SparseBooleanArray mPids = new SparseBooleanArray();
@@ -252,36 +253,29 @@ public class FileTsStreamer implements TsStreamer {
return mInputStream != null;
}
- /**
- * Returns the file path of the MPEG-2 TS file.
- */
+ /** Returns the file path of the MPEG-2 TS file. */
public String getFilepath() {
return mFilepath;
}
- /**
- * Adds a pid for filtering from the MPEG-2 TS file.
- */
+ /** Adds a pid for filtering from the MPEG-2 TS file. */
public void addPidFilter(int pid) {
mPids.put(pid, true);
}
- /**
- * Returns whether the current pid filter is empty or not.
- */
+ /** Returns whether the current pid filter is empty or not. */
public boolean isFilterEmpty() {
return mPids.size() == 0;
}
- /**
- * Clears the current pid filter.
- */
+ /** Clears the current pid filter. */
public void clearPidFilter() {
mPids.clear();
}
/**
* Returns whether a pid is in the pid filter or not.
+ *
* @param pid the pid to check
*/
public boolean isInFilter(int pid) {
@@ -347,6 +341,7 @@ public class FileTsStreamer implements TsStreamer {
/**
* Reads data from internal buffer.
+ *
* @param pos the position to read from
* @param buffer to read
* @param offset start position of the read buffer
@@ -387,7 +382,11 @@ public class FileTsStreamer implements TsStreamer {
}
System.arraycopy(mCircularBuffer, posInBuffer, buffer, offset, bytesToCopyInFirstPass);
if (bytesToCopyInFirstPass < amount) {
- System.arraycopy(mCircularBuffer, 0, buffer, offset + bytesToCopyInFirstPass,
+ System.arraycopy(
+ mCircularBuffer,
+ 0,
+ buffer,
+ offset + bytesToCopyInFirstPass,
amount - bytesToCopyInFirstPass);
}
mLastReadPosition = pos + amount;
@@ -416,9 +415,9 @@ public class FileTsStreamer implements TsStreamer {
}
/**
- * A thread managing a circular buffer that holds stream data to be consumed by player.
- * Keeps reading data in from a {@link StreamProvider} to hold enough amount for buffering.
- * Started and stopped by {@link #startStream()} and {@link #stopStream()}, respectively.
+ * A thread managing a circular buffer that holds stream data to be consumed by player. Keeps
+ * reading data in from a {@link StreamProvider} to hold enough amount for buffering. Started
+ * and stopped by {@link #startStream()} and {@link #stopStream()}, respectively.
*/
private class StreamingThread extends Thread {
@Override
@@ -466,10 +465,14 @@ public class FileTsStreamer implements TsStreamer {
if (posInBuffer + bytesToCopyInFirstPass > mCircularBuffer.length) {
bytesToCopyInFirstPass = mCircularBuffer.length - posInBuffer;
}
- System.arraycopy(dataBuffer, 0, mCircularBuffer, posInBuffer,
- bytesToCopyInFirstPass);
+ System.arraycopy(
+ dataBuffer, 0, mCircularBuffer, posInBuffer, bytesToCopyInFirstPass);
if (bytesToCopyInFirstPass < bytesWritten) {
- System.arraycopy(dataBuffer, bytesToCopyInFirstPass, mCircularBuffer, 0,
+ System.arraycopy(
+ dataBuffer,
+ bytesToCopyInFirstPass,
+ mCircularBuffer,
+ 0,
bytesWritten - bytesToCopyInFirstPass);
}
mBytesFetched += bytesWritten;
diff --git a/src/com/android/tv/tuner/source/TsDataSource.java b/tuner/src/com/android/tv/tuner/source/TsDataSource.java
index 2ce3e670..be902944 100644
--- a/src/com/android/tv/tuner/source/TsDataSource.java
+++ b/tuner/src/com/android/tv/tuner/source/TsDataSource.java
@@ -18,9 +18,7 @@ package com.android.tv.tuner.source;
import com.google.android.exoplayer.upstream.DataSource;
-/**
- * {@link DataSource} for MPEG-TS stream, which will be used by {@link TsExtractor}.
- */
+/** {@link DataSource} for MPEG-TS stream, which will be used by {@link TsExtractor}. */
public abstract class TsDataSource implements DataSource {
/**
@@ -42,9 +40,10 @@ public abstract class TsDataSource implements DataSource {
}
/**
- * Shifts start position by the specified offset.
- * Do not call this method when the class already provided MPEG-TS stream to the extractor.
+ * Shifts start position by the specified offset. Do not call this method when the class already
+ * provided MPEG-TS stream to the extractor.
+ *
* @param offset 0 <= offset <= buffered position
*/
- public void shiftStartPosition(long offset) { }
-} \ No newline at end of file
+ public void shiftStartPosition(long offset) {}
+}
diff --git a/src/com/android/tv/tuner/source/TsDataSourceManager.java b/tuner/src/com/android/tv/tuner/source/TsDataSourceManager.java
index 16be7582..08acbc88 100644
--- a/src/com/android/tv/tuner/source/TsDataSourceManager.java
+++ b/tuner/src/com/android/tv/tuner/source/TsDataSourceManager.java
@@ -18,24 +18,21 @@ package com.android.tv.tuner.source;
import android.content.Context;
import android.support.annotation.VisibleForTesting;
-
import com.android.tv.tuner.TunerHal;
import com.android.tv.tuner.data.TunerChannel;
import com.android.tv.tuner.data.nano.Channel;
import com.android.tv.tuner.tvinput.EventDetector;
-
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
- * Manages {@link DataSource} for playback and recording.
- * The class hides handling of {@link TunerHal} and {@link TsStreamer} from other classes.
- * One TsDataSourceManager should be created for per session.
+ * Manages {@link DataSource} for playback and recording. The class hides handling of {@link
+ * TunerHal} and {@link TsStreamer} from other classes. One TsDataSourceManager should be created
+ * for per session.
*/
public class TsDataSourceManager {
private static final Object sLock = new Object();
- private static final Map<TsDataSource, TsStreamer> sTsStreamers =
- new ConcurrentHashMap<>();
+ private static final Map<TsDataSource, TsStreamer> sTsStreamers = new ConcurrentHashMap<>();
private static int sSequenceId;
@@ -47,8 +44,9 @@ public class TsDataSourceManager {
private boolean mKeepTuneStatus;
/**
- * Creates TsDataSourceManager to create and release {@link DataSource} which will be
- * used for playing and recording.
+ * Creates TsDataSourceManager to create and release {@link DataSource} which will be used for
+ * playing and recording.
+ *
* @param isRecording {@code true} when for recording, {@code false} otherwise
* @return {@link TsDataSourceManager}
*/
@@ -68,14 +66,15 @@ public class TsDataSourceManager {
/**
* Creates or retrieves {@link TsDataSource} for playing or recording
+ *
* @param context a {@link Context} instance
* @param channel to play or record
* @param eventListener for program information which will be scanned from MPEG2-TS stream
* @return {@link TsDataSource} which will provide the specified channel stream
*/
- public TsDataSource createDataSource(Context context, TunerChannel channel,
- EventDetector.EventListener eventListener) {
- if (channel.getType() == Channel.TYPE_FILE) {
+ public TsDataSource createDataSource(
+ Context context, TunerChannel channel, EventDetector.EventListener eventListener) {
+ if (channel.getType() == Channel.TunerType.TYPE_FILE) {
// MPEG2 TS captured stream file recording is not supported.
if (mIsRecording) {
return null;
@@ -88,18 +87,18 @@ public class TsDataSourceManager {
}
return null;
}
- return mTunerStreamerManager.createDataSource(context, channel, eventListener,
- mId, !mIsRecording && mKeepTuneStatus);
+ return mTunerStreamerManager.createDataSource(
+ context, channel, eventListener, mId, !mIsRecording && mKeepTuneStatus);
}
/**
* Releases the specified {@link TsDataSource} and underlying {@link TunerHal}.
+ *
* @param source to release
*/
public void releaseDataSource(TsDataSource source) {
if (source instanceof TunerTsStreamer.TunerDataSource) {
- mTunerStreamerManager.releaseDataSource(
- source, mId, !mIsRecording && mKeepTuneStatus);
+ mTunerStreamerManager.releaseDataSource(source, mId, !mIsRecording && mKeepTuneStatus);
} else if (source instanceof FileTsStreamer.FileDataSource) {
FileTsStreamer streamer = (FileTsStreamer) sTsStreamers.get(source);
if (streamer != null) {
@@ -109,34 +108,28 @@ public class TsDataSourceManager {
}
}
- /**
- * Indicates that the current session has pending tunes.
- */
+ /** Indicates that the current session has pending tunes. */
public void setHasPendingTune() {
mTunerStreamerManager.setHasPendingTune(mId);
}
/**
- * Indicates whether the underlying {@link TunerHal} should be kept or not when data source
- * is being released.
- * TODO: If b/30750953 is fixed, we can remove this function.
+ * Indicates whether the underlying {@link TunerHal} should be kept or not when data source is
+ * being released. TODO: If b/30750953 is fixed, we can remove this function.
+ *
* @param keepTuneStatus underlying {@link TunerHal} will be reused when data source releasing.
*/
public void setKeepTuneStatus(boolean keepTuneStatus) {
mKeepTuneStatus = keepTuneStatus;
}
- /**
- * Add tuner hal into TunerTsStreamerManager for test.
- */
+ /** Add tuner hal into TunerTsStreamerManager for test. */
@VisibleForTesting
public void addTunerHalForTest(TunerHal tunerHal) {
mTunerStreamerManager.addTunerHal(tunerHal, mId);
}
- /**
- * Releases persistent resources.
- */
+ /** Releases persistent resources. */
public void release() {
mTunerStreamerManager.release(mId);
}
diff --git a/src/com/android/tv/tuner/source/TsStreamWriter.java b/tuner/src/com/android/tv/tuner/source/TsStreamWriter.java
index 30650555..f90136bf 100644
--- a/src/com/android/tv/tuner/source/TsStreamWriter.java
+++ b/tuner/src/com/android/tv/tuner/source/TsStreamWriter.java
@@ -19,7 +19,6 @@ package com.android.tv.tuner.source;
import android.content.Context;
import android.util.Log;
import com.android.tv.tuner.data.TunerChannel;
-
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
@@ -27,9 +26,7 @@ import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
-/**
- * Stores TS files to the disk for debugging.
- */
+/** Stores TS files to the disk for debugging. */
public class TsStreamWriter {
private static final String TAG = "TsStreamWriter";
private static final boolean DEBUG = false;
@@ -79,16 +76,19 @@ public class TsStreamWriter {
mChannel = channel;
}
- /**
- * Opens a file to store TS data.
- */
+ /** Opens a file to store TS data. */
public void openFile() {
if (mChannel == null || mDirectoryPath == null) {
return;
}
mFileStartTimeMs = System.currentTimeMillis();
- mFileName = mChannel.getDisplayNumber() + SEPARATOR + mFileStartTimeMs + SEPARATOR
- + mInstanceId + ".ts";
+ mFileName =
+ mChannel.getDisplayNumber()
+ + SEPARATOR
+ + mFileStartTimeMs
+ + SEPARATOR
+ + mInstanceId
+ + ".ts";
String filePath = mDirectoryPath + "/" + mFileName;
try {
mFileOutputStream = new FileOutputStream(filePath, false);
@@ -101,7 +101,7 @@ public class TsStreamWriter {
* Closes the file and stops storing TS data.
*
* @param calledWhenStopStream {@code true} if this method is called when the stream is stopped
- * {@code false} otherwise
+ * {@code false} otherwise
*/
public void closeFile(boolean calledWhenStopStream) {
if (mFileOutputStream == null) {
@@ -141,8 +141,8 @@ public class TsStreamWriter {
/**
* Deletes outdated files to save storage.
*
- * @param deleteAll {@code true} if all the files with the relative ID should be deleted
- * {@code false} if the most recent file should not be deleted
+ * @param deleteAll {@code true} if all the files with the relative ID should be deleted {@code
+ * false} if the most recent file should not be deleted
*/
private void deleteOutdatedFiles(boolean deleteAll) {
if (mFileName == null) {
@@ -157,7 +157,8 @@ public class TsStreamWriter {
return;
}
for (File file : mDirectory.listFiles()) {
- if (file.isFile() && getFileId(file) == mInstanceId
+ if (file.isFile()
+ && getFileId(file) == mInstanceId
&& (deleteAll || !mFileName.equals(file.getName()))) {
boolean deleted = file.delete();
if (DEBUG && !deleted) {
@@ -178,11 +179,11 @@ public class TsStreamWriter {
}
Set<Integer> idSet = getExistingIds();
if (idSet == null) {
- return NO_INSTANCE_ID;
+ return NO_INSTANCE_ID;
}
for (int i = 0; i < MAX_GET_ID_RETRY_COUNT; i++) {
// Range [1, MAX_INSTANCE_ID]
- int id = (int)Math.floor(Math.random() * MAX_INSTANCE_ID) + 1;
+ int id = (int) Math.floor(Math.random() * MAX_INSTANCE_ID) + 1;
if (!idSet.contains(id)) {
return id;
}
@@ -203,7 +204,7 @@ public class TsStreamWriter {
Set<Integer> idSet = new HashSet<>();
for (File file : mDirectory.listFiles()) {
int id = getFileId(file);
- if(id != NO_INSTANCE_ID) {
+ if (id != NO_INSTANCE_ID) {
idSet.add(id);
}
}
diff --git a/src/com/android/tv/tuner/source/TsStreamer.java b/tuner/src/com/android/tv/tuner/source/TsStreamer.java
index 1ac950bb..3dbba7e7 100644
--- a/src/com/android/tv/tuner/source/TsStreamer.java
+++ b/tuner/src/com/android/tv/tuner/source/TsStreamer.java
@@ -20,8 +20,8 @@ import com.android.tv.tuner.ChannelScanFileParser;
import com.android.tv.tuner.data.TunerChannel;
/**
- * Interface definition for a stream generator. The interface will provide streams
- * for scanning channels and/or playback.
+ * Interface definition for a stream generator. The interface will provide streams for scanning
+ * channels and/or playback.
*/
public interface TsStreamer {
/**
@@ -40,15 +40,12 @@ public interface TsStreamer {
*/
boolean startStream(TunerChannel channel);
- /**
- * Stops streaming the data.
- */
+ /** Stops streaming the data. */
void stopStream();
/**
- * Creates {@link TsDataSource} which will provide MPEG-2 TS stream for
- * {@link android.media.MediaExtractor}. The source will start from the position
- * where it is created.
+ * Creates {@link TsDataSource} which will provide MPEG-2 TS stream for {@link
+ * android.media.MediaExtractor}. The source will start from the position where it is created.
*
* @return {@link TsDataSource}
*/
diff --git a/src/com/android/tv/tuner/source/TunerTsStreamer.java b/tuner/src/com/android/tv/tuner/source/TunerTsStreamer.java
index 843cbdb7..21b7a1f8 100644
--- a/src/com/android/tv/tuner/source/TunerTsStreamer.java
+++ b/tuner/src/com/android/tv/tuner/source/TunerTsStreamer.java
@@ -19,9 +19,6 @@ package com.android.tv.tuner.source;
import android.content.Context;
import android.util.Log;
import android.util.Pair;
-
-import com.google.android.exoplayer.C;
-import com.google.android.exoplayer.upstream.DataSpec;
import com.android.tv.common.SoftPreconditions;
import com.android.tv.tuner.ChannelScanFileParser;
import com.android.tv.tuner.TunerHal;
@@ -29,21 +26,20 @@ import com.android.tv.tuner.TunerPreferences;
import com.android.tv.tuner.data.TunerChannel;
import com.android.tv.tuner.tvinput.EventDetector;
import com.android.tv.tuner.tvinput.EventDetector.EventListener;
-
+import com.google.android.exoplayer.C;
+import com.google.android.exoplayer.upstream.DataSpec;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
-/**
- * Provides MPEG-2 TS stream sources for channel playing from an underlying tuner device.
- */
+/** Provides MPEG-2 TS stream sources for channel playing from an underlying tuner device. */
public class TunerTsStreamer implements TsStreamer {
private static final String TAG = "TunerTsStreamer";
private static final int MIN_READ_UNIT = 1500;
private static final int READ_BUFFER_SIZE = MIN_READ_UNIT * 10; // ~15KB
- private static final int CIRCULAR_BUFFER_SIZE = MIN_READ_UNIT * 20000; // ~ 30MB
+ private static final int CIRCULAR_BUFFER_SIZE = MIN_READ_UNIT * 20000; // ~ 30MB
private static final int TS_PACKET_SIZE = 188;
private static final int READ_TIMEOUT_MS = 5000; // 5 secs.
@@ -100,20 +96,24 @@ public class TunerTsStreamer implements TsStreamer {
}
@Override
- public void close() {
- }
+ public void close() {}
@Override
public int read(byte[] buffer, int offset, int readLength) throws IOException {
- int ret = mTsStreamer.readAt(mStartBufferedPosition + mLastReadPosition.get(), buffer,
- offset, readLength);
+ int ret =
+ mTsStreamer.readAt(
+ mStartBufferedPosition + mLastReadPosition.get(),
+ buffer,
+ offset,
+ readLength);
if (ret > 0) {
mLastReadPosition.addAndGet(ret);
} else if (ret == READ_ERROR_BUFFER_OVERWRITTEN) {
long currentPosition = mStartBufferedPosition + mLastReadPosition.get();
long endPosition = mTsStreamer.getBufferedPosition();
- long diff = ((endPosition - currentPosition + TS_PACKET_SIZE - 1) / TS_PACKET_SIZE)
- * TS_PACKET_SIZE;
+ long diff =
+ ((endPosition - currentPosition + TS_PACKET_SIZE - 1) / TS_PACKET_SIZE)
+ * TS_PACKET_SIZE;
Log.w(TAG, "Demux position jump by overwritten buffer: " + diff);
mStartBufferedPosition = currentPosition + diff;
mLastReadPosition.set(0);
@@ -124,6 +124,7 @@ public class TunerTsStreamer implements TsStreamer {
}
/**
* Creates {@link TsStreamer} for playing or recording the specified channel.
+ *
* @param tunerHal the HAL for tuner device
* @param eventListener the listener for channel & program information
*/
@@ -133,8 +134,10 @@ public class TunerTsStreamer implements TsStreamer {
if (eventListener != null) {
mEventDetector.registerListener(eventListener);
}
- mTsStreamWriter = context != null && TunerPreferences.getStoreTsStream(context) ?
- new TsStreamWriter(context) : null;
+ mTsStreamWriter =
+ context != null && TunerPreferences.getStoreTsStream(context)
+ ? new TsStreamWriter(context)
+ : null;
}
public TunerTsStreamer(TunerHal tunerHal, EventListener eventListener) {
@@ -143,11 +146,10 @@ public class TunerTsStreamer implements TsStreamer {
@Override
public boolean startStream(TunerChannel channel) {
- if (mTunerHal.tune(channel.getFrequency(), channel.getModulation(),
- channel.getDisplayNumber(false))) {
+ if (mTunerHal.tune(
+ channel.getFrequency(), channel.getModulation(), channel.getDisplayNumber(false))) {
if (channel.hasVideo()) {
- mTunerHal.addPidFilter(channel.getVideoPid(),
- TunerHal.FILTER_TYPE_VIDEO);
+ mTunerHal.addPidFilter(channel.getVideoPid(), TunerHal.FILTER_TYPE_VIDEO);
}
boolean audioFilterSet = false;
for (Integer audioPid : channel.getAudioPids()) {
@@ -160,10 +162,11 @@ public class TunerTsStreamer implements TsStreamer {
mTunerHal.addPidFilter(audioPid, TunerHal.FILTER_TYPE_OTHER);
}
}
- mTunerHal.addPidFilter(channel.getPcrPid(),
- TunerHal.FILTER_TYPE_PCR);
+ mTunerHal.addPidFilter(channel.getPcrPid(), TunerHal.FILTER_TYPE_PCR);
if (mEventDetector != null) {
- mEventDetector.startDetecting(channel.getFrequency(), channel.getModulation(),
+ mEventDetector.startDetecting(
+ channel.getFrequency(),
+ channel.getModulation(),
channel.getProgramNumber());
}
mChannel = channel;
@@ -242,8 +245,9 @@ public class TunerTsStreamer implements TsStreamer {
}
/**
- * Returns incomplete channel lists which was scanned so far. Incomplete channel means
- * the channel whose channel information is not complete or is not well-formed.
+ * Returns incomplete channel lists which was scanned so far. Incomplete channel means the
+ * channel whose channel information is not complete or is not well-formed.
+ *
* @return {@link List} of {@link TunerChannel}
*/
public List<TunerChannel> getMalFormedChannels() {
@@ -252,6 +256,7 @@ public class TunerTsStreamer implements TsStreamer {
/**
* Returns the current {@link TunerHal} which provides MPEG-TS stream for TunerTsStreamer.
+ *
* @return {@link TunerHal}
*/
public TunerHal getTunerHal() {
@@ -260,6 +265,7 @@ public class TunerTsStreamer implements TsStreamer {
/**
* Returns the current tuned channel for TunerTsStreamer.
+ *
* @return {@link TunerChannel}
*/
public TunerChannel getChannel() {
@@ -268,6 +274,7 @@ public class TunerTsStreamer implements TsStreamer {
/**
* Returns the current buffered position from tuner.
+ *
* @return the current buffered position
*/
public long getBufferedPosition() {
@@ -348,10 +355,14 @@ public class TunerTsStreamer implements TsStreamer {
if (posInBuffer + bytesToCopyInFirstPass > mCircularBuffer.length) {
bytesToCopyInFirstPass = mCircularBuffer.length - posInBuffer;
}
- System.arraycopy(dataBuffer, 0, mCircularBuffer, posInBuffer,
- bytesToCopyInFirstPass);
+ System.arraycopy(
+ dataBuffer, 0, mCircularBuffer, posInBuffer, bytesToCopyInFirstPass);
if (bytesToCopyInFirstPass < bytesWritten) {
- System.arraycopy(dataBuffer, bytesToCopyInFirstPass, mCircularBuffer, 0,
+ System.arraycopy(
+ dataBuffer,
+ bytesToCopyInFirstPass,
+ mCircularBuffer,
+ 0,
bytesWritten - bytesToCopyInFirstPass);
}
mBytesFetched += bytesWritten;
@@ -365,6 +376,7 @@ public class TunerTsStreamer implements TsStreamer {
/**
* Reads data from internal buffer.
+ *
* @param pos the position to read from
* @param buffer to read
* @param offset start position of the read buffer
@@ -397,8 +409,8 @@ public class TunerTsStreamer implements TsStreamer {
int firstLength = (startPos > endPos ? CIRCULAR_BUFFER_SIZE : endPos) - startPos;
System.arraycopy(mCircularBuffer, startPos, buffer, offset, firstLength);
if (firstLength < amount) {
- System.arraycopy(mCircularBuffer, 0, buffer, offset + firstLength,
- amount - firstLength);
+ System.arraycopy(
+ mCircularBuffer, 0, buffer, offset + firstLength, amount - firstLength);
}
mCircularBufferMonitor.notifyAll();
return amount;
diff --git a/src/com/android/tv/tuner/source/TunerTsStreamerManager.java b/tuner/src/com/android/tv/tuner/source/TunerTsStreamerManager.java
index 258a4d86..44fb41e6 100644
--- a/src/com/android/tv/tuner/source/TunerTsStreamerManager.java
+++ b/tuner/src/com/android/tv/tuner/source/TunerTsStreamerManager.java
@@ -17,13 +17,11 @@
package com.android.tv.tuner.source;
import android.content.Context;
-
-import com.android.tv.common.AutoCloseableUtils;
import com.android.tv.common.SoftPreconditions;
+import com.android.tv.common.util.AutoCloseableUtils;
import com.android.tv.tuner.TunerHal;
import com.android.tv.tuner.data.TunerChannel;
import com.android.tv.tuner.tvinput.EventDetector;
-
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -31,9 +29,9 @@ import java.util.Map;
import java.util.Set;
/**
- * Manages {@link TunerTsStreamer} for playback and recording.
- * The class hides handling of {@link TunerHal} from other classes.
- * This class is used by {@link TsDataSourceManager}. Don't use this class directly.
+ * Manages {@link TunerTsStreamer} for playback and recording. The class hides handling of {@link
+ * TunerHal} from other classes. This class is used by {@link TsDataSourceManager}. Don't use this
+ * class directly.
*/
class TunerTsStreamerManager {
// The lock will protect mStreamerFinder, mSourceToStreamerMap and some part of TsStreamCreator
@@ -49,6 +47,7 @@ class TunerTsStreamerManager {
/**
* Returns the singleton instance for the class
+ *
* @return TunerTsStreamerManager
*/
static synchronized TunerTsStreamerManager getInstance() {
@@ -58,16 +57,19 @@ class TunerTsStreamerManager {
return sInstance;
}
- private TunerTsStreamerManager() { }
+ private TunerTsStreamerManager() {}
synchronized TsDataSource createDataSource(
- Context context, TunerChannel channel, EventDetector.EventListener listener,
- int sessionId, boolean reuse) {
+ Context context,
+ TunerChannel channel,
+ EventDetector.EventListener listener,
+ int sessionId,
+ boolean reuse) {
TsStreamerCreator creator;
synchronized (mCancelLock) {
if (mStreamerFinder.containsLocked(channel)) {
mStreamerFinder.appendSessionLocked(channel, sessionId);
- TunerTsStreamer streamer = mStreamerFinder.getStreamerLocked(channel);
+ TunerTsStreamer streamer = mStreamerFinder.getStreamerLocked(channel);
TsDataSource source = streamer.createDataSource();
mListeners.put(sessionId, listener);
streamer.registerListener(listener);
@@ -99,8 +101,7 @@ class TunerTsStreamerManager {
return null;
}
- synchronized void releaseDataSource(TsDataSource source, int sessionId,
- boolean reuse) {
+ synchronized void releaseDataSource(TsDataSource source, int sessionId, boolean reuse) {
TunerTsStreamer streamer;
synchronized (mCancelLock) {
streamer = mSourceToStreamerMap.get(source);
@@ -125,15 +126,13 @@ class TunerTsStreamerManager {
void setHasPendingTune(int sessionId) {
synchronized (mCancelLock) {
- if (mCreators.containsKey(sessionId)) {
- mCreators.get(sessionId).cancelLocked();
- }
+ if (mCreators.containsKey(sessionId)) {
+ mCreators.get(sessionId).cancelLocked();
+ }
}
}
- /**
- * Add tuner hal into TunerHalManager for test.
- */
+ /** Add tuner hal into TunerHalManager for test. */
void addTunerHal(TunerHal tunerHal, int sessionId) {
mTunerHalManager.addTunerHal(tunerHal, sessionId);
}
@@ -142,7 +141,7 @@ class TunerTsStreamerManager {
mTunerHalManager.releaseCachedHal(sessionId);
}
- private class StreamerFinder {
+ private static class StreamerFinder {
private final Map<TunerChannel, Set<Integer>> mSessions = new HashMap<>();
private final Map<TunerChannel, TunerTsStreamer> mStreamers = new HashMap<>();
@@ -183,8 +182,8 @@ class TunerTsStreamerManager {
}
/**
- * {@link TunerTsStreamer} creation can be cancelled by a new tune request for the same
- * session. The class supports the cancellation in creating new {@link TunerTsStreamer}.
+ * {@link TunerTsStreamer} creation can be cancelled by a new tune request for the same session.
+ * The class supports the cancellation in creating new {@link TunerTsStreamer}.
*/
private class TsStreamerCreator {
private final Context mContext;
@@ -195,8 +194,8 @@ class TunerTsStreamerManager {
private boolean mCancelled;
private TunerHal mTunerHal;
- private TsStreamerCreator(Context context, TunerChannel channel,
- EventDetector.EventListener listener) {
+ private TsStreamerCreator(
+ Context context, TunerChannel channel, EventDetector.EventListener listener) {
mContext = context;
mChannel = channel;
mEventListener = listener;
@@ -233,26 +232,26 @@ class TunerTsStreamerManager {
// @GuardedBy("mCancelLock")
private void cancelLocked() {
- if (mCancelled) {
- return;
- }
- mCancelled = true;
- if (mTunerHal != null) {
- mTunerHal.setHasPendingTune(true);
- }
+ if (mCancelled) {
+ return;
+ }
+ mCancelled = true;
+ if (mTunerHal != null) {
+ mTunerHal.setHasPendingTune(true);
+ }
}
// @GuardedBy("mCancelLock")
private boolean isCancelledLocked() {
- return mCancelled;
+ return mCancelled;
}
}
/**
- * Supports sharing {@link TunerHal} among multiple sessions.
- * The class also supports session affinity for {@link TunerHal} allocation.
+ * Supports sharing {@link TunerHal} among multiple sessions. The class also supports session
+ * affinity for {@link TunerHal} allocation.
*/
- private class TunerHalManager {
+ private static class TunerHalManager {
private final Map<Integer, TunerHal> mTunerHals = new HashMap<>();
private TunerHal getOrCreateTunerHal(Context context, int sessionId) {
@@ -301,4 +300,4 @@ class TunerTsStreamerManager {
mTunerHals.put(sessionId, tunerHal);
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/tv/tuner/ts/SectionParser.java b/tuner/src/com/android/tv/tuner/ts/SectionParser.java
index e1f890f3..27726c02 100644
--- a/src/com/android/tv/tuner/ts/SectionParser.java
+++ b/tuner/src/com/android/tv/tuner/ts/SectionParser.java
@@ -18,6 +18,7 @@ package com.android.tv.tuner.ts;
import android.media.tv.TvContentRating;
import android.media.tv.TvContract.Programs.Genres;
+import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -48,23 +49,19 @@ import com.android.tv.tuner.data.nano.Channel;
import com.android.tv.tuner.data.nano.Track.AtscAudioTrack;
import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
import com.android.tv.tuner.util.ByteArrayBuffer;
-
import com.android.tv.tuner.util.ConvertUtils;
-import com.ibm.icu.text.UnicodeDecompressor;
-
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
-import java.util.Calendar;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Calendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-/**
- * Parses ATSC PSIP sections.
- */
+/** Parses ATSC PSIP sections. */
public class SectionParser {
private static final String TAG = "SectionParser";
private static final boolean DEBUG = false;
@@ -99,7 +96,7 @@ public class SectionParser {
public static final int DVB_DESCRIPTOR_TAG_PARENTAL_RATING = 0x55;
private static final byte COMPRESSION_TYPE_NO_COMPRESSION = (byte) 0x00;
- private static final byte MODE_SELECTED_UNICODE_RANGE_1 = (byte) 0x00; // 0x0000 - 0x00ff
+ private static final byte MODE_SELECTED_UNICODE_RANGE_1 = (byte) 0x00; // 0x0000 - 0x00ff
private static final byte MODE_UTF16 = (byte) 0x3f;
private static final byte MODE_SCSU = (byte) 0x3e;
private static final int MAX_SHORT_NAME_BYTES = 14;
@@ -160,7 +157,6 @@ public class SectionParser {
private static final String STRING_US_TV_Y7 = "US_TV_Y7";
private static final String STRING_US_TV_FV = "US_TV_FV";
-
/*
* The following CRC table is from the code generated by the following command.
* $ python pycrc.py --model crc-32-mpeg --algorithm table-driven --generate c
@@ -236,126 +232,365 @@ public class SectionParser {
// A table which maps ATSC genres to TIF genres.
// See ATSC/65 Table 6.20.
private static final String[] CANONICAL_GENRES_TABLE = {
- null, null, null, null,
- null, null, null, null,
- null, null, null, null,
- null, null, null, null,
- null, null, null, null,
- null, null, null, null,
- null, null, null, null,
- null, null, null, null,
- Genres.EDUCATION, Genres.ENTERTAINMENT, Genres.MOVIES, Genres.NEWS,
- Genres.LIFE_STYLE, Genres.SPORTS, null, Genres.MOVIES,
- null,
- Genres.FAMILY_KIDS, Genres.DRAMA, null, Genres.ENTERTAINMENT, Genres.SPORTS,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ Genres.EDUCATION,
+ Genres.ENTERTAINMENT,
+ Genres.MOVIES,
+ Genres.NEWS,
+ Genres.LIFE_STYLE,
+ Genres.SPORTS,
+ null,
+ Genres.MOVIES,
+ null,
+ Genres.FAMILY_KIDS,
+ Genres.DRAMA,
+ null,
+ Genres.ENTERTAINMENT,
Genres.SPORTS,
- null, null,
- Genres.MUSIC, Genres.EDUCATION,
+ Genres.SPORTS,
+ null,
+ null,
+ Genres.MUSIC,
+ Genres.EDUCATION,
null,
Genres.COMEDY,
null,
Genres.MUSIC,
- null, null,
- Genres.MOVIES, Genres.ENTERTAINMENT, Genres.NEWS, Genres.DRAMA,
- Genres.EDUCATION, Genres.MOVIES, Genres.SPORTS, Genres.MOVIES,
null,
- Genres.LIFE_STYLE, Genres.ARTS, Genres.LIFE_STYLE, Genres.SPORTS,
- null, null,
- Genres.GAMING, Genres.LIFE_STYLE, Genres.SPORTS,
null,
- Genres.LIFE_STYLE, Genres.EDUCATION, Genres.EDUCATION, Genres.LIFE_STYLE,
- Genres.SPORTS, Genres.LIFE_STYLE, Genres.MOVIES, Genres.NEWS,
- null, null, null,
+ Genres.MOVIES,
+ Genres.ENTERTAINMENT,
+ Genres.NEWS,
+ Genres.DRAMA,
+ Genres.EDUCATION,
+ Genres.MOVIES,
+ Genres.SPORTS,
+ Genres.MOVIES,
+ null,
+ Genres.LIFE_STYLE,
+ Genres.ARTS,
+ Genres.LIFE_STYLE,
+ Genres.SPORTS,
+ null,
+ null,
+ Genres.GAMING,
+ Genres.LIFE_STYLE,
+ Genres.SPORTS,
+ null,
+ Genres.LIFE_STYLE,
Genres.EDUCATION,
- null, null, null,
Genres.EDUCATION,
- null, null, null,
- Genres.DRAMA, Genres.MUSIC, Genres.MOVIES,
+ Genres.LIFE_STYLE,
+ Genres.SPORTS,
+ Genres.LIFE_STYLE,
+ Genres.MOVIES,
+ Genres.NEWS,
+ null,
+ null,
+ null,
+ Genres.EDUCATION,
+ null,
+ null,
+ null,
+ Genres.EDUCATION,
+ null,
+ null,
+ null,
+ Genres.DRAMA,
+ Genres.MUSIC,
+ Genres.MOVIES,
null,
Genres.ANIMAL_WILDLIFE,
- null, null,
+ null,
+ null,
Genres.PREMIER,
- null, null, null, null,
- Genres.SPORTS, Genres.ARTS,
- null, null, null,
- Genres.MOVIES, Genres.TECH_SCIENCE, Genres.DRAMA,
null,
- Genres.SHOPPING, Genres.DRAMA,
null,
- Genres.MOVIES, Genres.ENTERTAINMENT, Genres.TECH_SCIENCE, Genres.SPORTS,
- Genres.TRAVEL, Genres.ENTERTAINMENT, Genres.ARTS, Genres.NEWS,
null,
- Genres.ARTS, Genres.SPORTS, Genres.SPORTS, Genres.NEWS,
- Genres.SPORTS, Genres.SPORTS, Genres.SPORTS, Genres.FAMILY_KIDS,
- Genres.FAMILY_KIDS, Genres.MOVIES,
null,
- Genres.TECH_SCIENCE, Genres.MUSIC,
+ Genres.SPORTS,
+ Genres.ARTS,
+ null,
+ null,
+ null,
+ Genres.MOVIES,
+ Genres.TECH_SCIENCE,
+ Genres.DRAMA,
+ null,
+ Genres.SHOPPING,
+ Genres.DRAMA,
+ null,
+ Genres.MOVIES,
+ Genres.ENTERTAINMENT,
+ Genres.TECH_SCIENCE,
+ Genres.SPORTS,
+ Genres.TRAVEL,
+ Genres.ENTERTAINMENT,
+ Genres.ARTS,
+ Genres.NEWS,
+ null,
+ Genres.ARTS,
+ Genres.SPORTS,
+ Genres.SPORTS,
+ Genres.NEWS,
+ Genres.SPORTS,
+ Genres.SPORTS,
+ Genres.SPORTS,
+ Genres.FAMILY_KIDS,
+ Genres.FAMILY_KIDS,
+ Genres.MOVIES,
+ null,
+ Genres.TECH_SCIENCE,
+ Genres.MUSIC,
null,
- Genres.SPORTS, Genres.FAMILY_KIDS, Genres.NEWS, Genres.SPORTS,
- Genres.NEWS, Genres.SPORTS, Genres.ANIMAL_WILDLIFE,
+ Genres.SPORTS,
+ Genres.FAMILY_KIDS,
+ Genres.NEWS,
+ Genres.SPORTS,
+ Genres.NEWS,
+ Genres.SPORTS,
+ Genres.ANIMAL_WILDLIFE,
null,
- Genres.MUSIC, Genres.NEWS, Genres.SPORTS,
+ Genres.MUSIC,
+ Genres.NEWS,
+ Genres.SPORTS,
null,
- Genres.NEWS, Genres.NEWS, Genres.NEWS, Genres.NEWS,
- Genres.SPORTS, Genres.MOVIES, Genres.ARTS, Genres.ANIMAL_WILDLIFE,
- Genres.MUSIC, Genres.MUSIC, Genres.MOVIES, Genres.EDUCATION,
- Genres.DRAMA, Genres.SPORTS, Genres.SPORTS, Genres.SPORTS,
+ Genres.NEWS,
+ Genres.NEWS,
+ Genres.NEWS,
+ Genres.NEWS,
+ Genres.SPORTS,
+ Genres.MOVIES,
+ Genres.ARTS,
+ Genres.ANIMAL_WILDLIFE,
+ Genres.MUSIC,
+ Genres.MUSIC,
+ Genres.MOVIES,
+ Genres.EDUCATION,
+ Genres.DRAMA,
+ Genres.SPORTS,
+ Genres.SPORTS,
+ Genres.SPORTS,
Genres.SPORTS,
null,
- Genres.SPORTS, Genres.SPORTS,
+ Genres.SPORTS,
+ Genres.SPORTS,
};
// A table which contains ATSC categorical genre code assignments.
// See ATSC/65 Table 6.20.
- private static final String[] BROADCAST_GENRES_TABLE = new String[] {
- null, null, null, null,
- null, null, null, null,
- null, null, null, null,
- null, null, null, null,
- null, null, null, null,
- null, null, null, null,
- null, null, null, null,
- null, null, null, null,
- "Education", "Entertainment", "Movie", "News",
- "Religious", "Sports", "Other", "Action",
- "Advertisement", "Animated", "Anthology", "Automobile",
- "Awards", "Baseball", "Basketball", "Bulletin",
- "Business", "Classical", "College", "Combat",
- "Comedy", "Commentary", "Concert", "Consumer",
- "Contemporary", "Crime", "Dance", "Documentary",
- "Drama", "Elementary", "Erotica", "Exercise",
- "Fantasy", "Farm", "Fashion", "Fiction",
- "Food", "Football", "Foreign", "Fund Raiser",
- "Game/Quiz", "Garden", "Golf", "Government",
- "Health", "High School", "History", "Hobby",
- "Hockey", "Home", "Horror", "Information",
- "Instruction", "International", "Interview", "Language",
- "Legal", "Live", "Local", "Math",
- "Medical", "Meeting", "Military", "Miniseries",
- "Music", "Mystery", "National", "Nature",
- "Police", "Politics", "Premier", "Prerecorded",
- "Product", "Professional", "Public", "Racing",
- "Reading", "Repair", "Repeat", "Review",
- "Romance", "Science", "Series", "Service",
- "Shopping", "Soap Opera", "Special", "Suspense",
- "Talk", "Technical", "Tennis", "Travel",
- "Variety", "Video", "Weather", "Western",
- "Art", "Auto Racing", "Aviation", "Biography",
- "Boating", "Bowling", "Boxing", "Cartoon",
- "Children", "Classic Film", "Community", "Computers",
- "Country Music", "Court", "Extreme Sports", "Family",
- "Financial", "Gymnastics", "Headlines", "Horse Racing",
- "Hunting/Fishing/Outdoors", "Independent", "Jazz", "Magazine",
- "Motorcycle Racing", "Music/Film/Books", "News-International", "News-Local",
- "News-National", "News-Regional", "Olympics", "Original",
- "Performing Arts", "Pets/Animals", "Pop", "Rock & Roll",
- "Sci-Fi", "Self Improvement", "Sitcom", "Skating",
- "Skiing", "Soccer", "Track/Field", "True",
- "Volleyball", "Wrestling",
- };
+ private static final String[] BROADCAST_GENRES_TABLE =
+ new String[] {
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ "Education",
+ "Entertainment",
+ "Movie",
+ "News",
+ "Religious",
+ "Sports",
+ "Other",
+ "Action",
+ "Advertisement",
+ "Animated",
+ "Anthology",
+ "Automobile",
+ "Awards",
+ "Baseball",
+ "Basketball",
+ "Bulletin",
+ "Business",
+ "Classical",
+ "College",
+ "Combat",
+ "Comedy",
+ "Commentary",
+ "Concert",
+ "Consumer",
+ "Contemporary",
+ "Crime",
+ "Dance",
+ "Documentary",
+ "Drama",
+ "Elementary",
+ "Erotica",
+ "Exercise",
+ "Fantasy",
+ "Farm",
+ "Fashion",
+ "Fiction",
+ "Food",
+ "Football",
+ "Foreign",
+ "Fund Raiser",
+ "Game/Quiz",
+ "Garden",
+ "Golf",
+ "Government",
+ "Health",
+ "High School",
+ "History",
+ "Hobby",
+ "Hockey",
+ "Home",
+ "Horror",
+ "Information",
+ "Instruction",
+ "International",
+ "Interview",
+ "Language",
+ "Legal",
+ "Live",
+ "Local",
+ "Math",
+ "Medical",
+ "Meeting",
+ "Military",
+ "Miniseries",
+ "Music",
+ "Mystery",
+ "National",
+ "Nature",
+ "Police",
+ "Politics",
+ "Premier",
+ "Prerecorded",
+ "Product",
+ "Professional",
+ "Public",
+ "Racing",
+ "Reading",
+ "Repair",
+ "Repeat",
+ "Review",
+ "Romance",
+ "Science",
+ "Series",
+ "Service",
+ "Shopping",
+ "Soap Opera",
+ "Special",
+ "Suspense",
+ "Talk",
+ "Technical",
+ "Tennis",
+ "Travel",
+ "Variety",
+ "Video",
+ "Weather",
+ "Western",
+ "Art",
+ "Auto Racing",
+ "Aviation",
+ "Biography",
+ "Boating",
+ "Bowling",
+ "Boxing",
+ "Cartoon",
+ "Children",
+ "Classic Film",
+ "Community",
+ "Computers",
+ "Country Music",
+ "Court",
+ "Extreme Sports",
+ "Family",
+ "Financial",
+ "Gymnastics",
+ "Headlines",
+ "Horse Racing",
+ "Hunting/Fishing/Outdoors",
+ "Independent",
+ "Jazz",
+ "Magazine",
+ "Motorcycle Racing",
+ "Music/Film/Books",
+ "News-International",
+ "News-Local",
+ "News-National",
+ "News-Regional",
+ "Olympics",
+ "Original",
+ "Performing Arts",
+ "Pets/Animals",
+ "Pop",
+ "Rock & Roll",
+ "Sci-Fi",
+ "Self Improvement",
+ "Sitcom",
+ "Skating",
+ "Skiing",
+ "Soccer",
+ "Track/Field",
+ "True",
+ "Volleyball",
+ "Wrestling",
+ };
// Audio language code map from ISO 639-2/B to 639-2/T, in order to show correct audio language.
private static final HashMap<String, String> ISO_LANGUAGE_CODE_MAP;
+
static {
ISO_LANGUAGE_CODE_MAP = new HashMap<>();
ISO_LANGUAGE_CODE_MAP.put("alb", "sqi");
@@ -381,17 +616,27 @@ public class SectionParser {
ISO_LANGUAGE_CODE_MAP.put("esl", "spa"); // Special entry for channel 9-1 KQED in bay area.
}
+ @Nullable
+ private static final Charset SCSU_CHARSET =
+ Charset.isSupported("SCSU") ? Charset.forName("SCSU") : null;
+
// Containers to store the last version numbers of the PSIP sections.
private final HashMap<PsipSection, Integer> mSectionVersionMap = new HashMap<>();
private final SparseArray<List<EttItem>> mParsedEttItems = new SparseArray<>();
public interface OutputListener {
void onPatParsed(List<PatItem> items);
+
void onPmtParsed(int programNumber, List<PmtItem> items);
+
void onMgtParsed(List<MgtItem> items);
+
void onVctParsed(List<VctItem> items, int sectionNumber, int lastSectionNumber);
+
void onEitParsed(int sourceId, List<EitItem> items);
+
void onEttParsed(int sourceId, List<EttItem> descriptions);
+
void onSdtParsed(List<SdtItem> items);
}
@@ -532,7 +777,7 @@ public class SectionParser {
Log.d(TAG, "PMT descriptors size: " + descriptors.size());
}
List<PmtItem> results = new ArrayList<>();
- for (; pos < data.length - 4;) {
+ for (; pos < data.length - 4; ) {
if (pos < 0) {
Log.e(TAG, "Broken PMT.");
return false;
@@ -607,8 +852,12 @@ public class SectionParser {
if (sectionNumber > lastSectionNumber) {
// According to section 6.3.1 of the spec ATSC A/65,
// last section number is the largest section number.
- Log.w(TAG, "Invalid VCT. Section Number " + sectionNumber + " > Last Section Number "
- + lastSectionNumber);
+ Log.w(
+ TAG,
+ "Invalid VCT. Section Number "
+ + sectionNumber
+ + " > Last Section Number "
+ + lastSectionNumber);
return false;
}
int pos = 10;
@@ -621,8 +870,8 @@ public class SectionParser {
String shortName = "";
int shortNameSize = getShortNameSize(data, pos);
try {
- shortName = new String(
- Arrays.copyOfRange(data, pos, pos + shortNameSize), "UTF-16");
+ shortName =
+ new String(Arrays.copyOfRange(data, pos, pos + shortNameSize), "UTF-16");
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "Broken VCT.", e);
return false;
@@ -652,8 +901,8 @@ public class SectionParser {
Log.e(TAG, "Broken VCT.");
return false;
}
- List<TsDescriptor> descriptors = parseDescriptors(
- data, descriptorsPos, descriptorsPos + descriptorsLength);
+ List<TsDescriptor> descriptors =
+ parseDescriptors(data, descriptorsPos, descriptorsPos + descriptorsLength);
String longName = null;
for (TsDescriptor descriptor : descriptors) {
if (descriptor instanceof ExtendedChannelNameDescriptor) {
@@ -664,18 +913,41 @@ public class SectionParser {
}
}
if (DEBUG) {
- Log.d(TAG, String.format(
- "Found channel [%s] %s - serviceType: %d tsid: 0x%x program: %d "
- + "channel: %d-%d encrypted: %b hidden: %b, descriptors: %d",
- shortName, longName, serviceType, channelTsid, programNumber, majorNumber,
- minorNumber, accessControlled, hidden, descriptors.size()));
- }
- if (!accessControlled && !hidden && (serviceType == Channel.SERVICE_TYPE_ATSC_AUDIO ||
- serviceType == Channel.SERVICE_TYPE_ATSC_DIGITAL_TELEVISION ||
- serviceType == Channel.SERVICE_TYPE_UNASSOCIATED_SMALL_SCREEN_SERVICE)) {
+ Log.d(
+ TAG,
+ String.format(
+ "Found channel [%s] %s - serviceType: %d tsid: 0x%x program: %d "
+ + "channel: %d-%d encrypted: %b hidden: %b, descriptors: %d",
+ shortName,
+ longName,
+ serviceType,
+ channelTsid,
+ programNumber,
+ majorNumber,
+ minorNumber,
+ accessControlled,
+ hidden,
+ descriptors.size()));
+ }
+ if (!accessControlled
+ && !hidden
+ && (serviceType == Channel.AtscServiceType.SERVICE_TYPE_ATSC_AUDIO
+ || serviceType
+ == Channel.AtscServiceType.SERVICE_TYPE_ATSC_DIGITAL_TELEVISION
+ || serviceType
+ == Channel.AtscServiceType
+ .SERVICE_TYPE_UNASSOCIATED_SMALL_SCREEN_SERVICE)) {
// Hide hidden, encrypted, or unsupported ATSC service type channels
- results.add(new VctItem(shortName, longName, serviceType, channelTsid,
- programNumber, majorNumber, minorNumber, sourceId));
+ results.add(
+ new VctItem(
+ shortName,
+ longName,
+ serviceType,
+ channelTsid,
+ programNumber,
+ majorNumber,
+ minorNumber,
+ sourceId));
}
}
// Skip the remaining descriptor part which we don't use.
@@ -710,10 +982,15 @@ public class SectionParser {
return false;
}
int eventId = ((data[pos] & 0x3f) << 8) + (data[pos + 1] & 0xff);
- long startTime = ((data[pos + 2] & (long) 0xff) << 24) | ((data[pos + 3] & 0xff) << 16)
- | ((data[pos + 4] & 0xff) << 8) | (data[pos + 5] & 0xff);
- int lengthInSecond = ((data[pos + 6] & 0x0f) << 16)
- | ((data[pos + 7] & 0xff) << 8) | (data[pos + 8] & 0xff);
+ long startTime =
+ ((data[pos + 2] & (long) 0xff) << 24)
+ | ((data[pos + 3] & 0xff) << 16)
+ | ((data[pos + 4] & 0xff) << 8)
+ | (data[pos + 5] & 0xff);
+ int lengthInSecond =
+ ((data[pos + 6] & 0x0f) << 16)
+ | ((data[pos + 7] & 0xff) << 8)
+ | (data[pos + 8] & 0xff);
int titleLength = (data[pos + 9] & 0xff);
if (data.length <= pos + 10 + titleLength + 1) {
Log.e(TAG, "Broken EIT.");
@@ -727,15 +1004,16 @@ public class SectionParser {
Log.e(TAG, "Broken EIT.");
return false;
}
- int descriptorsLength = ((data[pos + 10 + titleLength] & 0x0f) << 8)
- | (data[pos + 10 + titleLength + 1] & 0xff);
+ int descriptorsLength =
+ ((data[pos + 10 + titleLength] & 0x0f) << 8)
+ | (data[pos + 10 + titleLength + 1] & 0xff);
int descriptorsPos = pos + 10 + titleLength + 2;
if (data.length < descriptorsPos + descriptorsLength) {
Log.e(TAG, "Broken EIT.");
return false;
}
- List<TsDescriptor> descriptors = parseDescriptors(
- data, descriptorsPos, descriptorsPos + descriptorsLength);
+ List<TsDescriptor> descriptors =
+ parseDescriptors(data, descriptorsPos, descriptorsPos + descriptorsLength);
if (DEBUG) {
Log.d(TAG, String.format("EIT descriptors size: %d", descriptors.size()));
}
@@ -745,9 +1023,19 @@ public class SectionParser {
List<AtscAudioTrack> audioTracks = generateAudioTracks(descriptors);
List<AtscCaptionTrack> captionTracks = generateCaptionTracks(descriptors);
pos += 10 + titleLength + 2 + descriptorsLength;
- results.add(new EitItem(EitItem.INVALID_PROGRAM_ID, eventId, titleText,
- startTime, lengthInSecond, contentRating, audioTracks, captionTracks,
- broadcastGenre, canonicalGenre, null));
+ results.add(
+ new EitItem(
+ EitItem.INVALID_PROGRAM_ID,
+ eventId,
+ titleText,
+ startTime,
+ lengthInSecond,
+ contentRating,
+ audioTracks,
+ captionTracks,
+ broadcastGenre,
+ canonicalGenre,
+ null));
}
if (mListener != null) {
mListener.onEitParsed(sourceId, results);
@@ -812,8 +1100,13 @@ public class SectionParser {
serviceType = serviceDescriptor.getServiceType();
}
if (serviceDescriptors.size() > 0) {
- sdtItems.add(new SdtItem(serviceName, serviceProviderName, serviceType, serviceId,
- originalNetworkId));
+ sdtItems.add(
+ new SdtItem(
+ serviceName,
+ serviceProviderName,
+ serviceType,
+ serviceId,
+ originalNetworkId));
}
pos += descriptorsLength;
}
@@ -841,12 +1134,17 @@ public class SectionParser {
List<EitItem> results = new ArrayList<>();
while (pos + 12 < data.length) {
int eventId = ((data[pos] & 0xff) << 8) + (data[pos + 1] & 0xff);
- float modifiedJulianDate = ((data[pos + 2] & 0xff) << 8) | (data[pos + 3] & 0xff);
+ float modifiedJulianDate = ((data[pos + 2] & 0xff) << 8) | (data[pos + 3] & 0xff);
int startYear = (int) ((modifiedJulianDate - 15078.2f) / 365.25f);
- int mjdMonth = (int) ((modifiedJulianDate - 14956.1f
- - (int) (startYear * 365.25f)) / 30.6001f);
- int startDay = (int) modifiedJulianDate - 14956 - (int) (startYear * 365.25f)
- - (int) (mjdMonth * 30.6001f);
+ int mjdMonth =
+ (int)
+ ((modifiedJulianDate - 14956.1f - (int) (startYear * 365.25f))
+ / 30.6001f);
+ int startDay =
+ (int) modifiedJulianDate
+ - 14956
+ - (int) (startYear * 365.25f)
+ - (int) (mjdMonth * 30.6001f);
int startMonth = mjdMonth - 1;
if (mjdMonth == 14 || mjdMonth == 15) {
startYear += 1;
@@ -858,21 +1156,20 @@ public class SectionParser {
Calendar calendar = Calendar.getInstance();
startYear += 1900;
calendar.set(startYear, startMonth, startDay, startHour, startMinute, startSecond);
- long startTime = ConvertUtils.convertUnixEpochToGPSTime(
- calendar.getTimeInMillis() / 1000);
- int durationInSecond = (((data[pos + 7] & 0xf0) >> 4) * 10
- + (data[pos + 7] & 0x0f)) * 3600
- + (((data[pos + 8] & 0xf0) >> 4) * 10 + (data[pos + 8] & 0x0f)) * 60
- + (((data[pos + 9] & 0xf0) >> 4) * 10 + (data[pos + 9] & 0x0f));
- int descriptorsLength = ((data[pos + 10] & 0x0f) << 8)
- | (data[pos + 10 + 1] & 0xff);
+ long startTime =
+ ConvertUtils.convertUnixEpochToGPSTime(calendar.getTimeInMillis() / 1000);
+ int durationInSecond =
+ (((data[pos + 7] & 0xf0) >> 4) * 10 + (data[pos + 7] & 0x0f)) * 3600
+ + (((data[pos + 8] & 0xf0) >> 4) * 10 + (data[pos + 8] & 0x0f)) * 60
+ + (((data[pos + 9] & 0xf0) >> 4) * 10 + (data[pos + 9] & 0x0f));
+ int descriptorsLength = ((data[pos + 10] & 0x0f) << 8) | (data[pos + 10 + 1] & 0xff);
int descriptorsPos = pos + 10 + 2;
if (data.length < descriptorsPos + descriptorsLength) {
Log.e(TAG, "Broken EIT.");
return false;
}
- List<TsDescriptor> descriptors = parseDescriptors(
- data, descriptorsPos, descriptorsPos + descriptorsLength);
+ List<TsDescriptor> descriptors =
+ parseDescriptors(data, descriptorsPos, descriptorsPos + descriptorsLength);
if (DEBUG) {
Log.d(TAG, String.format("DVB EIT descriptors size: %d", descriptors.size()));
}
@@ -887,9 +1184,19 @@ public class SectionParser {
List<AtscAudioTrack> audioTracks = generateAudioTracks(descriptors);
List<AtscCaptionTrack> captionTracks = generateCaptionTracks(descriptors);
pos += 12 + descriptorsLength;
- results.add(new EitItem(EitItem.INVALID_PROGRAM_ID, eventId, titleText,
- startTime, durationInSecond, contentRating, audioTracks, captionTracks,
- broadcastGenre, canonicalGenre, null));
+ results.add(
+ new EitItem(
+ EitItem.INVALID_PROGRAM_ID,
+ eventId,
+ titleText,
+ startTime,
+ durationInSecond,
+ contentRating,
+ audioTracks,
+ captionTracks,
+ broadcastGenre,
+ canonicalGenre,
+ null));
}
if (mListener != null) {
mListener.onEitParsed(sourceId, results);
@@ -904,8 +1211,7 @@ public class SectionParser {
List<AtscAudioTrack> iso639LanguageTracks = new ArrayList<>();
for (TsDescriptor descriptor : descriptors) {
if (descriptor instanceof Ac3AudioDescriptor) {
- Ac3AudioDescriptor audioDescriptor =
- (Ac3AudioDescriptor) descriptor;
+ Ac3AudioDescriptor audioDescriptor = (Ac3AudioDescriptor) descriptor;
AtscAudioTrack audioTrack = new AtscAudioTrack();
if (audioDescriptor.getLanguage() != null) {
audioTrack.language = audioDescriptor.getLanguage();
@@ -913,7 +1219,7 @@ public class SectionParser {
if (audioTrack.language == null) {
audioTrack.language = "";
}
- audioTrack.audioType = AtscAudioTrack.AUDIOTYPE_UNDEFINED;
+ audioTrack.audioType = AtscAudioTrack.AudioType.AUDIOTYPE_UNDEFINED;
audioTrack.channelCount = audioDescriptor.getNumChannels();
audioTrack.sampleRate = audioDescriptor.getSampleRate();
ac3Tracks.add(audioTrack);
@@ -937,7 +1243,8 @@ public class SectionParser {
// Combines two descriptors into one in order to gather more audio track specific
// information as much as possible.
List<AtscAudioTrack> tracks = new ArrayList<>();
- if (!ac3Tracks.isEmpty() && !iso639LanguageTracks.isEmpty()
+ if (!ac3Tracks.isEmpty()
+ && !iso639LanguageTracks.isEmpty()
&& ac3Tracks.size() != iso639LanguageTracks.size()) {
// This shouldn't be happen. In here, it handles two cases. The first case is that the
// only one type of descriptors arrives. The second case is that the two types of
@@ -1161,8 +1468,7 @@ public class SectionParser {
private static String generateBroadcastGenre(List<TsDescriptor> descriptors) {
for (TsDescriptor descriptor : descriptors) {
if (descriptor instanceof GenreDescriptor) {
- GenreDescriptor genreDescriptor =
- (GenreDescriptor) descriptor;
+ GenreDescriptor genreDescriptor = (GenreDescriptor) descriptor;
return TextUtils.join(",", genreDescriptor.getBroadcastGenres());
}
}
@@ -1172,8 +1478,7 @@ public class SectionParser {
private static String generateCanonicalGenre(List<TsDescriptor> descriptors) {
for (TsDescriptor descriptor : descriptors) {
if (descriptor instanceof GenreDescriptor) {
- GenreDescriptor genreDescriptor =
- (GenreDescriptor) descriptor;
+ GenreDescriptor genreDescriptor = (GenreDescriptor) descriptor;
return Genres.encode(genreDescriptor.getCanonicalGenres());
}
}
@@ -1317,7 +1622,7 @@ public class SectionParser {
pos += 3;
boolean ccType = (data[pos] & 0x80) != 0;
if (!ccType) {
- pos +=3;
+ pos += 3;
continue;
}
int captionServiceNumber = data[pos] & 0x3f;
@@ -1392,8 +1697,8 @@ public class SectionParser {
return new ContentAdvisoryDescriptor(ratingRegions);
}
- private static ExtendedChannelNameDescriptor parseLongChannelName(byte[] data, int pos,
- int limit) {
+ private static ExtendedChannelNameDescriptor parseLongChannelName(
+ byte[] data, int pos, int limit) {
if (limit <= pos + 2) {
Log.e(TAG, "Broken ExtendedChannelName.");
return null;
@@ -1432,7 +1737,8 @@ public class SectionParser {
}
}
}
- return new GenreDescriptor(broadcastGenreSet.toArray(new String[broadcastGenreSet.size()]),
+ return new GenreDescriptor(
+ broadcastGenreSet.toArray(new String[broadcastGenreSet.size()]),
canonicalGenreSet.toArray(new String[canonicalGenreSet.size()]));
}
@@ -1517,9 +1823,22 @@ public class SectionParser {
if (limit <= pos + 1) {
Log.w(TAG, "Missing text and language fields on AC3 audio stream descriptor.");
- return new Ac3AudioDescriptor(sampleRateCode, bsid, bitRateCode, surroundMode, bsmod,
- numEncodedChannels, fullSvc, langCod, langCod2, mainId, priority, asvcflags,
- null, null, null);
+ return new Ac3AudioDescriptor(
+ sampleRateCode,
+ bsid,
+ bitRateCode,
+ surroundMode,
+ bsmod,
+ numEncodedChannels,
+ fullSvc,
+ langCod,
+ langCod2,
+ mainId,
+ priority,
+ asvcflags,
+ null,
+ null,
+ null);
}
++pos;
int textLen = (data[pos] & 0xfe) >> 1;
@@ -1562,9 +1881,22 @@ public class SectionParser {
}
}
- return new Ac3AudioDescriptor(sampleRateCode, bsid, bitRateCode, surroundMode, bsmod,
- numEncodedChannels, fullSvc, langCod, langCod2, mainId, priority, asvcflags, text,
- language, language2);
+ return new Ac3AudioDescriptor(
+ sampleRateCode,
+ bsid,
+ bitRateCode,
+ surroundMode,
+ bsmod,
+ numEncodedChannels,
+ fullSvc,
+ langCod,
+ langCod2,
+ mainId,
+ priority,
+ asvcflags,
+ text,
+ language,
+ language2);
}
private static TsDescriptor parseDvbService(byte[] data, int pos, int limit) {
@@ -1645,7 +1977,7 @@ public class SectionParser {
}
private static String extractText(byte[] data, int pos) {
- if (data.length < pos) {
+ if (data.length < pos) {
return null;
}
int numStrings = data[pos] & 0xff;
@@ -1669,19 +2001,22 @@ public class SectionParser {
Log.e(TAG, "Broken text.");
return null;
}
- byte[] bytes = Arrays.copyOfRange(data, pos + 3, pos + 3 + numBytes);
if (compressionType == COMPRESSION_TYPE_NO_COMPRESSION) {
- try {
- switch (mode) {
- case MODE_SELECTED_UNICODE_RANGE_1:
- return new String(bytes, "ISO-8859-1");
- case MODE_SCSU:
- return UnicodeDecompressor.decompress(bytes);
- case MODE_UTF16:
- return new String(bytes, "UTF-16");
- }
- } catch (UnsupportedEncodingException e) {
- Log.e(TAG, "Unsupported text format.", e);
+ switch (mode) {
+ case MODE_SELECTED_UNICODE_RANGE_1:
+ return new String(data, pos + 3, numBytes, StandardCharsets.ISO_8859_1);
+ case MODE_SCSU:
+ if (SCSU_CHARSET != null) {
+ return new String(data, pos + 3, numBytes, SCSU_CHARSET);
+ } else {
+ Log.w(TAG, "SCSU not supported");
+ return null;
+ }
+ case MODE_UTF16:
+ return new String(data, pos + 3, numBytes, StandardCharsets.UTF_16);
+ default:
+ Log.w(TAG, "Unsupported text mode " + mode);
+ return null;
}
}
pos += 3 + numBytes;
@@ -1746,11 +2081,11 @@ public class SectionParser {
boolean hasCRC = (data[1] & 0x80) != 0; // section_syntax_indicator
if (hasCRC) {
int crc = 0xffffffff;
- for(byte b : data) {
+ for (byte b : data) {
int index = ((crc >> 24) ^ (b & 0xff)) & 0xff;
crc = CRC_TABLE[index] ^ (crc << 8);
}
- if(crc != 0){
+ if (crc != 0) {
return false;
}
}
diff --git a/src/com/android/tv/tuner/ts/TsParser.java b/tuner/src/com/android/tv/tuner/ts/TsParser.java
index 7cdb534e..2307c22a 100644
--- a/src/com/android/tv/tuner/ts/TsParser.java
+++ b/tuner/src/com/android/tv/tuner/ts/TsParser.java
@@ -19,7 +19,6 @@ package com.android.tv.tuner.ts;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
-
import com.android.tv.tuner.data.PsiData.PatItem;
import com.android.tv.tuner.data.PsiData.PmtItem;
import com.android.tv.tuner.data.PsipData.EitItem;
@@ -30,7 +29,6 @@ import com.android.tv.tuner.data.PsipData.VctItem;
import com.android.tv.tuner.data.TunerChannel;
import com.android.tv.tuner.ts.SectionParser.OutputListener;
import com.android.tv.tuner.util.ByteArrayBuffer;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -38,9 +36,7 @@ import java.util.List;
import java.util.Map;
import java.util.TreeSet;
-/**
- * Parses MPEG-2 TS packets.
- */
+/** Parses MPEG-2 TS packets. */
public class TsParser {
private static final String TAG = "TsParser";
private static final boolean DEBUG = false;
@@ -84,15 +80,21 @@ public class TsParser {
public interface TsOutputListener {
void onPatDetected(List<PatItem> items);
+
void onEitPidDetected(int pid);
+
void onVctItemParsed(VctItem channel, List<PmtItem> pmtItems);
+
void onEitItemParsed(VctItem channel, List<EitItem> items);
+
void onEttPidDetected(int pid);
+
void onAllVctItemsParsed();
+
void onSdtItemParsed(SdtItem channel, List<PmtItem> pmtItems);
}
- private abstract class Stream {
+ private abstract static class Stream {
private static final int INVALID_CONTINUITY_COUNTER = -1;
private static final int NUM_CONTINUITY_COUNTER = 16;
@@ -108,6 +110,7 @@ public class TsParser {
}
protected abstract void handleData(byte[] data, boolean startIndicator);
+
protected abstract void resetDataVersions();
}
@@ -150,169 +153,187 @@ public class TsParser {
mSectionParser.resetVersionNumbers();
}
- private final OutputListener mSectionListener = new OutputListener() {
- @Override
- public void onPatParsed(List<PatItem> items) {
- for (PatItem i : items) {
- startListening(i.getPmtPid());
- }
- if (mListener != null) {
- mListener.onPatDetected(items);
- }
- }
-
- @Override
- public void onPmtParsed(int programNumber, List<PmtItem> items) {
- mProgramNumberToPMTMap.put(programNumber, items);
- if (DEBUG) {
- Log.d(TAG, "onPMTParsed, programNo " + programNumber + " handledStatus is "
- + mProgramNumberHandledStatus.get(programNumber, false));
- }
- int statusIndex = mProgramNumberHandledStatus.indexOfKey(programNumber);
- if (statusIndex < 0) {
- mProgramNumberHandledStatus.put(programNumber, false);
- }
- if (!mProgramNumberHandledStatus.get(programNumber)) {
- VctItem vctItem = mProgramNumberToVctItemMap.get(programNumber);
- if (vctItem != null) {
- // When PMT is parsed later than VCT.
- mProgramNumberHandledStatus.put(programNumber, true);
- handleVctItem(vctItem, items);
- mHandledVctItemCount++;
- if (mHandledVctItemCount >= mVctItemCount
- && mVctSectionParsedCount >= mVctSectionParsed.length
- && mListener != null) {
- mListener.onAllVctItemsParsed();
+ private final OutputListener mSectionListener =
+ new OutputListener() {
+ @Override
+ public void onPatParsed(List<PatItem> items) {
+ for (PatItem i : items) {
+ startListening(i.getPmtPid());
+ }
+ if (mListener != null) {
+ mListener.onPatDetected(items);
}
}
- SdtItem sdtItem = mProgramNumberToSdtItemMap.get(programNumber);
- if (sdtItem != null) {
- // When PMT is parsed later than SDT.
- mProgramNumberHandledStatus.put(programNumber, true);
- handleSdtItem(sdtItem, items);
- }
- }
- }
- @Override
- public void onMgtParsed(List<MgtItem> items) {
- for (MgtItem i : items) {
- if (mStreamMap.get(i.getTableTypePid()) != null) {
- continue;
- }
- if (i.getTableType() >= MgtItem.TABLE_TYPE_EIT_RANGE_START
- && i.getTableType() <= MgtItem.TABLE_TYPE_EIT_RANGE_END) {
- startListening(i.getTableTypePid());
- mEITPids.add(i.getTableTypePid());
- if (mListener != null) {
- mListener.onEitPidDetected(i.getTableTypePid());
+ @Override
+ public void onPmtParsed(int programNumber, List<PmtItem> items) {
+ mProgramNumberToPMTMap.put(programNumber, items);
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "onPMTParsed, programNo "
+ + programNumber
+ + " handledStatus is "
+ + mProgramNumberHandledStatus.get(
+ programNumber, false));
}
- } else if (i.getTableType() == MgtItem.TABLE_TYPE_CHANNEL_ETT ||
- (i.getTableType() >= MgtItem.TABLE_TYPE_ETT_RANGE_START
- && i.getTableType() <= MgtItem.TABLE_TYPE_ETT_RANGE_END)) {
- startListening(i.getTableTypePid());
- mETTPids.add(i.getTableTypePid());
- if (mListener != null) {
- mListener.onEttPidDetected(i.getTableTypePid());
+ int statusIndex = mProgramNumberHandledStatus.indexOfKey(programNumber);
+ if (statusIndex < 0) {
+ mProgramNumberHandledStatus.put(programNumber, false);
+ }
+ if (!mProgramNumberHandledStatus.get(programNumber)) {
+ VctItem vctItem = mProgramNumberToVctItemMap.get(programNumber);
+ if (vctItem != null) {
+ // When PMT is parsed later than VCT.
+ mProgramNumberHandledStatus.put(programNumber, true);
+ handleVctItem(vctItem, items);
+ mHandledVctItemCount++;
+ if (mHandledVctItemCount >= mVctItemCount
+ && mVctSectionParsedCount >= mVctSectionParsed.length
+ && mListener != null) {
+ mListener.onAllVctItemsParsed();
+ }
+ }
+ SdtItem sdtItem = mProgramNumberToSdtItemMap.get(programNumber);
+ if (sdtItem != null) {
+ // When PMT is parsed later than SDT.
+ mProgramNumberHandledStatus.put(programNumber, true);
+ handleSdtItem(sdtItem, items);
+ }
}
}
- }
- }
- @Override
- public void onVctParsed(List<VctItem> items, int sectionNumber, int lastSectionNumber) {
- if (mVctSectionParsed == null) {
- mVctSectionParsed = new boolean[lastSectionNumber + 1];
- } else if (mVctSectionParsed[sectionNumber]) {
- // The current section was handled before.
- if (DEBUG) {
- Log.d(TAG, "Duplicate VCT section found.");
- }
- return;
- }
- mVctSectionParsed[sectionNumber] = true;
- mVctSectionParsedCount++;
- mVctItemCount += items.size();
- for (VctItem i : items) {
- if (DEBUG) Log.d(TAG, "onVCTParsed " + i);
- if (i.getSourceId() != 0) {
- mSourceIdToVctItemMap.put(i.getSourceId(), i);
- i.setDescription(mSourceIdToVctItemDescriptionMap.get(i.getSourceId()));
+ @Override
+ public void onMgtParsed(List<MgtItem> items) {
+ for (MgtItem i : items) {
+ if (mStreamMap.get(i.getTableTypePid()) != null) {
+ continue;
+ }
+ if (i.getTableType() >= MgtItem.TABLE_TYPE_EIT_RANGE_START
+ && i.getTableType() <= MgtItem.TABLE_TYPE_EIT_RANGE_END) {
+ startListening(i.getTableTypePid());
+ mEITPids.add(i.getTableTypePid());
+ if (mListener != null) {
+ mListener.onEitPidDetected(i.getTableTypePid());
+ }
+ } else if (i.getTableType() == MgtItem.TABLE_TYPE_CHANNEL_ETT
+ || (i.getTableType() >= MgtItem.TABLE_TYPE_ETT_RANGE_START
+ && i.getTableType()
+ <= MgtItem.TABLE_TYPE_ETT_RANGE_END)) {
+ startListening(i.getTableTypePid());
+ mETTPids.add(i.getTableTypePid());
+ if (mListener != null) {
+ mListener.onEttPidDetected(i.getTableTypePid());
+ }
+ }
+ }
}
- int programNumber = i.getProgramNumber();
- mProgramNumberToVctItemMap.put(programNumber, i);
- List<PmtItem> pmtList = mProgramNumberToPMTMap.get(programNumber);
- if (pmtList != null) {
- mProgramNumberHandledStatus.put(programNumber, true);
- handleVctItem(i, pmtList);
- mHandledVctItemCount++;
- if (mHandledVctItemCount >= mVctItemCount
- && mVctSectionParsedCount >= mVctSectionParsed.length
- && mListener != null) {
- mListener.onAllVctItemsParsed();
+
+ @Override
+ public void onVctParsed(
+ List<VctItem> items, int sectionNumber, int lastSectionNumber) {
+ if (mVctSectionParsed == null) {
+ mVctSectionParsed = new boolean[lastSectionNumber + 1];
+ } else if (mVctSectionParsed[sectionNumber]) {
+ // The current section was handled before.
+ if (DEBUG) {
+ Log.d(TAG, "Duplicate VCT section found.");
+ }
+ return;
+ }
+ mVctSectionParsed[sectionNumber] = true;
+ mVctSectionParsedCount++;
+ mVctItemCount += items.size();
+ for (VctItem i : items) {
+ if (DEBUG) Log.d(TAG, "onVCTParsed " + i);
+ if (i.getSourceId() != 0) {
+ mSourceIdToVctItemMap.put(i.getSourceId(), i);
+ i.setDescription(
+ mSourceIdToVctItemDescriptionMap.get(i.getSourceId()));
+ }
+ int programNumber = i.getProgramNumber();
+ mProgramNumberToVctItemMap.put(programNumber, i);
+ List<PmtItem> pmtList = mProgramNumberToPMTMap.get(programNumber);
+ if (pmtList != null) {
+ mProgramNumberHandledStatus.put(programNumber, true);
+ handleVctItem(i, pmtList);
+ mHandledVctItemCount++;
+ if (mHandledVctItemCount >= mVctItemCount
+ && mVctSectionParsedCount >= mVctSectionParsed.length
+ && mListener != null) {
+ mListener.onAllVctItemsParsed();
+ }
+ } else {
+ mProgramNumberHandledStatus.put(programNumber, false);
+ Log.i(
+ TAG,
+ "onVCTParsed, but PMT for programNo "
+ + programNumber
+ + " is not found yet.");
+ }
}
- } else {
- mProgramNumberHandledStatus.put(programNumber, false);
- Log.i(TAG, "onVCTParsed, but PMT for programNo " + programNumber
- + " is not found yet.");
}
- }
- }
- @Override
- public void onEitParsed(int sourceId, List<EitItem> items) {
- if (DEBUG) Log.d(TAG, "onEITParsed " + sourceId);
- EventSourceEntry entry = new EventSourceEntry(mPid, sourceId);
- mEitMap.put(entry, items);
- handleEvents(sourceId);
- }
+ @Override
+ public void onEitParsed(int sourceId, List<EitItem> items) {
+ if (DEBUG) Log.d(TAG, "onEITParsed " + sourceId);
+ EventSourceEntry entry = new EventSourceEntry(mPid, sourceId);
+ mEitMap.put(entry, items);
+ handleEvents(sourceId);
+ }
- @Override
- public void onEttParsed(int sourceId, List<EttItem> descriptions) {
- if (DEBUG) {
- Log.d(TAG, String.format("onETTParsed sourceId: %d, descriptions.size(): %d",
- sourceId, descriptions.size()));
- }
- for (EttItem item : descriptions) {
- if (item.eventId == 0) {
- // Channel description
- mSourceIdToVctItemDescriptionMap.put(sourceId, item.text);
- VctItem vctItem = mSourceIdToVctItemMap.get(sourceId);
- if (vctItem != null) {
- vctItem.setDescription(item.text);
- List<PmtItem> pmtItems =
- mProgramNumberToPMTMap.get(vctItem.getProgramNumber());
- if (pmtItems != null) {
- handleVctItem(vctItem, pmtItems);
+ @Override
+ public void onEttParsed(int sourceId, List<EttItem> descriptions) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ String.format(
+ "onETTParsed sourceId: %d, descriptions.size(): %d",
+ sourceId, descriptions.size()));
+ }
+ for (EttItem item : descriptions) {
+ if (item.eventId == 0) {
+ // Channel description
+ mSourceIdToVctItemDescriptionMap.put(sourceId, item.text);
+ VctItem vctItem = mSourceIdToVctItemMap.get(sourceId);
+ if (vctItem != null) {
+ vctItem.setDescription(item.text);
+ List<PmtItem> pmtItems =
+ mProgramNumberToPMTMap.get(vctItem.getProgramNumber());
+ if (pmtItems != null) {
+ handleVctItem(vctItem, pmtItems);
+ }
+ }
}
}
- }
- }
- // Event Information description
- EventSourceEntry entry = new EventSourceEntry(mPid, sourceId);
- mETTMap.put(entry, descriptions);
- handleEvents(sourceId);
- }
+ // Event Information description
+ EventSourceEntry entry = new EventSourceEntry(mPid, sourceId);
+ mETTMap.put(entry, descriptions);
+ handleEvents(sourceId);
+ }
- @Override
- public void onSdtParsed(List<SdtItem> sdtItems) {
- for (SdtItem sdtItem : sdtItems) {
- if (DEBUG) Log.d(TAG, "onSdtParsed " + sdtItem);
- int programNumber = sdtItem.getServiceId();
- mProgramNumberToSdtItemMap.put(programNumber, sdtItem);
- List<PmtItem> pmtList = mProgramNumberToPMTMap.get(programNumber);
- if (pmtList != null) {
- mProgramNumberHandledStatus.put(programNumber, true);
- handleSdtItem(sdtItem, pmtList);
- } else {
- mProgramNumberHandledStatus.put(programNumber, false);
- Log.i(TAG, "onSdtParsed, but PMT for programNo " + programNumber
- + " is not found yet.");
+ @Override
+ public void onSdtParsed(List<SdtItem> sdtItems) {
+ for (SdtItem sdtItem : sdtItems) {
+ if (DEBUG) Log.d(TAG, "onSdtParsed " + sdtItem);
+ int programNumber = sdtItem.getServiceId();
+ mProgramNumberToSdtItemMap.put(programNumber, sdtItem);
+ List<PmtItem> pmtList = mProgramNumberToPMTMap.get(programNumber);
+ if (pmtList != null) {
+ mProgramNumberHandledStatus.put(programNumber, true);
+ handleSdtItem(sdtItem, pmtList);
+ } else {
+ mProgramNumberHandledStatus.put(programNumber, false);
+ Log.i(
+ TAG,
+ "onSdtParsed, but PMT for programNo "
+ + programNumber
+ + " is not found yet.");
+ }
+ }
}
- }
- }
- };
+ };
}
private static class EventSourceEntry {
@@ -470,13 +491,16 @@ public class TsParser {
if (DEBUG) Log.d(TAG, "Payload should be included in a single TS packet.");
return false;
}
- stream.feedData(Arrays.copyOfRange(tsData, payloadPos, pos + TS_PACKET_SIZE),
- continuityCounter, payloadStartIndicator);
+ stream.feedData(
+ Arrays.copyOfRange(tsData, payloadPos, pos + TS_PACKET_SIZE),
+ continuityCounter,
+ payloadStartIndicator);
return true;
}
/**
* Feeds MPEG-2 TS data to parse.
+ *
* @param tsData buffer for ATSC TS stream
* @param pos the offset where buffer starts
* @param length The length of available data
@@ -489,6 +513,7 @@ public class TsParser {
/**
* Retrieves the channel information regardless of being well-formed.
+ *
* @return {@link List} of {@link TunerChannel}
*/
public List<TunerChannel> getMalFormedChannels() {
@@ -506,9 +531,7 @@ public class TsParser {
return incompleteChannels;
}
- /**
- * Reset the versions so that data with old version number can be handled.
- */
+ /** Reset the versions so that data with old version number can be handled. */
public void resetDataVersions() {
for (int eitPid : mEITPids) {
Stream stream = mStreamMap.get(eitPid);
diff --git a/src/com/android/tv/tuner/tvinput/TunerTvInputService.java b/tuner/src/com/android/tv/tuner/tvinput/BaseTunerTvInputService.java
index 2725ddfc..e577e35e 100644
--- a/src/com/android/tv/tuner/tvinput/TunerTvInputService.java
+++ b/tuner/src/com/android/tv/tuner/tvinput/BaseTunerTvInputService.java
@@ -20,26 +20,20 @@ import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
-import android.media.tv.TvContract;
import android.media.tv.TvInputService;
import android.util.Log;
-
+import com.android.tv.common.feature.CommonFeatures;
import com.google.android.exoplayer.audio.AudioCapabilities;
import com.google.android.exoplayer.audio.AudioCapabilitiesReceiver;
-import com.android.tv.TvApplication;
-import com.android.tv.common.feature.CommonFeatures;
-
import java.util.Collections;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.TimeUnit;
-/**
- * {@link TunerTvInputService} serves TV channels coming from a tuner device.
- */
-public class TunerTvInputService extends TvInputService
- implements AudioCapabilitiesReceiver.Listener{
- private static final String TAG = "TunerTvInputService";
+/** {@link BaseTunerTvInputService} serves TV channels coming from a tuner device. */
+public class BaseTunerTvInputService extends TvInputService
+ implements AudioCapabilitiesReceiver.Listener {
+ private static final String TAG = "BaseTunerTvInputService";
private static final boolean DEBUG = false;
private static final int DVR_STORAGE_CLEANUP_JOB_ID = 100;
@@ -52,12 +46,11 @@ public class TunerTvInputService extends TvInputService
@Override
public void onCreate() {
- if (!TvApplication.getSingletons(this).getTvInputManagerHelper().hasTvInputManager()) {
+ if (getApplicationContext().getSystemService(Context.TV_INPUT_SERVICE) == null) {
Log.wtf(TAG, "Stopping because device does not have a TvInputManager");
this.stopSelf();
return;
}
- TvApplication.setCurrentRunningProcess(this, false);
super.onCreate();
if (DEBUG) Log.d(TAG, "onCreate");
mChannelDataManager = new ChannelDataManager(getApplicationContext());
@@ -70,9 +63,13 @@ public class TunerTvInputService extends TvInputService
if (pendingJob != null) {
// storage cleaning job is already scheduled.
} else {
- JobInfo job = new JobInfo.Builder(DVR_STORAGE_CLEANUP_JOB_ID,
- new ComponentName(this, TunerStorageCleanUpService.class))
- .setPersisted(true).setPeriodic(TimeUnit.DAYS.toMillis(1)).build();
+ JobInfo job =
+ new JobInfo.Builder(
+ DVR_STORAGE_CLEANUP_JOB_ID,
+ new ComponentName(this, TunerStorageCleanUpService.class))
+ .setPersisted(true)
+ .setPeriodic(TimeUnit.DAYS.toMillis(1))
+ .build();
jobScheduler.schedule(job);
}
}
@@ -95,6 +92,11 @@ public class TunerTvInputService extends TvInputService
public Session onCreateSession(String inputId) {
if (DEBUG) Log.d(TAG, "onCreateSession");
try {
+ // TODO(b/65445352): Support multiple TunerSessions for multiple tuners
+ if (!allSessionsReleased()) {
+ Log.d(TAG, "abort creating an session");
+ return null;
+ }
final TunerSession session = new TunerSession(this, mChannelDataManager);
mTunerSessions.add(session);
session.setAudioCapabilities(mAudioCapabilities);
@@ -117,7 +119,12 @@ public class TunerTvInputService extends TvInputService
}
}
- public static String getInputId(Context context) {
- return TvContract.buildInputId(new ComponentName(context, TunerTvInputService.class));
+ private boolean allSessionsReleased() {
+ for (TunerSession session : mTunerSessions) {
+ if (!session.isReleased()) {
+ return false;
+ }
+ }
+ return true;
}
}
diff --git a/src/com/android/tv/tuner/tvinput/ChannelDataManager.java b/tuner/src/com/android/tv/tuner/tvinput/ChannelDataManager.java
index d2b4998a..c1d8f278 100644
--- a/src/com/android/tv/tuner/tvinput/ChannelDataManager.java
+++ b/tuner/src/com/android/tv/tuner/tvinput/ChannelDataManager.java
@@ -16,7 +16,6 @@
package com.android.tv.tuner.tvinput;
-import android.content.ComponentName;
import android.content.ContentProviderOperation;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -33,13 +32,12 @@ import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.text.format.DateUtils;
import android.util.Log;
-
+import com.android.tv.common.BaseApplication;
+import com.android.tv.common.util.PermissionUtils;
import com.android.tv.tuner.TunerPreferences;
import com.android.tv.tuner.data.PsipData.EitItem;
import com.android.tv.tuner.data.TunerChannel;
import com.android.tv.tuner.util.ConvertUtils;
-import com.android.tv.util.PermissionUtils;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -53,26 +51,29 @@ import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
-/**
- * Manages the channel info and EPG data through {@link TvInputManager}.
- */
+/** Manages the channel info and EPG data through {@link TvInputManager}. */
public class ChannelDataManager implements Handler.Callback {
private static final String TAG = "ChannelDataManager";
- private static final String[] ALL_PROGRAMS_SELECTION_ARGS = new String[] {
- TvContract.Programs._ID,
- TvContract.Programs.COLUMN_TITLE,
- TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS,
- TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS,
- TvContract.Programs.COLUMN_CONTENT_RATING,
- TvContract.Programs.COLUMN_BROADCAST_GENRE,
- TvContract.Programs.COLUMN_CANONICAL_GENRE,
- TvContract.Programs.COLUMN_SHORT_DESCRIPTION,
- TvContract.Programs.COLUMN_VERSION_NUMBER };
- private static final String[] CHANNEL_DATA_SELECTION_ARGS = new String[] {
- TvContract.Channels._ID,
- TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA,
- TvContract.Channels.COLUMN_INTERNAL_PROVIDER_FLAG1};
+ private static final String[] ALL_PROGRAMS_SELECTION_ARGS =
+ new String[] {
+ TvContract.Programs._ID,
+ TvContract.Programs.COLUMN_TITLE,
+ TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS,
+ TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS,
+ TvContract.Programs.COLUMN_CONTENT_RATING,
+ TvContract.Programs.COLUMN_BROADCAST_GENRE,
+ TvContract.Programs.COLUMN_CANONICAL_GENRE,
+ TvContract.Programs.COLUMN_SHORT_DESCRIPTION,
+ TvContract.Programs.COLUMN_VERSION_NUMBER
+ };
+ private static final String[] CHANNEL_DATA_SELECTION_ARGS =
+ new String[] {
+ TvContract.Channels._ID,
+ TvContract.Channels.COLUMN_LOCKED,
+ TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA,
+ TvContract.Channels.COLUMN_INTERNAL_PROVIDER_FLAG1
+ };
private static final int MSG_HANDLE_EVENTS = 1;
private static final int MSG_HANDLE_CHANNEL = 2;
@@ -90,9 +91,9 @@ public class ChannelDataManager implements Handler.Callback {
/**
* A version number to enforce consistency of the channel data.
*
- * WARNING: If a change in the database serialization lead to breaking the backward
- * compatibility, you must increment this value so that the old data are purged,
- * and the user is requested to perform the auto-scan again to generate the new data set.
+ * <p>WARNING: If a change in the database serialization lead to breaking the backward
+ * compatibility, you must increment this value so that the old data are purged, and the user is
+ * requested to perform the auto-scan again to generate the new data set.
*/
private static final int VERSION = 6;
@@ -140,16 +141,13 @@ public class ChannelDataManager implements Handler.Callback {
}
public interface ChannelScanListener {
- /**
- * Invoked when all pending channels have been handled.
- */
+ /** Invoked when all pending channels have been handled. */
void onChannelHandlingDone();
}
public ChannelDataManager(Context context) {
mContext = context;
- mInputId = TvContract.buildInputId(new ComponentName(mContext.getPackageName(),
- TunerTvInputService.class.getName()));
+ mInputId = BaseApplication.getSingletons(context).getEmbeddedTunerInputId();
mChannelsUri = TvContract.buildChannelsUriForInput(mInputId);
mTunerChannelMap = new ConcurrentHashMap<>();
mTunerChannelIdMap = new ConcurrentSkipListMap<>();
@@ -211,10 +209,18 @@ public class ChannelDataManager implements Handler.Callback {
}
mHandler.sendEmptyMessage(MSG_BUILD_CHANNEL_MAP);
byte[] data = null;
- try (Cursor cursor = mContext.getContentResolver().query(TvContract.buildChannelUri(
- channelId), CHANNEL_DATA_SELECTION_ARGS, null, null, null)) {
+ boolean locked = false;
+ try (Cursor cursor =
+ mContext.getContentResolver()
+ .query(
+ TvContract.buildChannelUri(channelId),
+ CHANNEL_DATA_SELECTION_ARGS,
+ null,
+ null,
+ null)) {
if (cursor != null && cursor.moveToFirst()) {
- data = cursor.getBlob(1);
+ locked = cursor.getInt(1) > 0;
+ data = cursor.getBlob(2);
}
}
if (data == null) {
@@ -224,6 +230,7 @@ public class ChannelDataManager implements Handler.Callback {
if (channel == null) {
return null;
}
+ channel.setLocked(locked);
channel.setChannelId(channelId);
return channel;
}
@@ -255,15 +262,13 @@ public class ChannelDataManager implements Handler.Callback {
public void notifyScanStarted() {
mScannedChannels.clear();
mPreviousScannedChannels.clear();
- try (Cursor cursor = mContext.getContentResolver().query(mChannelsUri,
- CHANNEL_DATA_SELECTION_ARGS, null, null, null)) {
+ try (Cursor cursor =
+ mContext.getContentResolver()
+ .query(mChannelsUri, CHANNEL_DATA_SELECTION_ARGS, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
do {
- long channelId = cursor.getLong(0);
- byte[] data = cursor.getBlob(1);
- TunerChannel channel = TunerChannel.parseFrom(data);
+ TunerChannel channel = TunerChannel.fromCursor(cursor);
if (channel != null) {
- channel.setChannelId(channelId);
mPreviousScannedChannels.add(channel);
}
} while (cursor.moveToNext());
@@ -289,8 +294,10 @@ public class ChannelDataManager implements Handler.Callback {
if (!mPreviousScannedChannels.isEmpty()) {
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
for (TunerChannel channel : mPreviousScannedChannels) {
- ops.add(ContentProviderOperation.newDelete(
- TvContract.buildChannelUri(channel.getChannelId())).build());
+ ops.add(
+ ContentProviderOperation.newDelete(
+ TvContract.buildChannelUri(channel.getChannelId()))
+ .build());
}
try {
mContext.getContentResolver().applyBatch(TvContract.AUTHORITY, ops);
@@ -299,20 +306,19 @@ public class ChannelDataManager implements Handler.Callback {
}
}
if (mChannelScanListener != null && mChannelScanHandler != null) {
- mChannelScanHandler.post(new Runnable() {
- @Override
- public void run() {
- mChannelScanListener.onChannelHandlingDone();
- }
- });
+ mChannelScanHandler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ mChannelScanListener.onChannelHandlingDone();
+ }
+ });
} else {
Log.e(TAG, "Error. mChannelScanListener is null.");
}
}
- /**
- * Returns the number of scanned channels in the scanning mode.
- */
+ /** Returns the number of scanned channels in the scanning mode. */
public int getScannedChannelCount() {
return mScannedChannels.size();
}
@@ -327,46 +333,56 @@ public class ChannelDataManager implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
- case MSG_HANDLE_EVENTS: {
- ChannelEvent event = (ChannelEvent) msg.obj;
- handleEvents(event.channel, event.eitItems);
- return true;
- }
- case MSG_HANDLE_CHANNEL: {
- TunerChannel channel = (TunerChannel) msg.obj;
- if (channel != null) {
- handleChannel(channel);
+ case MSG_HANDLE_EVENTS:
+ {
+ ChannelEvent event = (ChannelEvent) msg.obj;
+ handleEvents(event.channel, event.eitItems);
+ return true;
}
- if (scanCompleted.get() && mIsScanning.get()
- && !mHandler.hasMessages(MSG_HANDLE_CHANNEL)) {
- // Complete the scan when all found channels have already been handled.
- scannedChannelHandlingCompleted();
+ case MSG_HANDLE_CHANNEL:
+ {
+ TunerChannel channel = (TunerChannel) msg.obj;
+ if (channel != null) {
+ handleChannel(channel);
+ }
+ if (scanCompleted.get()
+ && mIsScanning.get()
+ && !mHandler.hasMessages(MSG_HANDLE_CHANNEL)) {
+ // Complete the scan when all found channels have already been handled.
+ scannedChannelHandlingCompleted();
+ }
+ return true;
}
- return true;
- }
- case MSG_BUILD_CHANNEL_MAP: {
- mHandler.removeMessages(MSG_BUILD_CHANNEL_MAP);
- buildChannelMap();
- return true;
- }
- case MSG_REQUEST_PROGRAMS: {
- if (mHandler.hasMessages(MSG_REQUEST_PROGRAMS)) {
+ case MSG_BUILD_CHANNEL_MAP:
+ {
+ mHandler.removeMessages(MSG_BUILD_CHANNEL_MAP);
+ buildChannelMap();
return true;
}
- TunerChannel channel = (TunerChannel) msg.obj;
- if (mListener != null) {
- mListener.onRequestProgramsResponse(channel, getAllProgramsForChannel(channel));
+ case MSG_REQUEST_PROGRAMS:
+ {
+ if (mHandler.hasMessages(MSG_REQUEST_PROGRAMS)) {
+ return true;
+ }
+ TunerChannel channel = (TunerChannel) msg.obj;
+ if (mListener != null) {
+ mListener.onRequestProgramsResponse(
+ channel, getAllProgramsForChannel(channel));
+ }
+ return true;
}
- return true;
- }
- case MSG_CLEAR_CHANNELS: {
- clearChannels();
- return true;
- }
- case MSG_CHECK_VERSION: {
- checkVersion();
- return true;
- }
+ case MSG_CLEAR_CHANNELS:
+ {
+ clearChannels();
+ return true;
+ }
+ case MSG_CHECK_VERSION:
+ {
+ checkVersion();
+ return true;
+ }
+ default: // fall out
+ Log.w(TAG, "unexpected case in handleMessage ( " + msg.what + " )");
}
return false;
}
@@ -386,8 +402,9 @@ public class ChannelDataManager implements Handler.Callback {
}
long currentTime = System.currentTimeMillis();
- List<EitItem> oldItems = getAllProgramsForChannel(channel, currentTime,
- currentTime + PROGRAM_QUERY_DURATION);
+ List<EitItem> oldItems =
+ getAllProgramsForChannel(
+ channel, currentTime, currentTime + PROGRAM_QUERY_DURATION);
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
// TODO: Find a right way to check if the programs are added outside.
boolean addedOutside = false;
@@ -417,17 +434,21 @@ public class ChannelDataManager implements Handler.Callback {
} else if (newItemStartTime
> oldItems.get(oldItemCount - 1).getStartTimeUtcMillis()) {
// Start time larger than that of any old item. Insert if no overlap.
- if (newItemStartTime
- < oldItems.get(oldItemCount - 1).getEndTimeUtcMillis()) continue;
+ if (newItemStartTime < oldItems.get(oldItemCount - 1).getEndTimeUtcMillis())
+ continue;
} else {
- int pos = Collections.binarySearch(oldItems, newItem,
- new Comparator<EitItem>() {
- @Override
- public int compare(EitItem lhs, EitItem rhs) {
- return Long.compare(lhs.getStartTimeUtcMillis(),
- rhs.getStartTimeUtcMillis());
- }
- });
+ int pos =
+ Collections.binarySearch(
+ oldItems,
+ newItem,
+ new Comparator<EitItem>() {
+ @Override
+ public int compare(EitItem lhs, EitItem rhs) {
+ return Long.compare(
+ lhs.getStartTimeUtcMillis(),
+ rhs.getStartTimeUtcMillis());
+ }
+ });
if (pos >= 0) {
// Same start Time found. Overlapped.
continue;
@@ -439,8 +460,11 @@ public class ChannelDataManager implements Handler.Callback {
continue;
}
}
- ops.add(buildContentProviderOperation(ContentProviderOperation.newInsert(
- TvContract.Programs.CONTENT_URI), newItem, channel));
+ ops.add(
+ buildContentProviderOperation(
+ ContentProviderOperation.newInsert(TvContract.Programs.CONTENT_URI),
+ newItem,
+ channel));
if (ops.size() >= BATCH_OPERATION_COUNT) {
applyBatch(channel.getName(), ops);
ops.clear();
@@ -464,7 +488,8 @@ public class ChannelDataManager implements Handler.Callback {
// Since program descriptions arrive at different time, the older one may have the
// correct program description while the newer one has no clue what value is.
- if (oldItem.getDescription() != null && item.getDescription() == null
+ if (oldItem.getDescription() != null
+ && item.getDescription() == null
&& oldItem.getEventId() == item.getEventId()
&& oldItem.getStartTime() == item.getStartTime()
&& oldItem.getLengthInSecond() == item.getLengthInSecond()
@@ -474,8 +499,12 @@ public class ChannelDataManager implements Handler.Callback {
item.setDescription(oldItem.getDescription());
}
if (item.compareTo(oldItem) != 0) {
- ops.add(buildContentProviderOperation(ContentProviderOperation.newUpdate(
- TvContract.buildProgramUri(oldItem.getProgramId())), item, null));
+ ops.add(
+ buildContentProviderOperation(
+ ContentProviderOperation.newUpdate(
+ TvContract.buildProgramUri(oldItem.getProgramId())),
+ item,
+ null));
if (ops.size() >= BATCH_OPERATION_COUNT) {
applyBatch(channel.getName(), ops);
ops.clear();
@@ -494,8 +523,11 @@ public class ChannelDataManager implements Handler.Callback {
long newItemEndTime = item.getEndTimeUtcMillis();
if ((startTime >= newItemStartTime && startTime < newItemEndTime)
|| (endTime > newItemStartTime && endTime <= newItemEndTime)) {
- ops.add(ContentProviderOperation.newDelete(TvContract.buildProgramUri(
- unverifiedOldItems.getProgramId())).build());
+ ops.add(
+ ContentProviderOperation.newDelete(
+ TvContract.buildProgramUri(
+ unverifiedOldItems.getProgramId()))
+ .build());
if (ops.size() >= BATCH_OPERATION_COUNT) {
applyBatch(channel.getName(), ops);
ops.clear();
@@ -509,8 +541,11 @@ public class ChannelDataManager implements Handler.Callback {
if (item.getEndTimeUtcMillis() < currentTime) {
continue;
}
- ops.add(buildContentProviderOperation(ContentProviderOperation.newInsert(
- TvContract.Programs.CONTENT_URI), item, channel));
+ ops.add(
+ buildContentProviderOperation(
+ ContentProviderOperation.newInsert(TvContract.Programs.CONTENT_URI),
+ item,
+ channel));
if (ops.size() >= BATCH_OPERATION_COUNT) {
applyBatch(channel.getName(), ops);
ops.clear();
@@ -525,24 +560,23 @@ public class ChannelDataManager implements Handler.Callback {
if (channel != null) {
builder.withValue(TvContract.Programs.COLUMN_CHANNEL_ID, channel.getChannelId());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- builder.withValue(TvContract.Programs.COLUMN_RECORDING_PROHIBITED,
+ builder.withValue(
+ TvContract.Programs.COLUMN_RECORDING_PROHIBITED,
channel.isRecordingProhibited() ? 1 : 0);
}
}
if (item != null) {
builder.withValue(TvContract.Programs.COLUMN_TITLE, item.getTitleText())
- .withValue(TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS,
+ .withValue(
+ TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS,
item.getStartTimeUtcMillis())
- .withValue(TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS,
+ .withValue(
+ TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS,
item.getEndTimeUtcMillis())
- .withValue(TvContract.Programs.COLUMN_CONTENT_RATING,
- item.getContentRating())
- .withValue(TvContract.Programs.COLUMN_AUDIO_LANGUAGE,
- item.getAudioLanguage())
- .withValue(TvContract.Programs.COLUMN_SHORT_DESCRIPTION,
- item.getDescription())
- .withValue(TvContract.Programs.COLUMN_VERSION_NUMBER,
- item.getEventId());
+ .withValue(TvContract.Programs.COLUMN_CONTENT_RATING, item.getContentRating())
+ .withValue(TvContract.Programs.COLUMN_AUDIO_LANGUAGE, item.getAudioLanguage())
+ .withValue(TvContract.Programs.COLUMN_SHORT_DESCRIPTION, item.getDescription())
+ .withValue(TvContract.Programs.COLUMN_VERSION_NUMBER, item.getEventId());
}
return builder.build();
}
@@ -567,24 +601,28 @@ public class ChannelDataManager implements Handler.Callback {
values.put(TvContract.Channels.COLUMN_DESCRIPTION, channel.getDescription());
values.put(TvContract.Channels.COLUMN_VIDEO_FORMAT, channel.getVideoFormat());
values.put(TvContract.Channels.COLUMN_INTERNAL_PROVIDER_FLAG1, VERSION);
- values.put(TvContract.Channels.COLUMN_INTERNAL_PROVIDER_FLAG2,
+ values.put(
+ TvContract.Channels.COLUMN_INTERNAL_PROVIDER_FLAG2,
channel.isRecordingProhibited() ? 1 : 0);
if (channelId <= 0) {
values.put(TvContract.Channels.COLUMN_INPUT_ID, mInputId);
- values.put(TvContract.Channels.COLUMN_TYPE, "QAM256".equals(channel.getModulation())
- ? TvContract.Channels.TYPE_ATSC_C : TvContract.Channels.TYPE_ATSC_T);
+ values.put(
+ TvContract.Channels.COLUMN_TYPE,
+ "QAM256".equals(channel.getModulation())
+ ? TvContract.Channels.TYPE_ATSC_C
+ : TvContract.Channels.TYPE_ATSC_T);
values.put(TvContract.Channels.COLUMN_SERVICE_ID, channel.getProgramNumber());
// ATSC doesn't have original_network_id
values.put(TvContract.Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.getFrequency());
- Uri channelUri = mContext.getContentResolver().insert(TvContract.Channels.CONTENT_URI,
- values);
+ Uri channelUri =
+ mContext.getContentResolver().insert(TvContract.Channels.CONTENT_URI, values);
channelId = ContentUris.parseId(channelUri);
} else {
- mContext.getContentResolver().update(
- TvContract.buildChannelUri(channelId), values, null, null);
+ mContext.getContentResolver()
+ .update(TvContract.buildChannelUri(channelId), values, null, null);
}
channel.setChannelId(channelId);
mTunerChannelMap.put(channelId, channel);
@@ -612,18 +650,30 @@ public class ChannelDataManager implements Handler.Callback {
private void checkVersion() {
if (PermissionUtils.hasAccessAllEpg(mContext)) {
String selection = TvContract.Channels.COLUMN_INTERNAL_PROVIDER_FLAG1 + "<>?";
- try (Cursor cursor = mContext.getContentResolver().query(mChannelsUri,
- CHANNEL_DATA_SELECTION_ARGS, selection,
- new String[] {Integer.toString(VERSION)}, null)) {
+ try (Cursor cursor =
+ mContext.getContentResolver()
+ .query(
+ mChannelsUri,
+ CHANNEL_DATA_SELECTION_ARGS,
+ selection,
+ new String[] {Integer.toString(VERSION)},
+ null)) {
if (cursor != null && cursor.moveToFirst()) {
// The stored channel data seem outdated. Delete them all.
clearChannels();
}
}
} else {
- try (Cursor cursor = mContext.getContentResolver().query(mChannelsUri,
- new String[] { TvContract.Channels.COLUMN_INTERNAL_PROVIDER_FLAG1 },
- null, null, null)) {
+ try (Cursor cursor =
+ mContext.getContentResolver()
+ .query(
+ mChannelsUri,
+ new String[] {
+ TvContract.Channels.COLUMN_INTERNAL_PROVIDER_FLAG1
+ },
+ null,
+ null,
+ null)) {
if (cursor != null) {
while (cursor.moveToNext()) {
int version = cursor.getInt(0);
@@ -643,18 +693,16 @@ public class ChannelDataManager implements Handler.Callback {
return channelId;
}
mHandler.sendEmptyMessage(MSG_BUILD_CHANNEL_MAP);
- try (Cursor cursor = mContext.getContentResolver().query(mChannelsUri,
- CHANNEL_DATA_SELECTION_ARGS, null, null, null)) {
+ try (Cursor cursor =
+ mContext.getContentResolver()
+ .query(mChannelsUri, CHANNEL_DATA_SELECTION_ARGS, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
do {
- channelId = cursor.getLong(0);
- byte[] providerData = cursor.getBlob(1);
- TunerChannel tunerChannel = TunerChannel.parseFrom(providerData);
+ TunerChannel tunerChannel = TunerChannel.fromCursor(cursor);
if (tunerChannel != null && tunerChannel.compareTo(channel) == 0) {
- channel.setChannelId(channelId);
- mTunerChannelIdMap.put(channel, channelId);
- mTunerChannelMap.put(channelId, channel);
- return channelId;
+ mTunerChannelIdMap.put(channel, tunerChannel.getChannelId());
+ mTunerChannelMap.put(tunerChannel.getChannelId(), channel);
+ return tunerChannel.getChannelId();
}
} while (cursor.moveToNext());
}
@@ -666,31 +714,46 @@ public class ChannelDataManager implements Handler.Callback {
return getAllProgramsForChannel(channel, null, null);
}
- private List<EitItem> getAllProgramsForChannel(TunerChannel channel, @Nullable Long startTimeMs,
- @Nullable Long endTimeMs) {
+ private List<EitItem> getAllProgramsForChannel(
+ TunerChannel channel, @Nullable Long startTimeMs, @Nullable Long endTimeMs) {
List<EitItem> items = new ArrayList<>();
long channelId = channel.getChannelId();
- Uri programsUri = (startTimeMs == null || endTimeMs == null) ?
- TvContract.buildProgramsUriForChannel(channelId) :
- TvContract.buildProgramsUriForChannel(channelId, startTimeMs, endTimeMs);
- try (Cursor cursor = mContext.getContentResolver().query(programsUri,
- ALL_PROGRAMS_SELECTION_ARGS, null, null, null)) {
+ Uri programsUri =
+ (startTimeMs == null || endTimeMs == null)
+ ? TvContract.buildProgramsUriForChannel(channelId)
+ : TvContract.buildProgramsUriForChannel(channelId, startTimeMs, endTimeMs);
+ try (Cursor cursor =
+ mContext.getContentResolver()
+ .query(programsUri, ALL_PROGRAMS_SELECTION_ARGS, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
do {
long id = cursor.getLong(0);
String titleText = cursor.getString(1);
- long startTime = ConvertUtils.convertUnixEpochToGPSTime(
- cursor.getLong(2) / DateUtils.SECOND_IN_MILLIS);
- long endTime = ConvertUtils.convertUnixEpochToGPSTime(
- cursor.getLong(3) / DateUtils.SECOND_IN_MILLIS);
+ long startTime =
+ ConvertUtils.convertUnixEpochToGPSTime(
+ cursor.getLong(2) / DateUtils.SECOND_IN_MILLIS);
+ long endTime =
+ ConvertUtils.convertUnixEpochToGPSTime(
+ cursor.getLong(3) / DateUtils.SECOND_IN_MILLIS);
int lengthInSecond = (int) (endTime - startTime);
String contentRating = cursor.getString(4);
String broadcastGenre = cursor.getString(5);
String canonicalGenre = cursor.getString(6);
String description = cursor.getString(7);
int eventId = cursor.getInt(8);
- EitItem eitItem = new EitItem(id, eventId, titleText, startTime, lengthInSecond,
- contentRating, null, null, broadcastGenre, canonicalGenre, description);
+ EitItem eitItem =
+ new EitItem(
+ id,
+ eventId,
+ titleText,
+ startTime,
+ lengthInSecond,
+ contentRating,
+ null,
+ null,
+ broadcastGenre,
+ canonicalGenre,
+ description);
items.add(eitItem);
} while (cursor.moveToNext());
}
@@ -700,15 +763,13 @@ public class ChannelDataManager implements Handler.Callback {
private void buildChannelMap() {
ArrayList<TunerChannel> channels = new ArrayList<>();
- try (Cursor cursor = mContext.getContentResolver().query(mChannelsUri,
- CHANNEL_DATA_SELECTION_ARGS, null, null, null)) {
+ try (Cursor cursor =
+ mContext.getContentResolver()
+ .query(mChannelsUri, CHANNEL_DATA_SELECTION_ARGS, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
do {
- long channelId = cursor.getLong(0);
- byte[] data = cursor.getBlob(1);
- TunerChannel channel = TunerChannel.parseFrom(data);
+ TunerChannel channel = TunerChannel.fromCursor(cursor);
if (channel != null) {
- channel.setChannelId(channelId);
channels.add(channel);
}
} while (cursor.moveToNext());
diff --git a/tuner/src/com/android/tv/tuner/tvinput/EventDetector.java b/tuner/src/com/android/tv/tuner/tvinput/EventDetector.java
new file mode 100644
index 00000000..c529c6db
--- /dev/null
+++ b/tuner/src/com/android/tv/tuner/tvinput/EventDetector.java
@@ -0,0 +1,349 @@
+/*
+ * 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.tuner.tvinput;
+
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import com.android.tv.tuner.TunerHal;
+import com.android.tv.tuner.data.PsiData;
+import com.android.tv.tuner.data.PsipData;
+import com.android.tv.tuner.data.TunerChannel;
+import com.android.tv.tuner.data.nano.Track.AtscAudioTrack;
+import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
+import com.android.tv.tuner.ts.TsParser;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Detects channels and programs that are emerged or changed while parsing ATSC PSIP information.
+ */
+public class EventDetector {
+ private static final String TAG = "EventDetector";
+ private static final boolean DEBUG = false;
+ public static final int ALL_PROGRAM_NUMBERS = -1;
+
+ private final TunerHal mTunerHal;
+
+ private TsParser mTsParser;
+ private final Set<Integer> mPidSet = new HashSet<>();
+
+ // To prevent channel duplication
+ private final Set<Integer> mVctProgramNumberSet = new HashSet<>();
+ private final Set<Integer> mSdtProgramNumberSet = new HashSet<>();
+ private final SparseArray<TunerChannel> mChannelMap = new SparseArray<>();
+ private final SparseBooleanArray mVctCaptionTracksFound = new SparseBooleanArray();
+ private final SparseBooleanArray mEitCaptionTracksFound = new SparseBooleanArray();
+ private final List<EventListener> mEventListeners = new ArrayList<>();
+ private int mFrequency;
+ private String mModulation;
+ private int mProgramNumber = ALL_PROGRAM_NUMBERS;
+
+ private final TsParser.TsOutputListener mTsOutputListener =
+ new TsParser.TsOutputListener() {
+ @Override
+ public void onPatDetected(List<PsiData.PatItem> items) {
+ for (PsiData.PatItem i : items) {
+ if (mProgramNumber == ALL_PROGRAM_NUMBERS
+ || mProgramNumber == i.getProgramNo()) {
+ mTunerHal.addPidFilter(i.getPmtPid(), TunerHal.FILTER_TYPE_OTHER);
+ }
+ }
+ }
+
+ @Override
+ public void onEitPidDetected(int pid) {
+ startListening(pid);
+ }
+
+ @Override
+ public void onEitItemParsed(
+ PsipData.VctItem channel, List<PsipData.EitItem> items) {
+ TunerChannel tunerChannel = mChannelMap.get(channel.getProgramNumber());
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "onEitItemParsed tunerChannel:"
+ + tunerChannel
+ + " "
+ + channel.getProgramNumber());
+ }
+ int channelSourceId = channel.getSourceId();
+
+ // Source id 0 is useful for cases where a cable operator wishes to define a
+ // channel for
+ // which no EPG data is currently available.
+ // We don't handle such a case.
+ if (channelSourceId == 0) {
+ return;
+ }
+
+ // If at least a one caption track have been found in EIT items for the given
+ // channel,
+ // we starts to interpret the zero tracks as a clearance of the caption tracks.
+ boolean captionTracksFound = mEitCaptionTracksFound.get(channelSourceId);
+ for (PsipData.EitItem item : items) {
+ if (captionTracksFound) {
+ break;
+ }
+ List<AtscCaptionTrack> captionTracks = item.getCaptionTracks();
+ if (captionTracks != null && !captionTracks.isEmpty()) {
+ captionTracksFound = true;
+ }
+ }
+ mEitCaptionTracksFound.put(channelSourceId, captionTracksFound);
+ if (captionTracksFound) {
+ for (PsipData.EitItem item : items) {
+ item.setHasCaptionTrack();
+ }
+ }
+ if (tunerChannel != null && !mEventListeners.isEmpty()) {
+ for (EventListener eventListener : mEventListeners) {
+ eventListener.onEventDetected(tunerChannel, items);
+ }
+ }
+ }
+
+ @Override
+ public void onEttPidDetected(int pid) {
+ startListening(pid);
+ }
+
+ @Override
+ public void onAllVctItemsParsed() {
+ if (!mEventListeners.isEmpty()) {
+ for (EventListener eventListener : mEventListeners) {
+ eventListener.onChannelScanDone();
+ }
+ }
+ }
+
+ @Override
+ public void onVctItemParsed(
+ PsipData.VctItem channel, List<PsiData.PmtItem> pmtItems) {
+ if (DEBUG) {
+ Log.d(TAG, "onVctItemParsed VCT " + channel);
+ Log.d(TAG, " PMT " + pmtItems);
+ }
+
+ // Merges the audio and caption tracks located in PMT items into the tracks of
+ // the given
+ // tuner channel.
+ TunerChannel tunerChannel = new TunerChannel(channel, pmtItems);
+ List<AtscAudioTrack> audioTracks = new ArrayList<>();
+ List<AtscCaptionTrack> captionTracks = new ArrayList<>();
+ for (PsiData.PmtItem pmtItem : pmtItems) {
+ if (pmtItem.getAudioTracks() != null) {
+ audioTracks.addAll(pmtItem.getAudioTracks());
+ }
+ if (pmtItem.getCaptionTracks() != null) {
+ captionTracks.addAll(pmtItem.getCaptionTracks());
+ }
+ }
+ int channelProgramNumber = channel.getProgramNumber();
+
+ // If at least a one caption track have been found in VCT items for the given
+ // channel,
+ // we starts to interpret the zero tracks as a clearance of the caption tracks.
+ boolean captionTracksFound =
+ mVctCaptionTracksFound.get(channelProgramNumber)
+ || !captionTracks.isEmpty();
+ mVctCaptionTracksFound.put(channelProgramNumber, captionTracksFound);
+ if (captionTracksFound) {
+ tunerChannel.setHasCaptionTrack();
+ }
+ tunerChannel.setAudioTracks(audioTracks);
+ tunerChannel.setCaptionTracks(captionTracks);
+ tunerChannel.setFrequency(mFrequency);
+ tunerChannel.setModulation(mModulation);
+ mChannelMap.put(tunerChannel.getProgramNumber(), tunerChannel);
+ boolean found = mVctProgramNumberSet.contains(channelProgramNumber);
+ if (!found) {
+ mVctProgramNumberSet.add(channelProgramNumber);
+ }
+ if (!mEventListeners.isEmpty()) {
+ for (EventListener eventListener : mEventListeners) {
+ eventListener.onChannelDetected(tunerChannel, !found);
+ }
+ }
+ }
+
+ @Override
+ public void onSdtItemParsed(
+ PsipData.SdtItem channel, List<PsiData.PmtItem> pmtItems) {
+ if (DEBUG) {
+ Log.d(TAG, "onSdtItemParsed SDT " + channel);
+ Log.d(TAG, " PMT " + pmtItems);
+ }
+
+ // Merges the audio and caption tracks located in PMT items into the tracks of
+ // the given
+ // tuner channel.
+ TunerChannel tunerChannel = new TunerChannel(channel, pmtItems);
+ List<AtscAudioTrack> audioTracks = new ArrayList<>();
+ List<AtscCaptionTrack> captionTracks = new ArrayList<>();
+ for (PsiData.PmtItem pmtItem : pmtItems) {
+ if (pmtItem.getAudioTracks() != null) {
+ audioTracks.addAll(pmtItem.getAudioTracks());
+ }
+ if (pmtItem.getCaptionTracks() != null) {
+ captionTracks.addAll(pmtItem.getCaptionTracks());
+ }
+ }
+ int channelProgramNumber = channel.getServiceId();
+ tunerChannel.setAudioTracks(audioTracks);
+ tunerChannel.setCaptionTracks(captionTracks);
+ tunerChannel.setFrequency(mFrequency);
+ tunerChannel.setModulation(mModulation);
+ mChannelMap.put(tunerChannel.getProgramNumber(), tunerChannel);
+ boolean found = mSdtProgramNumberSet.contains(channelProgramNumber);
+ if (!found) {
+ mSdtProgramNumberSet.add(channelProgramNumber);
+ }
+ if (!mEventListeners.isEmpty()) {
+ for (EventListener eventListener : mEventListeners) {
+ eventListener.onChannelDetected(tunerChannel, !found);
+ }
+ }
+ }
+ };
+
+ /** Listener for detecting ATSC TV channels and receiving EPG data. */
+ public interface EventListener {
+
+ /**
+ * Fired when new information of an ATSC TV channel arrived.
+ *
+ * @param channel an ATSC TV channel
+ * @param channelArrivedAtFirstTime tells whether this channel arrived at first time
+ */
+ void onChannelDetected(TunerChannel channel, boolean channelArrivedAtFirstTime);
+
+ /**
+ * Fired when new program events of an ATSC TV channel arrived.
+ *
+ * @param channel an ATSC TV channel
+ * @param items a list of EIT items that were received
+ */
+ void onEventDetected(TunerChannel channel, List<PsipData.EitItem> items);
+
+ /**
+ * Fired when information of all detectable ATSC TV channels in current frequency arrived.
+ */
+ void onChannelScanDone();
+ }
+
+ /**
+ * Creates a detector for ATSC TV channles and program information.
+ *
+ * @param usbTunerInteface {@link TunerHal}
+ */
+ public EventDetector(TunerHal usbTunerInteface) {
+ mTunerHal = usbTunerInteface;
+ }
+
+ private void reset() {
+ // TODO: Use TsParser.reset()
+ int deliverySystemType = mTunerHal.getDeliverySystemType();
+ mTsParser =
+ new TsParser(
+ mTsOutputListener,
+ TunerHal.isDvbDeliverySystem(mTunerHal.getDeliverySystemType()));
+ mPidSet.clear();
+ mVctProgramNumberSet.clear();
+ mSdtProgramNumberSet.clear();
+ mVctCaptionTracksFound.clear();
+ mEitCaptionTracksFound.clear();
+ mChannelMap.clear();
+ }
+
+ /**
+ * Starts detecting channel and program information.
+ *
+ * @param frequency The frequency to listen to.
+ * @param modulation The modulation type.
+ * @param programNumber The program number if this is for handling tune request. For scanning
+ * purpose, supply {@link #ALL_PROGRAM_NUMBERS}.
+ */
+ public void startDetecting(int frequency, String modulation, int programNumber) {
+ reset();
+ mFrequency = frequency;
+ mModulation = modulation;
+ mProgramNumber = programNumber;
+ }
+
+ private void startListening(int pid) {
+ if (mPidSet.contains(pid)) {
+ return;
+ }
+ mPidSet.add(pid);
+ mTunerHal.addPidFilter(pid, TunerHal.FILTER_TYPE_OTHER);
+ }
+
+ /**
+ * Feeds ATSC TS stream to detect channel and program information.
+ *
+ * @param data buffer for ATSC TS stream
+ * @param startOffset the offset where buffer starts
+ * @param length The length of available data
+ */
+ public void feedTSStream(byte[] data, int startOffset, int length) {
+ if (mPidSet.isEmpty()) {
+ startListening(TsParser.ATSC_SI_BASE_PID);
+ }
+ if (mTsParser != null) {
+ mTsParser.feedTSData(data, startOffset, length);
+ }
+ }
+
+ /**
+ * Retrieves the channel information regardless of being well-formed.
+ *
+ * @return {@link List} of {@link TunerChannel}
+ */
+ public List<TunerChannel> getMalFormedChannels() {
+ return mTsParser.getMalFormedChannels();
+ }
+
+ /**
+ * Registers an EventListener.
+ *
+ * @param eventListener the listener to be registered
+ */
+ public void registerListener(EventListener eventListener) {
+ if (mTsParser != null) {
+ // Resets the version numbers so that the new listener can receive the EIT items.
+ // Otherwise, each EIT session is handled only once unless there is a new version.
+ mTsParser.resetDataVersions();
+ }
+ mEventListeners.add(eventListener);
+ }
+
+ /**
+ * Unregisters an EventListener.
+ *
+ * @param eventListener the listener to be unregistered
+ */
+ public void unregisterListener(EventListener eventListener) {
+ boolean removed = mEventListeners.remove(eventListener);
+ if (!removed && DEBUG) {
+ Log.d(TAG, "Cannot unregister a non-registered listener!");
+ }
+ }
+}
diff --git a/tuner/src/com/android/tv/tuner/tvinput/FileSourceEventDetector.java b/tuner/src/com/android/tv/tuner/tvinput/FileSourceEventDetector.java
new file mode 100644
index 00000000..ab05aa02
--- /dev/null
+++ b/tuner/src/com/android/tv/tuner/tvinput/FileSourceEventDetector.java
@@ -0,0 +1,259 @@
+/*
+ * 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.tuner.tvinput;
+
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import com.android.tv.tuner.data.PsiData.PatItem;
+import com.android.tv.tuner.data.PsiData.PmtItem;
+import com.android.tv.tuner.data.PsipData.EitItem;
+import com.android.tv.tuner.data.PsipData.SdtItem;
+import com.android.tv.tuner.data.PsipData.VctItem;
+import com.android.tv.tuner.data.TunerChannel;
+import com.android.tv.tuner.data.nano.Track.AtscAudioTrack;
+import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
+import com.android.tv.tuner.source.FileTsStreamer;
+import com.android.tv.tuner.ts.TsParser;
+import com.android.tv.tuner.tvinput.EventDetector.EventListener;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * PSIP event detector for a file source.
+ *
+ * <p>Uses {@link TsParser} to analyze input MPEG-2 transport stream, detects and reports various
+ * PSIP-related events via {@link TsParser.TsOutputListener}.
+ */
+public class FileSourceEventDetector {
+ private static final String TAG = "FileSourceEventDetector";
+ private static final boolean DEBUG = false;
+ public static final int ALL_PROGRAM_NUMBERS = 0;
+
+ private TsParser mTsParser;
+ private final Set<Integer> mVctProgramNumberSet = new HashSet<>();
+ private final Set<Integer> mSdtProgramNumberSet = new HashSet<>();
+ private final SparseArray<TunerChannel> mChannelMap = new SparseArray<>();
+ private final SparseBooleanArray mVctCaptionTracksFound = new SparseBooleanArray();
+ private final SparseBooleanArray mEitCaptionTracksFound = new SparseBooleanArray();
+ private final EventListener mEventListener;
+ private final boolean mEnableDvbSignal;
+ private FileTsStreamer.StreamProvider mStreamProvider;
+ private int mProgramNumber = ALL_PROGRAM_NUMBERS;
+
+ public FileSourceEventDetector(EventDetector.EventListener listener, boolean enableDvbSignal) {
+ mEventListener = listener;
+ mEnableDvbSignal = enableDvbSignal;
+ }
+
+ /**
+ * Starts detecting channel and program information.
+ *
+ * @param provider MPEG-2 transport stream source.
+ * @param programNumber The program number if this is for handling tune request. For scanning
+ * purpose, supply {@link #ALL_PROGRAM_NUMBERS}.
+ */
+ public void start(FileTsStreamer.StreamProvider provider, int programNumber) {
+ mStreamProvider = provider;
+ mProgramNumber = programNumber;
+ reset();
+ }
+
+ private void reset() {
+ mTsParser = new TsParser(mTsOutputListener, mEnableDvbSignal); // TODO: Use TsParser.reset()
+ mStreamProvider.clearPidFilter();
+ mVctProgramNumberSet.clear();
+ mSdtProgramNumberSet.clear();
+ mVctCaptionTracksFound.clear();
+ mEitCaptionTracksFound.clear();
+ mChannelMap.clear();
+ }
+
+ public void feedTSStream(byte[] data, int startOffset, int length) {
+ if (mStreamProvider.isFilterEmpty()) {
+ startListening(TsParser.ATSC_SI_BASE_PID);
+ startListening(TsParser.PAT_PID);
+ }
+ if (mTsParser != null) {
+ mTsParser.feedTSData(data, startOffset, length);
+ }
+ }
+
+ private void startListening(int pid) {
+ if (mStreamProvider.isInFilter(pid)) {
+ return;
+ }
+ mStreamProvider.addPidFilter(pid);
+ }
+
+ private final TsParser.TsOutputListener mTsOutputListener =
+ new TsParser.TsOutputListener() {
+ @Override
+ public void onPatDetected(List<PatItem> items) {
+ for (PatItem i : items) {
+ if (mProgramNumber == ALL_PROGRAM_NUMBERS
+ || mProgramNumber == i.getProgramNo()) {
+ mStreamProvider.addPidFilter(i.getPmtPid());
+ }
+ }
+ }
+
+ @Override
+ public void onEitPidDetected(int pid) {
+ startListening(pid);
+ }
+
+ @Override
+ public void onEitItemParsed(VctItem channel, List<EitItem> items) {
+ TunerChannel tunerChannel = mChannelMap.get(channel.getProgramNumber());
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "onEitItemParsed tunerChannel:"
+ + tunerChannel
+ + " "
+ + channel.getProgramNumber());
+ }
+ int channelSourceId = channel.getSourceId();
+
+ // Source id 0 is useful for cases where a cable operator wishes to define a
+ // channel for
+ // which no EPG data is currently available.
+ // We don't handle such a case.
+ if (channelSourceId == 0) {
+ return;
+ }
+
+ // If at least a one caption track have been found in EIT items for the given
+ // channel,
+ // we starts to interpret the zero tracks as a clearance of the caption tracks.
+ boolean captionTracksFound = mEitCaptionTracksFound.get(channelSourceId);
+ for (EitItem item : items) {
+ if (captionTracksFound) {
+ break;
+ }
+ List<AtscCaptionTrack> captionTracks = item.getCaptionTracks();
+ if (captionTracks != null && !captionTracks.isEmpty()) {
+ captionTracksFound = true;
+ }
+ }
+ mEitCaptionTracksFound.put(channelSourceId, captionTracksFound);
+ if (captionTracksFound) {
+ for (EitItem item : items) {
+ item.setHasCaptionTrack();
+ }
+ }
+ if (tunerChannel != null && mEventListener != null) {
+ mEventListener.onEventDetected(tunerChannel, items);
+ }
+ }
+
+ @Override
+ public void onEttPidDetected(int pid) {
+ startListening(pid);
+ }
+
+ @Override
+ public void onAllVctItemsParsed() {
+ // do nothing.
+ }
+
+ @Override
+ public void onVctItemParsed(VctItem channel, List<PmtItem> pmtItems) {
+ if (DEBUG) {
+ Log.d(TAG, "onVctItemParsed VCT " + channel);
+ Log.d(TAG, " PMT " + pmtItems);
+ }
+
+ // Merges the audio and caption tracks located in PMT items into the tracks of
+ // the given
+ // tuner channel.
+ TunerChannel tunerChannel = TunerChannel.forFile(channel, pmtItems);
+ List<AtscAudioTrack> audioTracks = new ArrayList<>();
+ List<AtscCaptionTrack> captionTracks = new ArrayList<>();
+ for (PmtItem pmtItem : pmtItems) {
+ if (pmtItem.getAudioTracks() != null) {
+ audioTracks.addAll(pmtItem.getAudioTracks());
+ }
+ if (pmtItem.getCaptionTracks() != null) {
+ captionTracks.addAll(pmtItem.getCaptionTracks());
+ }
+ }
+ int channelProgramNumber = channel.getProgramNumber();
+
+ // If at least a one caption track have been found in VCT items for the given
+ // channel,
+ // we starts to interpret the zero tracks as a clearance of the caption tracks.
+ boolean captionTracksFound =
+ mVctCaptionTracksFound.get(channelProgramNumber)
+ || !captionTracks.isEmpty();
+ mVctCaptionTracksFound.put(channelProgramNumber, captionTracksFound);
+ if (captionTracksFound) {
+ tunerChannel.setHasCaptionTrack();
+ }
+ tunerChannel.setFilepath(mStreamProvider.getFilepath());
+ tunerChannel.setAudioTracks(audioTracks);
+ tunerChannel.setCaptionTracks(captionTracks);
+
+ mChannelMap.put(tunerChannel.getProgramNumber(), tunerChannel);
+ boolean found = mVctProgramNumberSet.contains(channelProgramNumber);
+ if (!found) {
+ mVctProgramNumberSet.add(channelProgramNumber);
+ }
+ if (mEventListener != null) {
+ mEventListener.onChannelDetected(tunerChannel, !found);
+ }
+ }
+
+ @Override
+ public void onSdtItemParsed(SdtItem channel, List<PmtItem> pmtItems) {
+ if (DEBUG) {
+ Log.d(TAG, "onSdtItemParsed SDT " + channel);
+ Log.d(TAG, " PMT " + pmtItems);
+ }
+
+ // Merges the audio and caption tracks located in PMT items into the tracks of
+ // the given
+ // tuner channel.
+ TunerChannel tunerChannel = TunerChannel.forDvbFile(channel, pmtItems);
+ List<AtscAudioTrack> audioTracks = new ArrayList<>();
+ List<AtscCaptionTrack> captionTracks = new ArrayList<>();
+ for (PmtItem pmtItem : pmtItems) {
+ if (pmtItem.getAudioTracks() != null) {
+ audioTracks.addAll(pmtItem.getAudioTracks());
+ }
+ if (pmtItem.getCaptionTracks() != null) {
+ captionTracks.addAll(pmtItem.getCaptionTracks());
+ }
+ }
+ int channelProgramNumber = channel.getServiceId();
+ tunerChannel.setFilepath(mStreamProvider.getFilepath());
+ tunerChannel.setAudioTracks(audioTracks);
+ tunerChannel.setCaptionTracks(captionTracks);
+ mChannelMap.put(tunerChannel.getProgramNumber(), tunerChannel);
+ boolean found = mSdtProgramNumberSet.contains(channelProgramNumber);
+ if (!found) {
+ mSdtProgramNumberSet.add(channelProgramNumber);
+ }
+ if (mEventListener != null) {
+ mEventListener.onChannelDetected(tunerChannel, !found);
+ }
+ }
+ };
+}
diff --git a/src/com/android/tv/tuner/tvinput/PlaybackBufferListener.java b/tuner/src/com/android/tv/tuner/tvinput/PlaybackBufferListener.java
index 3908fe6c..1628bcfb 100644
--- a/src/com/android/tv/tuner/tvinput/PlaybackBufferListener.java
+++ b/tuner/src/com/android/tv/tuner/tvinput/PlaybackBufferListener.java
@@ -16,9 +16,7 @@
package com.android.tv.tuner.tvinput;
-/**
- * The listener for buffer events occurred during playback.
- */
+/** The listener for buffer events occurred during playback. */
public interface PlaybackBufferListener {
/**
@@ -35,8 +33,6 @@ public interface PlaybackBufferListener {
*/
void onBufferStateChanged(boolean available);
- /**
- * Invoked when the disk speed is too slow to write the buffers.
- */
+ /** Invoked when the disk speed is too slow to write the buffers. */
void onDiskTooSlow();
}
diff --git a/src/com/android/tv/tuner/tvinput/TunerDebug.java b/tuner/src/com/android/tv/tuner/tvinput/TunerDebug.java
index 2ddc946a..1df0b5c3 100644
--- a/src/com/android/tv/tuner/tvinput/TunerDebug.java
+++ b/tuner/src/com/android/tv/tuner/tvinput/TunerDebug.java
@@ -19,9 +19,7 @@ package com.android.tv.tuner.tvinput;
import android.os.SystemClock;
import android.util.Log;
-/**
- * A class to maintain various debugging information.
- */
+/** A class to maintain various debugging information. */
public class TunerDebug {
private static final String TAG = "TunerDebug";
public static final boolean ENABLED = false;
@@ -117,14 +115,13 @@ public class TunerDebug {
long duration = currentTime - sTunerDebug.mLastCheckTimestampMs;
if (duration != 0) {
sTunerDebug.mAudioPositionUsRate =
- (sTunerDebug.mAudioPositionUs - sTunerDebug.mLastAudioPositionUs) * 1000
- / duration;
+ (sTunerDebug.mAudioPositionUs - sTunerDebug.mLastAudioPositionUs)
+ * 1000
+ / duration;
sTunerDebug.mAudioPtsUsRate =
- (sTunerDebug.mAudioPtsUs - sTunerDebug.mLastAudioPtsUs) * 1000
- / duration;
+ (sTunerDebug.mAudioPtsUs - sTunerDebug.mLastAudioPtsUs) * 1000 / duration;
sTunerDebug.mVideoPtsUsRate =
- (sTunerDebug.mVideoPtsUs - sTunerDebug.mLastVideoPtsUs) * 1000
- / duration;
+ (sTunerDebug.mVideoPtsUs - sTunerDebug.mLastVideoPtsUs) * 1000 / duration;
}
sTunerDebug.mLastAudioPositionUs = sTunerDebug.mAudioPositionUs;
diff --git a/src/com/android/tv/tuner/tvinput/TunerRecordingSession.java b/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSession.java
index acdd149f..a1f0c773 100644
--- a/src/com/android/tv/tuner/tvinput/TunerRecordingSession.java
+++ b/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSession.java
@@ -17,7 +17,6 @@
package com.android.tv.tuner.tvinput;
import android.content.Context;
-import android.media.tv.TvInputManager;
import android.media.tv.TvInputService;
import android.net.Uri;
import android.support.annotation.MainThread;
@@ -25,20 +24,18 @@ import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import android.util.Log;
-/**
- * Processes DVR recordings, and deletes the previously recorded contents.
- */
+/** Processes DVR recordings, and deletes the previously recorded contents. */
public class TunerRecordingSession extends TvInputService.RecordingSession {
private static final String TAG = "TunerRecordingSession";
private static final boolean DEBUG = false;
private final TunerRecordingSessionWorker mSessionWorker;
- public TunerRecordingSession(Context context, String inputId,
- ChannelDataManager channelDataManager) {
+ public TunerRecordingSession(
+ Context context, String inputId, ChannelDataManager channelDataManager) {
super(context);
- mSessionWorker = new TunerRecordingSessionWorker(context, inputId, channelDataManager,
- this);
+ mSessionWorker =
+ new TunerRecordingSessionWorker(context, inputId, channelDataManager, this);
}
// RecordingSession
diff --git a/src/com/android/tv/tuner/tvinput/TunerRecordingSessionWorker.java b/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionWorker.java
index 34013bf1..b2001225 100644
--- a/src/com/android/tv/tuner/tvinput/TunerRecordingSessionWorker.java
+++ b/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionWorker.java
@@ -22,6 +22,7 @@ import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.media.tv.TvContract;
+import android.media.tv.TvContract.RecordedPrograms;
import android.media.tv.TvInputManager;
import android.net.Uri;
import android.os.AsyncTask;
@@ -31,15 +32,13 @@ import android.os.Message;
import android.support.annotation.IntDef;
import android.support.annotation.MainThread;
import android.support.annotation.Nullable;
+import android.support.media.tv.Program;
import android.util.Log;
-
import android.util.Pair;
-import com.google.android.exoplayer.C;
-import com.android.tv.TvApplication;
-import com.android.tv.common.SoftPreconditions;
+import com.android.tv.common.BaseApplication;
import com.android.tv.common.recording.RecordingCapability;
-import com.android.tv.dvr.DvrStorageStatusManager;
-import com.android.tv.dvr.data.RecordedProgram;
+import com.android.tv.common.recording.RecordingStorageStatusManager;
+import com.android.tv.common.util.CommonUtils;
import com.android.tv.tuner.DvbDeviceAccessor;
import com.android.tv.tuner.data.PsipData;
import com.android.tv.tuner.data.PsipData.EitItem;
@@ -51,8 +50,7 @@ import com.android.tv.tuner.exoplayer.buffer.BufferManager;
import com.android.tv.tuner.exoplayer.buffer.DvrStorageManager;
import com.android.tv.tuner.source.TsDataSource;
import com.android.tv.tuner.source.TsDataSourceManager;
-import com.android.tv.util.Utils;
-
+import com.google.android.exoplayer.C;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Retention;
@@ -63,18 +61,21 @@ import java.util.Locale;
import java.util.Random;
import java.util.concurrent.TimeUnit;
-/**
- * Implements a DVR feature.
- */
-public class TunerRecordingSessionWorker implements PlaybackBufferListener,
- EventDetector.EventListener, SampleExtractor.OnCompletionListener,
- Handler.Callback {
+/** Implements a DVR feature. */
+public class TunerRecordingSessionWorker
+ implements PlaybackBufferListener,
+ EventDetector.EventListener,
+ SampleExtractor.OnCompletionListener,
+ Handler.Callback {
private static final String TAG = "TunerRecordingSessionW";
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 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 long TUNING_RETRY_INTERVAL_MS = TimeUnit.SECONDS.toMillis(4);
private static final long STORAGE_MONITOR_INTERVAL_MS = TimeUnit.SECONDS.toMillis(4);
private static final long MIN_PARTIAL_RECORDING_DURATION_MS = TimeUnit.SECONDS.toMillis(10);
@@ -88,13 +89,29 @@ public class TunerRecordingSessionWorker implements PlaybackBufferListener,
private static final int MSG_UPDATE_CC_INFO = 7;
private final RecordingCapability mCapabilities;
- public RecordingCapability getCapabilities() {
- return mCapabilities;
- }
+ private static final String[] PROGRAM_PROJECTION = {
+ TvContract.Programs.COLUMN_CHANNEL_ID,
+ TvContract.Programs.COLUMN_TITLE,
+ TvContract.Programs.COLUMN_SEASON_TITLE,
+ TvContract.Programs.COLUMN_EPISODE_TITLE,
+ TvContract.Programs.COLUMN_SEASON_DISPLAY_NUMBER,
+ TvContract.Programs.COLUMN_EPISODE_DISPLAY_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,
+ TvContract.Programs.COLUMN_INTERNAL_PROVIDER_DATA
+ };
@IntDef({STATE_IDLE, STATE_TUNING, STATE_TUNED, STATE_RECORDING})
@Retention(RetentionPolicy.SOURCE)
public @interface DvrSessionState {}
+
private static final int STATE_IDLE = 1;
private static final int STATE_TUNING = 2;
private static final int STATE_TUNED = 3;
@@ -105,7 +122,7 @@ public class TunerRecordingSessionWorker implements PlaybackBufferListener,
private final Context mContext;
private final ChannelDataManager mChannelDataManager;
- private final DvrStorageStatusManager mDvrStorageStatusManager;
+ private final RecordingStorageStatusManager mRecordingStorageStatusManager;
private final Handler mHandler;
private final TsDataSourceManager mSourceManager;
private final Random mRandom = new Random();
@@ -126,15 +143,18 @@ public class TunerRecordingSessionWorker implements PlaybackBufferListener,
private List<AtscCaptionTrack> mCaptionTracks;
private DvrStorageManager mDvrStorageManager;
- public TunerRecordingSessionWorker(Context context, String inputId,
- ChannelDataManager dataManager, TunerRecordingSession session) {
+ 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);
- mDvrStorageStatusManager =
- TvApplication.getSingletons(context).getDvrStorageStatusManager();
+ mRecordingStorageStatusManager =
+ BaseApplication.getSingletons(context).getRecordingStorageStatusManager();
mChannelDataManager = dataManager;
mChannelDataManager.checkDataVersion(context);
mSourceManager = TsDataSourceManager.createSourceManager(true);
@@ -146,13 +166,13 @@ public class TunerRecordingSessionWorker implements PlaybackBufferListener,
// PlaybackBufferListener
@Override
- public void onBufferStartTimeChanged(long startTimeMs) { }
+ public void onBufferStartTimeChanged(long startTimeMs) {}
@Override
- public void onBufferStateChanged(boolean available) { }
+ public void onBufferStateChanged(boolean available) {}
@Override
- public void onDiskTooSlow() { }
+ public void onDiskTooSlow() {}
// EventDetector.EventListener
@Override
@@ -184,34 +204,26 @@ public class TunerRecordingSessionWorker implements PlaybackBufferListener,
reset();
}
- /**
- * Tunes to {@code channelUri}.
- */
+ /** Tunes to {@code channelUri}. */
@MainThread
public void tune(Uri channelUri) {
mHandler.removeCallbacksAndMessages(null);
mHandler.obtainMessage(MSG_TUNE, 0, 0, channelUri).sendToTarget();
}
- /**
- * Starts recording.
- */
+ /** Starts recording. */
@MainThread
public void startRecording(@Nullable Uri programUri) {
mHandler.obtainMessage(MSG_START_RECORDING, programUri).sendToTarget();
}
- /**
- * Stops recording.
- */
+ /** Stops recording. */
@MainThread
public void stopRecording() {
mHandler.sendEmptyMessage(MSG_STOP_RECORDING);
}
- /**
- * Releases all resources.
- */
+ /** Releases all resources. */
@MainThread
public void release() {
mHandler.removeCallbacksAndMessages(null);
@@ -221,95 +233,103 @@ public class TunerRecordingSessionWorker implements PlaybackBufferListener,
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
- case MSG_TUNE: {
- Uri channelUri = (Uri) msg.obj;
- int retryCount = msg.arg1;
- if (DEBUG) Log.d(TAG, "Tune to " + channelUri);
- if (doTune(channelUri)) {
- if (mSessionState == STATE_TUNED) {
- mSession.onTuned(channelUri);
- } else {
- Log.w(TAG, "Tuner stream cannot be created due to resource shortage.");
- if (retryCount < MAX_TUNING_RETRY) {
- Message tuneMsg =
- mHandler.obtainMessage(MSG_TUNE, retryCount + 1, 0, channelUri);
- mHandler.sendMessageDelayed(tuneMsg, TUNING_RETRY_INTERVAL_MS);
+ case MSG_TUNE:
+ {
+ Uri channelUri = (Uri) msg.obj;
+ int retryCount = msg.arg1;
+ if (DEBUG) Log.d(TAG, "Tune to " + channelUri);
+ if (doTune(channelUri)) {
+ if (mSessionState == STATE_TUNED) {
+ mSession.onTuned(channelUri);
} else {
- mSession.onError(TvInputManager.RECORDING_ERROR_RESOURCE_BUSY);
- reset();
+ Log.w(TAG, "Tuner stream cannot be created due to resource shortage.");
+ if (retryCount < MAX_TUNING_RETRY) {
+ Message tuneMsg =
+ mHandler.obtainMessage(
+ MSG_TUNE, retryCount + 1, 0, channelUri);
+ mHandler.sendMessageDelayed(tuneMsg, TUNING_RETRY_INTERVAL_MS);
+ } else {
+ mSession.onError(TvInputManager.RECORDING_ERROR_RESOURCE_BUSY);
+ reset();
+ }
}
}
- }
- return true;
- }
- case MSG_START_RECORDING: {
- if (DEBUG) Log.d(TAG, "Start recording");
- if (!doStartRecording((Uri) msg.obj)) {
- reset();
- }
- return true;
- }
- case MSG_PREPARE_RECODER: {
- if (DEBUG) Log.d(TAG, "Preparing recorder");
- if (!mRecorderRunning) {
return true;
}
- try {
- if (!mRecorder.prepare()) {
- mHandler.sendEmptyMessageDelayed(MSG_PREPARE_RECODER,
- PREPARE_RECORDER_POLL_MS);
+ case MSG_START_RECORDING:
+ {
+ if (DEBUG) Log.d(TAG, "Start recording");
+ if (!doStartRecording((Uri) msg.obj)) {
+ reset();
}
- } catch (IOException e) {
- Log.w(TAG, "Failed to start recording. Couldn't prepare an extractor");
- mSession.onError(TvInputManager.RECORDING_ERROR_UNKNOWN);
- reset();
- }
- return true;
- }
- case MSG_STOP_RECORDING: {
- if (DEBUG) Log.d(TAG, "Stop recording");
- if (mSessionState != STATE_RECORDING) {
- mSession.onError(TvInputManager.RECORDING_ERROR_UNKNOWN);
- reset();
return true;
}
- if (mRecorderRunning) {
- stopRecorder();
- }
- return true;
- }
- case MSG_MONITOR_STORAGE_STATUS: {
- if (mSessionState != STATE_RECORDING) {
+ case MSG_PREPARE_RECODER:
+ {
+ if (DEBUG) Log.d(TAG, "Preparing recorder");
+ if (!mRecorderRunning) {
+ return true;
+ }
+ try {
+ if (!mRecorder.prepare()) {
+ mHandler.sendEmptyMessageDelayed(
+ MSG_PREPARE_RECODER, PREPARE_RECORDER_POLL_MS);
+ }
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to start recording. Couldn't prepare an extractor");
+ mSession.onError(TvInputManager.RECORDING_ERROR_UNKNOWN);
+ reset();
+ }
return true;
}
- if (!mDvrStorageStatusManager.isStorageSufficient()) {
+ case MSG_STOP_RECORDING:
+ {
+ if (DEBUG) Log.d(TAG, "Stop recording");
+ if (mSessionState != STATE_RECORDING) {
+ mSession.onError(TvInputManager.RECORDING_ERROR_UNKNOWN);
+ reset();
+ return true;
+ }
if (mRecorderRunning) {
stopRecorder();
}
- new DeleteRecordingTask().execute(mStorageDir);
- mSession.onError(TvInputManager.RECORDING_ERROR_INSUFFICIENT_SPACE);
+ return true;
+ }
+ case MSG_MONITOR_STORAGE_STATUS:
+ {
+ if (mSessionState != STATE_RECORDING) {
+ return true;
+ }
+ if (!mRecordingStorageStatusManager.isStorageSufficient()) {
+ if (mRecorderRunning) {
+ stopRecorder();
+ }
+ new DeleteRecordingTask().execute(mStorageDir);
+ mSession.onError(TvInputManager.RECORDING_ERROR_INSUFFICIENT_SPACE);
+ reset();
+ } else {
+ mHandler.sendEmptyMessageDelayed(
+ MSG_MONITOR_STORAGE_STATUS, STORAGE_MONITOR_INTERVAL_MS);
+ }
+ return true;
+ }
+ case MSG_RELEASE:
+ {
+ // Since release was requested, current recording will be cancelled
+ // without notification.
reset();
- } else {
- mHandler.sendEmptyMessageDelayed(MSG_MONITOR_STORAGE_STATUS,
- STORAGE_MONITOR_INTERVAL_MS);
+ mSourceManager.release();
+ mHandler.removeCallbacksAndMessages(null);
+ mHandler.getLooper().quitSafely();
+ return true;
+ }
+ case MSG_UPDATE_CC_INFO:
+ {
+ Pair<TunerChannel, List<EitItem>> pair =
+ (Pair<TunerChannel, List<EitItem>>) msg.obj;
+ updateCaptionTracks(pair.first, pair.second);
+ return true;
}
- return true;
- }
- case MSG_RELEASE: {
- // Since release was requested, current recording will be cancelled
- // without notification.
- reset();
- mSourceManager.release();
- mHandler.removeCallbacksAndMessages(null);
- mHandler.getLooper().quitSafely();
- return true;
- }
- case MSG_UPDATE_CC_INFO: {
- Pair<TunerChannel, List<EitItem>> pair =
- (Pair<TunerChannel, List<EitItem>>) msg.obj;
- updateCaptionTracks(pair.first, pair.second);
- return true;
- }
}
return false;
}
@@ -364,7 +384,7 @@ public class TunerRecordingSessionWorker implements PlaybackBufferListener,
Log.w(TAG, "Failed to start recording. Not a recordable channel: " + mChannel);
return false;
}
- if (!mDvrStorageStatusManager.isStorageSufficient()) {
+ if (!mRecordingStorageStatusManager.isStorageSufficient()) {
mSession.onError(TvInputManager.RECORDING_ERROR_INSUFFICIENT_SPACE);
Log.w(TAG, "Tuning failed due to insufficient storage.");
return false;
@@ -385,9 +405,12 @@ public class TunerRecordingSessionWorker implements PlaybackBufferListener,
Log.e(TAG, "Recording session status abnormal");
return false;
}
- mStorageDir = mDvrStorageStatusManager.isStorageSufficient() ?
- new File(mDvrStorageStatusManager.getRecordingRootDataDirectory(),
- getStorageKey()) : null;
+ mStorageDir =
+ mRecordingStorageStatusManager.isStorageSufficient()
+ ? new File(
+ mRecordingStorageStatusManager.getRecordingRootDataDirectory(),
+ getStorageKey())
+ : null;
if (mStorageDir == null) {
mSession.onError(TvInputManager.RECORDING_ERROR_INSUFFICIENT_SPACE);
Log.w(TAG, "Failed to start recording due to insufficient storage.");
@@ -397,16 +420,16 @@ public class TunerRecordingSessionWorker implements PlaybackBufferListener,
mTunerSource.shiftStartPosition(mTunerSource.getBufferedPosition());
mRecordStartTime = System.currentTimeMillis();
mDvrStorageManager = new DvrStorageManager(mStorageDir, true);
- mRecorder = new ExoPlayerSampleExtractor(Uri.EMPTY, mTunerSource,
- new BufferManager(mDvrStorageManager), this, true);
+ mRecorder =
+ new ExoPlayerSampleExtractor(
+ Uri.EMPTY, mTunerSource, new BufferManager(mDvrStorageManager), this, true);
mRecorder.setOnCompletionListener(this, mHandler);
mProgramUri = programUri;
mSessionState = STATE_RECORDING;
mRecorderRunning = true;
mHandler.sendEmptyMessage(MSG_PREPARE_RECODER);
mHandler.removeMessages(MSG_MONITOR_STORAGE_STATUS);
- mHandler.sendEmptyMessageDelayed(MSG_MONITOR_STORAGE_STATUS,
- STORAGE_MONITOR_INTERVAL_MS);
+ mHandler.sendEmptyMessageDelayed(MSG_MONITOR_STORAGE_STATUS, STORAGE_MONITOR_INTERVAL_MS);
return true;
}
@@ -423,20 +446,25 @@ public class TunerRecordingSessionWorker implements PlaybackBufferListener,
}
private void updateCaptionTracks(TunerChannel channel, List<PsipData.EitItem> items) {
- if (mChannel == null || channel == null || mChannel.compareTo(channel) != 0
- || items == null || items.isEmpty()) {
+ if (mChannel == null
+ || channel == null
+ || mChannel.compareTo(channel) != 0
+ || items == null
+ || items.isEmpty()) {
return;
}
PsipData.EitItem currentProgram = getCurrentProgram(items);
- if (currentProgram == null || !currentProgram.hasCaptionTrack()
- || mCurrenProgram != null && mCurrenProgram.compareTo(currentProgram) == 0) {
+ if (currentProgram == null
+ || !currentProgram.hasCaptionTrack()
+ || (mCurrenProgram != null && mCurrenProgram.compareTo(currentProgram) == 0)) {
return;
}
mCurrenProgram = currentProgram;
mCaptionTracks = new ArrayList<>(currentProgram.getCaptionTracks());
if (DEBUG) {
- Log.d(TAG, "updated " + mCaptionTracks.size() + " caption tracks for "
- + currentProgram);
+ Log.d(
+ TAG,
+ "updated " + mCaptionTracks.size() + " caption tracks for " + currentProgram);
}
}
@@ -450,116 +478,6 @@ public class TunerRecordingSessionWorker implements PlaybackBufferListener,
return null;
}
- private static class Program {
- private final long mChannelId;
- private final String mTitle;
- private String mSeriesId;
- private final String mSeasonTitle;
- private final String mEpisodeTitle;
- private final String mSeasonNumber;
- private final String mEpisodeNumber;
- private final String mDescription;
- private final String mPosterArtUri;
- private final String mThumbnailUri;
- private final String mCanonicalGenres;
- private final String mContentRatings;
- private final long mStartTimeUtcMillis;
- private final long mEndTimeUtcMillis;
- private final int mVideoWidth;
- private final int mVideoHeight;
- private final byte[] mInternalProviderData;
-
- private static final String[] PROJECTION = {
- TvContract.Programs.COLUMN_CHANNEL_ID,
- TvContract.Programs.COLUMN_TITLE,
- TvContract.Programs.COLUMN_SEASON_TITLE,
- TvContract.Programs.COLUMN_EPISODE_TITLE,
- TvContract.Programs.COLUMN_SEASON_DISPLAY_NUMBER,
- TvContract.Programs.COLUMN_EPISODE_DISPLAY_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,
- TvContract.Programs.COLUMN_INTERNAL_PROVIDER_DATA
- };
-
- public Program(Cursor cursor) {
- int index = 0;
- mChannelId = cursor.getLong(index++);
- mTitle = cursor.getString(index++);
- mSeasonTitle = cursor.getString(index++);
- mEpisodeTitle = cursor.getString(index++);
- mSeasonNumber = cursor.getString(index++);
- mEpisodeNumber = cursor.getString(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.getInt(index++);
- mVideoHeight = cursor.getInt(index++);
- mInternalProviderData = cursor.getBlob(index++);
- SoftPreconditions.checkArgument(index == PROJECTION.length);
- }
-
- public Program(long channelId) {
- mChannelId = channelId;
- mTitle = "Unknown";
- mSeasonTitle = "";
- mEpisodeTitle = "";
- mSeasonNumber = "";
- mEpisodeNumber = "";
- mDescription = "Unknown";
- mPosterArtUri = null;
- mThumbnailUri = null;
- mCanonicalGenres = null;
- mContentRatings = null;
- mStartTimeUtcMillis = 0;
- mEndTimeUtcMillis = 0;
- mVideoWidth = 0;
- mVideoHeight = 0;
- mInternalProviderData = null;
- }
-
- 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();
- int index = 0;
- values.put(PROJECTION[index++], mChannelId);
- values.put(PROJECTION[index++], mTitle);
- values.put(PROJECTION[index++], mSeasonTitle);
- values.put(PROJECTION[index++], mEpisodeTitle);
- values.put(PROJECTION[index++], mSeasonNumber);
- values.put(PROJECTION[index++], mEpisodeNumber);
- values.put(PROJECTION[index++], mDescription);
- values.put(PROJECTION[index++], mPosterArtUri);
- values.put(PROJECTION[index++], mThumbnailUri);
- values.put(PROJECTION[index++], mCanonicalGenres);
- values.put(PROJECTION[index++], mContentRatings);
- values.put(PROJECTION[index++], mStartTimeUtcMillis);
- values.put(PROJECTION[index++], mEndTimeUtcMillis);
- values.put(PROJECTION[index++], mVideoWidth);
- values.put(PROJECTION[index++], mVideoHeight);
- values.put(PROJECTION[index++], mInternalProviderData);
- SoftPreconditions.checkArgument(index == PROJECTION.length);
- return values;
- }
- }
-
private Program getRecordedProgram() {
ContentResolver resolver = mContext.getContentResolver();
Uri programUri = mProgramUri;
@@ -567,9 +485,9 @@ public class TunerRecordingSessionWorker implements PlaybackBufferListener,
long avg = mRecordStartTime / 2 + mRecordEndTime / 2;
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);
+ try (Cursor c = resolver.query(programUri, PROGRAM_PROJECTION, null, null, SORT_BY_TIME)) {
+ if (c != null && c.moveToNext()) {
+ Program result = Program.fromCursor(c);
if (DEBUG) {
Log.v(TAG, "Finished query for " + this);
}
@@ -578,32 +496,34 @@ public class TunerRecordingSessionWorker implements PlaybackBufferListener,
if (c == null) {
Log.e(TAG, "Unknown query error for " + this);
} else {
- if (DEBUG) Log.d(TAG, "Canceled query for " + this);
+ if (DEBUG) Log.d(TAG, "Can not find program:" + programUri);
}
return null;
}
}
}
- private Uri insertRecordedProgram(Program program, long channelId, String storageUri,
- long totalBytes, long startTime, long endTime) {
- // TODO: Set title even though program is null.
- RecordedProgram recordedProgram = RecordedProgram.builder()
- .setInputId(mInputId)
- .setChannelId(channelId)
- .setDataUri(storageUri)
- .setDurationMillis(endTime - startTime)
- .setDataBytes(totalBytes)
- // startTime and endTime could be overridden by program's start and end value.
- .setStartTimeUtcMillis(startTime)
- .setEndTimeUtcMillis(endTime)
- .build();
- ContentValues values = RecordedProgram.toValues(recordedProgram);
+ private Uri insertRecordedProgram(
+ Program program,
+ long channelId,
+ String storageUri,
+ long totalBytes,
+ long startTime,
+ long endTime) {
+ ContentValues values = new ContentValues();
+ values.put(RecordedPrograms.COLUMN_INPUT_ID, mInputId);
+ values.put(RecordedPrograms.COLUMN_CHANNEL_ID, channelId);
+ values.put(RecordedPrograms.COLUMN_RECORDING_DATA_URI, storageUri);
+ values.put(RecordedPrograms.COLUMN_RECORDING_DURATION_MILLIS, endTime - startTime);
+ values.put(RecordedPrograms.COLUMN_RECORDING_DATA_BYTES, totalBytes);
+ // startTime and endTime could be overridden by program's start and end value.
+ values.put(RecordedPrograms.COLUMN_START_TIME_UTC_MILLIS, startTime);
+ values.put(RecordedPrograms.COLUMN_END_TIME_UTC_MILLIS, endTime);
if (program != null) {
- values.putAll(program.buildValues());
+ values.putAll(program.toContentValues());
}
- return mContext.getContentResolver().insert(TvContract.RecordedPrograms.CONTENT_URI,
- values);
+ return mContext.getContentResolver()
+ .insert(TvContract.RecordedPrograms.CONTENT_URI, values);
}
private void onRecordingResult(boolean success, long lastExtractedPositionUs) {
@@ -616,8 +536,9 @@ public class TunerRecordingSessionWorker implements PlaybackBufferListener,
// In case of recorder not being stopped, because of premature termination of recording.
stopRecorder();
}
- if (!success && lastExtractedPositionUs <
- TimeUnit.MILLISECONDS.toMicros(MIN_PARTIAL_RECORDING_DURATION_MS)) {
+ if (!success
+ && lastExtractedPositionUs
+ < TimeUnit.MILLISECONDS.toMicros(MIN_PARTIAL_RECORDING_DURATION_MS)) {
new DeleteRecordingTask().execute(mStorageDir);
mSession.onError(TvInputManager.RECORDING_ERROR_UNKNOWN);
Log.w(TAG, "Recording failed during recording");
@@ -653,8 +574,8 @@ public class TunerRecordingSessionWorker implements PlaybackBufferListener,
if (files == null || files.length == 0) {
return null;
}
- for(File file : files) {
- Utils.deleteDirOrFile(file);
+ for (File file : files) {
+ CommonUtils.deleteDirOrFile(file);
}
return null;
}
diff --git a/src/com/android/tv/tuner/tvinput/TunerSession.java b/tuner/src/com/android/tv/tuner/tvinput/TunerSession.java
index 44bae908..c9d997f1 100644
--- a/src/com/android/tv/tuner/tvinput/TunerSession.java
+++ b/tuner/src/com/android/tv/tuner/tvinput/TunerSession.java
@@ -35,25 +35,24 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
-
-import com.google.android.exoplayer.audio.AudioCapabilities;
+import com.android.tv.common.CommonPreferences.CommonPreferencesChangedListener;
+import com.android.tv.common.util.SystemPropertiesProxy;
import com.android.tv.tuner.R;
import com.android.tv.tuner.TunerPreferences;
-import com.android.tv.tuner.TunerPreferences.TunerPreferencesChangedListener;
import com.android.tv.tuner.cc.CaptionLayout;
import com.android.tv.tuner.cc.CaptionTrackRenderer;
import com.android.tv.tuner.data.Cea708Data.CaptionEvent;
import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
import com.android.tv.tuner.util.GlobalSettingsUtils;
import com.android.tv.tuner.util.StatusTextUtils;
-import com.android.tv.tuner.util.SystemPropertiesProxy;
+import com.google.android.exoplayer.audio.AudioCapabilities;
/**
- * Provides a tuner TV input session. It handles Overlay UI works. Main tuner input functions
- * are implemented in {@link TunerSessionWorker}.
+ * Provides a tuner TV input session. It handles Overlay UI works. Main tuner input functions are
+ * implemented in {@link TunerSessionWorker}.
*/
-public class TunerSession extends TvInputService.Session implements
- Handler.Callback, TunerPreferencesChangedListener {
+public class TunerSession extends TvInputService.Session
+ implements Handler.Callback, CommonPreferencesChangedListener {
private static final String TAG = "TunerSession";
private static final boolean DEBUG = false;
private static final String USBTUNER_SHOW_DEBUG = "persist.tv.tuner.show_debug";
@@ -87,8 +86,8 @@ public class TunerSession extends TvInputService.Session implements
super(context);
mContext = context;
mUiHandler = new Handler(this);
- LayoutInflater inflater = (LayoutInflater)
- context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ LayoutInflater inflater =
+ (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mOverlayView = inflater.inflate(R.layout.ut_overlay_view, null);
mMessageLayout = (ViewGroup) mOverlayView.findViewById(R.id.message_layout);
mMessageLayout.setVisibility(View.INVISIBLE);
@@ -101,7 +100,7 @@ public class TunerSession extends TvInputService.Session implements
CaptionLayout captionLayout = (CaptionLayout) mOverlayView.findViewById(R.id.caption);
mCaptionTrackRenderer = new CaptionTrackRenderer(captionLayout);
mSessionWorker = new TunerSessionWorker(context, channelDataManager, this);
- TunerPreferences.setTunerPreferencesChangedListener(this);
+ TunerPreferences.setCommonPreferencesChangedListener(this);
}
public boolean isReleased() {
@@ -150,14 +149,13 @@ public class TunerSession extends TvInputService.Session implements
@Override
public void onTimeShiftSeekTo(long timeMs) {
if (DEBUG) Log.d(TAG, "Timeshift seekTo requested position: " + timeMs / 1000);
- mSessionWorker.sendMessage(TunerSessionWorker.MSG_TIMESHIFT_SEEK_TO,
- mPlayPaused ? 1 : 0, 0, timeMs);
+ mSessionWorker.sendMessage(
+ TunerSessionWorker.MSG_TIMESHIFT_SEEK_TO, mPlayPaused ? 1 : 0, 0, timeMs);
}
@Override
public void onTimeShiftSetPlaybackParams(PlaybackParams params) {
- mSessionWorker.sendMessage(
- TunerSessionWorker.MSG_TIMESHIFT_SET_PLAYBACKPARAMS, params);
+ mSessionWorker.sendMessage(TunerSessionWorker.MSG_TIMESHIFT_SET_PLAYBACKPARAMS, params);
}
@Override
@@ -201,8 +199,7 @@ public class TunerSession extends TvInputService.Session implements
@Override
public void onUnblockContent(TvContentRating unblockedRating) {
- mSessionWorker.sendMessage(TunerSessionWorker.MSG_UNBLOCKED_RATING,
- unblockedRating);
+ mSessionWorker.sendMessage(TunerSessionWorker.MSG_UNBLOCKED_RATING, unblockedRating);
}
@Override
@@ -213,23 +210,24 @@ public class TunerSession extends TvInputService.Session implements
mReleased = true;
mSessionWorker.release();
mUiHandler.removeCallbacksAndMessages(null);
- TunerPreferences.setTunerPreferencesChangedListener(null);
+ TunerPreferences.setCommonPreferencesChangedListener(null);
}
- /**
- * Sets {@link AudioCapabilities}.
- */
+ /** Sets {@link AudioCapabilities}. */
public void setAudioCapabilities(AudioCapabilities audioCapabilities) {
- mSessionWorker.sendMessage(TunerSessionWorker.MSG_AUDIO_CAPABILITIES_CHANGED,
- audioCapabilities);
+ mSessionWorker.sendMessage(
+ TunerSessionWorker.MSG_AUDIO_CAPABILITIES_CHANGED, audioCapabilities);
}
@Override
public void notifyVideoAvailable() {
super.notifyVideoAvailable();
if (mTuneStartTimestamp != 0) {
- Log.i(TAG, "[Profiler] Video available in "
- + (SystemClock.elapsedRealtime() - mTuneStartTimestamp) + " ms");
+ Log.i(
+ TAG,
+ "[Profiler] Video available in "
+ + (SystemClock.elapsedRealtime() - mTuneStartTimestamp)
+ + " ms");
mTuneStartTimestamp = 0;
}
}
@@ -258,67 +256,86 @@ public class TunerSession extends TvInputService.Session implements
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
- case MSG_UI_SHOW_MESSAGE: {
- mMessageView.setText((String) msg.obj);
- mMessageLayout.setVisibility(View.VISIBLE);
- return true;
- }
- case MSG_UI_HIDE_MESSAGE: {
- mMessageLayout.setVisibility(View.INVISIBLE);
- return true;
- }
- case MSG_UI_SHOW_AUDIO_UNPLAYABLE: {
- // Showing message of enabling surround sound only when global surround sound
- // setting is "never".
- final int value = GlobalSettingsUtils.getEncodedSurroundOutputSettings(mContext);
- if (value == GlobalSettingsUtils.ENCODED_SURROUND_OUTPUT_NEVER) {
- mAudioStatusView.setText(Html.fromHtml(StatusTextUtils.getAudioWarningInHTML(
- mContext.getString(R.string.ut_surround_sound_disabled))));
- } else {
- mAudioStatusView.setText(Html.fromHtml(StatusTextUtils.getAudioWarningInHTML(
- mContext.getString(R.string.audio_passthrough_not_supported))));
+ case MSG_UI_SHOW_MESSAGE:
+ {
+ mMessageView.setText((String) msg.obj);
+ mMessageLayout.setVisibility(View.VISIBLE);
+ return true;
+ }
+ case MSG_UI_HIDE_MESSAGE:
+ {
+ mMessageLayout.setVisibility(View.INVISIBLE);
+ return true;
+ }
+ case MSG_UI_SHOW_AUDIO_UNPLAYABLE:
+ {
+ // Showing message of enabling surround sound only when global surround sound
+ // setting is "never".
+ final int value =
+ GlobalSettingsUtils.getEncodedSurroundOutputSettings(mContext);
+ if (value == GlobalSettingsUtils.ENCODED_SURROUND_OUTPUT_NEVER) {
+ mAudioStatusView.setText(
+ Html.fromHtml(
+ StatusTextUtils.getAudioWarningInHTML(
+ mContext.getString(
+ R.string.ut_surround_sound_disabled))));
+ } else {
+ mAudioStatusView.setText(
+ Html.fromHtml(
+ StatusTextUtils.getAudioWarningInHTML(
+ mContext.getString(
+ R.string
+ .audio_passthrough_not_supported))));
+ }
+ mAudioStatusView.setVisibility(View.VISIBLE);
+ return true;
+ }
+ case MSG_UI_HIDE_AUDIO_UNPLAYABLE:
+ {
+ mAudioStatusView.setVisibility(View.INVISIBLE);
+ return true;
+ }
+ case MSG_UI_PROCESS_CAPTION_TRACK:
+ {
+ mCaptionTrackRenderer.processCaptionEvent((CaptionEvent) msg.obj);
+ return true;
+ }
+ case MSG_UI_START_CAPTION_TRACK:
+ {
+ mCaptionTrackRenderer.start((AtscCaptionTrack) msg.obj);
+ return true;
+ }
+ case MSG_UI_STOP_CAPTION_TRACK:
+ {
+ mCaptionTrackRenderer.stop();
+ return true;
+ }
+ case MSG_UI_RESET_CAPTION_TRACK:
+ {
+ mCaptionTrackRenderer.reset();
+ return true;
+ }
+ case MSG_UI_CLEAR_CAPTION_RENDERER:
+ {
+ mCaptionTrackRenderer.clear();
+ return true;
+ }
+ case MSG_UI_SET_STATUS_TEXT:
+ {
+ mStatusView.setText((CharSequence) msg.obj);
+ return true;
+ }
+ case MSG_UI_TOAST_RESCAN_NEEDED:
+ {
+ Toast.makeText(mContext, R.string.ut_rescan_needed, Toast.LENGTH_LONG).show();
+ return true;
}
- mAudioStatusView.setVisibility(View.VISIBLE);
- return true;
- }
- case MSG_UI_HIDE_AUDIO_UNPLAYABLE: {
- mAudioStatusView.setVisibility(View.INVISIBLE);
- return true;
- }
- case MSG_UI_PROCESS_CAPTION_TRACK: {
- mCaptionTrackRenderer.processCaptionEvent((CaptionEvent) msg.obj);
- return true;
- }
- case MSG_UI_START_CAPTION_TRACK: {
- mCaptionTrackRenderer.start((AtscCaptionTrack) msg.obj);
- return true;
- }
- case MSG_UI_STOP_CAPTION_TRACK: {
- mCaptionTrackRenderer.stop();
- return true;
- }
- case MSG_UI_RESET_CAPTION_TRACK: {
- mCaptionTrackRenderer.reset();
- return true;
- }
- case MSG_UI_CLEAR_CAPTION_RENDERER: {
- mCaptionTrackRenderer.clear();
- return true;
- }
- case MSG_UI_SET_STATUS_TEXT: {
- mStatusView.setText((CharSequence) msg.obj);
- return true;
- }
- case MSG_UI_TOAST_RESCAN_NEEDED: {
- Toast.makeText(mContext, R.string.ut_rescan_needed, Toast.LENGTH_LONG).show();
- return true;
- }
}
return false;
}
@Override
- public void onTunerPreferencesChanged() {
+ public void onCommonPreferencesChanged() {
mSessionWorker.sendMessage(TunerSessionWorker.MSG_TUNER_PREFERENCES_CHANGED);
}
}
diff --git a/src/com/android/tv/tuner/tvinput/TunerSessionWorker.java b/tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorker.java
index e7eb017e..65750e08 100644
--- a/src/com/android/tv/tuner/tvinput/TunerSessionWorker.java
+++ b/tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorker.java
@@ -42,15 +42,13 @@ import android.util.Pair;
import android.util.SparseArray;
import android.view.Surface;
import android.view.accessibility.CaptioningManager;
-
-import com.google.android.exoplayer.audio.AudioCapabilities;
-import com.google.android.exoplayer.ExoPlayer;
+import com.android.tv.common.CommonPreferences.TrickplaySetting;
import com.android.tv.common.SoftPreconditions;
import com.android.tv.common.TvContentRatingCache;
-import com.android.tv.customization.TvCustomizationManager;
-import com.android.tv.customization.TvCustomizationManager.TRICKPLAY_MODE;
+import com.android.tv.common.customization.CustomizationManager;
+import com.android.tv.common.customization.CustomizationManager.TRICKPLAY_MODE;
+import com.android.tv.common.util.SystemPropertiesProxy;
import com.android.tv.tuner.TunerPreferences;
-import com.android.tv.tuner.TunerPreferences.TrickplaySetting;
import com.android.tv.tuner.data.Cea708Data;
import com.android.tv.tuner.data.PsipData.EitItem;
import com.android.tv.tuner.data.PsipData.TvTracksInterface;
@@ -58,18 +56,17 @@ import com.android.tv.tuner.data.TunerChannel;
import com.android.tv.tuner.data.nano.Channel;
import com.android.tv.tuner.data.nano.Track.AtscAudioTrack;
import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
+import com.android.tv.tuner.exoplayer.MpegTsPlayer;
import com.android.tv.tuner.exoplayer.MpegTsRendererBuilder;
import com.android.tv.tuner.exoplayer.buffer.BufferManager;
import com.android.tv.tuner.exoplayer.buffer.BufferManager.StorageManager;
import com.android.tv.tuner.exoplayer.buffer.DvrStorageManager;
-import com.android.tv.tuner.exoplayer.MpegTsPlayer;
import com.android.tv.tuner.exoplayer.buffer.TrickplayStorageManager;
-import com.android.tv.tuner.exoplayer.ffmpeg.FfmpegDecoderClient;
import com.android.tv.tuner.source.TsDataSource;
import com.android.tv.tuner.source.TsDataSourceManager;
import com.android.tv.tuner.util.StatusTextUtils;
-import com.android.tv.tuner.util.SystemPropertiesProxy;
-
+import com.google.android.exoplayer.ExoPlayer;
+import com.google.android.exoplayer.audio.AudioCapabilities;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
@@ -79,20 +76,24 @@ import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
- * {@link TunerSessionWorker} implements a handler thread which processes TV input jobs
- * such as handling {@link ExoPlayer}, managing a tuner device, trickplay, and so on.
+ * {@link TunerSessionWorker} implements a handler thread which processes TV input jobs such as
+ * handling {@link ExoPlayer}, managing a tuner device, trickplay, and so on.
*/
@WorkerThread
-public class TunerSessionWorker implements PlaybackBufferListener,
- MpegTsPlayer.VideoEventListener, MpegTsPlayer.Listener, EventDetector.EventListener,
- ChannelDataManager.ProgramInfoListener, Handler.Callback {
+public class TunerSessionWorker
+ implements PlaybackBufferListener,
+ MpegTsPlayer.VideoEventListener,
+ MpegTsPlayer.Listener,
+ EventDetector.EventListener,
+ ChannelDataManager.ProgramInfoListener,
+ Handler.Callback {
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 MAX_BUFFER_SIZE_KEY = "tv.tuner.buffersize_mbytes";
- private static final int MAX_BUFFER_SIZE_DEF = 2 * 1024; // 2GB
- private static final int MIN_BUFFER_SIZE_DEF = 256; // 256MB
+ private static final int MAX_BUFFER_SIZE_DEF = 2 * 1024; // 2GB
+ private static final int MIN_BUFFER_SIZE_DEF = 256; // 256MB
// Public messages
public static final int MSG_SELECT_TRACK = 1;
@@ -214,8 +215,8 @@ public class TunerSessionWorker implements PlaybackBufferListener,
private boolean mReleaseRequested; // Guarded by mReleaseLock
private final Object mReleaseLock = new Object();
- public TunerSessionWorker(Context context, ChannelDataManager channelDataManager,
- TunerSession tunerSession) {
+ public TunerSessionWorker(
+ Context context, ChannelDataManager channelDataManager, TunerSession tunerSession) {
if (DEBUG) Log.d(TAG, "TunerSessionWorker created");
mContext = context;
@@ -239,14 +240,14 @@ public class TunerSessionWorker implements PlaybackBufferListener,
mPlaybackParams.setSpeed(1.0f);
mMaxTrickplayBufferSizeMb =
SystemPropertiesProxy.getInt(MAX_BUFFER_SIZE_KEY, MAX_BUFFER_SIZE_DEF);
- mTrickplayModeCustomization = TvCustomizationManager.getTrickplayMode(context);
- if (mTrickplayModeCustomization ==
- TvCustomizationManager.TRICKPLAY_MODE_USE_EXTERNAL_STORAGE) {
+ mTrickplayModeCustomization = CustomizationManager.getTrickplayMode(context);
+ if (mTrickplayModeCustomization
+ == CustomizationManager.TRICKPLAY_MODE_USE_EXTERNAL_STORAGE) {
boolean useExternalStorage =
- Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) &&
- Environment.isExternalStorageRemovable();
+ Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
+ && Environment.isExternalStorageRemovable();
mTrickplayBufferDir = useExternalStorage ? context.getExternalCacheDir() : null;
- } else if (mTrickplayModeCustomization == TvCustomizationManager.TRICKPLAY_MODE_ENABLED) {
+ } else if (mTrickplayModeCustomization == CustomizationManager.TRICKPLAY_MODE_ENABLED) {
mTrickplayBufferDir = context.getCacheDir();
} else {
mTrickplayBufferDir = null;
@@ -255,7 +256,7 @@ public class TunerSessionWorker implements PlaybackBufferListener,
mTrickplaySetting = TunerPreferences.getTrickplaySetting(context);
if (mTrickplaySetting != TunerPreferences.TRICKPLAY_SETTING_NOT_SET
&& mTrickplayModeCustomization
- == TvCustomizationManager.TRICKPLAY_MODE_USE_EXTERNAL_STORAGE) {
+ == CustomizationManager.TRICKPLAY_MODE_USE_EXTERNAL_STORAGE) {
// Consider the case of Customization package updates the value of trickplay mode
// to TRICKPLAY_MODE_USE_EXTERNAL_STORAGE after install.
mTrickplaySetting = TunerPreferences.TRICKPLAY_SETTING_NOT_SET;
@@ -269,7 +270,8 @@ public class TunerSessionWorker implements PlaybackBufferListener,
// NOTE: We assume that TunerSessionWorker instance will be at most one.
// Only one TunerSessionWorker can be connected to FfmpegDecoderClient at any given time.
// connect() will return false, if there is a connected TunerSessionWorker already.
- mHasSoftwareAudioDecoder = FfmpegDecoderClient.connect(context);
+ mHasSoftwareAudioDecoder = false; // TODO reimplement ffmpeg for google3
+ // TODO connect the ffmpeg client and report if available.
}
// Public methods
@@ -286,9 +288,7 @@ public class TunerSessionWorker implements PlaybackBufferListener,
sendMessage(MSG_STOP_TUNE);
}
- /**
- * Sets {@link Surface}.
- */
+ /** Sets {@link Surface}. */
@MainThread
public void setSurface(Surface surface) {
if (surface != null && !surface.isValid()) {
@@ -301,9 +301,7 @@ public class TunerSessionWorker implements PlaybackBufferListener,
mHandler.sendEmptyMessage(MSG_SET_SURFACE);
}
- /**
- * Sets volume.
- */
+ /** Sets volume. */
@MainThread
public void setStreamVolume(float volume) {
// mVolume is kept even when tune is called right after. But, messages can be deleted by
@@ -313,9 +311,7 @@ public class TunerSessionWorker implements PlaybackBufferListener,
mHandler.sendEmptyMessage(MSG_SET_STREAM_VOLUME);
}
- /**
- * Sets if caption is enabled or disabled.
- */
+ /** Sets if caption is enabled or disabled. */
@MainThread
public void setCaptionEnabled(boolean captionEnabled) {
// mCaptionEnabled is kept even when tune is called right after. But, messages can be
@@ -334,18 +330,16 @@ public class TunerSessionWorker implements PlaybackBufferListener,
return mBufferStartTimeMs;
}
-
private String getRecordingPath() {
return Uri.parse(mRecordingId).getPath();
}
private Long getDurationForRecording(String recordingId) {
DvrStorageManager storageManager =
- new DvrStorageManager(new File(getRecordingPath()), false);
- List<BufferManager.TrackFormat> trackFormatList =
- storageManager.readTrackInfoFiles(false);
+ new DvrStorageManager(new File(getRecordingPath()), false);
+ List<BufferManager.TrackFormat> trackFormatList = storageManager.readTrackInfoFiles(false);
if (trackFormatList.isEmpty()) {
- trackFormatList = storageManager.readTrackInfoFiles(true);
+ trackFormatList = storageManager.readTrackInfoFiles(true);
}
if (!trackFormatList.isEmpty()) {
BufferManager.TrackFormat trackFormat = trackFormatList.get(0);
@@ -361,16 +355,23 @@ public class TunerSessionWorker implements PlaybackBufferListener,
public long getCurrentPosition() {
// TODO: More precise time may be necessary.
MpegTsPlayer mpegTsPlayer = mPlayer;
- long currentTime = mpegTsPlayer != null
- ? mRecordStartTimeMs + mpegTsPlayer.getCurrentPosition() : mRecordStartTimeMs;
+ long currentTime =
+ mpegTsPlayer != null
+ ? mRecordStartTimeMs + mpegTsPlayer.getCurrentPosition()
+ : mRecordStartTimeMs;
if (mChannel == null && mPlayerState == ExoPlayer.STATE_ENDED) {
currentTime = mRecordingDuration + mRecordStartTimeMs;
}
if (DEBUG) {
long systemCurrentTime = System.currentTimeMillis();
- Log.d(TAG, "currentTime = " + currentTime
- + " ; System.currentTimeMillis() = " + systemCurrentTime
- + " ; diff = " + (currentTime - systemCurrentTime));
+ Log.d(
+ TAG,
+ "currentTime = "
+ + currentTime
+ + " ; System.currentTimeMillis() = "
+ + systemCurrentTime
+ + " ; diff = "
+ + (currentTime - systemCurrentTime));
}
return currentTime;
}
@@ -397,7 +398,8 @@ public class TunerSessionWorker implements PlaybackBufferListener,
mReleaseRequested = true;
}
if (mHasSoftwareAudioDecoder) {
- FfmpegDecoderClient.disconnect(mContext);
+ // TODO reimplement for google3
+ // Here disconnect ffmpeg
}
mChannelDataManager.setListener(null);
mHandler.removeCallbacksAndMessages(null);
@@ -440,8 +442,9 @@ public class TunerSessionWorker implements PlaybackBufferListener,
public void onError(Exception e) {
if (TunerPreferences.getStoreTsStream(mContext)) {
// Crash intentionally to capture the error causing TS file.
- Log.e(TAG, "Crash intentionally to capture the error causing TS file. "
- + e.getMessage());
+ Log.e(
+ TAG,
+ "Crash intentionally to capture the error causing TS file. " + e.getMessage());
SoftPreconditions.checkState(false);
}
// There maybe some errors that finally raise ExoPlaybackException and will be handled here.
@@ -506,8 +509,7 @@ public class TunerSessionWorker implements PlaybackBufferListener,
return;
}
Log.i(TAG, "AC3 audio cannot be played due to device limitation");
- mSession.sendUiMessage(
- TunerSession.MSG_UI_SHOW_AUDIO_UNPLAYABLE);
+ mSession.sendUiMessage(TunerSession.MSG_UI_SHOW_AUDIO_UNPLAYABLE);
}
// MpegTsPlayer.VideoEventListener
@@ -592,7 +594,7 @@ public class TunerSessionWorker implements PlaybackBufferListener,
}
private static class RecordedProgram {
- private final long mChannelId;
+ // private final long mChannelId;
private final String mDataUri;
private static final String[] PROJECTION = {
@@ -602,12 +604,13 @@ public class TunerSessionWorker implements PlaybackBufferListener,
public RecordedProgram(Cursor cursor) {
int index = 0;
- mChannelId = cursor.getLong(index++);
+ // mChannelId = cursor.getLong(index++);
+ index++;
mDataUri = cursor.getString(index++);
}
public RecordedProgram(long channelId, String dataUri) {
- mChannelId = channelId;
+ // mChannelId = channelId;
mDataUri = dataUri;
}
@@ -626,7 +629,7 @@ public class TunerSessionWorker implements PlaybackBufferListener,
private RecordedProgram getRecordedProgram(Uri recordedUri) {
ContentResolver resolver = mContext.getContentResolver();
- try(Cursor c = resolver.query(recordedUri, RecordedProgram.PROJECTION, null, null, null)) {
+ try (Cursor c = resolver.query(recordedUri, RecordedProgram.PROJECTION, null, null, null)) {
if (c != null) {
RecordedProgram result = RecordedProgram.onQuery(c);
if (DEBUG) {
@@ -655,367 +658,417 @@ public class TunerSessionWorker implements PlaybackBufferListener,
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
- case MSG_TUNE: {
- if (DEBUG) Log.d(TAG, "MSG_TUNE");
-
- // When sequential tuning messages arrived, it skips middle tuning messages in order
- // to change to the last requested channel quickly.
- if (mHandler.hasMessages(MSG_TUNE)) {
- return true;
- }
- notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING);
- if (!mIsActiveSession) {
- // Wait until release is finished if there is a pending release.
- try {
- while (!sActiveSessionSemaphore.tryAcquire(
- RELEASE_WAIT_INTERVAL_MS, TimeUnit.MILLISECONDS)) {
- synchronized (mReleaseLock) {
- if (mReleaseRequested) {
- return true;
+ case MSG_TUNE:
+ {
+ if (DEBUG) Log.d(TAG, "MSG_TUNE");
+
+ // When sequential tuning messages arrived, it skips middle tuning messages in
+ // order
+ // to change to the last requested channel quickly.
+ if (mHandler.hasMessages(MSG_TUNE)) {
+ return true;
+ }
+ notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING);
+ if (!mIsActiveSession) {
+ // Wait until release is finished if there is a pending release.
+ try {
+ while (!sActiveSessionSemaphore.tryAcquire(
+ RELEASE_WAIT_INTERVAL_MS, TimeUnit.MILLISECONDS)) {
+ synchronized (mReleaseLock) {
+ if (mReleaseRequested) {
+ return true;
+ }
}
}
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
}
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
+ synchronized (mReleaseLock) {
+ if (mReleaseRequested) {
+ sActiveSessionSemaphore.release();
+ return true;
+ }
+ }
+ mIsActiveSession = true;
+ }
+ Uri channelUri = (Uri) msg.obj;
+ String recording = null;
+ long channelId = parseChannel(channelUri);
+ TunerChannel channel =
+ (channelId == -1) ? null : mChannelDataManager.getChannel(channelId);
+ if (channelId == -1) {
+ recording = parseRecording(channelUri);
}
- synchronized (mReleaseLock) {
- if (mReleaseRequested) {
- sActiveSessionSemaphore.release();
+ if (channel == null && recording == null) {
+ Log.w(TAG, "onTune() is failed. Can't find channel for " + channelUri);
+ stopTune();
+ notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN);
+ return true;
+ }
+ clearCallbacksAndMessagesSafely();
+ mChannelDataManager.removeAllCallbacksAndMessages();
+ if (channel != null) {
+ if (mTvInputManager.isParentalControlsEnabled() && channel.isLocked()) {
+ Log.i(TAG, "onTune() is failed. Channel is blocked" + channel);
+ mSession.notifyContentBlocked(TvContentRating.UNRATED);
return true;
}
+ mChannelDataManager.requestProgramsData(channel);
}
- mIsActiveSession = true;
- }
- Uri channelUri = (Uri) msg.obj;
- String recording = null;
- long channelId = parseChannel(channelUri);
- TunerChannel channel = (channelId == -1) ? null
- : mChannelDataManager.getChannel(channelId);
- if (channelId == -1) {
- recording = parseRecording(channelUri);
+ prepareTune(channel, recording);
+ // TODO: Need to refactor. notifyContentAllowed() should not be called if
+ // parental
+ // control is turned on.
+ mSession.notifyContentAllowed();
+ resetTvTracks();
+ resetPlayback();
+ mHandler.sendEmptyMessageDelayed(
+ MSG_RESCHEDULE_PROGRAMS, RESCHEDULE_PROGRAMS_INITIAL_DELAY_MS);
+ return true;
}
- if (channel == null && recording == null) {
- Log.w(TAG, "onTune() is failed. Can't find channel for " + channelUri);
- stopTune();
+ case MSG_STOP_TUNE:
+ {
+ if (DEBUG) Log.d(TAG, "MSG_STOP_TUNE");
+ mChannel = null;
+ stopPlayback(true);
+ stopCaptionTrack();
+ resetTvTracks();
notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN);
return true;
}
- clearCallbacksAndMessagesSafely();
- mChannelDataManager.removeAllCallbacksAndMessages();
- if (channel != null) {
- mChannelDataManager.requestProgramsData(channel);
- }
- prepareTune(channel, recording);
- // TODO: Need to refactor. notifyContentAllowed() should not be called if parental
- // control is turned on.
- mSession.notifyContentAllowed();
- resetTvTracks();
- resetPlayback();
- mHandler.sendEmptyMessageDelayed(MSG_RESCHEDULE_PROGRAMS,
- RESCHEDULE_PROGRAMS_INITIAL_DELAY_MS);
- return true;
- }
- case MSG_STOP_TUNE: {
- if (DEBUG) Log.d(TAG, "MSG_STOP_TUNE");
- mChannel = null;
- stopPlayback(true);
- stopCaptionTrack();
- resetTvTracks();
- notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN);
- return true;
- }
- case MSG_RELEASE: {
- if (DEBUG) Log.d(TAG, "MSG_RELEASE");
- mHandler.removeCallbacksAndMessages(null);
- stopPlayback(true);
- stopCaptionTrack();
- mSourceManager.release();
- mHandler.getLooper().quitSafely();
- if (mIsActiveSession) {
- sActiveSessionSemaphore.release();
- }
- return true;
- }
- case MSG_RETRY_PLAYBACK: {
- if (System.identityHashCode(mPlayer) == (int) msg.obj) {
- Log.i(TAG, "Retrying the playback for channel: " + mChannel);
- mHandler.removeMessages(MSG_RETRY_PLAYBACK);
- // When there is a request of retrying playback, don't reuse TunerHal.
- mSourceManager.setKeepTuneStatus(false);
- mRetryCount++;
- if (DEBUG) {
- Log.d(TAG, "MSG_RETRY_PLAYBACK " + mRetryCount);
- }
- mChannelDataManager.removeAllCallbacksAndMessages();
- if (mRetryCount <= MAX_IMMEDIATE_RETRY_COUNT) {
- resetPlayback();
- } else {
- // When it reaches this point, it may be due to an error that occurred in
- // the tuner device. Calling stopPlayback() resets the tuner device
- // to recover from the error.
- stopPlayback(false);
- stopCaptionTrack();
-
- notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL);
- Log.i(TAG, "Notify weak signal since fail to retry playback");
-
- // After MAX_IMMEDIATE_RETRY_COUNT, give some delay of an empirically chosen
- // value before recovering the playback.
- mHandler.sendEmptyMessageDelayed(MSG_RESET_PLAYBACK,
- RECOVER_STOPPED_PLAYBACK_PERIOD_MS);
+ case MSG_RELEASE:
+ {
+ if (DEBUG) Log.d(TAG, "MSG_RELEASE");
+ mHandler.removeCallbacksAndMessages(null);
+ stopPlayback(true);
+ stopCaptionTrack();
+ mSourceManager.release();
+ mHandler.getLooper().quitSafely();
+ if (mIsActiveSession) {
+ sActiveSessionSemaphore.release();
}
+ return true;
}
- return true;
- }
- case MSG_RESET_PLAYBACK: {
- if (DEBUG) Log.d(TAG, "MSG_RESET_PLAYBACK");
- mChannelDataManager.removeAllCallbacksAndMessages();
- resetPlayback();
- return true;
- }
- case MSG_START_PLAYBACK: {
- if (DEBUG) Log.d(TAG, "MSG_START_PLAYBACK");
- if (mChannel != null || mRecordingId != null) {
- startPlayback((int) msg.obj);
- }
- return true;
- }
- case MSG_UPDATE_PROGRAM: {
- if (mChannel != null) {
- EitItem program = (EitItem) msg.obj;
- updateTvTracks(program, false);
- mHandler.sendEmptyMessage(MSG_PARENTAL_CONTROLS);
+ case MSG_RETRY_PLAYBACK:
+ {
+ if (System.identityHashCode(mPlayer) == (int) msg.obj) {
+ Log.i(TAG, "Retrying the playback for channel: " + mChannel);
+ mHandler.removeMessages(MSG_RETRY_PLAYBACK);
+ // When there is a request of retrying playback, don't reuse TunerHal.
+ mSourceManager.setKeepTuneStatus(false);
+ mRetryCount++;
+ if (DEBUG) {
+ Log.d(TAG, "MSG_RETRY_PLAYBACK " + mRetryCount);
+ }
+ mChannelDataManager.removeAllCallbacksAndMessages();
+ if (mRetryCount <= MAX_IMMEDIATE_RETRY_COUNT) {
+ resetPlayback();
+ } else {
+ // When it reaches this point, it may be due to an error that occurred
+ // in
+ // the tuner device. Calling stopPlayback() resets the tuner device
+ // to recover from the error.
+ stopPlayback(false);
+ stopCaptionTrack();
+
+ notifyVideoUnavailable(
+ TvInputManager.VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL);
+ Log.i(TAG, "Notify weak signal since fail to retry playback");
+
+ // After MAX_IMMEDIATE_RETRY_COUNT, give some delay of an empirically
+ // chosen
+ // value before recovering the playback.
+ mHandler.sendEmptyMessageDelayed(
+ MSG_RESET_PLAYBACK, RECOVER_STOPPED_PLAYBACK_PERIOD_MS);
+ }
+ }
+ return true;
}
- return true;
- }
- case MSG_SCHEDULE_OF_PROGRAMS: {
- mHandler.removeMessages(MSG_UPDATE_PROGRAM);
- Pair<TunerChannel, List<EitItem>> pair =
- (Pair<TunerChannel, List<EitItem>>) msg.obj;
- TunerChannel channel = pair.first;
- if (mChannel == null) {
+ case MSG_RESET_PLAYBACK:
+ {
+ if (DEBUG) Log.d(TAG, "MSG_RESET_PLAYBACK");
+ mChannelDataManager.removeAllCallbacksAndMessages();
+ resetPlayback();
return true;
}
- if (mChannel != null && mChannel.compareTo(channel) != 0) {
+ case MSG_START_PLAYBACK:
+ {
+ if (DEBUG) Log.d(TAG, "MSG_START_PLAYBACK");
+ if (mChannel != null || mRecordingId != null) {
+ startPlayback((int) msg.obj);
+ }
return true;
}
- mPrograms = pair.second;
- EitItem currentProgram = getCurrentProgram();
- if (currentProgram == null) {
- mProgram = null;
+ case MSG_UPDATE_PROGRAM:
+ {
+ if (mChannel != null) {
+ EitItem program = (EitItem) msg.obj;
+ updateTvTracks(program, false);
+ mHandler.sendEmptyMessage(MSG_PARENTAL_CONTROLS);
+ }
+ return true;
}
- long currentTimeMs = getCurrentPosition();
- if (mPrograms != null) {
- for (EitItem item : mPrograms) {
- if (currentProgram != null && currentProgram.compareTo(item) == 0) {
- if (DEBUG) {
- Log.d(TAG, "Update current TvTracks " + item);
- }
- if (mProgram != null && mProgram.compareTo(item) == 0) {
- continue;
- }
- mProgram = item;
- updateTvTracks(item, false);
- } else if (item.getStartTimeUtcMillis() > currentTimeMs) {
- if (DEBUG) {
- Log.d(TAG, "Update next TvTracks " + item + " "
- + (item.getStartTimeUtcMillis() - currentTimeMs));
+ case MSG_SCHEDULE_OF_PROGRAMS:
+ {
+ mHandler.removeMessages(MSG_UPDATE_PROGRAM);
+ Pair<TunerChannel, List<EitItem>> pair =
+ (Pair<TunerChannel, List<EitItem>>) msg.obj;
+ TunerChannel channel = pair.first;
+ if (mChannel == null) {
+ return true;
+ }
+ if (mChannel != null && mChannel.compareTo(channel) != 0) {
+ return true;
+ }
+ mPrograms = pair.second;
+ EitItem currentProgram = getCurrentProgram();
+ if (currentProgram == null) {
+ mProgram = null;
+ }
+ long currentTimeMs = getCurrentPosition();
+ if (mPrograms != null) {
+ for (EitItem item : mPrograms) {
+ if (currentProgram != null && currentProgram.compareTo(item) == 0) {
+ if (DEBUG) {
+ Log.d(TAG, "Update current TvTracks " + item);
+ }
+ if (mProgram != null && mProgram.compareTo(item) == 0) {
+ continue;
+ }
+ mProgram = item;
+ updateTvTracks(item, false);
+ } else if (item.getStartTimeUtcMillis() > currentTimeMs) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "Update next TvTracks "
+ + item
+ + " "
+ + (item.getStartTimeUtcMillis()
+ - currentTimeMs));
+ }
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(MSG_UPDATE_PROGRAM, item),
+ item.getStartTimeUtcMillis() - currentTimeMs);
}
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(MSG_UPDATE_PROGRAM, item),
- item.getStartTimeUtcMillis() - currentTimeMs);
}
}
+ mHandler.sendEmptyMessage(MSG_PARENTAL_CONTROLS);
+ return true;
}
- mHandler.sendEmptyMessage(MSG_PARENTAL_CONTROLS);
- return true;
- }
- case MSG_UPDATE_CHANNEL_INFO: {
- TunerChannel channel = (TunerChannel) msg.obj;
- if (mChannel != null && mChannel.compareTo(channel) == 0) {
- updateChannelInfo(channel);
- }
- return true;
- }
- case MSG_PROGRAM_DATA_RESULT: {
- TunerChannel channel = (TunerChannel) ((Pair) msg.obj).first;
-
- // If there already exists, skip it since real-time data is a top priority,
- if (mChannel != null && mChannel.compareTo(channel) == 0
- && mPrograms == null && mProgram == null) {
- sendMessage(MSG_SCHEDULE_OF_PROGRAMS, msg.obj);
- }
- return true;
- }
- case MSG_TRICKPLAY_BY_SEEK: {
- if (mPlayer == null) {
+ case MSG_UPDATE_CHANNEL_INFO:
+ {
+ TunerChannel channel = (TunerChannel) msg.obj;
+ if (mChannel != null && mChannel.compareTo(channel) == 0) {
+ updateChannelInfo(channel);
+ }
return true;
}
- doTrickplayBySeek(msg.arg1);
- return true;
- }
- case MSG_SMOOTH_TRICKPLAY_MONITOR: {
- if (mPlayer == null) {
+ case MSG_PROGRAM_DATA_RESULT:
+ {
+ TunerChannel channel = (TunerChannel) ((Pair) msg.obj).first;
+
+ // If there already exists, skip it since real-time data is a top priority,
+ if (mChannel != null
+ && mChannel.compareTo(channel) == 0
+ && mPrograms == null
+ && mProgram == null) {
+ sendMessage(MSG_SCHEDULE_OF_PROGRAMS, msg.obj);
+ }
return true;
}
- long systemCurrentTime = System.currentTimeMillis();
- long position = getCurrentPosition();
- if (mRecordingId == null) {
- // Checks if the position exceeds the upper bound when forwarding,
- // or exceed the lower bound when rewinding.
- // If the direction is not checked, there can be some issues.
- // (See b/29939781 for more details.)
- if ((position > systemCurrentTime && mPlaybackParams.getSpeed() > 0L)
- || (position < mBufferStartTimeMs && mPlaybackParams.getSpeed() < 0L)) {
- doTimeShiftResume();
+ case MSG_TRICKPLAY_BY_SEEK:
+ {
+ if (mPlayer == null) {
return true;
}
- } else {
- if (position > mRecordingDuration || position < 0) {
- doTimeShiftPause();
+ doTrickplayBySeek(msg.arg1);
+ return true;
+ }
+ case MSG_SMOOTH_TRICKPLAY_MONITOR:
+ {
+ if (mPlayer == null) {
return true;
}
+ long systemCurrentTime = System.currentTimeMillis();
+ long position = getCurrentPosition();
+ if (mRecordingId == null) {
+ // Checks if the position exceeds the upper bound when forwarding,
+ // or exceed the lower bound when rewinding.
+ // If the direction is not checked, there can be some issues.
+ // (See b/29939781 for more details.)
+ if ((position > systemCurrentTime && mPlaybackParams.getSpeed() > 0L)
+ || (position < mBufferStartTimeMs
+ && mPlaybackParams.getSpeed() < 0L)) {
+ doTimeShiftResume();
+ return true;
+ }
+ } else {
+ if (position > mRecordingDuration || position < 0) {
+ doTimeShiftPause();
+ return true;
+ }
+ }
+ mHandler.sendEmptyMessageDelayed(
+ MSG_SMOOTH_TRICKPLAY_MONITOR, TRICKPLAY_MONITOR_INTERVAL_MS);
+ return true;
}
- mHandler.sendEmptyMessageDelayed(MSG_SMOOTH_TRICKPLAY_MONITOR,
- TRICKPLAY_MONITOR_INTERVAL_MS);
- return true;
- }
- case MSG_RESCHEDULE_PROGRAMS: {
- if (mHandler.hasMessages(MSG_SCHEDULE_OF_PROGRAMS)) {
- mHandler.sendEmptyMessage(MSG_RESCHEDULE_PROGRAMS);
- } else {
- doReschedulePrograms();
+ case MSG_RESCHEDULE_PROGRAMS:
+ {
+ if (mHandler.hasMessages(MSG_SCHEDULE_OF_PROGRAMS)) {
+ mHandler.sendEmptyMessage(MSG_RESCHEDULE_PROGRAMS);
+ } else {
+ doReschedulePrograms();
+ }
+ return true;
}
- return true;
- }
- case MSG_PARENTAL_CONTROLS: {
- doParentalControls();
- mHandler.removeMessages(MSG_PARENTAL_CONTROLS);
- mHandler.sendEmptyMessageDelayed(MSG_PARENTAL_CONTROLS,
- PARENTAL_CONTROLS_INTERVAL_MS);
- return true;
- }
- case MSG_UNBLOCKED_RATING: {
- mUnblockedContentRating = (TvContentRating) msg.obj;
- doParentalControls();
- mHandler.removeMessages(MSG_PARENTAL_CONTROLS);
- mHandler.sendEmptyMessageDelayed(MSG_PARENTAL_CONTROLS,
- PARENTAL_CONTROLS_INTERVAL_MS);
- return true;
- }
- case MSG_DISCOVER_CAPTION_SERVICE_NUMBER: {
- int serviceNumber = (int) msg.obj;
- doDiscoverCaptionServiceNumber(serviceNumber);
- return true;
- }
- case MSG_SELECT_TRACK: {
- if (mChannel != null || mRecordingId != null) {
- doSelectTrack(msg.arg1, (String) msg.obj);
+ case MSG_PARENTAL_CONTROLS:
+ {
+ doParentalControls();
+ mHandler.removeMessages(MSG_PARENTAL_CONTROLS);
+ mHandler.sendEmptyMessageDelayed(
+ MSG_PARENTAL_CONTROLS, PARENTAL_CONTROLS_INTERVAL_MS);
+ return true;
}
- return true;
- }
- case MSG_UPDATE_CAPTION_TRACK: {
- if (mCaptionEnabled) {
- startCaptionTrack();
- } else {
- stopCaptionTrack();
+ case MSG_UNBLOCKED_RATING:
+ {
+ mUnblockedContentRating = (TvContentRating) msg.obj;
+ doParentalControls();
+ mHandler.removeMessages(MSG_PARENTAL_CONTROLS);
+ mHandler.sendEmptyMessageDelayed(
+ MSG_PARENTAL_CONTROLS, PARENTAL_CONTROLS_INTERVAL_MS);
+ return true;
}
- return true;
- }
- case MSG_TIMESHIFT_PAUSE: {
- if (DEBUG) Log.d(TAG, "MSG_TIMESHIFT_PAUSE");
- if (mPlayer == null) {
+ case MSG_DISCOVER_CAPTION_SERVICE_NUMBER:
+ {
+ int serviceNumber = (int) msg.obj;
+ doDiscoverCaptionServiceNumber(serviceNumber);
return true;
}
- setTrickplayEnabledIfNeeded();
- doTimeShiftPause();
- return true;
- }
- case MSG_TIMESHIFT_RESUME: {
- if (DEBUG) Log.d(TAG, "MSG_TIMESHIFT_RESUME");
- if (mPlayer == null) {
+ case MSG_SELECT_TRACK:
+ {
+ if (mPlayer == null) {
+ Log.w(TAG, "mPlayer is null when doselectTrack is called");
+ return false;
+ }
+ if (mChannel != null || mRecordingId != null) {
+ doSelectTrack(msg.arg1, (String) msg.obj);
+ }
return true;
}
- setTrickplayEnabledIfNeeded();
- doTimeShiftResume();
- return true;
- }
- case MSG_TIMESHIFT_SEEK_TO: {
- long position = (long) msg.obj;
- if (DEBUG) Log.d(TAG, "MSG_TIMESHIFT_SEEK_TO (position=" + position + ")");
- if (mPlayer == null) {
+ case MSG_UPDATE_CAPTION_TRACK:
+ {
+ if (mCaptionEnabled) {
+ startCaptionTrack();
+ } else {
+ stopCaptionTrack();
+ }
return true;
}
- setTrickplayEnabledIfNeeded();
- doTimeShiftSeekTo(position);
- return true;
- }
- case MSG_TIMESHIFT_SET_PLAYBACKPARAMS: {
- if (mPlayer == null) {
+ case MSG_TIMESHIFT_PAUSE:
+ {
+ if (DEBUG) Log.d(TAG, "MSG_TIMESHIFT_PAUSE");
+ if (mPlayer == null) {
+ return true;
+ }
+ setTrickplayEnabledIfNeeded();
+ doTimeShiftPause();
return true;
}
- setTrickplayEnabledIfNeeded();
- doTimeShiftSetPlaybackParams((PlaybackParams) msg.obj);
- return true;
- }
- case MSG_AUDIO_CAPABILITIES_CHANGED: {
- AudioCapabilities capabilities = (AudioCapabilities) msg.obj;
- if (DEBUG) {
- Log.d(TAG, "MSG_AUDIO_CAPABILITIES_CHANGED " + capabilities);
+ case MSG_TIMESHIFT_RESUME:
+ {
+ if (DEBUG) Log.d(TAG, "MSG_TIMESHIFT_RESUME");
+ if (mPlayer == null) {
+ return true;
+ }
+ setTrickplayEnabledIfNeeded();
+ doTimeShiftResume();
+ return true;
+ }
+ case MSG_TIMESHIFT_SEEK_TO:
+ {
+ long position = (long) msg.obj;
+ if (DEBUG) Log.d(TAG, "MSG_TIMESHIFT_SEEK_TO (position=" + position + ")");
+ if (mPlayer == null) {
+ return true;
+ }
+ setTrickplayEnabledIfNeeded();
+ doTimeShiftSeekTo(position);
+ return true;
}
- if (capabilities == null) {
+ case MSG_TIMESHIFT_SET_PLAYBACKPARAMS:
+ {
+ if (mPlayer == null) {
+ return true;
+ }
+ setTrickplayEnabledIfNeeded();
+ doTimeShiftSetPlaybackParams((PlaybackParams) msg.obj);
return true;
}
- if (!capabilities.equals(mAudioCapabilities)) {
- // HDMI supported encodings are changed. restart player.
- mAudioCapabilities = capabilities;
- resetPlayback();
+ case MSG_AUDIO_CAPABILITIES_CHANGED:
+ {
+ AudioCapabilities capabilities = (AudioCapabilities) msg.obj;
+ if (DEBUG) {
+ Log.d(TAG, "MSG_AUDIO_CAPABILITIES_CHANGED " + capabilities);
+ }
+ if (capabilities == null) {
+ return true;
+ }
+ if (!capabilities.equals(mAudioCapabilities)) {
+ // HDMI supported encodings are changed. restart player.
+ mAudioCapabilities = capabilities;
+ resetPlayback();
+ }
+ return true;
}
- return true;
- }
- case MSG_SET_STREAM_VOLUME: {
- if (mPlayer != null && mPlayer.isPlaying()) {
- mPlayer.setVolume(mVolume);
+ case MSG_SET_STREAM_VOLUME:
+ {
+ if (mPlayer != null && mPlayer.isPlaying()) {
+ mPlayer.setVolume(mVolume);
+ }
+ return true;
}
- return true;
- }
- case MSG_TUNER_PREFERENCES_CHANGED: {
- mHandler.removeMessages(MSG_TUNER_PREFERENCES_CHANGED);
- @TrickplaySetting int trickplaySetting =
- TunerPreferences.getTrickplaySetting(mContext);
- if (trickplaySetting != mTrickplaySetting) {
- boolean wasTrcikplayEnabled =
- mTrickplaySetting != TunerPreferences.TRICKPLAY_SETTING_DISABLED;
- boolean isTrickplayEnabled =
- trickplaySetting != TunerPreferences.TRICKPLAY_SETTING_DISABLED;
- mTrickplaySetting = trickplaySetting;
- if (isTrickplayEnabled != wasTrcikplayEnabled) {
- sendMessage(MSG_RESET_PLAYBACK, System.identityHashCode(mPlayer));
+ case MSG_TUNER_PREFERENCES_CHANGED:
+ {
+ mHandler.removeMessages(MSG_TUNER_PREFERENCES_CHANGED);
+ @TrickplaySetting
+ int trickplaySetting = TunerPreferences.getTrickplaySetting(mContext);
+ if (trickplaySetting != mTrickplaySetting) {
+ boolean wasTrcikplayEnabled =
+ mTrickplaySetting != TunerPreferences.TRICKPLAY_SETTING_DISABLED;
+ boolean isTrickplayEnabled =
+ trickplaySetting != TunerPreferences.TRICKPLAY_SETTING_DISABLED;
+ mTrickplaySetting = trickplaySetting;
+ if (isTrickplayEnabled != wasTrcikplayEnabled) {
+ sendMessage(MSG_RESET_PLAYBACK, System.identityHashCode(mPlayer));
+ }
}
+ return true;
}
- return true;
- }
- case MSG_BUFFER_START_TIME_CHANGED: {
- if (mPlayer == null) {
+ case MSG_BUFFER_START_TIME_CHANGED:
+ {
+ if (mPlayer == null) {
+ return true;
+ }
+ mBufferStartTimeMs = (long) msg.obj;
+ if (!hasEnoughBackwardBuffer()
+ && (!mPlayer.isPlaying() || mPlaybackParams.getSpeed() < 1.0f)) {
+ mPlayer.setPlayWhenReady(true);
+ mPlayer.setAudioTrackAndClosedCaption(true);
+ mPlaybackParams.setSpeed(1.0f);
+ }
return true;
}
- mBufferStartTimeMs = (long) msg.obj;
- if (!hasEnoughBackwardBuffer()
- && (!mPlayer.isPlaying() || mPlaybackParams.getSpeed() < 1.0f)) {
- mPlayer.setPlayWhenReady(true);
- mPlayer.setAudioTrackAndClosedCaption(true);
- mPlaybackParams.setSpeed(1.0f);
+ case MSG_BUFFER_STATE_CHANGED:
+ {
+ boolean available = (boolean) msg.obj;
+ mSession.notifyTimeShiftStatusChanged(
+ available
+ ? TvInputManager.TIME_SHIFT_STATUS_AVAILABLE
+ : TvInputManager.TIME_SHIFT_STATUS_UNAVAILABLE);
+ return true;
}
- return true;
- }
- case MSG_BUFFER_STATE_CHANGED: {
- boolean available = (boolean) msg.obj;
- mSession.notifyTimeShiftStatusChanged(available
- ? TvInputManager.TIME_SHIFT_STATUS_AVAILABLE
- : TvInputManager.TIME_SHIFT_STATUS_UNAVAILABLE);
- return true;
- }
- case MSG_CHECK_SIGNAL: {
+ case MSG_CHECK_SIGNAL:
if (mChannel == null || mPlayer == null) {
return true;
}
@@ -1023,11 +1076,11 @@ public class TunerSessionWorker implements PlaybackBufferListener,
long limitInBytes = source != null ? source.getBufferedPosition() : 0L;
if (TunerDebug.ENABLED) {
TunerDebug.calculateDiff();
- mSession.sendUiMessage(TunerSession.MSG_UI_SET_STATUS_TEXT,
+ mSession.sendUiMessage(
+ TunerSession.MSG_UI_SET_STATUS_TEXT,
Html.fromHtml(
StatusTextUtils.getStatusWarningInHTML(
- (limitInBytes - mLastLimitInBytes)
- / TS_PACKET_SIZE,
+ (limitInBytes - mLastLimitInBytes) / TS_PACKET_SIZE,
TunerDebug.getVideoFrameDrop(),
TunerDebug.getBytesInQueue(),
TunerDebug.getAudioPositionUs(),
@@ -1035,43 +1088,52 @@ public class TunerSessionWorker implements PlaybackBufferListener,
TunerDebug.getAudioPtsUs(),
TunerDebug.getAudioPtsUsRate(),
TunerDebug.getVideoPtsUs(),
- TunerDebug.getVideoPtsUsRate()
- )));
+ TunerDebug.getVideoPtsUsRate())));
}
mSession.sendUiMessage(TunerSession.MSG_UI_HIDE_MESSAGE);
long currentTime = SystemClock.elapsedRealtime();
- long bufferingTimeMs = mBufferingStartTimeMs != INVALID_TIME
- ? currentTime - mBufferingStartTimeMs : mBufferingStartTimeMs;
- long preparingTimeMs = mPreparingStartTimeMs != INVALID_TIME
- ? currentTime - mPreparingStartTimeMs : mPreparingStartTimeMs;
+ long bufferingTimeMs =
+ mBufferingStartTimeMs != INVALID_TIME
+ ? currentTime - mBufferingStartTimeMs
+ : mBufferingStartTimeMs;
+ long preparingTimeMs =
+ mPreparingStartTimeMs != INVALID_TIME
+ ? currentTime - mPreparingStartTimeMs
+ : mPreparingStartTimeMs;
boolean isBufferingTooLong =
bufferingTimeMs > PLAYBACK_STATE_CHANGED_WAITING_THRESHOLD_MS;
boolean isPreparingTooLong =
preparingTimeMs > PLAYBACK_STATE_CHANGED_WAITING_THRESHOLD_MS;
- boolean isWeakSignal = source != null
- && mChannel.getType() != Channel.TYPE_FILE
- && (isBufferingTooLong || isPreparingTooLong);
+ boolean isWeakSignal =
+ source != null
+ && mChannel.getType() != Channel.TunerType.TYPE_FILE
+ && (isBufferingTooLong || isPreparingTooLong);
if (isWeakSignal && !mReportedWeakSignal) {
if (!mHandler.hasMessages(MSG_RETRY_PLAYBACK)) {
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RETRY_PLAYBACK,
- System.identityHashCode(mPlayer)), PLAYBACK_RETRY_DELAY_MS);
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(
+ MSG_RETRY_PLAYBACK, System.identityHashCode(mPlayer)),
+ PLAYBACK_RETRY_DELAY_MS);
}
if (mPlayer != null) {
mPlayer.setAudioTrackAndClosedCaption(false);
}
notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL);
- Log.i(TAG, "Notify weak signal due to signal check, " + String.format(
- "packetsPerSec:%d, bufferingTimeMs:%d, preparingTimeMs:%d, " +
- "videoFrameDrop:%d",
- (limitInBytes - mLastLimitInBytes) / TS_PACKET_SIZE,
- bufferingTimeMs,
- preparingTimeMs,
- TunerDebug.getVideoFrameDrop()
- ));
+ Log.i(
+ TAG,
+ "Notify weak signal due to signal check, "
+ + String.format(
+ "packetsPerSec:%d, bufferingTimeMs:%d, preparingTimeMs:%d, "
+ + "videoFrameDrop:%d",
+ (limitInBytes - mLastLimitInBytes) / TS_PACKET_SIZE,
+ bufferingTimeMs,
+ preparingTimeMs,
+ TunerDebug.getVideoFrameDrop()));
} else if (!isWeakSignal && mReportedWeakSignal) {
- boolean isPlaybackStable = mReadyStartTimeMs != INVALID_TIME
- && currentTime - mReadyStartTimeMs
- > PLAYBACK_STATE_CHANGED_WAITING_THRESHOLD_MS;
+ boolean isPlaybackStable =
+ mReadyStartTimeMs != INVALID_TIME
+ && currentTime - mReadyStartTimeMs
+ > PLAYBACK_STATE_CHANGED_WAITING_THRESHOLD_MS;
if (!isPlaybackStable) {
// Wait until playback becomes stable.
} else if (mReportedDrawnToSurface) {
@@ -1083,32 +1145,34 @@ public class TunerSessionWorker implements PlaybackBufferListener,
mLastLimitInBytes = limitInBytes;
mHandler.sendEmptyMessageDelayed(MSG_CHECK_SIGNAL, CHECK_NO_SIGNAL_PERIOD_MS);
return true;
- }
- case MSG_SET_SURFACE: {
- if (mPlayer != null) {
- mPlayer.setSurface(mSurface);
- } else {
- // TODO: Since surface is dynamically set, we can remove the dependency of
- // playback start on mSurface nullity.
- resetPlayback();
+ case MSG_SET_SURFACE:
+ {
+ if (mPlayer != null) {
+ mPlayer.setSurface(mSurface);
+ } else {
+ // TODO: Since surface is dynamically set, we can remove the dependency of
+ // playback start on mSurface nullity.
+ resetPlayback();
+ }
+ return true;
+ }
+ case MSG_NOTIFY_AUDIO_TRACK_UPDATED:
+ {
+ notifyAudioTracksUpdated();
+ return true;
+ }
+ default:
+ {
+ Log.w(TAG, "Unhandled message code: " + msg.what);
+ return false;
}
- return true;
- }
- case MSG_NOTIFY_AUDIO_TRACK_UPDATED: {
- notifyAudioTracksUpdated();
- return true;
- }
- default: {
- Log.w(TAG, "Unhandled message code: " + msg.what);
- return false;
- }
}
}
// Private methods
private void doSelectTrack(int type, String trackId) {
- int numTrackId = trackId != null
- ? Integer.parseInt(trackId.substring(TRACK_PREFIX_SIZE)) : -1;
+ int numTrackId =
+ trackId != null ? Integer.parseInt(trackId.substring(TRACK_PREFIX_SIZE)) : -1;
if (type == TvTrackInfo.TYPE_AUDIO) {
if (trackId == null) {
return;
@@ -1138,14 +1202,13 @@ public class TunerSessionWorker implements PlaybackBufferListener,
}
private void setTrickplayEnabledIfNeeded() {
- if (mChannel == null ||
- mTrickplayModeCustomization != TvCustomizationManager.TRICKPLAY_MODE_ENABLED) {
+ if (mChannel == null
+ || mTrickplayModeCustomization != CustomizationManager.TRICKPLAY_MODE_ENABLED) {
return;
}
if (mTrickplaySetting == TunerPreferences.TRICKPLAY_SETTING_NOT_SET) {
mTrickplaySetting = TunerPreferences.TRICKPLAY_SETTING_ENABLED;
- TunerPreferences.setTrickplaySetting(
- mContext, mTrickplaySetting);
+ TunerPreferences.setTrickplaySetting(mContext, mTrickplaySetting);
}
}
@@ -1154,7 +1217,7 @@ public class TunerSessionWorker implements PlaybackBufferListener,
Log.w(TAG, "No Audio Capabilities");
}
long now = System.currentTimeMillis();
- if (mTrickplayModeCustomization == TvCustomizationManager.TRICKPLAY_MODE_ENABLED
+ if (mTrickplayModeCustomization == CustomizationManager.TRICKPLAY_MODE_ENABLED
&& mTrickplaySetting == TunerPreferences.TRICKPLAY_SETTING_NOT_SET) {
if (mTrickplayExpiredMs == 0) {
mTrickplayExpiredMs = now + TRICKPLAY_OFF_DURATION_MS;
@@ -1171,18 +1234,26 @@ public class TunerSessionWorker implements PlaybackBufferListener,
StorageManager storageManager =
new DvrStorageManager(new File(getRecordingPath()), false);
bufferManager = new BufferManager(storageManager);
- updateCaptionTracks(((DvrStorageManager)storageManager).readCaptionInfoFiles());
+ updateCaptionTracks(((DvrStorageManager) storageManager).readCaptionInfoFiles());
} else if (!mTrickplayDisabledByStorageIssue
&& mTrickplaySetting != TunerPreferences.TRICKPLAY_SETTING_DISABLED
&& mMaxTrickplayBufferSizeMb >= MIN_BUFFER_SIZE_DEF) {
- bufferManager = new BufferManager(new TrickplayStorageManager(mContext,
- mTrickplayBufferDir, 1024L * 1024 * mMaxTrickplayBufferSizeMb));
+ bufferManager =
+ new BufferManager(
+ new TrickplayStorageManager(
+ mContext,
+ mTrickplayBufferDir,
+ 1024L * 1024 * mMaxTrickplayBufferSizeMb));
} else {
Log.w(TAG, "Trickplay is disabled.");
}
- MpegTsPlayer player = new MpegTsPlayer(
- new MpegTsRendererBuilder(mContext, bufferManager, this),
- mHandler, mSourceManager, capabilities, this);
+ MpegTsPlayer player =
+ new MpegTsPlayer(
+ new MpegTsRendererBuilder(mContext, bufferManager, this),
+ mHandler,
+ mSourceManager,
+ capabilities,
+ this);
Log.i(TAG, "Passthrough AC3 renderer");
if (DEBUG) Log.d(TAG, "ExoPlayer created");
return player;
@@ -1190,8 +1261,7 @@ public class TunerSessionWorker implements PlaybackBufferListener,
private void startCaptionTrack() {
if (mCaptionEnabled && mCaptionTrack != null) {
- mSession.sendUiMessage(
- TunerSession.MSG_UI_START_CAPTION_TRACK, mCaptionTrack);
+ mSession.sendUiMessage(TunerSession.MSG_UI_START_CAPTION_TRACK, mCaptionTrack);
if (mPlayer != null) {
mPlayer.setCaptionServiceNumber(mCaptionTrack.serviceNumber);
}
@@ -1220,10 +1290,13 @@ public class TunerSessionWorker implements PlaybackBufferListener,
}
List<AtscAudioTrack> audioTracks = tvTracksInterface.getAudioTracks();
List<AtscCaptionTrack> captionTracks = tvTracksInterface.getCaptionTracks();
- // According to ATSC A/69 chapter 6.9, both PMT and EIT should have descriptors for audio
- // tracks, but in real world, we see some bogus audio track info in EIT, so, we trust audio
+ // According to ATSC A/69 chapter 6.9, both PMT and EIT should have descriptors for
+ // audio
+ // tracks, but in real world, we see some bogus audio track info in EIT, so, we trust
+ // audio
// track info in PMT more and use info in EIT only when we have nothing.
- if (audioTracks != null && !audioTracks.isEmpty()
+ if (audioTracks != null
+ && !audioTracks.isEmpty()
&& (mChannel == null || mChannel.getAudioTracks() == null || fromPmt)) {
updateAudioTracks(audioTracks);
}
@@ -1249,8 +1322,11 @@ public class TunerSessionWorker implements PlaybackBufferListener,
private void updateVideoTrack(int width, int height) {
removeTvTracks(TvTrackInfo.TYPE_VIDEO);
- mTvTracks.add(new TvTrackInfo.Builder(TvTrackInfo.TYPE_VIDEO, VIDEO_TRACK_ID)
- .setVideoWidth(width).setVideoHeight(height).build());
+ mTvTracks.add(
+ new TvTrackInfo.Builder(TvTrackInfo.TYPE_VIDEO, VIDEO_TRACK_ID)
+ .setVideoWidth(width)
+ .setVideoHeight(height)
+ .build());
mSession.notifyTracksChanged(mTvTracks);
mSession.notifyTrackSelected(TvTrackInfo.TYPE_VIDEO, VIDEO_TRACK_ID);
}
@@ -1284,16 +1360,22 @@ public class TunerSessionWorker implements PlaybackBufferListener,
com.google.android.exoplayer.MediaFormat infoFromPlayer =
mPlayer.getTrackFormat(MpegTsPlayer.TRACK_TYPE_AUDIO, i);
AtscAudioTrack infoFromEit = mAudioTrackMap.get(i);
- AtscAudioTrack infoFromVct = (mChannel != null
- && mChannel.getAudioTracks().size() == mAudioTrackMap.size()
- && i < mChannel.getAudioTracks().size())
- ? mChannel.getAudioTracks().get(i) : null;
- String language = !TextUtils.isEmpty(infoFromPlayer.language) ? infoFromPlayer.language
- : (infoFromEit != null && infoFromEit.language != null) ? infoFromEit.language
- : (infoFromVct != null && infoFromVct.language != null)
- ? infoFromVct.language : null;
- TvTrackInfo.Builder builder = new TvTrackInfo.Builder(
- TvTrackInfo.TYPE_AUDIO, AUDIO_TRACK_PREFIX + i);
+ AtscAudioTrack infoFromVct =
+ (mChannel != null
+ && mChannel.getAudioTracks().size() == mAudioTrackMap.size()
+ && i < mChannel.getAudioTracks().size())
+ ? mChannel.getAudioTracks().get(i)
+ : null;
+ String language =
+ !TextUtils.isEmpty(infoFromPlayer.language)
+ ? infoFromPlayer.language
+ : (infoFromEit != null && infoFromEit.language != null)
+ ? infoFromEit.language
+ : (infoFromVct != null && infoFromVct.language != null)
+ ? infoFromVct.language
+ : null;
+ TvTrackInfo.Builder builder =
+ new TvTrackInfo.Builder(TvTrackInfo.TYPE_AUDIO, AUDIO_TRACK_PREFIX + i);
builder.setLanguage(language);
builder.setAudioChannelCount(infoFromPlayer.channelCount);
builder.setAudioSampleRate(infoFromPlayer.sampleRate);
@@ -1319,7 +1401,8 @@ public class TunerSessionWorker implements PlaybackBufferListener,
// The service number of the caption service is used for track id of a subtitle.
// Later, when a subtitle is chosen, track id will be passed on to TsParser.
TvTrackInfo.Builder builder =
- new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE,
+ new TvTrackInfo.Builder(
+ TvTrackInfo.TYPE_SUBTITLE,
SUBTITLE_TRACK_PREFIX + captionTrack.serviceNumber);
builder.setLanguage(language);
mTvTracks.add(builder.build());
@@ -1331,9 +1414,13 @@ public class TunerSessionWorker implements PlaybackBufferListener,
private void updateChannelInfo(TunerChannel channel) {
if (DEBUG) {
- Log.d(TAG, String.format("Channel Info (old) videoPid: %d audioPid: %d " +
- "audioSize: %d", mChannel.getVideoPid(), mChannel.getAudioPid(),
- mChannel.getAudioPids().size()));
+ Log.d(
+ TAG,
+ String.format(
+ "Channel Info (old) videoPid: %d audioPid: %d " + "audioSize: %d",
+ mChannel.getVideoPid(),
+ mChannel.getAudioPid(),
+ mChannel.getAudioPids().size()));
}
// The list of the audio tracks resided in a channel is often changed depending on a
@@ -1356,19 +1443,22 @@ public class TunerSessionWorker implements PlaybackBufferListener,
}
}
mChannel.selectAudioTrack(index);
- mSession.notifyTrackSelected(TvTrackInfo.TYPE_AUDIO,
- index == -1 ? null : AUDIO_TRACK_PREFIX + index);
+ mSession.notifyTrackSelected(
+ TvTrackInfo.TYPE_AUDIO, index == -1 ? null : AUDIO_TRACK_PREFIX + index);
// Reset playback if there is a change in the listening streaming PIDs.
- if (oldVideoPid != mChannel.getVideoPid()
- || oldAudioPid != mChannel.getAudioPid()) {
+ if (oldVideoPid != mChannel.getVideoPid() || oldAudioPid != mChannel.getAudioPid()) {
// TODO: Implement a switching between tracks more smoothly.
resetPlayback();
}
if (DEBUG) {
- Log.d(TAG, String.format("Channel Info (new) videoPid: %d audioPid: %d " +
- " audioSize: %d", mChannel.getVideoPid(), mChannel.getAudioPid(),
- mChannel.getAudioPids().size()));
+ Log.d(
+ TAG,
+ String.format(
+ "Channel Info (new) videoPid: %d audioPid: %d " + " audioSize: %d",
+ mChannel.getVideoPid(),
+ mChannel.getAudioPid(),
+ mChannel.getAudioPids().size()));
}
}
@@ -1405,9 +1495,10 @@ public class TunerSessionWorker implements PlaybackBufferListener,
notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN);
return;
}
- if (mChannel != null && ((mChannel.hasAudio() && !mPlayer.hasAudio())
- || (mChannel.hasVideo() && !mPlayer.hasVideo()))
- && mChannel.getType() != Channel.TYPE_NETWORK) {
+ if (mChannel != null
+ && ((mChannel.hasAudio() && !mPlayer.hasAudio())
+ || (mChannel.hasVideo() && !mPlayer.hasVideo()))
+ && mChannel.getType() != Channel.TunerType.TYPE_NETWORK) {
// If the channel is from network, skip this part since the video and audio tracks
// information for channels from network are more reliable in the extractor. Otherwise,
// tracks haven't been detected in the extractor. Try again.
@@ -1441,8 +1532,10 @@ public class TunerSessionWorker implements PlaybackBufferListener,
MpegTsPlayer player = createPlayer(mAudioCapabilities);
player.setCaptionServiceNumber(Cea708Data.EMPTY_SERVICE_NUMBER);
player.setVideoEventListener(this);
- player.setCaptionServiceNumber(mCaptionTrack != null ?
- mCaptionTrack.serviceNumber : Cea708Data.EMPTY_SERVICE_NUMBER);
+ player.setCaptionServiceNumber(
+ mCaptionTrack != null
+ ? mCaptionTrack.serviceNumber
+ : Cea708Data.EMPTY_SERVICE_NUMBER);
if (!player.prepare(mContext, mChannel, mHasSoftwareAudioDecoder, this)) {
mSourceManager.setKeepTuneStatus(false);
player.release();
@@ -1451,8 +1544,10 @@ public class TunerSessionWorker implements PlaybackBufferListener,
// case, retry playback immediately may not help.
notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL);
Log.i(TAG, "Notify weak signal due to player preparation failure");
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RETRY_PLAYBACK,
- System.identityHashCode(mPlayer)), PLAYBACK_RETRY_DELAY_MS);
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(
+ MSG_RETRY_PLAYBACK, System.identityHashCode(mPlayer)),
+ PLAYBACK_RETRY_DELAY_MS);
}
} else {
mPlayer = player;
@@ -1463,7 +1558,8 @@ public class TunerSessionWorker implements PlaybackBufferListener,
}
private void resetPlayback() {
- long timestamp, oldTimestamp;
+ long timestamp;
+ long oldTimestamp;
timestamp = SystemClock.elapsedRealtime();
stopPlayback(false);
stopCaptionTrack();
@@ -1500,8 +1596,8 @@ public class TunerSessionWorker implements PlaybackBufferListener,
private void doReschedulePrograms() {
long currentPositionMs = getCurrentPosition();
- long forwardDifference = Math.abs(currentPositionMs - mLastPositionMs
- - RESCHEDULE_PROGRAMS_INTERVAL_MS);
+ long forwardDifference =
+ Math.abs(currentPositionMs - mLastPositionMs - RESCHEDULE_PROGRAMS_INTERVAL_MS);
mLastPositionMs = currentPositionMs;
// A gap is measured as the time difference between previous and next current position
@@ -1510,23 +1606,27 @@ public class TunerSessionWorker implements PlaybackBufferListener,
// channel should be rescheduled to new playback timeline.
if (forwardDifference > RESCHEDULE_PROGRAMS_TOLERANCE_MS) {
if (DEBUG) {
- Log.d(TAG, "reschedule programs size:"
- + (mPrograms != null ? mPrograms.size() : 0) + " current program: "
- + getCurrentProgram());
+ Log.d(
+ TAG,
+ "reschedule programs size:"
+ + (mPrograms != null ? mPrograms.size() : 0)
+ + " current program: "
+ + getCurrentProgram());
}
mHandler.obtainMessage(MSG_SCHEDULE_OF_PROGRAMS, new Pair<>(mChannel, mPrograms))
.sendToTarget();
}
mHandler.removeMessages(MSG_RESCHEDULE_PROGRAMS);
- mHandler.sendEmptyMessageDelayed(MSG_RESCHEDULE_PROGRAMS,
- RESCHEDULE_PROGRAMS_INTERVAL_MS);
+ mHandler.sendEmptyMessageDelayed(MSG_RESCHEDULE_PROGRAMS, RESCHEDULE_PROGRAMS_INTERVAL_MS);
}
private int getTrickPlaySeekIntervalMs() {
- return Math.max(EXPECTED_KEY_FRAME_INTERVAL_MS / (int) Math.abs(mPlaybackParams.getSpeed()),
+ return Math.max(
+ EXPECTED_KEY_FRAME_INTERVAL_MS / (int) Math.abs(mPlaybackParams.getSpeed()),
MIN_TRICKPLAY_SEEK_INTERVAL_MS);
}
+ @SuppressWarnings("NarrowingCompoundAssignment")
private void doTrickplayBySeek(int seekPositionMs) {
mHandler.removeMessages(MSG_TRICKPLAY_BY_SEEK);
if (mPlaybackParams.getSpeed() == 1.0f || !mPlayer.isPrepared()) {
@@ -1562,8 +1662,8 @@ public class TunerSessionWorker implements PlaybackBufferListener,
delayForNextSeek = MIN_TRICKPLAY_SEEK_INTERVAL_MS;
}
seekPositionMs += mPlaybackParams.getSpeed() * delayForNextSeek;
- mHandler.sendMessageDelayed(mHandler.obtainMessage(
- MSG_TRICKPLAY_BY_SEEK, seekPositionMs, 0), delayForNextSeek);
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(MSG_TRICKPLAY_BY_SEEK, seekPositionMs, 0), delayForNextSeek);
}
private void doTimeShiftPause() {
@@ -1605,17 +1705,21 @@ public class TunerSessionWorker implements PlaybackBufferListener,
mHandler.removeMessages(MSG_TRICKPLAY_BY_SEEK);
mPlayer.setAudioTrackAndClosedCaption(false);
mPlayer.startSmoothTrickplay(mPlaybackParams);
- mHandler.sendEmptyMessageDelayed(MSG_SMOOTH_TRICKPLAY_MONITOR,
- TRICKPLAY_MONITOR_INTERVAL_MS);
+ mHandler.sendEmptyMessageDelayed(
+ MSG_SMOOTH_TRICKPLAY_MONITOR, TRICKPLAY_MONITOR_INTERVAL_MS);
} else {
mHandler.removeMessages(MSG_SMOOTH_TRICKPLAY_MONITOR);
if (!mHandler.hasMessages(MSG_TRICKPLAY_BY_SEEK)) {
mPlayer.setAudioTrackAndClosedCaption(false);
mPlayer.setPlayWhenReady(false);
// Initiate trickplay
- mHandler.sendMessage(mHandler.obtainMessage(MSG_TRICKPLAY_BY_SEEK,
- (int) (mPlayer.getCurrentPosition()
- + speed * getTrickPlaySeekIntervalMs()), 0));
+ mHandler.sendMessage(
+ mHandler.obtainMessage(
+ MSG_TRICKPLAY_BY_SEEK,
+ (int)
+ (mPlayer.getCurrentPosition()
+ + speed * getTrickPlaySeekIntervalMs()),
+ 0));
}
}
}
@@ -1624,11 +1728,12 @@ public class TunerSessionWorker implements PlaybackBufferListener,
if (mPrograms == null || mPrograms.isEmpty()) {
return null;
}
- if (mChannel.getType() == Channel.TYPE_FILE) {
+ if (mChannel.getType() == Channel.TunerType.TYPE_FILE) {
// For the playback from the local file, we use the first one from the given program.
EitItem first = mPrograms.get(0);
- if (first != null && (mProgram == null
- || first.getStartTimeUtcMillis() < mProgram.getStartTimeUtcMillis())) {
+ if (first != null
+ && (mProgram == null
+ || first.getStartTimeUtcMillis() < mProgram.getStartTimeUtcMillis())) {
return first;
}
return null;
@@ -1649,8 +1754,10 @@ public class TunerSessionWorker implements PlaybackBufferListener,
TvContentRating blockContentRating = getContentRatingOfCurrentProgramBlocked();
if (DEBUG) {
if (blockContentRating != null) {
- Log.d(TAG, "Check parental controls: blocked by content rating - "
- + blockContentRating);
+ Log.d(
+ TAG,
+ "Check parental controls: blocked by content rating - "
+ + blockContentRating);
} else {
Log.d(TAG, "Check parental controls: available");
}
@@ -1672,8 +1779,11 @@ public class TunerSessionWorker implements PlaybackBufferListener,
captionTrack.wideAspectRatio = false;
captionTrack.easyReader = false;
mCaptionTrackMap.put(serviceNumber, captionTrack);
- mTvTracks.add(new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE,
- SUBTITLE_TRACK_PREFIX + serviceNumber).build());
+ mTvTracks.add(
+ new TvTrackInfo.Builder(
+ TvTrackInfo.TYPE_SUBTITLE,
+ SUBTITLE_TRACK_PREFIX + serviceNumber)
+ .build());
mSession.notifyTracksChanged(mTvTracks);
}
}
@@ -1683,22 +1793,21 @@ public class TunerSessionWorker implements PlaybackBufferListener,
if (currentProgram == null) {
return null;
}
- TvContentRating[] ratings = mTvContentRatingCache
- .getRatings(currentProgram.getContentRating());
+ TvContentRating[] ratings =
+ mTvContentRatingCache.getRatings(currentProgram.getContentRating());
if (ratings == null || ratings.length == 0) {
ratings = new TvContentRating[] {TvContentRating.UNRATED};
}
for (TvContentRating rating : ratings) {
- if (!Objects.equals(mUnblockedContentRating, rating) && mTvInputManager
- .isRatingBlocked(rating)) {
+ if (!Objects.equals(mUnblockedContentRating, rating)
+ && mTvInputManager.isRatingBlocked(rating)) {
return rating;
}
}
return null;
}
- private void updateChannelBlockStatus(boolean channelBlocked,
- TvContentRating contentRating) {
+ private void updateChannelBlockStatus(boolean channelBlocked, TvContentRating contentRating) {
if (mChannelBlocked == channelBlocked) {
return;
}
@@ -1715,8 +1824,8 @@ public class TunerSessionWorker implements PlaybackBufferListener,
clearCallbacksAndMessagesSafely();
resetPlayback();
mSession.notifyContentAllowed();
- mHandler.sendEmptyMessageDelayed(MSG_RESCHEDULE_PROGRAMS,
- RESCHEDULE_PROGRAMS_INITIAL_DELAY_MS);
+ mHandler.sendEmptyMessageDelayed(
+ MSG_RESCHEDULE_PROGRAMS, RESCHEDULE_PROGRAMS_INITIAL_DELAY_MS);
mHandler.removeMessages(MSG_CHECK_SIGNAL);
mHandler.sendEmptyMessageDelayed(MSG_CHECK_SIGNAL, CHECK_NO_SIGNAL_INITIAL_DELAY_MS);
}
diff --git a/src/com/android/tv/tuner/tvinput/TunerStorageCleanUpService.java b/tuner/src/com/android/tv/tuner/tvinput/TunerStorageCleanUpService.java
index 6ad00daa..cdcc00d5 100644
--- a/src/com/android/tv/tuner/tvinput/TunerStorageCleanUpService.java
+++ b/tuner/src/com/android/tv/tuner/tvinput/TunerStorageCleanUpService.java
@@ -25,11 +25,9 @@ import android.media.tv.TvContract;
import android.net.Uri;
import android.os.AsyncTask;
import android.util.Log;
-
-import com.android.tv.TvApplication;
-import com.android.tv.dvr.DvrStorageStatusManager;
-import com.android.tv.util.Utils;
-
+import com.android.tv.common.BaseApplication;
+import com.android.tv.common.recording.RecordingStorageStatusManager;
+import com.android.tv.common.util.CommonUtils;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
@@ -37,8 +35,8 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
- * Creates {@link JobService} to clean up recorded program files which are not referenced
- * from database.
+ * Creates {@link JobService} to clean up recorded program files which are not referenced from
+ * database.
*/
public class TunerStorageCleanUpService extends JobService {
private static final String TAG = "TunerStorageCleanUpService";
@@ -47,12 +45,11 @@ public class TunerStorageCleanUpService extends JobService {
@Override
public void onCreate() {
- if (!TvApplication.getSingletons(this).getTvInputManagerHelper().hasTvInputManager()) {
+ if (getApplicationContext().getSystemService(Context.TV_INPUT_SERVICE) == null) {
Log.wtf(TAG, "Stopping because device does not have a TvInputManager");
this.stopSelf();
return;
}
- TvApplication.setCurrentRunningProcess(this, false);
super.onCreate();
mTask = new CleanUpStorageTask(this, this);
}
@@ -69,18 +66,18 @@ public class TunerStorageCleanUpService extends JobService {
}
/**
- * Cleans up recorded program files which are not referenced from database.
- * Cleaning up will be done periodically.
+ * Cleans up recorded program files which are not referenced from database. Cleaning up will be
+ * done periodically.
*/
public static class CleanUpStorageTask extends AsyncTask<JobParameters, Void, JobParameters[]> {
- private final static String[] mProjection = {
- TvContract.RecordedPrograms.COLUMN_PACKAGE_NAME,
- TvContract.RecordedPrograms.COLUMN_RECORDING_DATA_URI
+ private static final String[] mProjection = {
+ TvContract.RecordedPrograms.COLUMN_PACKAGE_NAME,
+ TvContract.RecordedPrograms.COLUMN_RECORDING_DATA_URI
};
- private final static long ELAPSED_MILLIS_TO_DELETE = TimeUnit.DAYS.toMillis(1);
+ private static final long ELAPSED_MILLIS_TO_DELETE = TimeUnit.DAYS.toMillis(1);
private final Context mContext;
- private final DvrStorageStatusManager mDvrStorageStatusManager;
+ private final RecordingStorageStatusManager mDvrStorageStatusManager;
private final JobService mJobService;
private final ContentResolver mContentResolver;
@@ -93,14 +90,19 @@ public class TunerStorageCleanUpService extends JobService {
public CleanUpStorageTask(Context context, JobService jobService) {
mContext = context;
mDvrStorageStatusManager =
- TvApplication.getSingletons(mContext).getDvrStorageStatusManager();
+ BaseApplication.getSingletons(context).getRecordingStorageStatusManager();
mJobService = jobService;
mContentResolver = mContext.getContentResolver();
}
private Set<String> getRecordedProgramsDirs() {
- try (Cursor c = mContentResolver.query(
- TvContract.RecordedPrograms.CONTENT_URI, mProjection, null, null, null)) {
+ try (Cursor c =
+ mContentResolver.query(
+ TvContract.RecordedPrograms.CONTENT_URI,
+ mProjection,
+ null,
+ null,
+ null)) {
if (c == null) {
return null;
}
@@ -112,8 +114,9 @@ public class TunerStorageCleanUpService extends JobService {
continue;
}
Uri dataUri = Uri.parse(dataUriString);
- if (!Utils.isInBundledPackageSet(packageName)
- || dataUri == null || dataUri.getPath() == null
+ if (!CommonUtils.isInBundledPackageSet(packageName)
+ || dataUri == null
+ || dataUri.getPath() == null
|| !ContentResolver.SCHEME_FILE.equals(dataUri.getScheme())) {
continue;
}
@@ -130,7 +133,7 @@ public class TunerStorageCleanUpService extends JobService {
@Override
protected JobParameters[] doInBackground(JobParameters... params) {
if (mDvrStorageStatusManager.getDvrStorageStatus()
- == DvrStorageStatusManager.STORAGE_STATUS_MISSING) {
+ == RecordingStorageStatusManager.STORAGE_STATUS_MISSING) {
return params;
}
File dvrRecordingDir = mDvrStorageStatusManager.getRecordingRootDataDirectory();
@@ -150,11 +153,10 @@ public class TunerStorageCleanUpService extends JobService {
if (!recordedProgramDirs.contains(recordingDir.getCanonicalPath())) {
long lastModified = recordingDir.lastModified();
long now = System.currentTimeMillis();
- if (lastModified != 0
- && lastModified < now - ELAPSED_MILLIS_TO_DELETE) {
+ if (lastModified != 0 && lastModified < now - ELAPSED_MILLIS_TO_DELETE) {
// To prevent current recordings from being deleted,
// deletes recordings which was not modified for long enough time.
- Utils.deleteDirOrFile(recordingDir);
+ CommonUtils.deleteDirOrFile(recordingDir);
}
}
} catch (IOException | SecurityException e) {
diff --git a/src/com/android/tv/tuner/util/ByteArrayBuffer.java b/tuner/src/com/android/tv/tuner/util/ByteArrayBuffer.java
index da887e7d..00ba039a 100644
--- a/src/com/android/tv/tuner/util/ByteArrayBuffer.java
+++ b/tuner/src/com/android/tv/tuner/util/ByteArrayBuffer.java
@@ -27,13 +27,14 @@
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
+ * For the license checker
+ * Licensed under the Apache License, Version 2.0
+ *
*/
package com.android.tv.tuner.util;
-/**
- * An expandable byte buffer built on byte array.
- */
+/** An expandable byte buffer built on byte array. */
public final class ByteArrayBuffer {
private byte[] buffer;
@@ -57,8 +58,11 @@ public final class ByteArrayBuffer {
if (b == null) {
return;
}
- if ((off < 0) || (off > b.length) || (len < 0) ||
- ((off + len) < 0) || ((off + len) > b.length)) {
+ if ((off < 0)
+ || (off > b.length)
+ || (len < 0)
+ || ((off + len) < 0)
+ || ((off + len) > b.length)) {
throw new IndexOutOfBoundsException();
}
if (len == 0) {
@@ -85,8 +89,11 @@ public final class ByteArrayBuffer {
if (b == null) {
return;
}
- if ((off < 0) || (off > b.length) || (len < 0) ||
- ((off + len) < 0) || ((off + len) > b.length)) {
+ if ((off < 0)
+ || (off > b.length)
+ || (len < 0)
+ || ((off + len) < 0)
+ || ((off + len) > b.length)) {
throw new IndexOutOfBoundsException();
}
if (len == 0) {
@@ -145,5 +152,4 @@ public final class ByteArrayBuffer {
public boolean isFull() {
return this.len == this.buffer.length;
}
-
}
diff --git a/src/com/android/tv/tuner/util/ConvertUtils.java b/tuner/src/com/android/tv/tuner/util/ConvertUtils.java
index abf18d8c..4b7fbdae 100644
--- a/src/com/android/tv/tuner/util/ConvertUtils.java
+++ b/tuner/src/com/android/tv/tuner/util/ConvertUtils.java
@@ -16,14 +16,12 @@
package com.android.tv.tuner.util;
-/**
- * Utility class for converting date and time.
- */
+/** Utility class for converting date and time. */
public class ConvertUtils {
// Time diff between 1.1.1970 00:00:00 and 6.1.1980 00:00:00
private static final long DIFF_BETWEEN_UNIX_EPOCH_AND_GPS = 315964800;
- private ConvertUtils() { }
+ private ConvertUtils() {}
public static long convertGPSTimeToUnixEpoch(long gpsTime) {
return gpsTime + DIFF_BETWEEN_UNIX_EPOCH_AND_GPS;
diff --git a/src/com/android/tv/tuner/util/GlobalSettingsUtils.java b/tuner/src/com/android/tv/tuner/util/GlobalSettingsUtils.java
index 0cefcbed..98463f3b 100644
--- a/src/com/android/tv/tuner/util/GlobalSettingsUtils.java
+++ b/tuner/src/com/android/tv/tuner/util/GlobalSettingsUtils.java
@@ -19,16 +19,14 @@ package com.android.tv.tuner.util;
import android.content.Context;
import android.provider.Settings;
-/**
- * Utility class that get information of global settings.
- */
+/** Utility class that get information of global settings. */
public class GlobalSettingsUtils {
// Since global surround setting is hided, add the related variable here for checking surround
// sound setting when the audio is unavailable. Remove this workaround after b/31254857 fixed.
private static final String ENCODED_SURROUND_OUTPUT = "encoded_surround_output";
public static final int ENCODED_SURROUND_OUTPUT_NEVER = 1;
- private GlobalSettingsUtils () { }
+ private GlobalSettingsUtils() {}
public static int getEncodedSurroundOutputSettings(Context context) {
return Settings.Global.getInt(context.getContentResolver(), ENCODED_SURROUND_OUTPUT, 0);
diff --git a/tuner/src/com/android/tv/tuner/util/Ints.java b/tuner/src/com/android/tv/tuner/util/Ints.java
new file mode 100644
index 00000000..22c6bbb9
--- /dev/null
+++ b/tuner/src/com/android/tv/tuner/util/Ints.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 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.tuner.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Static utility methods pertaining to int primitives. (Referred Guava's Ints class) */
+public class Ints {
+ private Ints() {}
+
+ public static int[] toArray(List<Integer> integerList) {
+ int[] intArray = new int[integerList.size()];
+ int i = 0;
+ for (Integer data : integerList) {
+ intArray[i++] = data;
+ }
+ return intArray;
+ }
+
+ public static List<Integer> asList(int[] intArray) {
+ List<Integer> integerList = new ArrayList<>(intArray.length);
+ for (int data : intArray) {
+ integerList.add(data);
+ }
+ return integerList;
+ }
+}
diff --git a/src/com/android/tv/tuner/util/StatusTextUtils.java b/tuner/src/com/android/tv/tuner/util/StatusTextUtils.java
index 2633834b..84e2fc5a 100644
--- a/src/com/android/tv/tuner/util/StatusTextUtils.java
+++ b/tuner/src/com/android/tv/tuner/util/StatusTextUtils.java
@@ -18,9 +18,7 @@ package com.android.tv.tuner.util;
import java.util.Locale;
-/**
- * Utility class for tuner status messages.
- */
+/** Utility class for tuner status messages. */
public class StatusTextUtils {
private static final int PACKETS_PER_SEC_YELLOW = 1500;
private static final int PACKETS_PER_SEC_RED = 1000;
@@ -31,18 +29,23 @@ public class StatusTextUtils {
private static final String COLOR_GREEN = "green";
private static final String COLOR_GRAY = "gray";
- private StatusTextUtils() { }
+ private StatusTextUtils() {}
/**
* Returns tuner status warning message in HTML.
*
- * <p>This is only called for debuging and always shown in english.</p>
+ * <p>This is only called for debuging and always shown in english.
*/
- public static String getStatusWarningInHTML(long packetsPerSec,
- int videoFrameDrop, int bytesInQueue,
- long audioPositionUs, long audioPositionUsRate,
- long audioPtsUs, long audioPtsUsRate,
- long videoPtsUs, long videoPtsUsRate) {
+ public static String getStatusWarningInHTML(
+ long packetsPerSec,
+ int videoFrameDrop,
+ int bytesInQueue,
+ long audioPositionUs,
+ long audioPositionUsRate,
+ long audioPtsUs,
+ long audioPtsUsRate,
+ long videoPtsUs,
+ long videoPtsUsRate) {
StringBuffer buffer = new StringBuffer();
// audioPosition should go in rate of 1000ms.
@@ -57,34 +60,49 @@ public class StatusTextUtils {
}
buffer.append(String.format(Locale.US, "<font color=%s>", audioPositionColor));
buffer.append(
- String.format(Locale.US, "audioPositionMs: %d (%d)<br>", audioPositionUs / 1000,
+ String.format(
+ Locale.US,
+ "audioPositionMs: %d (%d)<br>",
+ audioPositionUs / 1000,
audioPositionMsRate));
buffer.append("</font>\n");
buffer.append("<font color=" + COLOR_GRAY + ">");
- buffer.append(String.format(Locale.US, "audioPtsMs: %d (%d, %d)<br>", audioPtsUs / 1000,
- audioPtsUsRate / 1000, (audioPtsUs - audioPositionUs) / 1000));
- buffer.append(String.format(Locale.US, "videoPtsMs: %d (%d, %d)<br>", videoPtsUs / 1000,
- videoPtsUsRate / 1000, (videoPtsUs - audioPositionUs) / 1000));
+ buffer.append(
+ String.format(
+ Locale.US,
+ "audioPtsMs: %d (%d, %d)<br>",
+ audioPtsUs / 1000,
+ audioPtsUsRate / 1000,
+ (audioPtsUs - audioPositionUs) / 1000));
+ buffer.append(
+ String.format(
+ Locale.US,
+ "videoPtsMs: %d (%d, %d)<br>",
+ videoPtsUs / 1000,
+ videoPtsUsRate / 1000,
+ (videoPtsUs - audioPositionUs) / 1000));
buffer.append("</font>\n");
appendStatusLine(buffer, "KbytesInQueue", bytesInQueue / 1000, 1, 10);
buffer.append("<br/>");
appendErrorStatusLine(buffer, "videoFrameDrop", videoFrameDrop, 0, 2);
buffer.append("<br/>");
- appendStatusLine(buffer, "packetsPerSec", packetsPerSec, PACKETS_PER_SEC_RED,
+ appendStatusLine(
+ buffer,
+ "packetsPerSec",
+ packetsPerSec,
+ PACKETS_PER_SEC_RED,
PACKETS_PER_SEC_YELLOW);
return buffer.toString();
}
- /**
- * Returns audio unavailable warning message in HTML.
- */
+ /** Returns audio unavailable warning message in HTML. */
public static String getAudioWarningInHTML(String msg) {
return String.format("<font color=%s>%s</font>\n", COLOR_YELLOW, msg);
}
- private static void appendStatusLine(StringBuffer buffer, String factorName, long value,
- int minRed, int minYellow) {
+ private static void appendStatusLine(
+ StringBuffer buffer, String factorName, long value, int minRed, int minYellow) {
buffer.append("<font color=");
if (value <= minRed) {
buffer.append(COLOR_RED);
@@ -100,8 +118,8 @@ public class StatusTextUtils {
buffer.append("</font>");
}
- private static void appendErrorStatusLine(StringBuffer buffer, String factorName, int value,
- int minGreen, int minYellow) {
+ private static void appendErrorStatusLine(
+ StringBuffer buffer, String factorName, int value, int minGreen, int minYellow) {
buffer.append("<font color=");
if (value <= minGreen) {
buffer.append(COLOR_GREEN);
diff --git a/src/com/android/tv/tuner/util/TunerInputInfoUtils.java b/tuner/src/com/android/tv/tuner/util/TunerInputInfoUtils.java
index f421bf1a..fad71335 100644
--- a/src/com/android/tv/tuner/util/TunerInputInfoUtils.java
+++ b/tuner/src/com/android/tv/tuner/util/TunerInputInfoUtils.java
@@ -26,22 +26,18 @@ import android.os.Build;
import android.support.annotation.Nullable;
import android.util.Log;
import android.util.Pair;
-
+import com.android.tv.common.BaseApplication;
+import com.android.tv.common.BuildConfig;
import com.android.tv.common.feature.CommonFeatures;
import com.android.tv.tuner.R;
import com.android.tv.tuner.TunerHal;
-import com.android.tv.tuner.tvinput.TunerTvInputService;
-/**
- * Utility class for providing tuner input info.
- */
+/** Utility class for providing tuner input info. */
public class TunerInputInfoUtils {
private static final String TAG = "TunerInputInfoUtils";
private static final boolean DEBUG = false;
- /**
- * Builds tuner input's info.
- */
+ /** Builds tuner input's info. */
@Nullable
@TargetApi(Build.VERSION_CODES.N)
public static TvInputInfo buildTunerInputInfo(Context context) {
@@ -62,14 +58,15 @@ public class TunerInputInfoUtils {
break;
}
try {
- TvInputInfo.Builder builder = new TvInputInfo.Builder(context,
- new ComponentName(context, TunerTvInputService.class));
+ String inputId = BaseApplication.getSingletons(context).getEmbeddedTunerInputId();
+ TvInputInfo.Builder builder =
+ new TvInputInfo.Builder(context, ComponentName.unflattenFromString(inputId));
return builder.setLabel(inputLabelId)
.setCanRecord(CommonFeatures.DVR.isEnabled(context))
.setTunerCount(tunerTypeAndCount.second)
.build();
} catch (IllegalArgumentException | NullPointerException e) {
- // TunerTvInputService is not enabled.
+ // BaseTunerTvInputService is not enabled.
return null;
}
}
@@ -81,7 +78,7 @@ public class TunerInputInfoUtils {
*/
public static void updateTunerInputInfo(Context context) {
final Context appContext = context.getApplicationContext();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ if (!BuildConfig.NO_JNI_TEST && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
new AsyncTask<Void, Void, TvInputInfo>() {
@Override
protected TvInputInfo doInBackground(Void... params) {
@@ -112,4 +109,4 @@ public class TunerInputInfoUtils {
}.execute();
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/exoplayer/MediaFormatUtil.java b/tuner/src/com/google/android/exoplayer/MediaFormatUtil.java
index d7a981f6..c7571b23 100644
--- a/src/com/android/exoplayer/MediaFormatUtil.java
+++ b/tuner/src/com/google/android/exoplayer/MediaFormatUtil.java
@@ -16,9 +16,7 @@
package com.google.android.exoplayer;
import android.support.annotation.Nullable;
-
import com.google.android.exoplayer.util.MimeTypes;
-
import java.nio.ByteBuffer;
import java.util.ArrayList;
@@ -26,9 +24,9 @@ import java.util.ArrayList;
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.
+ * 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) {
String mimeType = format.getString(android.media.MediaFormat.KEY_MIME);
@@ -51,15 +49,38 @@ public class MediaFormatUtil {
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;
- int pcmEncoding = MimeTypes.AUDIO_RAW.equals(mimeType) ? C.ENCODING_PCM_16BIT
- : MediaFormat.NO_VALUE;
- 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, pcmEncoding,
- encoderDelay, encoderPadding, null, MediaFormat.NO_VALUE);
+ long durationUs =
+ format.containsKey(android.media.MediaFormat.KEY_DURATION)
+ ? format.getLong(android.media.MediaFormat.KEY_DURATION)
+ : C.UNKNOWN_TIME_US;
+ int pcmEncoding =
+ MimeTypes.AUDIO_RAW.equals(mimeType) ? C.ENCODING_PCM_16BIT : MediaFormat.NO_VALUE;
+ MediaFormat mediaFormat =
+ new MediaFormat(
+ null, // trackId
+ 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,
+ pcmEncoding,
+ encoderDelay,
+ encoderPadding,
+ null, // projectionData
+ MediaFormat.NO_VALUE,
+ null // colorValue
+ );
mediaFormat.setFrameworkFormatV16(format);
return mediaFormat;
}
@@ -72,5 +93,4 @@ public class MediaFormatUtil {
private static int getOptionalIntegerV16(android.media.MediaFormat format, String key) {
return format.containsKey(key) ? format.getInteger(key) : MediaFormat.NO_VALUE;
}
-
}
diff --git a/src/com/android/exoplayer/MediaSoftwareCodecUtil.java b/tuner/src/com/google/android/exoplayer/MediaSoftwareCodecUtil.java
index 8c2509d4..cf74f106 100644
--- a/src/com/android/exoplayer/MediaSoftwareCodecUtil.java
+++ b/tuner/src/com/google/android/exoplayer/MediaSoftwareCodecUtil.java
@@ -21,9 +21,7 @@ import android.media.MediaCodecList;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
-
import com.google.android.exoplayer.util.MimeTypes;
-
import java.util.HashMap;
/**
@@ -35,8 +33,8 @@ public class 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
+ *
+ * <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 {
@@ -44,15 +42,12 @@ public class MediaSoftwareCodecUtil {
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.
- */
+ /** 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.
@@ -64,11 +59,10 @@ public class MediaSoftwareCodecUtil {
return new DecoderInfo(info.first, info.second);
}
- /**
- * Returns the name of the software decoder and its capabilities for the given mimeType.
- */
+ /** 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 {
+ getMediaSoftwareCodecInfo(String mimeType, boolean secure)
+ throws DecoderQueryException {
CodecKey key = new CodecKey(mimeType, secure);
if (sSwCodecs.containsKey(key)) {
return sSwCodecs.get(key);
@@ -81,8 +75,12 @@ public class MediaSoftwareCodecUtil {
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);
+ Log.w(
+ TAG,
+ "MediaCodecList API didn't list secure decoder for: "
+ + mimeType
+ + ". Assuming: "
+ + codecInfo.first);
}
}
return codecInfo;
@@ -108,29 +106,33 @@ public class MediaSoftwareCodecUtil {
for (int i = 0; i < numberOfCodecs; i++) {
MediaCodecInfo info = mediaCodecList.getCodecInfoAt(i);
String codecName = info.getName();
- if (!info.isEncoder() && codecName.startsWith("OMX.google.")
+ if (!info.isEncoder()
+ && codecName.startsWith("OMX.google.")
&& (secureDecodersExplicit || !codecName.endsWith(".secure"))) {
String[] supportedTypes = info.getSupportedTypes();
for (String supportedType : supportedTypes) {
if (supportedType.equalsIgnoreCase(mimeType)) {
MediaCodecInfo.CodecCapabilities capabilities =
info.getCapabilitiesForType(supportedType);
- boolean secure = mediaCodecList.isSecurePlaybackSupported(
- key.mimeType, capabilities);
+ 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,
+ sSwCodecs.put(
+ key.secure ? new CodecKey(mimeType, false) : key,
Pair.create(codecName, capabilities));
if (secure) {
- sSwCodecs.put(key.secure ? key : new CodecKey(mimeType, true),
+ 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),
+ key.secure == secure ? key : new CodecKey(mimeType, secure),
Pair.create(codecName, capabilities));
}
if (sSwCodecs.containsKey(key)) {
@@ -146,9 +148,7 @@ public class MediaSoftwareCodecUtil {
private interface MediaCodecListCompat {
- /**
- * Returns the number of codecs in the list.
- */
+ /** Returns the number of codecs in the list. */
int getCodecCount();
/**
@@ -158,19 +158,16 @@ public class MediaSoftwareCodecUtil {
*/
MediaCodecInfo getCodecInfoAt(int index);
- /**
- * Returns whether secure decoders are explicitly listed, if present.
- */
+ /** Returns whether secure decoders are explicitly listed, if present. */
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.
+ * 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.
*/
- boolean isSecurePlaybackSupported(String mimeType,
- MediaCodecInfo.CodecCapabilities capabilities);
-
+ boolean isSecurePlaybackSupported(
+ String mimeType, MediaCodecInfo.CodecCapabilities capabilities);
}
@TargetApi(21)
@@ -202,8 +199,8 @@ public class MediaSoftwareCodecUtil {
}
@Override
- public boolean isSecurePlaybackSupported(String mimeType,
- MediaCodecInfo.CodecCapabilities capabilities) {
+ public boolean isSecurePlaybackSupported(
+ String mimeType, MediaCodecInfo.CodecCapabilities capabilities) {
return capabilities.isFeatureSupported(
MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback);
}
@@ -213,7 +210,6 @@ public class MediaSoftwareCodecUtil {
mediaCodecInfos = new MediaCodecList(codecKind).getCodecInfos();
}
}
-
}
@SuppressWarnings("deprecation")
@@ -235,13 +231,12 @@ public class MediaSoftwareCodecUtil {
}
@Override
- public boolean isSecurePlaybackSupported(String mimeType,
- MediaCodecInfo.CodecCapabilities capabilities) {
+ 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 {
@@ -274,7 +269,5 @@ public class MediaSoftwareCodecUtil {
CodecKey other = (CodecKey) obj;
return TextUtils.equals(mimeType, other.mimeType) && secure == other.secure;
}
-
}
-
}
diff --git a/tuner/src/com/mediatek/tunerservice/IMtkTuner.aidl b/tuner/src/com/mediatek/tunerservice/IMtkTuner.aidl
new file mode 100644
index 00000000..b15c736a
--- /dev/null
+++ b/tuner/src/com/mediatek/tunerservice/IMtkTuner.aidl
@@ -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.mediatek.tunerservice;
+
+interface IMtkTuner {
+ boolean tune(int frequency, String modulation, int timeOutMs);
+ void addPidFilter(int pid, int filterType);
+ void closeAllPidFilters();
+ void stopTune();
+ byte[] getTsData(int maxDataSize, int timeOutMs);
+ void setHasPendingTune(boolean hasPendingTune);
+ void release();
+}
diff --git a/tuner/tests/TestManifest.xml b/tuner/tests/TestManifest.xml
new file mode 100644
index 00000000..f84aa90f
--- /dev/null
+++ b/tuner/tests/TestManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tv.tuner.tests">
+ <!-- android_local_test needs minSdkVersion set -->
+ <uses-sdk android:minSdkVersion="23"/>
+
+</manifest> \ No newline at end of file
diff --git a/tuner/tests/assets/capture_kqed.ts b/tuner/tests/assets/capture_kqed.ts
new file mode 100644
index 00000000..624ac553
--- /dev/null
+++ b/tuner/tests/assets/capture_kqed.ts
Binary files differ
diff --git a/tuner/tests/assets/capture_stream.ts b/tuner/tests/assets/capture_stream.ts
new file mode 100644
index 00000000..97ee15c1
--- /dev/null
+++ b/tuner/tests/assets/capture_stream.ts
Binary files differ
diff --git a/tuner/tests/testing/Android.mk b/tuner/tests/testing/Android.mk
new file mode 100644
index 00000000..0d71b739
--- /dev/null
+++ b/tuner/tests/testing/Android.mk
@@ -0,0 +1,29 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# Include all test java files.
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ $(call all-Iaidl-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-annotations \
+ android-support-test \
+ guava \
+ mockito-target \
+ platform-robolectric-3.6.1-prebuilt \
+ truth-0-36-prebuilt-jar \
+ ub-uiautomator \
+
+# Link tv-common as shared library to avoid the problem of initialization of the constants
+LOCAL_JAVA_LIBRARIES := tv-common
+
+LOCAL_INSTRUMENTATION_FOR := LiveTv
+LOCAL_MODULE := tv-tuner-testing
+LOCAL_MODULE_TAGS := optional
+LOCAL_SDK_VERSION := system_current
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_AIDL_INCLUDES += $(LOCAL_PATH)/src
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tuner/tests/testing/AndroidManifest.xml b/tuner/tests/testing/AndroidManifest.xml
new file mode 100644
index 00000000..f244ae7b
--- /dev/null
+++ b/tuner/tests/testing/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+<!-- Stub AndroidManifest.xml to build resources -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tv.tuner.testing"
+ android:versionCode="1">
+ <uses-sdk android:targetSdkVersion="26" android:minSdkVersion="21"/>
+ <application />
+</manifest>
diff --git a/tuner/tests/testing/src/com/android/tv/tuner/testing/buffer/VerySlowSampleChunk.java b/tuner/tests/testing/src/com/android/tv/tuner/testing/buffer/VerySlowSampleChunk.java
new file mode 100644
index 00000000..b68431cc
--- /dev/null
+++ b/tuner/tests/testing/src/com/android/tv/tuner/testing/buffer/VerySlowSampleChunk.java
@@ -0,0 +1,55 @@
+/*
+ * 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.tuner.testing.buffer;
+
+import android.os.SystemClock;
+import com.android.tv.tuner.exoplayer.buffer.SampleChunk;
+import com.android.tv.tuner.exoplayer.buffer.SamplePool;
+import com.google.android.exoplayer.SampleHolder;
+import java.io.File;
+import java.io.IOException;
+
+/** A Sample chunk that is slow for testing */
+public class VerySlowSampleChunk extends SampleChunk {
+
+ /** Creates a {@link VerySlowSampleChunk}. */
+ public static class VerySlowSampleChunkCreator extends SampleChunkCreator {
+ @Override
+ public SampleChunk createSampleChunk(
+ SamplePool samplePool,
+ File file,
+ long startPositionUs,
+ ChunkCallback chunkCallback) {
+ return new VerySlowSampleChunk(
+ samplePool, file, startPositionUs, System.currentTimeMillis(), chunkCallback);
+ }
+ }
+
+ private VerySlowSampleChunk(
+ SamplePool samplePool,
+ File file,
+ long startPositionUs,
+ long createdTimeMs,
+ ChunkCallback chunkCallback) {
+ super(samplePool, file, startPositionUs, createdTimeMs, chunkCallback);
+ }
+
+ @Override
+ protected void write(SampleHolder sample, IoState state) throws IOException {
+ SystemClock.sleep(10);
+ super.write(sample, state);
+ }
+}
diff --git a/tuner/tests/unittests/javatests/AndroidManifest.xml b/tuner/tests/unittests/javatests/AndroidManifest.xml
new file mode 100644
index 00000000..8a5fda8f
--- /dev/null
+++ b/tuner/tests/unittests/javatests/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tv.tuner.layout.tests" >
+
+ <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/>
+
+ <instrumentation
+ android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.android.tv.tuner" />
+
+ <application android:label="TunerTvInputLayoutTests" >
+ <uses-library android:name="android.test.runner" />
+
+ <activity android:name="ScaledLayoutActivity"
+ android:label="ScaledLayout Test" />
+ </application>
+
+</manifest>
diff --git a/tuner/tests/unittests/javatests/com/android/tv/tuner/AndroidManifest.xml b/tuner/tests/unittests/javatests/com/android/tv/tuner/AndroidManifest.xml
new file mode 100644
index 00000000..9c815600
--- /dev/null
+++ b/tuner/tests/unittests/javatests/com/android/tv/tuner/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tv.tuner.tests" >
+
+ <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="26" />
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.tv" />
+
+ <application android:label="TunerTvInputTests" >
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+</manifest>
diff --git a/tuner/tests/unittests/javatests/com/android/tv/tuner/FakeTunerHal.java b/tuner/tests/unittests/javatests/com/android/tv/tuner/FakeTunerHal.java
new file mode 100644
index 00000000..cc4f6fde
--- /dev/null
+++ b/tuner/tests/unittests/javatests/com/android/tv/tuner/FakeTunerHal.java
@@ -0,0 +1,79 @@
+/*
+ * 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.tuner;
+
+public class FakeTunerHal extends TunerHal {
+
+ private boolean mDeviceOpened;
+
+ public FakeTunerHal() {
+ super(null);
+ }
+
+ @Override
+ protected boolean openFirstAvailable() {
+ mDeviceOpened = true;
+ getDeliverySystemTypeFromDevice();
+ return true;
+ }
+
+ @Override
+ protected boolean isDeviceOpen() {
+ return mDeviceOpened;
+ }
+
+ @Override
+ protected long getDeviceId() {
+ return 0;
+ }
+
+ @Override
+ public void close() throws Exception {
+ mDeviceOpened = false;
+ }
+
+ @Override
+ protected void nativeFinalize(long deviceId) {}
+
+ @Override
+ protected boolean nativeTune(
+ long deviceId, int frequency, @ModulationType String modulation, int timeoutMs) {
+ return true;
+ }
+
+ @Override
+ protected void nativeAddPidFilter(long deviceId, int pid, @FilterType int filterType) {}
+
+ @Override
+ protected void nativeCloseAllPidFilters(long deviceId) {}
+
+ @Override
+ protected void nativeStopTune(long deviceId) {}
+
+ @Override
+ protected int nativeWriteInBuffer(long deviceId, byte[] javaBuffer, int javaBufferSize) {
+ return 0;
+ }
+
+ @Override
+ protected void nativeSetHasPendingTune(long deviceId, boolean hasPendingTune) {}
+
+ @Override
+ protected int nativeGetDeliverySystemType(long deviceId) {
+ return DELIVERY_SYSTEM_ATSC;
+ }
+}
diff --git a/tuner/tests/unittests/javatests/com/android/tv/tuner/FileTunerHal.java b/tuner/tests/unittests/javatests/com/android/tv/tuner/FileTunerHal.java
new file mode 100644
index 00000000..73d234e0
--- /dev/null
+++ b/tuner/tests/unittests/javatests/com/android/tv/tuner/FileTunerHal.java
@@ -0,0 +1,274 @@
+/*
+ * 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.tuner;
+
+import android.content.Context;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.SparseBooleanArray;
+import com.android.tv.testing.utils.Utils;
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.Random;
+
+/** This class simulate the actions happened in TunerHal. */
+public class FileTunerHal extends TunerHal {
+
+ private static final String TAG = "FileTunerHal";
+ private static final int DEVICE_ID = 0;
+ private static final int TS_PACKET_SIZE = 188;
+ // To keep consistent with tunertvinput_jni, which fit Ethernet MTU (1500)
+ private static final int TS_PACKET_COUNT_PER_PAYLOAD = 7;
+ private static final int TS_PAYLOAD_SIZE = TS_PACKET_SIZE * TS_PACKET_COUNT_PER_PAYLOAD;
+ private static final int TS_SYNC_BYTE = 0x47;
+ // Make the read size from file and size in nativeWriteInBuffer same to make read logic simpler.
+ private static final int MIN_READ_UNIT = TS_PACKET_SIZE * TS_PACKET_COUNT_PER_PAYLOAD;
+ private static final long DELAY_IOCTL_SET_FRONTEND = 100;
+ private static final long DELAY_IOCTL_WAIT_FRONTEND_LOCKED = 500;
+
+ // The terrestrial broadcast mode (known as 8-VSB) delivers an MPEG-2 TS at rate
+ // approximately 19.39 Mbps in a 6 MHz channel. (See section #5 of ATSC A/53 Part 2:2011)
+ private static final double TS_READ_BYTES_PER_MS = 19.39 * 1024 * 1024 / (8 * 1000.0);
+ private static final long INITIAL_BUFFERED_TS_BYTES = (long) (TS_READ_BYTES_PER_MS * 150);
+
+ private static boolean sIsDeviceOpen;
+
+ private final SparseBooleanArray mPids = new SparseBooleanArray();
+ private final byte[] mBuffer = new byte[MIN_READ_UNIT];
+ private final File mTestFile;
+ private final Random mGenerator;
+ private RandomAccessFile mAccessFile;
+ private boolean mHasPendingTune;
+ private long mReadStartMs;
+ private long mTotalReadBytes;
+ private long mInitialSkipMs;
+ private boolean mPacketMissing;
+ private boolean mEnableArtificialDelay;
+
+ FileTunerHal(Context context, File testFile) {
+ super(context);
+ mTestFile = testFile;
+ mGenerator = Utils.createTestRandom();
+ }
+
+ /**
+ * Skip Initial parts of the TS file in order to start from specified time position.
+ *
+ * @param initialSkipMs initial position from where TS stream should be provided
+ */
+ void setInitialSkipMs(long initialSkipMs) {
+ mInitialSkipMs = initialSkipMs;
+ }
+
+ @Override
+ protected boolean openFirstAvailable() {
+ sIsDeviceOpen = true;
+ getDeliverySystemTypeFromDevice();
+ return true;
+ }
+
+ @Override
+ public void close() {}
+
+ @Override
+ protected boolean isDeviceOpen() {
+ return sIsDeviceOpen;
+ }
+
+ @Override
+ protected long getDeviceId() {
+ return DEVICE_ID;
+ }
+
+ @Override
+ protected void nativeFinalize(long deviceId) {
+ if (deviceId != DEVICE_ID) {
+ return;
+ }
+ mPids.clear();
+ }
+
+ @Override
+ protected boolean nativeTune(
+ long deviceId, int frequency, @ModulationType String modulation, int timeoutMs) {
+ if (deviceId != DEVICE_ID) {
+ return false;
+ }
+ if (mHasPendingTune) {
+ return false;
+ }
+ closeInputStream();
+ openInputStream();
+ if (mAccessFile == null) {
+ return false;
+ }
+
+ // Sleeping to simulate calling FE_GET_INFO and FE_SET_FRONTEND.
+ if (mEnableArtificialDelay) {
+ SystemClock.sleep(DELAY_IOCTL_SET_FRONTEND);
+ }
+ if (mHasPendingTune) {
+ return false;
+ }
+
+ // Sleeping to simulate waiting frontend locked.
+ if (mEnableArtificialDelay) {
+ SystemClock.sleep(DELAY_IOCTL_WAIT_FRONTEND_LOCKED);
+ }
+ if (mHasPendingTune) {
+ return false;
+ }
+ mTotalReadBytes = 0;
+ mReadStartMs = System.currentTimeMillis();
+ return true;
+ }
+
+ @Override
+ protected void nativeAddPidFilter(long deviceId, int pid, @FilterType int filterType) {
+ if (deviceId != DEVICE_ID) {
+ return;
+ }
+ mPids.put(pid, true);
+ }
+
+ @Override
+ protected void nativeCloseAllPidFilters(long deviceId) {
+ if (deviceId != DEVICE_ID) {
+ return;
+ }
+ mPids.clear();
+ }
+
+ @Override
+ protected void nativeStopTune(long deviceId) {
+ if (deviceId != DEVICE_ID) {
+ return;
+ }
+ mPids.clear();
+ }
+
+ @Override
+ protected int nativeWriteInBuffer(long deviceId, byte[] javaBuffer, int javaBufferSize) {
+ if (deviceId != DEVICE_ID) {
+ return 0;
+ }
+ if (mEnableArtificialDelay) {
+ long estimatedReadBytes =
+ (long) (TS_READ_BYTES_PER_MS * (System.currentTimeMillis() - mReadStartMs))
+ + INITIAL_BUFFERED_TS_BYTES;
+ if (estimatedReadBytes < mTotalReadBytes) {
+ return 0;
+ }
+ }
+ int readSize = readInternal();
+ if (readSize <= 0) {
+ closeInputStream();
+ openInputStream();
+ if (mAccessFile == null) {
+ return -1;
+ }
+ readSize = readInternal();
+ } else {
+ mTotalReadBytes += readSize;
+ }
+
+ if (mBuffer[0] != TS_SYNC_BYTE) {
+ return -1;
+ }
+ int filteredSize = 0;
+ javaBufferSize = (javaBufferSize / TS_PACKET_SIZE) * TS_PACKET_SIZE;
+ javaBufferSize = (javaBufferSize < TS_PAYLOAD_SIZE) ? javaBufferSize : TS_PAYLOAD_SIZE;
+ for (int i = 0, destPos = 0;
+ i < readSize && destPos + TS_PACKET_SIZE <= javaBufferSize;
+ i += TS_PACKET_SIZE) {
+ if (mBuffer[i] == TS_SYNC_BYTE) {
+ int pid = ((mBuffer[i + 1] & 0x1f) << 8) + (mBuffer[i + 2] & 0xff);
+ if (mPids.get(pid)) {
+ System.arraycopy(mBuffer, i, javaBuffer, destPos, TS_PACKET_SIZE);
+ destPos += TS_PACKET_SIZE;
+ filteredSize += TS_PACKET_SIZE;
+ }
+ }
+ }
+ return filteredSize;
+ }
+
+ @Override
+ protected void nativeSetHasPendingTune(long deviceId, boolean hasPendingTune) {
+ if (deviceId != DEVICE_ID) {
+ return;
+ }
+ mHasPendingTune = hasPendingTune;
+ }
+
+ @Override
+ protected int nativeGetDeliverySystemType(long deviceId) {
+ return DELIVERY_SYSTEM_ATSC;
+ }
+
+ private int readInternal() {
+ int readSize;
+ try {
+ if (mPacketMissing) {
+ mAccessFile.skipBytes(
+ mGenerator.nextInt(TS_PACKET_COUNT_PER_PAYLOAD) * TS_PACKET_SIZE);
+ }
+ readSize = mAccessFile.read(mBuffer, 0, mBuffer.length);
+ } catch (IOException e) {
+ return -1;
+ }
+ return readSize;
+ }
+
+ private void closeInputStream() {
+ try {
+ if (mAccessFile != null) {
+ mAccessFile.close();
+ }
+ } catch (IOException e) {
+ }
+ mAccessFile = null;
+ }
+
+ private void openInputStream() {
+ try {
+ mAccessFile = new RandomAccessFile(mTestFile, "r");
+
+ // Since sync frames are located once per 2 seconds, test with various
+ // starting offsets according to mInitialSkipMs.
+ long skipBytes = (long) (mInitialSkipMs * TS_READ_BYTES_PER_MS);
+ skipBytes = skipBytes / TS_PACKET_SIZE * TS_PACKET_SIZE;
+ mAccessFile.seek(skipBytes);
+ } catch (IOException e) {
+ Log.i(TAG, "open input stream failed:" + e);
+ }
+ }
+
+ /** Gets the number of built-in tuner devices. Always 1 in this case. */
+ public static int getNumberOfDevices(Context context) {
+ return 1;
+ }
+
+ public void setEnablePacketMissing(boolean packetMissing) {
+ mPacketMissing = packetMissing;
+ }
+
+ public void setEnableArtificialDelay(boolean enableArtificialDelay) {
+ mEnableArtificialDelay = enableArtificialDelay;
+ }
+}
diff --git a/tuner/tests/unittests/javatests/com/android/tv/tuner/ZappingTimeTest.java b/tuner/tests/unittests/javatests/com/android/tv/tuner/ZappingTimeTest.java
new file mode 100644
index 00000000..0e9bd357
--- /dev/null
+++ b/tuner/tests/unittests/javatests/com/android/tv/tuner/ZappingTimeTest.java
@@ -0,0 +1,413 @@
+/*
+ * 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.tuner;
+
+import android.content.Context;
+import android.graphics.SurfaceTexture;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.support.test.filters.LargeTest;
+import android.test.InstrumentationTestCase;
+import android.util.Log;
+import android.view.Surface;
+import com.android.tv.tuner.data.Cea708Data;
+import com.android.tv.tuner.data.PsiData;
+import com.android.tv.tuner.data.PsipData;
+import com.android.tv.tuner.data.TunerChannel;
+import com.android.tv.tuner.data.nano.Channel;
+import com.android.tv.tuner.exoplayer.MpegTsPlayer;
+import com.android.tv.tuner.exoplayer.MpegTsRendererBuilder;
+import com.android.tv.tuner.exoplayer.buffer.BufferManager;
+import com.android.tv.tuner.exoplayer.buffer.TrickplayStorageManager;
+import com.android.tv.tuner.source.TsDataSourceManager;
+import com.android.tv.tuner.tvinput.EventDetector;
+import com.android.tv.tuner.tvinput.PlaybackBufferListener;
+import com.google.android.exoplayer.ExoPlayer;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import org.junit.Ignore;
+
+/** This class use {@link FileTunerHal} to simulate tunerhal's actions to test zapping time. */
+@LargeTest
+public class ZappingTimeTest extends InstrumentationTestCase {
+ private static final String TAG = "ZappingTimeTest";
+ private static final boolean DEBUG = false;
+ private static final int TS_COPY_BUFFER_SIZE = 1024 * 512;
+ private static final int PROGRAM_NUMBER = 1;
+ private static final int VIDEO_PID = 49;
+ private static final int PCR_PID = 49;
+ private static final List<Integer> AUDIO_PIDS = Arrays.asList(51, 52, 53);
+ private static final long BUFFER_SIZE_DEF = 2 * 1024;
+ private static final int FREQUENCY = -1;
+ private static final String MODULATION = "";
+ private static final long ZAPPING_TIME_OUT_MS = 10000;
+ private static final long MAX_AVERAGE_ZAPPING_TIME_MS = 4000;
+ private static final int TEST_ITERATION_COUNT = 10;
+ private static final int STRESS_ZAPPING_TEST_COUNT = 50;
+ private static final long SKIP_DURATION_MS_TO_ADD = 200;
+ private static final String TEST_TS_FILE_PATH = "capture_kqed.ts";
+
+ private static final int MSG_START_PLAYBACK = 1;
+
+ private TunerChannel mChannel;
+ private FileTunerHal mTunerHal;
+ private MpegTsPlayer mPlayer;
+ private TsDataSourceManager mSourceManager;
+ private Handler mHandler;
+ private Context mTargetContext;
+ private File mTrickplayBufferDir;
+ private Surface mSurface;
+ private CountDownLatch mErrorLatch;
+ private CountDownLatch mDrawnToSurfaceLatch;
+ private CountDownLatch mWaitTuneExecuteLatch;
+ private AtomicLong mOnDrawnToSurfaceTimeMs = new AtomicLong(0);
+ private MockMpegTsPlayerListener mMpegTsPlayerListener = new MockMpegTsPlayerListener();
+ private MockPlaybackBufferListener mPlaybackBufferListener = new MockPlaybackBufferListener();
+ private MockEventListener mEventListener = new MockEventListener();
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mTargetContext = getInstrumentation().getTargetContext();
+ mTrickplayBufferDir = mTargetContext.getCacheDir();
+ HandlerThread handlerThread = new HandlerThread(TAG);
+ handlerThread.start();
+ List<PsiData.PmtItem> pmtItems = new ArrayList<>();
+ pmtItems.add(new PsiData.PmtItem(Channel.VideoStreamType.MPEG2, VIDEO_PID, null, null));
+ for (int audioPid : AUDIO_PIDS) {
+ pmtItems.add(
+ new PsiData.PmtItem(Channel.AudioStreamType.A52AC3AUDIO, audioPid, null, null));
+ }
+
+ Context context = getInstrumentation().getContext();
+ // Since assets and resource files are compressed, random access to the specified offset
+ // in assets or resource files will add some delay which is proportional to the offset.
+ // So the TS stream asset file are copied to a cache file, and the starting stream position
+ // in the file will be accessed by underlying {@link RandomAccessFile}.
+ File tsCacheFile = createCacheFile(context, mTargetContext, TEST_TS_FILE_PATH);
+ pmtItems.add(new PsiData.PmtItem(0x100, PCR_PID, null, null));
+ mChannel = new TunerChannel(PROGRAM_NUMBER, pmtItems);
+ mChannel.setFrequency(FREQUENCY);
+ mChannel.setModulation(MODULATION);
+ mTunerHal = new FileTunerHal(context, tsCacheFile);
+ mTunerHal.openFirstAvailable();
+ mSourceManager = TsDataSourceManager.createSourceManager(false);
+ mSourceManager.addTunerHalForTest(mTunerHal);
+ mHandler =
+ new Handler(
+ handlerThread.getLooper(),
+ new Handler.Callback() {
+ @Override
+ public boolean handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_START_PLAYBACK:
+ {
+ mHandler.removeCallbacksAndMessages(null);
+ stopPlayback();
+ mOnDrawnToSurfaceTimeMs.set(0);
+ mDrawnToSurfaceLatch = new CountDownLatch(1);
+ if (mWaitTuneExecuteLatch != null) {
+ mWaitTuneExecuteLatch.countDown();
+ }
+ int frequency = msg.arg1;
+ boolean useSimpleSampleBuffer = (msg.arg2 == 1);
+ BufferManager bufferManager = null;
+ if (!useSimpleSampleBuffer) {
+ bufferManager =
+ new BufferManager(
+ new TrickplayStorageManager(
+ mTargetContext,
+ mTrickplayBufferDir,
+ 1024L
+ * 1024L
+ * BUFFER_SIZE_DEF));
+ }
+ mChannel.setFrequency(frequency);
+ mSourceManager.setKeepTuneStatus(true);
+ mPlayer =
+ new MpegTsPlayer(
+ new MpegTsRendererBuilder(
+ mTargetContext,
+ bufferManager,
+ mPlaybackBufferListener),
+ mHandler,
+ mSourceManager,
+ null,
+ mMpegTsPlayerListener);
+ mPlayer.setCaptionServiceNumber(
+ Cea708Data.EMPTY_SERVICE_NUMBER);
+ mPlayer.prepare(
+ mTargetContext,
+ mChannel,
+ false,
+ mEventListener);
+ return true;
+ }
+ default:
+ {
+ Log.i(TAG, "Unhandled message code: " + msg.what);
+ return true;
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (mPlayer != null) {
+ mPlayer.release();
+ }
+ if (mSurface != null) {
+ mSurface.release();
+ }
+ mHandler.getLooper().quitSafely();
+ super.tearDown();
+ }
+
+ public void testZappingTime() {
+ zappingTimeTest(false, TEST_ITERATION_COUNT, true);
+ }
+
+ public void testZappingTimeWithSimpleSampleBuffer() {
+ zappingTimeTest(true, TEST_ITERATION_COUNT, true);
+ }
+
+ @Ignore("b/69978026")
+ @SuppressWarnings("JUnit4ClassUsedInJUnit3")
+ public void testStressZapping() {
+ zappingTimeTest(false, STRESS_ZAPPING_TEST_COUNT, false);
+ }
+
+ @Ignore("b/69978093")
+ @SuppressWarnings("JUnit4ClassUsedInJUnit3")
+ public void testZappingWithPacketMissing() {
+ mTunerHal.setEnablePacketMissing(true);
+ mTunerHal.setEnableArtificialDelay(true);
+ SurfaceTexture surfaceTexture = new SurfaceTexture(0);
+ mSurface = new Surface(surfaceTexture);
+ long zappingStartTimeMs = System.currentTimeMillis();
+ mErrorLatch = new CountDownLatch(1);
+ mHandler.obtainMessage(MSG_START_PLAYBACK, FREQUENCY, 0).sendToTarget();
+ boolean errorAppeared = false;
+ while (System.currentTimeMillis() - zappingStartTimeMs < ZAPPING_TIME_OUT_MS) {
+ try {
+ errorAppeared = mErrorLatch.await(100, TimeUnit.MILLISECONDS);
+ if (errorAppeared) {
+ break;
+ }
+ } catch (InterruptedException e) {
+ }
+ }
+ assertFalse("Error happened when packet lost", errorAppeared);
+ }
+
+ private static File createCacheFile(Context context, Context targetContext, String filename)
+ throws IOException {
+ File cacheFile = new File(targetContext.getCacheDir(), filename);
+
+ if (cacheFile.createNewFile() == false) {
+ cacheFile.delete();
+ cacheFile.createNewFile();
+ }
+
+ InputStream inputStream = context.getResources().getAssets().open(filename);
+ FileOutputStream fileOutputStream = new FileOutputStream(cacheFile);
+
+ byte[] buffer = new byte[TS_COPY_BUFFER_SIZE];
+ while (inputStream.read(buffer, 0, TS_COPY_BUFFER_SIZE) != -1) {
+ fileOutputStream.write(buffer);
+ }
+
+ fileOutputStream.close();
+ inputStream.close();
+
+ return cacheFile;
+ }
+
+ private void zappingTimeTest(
+ boolean useSimpleSampleBuffer, int testIterationCount, boolean enableArtificialDelay) {
+ String bufferManagerLogString =
+ !enableArtificialDelay
+ ? "for stress test"
+ : useSimpleSampleBuffer ? "with simple sample buffer" : "";
+ SurfaceTexture surfaceTexture = new SurfaceTexture(0);
+ mSurface = new Surface(surfaceTexture);
+ mTunerHal.setEnablePacketMissing(false);
+ mTunerHal.setEnableArtificialDelay(enableArtificialDelay);
+ double totalZappingTime = 0.0;
+ for (int i = 0; i < testIterationCount; i++) {
+ mWaitTuneExecuteLatch = new CountDownLatch(1);
+ long zappingStartTimeMs = System.currentTimeMillis();
+ mTunerHal.setInitialSkipMs(SKIP_DURATION_MS_TO_ADD * (i % TEST_ITERATION_COUNT));
+ mHandler.obtainMessage(MSG_START_PLAYBACK, FREQUENCY + i, useSimpleSampleBuffer ? 1 : 0)
+ .sendToTarget();
+ try {
+ mWaitTuneExecuteLatch.await();
+ } catch (InterruptedException e) {
+ }
+ boolean drawnToSurface = false;
+ while (System.currentTimeMillis() - zappingStartTimeMs < ZAPPING_TIME_OUT_MS) {
+ try {
+ drawnToSurface = mDrawnToSurfaceLatch.await(100, TimeUnit.MILLISECONDS);
+ if (drawnToSurface) {
+ break;
+ }
+ } catch (InterruptedException e) {
+ }
+ }
+ if (i == 0) {
+ continue;
+ // Get rid of the first result, which shows outlier often.
+ }
+ // In 10s, all zapping request will finish. Set the maximum zapping time as 10s could be
+ // reasonable.
+ totalZappingTime +=
+ (mOnDrawnToSurfaceTimeMs.get() > 0
+ ? mOnDrawnToSurfaceTimeMs.get() - zappingStartTimeMs
+ : ZAPPING_TIME_OUT_MS);
+ }
+ double averageZappingTime = totalZappingTime / (testIterationCount - 1);
+ Log.i(TAG, "Average zapping time " + bufferManagerLogString + ":" + averageZappingTime);
+ assertTrue(
+ "Average Zapping time "
+ + bufferManagerLogString
+ + " is too large:"
+ + averageZappingTime,
+ averageZappingTime < MAX_AVERAGE_ZAPPING_TIME_MS);
+ }
+
+ private void stopPlayback() {
+ if (mPlayer != null) {
+ mPlayer.setPlayWhenReady(false);
+ mPlayer.release();
+ mPlayer = null;
+ }
+ }
+
+ private class MockMpegTsPlayerListener implements MpegTsPlayer.Listener {
+
+ @Override
+ public void onStateChanged(boolean playWhenReady, int playbackState) {
+ if (DEBUG) {
+ Log.d(TAG, "ExoPlayer state change: " + playbackState + " " + playWhenReady);
+ }
+ if (playbackState == ExoPlayer.STATE_READY) {
+ mPlayer.setSurface(mSurface);
+ mPlayer.setPlayWhenReady(true);
+ mPlayer.setVolume(1.0f);
+ }
+ }
+
+ @Override
+ public void onError(Exception e) {
+ if (DEBUG) {
+ Log.d(TAG, "onError");
+ }
+ if (mErrorLatch != null) {
+ mErrorLatch.countDown();
+ }
+ }
+
+ @Override
+ public void onVideoSizeChanged(int width, int height, float pixelWidthHeightRatio) {
+ if (DEBUG) {
+ Log.d(TAG, "onVideoSizeChanged");
+ }
+ }
+
+ @Override
+ public void onDrawnToSurface(MpegTsPlayer player, Surface surface) {
+ if (DEBUG) {
+ Log.d(TAG, "onDrawnToSurface");
+ }
+ mOnDrawnToSurfaceTimeMs.set(System.currentTimeMillis());
+ if (mDrawnToSurfaceLatch != null) {
+ mDrawnToSurfaceLatch.countDown();
+ }
+ }
+
+ @Override
+ public void onAudioUnplayable() {
+ if (DEBUG) {
+ Log.d(TAG, "onAudioUnplayable");
+ }
+ }
+
+ @Override
+ public void onSmoothTrickplayForceStopped() {
+ if (DEBUG) {
+ Log.d(TAG, "onSmoothTrickplayForceStopped");
+ }
+ }
+ }
+
+ private static class MockPlaybackBufferListener implements PlaybackBufferListener {
+ @Override
+ public void onBufferStartTimeChanged(long startTimeMs) {
+ if (DEBUG) {
+ Log.d(TAG, "onBufferStartTimeChanged");
+ }
+ }
+
+ @Override
+ public void onBufferStateChanged(boolean available) {
+ if (DEBUG) {
+ Log.d(TAG, "onBufferStateChanged");
+ }
+ }
+
+ @Override
+ public void onDiskTooSlow() {
+ if (DEBUG) {
+ Log.d(TAG, "onDiskTooSlow");
+ }
+ }
+ }
+
+ private static class MockEventListener implements EventDetector.EventListener {
+ @Override
+ public void onChannelDetected(TunerChannel channel, boolean channelArrivedAtFirstTime) {
+ if (DEBUG) {
+ Log.d(TAG, "onChannelDetected");
+ }
+ }
+
+ @Override
+ public void onEventDetected(TunerChannel channel, List<PsipData.EitItem> items) {
+ if (DEBUG) {
+ Log.d(TAG, "onEventDetected");
+ }
+ }
+
+ @Override
+ public void onChannelScanDone() {
+ if (DEBUG) {
+ Log.d(TAG, "onChannelScanDone");
+ }
+ }
+ }
+}
diff --git a/tuner/tests/unittests/javatests/com/android/tv/tuner/layout/tests/AndroidManifest.xml b/tuner/tests/unittests/javatests/com/android/tv/tuner/layout/tests/AndroidManifest.xml
new file mode 100644
index 00000000..3e6946a9
--- /dev/null
+++ b/tuner/tests/unittests/javatests/com/android/tv/tuner/layout/tests/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 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
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tv.tuner"
+ android:versionCode="1">
+ <uses-sdk android:targetSdkVersion="26" android:minSdkVersion="23"/>
+ <application android:label="TunerTvInputLayoutTests" >
+ <activity android:name="com.android.tv.tuner.layout.tests.ScaledLayoutActivity"
+ android:label="ScaledLayout Test" />
+ </application>
+</manifest>
diff --git a/tuner/tests/unittests/javatests/com/android/tv/tuner/layout/tests/ScaledLayoutActivity.java b/tuner/tests/unittests/javatests/com/android/tv/tuner/layout/tests/ScaledLayoutActivity.java
new file mode 100644
index 00000000..681465e1
--- /dev/null
+++ b/tuner/tests/unittests/javatests/com/android/tv/tuner/layout/tests/ScaledLayoutActivity.java
@@ -0,0 +1,83 @@
+/*
+ * 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.tuner.layout.tests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.FrameLayout;
+import com.android.tv.tuner.layout.ScaledLayout;
+
+public class ScaledLayoutActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_scaled_layout_test);
+
+ FrameLayout frameLayout = findViewById(R.id.root_layout);
+ frameLayout.addView(createScaledLayoutTestcaseLayout());
+ frameLayout.addView(createScaledLayoutTestcaseBounceY());
+ }
+
+ private ScaledLayout createScaledLayoutTestcaseLayout() {
+ ScaledLayout scaledLayout = new ScaledLayout(this);
+ scaledLayout.setLayoutParams(new FrameLayout.LayoutParams(100, 100));
+
+ View view1 = new View(this);
+ view1.setId(R.id.view1);
+ view1.setLayoutParams(new ScaledLayout.ScaledLayoutParams(0f, 0.5f, 0f, 0.5f));
+
+ View view2 = new View(this);
+ view2.setId(R.id.view2);
+ view2.setLayoutParams(new ScaledLayout.ScaledLayoutParams(0f, 0.5f, 0.5f, 1f));
+
+ View view3 = new View(this);
+ view3.setId(R.id.view3);
+ view3.setLayoutParams(new ScaledLayout.ScaledLayoutParams(0.5f, 1f, 0f, 0.5f));
+
+ View view4 = new View(this);
+ view4.setId(R.id.view4);
+ view4.setLayoutParams(new ScaledLayout.ScaledLayoutParams(0.5f, 1f, 0.5f, 1f));
+
+ scaledLayout.addView(view1);
+ scaledLayout.addView(view2);
+ scaledLayout.addView(view3);
+ scaledLayout.addView(view4);
+
+ return scaledLayout;
+ }
+
+ private ScaledLayout createScaledLayoutTestcaseBounceY() {
+ ScaledLayout scaledLayout = new ScaledLayout(this);
+ scaledLayout.setLayoutParams(new FrameLayout.LayoutParams(100, 100));
+
+ View view1 = new View(this);
+ view1.setId(R.id.view1);
+ view1.setLayoutParams(new ScaledLayout.ScaledLayoutParams(0.7f, 0.9f, 0f, 1f));
+
+ View view2 = new View(this);
+ view2.setId(R.id.view2);
+ view2.setLayoutParams(new ScaledLayout.ScaledLayoutParams(0.8f, 1f, 0f, 1f));
+
+ scaledLayout.addView(view1);
+ scaledLayout.addView(view2);
+
+ return scaledLayout;
+ }
+}
diff --git a/tuner/tests/unittests/javatests/com/android/tv/tuner/layout/tests/ScaledLayoutTest.java b/tuner/tests/unittests/javatests/com/android/tv/tuner/layout/tests/ScaledLayoutTest.java
new file mode 100644
index 00000000..214b0631
--- /dev/null
+++ b/tuner/tests/unittests/javatests/com/android/tv/tuner/layout/tests/ScaledLayoutTest.java
@@ -0,0 +1,147 @@
+/*
+ * 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.tuner.layout.tests;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+
+import android.content.Intent;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+import android.widget.FrameLayout;
+import com.android.tv.tuner.layout.ScaledLayout;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ScaledLayoutTest {
+
+ @Rule public ScaledActivityTestRule mActivityRule = new ScaledActivityTestRule();
+
+ @Before
+ public void setup() {
+ mActivityRule.launchActivity(new Intent());
+ }
+
+ @After
+ public void tearDown() {
+ mActivityRule.finishActivity();
+ }
+
+ @Test
+ public void testScaledLayout_layoutInXml() {
+ ScaledLayoutActivity activity = mActivityRule.getActivity();
+ assertNotNull(activity);
+ FrameLayout rootLayout = mActivityRule.getActivity().findViewById(R.id.root_layout);
+ assertNotNull(rootLayout);
+ ScaledLayout scaledLayout = (ScaledLayout) rootLayout.getChildAt(0);
+ assertNotNull(scaledLayout);
+ View view1 = scaledLayout.findViewById(R.id.view1);
+ assertNotNull(view1);
+ View view2 = scaledLayout.findViewById(R.id.view2);
+ assertNotNull(view2);
+ View view3 = scaledLayout.findViewById(R.id.view3);
+ assertNotNull(view3);
+ View view4 = scaledLayout.findViewById(R.id.view4);
+ assertNotNull(view4);
+ assertEquals((int) (400 * 0.1), view1.getWidth());
+ assertEquals((int) (300 * 0.2), view1.getHeight());
+ assertEquals((int) (400 * 0.8), view1.getLeft());
+ assertEquals((int) (300 * 0.1), view1.getTop());
+ assertEquals((int) (400 * 0.1), view2.getWidth());
+ assertEquals(300, view2.getHeight());
+ assertEquals((int) (400 * 0.2), view2.getLeft());
+ assertEquals(0, view2.getTop());
+ assertEquals((int) (400 * 0.2), view3.getWidth());
+ assertEquals((int) (300 * 0.1), view3.getHeight());
+ assertEquals((int) (400 * 0.3), view3.getLeft());
+ assertEquals((int) (300 * 0.4), view3.getTop());
+ assertEquals((int) (400 * 0.1), view4.getWidth());
+ assertEquals((int) (300 * 0.8), view4.getHeight());
+ assertEquals((int) (400 * 0.05), view4.getLeft());
+ assertEquals((int) (300 * 0.15), view4.getTop());
+ }
+
+ @Test
+ public void testScaledLayout_layoutThroughCode() {
+ ScaledLayoutActivity activity = mActivityRule.getActivity();
+ assertNotNull(activity);
+ FrameLayout rootLayout = mActivityRule.getActivity().findViewById(R.id.root_layout);
+ assertNotNull(rootLayout);
+ ScaledLayout scaledLayout = (ScaledLayout) rootLayout.getChildAt(1);
+ assertNotNull(scaledLayout);
+ View view1 = scaledLayout.findViewById(R.id.view1);
+ assertNotNull(view1);
+ View view2 = scaledLayout.findViewById(R.id.view2);
+ assertNotNull(view2);
+ View view3 = scaledLayout.findViewById(R.id.view3);
+ assertNotNull(view3);
+ View view4 = scaledLayout.findViewById(R.id.view4);
+ assertNotNull(view4);
+ assertEquals(50, view1.getWidth());
+ assertEquals(50, view1.getHeight());
+ assertEquals(0, view1.getLeft());
+ assertEquals(0, view1.getTop());
+ assertEquals(50, view2.getWidth());
+ assertEquals(50, view2.getHeight());
+ assertEquals(50, view2.getLeft());
+ assertEquals(0, view2.getTop());
+ assertEquals(50, view3.getWidth());
+ assertEquals(50, view3.getHeight());
+ assertEquals(0, view3.getLeft());
+ assertEquals(50, view3.getTop());
+ assertEquals(50, view4.getWidth());
+ assertEquals(50, view4.getHeight());
+ assertEquals(50, view4.getLeft());
+ assertEquals(50, view4.getTop());
+ }
+
+ @Test
+ public void testScaledLayout_bounceY() {
+ ScaledLayoutActivity activity = mActivityRule.getActivity();
+ assertNotNull(activity);
+ FrameLayout rootLayout = mActivityRule.getActivity().findViewById(R.id.root_layout);
+ assertNotNull(rootLayout);
+ ScaledLayout scaledLayout = (ScaledLayout) rootLayout.getChildAt(2);
+ assertNotNull(scaledLayout);
+ View view1 = scaledLayout.findViewById(R.id.view1);
+ assertNotNull(view1);
+ View view2 = scaledLayout.findViewById(R.id.view2);
+ assertNotNull(view2);
+ assertEquals(100, view1.getWidth());
+ assertEquals(20, view1.getHeight());
+ assertEquals(0, view1.getLeft());
+ assertEquals(60, view1.getTop());
+ assertEquals(100, view2.getWidth());
+ assertEquals(20, view2.getHeight());
+ assertEquals(0, view2.getLeft());
+ assertEquals(80, view2.getTop());
+ }
+
+ private static class ScaledActivityTestRule extends ActivityTestRule<ScaledLayoutActivity> {
+
+ public ScaledActivityTestRule() {
+ super(ScaledLayoutActivity.class);
+ }
+ }
+}
diff --git a/tuner/tests/unittests/javatests/com/android/tv/tuner/setup/TunerHalFactoryTest.java b/tuner/tests/unittests/javatests/com/android/tv/tuner/setup/TunerHalFactoryTest.java
new file mode 100644
index 00000000..2354c827
--- /dev/null
+++ b/tuner/tests/unittests/javatests/com/android/tv/tuner/setup/TunerHalFactoryTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.tuner.setup;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import android.os.AsyncTask;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import com.android.tv.tuner.TunerHal;
+import com.android.tv.tuner.setup.BaseTunerSetupActivity.TunerHalFactory;
+import java.util.concurrent.Executor;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for {@link TunerHalFactory}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TunerHalFactoryTest {
+ private final FakeExecutor mFakeExecutor = new FakeExecutor();
+
+ private static class TestTunerHalFactory extends TunerHalFactory {
+ private TestTunerHalFactory(Executor executor) {
+ super(null, executor);
+ }
+
+ @Override
+ protected TunerHal createInstance() {
+ return new com.android.tv.tuner.FakeTunerHal() {};
+ }
+ }
+
+ private static class FakeExecutor implements Executor {
+ Runnable mCommand;
+
+ @Override
+ public synchronized void execute(final Runnable command) {
+ mCommand = command;
+ }
+
+ private synchronized void executeActually() {
+ mCommand.run();
+ }
+ }
+
+ @Test
+ public void test_asyncGet() {
+ TunerHalFactory tunerHalFactory = new TestTunerHalFactory(mFakeExecutor);
+ assertNull(tunerHalFactory.mTunerHal);
+ tunerHalFactory.generate();
+ assertNull(tunerHalFactory.mTunerHal);
+ mFakeExecutor.executeActually();
+ TunerHal tunerHal = tunerHalFactory.getOrCreate();
+ assertNotNull(tunerHal);
+ assertSame(tunerHal, tunerHalFactory.getOrCreate());
+ tunerHalFactory.clear();
+ }
+
+ @Test
+ public void test_syncGet() {
+ TunerHalFactory tunerHalFactory = new TestTunerHalFactory(AsyncTask.SERIAL_EXECUTOR);
+ assertNull(tunerHalFactory.mTunerHal);
+ tunerHalFactory.generate();
+ assertNotNull(tunerHalFactory.getOrCreate());
+ }
+
+ @Test
+ public void test_syncGetWithoutGenerate() {
+ TunerHalFactory tunerHalFactory = new TestTunerHalFactory(mFakeExecutor);
+ assertNull(tunerHalFactory.mTunerHal);
+ assertNotNull(tunerHalFactory.getOrCreate());
+ }
+}
diff --git a/tuner/tests/unittests/javatests/res/layout/activity_scaled_layout_test.xml b/tuner/tests/unittests/javatests/res/layout/activity_scaled_layout_test.xml
new file mode 100644
index 00000000..be2ba950
--- /dev/null
+++ b/tuner/tests/unittests/javatests/res/layout/activity_scaled_layout_test.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.
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/root_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.android.tv.tuner.layout.ScaledLayout android:id="@+id/scaled_layout"
+ android:layout_width="400px"
+ android:layout_height="300px">
+
+ <View android:id="@+id/view1"
+ app:layout_scaleStartRow="0.1"
+ app:layout_scaleEndRow="0.3"
+ app:layout_scaleStartCol="0.8"
+ app:layout_scaleEndCol="0.9" />
+
+ <View android:id="@+id/view2"
+ app:layout_scaleStartRow="0"
+ app:layout_scaleEndRow="1"
+ app:layout_scaleStartCol="0.2"
+ app:layout_scaleEndCol="0.3" />
+
+ <View android:id="@+id/view3"
+ app:layout_scaleStartRow="0.4"
+ app:layout_scaleEndRow="0.5"
+ app:layout_scaleStartCol="0.3"
+ app:layout_scaleEndCol="0.5" />
+
+ <View android:id="@+id/view4"
+ app:layout_scaleStartRow="0.15"
+ app:layout_scaleEndRow="0.95"
+ app:layout_scaleStartCol="0.05"
+ app:layout_scaleEndCol="0.15" />
+
+ </com.android.tv.tuner.layout.ScaledLayout>
+</FrameLayout>
diff --git a/tuner/tests/unittests/javatests/res/values/attrs.xml b/tuner/tests/unittests/javatests/res/values/attrs.xml
new file mode 100644
index 00000000..4249ed2f
--- /dev/null
+++ b/tuner/tests/unittests/javatests/res/values/attrs.xml
@@ -0,0 +1,25 @@
+<?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>
+ <declare-styleable name="utScaledLayout">
+ <attr name="layout_scaleStartRow" format="float" />
+ <attr name="layout_scaleEndRow" format="float" />
+ <attr name="layout_scaleStartCol" format="float" />
+ <attr name="layout_scaleEndCol" format="float" />
+ </declare-styleable>
+</resources>
diff --git a/usbtuner-res/values-af/strings.xml b/usbtuner-res/values-af/strings.xml
deleted file mode 100644
index 8fd50706..00000000
--- a/usbtuner-res/values-af/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"TV-ontvanger"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB-TV-ontvanger"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Netwerk-TV-ontvanger (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Wag asseblief dat verwerking voltooi word"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Ontvangersagteware is onlangs opgedateer. Herskandeer die kanale asseblief."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Aktiveer omringklank in stelselklankinstellings om oudio te aktiveer"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Kan nie oudio speel nie. Probeer asseblief \'n ander TV"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Opstelling van kanaalontvanger"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Opstelling van TV-ontvanger"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Opstelling van USB-kanaalontvanger"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Opstelling van netwerkontvanger"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Maak seker dat jou TV aan \'n TV-seinbron gekoppel is.\n\nAs jy \'n oor-die-lug-antenna gebruik, sal jy dalk sy plasing of rigting moet verander om die meeste kanale te ontvang. Plaas dit vir die beste resultate hoog en naby \'n venster."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Maak seker dat die USB-ontvanger ingeprop en aan \'n TV-seinbron gekoppel is.\n\nAs jy \'n oor-die-lug-antenna gebruik, sal jy dalk sy posisie of rigting moet verander om die meeste kanale te ontvang. Plaas dit vir die beste resultate hoog en naby \'n venster."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Maak seker dat die netwerkontvanger aangeskakel is en aan \'n TV-seinbron gekoppel is.\n\nAs jy \'n oor-die-lug-antenna gebruik, sal jy dalk sy plasing of rigting waarin hy wys, moet verstel om die meeste kanale te ontvang. Plaas dit vir die beste resultate hoog en naby \'n venster."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Gaan voort"</item>
- <item msgid="727245208787621142">"Nie nou nie"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Doen kanaalopstelling weer?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Dit sal die kanale wat ontvang is, uit die TV-ontvanger verwyder en weer nuwe kanale soek.\n\nMaak seker dat jou TV aan \'n TV-seinbron gekoppel is.\n\nAs jy \'n oor-die-lug-antenna gebruik, sal jy dalk sy plasing of rigting moet verander om die meeste kanale te ontvang. Plaas dit vir die beste resultate hoog en naby \'n venster."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Dit sal die kanale wat gevind is, uit die USB-ontvanger verwyder en weer nuwe kanale soek.\n\nMaak seker dat die USB-ontvanger ingeprop en aan \'n TV-seinbron gekoppel is.\n\nAs jy \'n oor-die-lug-antenna gebruik, sal jy dalk sy posisie of rigting moet verander om die meeste kanale te ontvang. Plaas dit vir die beste resultate hoog en naby \'n venster."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Dit sal die kanale wat gevind is van die netwerkontvanger af verwyder en weer na nuwe kanale soek.\n\nMaak seker dat die netwerkontvanger aangeskakel is en aan \'n TV-seinbron gekoppel is.\n\nAs jy \'n oor-die-lug-antenna gebruik, sal jy dalk sy plasing of rigting waarin hy wys, moet verstel om die meeste kanale te ontvang. Plaas dit vir die beste resultate hoog en naby \'n venster."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Gaan voort"</item>
- <item msgid="235450158666155406">"Kanselleer"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Kies die soort verbinding"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Kies Antenna as \'n eksterne antenna aan die ontvanger gekoppel is. Kies Kabel as jou kanale van \'n kabeldiensverskaffer af kom. As jy nie seker is nie, sal albei soorte geskandeer word, maar dit sal dalk langer neem."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antenna"</item>
- <item msgid="2670079958754180142">"Kabel"</item>
- <item msgid="36774059871728525">"Nie seker nie"</item>
- <item msgid="6881204453182153978">"Net ontwikkeling"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Opstelling van TV-ontvanger"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Opstelling van USB-kanaalontvanger"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Opstelling van netwerkkanaalontvanger"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Dit kan \'n paar minute neem"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Seinontvanger is tydelik nie beskikbaar nie of word reeds deur opname gebruik."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d kanale is gevind</item>
- <item quantity="one">%1$d kanaal is gevind</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"STOP KANAALSKANDERING"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d kanale is gevind</item>
- <item quantity="one">%1$d kanaal is gevind</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Lekker! %1$d kanale is tydens die kanaalsoektog gevind. As dit nie reg lyk nie, probeer die antennaposisie verstel en soek weer.</item>
- <item quantity="one">Lekker! %1$d kanaal is tydens die kanaalsoektog gevind. As dit nie reg lyk nie, probeer die antennaposisie verstel en soek weer.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Klaar"</item>
- <item msgid="2480490326672924828">"Soek weer"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Geen kanale gevind nie"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Geen kanale is in die soektog gevind nie. Maak seker dat jou TV aan \'n TV-seinbron gekoppel is.\n\nAs jy \'n oor-die-lug-antenna gebruik, verander sy plasing of rigting. Plaas dit vir die beste resultate hoog en naby \'n venster en soek weer."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Geen kanale is in die soektog gevind nie. Maak seker dat die USB-ontvanger ingeprop en aan \'n TV-seinbron gekoppel is.\n\nAs jy \'n oor-die-lug-antenna gebruik, verander sy posisie of rigting. Plaas dit vir die beste resultate hoog en naby \'n venster en soek weer."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Geen kanale is in die soektog gevind nie. Maak seker dat die netwerkontvanger aangeskakel is en aan \'n TV-seinbron gekoppel is.\n\nAs jy \'n oor-die-lug-antenna gebruik, verstel sy plasing of rigting waarin hy wys. Plaas dit vir die beste resultate hoog en naby \'n venster en soek weer."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Soek weer"</item>
- <item msgid="2092797862490235174">"Klaar"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Soek TV-kanale"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Opstelling van TV-ontvanger"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Opstelling van USB-TV-ontvanger"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Opstelling van netwerk-TV-ontvanger"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB-TV-ontvanger is ontkoppel."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Netwerkontvanger is ontkoppel."</string>
-</resources>
diff --git a/usbtuner-res/values-am/strings.xml b/usbtuner-res/values-am/strings.xml
deleted file mode 100644
index cbf1e9fe..00000000
--- a/usbtuner-res/values-am/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"የቴሌቪዥን መቃኛ"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"የዩኤስቢ ቴሌቪዥን መቃኛ"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"የአውታረ መረብ ቴሌቪዥን መቃኛ (ቅድመ-ይሁንታ)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"ማስኬድን ለማጠናቀቅ እባክዎ ይጠብቁ"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"የቴሌቪዥን መቃኛ ሶፍትዌር በቅርብ ጊዜ ተዘምኗል። እባክዎ ሰርጦቹን እንደገና ይቃኟቸው።"</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"ኦዲዮን ለማንቃት በስርዓት ድምጽ ቅንብሮች ውስጥ የዙሪያ ድምጽን ያንቁ"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"ኦዲዮ ማጫወት አይቻልም። እባክዎ ሌላ ቲቪ ይሞክሩ።"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"የጣቢያ መቃኛ ማዋቀር"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"የቴሌቪዥን መቃኛ ማዋቀር"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"የዩኤስቢ ጣቢያ መቃኛ ማዋቀር"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"የአውታረ መረብ መቃኛ ማዋቀር"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"የእርስዎ ቴሌቪዥን ከቴሌቪዥን ሲግናል ምልክት ምንጭ ጋር መገናኘቱን ያረጋግጡ።\n\nየአየር ላይ አንቴና የሚጠቀሙ ከሆነ አብዛኛዎቹን ጣቢያዎች ለመቀበል አቀማመጡን ወይም አቅጣጫውን ማስተካከል ሊኖርብዎት ይችላል። ለተሻሉ ውጤቶች ከፍ አድርገው ከመስኮት አጠገብ ያስቀምጡት።"</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"የዩኤስቢ መቃኛው መሰካቱን እና ከቴሌቪዥን ምልክት ምንጭ ጋር መገናኘቱን ያረጋግጡ።\n\nየአየር ላይ አንቴና የሚጠቀሙ ከሆነ አቀማመጡን ወይም አቅጣጫውን ያስተካክሉ። ለተሻሉ ውጤቶች ከፍ አድርገው ከመስኮት አጠገብ ያስቀምጡት።"</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"የአውታረ መረብ መቃኛው እና ከቴሌቪዥን ምልክት ምንጭ ጋር መገናኘቱን ያረጋግጡ።\n\nየአየር ላይ አንቴና የሚጠቀሙ ከሆነ አቀማመጡን ወይም አቅጣጫውን ያስተካክሉ። ለተሻሉ ውጤቶች ከፍ አድርገው ከመስኮት አጠገብ ያስቀምጡት።"</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"ቀጥል"</item>
- <item msgid="727245208787621142">"አሁን አይደለም"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"የጣቢያ ቅንብር እንደገና እንዲሄድ ይደረግ?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"ይሄ ከቴሌቪዥን መቃኛ የተገኙ ጣቢያዎችን አስወግዶ አዲስ ጣቢያዎችን እንደገና ይቃኛል።\n\nየእርስዎ ቴሌቪዥን ከቴሌቪዥን ሲግናል ምልክት ምንጭ ጋር መገናኘቱን ያረጋግጡ።\n\nየአየር ላይ አንቴና የሚጠቀሙ ከሆነ አብዛኛዎቹን ጣቢያዎች ለመቀበል አቀማመጡን ወይም አቅጣጫውን ማስተካከል ሊኖርብዎት ይችላል። ለተሻሉ ውጤቶች ከፍ አድርገው ከመስኮት አጠገብ ያስቀምጡት።"</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"ይሄ ከዩኤስቢ መቃኛ የተገኙ ጣቢያዎችን አስወግዶ አዲስ ጣቢያዎችን እንደገና ይቃኛል።\n\nየዩኤስቢ መቃኛው መሰካቱን እና ከቴሌቪዥን ምልክት ምንጭ ጋር መገናኘቱን ያረጋግጡ።\n\nየአየር ላይ አንቴና የሚጠቀሙ ከሆነ አቀማመጡን ወይም አቅጣጫውን ያስተካክሉ። ለተሻሉ ውጤቶች ከፍ አድርገው ከመስኮት አጠገብ ያስቀምጡት።"</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"ይሄ ከአውታረ መረብ መቃኛ የተገኙ ጣቢያዎችን አስወግዶ አዲስ ጣቢያዎችን እንደገና ይቃኛል።\n\nየአውታረ መረብ መቃኛው እና ከቴሌቪዥን ምልክት ምንጩ መገናኘቱን ያረጋግጡ።\n\nየአየር ላይ አንቴና የሚጠቀሙ ከሆነ አቀማመጡን ወይም አቅጣጫውን ያስተካክሉ። ለተሻሉ ውጤቶች ከፍ አድርገው ከመስኮት አጠገብ ያስቀምጡት።"</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"ቀጥል"</item>
- <item msgid="235450158666155406">"ይቅር"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"የግንኙነት ዓይነቱን ይምረጡ"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"ከመቃኛው ጋር የተገናኘ ውጫዊ አንቴና ካለ አንቴናን ይምረጡ። የእርስዎ ጣቢያዎች ከገመድ አገልግሎት አቅራቢ የሚመጡ ከሆነ ገመድን ይምረጡ። እርግጠኛ ካልሆኑ ሁለቱም ዓይነቶች ይቃኛሉ፣ ሆኖም ግን ይሄ ረዘም ያለ ጊዜ ሊወስድ ይችላል።"</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"አንቴና"</item>
- <item msgid="2670079958754180142">"ገመድ"</item>
- <item msgid="36774059871728525">"እርግጠኛ አይደሉም"</item>
- <item msgid="6881204453182153978">"ግንባታ ብቻ"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"የቴሌቪዥን መቃኛ ማዋቀር"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"የዩኤስቢ ጣቢያ መቃኛ ማዋቀር"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"የአውታረ መረብ ጣቢያ መቃኛ ማዋቀር"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"ይሄ በርካታ ደቂቃዎችን ሊወስድ ይችላል"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"መቃኛው ለጊዜው አይገኝም ወይም አስቀድሞ በቀረጻው ጥቅም ላይ ውሏል።"</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="one">%1$d ጣቢያዎች ተገኝተዋል</item>
- <item quantity="other">%1$d ጣቢያዎች ተገኝተዋል</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"የጣቢያ ቅኝትን አቁም"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="one">%1$d ጣቢያዎች ተገኝተዋል</item>
- <item quantity="other">%1$d ጣቢያዎች ተገኝተዋል</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="one">ግሩም! በጣቢያ ቅኝት ጊዜ %1$d ጣቢያዎች ተገኝተዋል። ትክክል የማይመስል ከሆነ የአንቴናውን አቀማመጥ አስተካክለው እንደገና ለመቃኘት ይሞክሩ።</item>
- <item quantity="other">ግሩም! በጣቢያ ቅኝት ጊዜ %1$d ጣቢያዎች ተገኝተዋል። ትክክል የማይመስል ከሆነ የአንቴናውን አቀማመጥ አስተካክለው እንደገና ለመቃኘት ይሞክሩ።</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"ተከናውኗል"</item>
- <item msgid="2480490326672924828">"እንደገና ቃኝ"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"ምንም ጣቢያዎች አልተገኙም"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"ቅኝቱ ምንም አዲስ ጣቢያዎችን አላገኘም። የእርስዎ ቴሌቪዥን ከቴሌቪዥን ሲግናል ምንጭ ጋር መገናኘቱን ያረጋግጡ።\n\nየአየር ላይ አንቴና ከሆነ የሚጠቀሙት አቀማመጡን ወይም አቅጣጫውን ያስተካክሉት። ለተሻሉ ውጤቶች ከፍ አድርገው ከመስኮት አጠገብ ያስቀምጡትና እንደገና ይቃኙ።"</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"ቅኝቱ ምንም ጣቢያዎችን አላገኘም። የዩኤስቢ መቃኛው መሰካቱን እና ከቴሌቪዥን ሲግናል ምንጩ ጋር መገናኘቱን ያረጋግጡ።\n\nየአየር ላይ አንቴና የሚጠቀሙ ከሆነ አቀማመጡን ወይም አቅጣጫውን ያስተካክሉ። ለተሻሉ ውጤቶች ከፍ አድርገው ከመስኮት አጠገብ ያስቀምጡት እና እንደገና ይቃኙ።"</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"ቅኝቱ ምንም ጣቢያዎችን አላገኘም። የአውታረ መረብ መቃኛው እንደበራ እና ከቴሌቪዥን ሲግናል ምንጩ ጋር መገናኘቱን ያረጋግጡ።\n\nየአየር ላይ አንቴና የሚጠቀሙ ከሆነ አቀማመጡን ወይም አቅጣጫውን ያስተካክሉ። ለተሻሉ ውጤቶች ከፍ አድርገው ከመስኮት አጠገብ ያስቀምጡት እና እንደገና ይቃኙ።"</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"እንደገና ቃኝ"</item>
- <item msgid="2092797862490235174">"ተከናውኗል"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"የቴሌቪዥን ጣቢያዎችን ቃኝ"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"የቴሌቪዥን መቃኛ ማዋቀር"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"የዩኤስቢ ቴሌቪዥን መቃኛ ማዋቀር"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"የአውታረ መረብ ቴሌቪዥን መቃኛ ማዋቀር"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"የዩኤስቢ ቴሌቪዥን መቃኛው ግንኙነት ተቋርጧል።"</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"የአውታረ መረብ መቃኛ ግንኙነት ተቋርጧል።"</string>
-</resources>
diff --git a/usbtuner-res/values-ar/strings.xml b/usbtuner-res/values-ar/strings.xml
deleted file mode 100644
index ae197d19..00000000
--- a/usbtuner-res/values-ar/strings.xml
+++ /dev/null
@@ -1,102 +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="bt_app_name" msgid="5515382901862469770">"موالف التلفزيون"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"‏موالف التلفزيون عبر USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"موالف التلفزيون على الشبكة (تجريبي)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"الرجاء الانتظار لحين انتهاء المعالجة"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"تم تحديث برنامج الموالف مؤخرًا. الرجاء إعادة البحث عن القنوات."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"يمكنك تشغيل الصوت المحيطي في إعدادات صوت النظام لتفعيل الصوت"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"لا يمكن تشغيل الصوت. الرجاء تجربة تلفزيون آخر."</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"إعداد موالف القنوات"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"إعداد موالف التلفزيون"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"‏إعداد موالف قنوات USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"إعداد موالف الشبكة"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"تحقق من توصيل التلفزيون بمصدر إشارة البث التلفزيوني.\n\nإذا كنت تستخدم هوائيًا للتحديث عبر الهواء، فقد تحتاج إلى ضبط موضعه أو اتجاهه لاستقبال معظم القنوات، وللحصول على أفضل النتائج، ضعه عاليًا بالقرب من النافذة."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"‏تحقق من توصيل الموالف عبر USB بمصدر إشارة البث التلفزيوني.\n\nإذا كنت تستخدم هوائيًا للتحديث عبر الهواء، فقد تحتاج إلى ضبط موضعه أو اتجاهه لاستقبال معظم القنوات، وللحصول على أفضل النتائج، ضعه عاليًا بالقرب من النافذة."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"تحقق من تشغيل موالف الشبكة وتوصيله بمصدر إشارة التلفزيون.\n\nفي حالة استخدام هوائي للتحديث عبر الهواء، قد تحتاج إلى ضبط موضعه أو تجاهه لاستقبال معظم القنوات. وللحصول على أفضل النتائج، يمكنك وضعه في مكان مرتفع أو بالقرب من النافذة."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"متابعة"</item>
- <item msgid="727245208787621142">"ليس الآن"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"هل تريد إعادة تشغيل إعداد القنوات؟"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"سيؤدي هذا إلى إزالة القنوات التي تم العثور عليها من موالف التلفزيون والبحث مرة أخرى عن قنوات جديدة.\n\nتحقق من توصيل التلفزيون بمصدر إشارة البث التلفزيوني.\n\nإذا كنت تستخدم هوائيًا للتحديث عبر الهواء، فقد تحتاج إلى ضبط موضعه أو اتجاهه لاستقبال معظم القنوات، وللحصول على أفضل النتائج، ضعه عاليًا بالقرب من النافذة."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"‏سيؤدي هذا إلى إزالة القنوات التي تم العثور عليها من الموالف عبر USB والبحث مرة أخرى عن قنوات جديدة.\n\nتحقق من توصيل الموالف عبر USB بمصدر إشارة البث التلفزيوني.\n\nإذا كنت تستخدم هوائيًا للتحديث عبر الهواء، فقد تحتاج إلى ضبط موضعه أو اتجاهه لاستقبال معظم القنوات، وللحصول على أفضل النتائج، ضعه عاليًا بالقرب من النافذة."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"سيؤدي هذا إلى إزالة القنوات الموجودة من موالف الشبكة وإعادة المسح بحثًا عن القنوات الجديدة.\n\nتحقق من تشغيل موالف الشبكة وتوصيله بمصدر إشارة التلفزيون.\n\nفي حالة استخدام هوائي للتحديث عبر الهواء، قد تحتاج إلى ضبط موضعه أو تجاهه لاستقبال معظم القنوات. وللحصول على أفضل النتائج، يمكنك وضعه في مكان مرتفع أو بالقرب من النافذة."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"متابعة"</item>
- <item msgid="235450158666155406">"إلغاء"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"تحديد نوع الاتصال"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"اختر \"الهوائي\" في حالة توصيل هوائي خارجي بالموالف، واختر \"الكابل\" إذا كان مصدر القنوات هو مقدم خدمة عبر الكابل، أما إذا لم تكن متأكدًا، فسيتم البحث عن القنوات من خلال هذين النوعين، إلا أن هذا قد يستغرق وقتًا أطول."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"الهوائي"</item>
- <item msgid="2670079958754180142">"الكابل"</item>
- <item msgid="36774059871728525">"لست متأكدًا"</item>
- <item msgid="6881204453182153978">"للتطوير فقط"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"إعداد موالف التلفزيون"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"‏إعداد موالف قنوات USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"إعداد موالف قناة الشبكة"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"قد يستغرق هذا عدة دقائق"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"لا يتوفر الموالف مؤقتًا أو سبق استخدامه بواسطة التسجيل."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="zero">‏تم العثور على %1$d قناة</item>
- <item quantity="two">‏تم العثور على قناتين (%1$d)</item>
- <item quantity="few">‏تم العثور على %1$d قنوات</item>
- <item quantity="many">‏تم العثور على %1$d قناة</item>
- <item quantity="other">‏تم العثور على %1$d قناة</item>
- <item quantity="one">‏تم العثور على %1$d قناة</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"إيقاف البحث عن القنوات"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="zero">‏تم العثور على %1$d قناة</item>
- <item quantity="two">‏تم العثور على قناتين (%1$d)</item>
- <item quantity="few">‏تم العثور على %1$d قنوات</item>
- <item quantity="many">‏تم العثور على %1$d قناة</item>
- <item quantity="other">‏تم العثور على %1$d قناة</item>
- <item quantity="one">‏تم العثور على %1$d قناة</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="zero">‏جيد! تم العثور على %1$d قناة أثناء البحث عن القنوات. إذا كان ذلك لا يبدو صحيحًا، فجرِّب ضبط موضع الهوائي وإعادة البحث.</item>
- <item quantity="two">‏جيد! تم العثور على قناتين (%1$d) أثناء البحث عن القنوات. إذا كان ذلك لا يبدو صحيحًا، فجرِّب ضبط موضع الهوائي وإعادة البحث.</item>
- <item quantity="few">‏جيد! تم العثور على %1$d قنوات أثناء البحث عن القنوات. إذا كان ذلك لا يبدو صحيحًا، فجرِّب ضبط موضع الهوائي وإعادة البحث.</item>
- <item quantity="many">‏جيد! تم العثور على %1$d قناة أثناء البحث عن القنوات. إذا كان ذلك لا يبدو صحيحًا، فجرِّب ضبط موضع الهوائي وإعادة البحث.</item>
- <item quantity="other">‏جيد! تم العثور على %1$d قناة أثناء البحث عن القنوات. إذا كان ذلك لا يبدو صحيحًا، فجرِّب ضبط موضع الهوائي وإعادة البحث.</item>
- <item quantity="one">‏جيد! تم العثور على %1$d قناة أثناء البحث عن القنوات. إذا كان ذلك لا يبدو صحيحًا، فجرِّب ضبط موضع الهوائي وإعادة البحث.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"تم"</item>
- <item msgid="2480490326672924828">"بحث مرة أخرى"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"لم يتم العثور على قنوات"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"لم يتم العثور على أي قنوات أثناء البحث، لذا عليك التحقق من توصيل التلفزيون بمصدر إشارة البث التلفزيوني.\n\nإذا كنت تستخدم هوائيًا للتحديث عبر الهواء، فاضبط موضعه أو اتجاهه، وللحصول على أفضل النتائج، ضعه عاليًا بالقرب من النافذة ثم أعد البحث."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"‏لم يتم العثور على أي قنوات أثناء البحث، تحقق من توصيل الموالف عبر USB بمصدر إشارة البث التلفزيوني.\n\nإذا كنت تستخدم هوائيًا للتحديث عبر الهواء، فاضبط موضعه أو اتجاهه، وللحصول على أفضل النتائج، ضعه عاليًا بالقرب من النافذة ثم أعد البحث."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"لم يتم العثور على أي قنوات خلال المسح. تحقق من تشغيل موالف الشبكة وتوصيله بمصدر إشارة التلفزيون.\n\nفي حالة استخدام هوائي للتحديث عبر الهواء، يجب ضبط موضعه أو تجاهه. وللحصول على أفضل النتائج، يمكنك وضعه في مكان مرتفع أو بالقرب من النافذة وإعادة المسح."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"بحث مرة أخرى"</item>
- <item msgid="2092797862490235174">"تم"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"البحث عن قنوات تلفزيونية"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"إعداد موالف التلفزيون"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"‏إعداد موالف التلفزيون عبر USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"إعداد موالف التلفزيون على الشبكة"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"‏تم فصل موالف التلفزيون عبر USB."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"تم فصل موالف الشبكة."</string>
-</resources>
diff --git a/usbtuner-res/values-az-rAZ/strings.xml b/usbtuner-res/values-az-rAZ/strings.xml
deleted file mode 100644
index 2022ad7b..00000000
--- a/usbtuner-res/values-az-rAZ/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"TV Kökləyici"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB TV Kökləyici"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Network TV Tuner (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Lütfən, prosesi başa çatdırmaq üçün gözləyin"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Sazlayıcı proqram təminatı yenicə güncəllənib. Kanalları yenidən skan edin."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Audionu aktiv etmək üçün sistem səs ayarlarında əhatəli səsi aktiv edin"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Audio oxuna bilmir. Digər TV-dən istifadə edin"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Kanal kökləyici quraşdırması"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"TV Kökləyici quraşdırması"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB Kanal kökləyici quraşdırması"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Şəbəkə kökləyici quraşdırması"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"TV-nizin TV siqnal mənbəyinə qoşulu olduğunu doğrulayın.\n\nHava antenası istifadə etdikdə, daha çox kanal üçün onun yerini və istiqamətini tənzimləməlisiniz. Daha yaxşı nəticələr üçün hündür yerə və pəncərəyə yaxın yerləşdirin."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"USB kökləyicinin taxılı olduğunu və TV siqnal mənbəyinə qoşulu olduğunu doğrulayın.\n\nHava antenası istifadə etdikdə, daha çox kanal üçün onun yerini və istiqamətini tənzimləməlisiniz. Daha yaxşı nəticələr üçün hündür yerə və pəncərəyə yaxın yerləşdirin."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Şəbəkə kökləyicinin yanılı olduğunu və TV siqnal mənbəyinə qoşulu olduğunu doğrulayın.\n\nHava antenası istifadə etdikdə, daha çox kanal üçün onun yerini və istiqamətini tənzimləməlisiniz. Daha yaxşı nəticələr üçün hündür yerə və pəncərəyə yaxın yerləşdirin."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Davam edin"</item>
- <item msgid="727245208787621142">"İndi yox"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Kanal quraşdırması yenidən işə salınsın?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Bu, TV kökləyici ilə tapılmış kanalları siləcək və yeni kanalları yenidən skan edəcək.\n\nTV-nizin TV siqnal mənbəyinə qoşulu olduğunu doğrulayın.\n\nHava antenası istifadə etdikdə, daha çox kanal üçün onun yerini və istiqamətini tənzimləməlisiniz. Daha yaxşı nəticələr üçün hündür yerə və pəncərəyə yaxın yerləşdirin."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Bu USB kökləyici ilə tapılmış kanalları siləcək və yeni kanalları yenidən skan edəcək.\n\nUSB kökləyicinin taxılı olduğunu və TV siqnal mənbəyinə qoşulu olduğunu doğrulayın.\n\nHava antenası istifadə etdikdə, daha çox kanal üçün onun yerini və istiqamətini tənzimləməlisiniz. Daha yaxşı nəticələr üçün hündür yerə və pəncərəyə yaxın yerləşdirin."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Bu Şəbəkə kökləyici ilə tapılmış kanalları siləcək və yeni kanalları yenidən skan edəcək.\n\nŞəbəkə kökləyicinin yanılı olduğunu və TV siqnal mənbəyinə qoşulu olduğunu doğrulayın.\n\nHava antenası istifadə etdikdə, daha çox kanal üçün onun yerini və istiqamətini tənzimləməlisiniz. Daha yaxşı nəticələr üçün hündür yerə və pəncərəyə yaxın yerləşdirin."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Davam edin"</item>
- <item msgid="235450158666155406">"Ləğv edin"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Bağlantı növünü seçin"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Əgər kökləyiciyə xarici antena qoşulubsa Antena seçimini edin. Əgər kanallar kabel xidməti provayderi ilə gəlirsə, onda Kabel seçimini edin. Əgər əmin deyilsinizsə, hər iki növ skan ediləcək, amma bu çox vaxt çəkəcək."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antena"</item>
- <item msgid="2670079958754180142">"Kabel"</item>
- <item msgid="36774059871728525">"Əmin deyiləm"</item>
- <item msgid="6881204453182153978">"Yalnız inkişaf"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"TV kökləyici quraşdırması"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB Kanal kökləyici quraşdırması"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Şəbəkə kanalı kökləyici quraşdırması"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Bu bir neçə dəqiqə çəkə bilər"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Kökləyici müvəqqəti əlçatan deyil və qeydə alma tərəfindən istifadə olunub."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d kanal tapıldı</item>
- <item quantity="one">%1$d kanal tapıldı</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"KANAL SKANINI DAYANDIRIN"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d kanal tapıldı</item>
- <item quantity="one">%1$d kanal tapıldı</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Yaxşıdır! Kanal skanı zamanı %1$d kanal tapıldı. Əgər düzgün deyilsə, antenanın yerini dəyişin və yenidən skan edin.</item>
- <item quantity="one">Yaxşıdır! Kanal skanı zamanı %1$d kanal tapıldı. Əgər düzgün deyilsə, antenanın yerini dəyişin və yenidən skan edin.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Hazırdır"</item>
- <item msgid="2480490326672924828">"Yenidən skan edin"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Kanal tapılmadı"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Skan ilə heç bir kanal tapılmadı. TV-nizin TV siqnal mənbəyinə qoşulu olduğunu doğrulayın.\n\nHava antenası istifadə etdikdə, daha çox kanal üçün onun yerini və istiqamətini tənzimləməlisiniz. Daha yaxşı nəticələr üçün hündür yerə və pəncərəyə yaxın yerləşdirin."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Skan ilə heç bir kanal tapılmadı. USB kökləyicinin taxılı olduğunu və TV siqnal mənbəyinə qoşulu olduğunu doğrulayın.\n\nHava antenası istifadə etdikdə, daha çox kanal üçün onun yerini və istiqamətini tənzimləməlisiniz. Daha yaxşı nəticələr üçün hündür yerə və pəncərəyə yaxın yerləşdirin."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Skan ilə heç bir kanal tapılmadı. Şəbəkə kökləyicinin yanılı olduğunu və TV siqnal mənbəyinə qoşulu olduğunu doğrulayın.\n\nHava antenası istifadə etdikdə, daha çox kanal üçün onun yerini və istiqamətini tənzimləməlisiniz. Daha yaxşı nəticələr üçün hündür yerə və pəncərəyə yaxın yerləşdirin."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Yenidən skan edin"</item>
- <item msgid="2092797862490235174">"Hazırdır"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"TV kanalları üçün skan"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"TV Kökləyici quraşdırması"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB TV Kökləyici quraşdırması"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Şəbəkə TV Kökləyici quraşdırması"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB TV kökləyicisinin bağlantısı kəsildi."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Şəbəkə kökləyicisinin bağlantısı kəsildi."</string>
-</resources>
diff --git a/usbtuner-res/values-bg/strings.xml b/usbtuner-res/values-bg/strings.xml
deleted file mode 100644
index 996023ff..00000000
--- a/usbtuner-res/values-bg/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"Телевизионен тунер"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"Телевизионен USB тунер"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Network TV Tuner (БЕТА)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Моля, изчакайте обработването да завърши"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Софтуерът на тунера е актуализиран наскоро. Моля, сканирайте отново каналите."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Активирайте обемния звук от настройките за системния, за да включите аудиото"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Звукът не може да се възпроизведе. Моля, опитайте на друг телевизор"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Настройване на тунера за канали"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Настройване на телевизионния тунер"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Настройване на USB тунера за канали"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Настройване на мрежовия тунер"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Уверете се, че телевизорът ви е свързан с източник на телевизионен сигнал.\n\nАко използвате безжична антена, може да се наложи да коригирате разположението или посоката й, за да получите оптимален брой канали. За най-добри резултати я поставете високо и близо до прозорец."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Уверете се, че USB тунерът е включен и свързан с източник на телевизионен сигнал.\n\nАко използвате безжична антена, може да се наложи да коригирате разположението или посоката й, за да получите оптимален брой канали. За най-добри резултати я поставете високо и близо до прозорец."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Уверете се, че мрежовият тунер е включен и свързан с източник на телевизионен сигнал.\n\nАко използвате безжична антена, може да се наложи да коригирате разположението или посоката й, за да уловите най-много канали. За най-добри резултати я поставете високо и близо до прозорец."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Напред"</item>
- <item msgid="727245208787621142">"Не сега"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Да се стартира ли отново настройването на каналите?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Така ще премахнете намерените от телевизионния тунер канали и ще сканирате за нови.\n\nУверете се, че телевизорът ви е свързан с източник на телевизионен сигнал.\n\nАко използвате безжична антена, може да се наложи да коригирате разположението или посоката й, за да получите оптимален брой канали. За най-добри резултати я поставете високо и близо до прозорец."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Така ще премахнете намерените от USB тунера канали и ще сканирате за нови.\n\nУверете се, че USB тунерът е включен и свързан с източник на телевизионен сигнал.\n\nАко използвате безжична антена, може да се наложи да коригирате разположението или посоката й, за да получите оптимален брой канали. За най-добри резултати я поставете високо и близо до прозорец."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Така ще премахнете намерените от мрежовия тунер канали и ще сканирате за нови.\n\nУверете се, че мрежовият тунер е включен и свързан с източник на телевизионен сигнал.\n\nАко използвате безжична антена, може да се наложи да коригирате разположението или посоката й, за да уловите най-много канали. За най-добри резултати я поставете високо и близо до прозорец."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Напред"</item>
- <item msgid="235450158666155406">"Отказ"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Избиране на типа връзка"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Изберете „Антена“, ако с тунера е свързана външна антена. Посочете „Кабел“, ако източникът на каналите ви е доставчик на кабелна услуга. В случай че не сте сигурни, ще се сканира и за двата типа, но това може да отнеме по-дълго време."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Антена"</item>
- <item msgid="2670079958754180142">"Кабел"</item>
- <item msgid="36774059871728525">"Не знам"</item>
- <item msgid="6881204453182153978">"Само програмиране"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Настройване на телевизионния тунер"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Настройване на USB тунера за канали"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Настройване на мрежовия тунер за канали"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Това може да отнеме няколко минути"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Временно няма достъп до тунера или той вече се използва за запис."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">Намерени са %1$d канала</item>
- <item quantity="one">Намерен е %1$d канал</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"СПИРАНЕ НА СКАНИРАНЕТО ЗА КАНАЛИ"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">Намерени са %1$d канала</item>
- <item quantity="one">Намерен е %1$d канал</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Много добре! По време на сканирането бяха намерени %1$d канала. Ако това не изглежда вярно, коригирайте позицията на антената и сканирайте отново.</item>
- <item quantity="one">Много добре! По време на сканирането бе намерен %1$d канал. Ако това не изглежда вярно, коригирайте позицията на антената и сканирайте отново.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Готово"</item>
- <item msgid="2480490326672924828">"Повторно сканиране"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Няма намерени канали"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"При сканирането не бяха открити канали. Уверете се, че телевизорът ви е свързан с източник на телевизионен сигнал.\n\nАко използвате безжична антена, коригирайте разположението или посоката й. За най-добри резултати я поставете високо и близо до прозорец и сканирайте отново."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"При сканирането не бяха открити канали. Уверете се, че USB тунерът е включен и свързан с източник на телевизионен сигнал.\n\nАко използвате безжична антена, коригирайте разположението или посоката й. За най-добри резултати я поставете високо и близо до прозорец и сканирайте отново."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"При сканирането не бяха открити канали. Уверете се, че мрежовият тунер е включен и свързан с източник на телевизионен сигнал.\n\nАко използвате безжична антена, коригирайте разположението или посоката й. За най-добри резултати я поставете високо и близо до прозорец и сканирайте отново."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Повторно сканиране"</item>
- <item msgid="2092797862490235174">"Готово"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Сканиране за телевизионни канали"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Настройване на телевизионния тунер"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Настройване на телевизионния USB тунер"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Настройване на Network TV Tuner"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"Връзката с телевизионния USB тунер е прекратена."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Връзката с мрежовия тунер е прекратена."</string>
-</resources>
diff --git a/usbtuner-res/values-bn-rBD/strings.xml b/usbtuner-res/values-bn-rBD/strings.xml
deleted file mode 100644
index 8eb8a558..00000000
--- a/usbtuner-res/values-bn-rBD/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"TV টিউনার"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB TV টিউনার"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"নেটওয়ার্ক TV টিউনার (বিটা)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"প্রক্রিয়াকরণ সম্পূর্ণ না হওয়া পর্যন্ত অনুগ্রহ করে অপেক্ষা করুন"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"টিউনার সফ্টওয়্যার সম্প্রতি আপডেট করা হয়েছে৷ অনুগ্রহ করে চ্যানেলগুলি আবার স্ক্যান করুন৷"</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"অডিও সক্ষম করতে সিস্টেম সাউন্ড সেটিংসে সারাউন্ড সাউন্ড সক্ষম করুন"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"অডিও প্লে করা যাবে না৷ অনুগ্রহ করে অন্য টিভি ব্যবহার করার চেষ্ট করুন"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"চ্যানেল টিউনার সেট আপ"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"TV টিউনার সেট আপ"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB চ্যানেল টিউনার সেটআপ"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"নেটওয়ার্ক টিউনার সেট আপ"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"আপনার TV একটি TV সিগন্যাল উৎসের সাথে সংযুক্ত রয়েছে কিনা যাচাই করুন৷\n\nযদি কোনো ওভার-দ্য-এয়ার অ্যান্টেনা ব্যবহার করা হয় তাহলে অধিকাংশ চ্যানেল পাওয়ার জন্য আপনাকে সেটির অবস্থান এবং দিক ঠিক করতে হতে পারে৷ আরো ভাল ফলাফলের জন্য, এটিকে উচুঁতে কোনো জানলার সামনে রাখুন এবং আবার স্ক্যান করুন৷"</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"USB টিউনার প্ল্যাগ ইন রয়েছে এবং একটি TV সিগন্যাল উৎসের সাথে সংযুক্ত রয়েছে তা যাচাই করুন৷\n\nযদি কোনো ওভার-দ্য-এয়ার অ্যান্টেনা ব্যবহার করা হয় তাহলে অধিকাংশ চ্যানেল পাওয়ার জন্য আপনাকে সেটির অবস্থান এবং দিক ঠিক করতে হতে পারে৷ আরো ভাল ফলাফলের জন্য, এটিকে উচুঁতে কোনো জানলার সামনে রাখুন এবং আবার স্ক্যান করুন৷"</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"নেটওয়ার্ক টিউনার চালু রয়েছে এবং কোনো TV সিগন্যাল উৎসের সাথে সংযুক্ত রয়েছে কিনা যাচাই করুন৷\n\nযদি কোনো ওভার-দ্য-এয়ার অ্যান্টেনা ব্যবহার করা হয় তাহলে অধিকাংশ চ্যানেল পাওয়ার জন্য আপনাকে সেটির অবস্থান এবং দিক ঠিক করতে হতে পারে৷ আরো ভাল ফলাফলের জন্য, এটিকে উঁচুতে কোনো জানলার সামনে রেখে আবার স্ক্যান করুন৷"</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"চালিয়ে যান"</item>
- <item msgid="727245208787621142">"এখনই নয়"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"আবার চ্যানেল সেট আপ করবেন?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"এটি TV টিউনার থেকে পাওয়া চ্যানেলগুলিকে মুছবে এবং নতুন চ্যানেলগুলির জন্য আবার স্ক্যান করবে৷\n\nআপনার TV একটি TV সিগন্যাল উৎসের সাথে সংযুক্ত রয়েছে কিনা যাচাই করুন৷\n\nযদি কোনো ওভার-দ্য-এয়ার অ্যান্টেনা ব্যবহার করা হয় তাহলে অধিকাংশ চ্যানেল পাওয়ার জন্য আপনাকে সেটির অবস্থান এবং দিক ঠিক করতে হতে পারে৷ আরো ভাল ফলাফলের জন্য, এটিকে উচুঁতে কোনো জানলার সামনে রাখুন এবং আবার স্ক্যান করুন৷"</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"এটি USB টিউনার থেকে পাওয়া চ্যানেলগুলিকে মুছবে এবং নতুন চ্যানেলগুলির জন্য আবার স্ক্যান করবে৷\n\nUSB টিউনার প্ল্যাগ ইন রয়েছে এবং একটি TV সিগন্যাল উৎসের সাথে সংযুক্ত রয়েছে তা যাচাই করুন৷\n\nযদি কোনো ওভার-দ্য-এয়ার অ্যান্টেনা ব্যবহার করা হয় তাহলে অধিকাংশ চ্যানেল পাওয়ার জন্য আপনাকে সেটির অবস্থান এবং দিক ঠিক করতে হতে পারে৷ আরো ভাল ফলাফলের জন্য, এটিকে উচুঁতে কোনো জানলার সামনে রাখুন এবং আবার স্ক্যান করুন৷"</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"এটি নেটওয়ার্ক টিউনার থেকে পাওয়া চ্যানেলগুলি মুছবে এবং নতুন চ্যানেলগুলিকে আবার স্ক্যান করবে৷\n\nনেটওয়ার্ক টিউনার চালু রয়েছে এবং কোনো TV সিগন্যাল উৎসের সাথে সংযুক্ত রয়েছে কিনা যাচাই করুন৷\n\nযদি কোনো ওভার-দ্য-এয়ার অ্যান্টেনা ব্যবহার করা হয় তাহলে অধিকাংশ চ্যানেল পাওয়ার জন্য আপনাকে সেটির অবস্থান এবং দিক ঠিক করতে হতে পারে৷ আরো ভাল ফলাফলের জন্য, এটিকে উঁচুতে কোনো জানলার সামনে রেখে আবার স্ক্যান করুন৷"</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"চালিয়ে যান"</item>
- <item msgid="235450158666155406">"বাতিল করুন"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"সংযোগের প্রকার নির্বাচন করুন"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"যদি আপনার টিউনারের সাথে কোনো বাহ্যিক অ্যান্টেনা সংযুক্ত থাকে তাহলে অ্যান্টেনা বাছুন৷ আপনার চ্যানেলগুলি যদি কোনো কেবল পরিষেবা প্রদানকারীর থেকে আসে তাহলে কেবল বাছুন৷ আপনি যদি নিশ্চিত না হন তাহলে উভয় প্রকারের স্ক্যান করা হবে, কিন্তু এটি অনেক সময় নিতে পারে৷"</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"অ্যান্টেনা"</item>
- <item msgid="2670079958754180142">"কেবল"</item>
- <item msgid="36774059871728525">"নিশ্চিত নই"</item>
- <item msgid="6881204453182153978">"শুধুমাত্র বিকাশের উদ্দেশ্য"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"TV টিউনার সেট আপ"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB চ্যানেল টিউনার সেটআপ"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"নেটওয়ার্ক চ্যানেল টিউনার সেটআপ"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"এটি কয়েক মিনিট সময় নিতে পারে"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"টিউনার অস্থায়ীভাবে অনুপলব্ধ বা রেকডিংয়ে ইতিমধ্যেই ব্যবহৃত হয়েছে৷"</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="one">%1$dটি চ্যানেল পাওয়া গেছে</item>
- <item quantity="other">%1$dটি চ্যানেল পাওয়া গেছে</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"চ্যানেল স্ক্যান করা থামান"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="one">%1$dটি চ্যানেল পাওয়া গেছে</item>
- <item quantity="other">%1$dটি চ্যানেল পাওয়া গেছে</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="one">সুন্দর! চ্যানেল স্ক্যান করার সময়ে %1$dটি চ্যানেল পাওয়া গেছে। এটিকে যদি ঠিক বলে না মনে হয়, তাহলে অ্যান্টেনার অবস্থান ঠিক করার চেষ্টা করুন ও আবার স্ক্যান করুন।</item>
- <item quantity="other">সুন্দর! চ্যানেল স্ক্যান করার সময়ে %1$dটি চ্যানেল পাওয়া গেছে। এটিকে যদি ঠিক বলে না মনে হয়, তাহলে অ্যান্টেনার অবস্থান ঠিক করার চেষ্টা করুন ও আবার স্ক্যান করুন।</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"সম্পন্ন"</item>
- <item msgid="2480490326672924828">"আবার স্ক্যান করুন"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"কোনো চ্যানেল খুঁজে পাওয়া যায়নি"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"স্ক্যান করে কোনো চ্যানেল খুঁজে পাওয়া যায়নি৷ আপনার TV একটি TV সিগন্যাল উৎসের সাথে সংযুক্ত রয়েছে কিনা যাচাই করুন৷\n\nযদি কোনো ওভার-দ্য-এয়ার অ্যান্টেনা ব্যবহার করা হয় তাহলে সেটির অবস্থান এবং দিক ঠিক করুন৷ আরো ভাল ফলাফলের জন্য, এটিকে উচুঁতে কোনো জানলার সামনে রাখুন এবং আবার স্ক্যান করুন৷"</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"স্ক্যান করে কোনো চ্যানেল খুঁজে পাওয়া যায়নি৷ USB টিউনার প্ল্যাগ ইন রয়েছে এবং একটি TV সিগন্যাল উৎসের সাথে সংযুক্ত রয়েছে তা যাচাই করুন৷\n\nযদি কোনো ওভার-দ্য-এয়ার অ্যান্টেনা ব্যবহার করা হয় তাহলে সেটির অবস্থান এবং দিক ঠিক করুন৷ আরো ভাল ফলাফলের জন্য, এটিকে উচুঁতে কোনো জানলার সামনে রাখুন এবং আবার স্ক্যান করুন৷"</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"স্ক্যান করে কোনো চ্যানেল খুঁজে পাওয়া যায়নি৷ নেটওয়ার্ক টিউনার চালু এবং কোনো TV সিগন্যাল উৎসের সাথে সংযুক্ত রয়েছে কিনা যাচাই করুন৷\n\nযদি কোনো ওভার-দ্য-এয়ার অ্যান্টেনা ব্যবহার করা হয় তাহলে সেটির অবস্থান এবং দিক ঠিক করুন৷ আরো ভাল ফলাফলের জন্য, এটিকে উঁচুতে কোনো জানলার সামনে রেখে আবার স্ক্যান করুন৷"</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"আবার স্ক্যান করুন"</item>
- <item msgid="2092797862490235174">"সম্পন্ন"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"টিভি চ্যানেলগুলি স্ক্যান করুন"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"TV টিউনার সেট আপ"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB টিভি টিউনার সেট আপ"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"নেটওয়ার্ক TV টিউনার সেট আপ"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB টিভি টিউনারের সংযোগ বিচ্ছিন্ন হয়েছে।"</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"নেটওয়ার্ক টিউনারের সংযোগ বিচ্ছিন্ন হয়েছে।"</string>
-</resources>
diff --git a/usbtuner-res/values-ca/strings.xml b/usbtuner-res/values-ca/strings.xml
deleted file mode 100644
index d005204e..00000000
--- a/usbtuner-res/values-ca/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"Sintonitzador de televisió"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"Sintonitzador de televisió USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Sintonitzador de televisió en xarxa (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Espera per finalitzar el processament"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"El programari del sintonitzador s\'ha actualitzat fa poc. Torna a cercar els canals."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Activa el so envoltant a la configuració de so del sistema per activar l\'àudio"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"No es pot reproduir l\'àudio. Prova-ho amb un altre televisor."</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Configuració del sintonitzador de canals"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Configuració del sintonitzador de televisió"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Configuració del sintonitzador de canals USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Configuració del sintonitzador en xarxa"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Verifica que el teu televisor estigui connectat a una font de senyal de televisió.\n\nSi fas servir una antena aèria, pot ser que calgui ajustar-ne la ubicació o la direcció per rebre el màxim de canals. Per obtenir els millors resultats, col·loca-la en un lloc elevat i a prop d\'una finestra."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Verifica que el sintonitzador USB estigui endollat i connectat a una font de senyal de televisió.\n\nSi fas servir una antena aèria, pot ser que calgui ajustar-ne la ubicació o la direcció per rebre el màxim de canals. Per obtenir els millors resultats, col·loca-la en un lloc elevat i a prop d\'una finestra."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Comprova que el sintonitzador en xarxa estigui engegat i connectat a una font de senyal de televisió.\n\nSi fas servir una antena aèria, pot ser que calgui ajustar-ne la ubicació o la direcció per rebre el màxim de canals. Per obtenir uns resultats millors, col·loca-la en un lloc elevat i a prop d\'una finestra."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Continua"</item>
- <item msgid="727245208787621142">"Ara no"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Vols tornar a executar la configuració de canals?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Això farà que se suprimeixin del sintonitzador de televisió els canals que s\'han trobat i que es tornin a cercar canals nous.\n\nVerifica que el teu televisor estigui connectat a una font de senyal de televisió.\n\nSi fas servir una antena aèria, pot ser que calgui ajustar-ne la ubicació o la direcció per rebre el màxim de canals. Per obtenir els millors resultats, col·loca-la en un lloc elevat i a prop d\'una finestra."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Això farà que se suprimeixin del sintonitzador USB els canals que s\'han trobat i que es tornin a cercar canals nous.\n\nVerifica que el sintonitzador USB estigui endollat i connectat a una font de senyal de televisió.\n\nSi fas servir una antena aèria, pot ser que calgui ajustar-ne la ubicació o la direcció per rebre el màxim de canals. Per obtenir els millors resultats, col·loca-la en un lloc elevat i a prop d\'una finestra."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Això farà que se suprimeixin del sintonitzador en xarxa els canals trobats i que se\'n tornin a cercar de nous.\n\nComprova que el sintonitzador en xarxa estigui engegat i connectat a una font de senyal de televisió.\n\nSi fas servir una antena aèria, pot ser que calgui ajustar-ne la ubicació o la direcció per rebre el màxim de canals. Per obtenir uns resultats millors, col·loca-la en un lloc elevat i a prop d\'una finestra."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Continua"</item>
- <item msgid="235450158666155406">"Cancel·la"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Selecciona el tipus de connexió"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Tria Antena si hi ha una antena externa connectada al sintonitzador. Tria Cable si els canals provenen d\'un proveïdor de serveis per cable. Si no n\'estàs segur, se cercaran els dos tipus de canals, però el procés pot trigar més."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antena"</item>
- <item msgid="2670079958754180142">"Cable"</item>
- <item msgid="36774059871728525">"No ho sé"</item>
- <item msgid="6881204453182153978">"Només per a desenvolupament"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Configuració del sintonitzador de televisió"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Configuració del sintonitzador de canals USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Configuració del sintonitzador de canals en xarxa"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Aquesta acció pot tardar uns quants minuts"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"El sintonitzador no està disponible en aquest moment o bé ja s\'està utilitzant en un enregistrament."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">S\'han trobat %1$d canals</item>
- <item quantity="one">S\'ha trobat %1$d canal</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"ATURA LA CERCA DE CANALS"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">S\'han trobat %1$d canals</item>
- <item quantity="one">S\'ha trobat %1$d canal</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Molt bé! S\'han trobat %1$d canals durant la cerca. Si creus que no és correcte, prova d\'ajustar la posició de l\'antena i torna a cercar canals.</item>
- <item quantity="one">Molt bé! S\'ha trobat %1$d canal durant la cerca. Si creus que no és correcte, prova d\'ajustar la posició de l\'antena i torna a cercar canals.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Fet"</item>
- <item msgid="2480490326672924828">"Torna a cercar"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"No s\'ha trobat cap canal"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"No s\'ha trobat cap canal. Verifica que el teu televisor estigui connectat a una font de senyal de televisió.\n\nSi fas servir una antena aèria, ajusta\'n la ubicació o la direcció. Per obtenir els millors resultats, col·loca-la en un lloc elevat i a prop d\'una finestra i torna a cercar canals."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"La cerca no ha trobat cap canal. Verifica que el sintonitzador USB estigui endollat i connectat a una font de senyal de televisió.\n\nSi fas servir una antena aèria, ajusta\'n la ubicació o la direcció. Per obtenir els millors resultats, col·loca-la en un lloc elevat i a prop d\'una finestra i torna a cercar."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"No s\'ha trobat cap canal. Comprova que el sintonitzador en xarxa estigui engegat i connectat a una font de senyal de televisió.\n\nSi fas servir una antena aèria, ajusta\'n la ubicació o la direcció. Per obtenir uns resultats millors, col·loca-la en un lloc elevat i a prop d\'una finestra i torna a cercar."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Torna a cercar"</item>
- <item msgid="2092797862490235174">"Fet"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Cerca canals de televisió"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Configuració del sintonitzador de televisió"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Configuració del sintonitzador de televisió USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Configuració del sintonitzador de televisió en xarxa"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"El sintonitzador de televisió USB no està connectat."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"El sintonitzador de la xarxa no està connectat."</string>
-</resources>
diff --git a/usbtuner-res/values-cs/strings.xml b/usbtuner-res/values-cs/strings.xml
deleted file mode 100644
index 69436042..00000000
--- a/usbtuner-res/values-cs/strings.xml
+++ /dev/null
@@ -1,96 +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="bt_app_name" msgid="5515382901862469770">"Televizní tuner"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"Televizní tuner USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Síťový televizní tuner (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Vyčkejte prosím, než bude zpracování dokončeno"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Software tuneru byl nedávno aktualizován. Vyhledejte prosím kanály znovu."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Chcete-li zapnout zvuk, v nastavení systémového zvuku povolte prostorový zvuk"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Zvuk nelze přehrát. Zkuste použít jinou televizi."</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Nastavení tuneru kanálů"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Nastavení televizního tuneru"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Nastavení tuneru kanálů USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Nastavení síťového tuneru"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Zkontrolujte, zda je televize připojena ke zdroji televizního signálu.\n\nPokud používáte bezdrátovou anténu, možná budete muset za účelem příjmu co největšího počtu kanálů upravit její umístění nebo nasměrování. Nejlepších výsledků dosáhnete, pokud ji umístíte vysoko a blízko okna."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Zkontrolujte, zda je tuner USB připojen k zařízení a ke zdroji televizního signálu.\n\nPokud používáte bezdrátovou anténu, možná budete muset za účelem příjmu co největšího počtu kanálů upravit její umístění nebo nasměrování. Nejlepších výsledků dosáhnete, pokud ji umístíte vysoko a blízko okna."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Zkontrolujte, zda je síťový tuner zapnut a připojen ke zdroji televizního signálu.\n\nPokud používáte bezdrátovou anténu, možná budete muset za účelem příjmu co největšího počtu kanálů upravit její umístění nebo nasměrování. Nejlepších výsledků dosáhnete, pokud ji umístíte vysoko a blízko okna."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Pokračovat"</item>
- <item msgid="727245208787621142">"Teď ne"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Znovu spustit nastavení kanálů?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Tímto odstraníte kanály nalezené pomocí televizního tuneru a znovu vyhledáte nové kanály.\n\nZkontrolujte, zda je televize připojena ke zdroji televizního signálu.\n\nPokud používáte bezdrátovou anténu, možná budete muset za účelem příjmu co největšího počtu kanálů upravit její umístění nebo nasměrování. Nejlepších výsledků dosáhnete, pokud ji umístíte vysoko a blízko okna."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Tímto odstraníte kanály nalezené pomocí tuneru USB a znovu vyhledáte nové kanály.\n\nZkontrolujte, zda je tuner USB připojen k zařízení a ke zdroji televizního signálu.\n\nPokud používáte bezdrátovou anténu, možná budete muset za účelem příjmu co největšího počtu kanálů upravit její umístění nebo nasměrování. Nejlepších výsledků dosáhnete, pokud ji umístíte vysoko a blízko okna."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Tímto odstraníte kanály nalezené pomocí síťového tuneru a znovu vyhledáte nové kanály.\n\nZkontrolujte, zda je síťový tuner zapnut a připojen ke zdroji televizního signálu.\n\nPokud používáte bezdrátovou anténu, možná budete muset za účelem příjmu co největšího počtu kanálů upravit její umístění nebo nasměrování. Nejlepších výsledků dosáhnete, pokud ji umístíte vysoko a blízko okna."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Pokračovat"</item>
- <item msgid="235450158666155406">"Zrušit"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Vyberte typ připojení"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Pokud je k tuneru připojena externí anténa, zvolte možnost Anténa. Pokud je zdrojem kanálů poskytovatel kabelových služeb, zvolte možnost Kabel. Pokud si nejste jisti, budou prohledány oba typy. Může to však trvat déle."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Anténa"</item>
- <item msgid="2670079958754180142">"Kabel"</item>
- <item msgid="36774059871728525">"Nevím"</item>
- <item msgid="6881204453182153978">"Pouze pro vývojáře"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Nastavení televizního tuneru"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Nastavení tuneru kanálů USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Nastavení kanálů síťového tuneru"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Tato akce může trvat několik minut."</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Tuner dočasně není k dispozici, případně je právě používán k nahrávání."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="few">Byly nalezeny %1$d kanály</item>
- <item quantity="many">Bylo nalezeno %1$d kanálu</item>
- <item quantity="other">Bylo nalezeno %1$d kanálů</item>
- <item quantity="one">Byl nalezen %1$d kanál</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"ZASTAVIT VYHLEDÁVÁNÍ KANÁLŮ"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="few">Byly nalezeny %1$d kanály</item>
- <item quantity="many">Bylo nalezeno %1$d kanálu</item>
- <item quantity="other">Bylo nalezeno %1$d kanálů</item>
- <item quantity="one">Byl nalezen %1$d kanál</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="few">Skvělé! Při vyhledávání byly nalezeny %1$d kanály. Pokud vám to nepřipadá správně, zkuste upravit pozici antény a provést vyhledávání znovu.</item>
- <item quantity="many">Skvělé! Při vyhledávání bylo nalezeno %1$d kanálu. Pokud vám to nepřipadá správně, zkuste upravit pozici antény a provést vyhledávání znovu.</item>
- <item quantity="other">Skvělé! Při vyhledávání bylo nalezeno %1$d kanálů. Pokud vám to nepřipadá správně, zkuste upravit pozici antény a provést vyhledávání znovu.</item>
- <item quantity="one">Skvělé! Při vyhledávání byl nalezen %1$d kanál. Pokud vám to nepřipadá správně, zkuste upravit pozici antény a provést vyhledávání znovu.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Hotovo"</item>
- <item msgid="2480490326672924828">"Znovu vyhledat"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Nebyly nalezeny žádné kanály"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Při vyhledávání nebyly nalezeny žádné kanály. Zkontrolujte, zda je televize připojena ke zdroji televizního signálu.\n\nPokud používáte bezdrátovou anténu, upravte její umístění nebo nasměrování. Nejlepších výsledků dosáhnete, pokud ji umístíte vysoko a blízko okna. Poté spusťte vyhledávání znovu."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Při vyhledávání nebyly nalezeny žádné kanály. Zkontrolujte, zda je tuner USB připojen k zařízení a ke zdroji televizního signálu.\n\nPokud používáte bezdrátovou anténu, upravte její umístění nebo nasměrování. Nejlepších výsledků dosáhnete, pokud ji umístíte vysoko a blízko okna. Poté spusťte vyhledávání znovu."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Při vyhledávání nebyly nalezeny žádné kanály. Zkontrolujte, zda je síťový tuner zapnut a připojen ke zdroji televizního signálu.\n\nPokud používáte bezdrátovou anténu, upravte její umístění nebo nasměrování. Nejlepších výsledků dosáhnete, pokud ji umístíte vysoko a blízko okna. Poté spusťte vyhledávání znovu."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Znovu vyhledat"</item>
- <item msgid="2092797862490235174">"Hotovo"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Vyhledejte televizní kanály"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Nastavení televizního tuneru"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Nastavení televizního tuneru USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Nastavení síťového televizního tuneru"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"Televizní tuner USB byl odpojen."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Síťový tuner byl odpojen."</string>
-</resources>
diff --git a/usbtuner-res/values-da/strings.xml b/usbtuner-res/values-da/strings.xml
deleted file mode 100644
index 96a902b8..00000000
--- a/usbtuner-res/values-da/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"TV Tuner"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB-tuner til fjernsynet"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Netværkstuner til tv (beta)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Vent, mens behandlingen afsluttes"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Tunerens software er blevet opdateret for nylig. Scan efter kanalerne igen."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Aktivér surroundsound i systemets lydindstillinger for at aktivere lyd"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Der kan ikke afspilles lyd. Prøv på et andet fjernsyn"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Konfiguration af kanaltuneren"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Konfiguration med TV Tuner"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Konfiguration af USB-kanaltuneren"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Konfiguration af netværkstuner"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Tjek, at dit fjernsyn er forbundet til en tv-signalkilde.\n\nHvis du bruger en luftantenne, kan det være nødvendigt at justere dens position eller retning for at modtage flest muligt kanaler. Du opnår det bedste resultat ved at placere den højt oppe og i nærheden af et vindue."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Tjek, at USB-tuneren er tilsluttet og forbundet til en tv-signalkilde.\n\nHvis du bruger en luftantenne, kan det være nødvendigt at justere dens position eller retning for at modtage flest muligt kanaler. Du opnår det bedste resultat ved at placere den højt oppe og i nærheden af et vindue."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Tjek, at netværkstuneren er tændt og sluttet til en tv-signalkilde.\n\nHvis du bruger en luftantenne, kan det være nødvendigt at justere dens position eller retning for at modtage flest muligt kanaler. Du opnår det bedste resultat ved at placere den højt oppe og i nærheden af et vindue."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Fortsæt"</item>
- <item msgid="727245208787621142">"Ikke nu"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Vil du gentage kanalkonfigurationen?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Dette fjerner de kanaler, som blev fundet, fra fjernsynets tuner og scanner efter nye kanaler igen.\n\nTjek, at dit fjernsyn er forbundet til en tv-signalkilde.\n\nHvis du bruger en luftantenne, kan det være nødvendigt at justere dens position eller retning for at modtage flest muligt kanaler. Du opnår det bedste resultat ved at placere den højt oppe og i nærheden af et vindue."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Denne handling fjerner de kanaler, der blev fundet af USB-tuneren, og starter en ny scanning efter kanaler.\n\nTjek, at USB-tuneren er tilsluttet og forbundet til en tv-signalkilde.\n\nHvis du bruger en luftantenne, kan det være nødvendigt at justere dens position eller retning for at modtage flest muligt kanaler. Du opnår det bedste resultat ved at placere den højt oppe og i nærheden af et vindue."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Denne handling fjerner de kanaler, der blev fundet af netværkstuneren, og starter en ny kanalsøgning.\n\nTjek, at netværkstuneren er tændt og sluttet til en tv-signalkilde.\n\nHvis du bruger en luftantenne, kan det være nødvendigt at justere dens position eller retning for at modtage flest muligt kanaler. Du opnår det bedste resultat ved at placere den højt oppe og i nærheden af et vindue."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Fortsæt"</item>
- <item msgid="235450158666155406">"Annuller"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Vælg forbindelsestype"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Vælg Antenne, hvis en ekstern antenne er sluttet til tuneren. Vælg Kabel, hvis dine kanaler leveres via kabel af en tjenesteudbyder. Hvis du ikke er sikker, kan du scanne begge typer, men dette kan tage længere tid."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antenne"</item>
- <item msgid="2670079958754180142">"Kabel"</item>
- <item msgid="36774059871728525">"Jeg er ikke sikker"</item>
- <item msgid="6881204453182153978">"Kun til udvikling"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Konfiguration med fjernsynets tuner"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Konfiguration af USB-kanaltuneren"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Konfiguration af netværkstuner til kanalsøgning"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Dette kan tage flere minutter"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Tuneren er midlertidigt utilgængelig eller benyttes allerede til en optagelse."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="one">Der blev fundet %1$d kanal</item>
- <item quantity="other">Der blev fundet %1$d kanaler</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"STOP KANALSCANNINGEN"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="one">Der blev fundet %1$d kanal</item>
- <item quantity="other">Der blev fundet %1$d kanaler</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="one">Sådan! Der blev fundet %1$d kanal ved kanalscanningen. Hvis du mener, det er forkert, kan du prøve at justere antennens position og scanne igen.</item>
- <item quantity="other">Sådan! Der blev fundet %1$d kanaler ved kanalscanningen. Hvis du mener, det er forkert, kan du prøve at justere antennens position og scanne igen.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Udført"</item>
- <item msgid="2480490326672924828">"Scan igen"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Der blev ikke fundet nogen kanaler"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Der blev ikke fundet nogen kanaler under scanningen. Tjek, at dit fjernsyn er forbundet til en tv-signalkilde.\n\nHvis du bruger en luftantenne, skal du justere dens position eller retning. Du opnår det bedste resultat ved at placere den højt oppe og i nærheden af et vindue. Scan igen."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Der blev ikke fundet nogen kanaler under scanningen. Tjek, at USB-tuneren er tilsluttet og forbundet til en tv-signalkilde.\n\nHvis du bruger en luftantenne, skal du justere dens position eller retning. Du opnår det bedste resultat ved at placere den højt oppe og i nærheden af et vindue. Scan igen."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Der blev ikke fundet nogen kanaler under søgningen. Tjek, at netværkstuneren er tændt og sluttet til en tv-signalkilde.\n\nHvis du bruger en luftantenne, skal du justere dens position eller retning. Du opnår det bedste resultat ved at placere den højt oppe og i nærheden af et vindue. Søg igen."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Scan igen"</item>
- <item msgid="2092797862490235174">"Udført"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Søg efter tv-kanaler"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Konfiguration af tuner til fjernsynet"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Konfiguration af USB-tuner til fjernsynet"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Konfiguration af netværkstuner til fjernsynet"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB-tuneren til fjernsynet er ikke tilsluttet."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Netværkstuneren er ikke tilsluttet."</string>
-</resources>
diff --git a/usbtuner-res/values-de/strings.xml b/usbtuner-res/values-de/strings.xml
deleted file mode 100644
index b0f17dfc..00000000
--- a/usbtuner-res/values-de/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"TV-Tuner"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB-TV-Tuner"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Netzwerk-TV-Tuner (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Bitte warten Sie, bis die Verarbeitung abgeschlossen ist"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Die Tunersoftware wurde kürzlich aktualisiert. Bitte führen Sie die Kanalsuche noch einmal durch."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Aktivieren Sie in den Systemeinstellungen Surround-Sound, um Audio einschalten zu können"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Audio kann nicht wiedergegeben werden. Bitte versuch es mit einem anderen Fernseher."</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Kanaleinrichtung über den Tuner"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"TV-Tuner einrichten"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Kanaleinrichtung über den USB-Tuner"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Einrichtung des Netzwerk-Tuners"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Vergewissern Sie sich, dass Ihr Fernseher mit einer TV-Signalquelle verbunden ist.\n\nWenn Sie eine terrestrische Antenne verwenden, ändern Sie die Position oder Ausrichtung, um mehr Kanäle zu finden. Die besten Ergebnisse erhalten Sie, wenn Sie sie an eine erhöhte Position in Fensternähe stellen."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Vergewissern Sie sich, dass der USB-Tuner angeschlossen und mit einer TV-Signalquelle verbunden ist.\n\nWenn Sie eine terrestrische Antenne verwenden, ändern Sie die Position oder Ausrichtung, um mehr Kanäle zu finden. Die besten Ergebnisse erhalten Sie, wenn Sie sie an einer erhöhten Position in Fensternähe stellen."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Vergewissere dich, dass der Netzwerk-Tuner eingeschaltet und mit einer TV-Signalquelle verbunden ist.\n\nWenn du eine terrestrische Antenne verwendest, ändere die Position oder Ausrichtung, um mehr Kanäle zu empfangen. Die besten Ergebnisse erhältst du, wenn du die Antenne an eine erhöhte Position in Fensternähe stellst."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Weiter"</item>
- <item msgid="727245208787621142">"Jetzt nicht"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Kanaleinrichtung erneut durchführen?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Dies entfernt die vom TV-Tuner gefundenen Kanäle und sucht noch einmal nach neuen Kanälen.\n\nVergewissern Sie sich, dass Ihr Fernseher mit einer TV-Signalquelle verbunden ist.\n\nWenn Sie eine terrestrische Antenne verwenden, ändern Sie die Position oder Ausrichtung, um mehr Kanäle zu finden. Die besten Ergebnisse erhalten Sie, wenn Sie sie an eine erhöhte Position in Fensternähe stellen."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Durch diese Aktion werden die gefundenen Kanäle vom USB-Tuner entfernt und die Kanalsuche wird erneut gestartet.\n\nVergewissern Sie sich, dass der USB-Tuner angeschlossen und mit einer TV-Signalquelle verbunden ist.\n\nWenn Sie eine terrestrische Antenne verwenden, ändern Sie die Position oder Ausrichtung, um mehr Kanäle zu finden. Die besten Ergebnisse erhalten Sie, wenn Sie sie an einer erhöhten Position in Fensternähe stellen."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Durch diese Aktion werden die vom Netzwerk-Tuner gefundenen Kanäle entfernt und die Kanalsuche wird neu gestartet.\n\nVergewissere dich, dass der Netzwerk-Tuner eingeschaltet und mit einer TV-Signalquelle verbunden ist.\n\nWenn du eine terrestrische Antenne verwendest, ändere die Position oder Ausrichtung, um mehr Kanäle zu empfangen. Die besten Ergebnisse erhältst du, wenn du die Antenne an eine erhöhte Position in Fensternähe stellst."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Weiter"</item>
- <item msgid="235450158666155406">"Abbrechen"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Verbindungstyp auswählen"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Wählen Sie \"Antenne\" aus, wenn eine externe Antenne mit dem Tuner verbunden ist. Wählen Sie \"Kabel\" aus, wenn Sie Ihre Kanäle bei einem Kabel-TV-Anbieter abrufen. Wenn Sie \"Nicht sicher\" auswählen, wird nach beiden Typen gesucht. Der Vorgang dauert dann unter Umständen länger."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antenne"</item>
- <item msgid="2670079958754180142">"Kabel"</item>
- <item msgid="36774059871728525">"Nicht sicher"</item>
- <item msgid="6881204453182153978">"Nur Entwicklung"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"TV-Tuner einrichten"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Kanaleinrichtung über den USB-Tuner"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Kanaleinrichtung über den Netzwerk-Tuner"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Dies kann einige Minuten dauern"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Der Tuner ist vorübergehend nicht verfügbar oder wird schon für eine Aufnahme verwendet."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d Kanäle gefunden</item>
- <item quantity="one">%1$d Kanal gefunden</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"KANALSUCHE STOPPEN"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d Kanäle gefunden</item>
- <item quantity="one">%1$d Kanal gefunden</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Wunderbar. Bei der Kanalsuche wurden %1$d Kanäle gefunden. Wenn Sie vermuten, dass dies nicht korrekt ist, ändern Sie die Antennenposition und versuchen Sie es noch einmal.</item>
- <item quantity="one">Wunderbar. Bei der Kanalsuche wurde %1$d Kanal gefunden. Wenn Sie vermuten, dass dies nicht korrekt ist, ändern Sie die Antennenposition und versuchen Sie es noch einmal.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Fertig"</item>
- <item msgid="2480490326672924828">"Noch einmal suchen"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Keine Kanäle gefunden"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Bei der Suche wurden keine Kanäle gefunden. Vergewissern Sie sich, dass Ihr Fernseher mit einer TV-Signalquelle verbunden ist.\n\nWenn Sie eine terrestrische Antenne verwenden, ändern Sie die Position oder Ausrichtung. Um die besten Ergebnisse zu erhalten, stellen Sie sie an eine erhöhte Position in Fensternähe und führen Sie die Suche noch einmal durch."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Bei der Suche wurden keine Kanäle gefunden. Vergewissern Sie sich, dass der USB-Tuner angeschlossen und mit einer TV-Signalquelle verbunden ist.\n\nWenn Sie eine terrestrische Antenne verwenden, ändern Sie die Position oder Ausrichtung. Die besten Ergebnisse erhalten Sie, wenn Sie sie an einer erhöhten Position in Fensternähe stellen. Führen Sie dann die Suche erneut durch."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Bei der Suche wurden keine Kanäle gefunden. Vergewissere dich, dass der Netzwerk-Tuner eingeschaltet und mit einer TV-Signalquelle verbunden ist.\n\nWenn du eine terrestrische Antenne verwendest, ändere die Position oder Ausrichtung. Die besten Ergebnisse erhältst du, wenn du sie an eine erhöhte Position in Fensternähe stellst und die Suche noch einmal durchführst."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Noch einmal suchen"</item>
- <item msgid="2092797862490235174">"Fertig"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Nach TV-Kanälen suchen"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Einrichtung des TV-Tuners"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Einrichtung des USB-TV-Tuners"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Einrichtung des Netzwerk-TV-Tuners"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"Verbindung zum USB-TV-Tuner wurde aufgehoben."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Verbindung zum Netzwerk-Tuner wurde aufgehoben."</string>
-</resources>
diff --git a/usbtuner-res/values-el/strings.xml b/usbtuner-res/values-el/strings.xml
deleted file mode 100644
index 1ea637d4..00000000
--- a/usbtuner-res/values-el/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"Δέκτης τηλεόρασης"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"Δέκτης τηλεόρασης USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Δέκτης τηλεόρασης δικτύου (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Περιμένετε να ολοκληρωθεί η επεξεργασία"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Το λογισμικό δέκτη ενημερώθηκε πρόσφατα. Επαναλάβετε τη σάρωση των καναλιών."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Ενεργοποιήστε τον περιφερειακό ήχο στις ρυθμίσεις ήχου συστήματος για να ενεργοποιήσετε τον ήχο"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Δεν είναι δυνατή η αναπαραγωγή ήχου. Δοκιμάστε μια άλλη τηλεόραση."</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Ρύθμιση δέκτη καναλιών"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Ρύθμιση δέκτη τηλεόρασης"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Ρύθμιση δέκτη καναλιών USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Ρύθμιση δέκτη δικτύου"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Βεβαιωθείτε ότι η τηλεόρασή σας είναι συνδεδεμένη σε μια πηγή τηλεοπτικού σήματος.\n\nΕάν χρησιμοποιείτε ασύρματη κεραία, ίσως χρειαστεί να προσαρμόσετε την τοποθέτηση ή την κατεύθυνσή της για να λάβετε περισσότερα κανάλια. Για καλύτερα αποτελέσματα, τοποθετήστε την ψηλά και κοντά σε ένα παράθυρο."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Βεβαιωθείτε ότι ο δέκτης USB είναι συνδεδεμένος στην πρίζα και σε μια πηγή τηλεοπτικού σήματος.\n\nΕάν χρησιμοποιείτε ασύρματη κεραία, ίσως χρειαστεί να προσαρμόσετε την τοποθέτηση ή την κατεύθυνσή της για να λάβετε περισσότερα κανάλια. Για καλύτερα αποτελέσματα, τοποθετήστε την ψηλά και κοντά σε ένα παράθυρο."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Βεβαιωθείτε ότι ο δέκτης του δικτύου είναι ενεργοποιημένος και συνδεδεμένος σε μια πηγή τηλεοπτικού σήματος.\n\nΕάν χρησιμοποιείτε ασύρματη κεραία, ίσως χρειαστεί να προσαρμόσετε την τοποθέτηση ή την κατεύθυνσή της για να λάβετε περισσότερα κανάλια. Για καλύτερα αποτελέσματα, τοποθετήστε την ψηλά και κοντά σε ένα παράθυρο."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Συνέχεια"</item>
- <item msgid="727245208787621142">"Όχι τώρα"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Επανάληψη ρύθμισης καναλιών;"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Με αυτόν τον τρόπο θα καταργηθούν τα κανάλια που βρέθηκαν από τον δέκτη τηλεόρασης και θα γίνει ξανά σάρωση για νέα κανάλια.\n\nΒεβαιωθείτε ότι η τηλεόρασή σας είναι συνδεδεμένη σε μια πηγή τηλεοπτικού σήματος.\n\nΕάν χρησιμοποιείτε ασύρματη κεραία, ίσως χρειαστεί να προσαρμόσετε την τοποθέτηση ή την κατεύθυνσή της για να λάβετε περισσότερα κανάλια. Για καλύτερα αποτελέσματα, τοποθετήστε την ψηλά και κοντά σε ένα παράθυρο."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Με αυτόν τον τρόπο θα καταργηθούν τα κανάλια που βρέθηκαν από τον δέκτη USB και θα γίνει ξανά σάρωση για νέα κανάλια.\n\nΒεβαιωθείτε ότι ο δέκτης USB είναι συνδεδεμένος στην πρίζα και σε μια πηγή τηλεοπτικού σήματος.\n\nΕάν χρησιμοποιείτε ασύρματη κεραία, ίσως χρειαστεί να προσαρμόσετε την τοποθέτηση ή την κατεύθυνσή της για να λάβετε περισσότερα κανάλια. Για καλύτερα αποτελέσματα, τοποθετήστε την ψηλά και κοντά σε ένα παράθυρο."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Με αυτόν τον τρόπο, τα κανάλια που βρέθηκαν από τον δέκτη του δικτύου θα καταργηθούν και θα γίνει ξανά σάρωση για νέα κανάλια.\n\nΒεβαιωθείτε ότι ο δέκτης του δικτύου είναι ενεργοποιημένος και συνδεδεμένος σε μια πηγή τηλεοπτικού σήματος.\n\nΕάν χρησιμοποιείτε ασύρματη κεραία, ίσως χρειαστεί να προσαρμόσετε την τοποθέτηση ή την κατεύθυνσή της για να λάβετε περισσότερα κανάλια. Για καλύτερα αποτελέσματα, τοποθετήστε την ψηλά και κοντά σε ένα παράθυρο."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Συνέχεια"</item>
- <item msgid="235450158666155406">"Ακύρωση"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Επιλέξτε τον τύπο σύνδεσης"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Επιλέξτε \"Κεραία\" εάν έχει συνδεθεί μια εξωτερική κεραία στον δέκτη. Επιλέξτε \"Καλωδιακή\" εάν τα κανάλια σας προέρχονται από έναν παροχέα υπηρεσιών καλωδιακής τηλεόρασης. Εάν δεν είστε σίγουροι, θα γίνει σάρωση και για τους δύο τύπους, αλλά αυτό ενδέχεται να διαρκέσει περισσότερο."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Κεραία"</item>
- <item msgid="2670079958754180142">"Καλωδιακή"</item>
- <item msgid="36774059871728525">"Δεν είμαι σίγουρος/η"</item>
- <item msgid="6881204453182153978">"Μόνο ανάπτυξη"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Ρύθμιση δέκτη τηλεόρασης"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Ρύθμιση δέκτη καναλιών USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Ρύθμιση δέκτη καναλιών δικτύου"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Αυτό μπορεί να διαρκέσει αρκετά λεπτά"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Ο δέκτης δεν είναι διαθέσιμος προσωρινά ή χρησιμοποιείται ήδη από την εγγραφή."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">Βρέθηκαν %1$d κανάλια</item>
- <item quantity="one">Βρέθηκε %1$d κανάλι</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"ΔΙΑΚΟΠΗ ΣΑΡΩΣΗΣ ΚΑΝΑΛΙΩΝ"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">Βρέθηκαν %1$d κανάλια</item>
- <item quantity="one">Βρέθηκε %1$d κανάλι</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Ωραία! Βρέθηκαν %1$d κανάλια κατά τη σάρωση καναλιών. Εάν αυτό δεν φαίνεται σωστό, δοκιμάστε να ρυθμίσετε τη θέση της κεραίας και επαναλάβετε τη σάρωση.</item>
- <item quantity="one">Ωραία! Βρέθηκε %1$d κανάλι κατά τη σάρωση καναλιών. Εάν αυτό δεν φαίνεται σωστό, δοκιμάστε να ρυθμίσετε τη θέση της κεραίας και επαναλάβετε τη σάρωση.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Ολοκληρώθηκε"</item>
- <item msgid="2480490326672924828">"Εκ νέου σάρωση"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Δεν βρέθηκαν κανάλια"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Δεν βρέθηκαν κανάλια κατά τη σάρωση. Βεβαιωθείτε ότι η τηλεόρασή σας είναι συνδεδεμένη σε μια πηγή τηλεοπτικού σήματος.\n\nΕάν χρησιμοποιείτε ασύρματη κεραία, προσαρμόστε την τοποθέτηση ή την κατεύθυνσή της. Για καλύτερα αποτελέσματα, τοποθετήστε την ψηλά και κοντά σε ένα παράθυρο και επαναλάβετε τη σάρωση."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Δεν βρέθηκαν κανάλια κατά τη σάρωση. Βεβαιωθείτε ότι ο δέκτης USB είναι συνδεδεμένος στην πρίζα και σε μια πηγή τηλεοπτικού σήματος.\n\nΕάν χρησιμοποιείτε ασύρματη κεραία, προσαρμόστε την τοποθέτηση ή την κατεύθυνσή της. Για καλύτερα αποτελέσματα, τοποθετήστε την ψηλά και κοντά σε ένα παράθυρο και επαναλάβετε τη σάρωση."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Δεν εντοπίστηκε κανένα κανάλι κατά τη σάρωση. Βεβαιωθείτε ότι ο δέκτης του δικτύου είναι ενεργοποιημένος και συνδεδεμένος σε μια πηγή τηλεοπτικού σήματος.\n\nΕάν χρησιμοποιείτε ασύρματη κεραία, προσαρμόστε την τοποθέτηση ή την κατεύθυνσή της. Για καλύτερα αποτελέσματα, τοποθετήστε την ψηλά και κοντά σε κάποιο παράθυρο και επαναλάβετε τη σάρωση."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Εκ νέου σάρωση"</item>
- <item msgid="2092797862490235174">"Ολοκληρώθηκε"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Σάρωση για τηλεοπτικά κανάλια"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Ρύθμιση δέκτη τηλεόρασης"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Ρύθμιση δέκτη τηλεόρασης USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Ρύθμιση δέκτη τηλεόρασης δικτύου"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"Ο δέκτης τηλεόρασης USB έχει αποσυνδεθεί."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Ο δέκτης δικτύου έχει αποσυνδεθεί."</string>
-</resources>
diff --git a/usbtuner-res/values-en-rAU/strings.xml b/usbtuner-res/values-en-rAU/strings.xml
deleted file mode 100644
index dccba99e..00000000
--- a/usbtuner-res/values-en-rAU/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"TV Tuner"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB TV Tuner"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Network TV Tuner (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Please wait to finish processing"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Tuner software has been recently updated. Please re-scan the channels."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"To enable audio, enable surround sound in system sound settings"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Cannot play audio. Please try another TV"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Channel tuner setup"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"TV Tuner setup"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB channel tuner setup"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Network Tuner Setup"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Check that your TV is connected to a TV signal source.\n\nIf using an over-the-air aerial, you may need to adjust its placement or direction to receive the most channels. For best results, place it high and near a window."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Check that the USB tuner is plugged in and connected to a TV signal source.\n\nIf using an over-the-air aerial, you may need to adjust its placement or direction to receive the most channels. For best results, place it high and near a window."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Verify the network tuner is powered on and connected to a TV signal source.\n\nIf using an over-the-air antenna, you may need to adjust its placement or direction to receive the most channels. For best results, place it high and near a window."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Continue"</item>
- <item msgid="727245208787621142">"Not now"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Re-run channel setup?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"This will remove the channels found from the TV tuner and scan for new channels again.\n\nCheck that your TV is connected to a TV signal source.\n\nIf using an over-the-air aerial, you may need to adjust its placement or direction to receive the most channels. For best results, place it high and near a window."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"This will remove the channels found from the USB tuner and scan for new channels again.\n\nCheck that the USB tuner is plugged in and connected to a TV signal source.\n\nIf using an over-the-air antenna, you may need to adjust its placement or direction to receive the most channels. For best results, place it high and near a window."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"This will remove the channels found from the network tuner and scan for new channels again.\n\nVerify the network tuner is powered on and connected to a TV signal source.\n\nIf using an over-the-air antenna, you may need to adjust its placement or direction to receive the most channels. For best results, place it high and near a window."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Continue"</item>
- <item msgid="235450158666155406">"Cancel"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Select the connection type"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Choose Aerial if there is an external Aerial connected to the tuner. Choose Cable if your channels come from a cable service provider. If you are not sure, both types will be scanned, but this may take longer."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Aerial"</item>
- <item msgid="2670079958754180142">"Cable"</item>
- <item msgid="36774059871728525">"Not sure"</item>
- <item msgid="6881204453182153978">"Development only"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"TV tuner setup"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB channel tuner setup"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Network channel tuner setup"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"This may take several minutes"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Tuner is temporarily unavailable or already used by recording."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d channels found</item>
- <item quantity="one">%1$d channel found</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"STOP CHANNEL SCAN"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d channels found</item>
- <item quantity="one">%1$d channel found</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Nice! %1$d channels were found during the channel scan. If this doesn’t seem right, try adjusting the antenna position and scan again.</item>
- <item quantity="one">Nice! %1$d channel was found during the channel scan. If this doesn’t seem right, try adjusting the antenna position and scan again.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Finished"</item>
- <item msgid="2480490326672924828">"Scan again"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"No channels found"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"The scan did not find any channels. Check that your TV is connected to a TV signal source.\n\nIf using an over-the-air aerial, adjust its placement or direction. For best results, place it high and near a window and scan again."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"The scan did not find any channels. Check that the USB tuner is plugged in and connected to a TV signal source.\n\nIf using an over-the-air aerial, adjust its placement or direction. For best results, place it high and near a window and scan again."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"The scan did not find any channels. Verify the network tuner is powered on and connected to a TV signal source.\n\nIf using an over-the-air antenna, adjust its placement or direction. For best results, place it high and near a window and scan again."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Scan again"</item>
- <item msgid="2092797862490235174">"Finished"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Scan for TV channels"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"TV Tuner setup"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB TV Tuner setup"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Network TV Tuner setup"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB TV tuner disconnected."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Network tuner disconnected."</string>
-</resources>
diff --git a/usbtuner-res/values-en-rGB/strings.xml b/usbtuner-res/values-en-rGB/strings.xml
deleted file mode 100644
index dccba99e..00000000
--- a/usbtuner-res/values-en-rGB/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"TV Tuner"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB TV Tuner"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Network TV Tuner (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Please wait to finish processing"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Tuner software has been recently updated. Please re-scan the channels."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"To enable audio, enable surround sound in system sound settings"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Cannot play audio. Please try another TV"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Channel tuner setup"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"TV Tuner setup"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB channel tuner setup"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Network Tuner Setup"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Check that your TV is connected to a TV signal source.\n\nIf using an over-the-air aerial, you may need to adjust its placement or direction to receive the most channels. For best results, place it high and near a window."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Check that the USB tuner is plugged in and connected to a TV signal source.\n\nIf using an over-the-air aerial, you may need to adjust its placement or direction to receive the most channels. For best results, place it high and near a window."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Verify the network tuner is powered on and connected to a TV signal source.\n\nIf using an over-the-air antenna, you may need to adjust its placement or direction to receive the most channels. For best results, place it high and near a window."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Continue"</item>
- <item msgid="727245208787621142">"Not now"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Re-run channel setup?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"This will remove the channels found from the TV tuner and scan for new channels again.\n\nCheck that your TV is connected to a TV signal source.\n\nIf using an over-the-air aerial, you may need to adjust its placement or direction to receive the most channels. For best results, place it high and near a window."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"This will remove the channels found from the USB tuner and scan for new channels again.\n\nCheck that the USB tuner is plugged in and connected to a TV signal source.\n\nIf using an over-the-air antenna, you may need to adjust its placement or direction to receive the most channels. For best results, place it high and near a window."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"This will remove the channels found from the network tuner and scan for new channels again.\n\nVerify the network tuner is powered on and connected to a TV signal source.\n\nIf using an over-the-air antenna, you may need to adjust its placement or direction to receive the most channels. For best results, place it high and near a window."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Continue"</item>
- <item msgid="235450158666155406">"Cancel"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Select the connection type"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Choose Aerial if there is an external Aerial connected to the tuner. Choose Cable if your channels come from a cable service provider. If you are not sure, both types will be scanned, but this may take longer."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Aerial"</item>
- <item msgid="2670079958754180142">"Cable"</item>
- <item msgid="36774059871728525">"Not sure"</item>
- <item msgid="6881204453182153978">"Development only"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"TV tuner setup"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB channel tuner setup"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Network channel tuner setup"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"This may take several minutes"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Tuner is temporarily unavailable or already used by recording."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d channels found</item>
- <item quantity="one">%1$d channel found</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"STOP CHANNEL SCAN"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d channels found</item>
- <item quantity="one">%1$d channel found</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Nice! %1$d channels were found during the channel scan. If this doesn’t seem right, try adjusting the antenna position and scan again.</item>
- <item quantity="one">Nice! %1$d channel was found during the channel scan. If this doesn’t seem right, try adjusting the antenna position and scan again.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Finished"</item>
- <item msgid="2480490326672924828">"Scan again"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"No channels found"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"The scan did not find any channels. Check that your TV is connected to a TV signal source.\n\nIf using an over-the-air aerial, adjust its placement or direction. For best results, place it high and near a window and scan again."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"The scan did not find any channels. Check that the USB tuner is plugged in and connected to a TV signal source.\n\nIf using an over-the-air aerial, adjust its placement or direction. For best results, place it high and near a window and scan again."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"The scan did not find any channels. Verify the network tuner is powered on and connected to a TV signal source.\n\nIf using an over-the-air antenna, adjust its placement or direction. For best results, place it high and near a window and scan again."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Scan again"</item>
- <item msgid="2092797862490235174">"Finished"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Scan for TV channels"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"TV Tuner setup"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB TV Tuner setup"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Network TV Tuner setup"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB TV tuner disconnected."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Network tuner disconnected."</string>
-</resources>
diff --git a/usbtuner-res/values-en-rIN/strings.xml b/usbtuner-res/values-en-rIN/strings.xml
deleted file mode 100644
index dccba99e..00000000
--- a/usbtuner-res/values-en-rIN/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"TV Tuner"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB TV Tuner"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Network TV Tuner (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Please wait to finish processing"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Tuner software has been recently updated. Please re-scan the channels."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"To enable audio, enable surround sound in system sound settings"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Cannot play audio. Please try another TV"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Channel tuner setup"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"TV Tuner setup"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB channel tuner setup"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Network Tuner Setup"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Check that your TV is connected to a TV signal source.\n\nIf using an over-the-air aerial, you may need to adjust its placement or direction to receive the most channels. For best results, place it high and near a window."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Check that the USB tuner is plugged in and connected to a TV signal source.\n\nIf using an over-the-air aerial, you may need to adjust its placement or direction to receive the most channels. For best results, place it high and near a window."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Verify the network tuner is powered on and connected to a TV signal source.\n\nIf using an over-the-air antenna, you may need to adjust its placement or direction to receive the most channels. For best results, place it high and near a window."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Continue"</item>
- <item msgid="727245208787621142">"Not now"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Re-run channel setup?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"This will remove the channels found from the TV tuner and scan for new channels again.\n\nCheck that your TV is connected to a TV signal source.\n\nIf using an over-the-air aerial, you may need to adjust its placement or direction to receive the most channels. For best results, place it high and near a window."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"This will remove the channels found from the USB tuner and scan for new channels again.\n\nCheck that the USB tuner is plugged in and connected to a TV signal source.\n\nIf using an over-the-air antenna, you may need to adjust its placement or direction to receive the most channels. For best results, place it high and near a window."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"This will remove the channels found from the network tuner and scan for new channels again.\n\nVerify the network tuner is powered on and connected to a TV signal source.\n\nIf using an over-the-air antenna, you may need to adjust its placement or direction to receive the most channels. For best results, place it high and near a window."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Continue"</item>
- <item msgid="235450158666155406">"Cancel"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Select the connection type"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Choose Aerial if there is an external Aerial connected to the tuner. Choose Cable if your channels come from a cable service provider. If you are not sure, both types will be scanned, but this may take longer."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Aerial"</item>
- <item msgid="2670079958754180142">"Cable"</item>
- <item msgid="36774059871728525">"Not sure"</item>
- <item msgid="6881204453182153978">"Development only"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"TV tuner setup"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB channel tuner setup"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Network channel tuner setup"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"This may take several minutes"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Tuner is temporarily unavailable or already used by recording."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d channels found</item>
- <item quantity="one">%1$d channel found</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"STOP CHANNEL SCAN"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d channels found</item>
- <item quantity="one">%1$d channel found</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Nice! %1$d channels were found during the channel scan. If this doesn’t seem right, try adjusting the antenna position and scan again.</item>
- <item quantity="one">Nice! %1$d channel was found during the channel scan. If this doesn’t seem right, try adjusting the antenna position and scan again.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Finished"</item>
- <item msgid="2480490326672924828">"Scan again"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"No channels found"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"The scan did not find any channels. Check that your TV is connected to a TV signal source.\n\nIf using an over-the-air aerial, adjust its placement or direction. For best results, place it high and near a window and scan again."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"The scan did not find any channels. Check that the USB tuner is plugged in and connected to a TV signal source.\n\nIf using an over-the-air aerial, adjust its placement or direction. For best results, place it high and near a window and scan again."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"The scan did not find any channels. Verify the network tuner is powered on and connected to a TV signal source.\n\nIf using an over-the-air antenna, adjust its placement or direction. For best results, place it high and near a window and scan again."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Scan again"</item>
- <item msgid="2092797862490235174">"Finished"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Scan for TV channels"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"TV Tuner setup"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB TV Tuner setup"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Network TV Tuner setup"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB TV tuner disconnected."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Network tuner disconnected."</string>
-</resources>
diff --git a/usbtuner-res/values-es-rUS/strings.xml b/usbtuner-res/values-es-rUS/strings.xml
deleted file mode 100644
index cca69f4e..00000000
--- a/usbtuner-res/values-es-rUS/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"Sintonizador de TV"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"Sintonizador de TV USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Sintonizador de TV de red (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Espera a que finalice el procesamiento"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"El software del sintonizador se actualizó recientemente. Vuelve a buscar los canales."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Para habilitar el audio, deberás activar el sonido envolvente en la configuración del sistema de sonido"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"No se puede reproducir el audio. Intenta usar otra TV."</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Configuración del sintonizador de canales"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Configuración del sintonizador de TV"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Configuración del sintonizador de canales USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Configuración del sintonizador de red"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Verifica que tu TV esté conectada a una fuente de señal de TV.\n\nSi usas una antena inalámbrica, es posible que tengas que ajustar su posición o dirección para recibir la mayor cantidad de canales posible. Para obtener mejores resultados, ubica la antena en un lugar alto y cerca de una ventana."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Verifica que el sintonizador USB esté enchufado y conectado a una fuente de señal de TV.\n\nSi usas una antena inalámbrica, es posible que necesites ajustar su ubicación y dirección para recibir la mayor cantidad de canales. Para obtener mejores resultados, ubica la antena en un lugar alto y cerca de una ventana."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Comprueba que el sintonizador de red esté encendido y conectado a una fuente de señal de TV.\n\nSi estás usando una antena inalámbrica, es posible que debas ajustar su ubicación o dirección para recibir la mayoría de los canales. Si deseas obtener mejores resultados, colócala en un lugar alto y cerca de una ventana."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Continuar"</item>
- <item msgid="727245208787621142">"Ahora no"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"¿Quieres volver a configurar los canales?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Esta acción quitará los canales encontrados desde el sintonizador de TV y se volverán a buscar canales nuevos.\n\nVerifica que tu TV esté conectada a una fuente de señal de TV.\n\nSi usas una antena inalámbrica, es posible que tengas que ajustar su posición o dirección para recibir la mayor cantidad de canales posible. Para obtener mejores resultados, ubica la antena en un lugar alto y cerca de una ventana."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Esta acción quitará los canales encontrados desde el sintonizador de TV y se volverán a buscar canales nuevos.\n\nVerifica que el sintonizador USB esté enchufado y conectado a una fuente de señal de TV.\n\nSi usas una antena inalámbrica, es posible que necesites ajustar su ubicación y dirección para recibir la mayor cantidad de canales. Para obtener mejores resultados, ubica la antena en un lugar alto y cerca de una ventana."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"De esta manera, se quitarán los canales encontrados desde el sintonizador de red y se buscarán canales nuevos.\n\nComprueba que el sintonizador de red esté encendido y conectado a una fuente de señal de TV.\n\nSi estás usando una antena inalámbrica, es posible que debas ajustar su ubicación o dirección para recibir la mayor cantidad de canales. Para obtener mejores resultados, colócala en un lugar alto y cerca de una ventana."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Continuar"</item>
- <item msgid="235450158666155406">"Cancelar"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Selecciona el tipo de conexión"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Elige la opción de antena si hay una antena externa conectada al sintonizador o la opción de cable si recibes los canales de un proveedor de servicios de cable. Si no estás seguro, se buscarán ambos tipos, pero es posible que este proceso demore más tiempo."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antena"</item>
- <item msgid="2670079958754180142">"Cable"</item>
- <item msgid="36774059871728525">"No estoy seguro"</item>
- <item msgid="6881204453182153978">"Solo para desarrollo"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Configuración del sintonizador de TV"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Configuración del sintonizador de canales USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Configuración del sintonizador de canales de red"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Este proceso podría demorar varios minutos"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"El sintonizador no está disponible en este momento o está grabando."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">Se encontraron %1$d canales</item>
- <item quantity="one">Se encontró %1$d canal</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"DETENER LA BÚSQUEDA DE CANALES"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">Se encontraron %1$d canales</item>
- <item quantity="one">Se encontró %1$d canal</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Genial. Se encontraron %1$d canales durante la búsqueda de canales. Si crees que el resultado es incorrecto, ajusta la posición de la antena y vuelve a realizar la búsqueda.</item>
- <item quantity="one">Genial. Se encontró %1$d canal durante la búsqueda de canales. Si crees que el resultado es incorrecto, ajusta la posición de la antena y vuelve a realizar la búsqueda.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Listo"</item>
- <item msgid="2480490326672924828">"Volver a buscar"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"No se encontraron canales"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"No se encontró ningún canal. Verifica que tu TV esté conectada a una fuente de señal de TV.\n\nSi usas una antena inalámbrica, ajusta su posición o dirección. Para obtener mejores resultados, ubícala en un lugar alto y cerca de una ventana. Luego, vuelve a hacer la búsqueda."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"No se encontraron canales en la búsqueda. Verifica que el sintonizador USB esté enchufado y conectado a una fuente de señal de TV.\n\nSi usas una antena inalámbrica, ajusta su ubicación o dirección. Para obtener mejores resultados, ubícala en un lugar alto y cerca de una ventana. Luego, vuelve a hacer la búsqueda."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"No se encontró ningún canal durante la búsqueda. Comprueba que el sintonizador de red esté encendido y conectado a una fuente de señal de TV.\n\nSi estás usando una antena inalámbrica, ajusta su ubicación o dirección. Para obtener mejores resultados, colócala en un lugar alto y cerca de una ventana, y repite la búsqueda."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Volver a buscar"</item>
- <item msgid="2092797862490235174">"Listo"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Busca canales de TV"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Configuración del sintonizador de TV"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Configuración del sintonizador de TV USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Configuración del sintonizador de TV de red"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"Se desconectó el sintonizador de TV USB."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Se desconectó el sintonizador de red."</string>
-</resources>
diff --git a/usbtuner-res/values-es/strings.xml b/usbtuner-res/values-es/strings.xml
deleted file mode 100644
index 26742e65..00000000
--- a/usbtuner-res/values-es/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"Sintonizador de canales"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"Sintonizador de canales USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Sintonizador de TV en red (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Espera hasta que finalice el procesamiento"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"El software del sintonizador se ha actualizado recientemente. Vuelve a buscar los canales."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Habilita el sonido envolvente en los ajustes del sistema de sonido para activar el audio"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"No se puede reproducir el audio. Prueba con otra TV"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Configuración del sintonizador de canales"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Configuración del sintonizador de canales"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Configuración del sintonizador de canales USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Configuración del sintonizador en red"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Comprueba que la TV esté conectada a una fuente de señal de TV.\n\n Si utilizas una antena inalámbrica, puede que tengas que cambiar su posición o dirección para recibir la mayor cantidad posible de canales. Para conseguir los mejores resultados, colócala en alto y cerca de una ventana."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Comprueba que el sintonizador USB esté enchufado y conectado a una fuente de señal de TV.\n\nSi utilizas una antena inalámbrica, puede que tengas que cambiar su emplazamiento o dirección para recibir la mayor cantidad posible de canales. Para conseguir los mejores resultados, colócala en alto y cerca de una ventana."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Comprueba que el sintonizador en red esté encendido y conectado a una fuente de señal de TV.\n\nSi utilizas una antena inalámbrica, es posible que tengas que cambiar su emplazamiento o su orientación para ver la mayoría de los canales. Para obtener los mejores resultados, colócala en un lugar alto cerca de una ventana."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Continuar"</item>
- <item msgid="727245208787621142">"Ahora no"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"¿Quieres volver a ejecutar la configuración de canales?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Se quitarán los canales encontrados del sintonizador de canales y se volverán a buscar nuevos canales.\n\nComprueba que la TV esté conectada a una fuente de señal de TV.\n\nSi utilizas una antena inalámbrica, puede que tengas que cambiar su posición o dirección para recibir la mayor cantidad posible de canales. Para conseguir los mejores resultados, colócala en alto y cerca de una ventana."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Se quitarán los canales encontrados con el sintonizador USB y se volverán a buscar nuevos canales.\n\nComprueba que el sintonizador USB esté enchufado y conectado a una fuente de señal de TV.\n\nSi utilizas una antena inalámbrica, puede que tengas que cambiar su ubicación o dirección para recibir la mayor cantidad posible de canales. Para conseguir los mejores resultados, colócala en alto y cerca de una ventana."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Se quitarán los canales encontrados del sintonizador en red y se repetirá la búsqueda de canales.\n\nComprueba que el sintonizador en red esté encendido y conectado a una fuente de señal de TV.\n\nSi utilizas una antena inalámbrica, es posible que tengas que cambiar su emplazamiento o su orientación para ver la mayoría de los canales. Para obtener los mejores resultados, colócala en un lugar alto cerca de una ventana."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Continuar"</item>
- <item msgid="235450158666155406">"Cancelar"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Selecciona un tipo de conexión"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Selecciona Antena si hay una antena externa conectada al sintonizador. Selecciona Cable si tus canales proceden de un proveedor de servicios de cable. Si no lo sabes con seguridad, se buscarán ambos tipos, pero este proceso puede tardar más tiempo."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antena"</item>
- <item msgid="2670079958754180142">"Cable"</item>
- <item msgid="36774059871728525">"No lo sé con seguridad"</item>
- <item msgid="6881204453182153978">"Solo desarrollo"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Configuración del sintonizador de canales"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Configuración del sintonizador de canales USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Configuración del sintonizador de canales en red"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Este proceso puede tardar varios minutos"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"El sintonizador no está disponible temporalmente o se está utilizando en otra grabación."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d canales encontrados</item>
- <item quantity="one">%1$d canal encontrado</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"DETENER BÚSQUEDA DE CANALES"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d canales encontrados</item>
- <item quantity="one">%1$d canal encontrado</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">¡Genial! Se han encontrado %1$d canales durante la búsqueda de canales. Si no te parece correcto, cambia la posición de la antena y vuelve a realizar la búsqueda.</item>
- <item quantity="one">¡Genial! Se ha encontrado %1$d canal durante la búsqueda de canales. Si no te parece correcto, cambia la posición de la antena y vuelve a realizar la búsqueda.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Listo"</item>
- <item msgid="2480490326672924828">"Volver a buscar"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"No se han encontrado canales"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"No se ha encontrado ningún canal. Comprueba que la TV esté conectada a una fuente de señal de TV.\n\nSi utilizas una antena inalámbrica, cambia su posición o dirección. Para conseguir los mejores resultados, colócala en alto y cerca de una ventana. A continuación, vuelve a realizar la búsqueda."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"No se ha encontrado ningún canal. Comprueba que el sintonizador USB esté enchufado y conectado a una fuente de señal de TV.\n\nSi utilizas una antena inalámbrica, cambia su emplazamiento o dirección. Para conseguir los mejores resultados, colócala en alto y cerca de una ventana. A continuación, vuelve a realizar la búsqueda."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"El análisis no ha encontrado ningún canal. Comprueba que el sintonizador en red esté encendido y conectado a una fuente de señal de TV.\n\nSi utilizas una antena inalámbrica, cambia su emplazamiento u orientación. Para obtener los mejores resultados, colócala en un lugar alto cerca de una ventana y repite el análisis."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Volver a buscar"</item>
- <item msgid="2092797862490235174">"Listo"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Buscar canales de TV"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Configuración del sintonizador de TV"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Configuración de sintonizador de TV USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Configuración del sintonizador de TV en red"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"El sintonizador de canales USB está desconectado."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"El sintonizador en red está desconectado."</string>
-</resources>
diff --git a/usbtuner-res/values-et-rEE/strings.xml b/usbtuner-res/values-et-rEE/strings.xml
deleted file mode 100644
index 3d97a31a..00000000
--- a/usbtuner-res/values-et-rEE/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"Telerituuner"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB-telerituuner"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Teleri võrgutuuner (BEETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Oodake, kuni töötlemine on lõppenud"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Tuuneri tarkvara värskendati hiljuti. Otsige kanaleid uuesti."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Heli lubamiseks lubage ruumiline heli süsteemi heliseadetes"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Heli ei saa esitada. Proovige teist telerit"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Kanalituuneri seadistus"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Telerituuneri seadistus"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB-kanalituuneri seadistus"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Võrgutuuneri seadistus"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Veenduge, et teler oleks ühendatud teleri signaaliallikaga.\n\nKui kasutate õhuantenni, peate võimalikult paljude kanalite nägemiseks võib-olla kohandama selle asendit või suunda. Parimate tulemuste saavutamiseks asetage see kõrgele ja akna lähedale."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Veenduge, et USB-tuuner oleks pistikus ja ühendatud teleri signaaliallikaga.\n\nKui kasutate õhuantenni, peate enamiku kanalite nägemiseks võib-olla kohandama selle asendit või suunda. Parimate tulemuste saavutamiseks asetage see kõrgele ja akna lähedale."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Veenduge, et võrgutuuner oleks sisse lülitatud ja teleri signaaliallikaga ühendatud.\n\nKui kasutate õhuantenni, peate võib-olla muutma selle asendit või suunda, et rohkem kanaleid leida. Parimate tulemuste saavutamiseks asetage see kõrgesse kohta akna lähedale."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Jätka"</item>
- <item msgid="727245208787621142">"Mitte praegu"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Kas käitada kanali seadistust uuesti?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"See eemaldab telerituuneri leitud kanalid ja otsib uuesti uusi kanaleid.\n\nVeenduge, et teler oleks ühendatud teleri signaaliallikaga.\n\nKui kasutate õhuantenni, peate võimalikult paljude kanalite nägemiseks võib-olla kohandama selle asendit või suunda. Parimate tulemuste saavutamiseks asetage see kõrgele ja akna lähedale."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"See eemaldab USB-tuuneri leitud kanalid ja otsib uuesti uusi kanaleid.\n\nVeenduge, et USB-tuuner oleks pistikus ja ühendatud teleri signaaliallikaga.\n\nKui kasutate õhuantenni, peate enamiku kanalite nägemiseks võib-olla kohandama selle asendit või suunda. Parimate tulemuste saavutamiseks asetage see kõrgele ja akna lähedale."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"See eemaldab võrgutuunerist leitud kanalid ja skannib uuesti uusi kanaleid.\n\nVeenduge, et võrgutuuner oleks sisse lülitatud ja teleri signaaliallikaga ühendatud.\n\nKui kasutate õhuantenni, peate võib-olla muutma selle asendit või suunda, et rohkem kanaleid leida. Parimate tulemuste saavutamiseks asetage see kõrgesse kohta akna lähedale."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Jätka"</item>
- <item msgid="235450158666155406">"Tühista"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Valige ühenduse tüüp"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Kui tuuneriga on ühendatud väline antenn, tehke valik Antenn. Kui kanaleid pakub kaabelteenuse pakkuja, tehke valik Kaabel. Kui te pole kindel, otsitakse mõlemat tüüpi, kuid see võib kauem aega võtta."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antenn"</item>
- <item msgid="2670079958754180142">"Kaabel"</item>
- <item msgid="36774059871728525">"Ei ole kindel"</item>
- <item msgid="6881204453182153978">"Ainult arenduseks"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Telerituuneri seadistus"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB-kanalituuneri seadistus"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Võrgutuuneri kanalite seadistus"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"See võib võtta mitu minutit"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Tuuner pole ajutiselt saadaval või seda kasutatakse juba salvestamiseks."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">Leiti %1$d kanalit</item>
- <item quantity="one">Leiti %1$d kanal</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"PEATA KANALITE OTSIMINE"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">Leiti %1$d kanalit</item>
- <item quantity="one">Leiti %1$d kanal</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Hästi! Kanalite otsimisel leiti %1$d kanalit. Kui see ei tundu õige, kohandage antenni asendit ja otsige uuesti.</item>
- <item quantity="one">Hästi! Kanalite otsimisel leiti %1$d kanal. Kui see ei tundu õige, kohandage antenni asendit ja otsige uuesti.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Valmis"</item>
- <item msgid="2480490326672924828">"Otsi uuesti"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Ühtegi kanalit ei leitud"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Otsimisel ei leitud ühtegi kanalit. Veenduge, et teler oleks ühendatud teleri signaaliallikaga.\n\nKui kasutate õhuantenni, kohandage selle asendit või suunda. Parimate tulemuste saavutamiseks asetage see kõrgele ja akna lähedale ning otsige uuesti."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Otsimisel ei leitud ühtegi kanalit. Veenduge, et USB-tuuner oleks pistikus ja ühendatud teleri signaaliallikaga.\n\nKui kasutate õhuantenni, kohandage selle asendit või suunda. Parimate tulemuste saavutamiseks asetage see kõrgele ja akna lähedale ning otsige uuesti."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Skannimisel ei leitud ühtegi kanalit. Veenduge, et võrgutuuner oleks sisse lülitatud ja teleri signaaliallikaga ühendatud.\n\nKui kasutate õhuantenni, muutke selle asendit või suunda. Parimate tulemuste saavutamiseks asetage see kõrgesse kohta akna lähedale ja skannige uuesti."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Otsi uuesti"</item>
- <item msgid="2092797862490235174">"Valmis"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Telekanalite otsimine"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Telerituuneri seadistus"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB-telerituuneri seadistus"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Teleri võrgutuuneri seadistus"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB-telerituuner eemaldati."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Võrgutuuner eemaldati."</string>
-</resources>
diff --git a/usbtuner-res/values-eu-rES/strings.xml b/usbtuner-res/values-eu-rES/strings.xml
deleted file mode 100644
index 0f7ac943..00000000
--- a/usbtuner-res/values-eu-rES/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"Sintonizadorea"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB bidezko sintonizadorea"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Sareko sintonizadorea (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Itxaron prozesatzen amaitu arte"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Sintonizadorearen softwarea berriki eguneratu da. Bilatu kanalak berriro."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Audioa gaitzeko, joan sistemaren soinuaren ezarpenetara eta gaitu soinu inguratzailea"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Ezin da erreproduzitu audioa. Saiatu beste telebista batean."</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Kanal-sintonizadorearen konfigurazioa"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Sintonizadorearen konfigurazioa"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB kanal-sintonizadorearen konfigurazioa"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Sareko sintonizadorearen konfigurazioa"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Egiaztatu telebista-seinalearen iturburu batera konektatuta dagoela telebista.\n\nAntena analogiko bat badarabilzu, agian bere kokapena edo norabidea doitu beharko dituzu kanal gehienak eskuratzeko. Emaitzarik onenak lortzeko, ezarri toki altu batean edo leiho baten ondoan."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Egiaztatu USB sintonizadorea entxufatuta eta telebistako seinale-iturburu batera konektatuta dagoela.\n\nHari gabeko antena badarabilzu, doitu bere kokapena edo norabidea. Emaitzarik onenak lortzeko, ezarri toki altu batean edo leiho baten ondoan eta gauzatu bilaketa berriro."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Egiaztatu sintonizadorea piztuta dagoela eta telebista-seinalea igortzen duen iturburu batera konektatu duzula.\n\nHari-gabeko antena bat erabiltzen ari bazara, doi ezazu haren kokapena edo norabidea ahal bezain beste kanal aurkitzeko. Emaitzarik onenak lortzeko, ezar ezazu ahal bezain altu eta leiho batetik gertu."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Jarraitu"</item>
- <item msgid="727245208787621142">"Orain ez"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Berriro konfiguratu nahi dituzu kanalak?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Sintonizadoreak aurkitutako kanalak kenduko dira eta berriro bilatuko dira kanalak.\n\nEgiaztatu telebista-seinalearen iturburu batera konektatuta dagoela telebista.\n\nAntena analogiko bat badarabilzu, agian bere kokapena edo norabidea doitu beharko dituzu kanal gehienak eskuratzeko. Emaitzarik onenak lortzeko, ezarri toki altu batean edo leiho baten ondoan."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"USB sintonizadoreak aurkitutako kanalak kenduko dira eta berriro bilatuko dira kanalak.\n\nEgiaztatu USB sintonizadorea entxufatuta eta telebistako seinale-iturburu batera konektatuta dagoela.\n\nAntena analogiko bat badarabilzu, agian bere kokapena edo norabidea doitu beharko dituzu kanal gehienak eskuratzeko. Emaitzarik onenak lortzeko, ezarri toki altu batean edo leiho baten ondoan."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Hori eginez gero, kendu egingo dira sareko sintonizadoreak aurkitutako kanalak eta zerotik hasiko da berriro kanalak bilatzen.\n\n Egiaztatu sintonizadorea piztuta dagoela eta telebista-seinalea igortzen duen iturburu batera konektatu duzula.\n\nHari-gabeko antena bat erabiltzen ari bazara, doi ezazu haren kokapena edo norabidea ahal bezain beste kanal aurkitzeko. Emaitzarik onenak lortzeko, ezar ezazu ahal bezain altu eta leiho batetik gertu."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Jarraitu"</item>
- <item msgid="235450158666155406">"Utzi"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Hautatu konexio mota"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Kanpoko antena badago sintonizadorera konektatuta, aukeratu Antena. Kableko zerbitzu-hornitzaile batek eskaintzen badizkizu kanalak, aukeratu Kablea. Ziur ez bazaude, mota bietakoak bilatuko dira, baina zertxobait luzatuko da."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antena"</item>
- <item msgid="2670079958754180142">"Kablea"</item>
- <item msgid="36774059871728525">"Ez dakit ziur"</item>
- <item msgid="6881204453182153978">"Garapenerako soilik"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Sintonizadorearen konfigurazioa"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB kanal-sintonizadorearen konfigurazioa"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Sareko kanalen sintonizadorearen konfigurazioa"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Zenbait minutu beharko dira"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Sintonizadorea ez dago erabilgarri edo beste zerbait ari da grabatzen."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d kanal aurkitu dira</item>
- <item quantity="one">%1$d kanal aurkitu da</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"UTZI KANALAK BILATZEARI"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d kanal aurkitu dira</item>
- <item quantity="one">%1$d kanal aurkitu da</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Primeran! %1$d kanal aurkitu da kanalak bilatzean. Oker gaudela uste baduzu, doitu antenaren posizioa eta saiatu berriro.</item>
- <item quantity="one">Primeran! %1$d kanal aurkitu da kanalak bilatzean. Oker gaudela uste baduzu, doitu antenaren posizioa eta saiatu berriro.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Eginda"</item>
- <item msgid="2480490326672924828">"Bilatu berriro"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Ez da aurkitu kanalik"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Bilaketak ez du aurkitu kanalik. Egiaztatu telebista-seinalearen iturburu batera konektatuta dagoela telebista.\n\nAntena analogiko bat badarabilzu, doitu bere kokapena edo norabidea. Emaitzarik onenak lortzeko, ezarri toki altu batean edo leiho baten ondoan eta gauzatu bilaketa berriro."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Bilaketak ez du aurkitu kanalik. Egiaztatu USB sintonizadorea entxufatuta eta telebistako seinale-iturburu batera konektatuta dagoela.\n\nAntena analogiko bat badarabilzu, doitu bere kokapena edo norabidea. Emaitzarik onenak lortzeko, ezarri toki altu batean edo leiho baten ondoan eta gauzatu bilaketa berriro."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Ez da aurkitu kanalik. Egiaztatu sintonizadorea piztuta dagoela eta telebista-seinalea igortzen duen iturburu batera konektatu duzula.\n\nHari-gabeko antena bat erabiltzen ari bazara, doi ezazu haren kokapena edo norabidea. Emaitzarik onenak lortzeko, ezar ezazu ahal bezain altu eta leiho batetik gertu. Ondoren, bila itzazu kanalak berriro."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Bilatu berriro"</item>
- <item msgid="2092797862490235174">"Eginda"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Bilatu telebista-kateak"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Sintonizadorearen konfigurazioa"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB bidezko sintonizadorearen konfigurazioa"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Sareko sintonizadorearen konfigurazioa"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"Deskonektatu da USB bidezko sintonizadorea."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Deskonektatu da sareko sintonizadorea."</string>
-</resources>
diff --git a/usbtuner-res/values-fa/strings.xml b/usbtuner-res/values-fa/strings.xml
deleted file mode 100644
index 586fc926..00000000
--- a/usbtuner-res/values-fa/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"تنظیم‌کننده تلویزیون"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"‏تنظیم‌کننده تلویزیون USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"تنظیم‌کننده تلویزیون شبکه (بتا)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"لطفاً تا پایان پردازش صبر کنید"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"نرم‌افزار تنظیم‌کننده اخیراً به‌روزرسانی شده است. لطفاً کانال‌ها را دوباره اسکن کنید."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"برای فعال کردن صدا، صدای فراگیر را در تنظیمات صدای سیستم فعال کنید"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"صوت پخش نمی‌شود. لطفاً تلویزیون دیگری را امتحان کنید."</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"راه‌اندازی تنظیم‌کننده کانال"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"راه‌اندازی تنظیم‌کننده تلویزیون"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"‏راه‌اندازی تنظیم‌کننده کانال USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"راه‌اندازی تنظیم‌کننده شبکه‌ای"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"مطمئن شوید تلویزیونتان به منبع سیگنال تلویزیونی متصل است.\n\nدر صورت استفاده از آنتن بی‌سیم شاید لازم باشد برای دریافت کانال‌های بیشتر، موقعیت یا جهت آن را تنظیم کنید. برای بهترین نتایج، آن را در جای بلند و نزدیک پنجره قرار دهید."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"‏مطمئن شوید تنظیم‌کننده USB به منبع نیرو و منبع سیگنال تلویزیونی متصل است.\n\nدر صورت استفاده از آنتن بی‌سیم، موقعیت یا جهت آن را تنظیم کنید. برای بهترین نتایج، آن را در جای بلند و نزدیک پنجره قرار دهید."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"مطمئن شوید تنظیم‌کننده شبکه‌ای به برق و منبع سیگنال تلویزیونی وصل است.\n\nاگر از آنتن هوایی استفاده می‌کنید، ممکن است لازم باشد برای دریافت بیشترین تعداد کانال مکان و موقعیت آن را تنظیم کنید. برای بهترین نتایج، آن را در جای بلند و نزدیک پنجره قرار دهید."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"ادامه"</item>
- <item msgid="727245208787621142">"فعلاً نه"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"تنظیم کانال دوباره اجرا شود؟"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"با این کار کانال‌های پیداشده از تنظیم‌کننده تلویزیون حذف می‌شوند و دوباره برای کانال‌های جدید اسکن می‌کند.\n\nمطمئن شوید تلویزیونتان به منبع سیگنال تلویزیونی متصل است.\n\nدر صورت استفاده از آنتن بی‌سیم، شاید لازم باشد برای دریافت کانال‌های بیشتر، موقعیت یا جهت آن را تنظیم کنید. برای بهترین نتایج، آن را در جای بلند و نزدیک پنجره قرار دهید."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"‏این کار کانال‌های پیداشده از تنظیم‌کننده USB را حذف می‌کند و دوباره کانال‌های جدید را اسکن می‌کند.\n\nمطمئن شوید تنظیم‌کننده USB به منبع نیرو و منبع سیگنال تلویزیونی متصل است.\n\nدر صورت استفاده از آنتن بی‌سیم، برای دریافت کانال‌های بیشتر، موقعیت یا جهت آن را تنظیم کنید. برای بهترین نتایج، آن را در جای بلند و نزدیک پنجره قرار دهید و دوباره اسکن کنید."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"این کار کانال‌های پیداشده را از تنظیم‌کننده شبکه‌ای حذف می‌کند و دوباره کانال‌های جدید را اسکن می‌کند.\n\nمطمئن شوید تنظیم‌کننده شبکه‌ای به برق و منبع سیگنال تلویزیونی وصل است.\n\nاگر از آنتن هوایی استفاده می‌کنید، ممکن است لازم باشد برای دریافت بیشترین تعداد کانال مکان و موقعیت آن را تنظیم کنید. برای بهترین نتایج، آن را در جای بلند و نزدیک پنجره قرار دهید."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"ادامه"</item>
- <item msgid="235450158666155406">"لغو"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"انتخاب نوع اتصال"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"اگر یک آنتن خارجی به تنظیم‌کننده وصل است، «آنتن» را انتخاب کنید. اگر کانال‌های شما از یک ارائه‌دهنده خدمات کابلی ارائه می‌شوند، «کابل» را انتخاب کنید. اگر مطمئن نیستید، هر دو نوع اسکن می‌شوند اما بیشتر طول می‌کشد."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"آنتن"</item>
- <item msgid="2670079958754180142">"کابل"</item>
- <item msgid="36774059871728525">"مطمئن نیستم"</item>
- <item msgid="6881204453182153978">"فقط برای برنامه‌نویسی"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"راه‌اندازی تنظیم‌کننده تلویزیون"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"‏راه‌اندازی تنظیم‌کننده کانال USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"راه‌اندازی تنظیم‌کننده کانال شبکه‌ای"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"ممکن است چند دقیقه طول بکشد"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"تنظیم‌کننده موقتاً دردسترس نیست یا در این لحظه در ضبط شدن استفاده می‌شود."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="one">‏%1$d کانال پیدا شد</item>
- <item quantity="other">‏%1$d کانال پیدا شد</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"توقف اسکن کانال"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="one">‏%1$d کانال پیدا شد</item>
- <item quantity="other">‏%1$d کانال پیدا شد</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="one">‏خوب است! در طول اسکن کانال %1$d کانال پیدا شد. اگر این تعداد درست به نظر نمی‌رسد، موقعیت آنتن را تنظیم کنید و دوباره اسکن کنید.</item>
- <item quantity="other">‏خوب است! در طول اسکن کانال %1$d کانال پیدا شد. اگر این تعداد درست به نظر نمی‌رسد، موقعیت آنتن را تنظیم کنید و دوباره اسکن کنید.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"تمام"</item>
- <item msgid="2480490326672924828">"اسکن دوباره"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"کانالی پیدا نشد"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"اسکن هیچ کانالی پیدا نکرد. مطمئن شوید تلویزیونتان به یک منبع سیگنال تلویزیونی متصل است. \n\nدر صورت استفاده از آنتن بی‌سیم، موقعیت یا جهت آن را تنظیم کنید. برای بهترین نتایج، آن را در جای بلند و نزدیک پنجره قرار دهید و دوباره اسکن کنید."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"‏اسکن هیچ کانالی پیدا نکرد. مطمئن شوید تنظیم‌کننده USB به منبع نیرو و منبع سیگنال تلویزیونی متصل است.\n\nدر صورت استفاده از آنتن بی‌سیم، موقعیت یا جهت آن را تنظیم کنید. برای بهترین نتایج، آن را در جای بلند و نزدیک پنجره قرار دهید و دوباره اسکن کنید."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"اسکن، کانالی پیدا نکرد. مطمئن شوید تنظیم‌کننده شبکه‌ای به برق و منبع سیگنال تلویزیونی وصل است.\n\nاگر از آنتن هوایی استفاده می‌کنید، موقعیت یا جهت آن را تنظیم کنید. برای بهترین نتایج، آن را در جای بلند و نزدیک پنجره قرار دهید و دوباره اسکن کنید."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"اسکن دوباره"</item>
- <item msgid="2092797862490235174">"تمام"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"اسکن کانال‌های تلویزیون"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"راه‌اندازی تنظیم‌کننده تلویزیون"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"‏راه‌اندازی تنظیم‌کننده تلویزیون USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"راه‌اندازی تنظیم‌کننده تلویزیون شبکه‌ای"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"‏ارتباط تیونر USB تلویزیون قطع شد."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"ارتباط تیونر شبکه قطع شد."</string>
-</resources>
diff --git a/usbtuner-res/values-fi/strings.xml b/usbtuner-res/values-fi/strings.xml
deleted file mode 100644
index 4dabc97c..00000000
--- a/usbtuner-res/values-fi/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"TV-viritin"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB-TV-viritin"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Antenni-TV-viritin (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Odota, että käsittely on valmis."</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Viritinohjelmisto on päivitetty äskettäin. Hae kanavat uudelleen."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Ota ääni käyttöön kytkemällä tilaääni päälle järjestelmän ääniasetuksissa."</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Äänen toistaminen ei onnistu. Yritä käyttää toista TV:tä."</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Kanavavirittimen määritys"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"TV-virittimen määritys"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB-kanavavirittimen määritys"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Antennivirittimen määritys"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Tarkista, että TV on liitetty TV-signaalilähteeseen.\n\nJos käytät antennia, useampien kanavien löytäminen saattaa edellyttää sen paikan tai suuntauksen säätämistä. Saat parhaan tuloksen asettamalla antennin korkealle ja lähelle ikkunaa."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Tarkista, että USB-viritin on kytketty oikein ja liitetty TV-signaalilähteeseen.\n\nJos käytät antennia, useampien kanavien löytäminen saattaa edellyttää sen paikan ja suuntauksen säätämistä. Saat parhaan tuloksen asettamalla antennin korkealle ja lähelle ikkunaa."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Tarkista, että viritin on päällä ja liitetty TV-signaalilähteeseen.\n\nJos käytät antennia, säädä sen paikkaa tai suuntausta. Aseta antenni korkealle ja lähelle ikkunaa, jotta saat parhaat tulokset, ja tee uusi haku."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Jatka"</item>
- <item msgid="727245208787621142">"Ei nyt"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Haetaanko kanavia uudelleen?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Tämä poistaa TV-virittimen löytämät kanavat ja tekee kanavahaun uudelleen.\n\nTarkista, että TV on liitetty TV-signaalilähteeseen.\n\nJos käytät antennia, useampien kanavien löytäminen saattaa edellyttää sen paikan tai suuntauksen säätämistä. Saat parhaan tuloksen asettamalla antennin korkealle ja lähelle ikkunaa."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Tämä poistaa USB-virittimen löytämät kanavat ja tekee kanavahaun uudelleen.\n\nTarkista, että USB-viritin on kytketty oikein ja liitetty TV-signaalilähteeseen.\n\nJos käytät antennia, useampien kanavien löytäminen saattaa edellyttää sen paikan tai suuntauksen säätämistä. Saat parhaan tuloksen asettamalla antennin korkealle ja lähelle ikkunaa."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Tämä poistaa virittimen löytämät kanavat ja etsii kanavia uudestaan.\n\nTarkista, että viritin on päällä ja liitetty TV-signaalilähteeseen.\n\nJos käytät antennia, säädä sen paikkaa tai suuntausta. Aseta antenni korkealle ja lähelle ikkunaa, jotta saat parhaat tulokset, ja tee uusi haku."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Jatka"</item>
- <item msgid="235450158666155406">"Peruuta"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Valitse yhteystyyppi"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Valitse Antenni, jos virittimeen on liitetty ulkoinen antenni. Valitse Kaapeli, jos kanavasi tulevat kaapelipalveluntarjoajalta. Jos et ole varma, haku tarkistaa molemmat tyypit, mutta se voi kestää kauemmin."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antenni"</item>
- <item msgid="2670079958754180142">"Kaapeli"</item>
- <item msgid="36774059871728525">"En ole varma"</item>
- <item msgid="6881204453182153978">"Vain kehitystä varten"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"TV-virittimen määritys"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB-kanavavirittimen määritys"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Antennikanavavirittimen määritys"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Tämä voi kestää useita minuutteja."</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Viritin ei ole toistaiseksi käytettävissä tai se on nauhoitteen käytössä."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d kanavaa löytyi.</item>
- <item quantity="one">%1$d kanava löytyi.</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"LOPETA KANAVAHAKU"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d kanavaa löytyi</item>
- <item quantity="one">%1$d kanava löytyi</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Hienoa! Kanavahaussa löytyi %1$d kanavaa. Jos tämä tulos ei vaikuta oikealta, säädä antennin asentoa ja tee haku uudelleen.</item>
- <item quantity="one">Hienoa! Kanavahaussa löytyi %1$d kanava. Jos tämä tulos ei vaikuta oikealta, säädä antennin asentoa ja tee haku uudelleen.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Valmis"</item>
- <item msgid="2480490326672924828">"Hae uudelleen"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Kanavia ei löytynyt"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Haku ei löytänyt yhtään kanavaa. Tarkista, että TV on liitetty TV-signaalilähteeseen.\n\nJos käytät antennia, säädä sen paikkaa tai suuntausta. Saat parhaan tuloksen asettamalla antennin korkealle ja lähelle ikkunaa. Tee haku sitten uudelleen."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Haku ei löytänyt yhtään kanavaa. Tarkista, että USB-viritin on kytketty oikein ja liitetty TV-signaalilähteeseen.\n\nJos käytät antennia, säädä sen paikkaa tai suuntausta. Saat parhaan tuloksen asettamalla antennin korkealle ja lähelle ikkunaa. Tee haku sitten uudelleen."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Haussa ei löytynyt kanavia. Tarkista, että viritin on päällä ja liitetty TV-signaalilähteeseen.\n\nJos käytät antennia, säädä sen paikkaa tai suuntausta. Aseta antenni korkealle ja lähelle ikkunaa, jotta saat parhaat tulokset, ja tee uusi haku."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Hae uudelleen"</item>
- <item msgid="2092797862490235174">"Valmis"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Hae TV-kanavia"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"TV-virittimen määritys"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB-TV-virittimen määritys"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Antenni-TV-virittimen määritys"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB-TV-viritin ei ole kytketty."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Verkkoviritin ei ole kytketty."</string>
-</resources>
diff --git a/usbtuner-res/values-fr-rCA/strings.xml b/usbtuner-res/values-fr-rCA/strings.xml
deleted file mode 100644
index 754649ae..00000000
--- a/usbtuner-res/values-fr-rCA/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"Syntoniseur télé"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"Syntoniseur télé USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Syntoniseur télé réseau (BÊTA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Veuillez patienter jusqu\'à la fin du traitement"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Le logiciel du syntoniseur a été mis à jour récemment. Veuillez rechercher les chaînes à nouveau."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Pour activer l\'audio, vous devez activer le son ambiophonique dans les paramètres sonores du système"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Impossible de lire l\'audio. Veuillez essayer sur un autre téléviseur."</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Configuration du syntoniseur de chaînes"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Configuration du syntoniseur télé"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Configurer les chaînes du syntoniseur USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Configuration du syntoniseur réseau"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Vérifiez que le téléviseur est connecté à la source d\'un signal de télévision.\n\nSi vous utilisez une antenne, vous devrez peut-être ajuster sa position ou son orientation pour recevoir le plus de chaînes possible. Pour obtenir les meilleurs résultats, placez-la dans une position élevée et près d\'une fenêtre."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Vérifiez que le syntoniseur USB est branché et connecté à la source d\'un signal de télévision.\n\nSi vous utilisez une antenne, ajustez sa position ou son orientation. Pour obtenir les meilleurs résultats, placez-la dans une position élevée et près d\'une fenêtre, puis relancez la recherche."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Assurez-vous que le syntoniseur réseau est allumé et connecté à une source de signal télé.\n\nSi vous utilisez une antenne, il se peut que vous deviez ajuster sa position ou sa direction pour capter un maximum de chaînes. Pour obtenir des résultats optimaux, placez-la en hauteur et près d\'une fenêtre."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Continuer"</item>
- <item msgid="727245208787621142">"Pas maintenant"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Relancer la configuration de la chaîne?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Cette opération permet de supprimer les chaînes détectées du syntoniseur télé et d\'en rechercher de nouvelles.\n\nVérifiez que le téléviseur est connecté à la source d\'un signal de télévision.\n\nSi vous utilisez une antenne, vous devrez peut-être ajuster sa position ou son orientation pour recevoir le plus de chaînes possible. Pour obtenir les meilleurs résultats, placez-la dans une position élevée et près d\'une fenêtre."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Cette opération permet de supprimer les chaînes détectées du syntoniseur USB et d\'en rechercher de nouvelles.\n\nVérifiez que le syntoniseur USB est branché et connecté à la source d\'un signal de télévision.\n\nSi vous utilisez une antenne, vous devrez peut-être ajuster sa position ou son orientation pour recevoir le plus de chaînes possible. Pour obtenir les meilleurs résultats, placez-la dans une position élevée et près d\'une fenêtre."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Cela supprimera les chaînes trouvées du syntoniseur réseau et lancera une nouvelle recherche.\n\nAssurez-vous que le syntoniseur réseau est allumé et connecté à une source de signal télé.\n\nSi vous utilisez une antenne, il se peut que vous deviez ajuster sa position ou sa direction pour capter un maximum de chaînes. Pour obtenir des résultats optimaux, placez-la en hauteur et près d\'une fenêtre."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Continuer"</item>
- <item msgid="235450158666155406">"Annuler"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Sélectionnez le type de connexion"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Sélectionnez l\'option « Antenne » si une antenne externe est connectée au syntoniseur, ou « Câble » si les chaînes sont fournies par un service de câble. Si vous n\'êtes pas sûr, vous pouvez effectuer la recherche sur ces deux types de connexion, mais cela risque de prendre plus de temps."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antenne"</item>
- <item msgid="2670079958754180142">"Câble"</item>
- <item msgid="36774059871728525">"Pas certain"</item>
- <item msgid="6881204453182153978">"Developpement seulement"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Configuration du syntoniseur télé"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Configurer les chaînes du syntoniseur USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Configuration du syntoniseur de chaînes réseau"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Cela peut prendre plusieurs minutes"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Le syntoniseur n\'est pas accessible ou bien il est en cours d\'utilisation par l\'enregistreur."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="one">%1$d chaîne détectée</item>
- <item quantity="other">%1$d chaînes détectées</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"ARRÊTER LA RECHERCHE DE CHAÎNES"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="one">%1$d chaîne détectée</item>
- <item quantity="other">%1$d chaînes détectées</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="one">Excellent! %1$d chaînes ont été détectées pendant la recherche de chaînes. Si cela vous semble incorrect, essayez d\'ajuster la position de l\'antenne et d\'effectuer une nouvelle recherche.</item>
- <item quantity="other">Excellent! %1$d chaînes ont été détectées pendant la recherche de chaînes. Si cela vous semble incorrect, essayez d\'ajuster la position de l\'antenne et d\'effectuer une nouvelle recherche.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Terminé"</item>
- <item msgid="2480490326672924828">"Rechercher à nouveau"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Aucune chaîne"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"La recherche n\'a détecté aucune chaîne. Vérifiez que le téléviseur est connecté à la source d\'un signal de télévision.\n\nSi vous utilisez une antenne, ajustez sa position ou son orientation. Pour obtenir les meilleurs résultats, placez-la dans une position élevée et près d\'une fenêtre, puis relancez la recherche."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"La recherche n\'a détecté aucune chaîne. Vérifiez que le syntoniseur USB est branché et connecté à la source d\'un signal de télévision.\n\nSi vous utilisez une antenne, ajustez sa position ou son orientation. Pour obtenir les meilleurs résultats, placez-la dans une position élevée et près d\'une fenêtre, puis relancez la recherche."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"La recherche n\'a détecté aucune chaîne. Assurez-vous que le syntoniseur réseau est allumé et connecté à une source de signal télé.\n\nSi vous utilisez une antenne, ajustez sa position ou sa direction. Pour obtenir des résultats optimaux, placez-la en hauteur et près d\'une fenêtre, puis relancez la recherche."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Rechercher à nouveau"</item>
- <item msgid="2092797862490235174">"Terminé"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Rechercher les chaînes de télévision"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Configuration du syntoniseur télé"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Configurer le syntoniseur télé USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Configuration du syntoniseur télé réseau"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"Le syntoniseur télé USB est déconnecté."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Le syntoniseur réseau est déconnecté."</string>
-</resources>
diff --git a/usbtuner-res/values-fr/strings.xml b/usbtuner-res/values-fr/strings.xml
deleted file mode 100644
index 3b4a6589..00000000
--- a/usbtuner-res/values-fr/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"Tuner TV"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"Tuner TV USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Tuner TV réseau (VERSION BÊTA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Veuillez patienter jusqu\'à la fin du traitement."</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Le logiciel du tuner a été mis à jour récemment. Veuillez lancer une nouvelle recherche des chaînes."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Activer le son surround dans les paramètres sonores du système pour activer l\'audio"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Impossible de lire le fichier audio. Veuillez utiliser un autre téléviseur"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Configuration du tuner de chaînes"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Configuration du tuner TV"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Configuration du tuner de chaînes USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Configuration du tuner réseau"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Vérifiez que le téléviseur est connecté à la source d\'un signal de télévision.\n\nSi vous utilisez une antenne Over The Air, vous devrez peut-être ajuster sa position ou son orientation pour recevoir le plus de chaînes possible. Pour obtenir les meilleurs résultats, placez-la dans une position élevée et près d\'une fenêtre."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Vérifiez que le tuner USB est branché et connecté à la source d\'un signal de télévision.\n\nSi vous utilisez une antenne Over The Air, ajustez sa position ou son orientation. Pour obtenir les meilleurs résultats, placez-la dans une position élevée et près d\'une fenêtre, puis relancez la recherche."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Vérifiez que le tuner réseau est allumé et connecté à la source d\'un signal de télévision.\n\nSi vous utilisez une antenne Over The Air, vous devrez peut-être ajuster sa position ou son orientation pour recevoir le plus de chaînes possible. Pour de meilleurs résultats, placez-la en hauteur et près d\'une fenêtre."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Continuer"</item>
- <item msgid="727245208787621142">"Pas maintenant"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Relancer la configuration de la chaîne ?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Cette opération permet de supprimer les chaînes détectées du tuner TV et d\'en rechercher de nouvelles.\n\nVérifiez que le téléviseur est connecté à la source d\'un signal de télévision.\n\nSi vous utilisez une antenne Over The Air, vous devrez peut-être ajuster sa position ou son orientation pour recevoir le plus de chaînes possible. Pour obtenir les meilleurs résultats, placez-la dans une position élevée et près d\'une fenêtre."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Cette opération permet de supprimer les chaînes détectées du tuner USB et d\'en rechercher de nouvelles.\n\nVérifiez que le tuner USB est branché et connecté à la source d\'un signal de télévision.\n\nSi vous utilisez une antenne Over The Air, vous devrez peut-être ajuster sa position ou son orientation pour recevoir le plus de chaînes possible. Pour obtenir les meilleurs résultats, placez-la dans une position élevée et près d\'une fenêtre."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Cette opération permet de supprimer les chaînes détectées du tuner réseau et d\'en rechercher de nouvelles.\n\nVérifiez que le tuner réseau est allumé et connecté à la source d\'un signal de télévision.\n\nSi vous utilisez une antenne Over The Air, vous devrez peut-être ajuster sa position ou son orientation pour recevoir le plus de chaînes possible. Pour de meilleurs résultats, placez-la en hauteur et près d\'une fenêtre."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Continuer"</item>
- <item msgid="235450158666155406">"Annuler"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Sélectionner le type de connexion"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Sélectionnez l\'option \"Antenne\" si une antenne externe est connectée au tuner, ou \"Câble\" si les chaînes sont fournies via un service de câble. Si vous n\'êtes pas sûr, vous pouvez effectuer la recherche sur ces deux types de connexion, mais cela risque de prendre plus de temps."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antenne"</item>
- <item msgid="2670079958754180142">"Câble"</item>
- <item msgid="36774059871728525">"Je ne sais pas"</item>
- <item msgid="6881204453182153978">"Développement uniquement"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Configuration du tuner TV"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Configuration du tuner de chaînes USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Configuration du tuner de chaînes réseau"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Cette opération peut prendre plusieurs minutes."</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Le tuner est temporairement indisponible ou est déjà utilisé pour l\'enregistrement."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="one">%1$d chaîne a été détectée.</item>
- <item quantity="other">%1$d chaînes ont été détectées.</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"ARRÊTER LA RECHERCHE DE CHAÎNES"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="one">%1$d chaîne a été détectée</item>
- <item quantity="other">%1$d chaînes ont été détectées</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="one">Bravo ! %1$d chaîne a été détectée pendant la recherche de chaînes. Si cela vous semble incorrect, essayez d\'ajuster la position de l\'antenne et d\'effectuer une nouvelle recherche.</item>
- <item quantity="other">Bravo ! %1$d chaînes ont été détectées pendant la recherche de chaînes. Si cela vous semble incorrect, essayez d\'ajuster la position de l\'antenne et d\'effectuer une nouvelle recherche.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"OK"</item>
- <item msgid="2480490326672924828">"Rechercher à nouveau"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Aucune chaîne n\'a été détectée"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"La recherche n\'a détecté aucune chaîne. Vérifiez que le téléviseur est connecté à la source d\'un signal de télévision.\n\nSi vous utilisez une antenne Over The Air, ajustez sa position ou son orientation. Pour obtenir les meilleurs résultats, placez-la dans une position élevée et près d\'une fenêtre, puis relancez la recherche."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"La recherche n\'a détecté aucune chaîne. Vérifiez que le tuner USB est branché et connecté à la source d\'un signal de télévision.\n\nSi vous utilisez une antenne Over The Air, ajustez sa position ou son orientation. Pour obtenir les meilleurs résultats, placez-la dans une position élevée et près d\'une fenêtre, puis relancez la recherche."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"La recherche n\'a détecté aucune chaîne. Vérifiez que le tuner réseau est allumé et connecté à la source d\'un signal de télévision.\n\nSi vous utilisez une antenne Over The Air, ajustez sa position ou son orientation. Pour de meilleurs résultats, placez-la en hauteur et près d\'une fenêtre, puis relancez la recherche."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Rechercher à nouveau"</item>
- <item msgid="2092797862490235174">"OK"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Rechercher les chaînes de télévision"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Configuration du tuner TV"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Configuration du tuner TV USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Configuration du tuner TV réseau"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"Le tuner TV USB est déconnecté."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Tuner réseau déconnecté."</string>
-</resources>
diff --git a/usbtuner-res/values-gl-rES/strings.xml b/usbtuner-res/values-gl-rES/strings.xml
deleted file mode 100644
index 4c75fc65..00000000
--- a/usbtuner-res/values-gl-rES/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"Sintonizador de televisión"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"Sintonizador USB de televisión"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Sintonizador de televisión de rede (beta)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Espera a que finalice o procesamento"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"O software do sintonizador actualizouse recentemente. Volve buscar canles."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Activa o son envolvente na configuración de son do sistema para activar o audio"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Non se pode reproducir o audio. Proba con outra televisión"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Configuración do sintonizador de canles"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Configuración do sintonizador de televisión"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Configuración do sintonizador de canles USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Configuración do sintonizador de rede"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Comproba que a televisión está conectada a unha fonte de sinal de televisión.\n\nSe usas unha antena sen fíos, é posible que teñas que axustar a súa posición ou dirección para recibir a maioría das canles. Para conseguir os mellores resultados, colócaa nun lugar alto e preto dunha ventá."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Comproba que o sintonizador USB está enchufado e conectado a unha fonte de sinal de televisión.\n\nSe usas unha antena sen fíos, é posible que teñas que axustar a súa posición ou dirección para recibir a maioría das canles. Para conseguir os mellores resultados, colócaa nun lugar alto e preto dunha ventá."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Verifica que o sintonizador de rede estea acendido e conectado a unha fonte de sinal de televisión.\n\nSe usas unha antena sen fíos, é posible que teñas que axustar a súa posición ou dirección para recibir a maioría das canles. Para conseguir os mellores resultados, colócaa nun lugar alto e preto dunha ventá."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Continuar"</item>
- <item msgid="727245208787621142">"Agora non"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Queres volver executar a configuración da canle?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Esta acción eliminará as canles que atopaches co sintonizador de televisión e buscará outras novas.\n\nComproba que a televisión está conectada a unha fonte de sinal de televisión.\n\nSe usas unha antena sen fíos, é posible que teñas que axustar a súa posición ou dirección para recibir a maioría das canles. Para conseguir os mellores resultados, colócaa nun lugar alto e preto dunha ventá."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Esta acción eliminará as canles que atopaches co sintonizador USB e buscará outras novas.\n\nComproba que o sintonizador USB está enchufado e conectado a unha fonte de sinal de televisión.\n\nSe usas unha antena sen fíos, é posible que teñas que axustar a súa posición ou dirección para recibir a maioría das canles. Para conseguir os mellores resultados, colócaa nun lugar alto e preto dunha ventá."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Con esta acción quitaranse as canles atopadas do teu sintonizador de rede e buscaranse novas canles outra vez.\n\nVerifica que o sintonizador de rede estea acendido e conectado a unha fonte de sinal de televisión.\n\n}Se usas unha antena sen fíos, é posible que teñas que axustar a súa posición ou dirección para recibir a maioría das canles. Para conseguir os mellores resultados, colócaa nun lugar alto e preto dunha ventá."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Continuar"</item>
- <item msgid="235450158666155406">"Cancelar"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Seleccionar tipo de conexión"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Selecciona Antena se hai unha antena externa conectada ao sintonizador. Selecciona Cable se as túas canles proceden dun fornecedor de servizo de cable. Se non o sabes con seguridade, realizarase a busca usando os dous tipos de conexión, pero este proceso pode tardar máis tempo."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antena"</item>
- <item msgid="2670079958754180142">"Cable"</item>
- <item msgid="36774059871728525">"Non estou seguro"</item>
- <item msgid="6881204453182153978">"Só desenvolvemento"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Configuración do sintonizador de televisión"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Configuración do sintonizador de canles USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Configuración do sintonizador de canles de rede"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Esta acción pode tardar varios minutos"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"O sintonizador non está dispoñible temporalmente ou xa se utiliza para unha gravación."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">Atopáronse %1$d canles</item>
- <item quantity="one">Atopouse %1$d canle</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"DETER BUSCA DE CANLES"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">Atopáronse %1$d canles</item>
- <item quantity="one">Atopouse %1$d canle</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Estupendo! Atopáronse %1$d canles durante a busca de canles. Se non che parece correcto, tenta axustar a posición da antena e realiza a busca de novo.</item>
- <item quantity="one">Estupendo! Atopouse %1$d canle durante a busca de canles. Se non che parece correcto, tenta axustar a posición da antena e realiza a busca de novo.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Feito"</item>
- <item msgid="2480490326672924828">"Buscar de novo"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Non se atopou ningunha canle"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Durante a busca non se atopou ningunha canle. Comproba que a televisión está conectada a unha fonte de sinal de televisión.\n\nSe usas unha antena sen fíos, axusta a súa posición ou dirección. Para conseguir os mellores resultados, colócaa nun lugar alto, preto dunha ventá e realiza a fai a busca de novo."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"A busca non atopou ningunha canle. Comproba que o sintonizador USB está enchufado e conectado a unha fonte de sinal de televisión.\n\nSe usas unha antena sen fíos, axusta a súa posición ou dirección. Para conseguir os mellores resultados, colócaa nun lugar alto, preto dunha ventá e realiza a busca de novo."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"A busca de canles non obtivo resultados. Verifica que o sintonizador de rede estea acendido e conectado a unha fonte de sinal de televisión.\n\nSe usas unha antena sen fíos, axusta a súa posición ou dirección. Para conseguir os mellores resultados, colócaa nun lugar alto, preto dunha ventá, e realiza a busca de novo."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Buscar de novo"</item>
- <item msgid="2092797862490235174">"Feito"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Buscar canles de televisión"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Configuración do sintonizador de televisión"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Configuración do sintonizador USB de televisión"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Configuración do sintonizador de televisión de rede"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"Desconectouse o sintonizador de televisión USB."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Desconectouse o sintonizador de rede."</string>
-</resources>
diff --git a/usbtuner-res/values-hi/strings.xml b/usbtuner-res/values-hi/strings.xml
deleted file mode 100644
index 265670e7..00000000
--- a/usbtuner-res/values-hi/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"टीवी ट्यूनर"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB टीवी ट्यूनर"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"नेटवर्क टीवी ट्यूनर (बीटा)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"कृपया प्रक्रिया पूरी होने का इंतज़ार करें"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"ट्यूनर सॉफ़्टवेयर को हाल ही में अपडेट किया गया है. कृपया चैनलों के लिए दोबारा स्कैन करें."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"ऑडियो सक्षम करने के लिए सिस्टम साउंड सेटिंग में सराउंड साउंड सक्षम करें"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"ऑडियो नहीं चल पा रहा है. कृपया कोई दूसरा टीवी आज़माएं"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"चैनल ट्यूनर सेटअप"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"टीवी ट्यूनर सेटअप"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB चैनल ट्यूनर सेटअप"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"नेटवर्क ट्यूनर सेटअप"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"पुष्टि करें कि आपका टीवी किसी टीवी सिग्नल स्रोत से कनेक्ट किया हुआ है.\n\nयदि ओवर द एयर एंटेना का उपयोग कर रहे हैं, तो अधिकांश चैनल पाने के लिए आपको उसकी स्थिति या दिशा समायोजित करने की आवश्यकता हो सकती है. सबसे अच्छे परिणामों के लिए, उसे ऊंचाई पर और किसी खिड़की के पास लगाएं."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"पुष्टि करें कि USB ट्यूनर प्लग इन है और टीवी सिग्नल स्रोत से कनेक्ट किया हुआ है.\n\nयदि आप ओवर द एयर एंटेना का उपयोग कर रहे हैं, तो अधिकांश चैनल पाने के लिए आपको उसकी स्थिति या दिशा समायोजित करने की आवश्यकता हो सकती है. अच्छे परिणामों के लिए, उसे ऊंचाई पर और किसी खिड़की के पास लगाएं."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"सत्यापित करें कि नेटवर्क ट्यूनर चालू है और किसी टीवी सिग्नल स्रोत से कनेक्ट है.\n\nयदि किसी ओवर-द-एयर एंटेना का उपयोग कर रहे हैं, तो अधिकांश चैनल प्राप्त करने के लिए आपको एंटेना का स्थान या दिशा समायोजित करनी पड़ सकती है. सर्वश्रेष्ठ परिणामों के लिए, उसे ऊंचाई पर और किसी खिड़की के पास रखें."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"जारी रखें"</item>
- <item msgid="727245208787621142">"अभी नहीं"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"चैनल सेटअप फिर से चलाएं?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"इससे टीवी ट्यूनर से मिले चैनल निकल जाएंगे और नए चैनल दोबारा स्कैन किए जाएंगे.\n\nपुष्टि करें कि आपका टीवी किसी टीवी सिग्नल स्रोत से कनेक्ट किया हुआ है.\n\nयदि ओवर द एयर एंटेना का उपयोग कर रहे हैं, तो अधिकांश चैनल पाने के लिए आपको उसकी स्थिति या दिशा समायोजित करने की आवश्यकता हो सकती है. सबसे अच्छे परिणामों के लिए, उसे ऊंचाई पर और किसी खिड़की के पास लगाएं."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"इससे USB ट्यूनर से मिले चैनल निकल जाएंगे और नए चैनलों के लिए फिर से स्कैन किया जाएगा.\n\nपुष्टि करें कि USB ट्यूनर प्लग इन है और टीवी सिग्नल स्रोत से कनेक्ट किया हुआ है.\n\nयदि आप ओवर द एयर एंटेना का उपयोग कर रहे हैं, तो अधिकांश चैनल पाने के लिए आपको उसकी स्थिति या दिशा समायोजित करने की आवश्यकता हो सकती है. अच्छे परिणामों के लिए, उसे ऊंचाई पर और किसी खिड़की के पास लगाएं."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"ऐसा करने से नेटवर्क ट्यूनर से मिले चैनल निकाल दिए जाएंगे और नए चैनल के लिए फिर से स्कैन किया जाएगा.\n\nसत्यापित करें कि नेटवर्क ट्यूनर चालू है और किसी टीवी सिग्नल स्रोत से कनेक्ट है.\n\nयदि किसी ओवर-द-एयर एंटेना का उपयोग कर रहे हैं, तो अधिकांश चैनल प्राप्त करने के लिए आपको एंटेना का स्थान या दिशा समायोजित करनी पड़ सकती है. सर्वश्रेष्ठ परिणामों के लिए, उसे ऊंचाई पर और किसी खिड़की के पास रखें."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"जारी रखें"</item>
- <item msgid="235450158666155406">"रद्द करें"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"कनेक्शन का प्रकार चुनें"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"यदि ट्यूनर से कोई बाहरी एंटेना कनेक्ट है, तो एंटेना चुनें. यदि आपके चैनल किसी केबल सेवा प्रदाता से आते हैं, तो केबल चुनें. यदि आप सुनिश्चित नहीं हैं, तो दोनों प्रकारों को स्कैन किया जाएगा, लेकिन इसमें अधिक समय लग सकता है."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"एंटेना"</item>
- <item msgid="2670079958754180142">"केबल"</item>
- <item msgid="36774059871728525">"सुनिश्चित नहीं हैं"</item>
- <item msgid="6881204453182153978">"केवल डेवलपमेंट"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"टीवी ट्यूनर सेटअप"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB चैनल ट्यूनर सेटअप"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"नेटवर्क चैनल ट्यूनर सेटअप"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"इसमें कुछ मिनट लग सकते हैं"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"ट्यूनर अस्थायी रूप से उपलब्ध नहीं है या रिकॉर्डिंग में उसका उपयोग पहले ही हो रहा है."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="one">%1$d चैनल मिले</item>
- <item quantity="other">%1$d चैनल मिले</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"चैनल स्कैन रोकें"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="one">%1$d चैनल मिले</item>
- <item quantity="other">%1$d चैनल मिले</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="one">बढ़िया! चैनल स्कैन करने के दौरान %1$d चैनल मिले. यदि यह सही नहीं लग रहा है, तो एंटेना की स्थिति समायोजित करके देखें और दोबारा स्कैन करें.</item>
- <item quantity="other">बढ़िया! चैनल स्कैन करने के दौरान %1$d चैनल मिले. यदि यह सही नहीं लग रहा है, तो एंटेना की स्थिति समायोजित करके देखें और दोबारा स्कैन करें.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"हो गया"</item>
- <item msgid="2480490326672924828">"दोबारा स्‍कैन करें"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"कोई चैनल नहीं मिला"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"स्कैन करने से कोई भी चैनल नहीं मिला. पुष्टि करें कि आपका टीवी किसी टीवी सिग्नल स्रोत से कनेक्ट किया हुआ है.\n\nयदि ओवर द एयर एंटेना का उपयोग कर रहे हैं, तो उसकी स्थिति या दिशा समायोजित करें. सबसे अच्छे परिणामों के लिए, उसे ऊंचाई पर और किसी खिड़की के पास लगाएं और दोबारा स्कैन करें."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"स्कैन करने से कोई चैनल नहीं मिला. पुष्टि करें कि USB ट्यूनर प्लग इन है और टीवी सिग्नल स्रोत से कनेक्ट किया हुआ है.\n\nयदि आप ओवर द एयर एंटेना का उपयोग कर रहे हैं, तो उसकी स्थिति या दिशा समायोजित करें. अच्छे परिणामों के लिए, उसे ऊंचाई पर और किसी खिड़की के पास लगाएं और दोबारा स्कैन करें."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"स्कैन में कोई चैनल नहीं मिला. सत्यापित करें कि नेटवर्क ट्यूनर चालू है और किसी टीवी संकेत स्रोत से कनेक्ट है.\n\nयदि किसी ओवर-द-एयर एंटेना का उपयोग कर रहे हैं, तो उसका स्थान या दिशा समायोजित करें. सर्वश्रेष्ठ परिणामों के लिए, उसे ऊंचाई पर और किसी खिड़की के पास रखें और फिर से स्कैन करें."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"दोबारा स्‍कैन करें"</item>
- <item msgid="2092797862490235174">"हो गया"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"टीवी चैनलों के लिए स्‍कैन करें"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"टीवी ट्यूनर सेटअप"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB टीवी ट्यूनर सेटअप"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"नेटवर्क टीवी ट्यूनर सेटअप"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB टीवी ट्यूनर डिसकनेक्ट किया गया."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"नेटवर्क ट्यूनर डिसकनेक्ट किया गया."</string>
-</resources>
diff --git a/usbtuner-res/values-hr/strings.xml b/usbtuner-res/values-hr/strings.xml
deleted file mode 100644
index 42f5c3aa..00000000
--- a/usbtuner-res/values-hr/strings.xml
+++ /dev/null
@@ -1,93 +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="bt_app_name" msgid="5515382901862469770">"TV prijemnik"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB TV prijemnik"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Mrežni TV prijemnik (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Pričekajte da obrada završi"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Softver prijemnika nedavno je ažuriran. Ponovite traženje kanala."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Omogućite okružujući zvuk u postavkama zvuka na razini sustava da biste omogućili audio"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Zvuk se ne može reproducirati. Pokušajte s drugim televizorom"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Postavljanje prijemnika kanala"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Postavljanje TV prijemnika"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Postavljanje USB prijemnika za kanale"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Postavljanje mrežnog prijemnika"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Provjerite je li televizor povezan s izvorom televizijskog signala.\n\nAko upotrebljavate antenu zemaljske televizije, možda ćete joj morati promijeniti položaj ili smjer da biste pronašli najviše kanala. Za najbolje rezultate postavite je visoko i u blizini prozora."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Provjerite je li USB prijemnik priključen i povezan s izvorom TV signala.\n\nAko upotrebljavate antenu zemaljske televizije, možda trebate prilagoditi njezin položaj ili smjer da biste primali najviše kanala. Za najbolje rezultate postavite je visoko i u blizini prozora."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Provjerite je li mrežni prijemnik uključen i povezan s izvorom TV signala.\n\nAko upotrebljavate bežičnu antenu, možda ćete trebati prilagoditi položaj ili smjer da biste pronašli najviše kanala. Za najbolje rezultate postavite je visoko i blizu prozora."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Nastavi"</item>
- <item msgid="727245208787621142">"Ne sada"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Želite li ponovo pokrenuti postavljanje kanala?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Time će se ukloniti kanali pronađeni pomoću TV prijemnika i ponovo pokrenuti pretraživanje kanala.\n\nProvjerite je li televizor povezan s izvorom televizijskog signala.\n\nAko upotrebljavate antenu zemaljske televizije, možda ćete joj morati promijeniti položaj ili smjer da biste pronašli najviše kanala. Za najbolje rezultate postavite je visoko i u blizini prozora."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Time će se ukloniti kanali pronađeni putem USB prijemnika i ponoviti pretraživanje kanala.\n\nProvjerite je li USB prijemnik priključen i povezan s izvorom televizijskog signala.\n\nAko upotrebljavate antenu zemaljske televizije, možda trebate prilagoditi njezin položaj ili smjer da biste primali najviše kanala. Za najbolje rezultate postavite je visoko i u blizini prozora."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Time će se ukloniti kanali pronađeni mrežnim prijemnikom i pokrenuti pretraživanje novih kanala.\n\nProvjerite je li mrežni prijemnik uključen i povezan s izvorom TV signala.\n\nAko upotrebljavate bežičnu antenu, možda ćete trebati prilagoditi položaj ili smjer da biste pronašli najviše kanala. Za najbolje rezultate postavite je visoko i blizu prozora."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Nastavi"</item>
- <item msgid="235450158666155406">"Odustani"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Odaberite vrstu veze"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Ako je s prijemnikom povezana vanjska antena, odaberite opciju Antena. Ako gledate kanale s kabelske televizije, odaberite opciju Kabel. Ako niste sigurni, pretražit će se obje vrste, no to može trajati dulje."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antena"</item>
- <item msgid="2670079958754180142">"Kabel"</item>
- <item msgid="36774059871728525">"Nisam siguran"</item>
- <item msgid="6881204453182153978">"Samo razvoj"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Postavljanje TV prijemnika"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Postavljanje USB prijemnika za kanale"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Postavljanje prijemnika za mrežne kanale"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"To može potrajati nekoliko minuta"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Prijemnik trenutačno nije dostupan ili se već upotrebljava za snimanje."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="one">Pronađen je %1$d kanal</item>
- <item quantity="few">Pronađena su %1$d kanala</item>
- <item quantity="other">Pronađeno je %1$d kanala</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"ZAUSTAVI PRETRAŽIVANJE KANALA"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="one">Pronađen je %1$d kanal</item>
- <item quantity="few">Pronađena su %1$d kanala</item>
- <item quantity="other">Pronađeno je %1$d kanala</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="one">Lijepo! Tijekom pretraživanja kanala pronađen je %1$d kanal. Ako mislite da to nije u redu, promijenite položaj antene i pretražite ponovo.</item>
- <item quantity="few">Lijepo! Tijekom pretraživanja kanala pronađena su %1$d kanala. Ako mislite da to nije u redu, promijenite položaj antene i pretražite ponovo.</item>
- <item quantity="other">Lijepo! Tijekom pretraživanja kanala pronađeno je %1$d kanala. Ako mislite da to nije u redu, promijenite položaj antene i pretražite ponovo.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Gotovo"</item>
- <item msgid="2480490326672924828">"Pretraži ponovo"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Nije pronađen nijedan kanal"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Tijekom pretraživanja nije pronađen nijedan kanal. Provjerite je li televizor povezan s izvorom televizijskog signala.\n\nAko upotrebljavate antenu zemaljske televizije, promijenite joj položaj ili smjer. Za najbolje rezultate postavite je visoko i u blizini prozora, a zatim pretražite ponovo."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Pretraživanjem nije pronađen nijedan kanal. Provjerite je li USB prijemnik priključen i povezan s izvorom televizijskog signala.\n\nAko upotrebljavate antenu zemaljske televizije, prilagodite joj položaj ili smjer. Za najbolje rezultate postavite je visoko i u blizini prozora i pretražite ponovo."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Nije pronađen nijedan kanal. Provjerite je li mrežni prijemnik uključen i povezan s izvorom TV signala.\n\nAko upotrebljavate bežičnu antenu, prilagodite položaj ili smjer. Za najbolje rezultate postavite je visoko i blizu prozora i pretražite ponovo."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Pretraži ponovo"</item>
- <item msgid="2092797862490235174">"Gotovo"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Pretražite TV kanale"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Postavljanje TV prijemnika"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Postavljanje USB TV prijemnika"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Postavljanje mrežnog TV prijemnika"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB TV prijemnik isključen."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Mrežni prijemnik isključen."</string>
-</resources>
diff --git a/usbtuner-res/values-hu/strings.xml b/usbtuner-res/values-hu/strings.xml
deleted file mode 100644
index 0d83fe31..00000000
--- a/usbtuner-res/values-hu/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"Tévétuner"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB-s tévétuner"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Hálózati tévétuner (BÉTA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Kérjük, várja meg a folyamat befejezését"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"A tuner szoftverét nemrég frissítették. Kérjük, ismételje meg a csatornakeresést."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"A hang aktiválásához engedélyezze a térhatású hangot a rendszerszintű hangbeállításokban"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"A hangot nem lehet lejátszani. Kérjük, próbálkozzon másik tévén"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Csatornatuner beállítása"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Tévétuner beállítása"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB-s csatornatuner beállítása"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Hálózati tuner beállítása"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Ellenőrizze, hogy tévéje csatlakoztatva van-e a televíziós jelforráshoz.\n\nAntenna használata esetén szükség lehet az elhelyezés, illetve az irány módosítására a lehető legtöbb csatorna befogásához. A legjobb eredmény érdekében helyezze magasra és ablak közelébe."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Győződjön meg arról, hogy az USB-tuner be van dugva, és csatlakoztatva van a televíziós jelforráshoz.\n\nAntenna használata esetén szükség lehet az elhelyezés, illetve irány módosítására a lehető legtöbb csatorna befogásához. A legjobb eredmény érdekében helyezze magasra és ablak közelébe."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Ellenőrizze, hogy a hálózati tuner be van-e kapcsolva, és csatlakozik-e televíziós jelforráshoz.\n\nHa antennát használ, módosítsa az elhelyezkedését, illetve irányát, hogy minél több csatornát fogjon. A legjobb eredmény érdekében helyezze magasra és ablak közelébe."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Folytatás"</item>
- <item msgid="727245208787621142">"Most nem"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Újra végrehajtja a csatornabeállítást?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Ezzel eltávolítja a tévétuner segítségével megtalált csatornákat, és új csatornakeresést indít.\n\nEllenőrizze, hogy tévéje csatlakoztatva van-e a televíziós jelforráshoz.\n\nAntenna használata esetén szükség lehet az elhelyezés, illetve az irány módosítására a lehető legtöbb csatorna befogásához. A legjobb eredmény érdekében helyezze magasra és ablak közelébe."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Ezzel eltávolítja a már megtalált csatornákat az USB-tunerről, és újból elvégzi a csatornakeresést.\n\nGyőződjön meg arról, hogy az USB-tuner be van dugva, és csatlakoztatva van a televíziós jelforráshoz.\n\nAntenna használata esetén szükség lehet az elhelyezés, illetve irány módosítására a lehető legtöbb csatorna befogásához. A legjobb eredmény érdekében helyezze magasra és ablak közelébe."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Ezzel eltávolítja a hálózati tuner által talált csatornákat, és újabb csatornakeresést indít el.\n\nEllenőrizze, hogy a hálózati tuner be van-e kapcsolva, és csatlakozik-e televíziós jelforráshoz.\n\nHa antennát használ, módosítsa az elhelyezkedését, illetve irányát, hogy minél több csatornát fogjon. A legjobb eredmény érdekében helyezze magasra és ablak közelébe."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Folytatás"</item>
- <item msgid="235450158666155406">"Mégse"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Válassza ki a csatlakozás típusát"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Válassza az „Antenna” lehetőséget, ha a tunerhez külső antenna csatlakozik. Válassza a „Kábel” lehetőséget, ha a csatornákat kábelszolgáltató biztosítja. Ha nem biztos egyikben sem, akkor a rendszer elvégzi mindkét típusú keresést, azonban elképzelhető, hogy ez tovább tart."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antenna"</item>
- <item msgid="2670079958754180142">"Kábel"</item>
- <item msgid="36774059871728525">"Nem tudom"</item>
- <item msgid="6881204453182153978">"Csak fejlesztés"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Tévétuner beállítása"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB-s csatornatuner beállítása"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Hálózati csatornatuner beállítása"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Ez néhány percet is igénybe vehet"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"A tuner átmenetileg nem áll rendelkezésre, vagy már fel lett használva a felvétel során."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d csatorna észlelve</item>
- <item quantity="one">%1$d csatorna észlelve</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"CSATORNAKERESÉS LEÁLLÍTÁSA"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d csatorna észlelve</item>
- <item quantity="one">%1$d csatorna észlelve</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Remek! A rendszer %1$d csatornát talált a keresés során. Ha ez nem tűnik megfelelőnek, állítson az antenna helyzetén, majd végezze el újra a keresést.</item>
- <item quantity="one">Remek! A rendszer %1$d csatornát talált a keresés során. Ha ez nem tűnik megfelelőnek, állítson az antenna helyzetén, majd végezze el újra a keresést.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Kész"</item>
- <item msgid="2480490326672924828">"Keresés újra"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Nem található csatorna"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"A keresés nem talált csatornát. Ellenőrizze, hogy tévéje csatlakoztatva van-e a televíziós jelforráshoz.\n\nAntenna használata esetén módosítsa annak elhelyezését, illetve irányát. A legjobb eredmény érdekében helyezze magasra és ablak közelébe, majd ismételje meg a keresést."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"A keresés nem talált csatornát. Győződjön meg arról, hogy az USB-tuner be van dugva, és csatlakoztatva van a televíziós jelforráshoz.\n\nHa antennát használ, állítson annak helyzetén, illetve irányán. A legjobb eredmény érdekében helyezze magasra és ablak közelébe, majd ismételje meg a keresést."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"A rendszer nem talált egyetlen csatornát sem. Ellenőrizze, hogy a hálózati tuner be van-e kapcsolva, és csatlakozik-e televíziós jelforráshoz.\n\nHa antennát használ, módosítsa az elhelyezkedését, illetve irányát. A legjobb eredmény érdekében helyezze magasra és ablak közelébe, majd ismételje meg a keresést."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Keresés újra"</item>
- <item msgid="2092797862490235174">"Kész"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Tévécsatornák keresése"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Tévétuner beállítása"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB-s tévétuner beállítása"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Hálózati tévétuner beállítása"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"Megszakadt a kapcsolat az USB-s tévétunerrel."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Megszakadt a kapcsolat a hálózati tunerrel."</string>
-</resources>
diff --git a/usbtuner-res/values-hy-rAM/strings.xml b/usbtuner-res/values-hy-rAM/strings.xml
deleted file mode 100644
index 2d3080c1..00000000
--- a/usbtuner-res/values-hy-rAM/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"Հեռուստակարգավորիչ"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB հեռուստակարգավորիչ"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Ցանցային հեռուստաընդունիչ (ԲԵՏԱ)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Սպասեք՝ մինչ որոնումը ավարտվի"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Ընդունիչի ծրագրակազմը վերջերս թարմացվել է: Նորից որոնեք ալիքները:"</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Ձայնը միացնելու համար համակարգի ձայնի կարգավորումներում ակտիվացրեք ծավալային ձայնը"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Հնարավոր չէ վերարտադրել ձայնը: Փորձեք մեկ այլ հեռուստացույց:"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Ալիքների կարգավորիչի տեղադրում"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Հեռուստակարգավորիչի տեղադրում"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Ալիքների USB կարգավորիչի տեղադրում"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Ցանցային ընդունիչի կարգավորում"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Համոզվեք, որ հեռուստացույցը միացված է հեռուստատեսային ազդանշանի աղբյուրին:\n\nԵթերային ալեհավաք օգտագործելիս հնարավոր է՝ անհրաժեշտ լինի կարգավորել դրա դիրքն ու ուղղությունը՝ ավելի շատ ալիքներ գտնելու համար: Լավագույն արդյունքներն ապահովելու համար այն դրեք բարձր տեղում՝ պատուհանի մոտ:"</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Համոզվեք, որ USB կարգավորիչը միացված է ցանցին և հեռուստատեսային ազդանշանի աղբյուրին:\n\nԵթերային ալեհավաք օգտագործելիս հնարավոր է՝ անհրաժեշտ լինի կարգավորել դրա դիրքն ու ուղղությունը՝ ավելի շատ ալիքներ գտնելու համար: Լավագույն արդյունքներն ապահովելու համար այն դրեք բարձր տեղում՝ պատուհանի մոտ:"</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Համոզվեք, որ ցանցային ընդունիրչը միացված է և կապակցված հեռուստատեսային ազդանշանի աղբյուրին:\n\nԵթերային ալեհավաք օգտագործելիս հնարավոր է՝ անհրաժեշտ լինի կարգավորել դրա դիրքն ու ուղղությունը՝ ավելի շատ ալիքներ գտնելու համար: Լավագույն արդյունքներն ապահովելու համար այն դրեք բարձր տեղում՝ պատուհանի մոտ:"</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Շարունակել"</item>
- <item msgid="727245208787621142">"Ոչ հիմա"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Կրկի՞ն կարգավորել ալիքները:"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Այս գործողության արդյունքում կհեռացվեն հեռուստակարգավորիչից ստացված ալիքները և կկատարվի ալիքների նոր որոնում:\n\nՀամոզվեք, որ հեռուստացույցը միացված է հեռուստատեսային ազդանշանի աղբյուրին:\n\nԵթերային ալեհավաք օգտագործելիս հնարավոր է՝ անհրաժեշտ լինի կարգավորել դրա դիրքն ու ուղղությունը՝ ավելի շատ ալիքներ գտնելու համար: Լավագույն արդյունքներն ապահովելու համար այն դրեք բարձր տեղում՝ պատուհանի մոտ:"</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Այս գործողության արդյունքում կհեռացվեն USB կարգավորիչից ստացված ալիքները և կկատարվի ալիքների նոր որոնում:\n\nՀամոզվեք, որ USB կարգավորիչը միացված է ցանցին և հեռուստատեսային ազդանշանի աղբյուրին:\n\nԵթերային ալեհավաք օգտագործելիս հնարավոր է՝ անհրաժեշտ լինի կարգավորել դրա դիրքն ու ուղղությունը՝ ավելի շատ ալիքներ գտնելու համար: Լավագույն արդյունքներն ապահովելու համար այն դրեք բարձր տեղում՝ պատուհանի մոտ:"</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Այս գործողության արդյունքում կհեռացվեն ցանցային ընդունիչից ստացված ալիքները և կկատարվի ալիքների նոր որոնում:\n\nՀամոզվեք, որ ցանցային ընդունիչը միացված է և կապակցված հեռուստատեսային ազդանշանի աղբյուրին:\n\nԵթերային ալեհավաք օգտագործելիս հնարավոր է՝ անհրաժեշտ լինի կարգավորել դրա դիրքն ու ուղղությունը՝ ավելի շատ ալիքներ գտնելու համար: Լավագույն արդյունքներն ապահովելու համար այն դրեք բարձր տեղում՝ պատուհանի մոտ:"</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Շարունակել"</item>
- <item msgid="235450158666155406">"Չեղարկել"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Ընտրեք միացման տեսակը"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Եթե ընդունիչին միացված է արտաքին ալեհավաք, ապա ընտրեք Ալեհավաքը: Եթե ալիքների ազդանշանը ստանում եք մալուխային հեռուստաընկերությունից, ապա ընտրեք Մալուխը: Եթե համոզված չեք, ապա կարող եք որոնել երկու տեսակն էլ, սակայն դա ավելի երկար կտևի:"</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Ալեհավաք"</item>
- <item msgid="2670079958754180142">"Մալուխ"</item>
- <item msgid="36774059871728525">"Չգիտեմ"</item>
- <item msgid="6881204453182153978">"Միայն մշակման"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Հեռուստակարգավորիչի տեղադրում"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Ալիքների USB կարգավորիչի տեղադրում"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Ցանցային ալիքների ընդունիչի տեղադրում"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Դա կարող է տևել մի քանի րոպե"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Ընդունիչը ժամանակավորապես անհասանելի է կամ արդեն օգտագործվում է տեսագրելու համար:"</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="one">%1$d channels found</item>
- <item quantity="other">Գտնվել է %1$d ալիք</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"ԴԱԴԱՐԵՑՆԵԼ ԱԼԻՔՆԵՐԻ ՈՐՈՆՈՒՄԸ"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="one">%1$d channels found</item>
- <item quantity="other">Գտնվել է %1$d ալիք</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="one">Nice! %1$d channels were found during the channel scan. If this doesn’t seem right, try adjusting the antenna position and scan again.</item>
- <item quantity="other">Գերազանց է: Ալիքների որոնման արդյունքում գտնվել է %1$d ալիք: Եթե դա բավարար չէ, փորձեք փոխել ալեհավաքի դիրքը և որոնել նորից:</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Պատրաստ է"</item>
- <item msgid="2480490326672924828">"Կրկին որոնել"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Ալիքներ չեն գտնվել"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Որոնման արդյունքում ալիքներ չեն գտնվել: Համոզվեք, որ հեռուստացույցը միացված է հեռուստատեսային ազդանշանի աղբյուրին:\n\nԵթերային ալեհավաք օգտագործելիս կարգավորեք դրա դիրքն ու ուղղությունը: Լավագույն արդյունքներն ապահովելու համար այն դրեք բարձր տեղում՝ պատուհանի մոտ, ապա որոնեք նորից:"</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Որոնման արդյունքում ալիքներ չեն գտնվել: Համոզվեք, որ USB կարգավորիչը միացված է ցանցին և հեռուստատեսային ազդանշանի աղբյուրին:\n\nԵթերային ալեհավաք օգտագործելիս կարգավորեք դրա դիրքն ու ուղղությունը: Լավագույն արդյունքներն ապահովելու համար այն դրեք բարձր տեղում՝ պատուհանի մոտ, ապա որոնեք նորից:"</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Որոնման արդյունքում ալիքներ չեն գտնվել: Համոզվեք, որ ցանցային ընդունիչը միացված է և կապակցված հեռուստատեսային ազդանշանի աղբյուրին:\n\nԵթերային ալեհավաք օգտագործելիս կարգավորեք դրա դիրքն ու ուղղությունը: Լավագույն արդյունքներն ապահովելու համար այն դրեք բարձր տեղում՝ պատուհանի մոտ, ապա որոնեք նորից:"</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Կրկին որոնել"</item>
- <item msgid="2092797862490235174">"Պատրաստ է"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"TV ալիքների որոնում"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"TV ընդունիչի տեղադրում"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB TV ընդունիչի տեղադրում"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Ցանցային TV ընդունիչի տեղադրում"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB հեռուստաընդունիչն անջատված է:"</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Ցանցային ընդունիչն անջատված է։"</string>
-</resources>
diff --git a/usbtuner-res/values-in/strings.xml b/usbtuner-res/values-in/strings.xml
deleted file mode 100644
index 8f3aa4d1..00000000
--- a/usbtuner-res/values-in/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"Tuner TV"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"Tuner TV USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Tuner TV Jaringan (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Harap tunggu sampai pemrosesan selesai"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Software tuner ini baru saja diperbarui. Pindai ulang saluran Anda."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Aktifkan suara surround di setelan suara sistem untuk mengaktifkan audio"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Tidak dapat memutar audio. Coba TV lainnya"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Penyiapan penyetel saluran"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Penyiapan Tuner TV"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Penyiapan tuner saluran USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Penyiapan tuner jaringan"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Pastikan TV terhubung ke sumber sinyal TV.\n\nJika menggunakan antena udara, Anda mungkin perlu menyesuaikan tempat atau arahnya untuk menerima sebagian besar saluran. Untuk hasil terbaik, tempatkan di lokasi yang tinggi dan dekat dengan jendela."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Pastikan tuner USB dicolokkan dan tersambung ke sumber sinyal TV.\n\nJika menggunakan antena udara, Anda mungkin perlu menyesuaikan tempat atau arahnya untuk menerima jumlah saluran paling banyak. Untuk hasil terbaik, letakkan di tempat yang tinggi dan dekat jendela."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Pastikan tuner jaringan aktif dan terhubung ke sumber sinyal TV.\n\nJika menggunakan antena udara, Anda mungkin perlu menyesuaikan tempat dan arahnya agar dapat menerima sebagian besar saluran. Untuk hasil terbaik, tempatkan antena di lokasi yang tinggi dan dekat jendela."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Lanjutkan"</item>
- <item msgid="727245208787621142">"Jangan sekarang"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Jalankan lagi penyiapan saluran?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Tindakan ini akan menghapus saluran yang ditemukan dari tuner TV dan memindai saluran baru lagi.\n\nPastikan TV terhubung ke sumber sinyal TV.\n\nJika menggunakan antena udara, mungkin Anda perlu menyesuaikan tempat atau arahnya untuk menerima sebagian besar saluran. Untuk hasil terbaik, tempatkan di lokasi yang tinggi dan dekat dengan jendela."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Tindakan ini akan menghapus saluran yang ditemukan dari tuner USB dan memindai saluran baru lagi.\n\nPastikan tuner USB dicolokkan dan tersambung ke sumber sinyal TV.\n\nJika menggunakan antena udara, Anda mungkin perlu menyesuaikan tempat atau arahnya untuk menerima jumlah saluran paling banyak. Untuk hasil terbaik, letakkan di tempat yang tinggi dan dekat jendela."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Hal ini akan menghapus saluran yang ditemukan dari tuner jaringan dan memindai saluran baru lagi.\n\nPastikan tuner jaringan aktif dan terhubung ke sumber sinyal TV.\n\nJika menggunakan antena udara, Anda mungkin perlu menyesuaikan penempatan atau arahnya agar dapat menerima sebagian besar saluran. Untuk hasil terbaik, tempatkan antena di lokasi yang tinggi dan dekat dengan jendela."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Lanjutkan"</item>
- <item msgid="235450158666155406">"Batal"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Pilih jenis sambungan"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Pilih Antena jika ada antena eksternal yang tersambung ke tuner. Pilih Kabel jika saluran Anda berasal dari penyedia layanan TV kabel. Jika tidak yakin, kedua jenis tersebut akan dipindai, namun prosesnya akan lebih lama."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antena"</item>
- <item msgid="2670079958754180142">"Kabel"</item>
- <item msgid="36774059871728525">"Tidak yakin"</item>
- <item msgid="6881204453182153978">"Hanya pengembangan"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Penyiapan tuner TV"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Penyiapan tuner saluran USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Penyiapan tuner saluran jaringan"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Proses ini dapat memakan waktu beberapa menit"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Tuner sementara tidak tersedia atau sudah digunakan oleh rekaman."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d saluran ditemukan</item>
- <item quantity="one">%1$d saluran ditemukan</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"HENTIKAN PEMINDAIAN SALURAN"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d saluran ditemukan</item>
- <item quantity="one">%1$d saluran ditemukan</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Bagus! %1$d saluran ditemukan selama pemindaian saluran. Jika ada yang tidak beres, coba sesuaikan posisi antena dan pindai lagi.</item>
- <item quantity="one">Bagus! %1$d saluran ditemukan selama pemindaian saluran. Jika ada yang tidak beres, coba sesuaikan posisi antena dan pindai lagi.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Selesai"</item>
- <item msgid="2480490326672924828">"Pindai lagi"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Tidak ditemukan Saluran"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Pemindaian tidak menemukan saluran apa pun. Pastikan TV terhubung ke sumber sinyal TV.\n\nJika menggunakan antena udara, sesuaikan tempat atau arahnya. Untuk hasil terbaik, tempatkan di lokasi yang tinggi dan dekat dengan jendela kemudian pindai lagi."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Pemindaian tidak menemukan saluran apa pun. Pastikan tuner USB dicolokkan dan tersambung ke sumber sinyal TV.\n\nJika menggunakan antena udara, sesuaikan tempat atau arahnya. Untuk hasil terbaik, letakkan di tempat yang tinggi dan dekat jendela lalu pindai lagi."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Tidak menemukan saluran apa pun saat memindai. Pastikan tuner jaringan aktif dan terhubung ke sumber sinyal TV.\n\nJika Anda menggunakan antena udara, sesuaikan penempatan atau arahnya. Untuk hasil terbaik, tempatkan di lokasi yang tinggi dan dekat jendela, lalu pindai lagi."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Pindai lagi"</item>
- <item msgid="2092797862490235174">"Selesai"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Pindai saluran TV"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Penyiapan Tuner TV"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Penyiapan Tuner TV USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Penyiapan Tuner TV Jaringan"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"Koneksi tuner TV USB terputus."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Koneksi tuner jaringan terputus."</string>
-</resources>
diff --git a/usbtuner-res/values-is-rIS/strings.xml b/usbtuner-res/values-is-rIS/strings.xml
deleted file mode 100644
index fa1e85c7..00000000
--- a/usbtuner-res/values-is-rIS/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"Sjónvarpsmóttakari"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB-sjónvarpsmóttakari"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Netsjónvarpsmóttakari (TILRAUNAÚTGÁFA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Bíddu þar til vinnslu lýkur"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Hugbúnaður sjónvarpsmóttakarans var uppfærður nýlega. Leitaðu aftur að rásum."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Kveikja á víðómastillingu í hljóðstillingum kerfisins til að kveikja á hljóði"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Ekki er hægt að spila hljóð. Prófaðu annað sjónvarp"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Uppsetning sjónvarpskorts"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Uppsetning sjónvarpsmóttakara"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Uppsetning USB-sjónvarpsrásakorts"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Uppsetning netsjónvarpsmóttakara"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Gakktu úr skugga um að sjónvarpið sé tengt við sjónvarpstengi.\n\nEf þú ert að nota loftnet skaltu færa það á annan stað eða snúa því í aðra átt. Best er að hafa það hátt uppi og nálægt glugga þegar leitað er."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Gakktu úr skugga um að USB-sjónvarpskortið sé í sambandi og tengt við sjónvarpstengi.\n\nEf þú ert að nota loftnet skaltu færa það á annan stað eða snúa því í aðra átt til að ná fleiri rásum. Best er að hafa það hátt uppi og nálægt glugga."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Gakktu úr skugga um að kveikt sé á netsjónvarpsmóttakaranum og að hann sé tengdur við sjónvarpstengi.\n\nEf þú ert að nota loftnet skaltu færa það á annan stað eða snúa því í aðra átt. Best er að hafa það hátt uppi og nálægt glugga þegar leitað er."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Halda áfram"</item>
- <item msgid="727245208787621142">"Ekki núna"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Viltu keyra rásauppsetningu aftur?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Þetta fjarlægir stöðvar sem sjónvarpsmóttakarinn fann og leitar aftur að nýjum stöðvum.\n\nGakktu úr skugga um að sjónvarpið sé tengt við sjónvarpstengi.\n\nEf þú ert að nota loftnet skaltu færa það á annan stað eða snúa því í aðra átt. Best er að hafa það hátt uppi og nálægt glugga þegar leitað er."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Þetta fjarlægir rásir sem skráðar eru á USB-sjónvarpskortinu og leitar að nýjum stöðvum.\n\nGakktu úr skugga um að USB-sjónvarpskortið sé í sambandi og tengt við sjónvarpstengi.\n\nEf þú ert að nota loftnet skaltu færa það á annan stað eða snúa því í aðra átt til að ná fleiri rásum. Best er að hafa það hátt uppi og nálægt glugga."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Þetta fjarlægir rásirnar sem sjónvarpsmóttakarinn þinn fann og leitar aftur að nýjum rásum.\n\nGakktu úr skugga um að kveikt sé á netsjónvarpsmóttakaranum og að hann sé tengdur við sjónvarpstengi.\n\nEf þú ert að nota loftnet skaltu færa það á annan stað eða snúa því í aðra átt. Best er að hafa það hátt uppi og nálægt glugga þegar leitað er."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Halda áfram"</item>
- <item msgid="235450158666155406">"Hætta við"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Veldu tengigerðina"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Veldu „Loftnet“ ef utanáliggjandi loftnet er tengt við sjónvarpskortið. Veldu „Kapall“ ef rásirnar berast þér í gegnum kapal. Ef þú ert ekki viss verður leitað að báðum gerðum og það kann að taka lengri tíma."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Loftnet"</item>
- <item msgid="2670079958754180142">"Kapall"</item>
- <item msgid="36774059871728525">"Ekki viss"</item>
- <item msgid="6881204453182153978">"Aðeins þróunaraðilar"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Uppsetning sjónvarpsmóttakara"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Uppsetning USB-sjónvarpsrásakorts"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Uppsetning netsjónvarpsmóttakara"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Þetta getur tekið nokkrar mínútur"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Móttakari er tímabundið ekki í boði eða er þegar að taka upp."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="one">%1$d rás fannst</item>
- <item quantity="other">%1$d rásir fundust</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"STÖÐVA RÁSALEIT"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="one">%1$d rás fannst</item>
- <item quantity="other">%1$d rásir fundust</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="one">Glæsilegt! %1$d rás fannst við leitina. Ef þetta er ekki eins og það á að vera skaltu prófa að stilla loftnetið og leita aftur.</item>
- <item quantity="other">Glæsilegt! %1$d rásir fundust við leitina. Ef þetta er ekki eins og það á að vera skaltu prófa að stilla loftnetið og leita aftur.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Lokið"</item>
- <item msgid="2480490326672924828">"Leita aftur"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Engar rásir fundust"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Engar rásir fundust. Gakktu úr skugga um að sjónvarpið sé tengt við sjónvarpstengi.\n\nEf þú ert að nota loftnet skaltu færa það á annan stað eða snúa því í aðra átt. Best er að hafa það hátt uppi og nálægt glugga þegar leitað er."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Engar rásir fundust. Gakktu úr skugga um að USB-sjónvarpskortið sé í sambandi og tengt við sjónvarpstengi.\n\nEf þú ert að nota loftnet skaltu færa það á annan stað eða snúa því í aðra átt. Best er að hafa það hátt uppi og nálægt glugga þegar leitað er."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Leitin fann engar stöðvar. Gakktu úr skugga um að kveikt sé á netsjónvarpsmóttakaranum og hann sé tengdur við sjónvarpstengi.\n\nEf þú ert að nota loftnet skaltu færa það á annan stað eða snúa því í aðra átt. Best er að hafa það hátt uppi og nálægt glugga þegar leitað er."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Leita aftur"</item>
- <item msgid="2092797862490235174">"Lokið"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Skanna eftir sjónvarpsstöðvum"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Uppsetning sjónvarpsmóttakara"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Uppsetning USB-sjónvarpsmóttakara"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Uppsetning netsjónvarpsmóttakara"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB-sjónvarpsmóttakari tekinn úr sambandi."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Netmóttakari tekinn úr sambandi."</string>
-</resources>
diff --git a/usbtuner-res/values-it/strings.xml b/usbtuner-res/values-it/strings.xml
deleted file mode 100644
index 3e15fb24..00000000
--- a/usbtuner-res/values-it/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"Sintonizzatore TV"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"Sintonizzatore TV USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Network TV Tuner (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Attendi il completamento dell\'elaborazione"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Il software del sintonizzatore è stato aggiornato di recente. Esegui nuovamente la scansione dei canali."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Per attivare l\'audio, attiva l\'audio surround nelle impostazioni del sistema"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Impossibile riprodurre l\'audio. Prova con un\'altra TV"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Configurazione del sintonizzatore di canali"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Configurazione del sintonizzatore TV"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Configurazione del sintonizzatore di canali USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Configurazione del sintonizzatore di rete"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Verifica che la TV sia connessa a un\'origine del segnale TV.\n\nSe utilizzi un\'antenna OTA, regolane la posizione o la direzione per ricevere la maggior parte dei canali. Per ottenere risultati ottimali, posizionala in alto e vicino a una finestra."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Verifica che il sintonizzatore USB sia collegato e connesso a un\'origine del segnale TV.\n\nSe utilizzi un\'antenna OTA, potresti dover regolare la sua posizione o direzione per ricevere la maggior parte dei canali. Per risultati ottimali, posizionala in alto e vicino a una finestra."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Verifica che il sintonizzatore di rete sia acceso e connesso a un\'origine del segnale TV.\n\nSe utilizzi un\'antenna OTA (over-the-air), potresti doverne regolare la posizione o la direzione per ricevere il maggior numero di canali. Per ottenere risultati ottimali, posizionala in alto, vicino a una finestra."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Continua"</item>
- <item msgid="727245208787621142">"Non ora"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Eseguire nuovamente la configurazione dei canali?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Questa operazione rimuoverà i canali trovati dal sintonizzatore TV ed eseguirà la ricerca di nuovi canali.\n\nVerifica che la TV sia connessa a un\'origine del segnale TV.\n\nSe utilizzi un\'antenna OTA, regolane la posizione o la direzione per ricevere la maggior parte dei canali. Per ottenere risultati ottimali, posizionala in alto e vicino a una finestra."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Questa operazione rimuoverà i canali trovati dal sintonizzatore USB ed eseguirà la ricerca di nuovi canali.\n\nVerifica che il sintonizzatore USB sia collegato e connesso a un\'origine del segnale TV.\n\nSe utilizzi un\'antenna OTA, potresti dover regolare la sua posizione o direzione per ricevere il maggior numero di canali. Per ottenere risultati ottimali, posizionala in alto e vicino a una finestra."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"I canali trovati dal sintonizzatore di rete verranno rimossi e verrà eseguita nuovamente la ricerca di nuovi canali.\n\nVerifica che il sintonizzatore di rete sia acceso e connesso a un\'origine del segnale TV.\n\nSe utilizzi un\'antenna OTA (over-the-air), potresti doverne regolare la posizione o la direzione per ricevere il maggior numero di canali. Per ottenere risultati ottimali, posizionala in alto, vicino a una finestra."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Continua"</item>
- <item msgid="235450158666155406">"Annulla"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Seleziona il tipo di connessione"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Seleziona Antenna se un\'antenna esterna è collegata al sintonizzatore. Seleziona Cavo se i tuoi canali sono forniti da un provider Internet via cavo. Se non sei sicuro, verranno cercati entrambi i tipi di canale ma tale operazione potrebbe richiedere più tempo."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antenna"</item>
- <item msgid="2670079958754180142">"Cavo"</item>
- <item msgid="36774059871728525">"Non sono sicuro"</item>
- <item msgid="6881204453182153978">"Solo per sviluppatori"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Configurazione del sintonizzatore TV"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Configurazione del sintonizzatore di canali USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Configurazione del sintonizzatore di canali della rete"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"L\'operazione potrebbe richiedere alcuni minuti"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Il sintonizzatore è temporaneamente non disponibile o già utilizzato dal registratore."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d canali trovati</item>
- <item quantity="one">%1$d canale trovato</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"INTERROMPI RICERCA CANALI"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d canali trovati</item>
- <item quantity="one">%1$d canale trovato</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Bene! Sono stati trovati %1$d canali durante la ricerca dei canali. Se non è corretto, prova a regolare la posizione dell\'antenna ed esegui nuovamente la ricerca.</item>
- <item quantity="one">Bene! È stato trovato %1$d canale durante la ricerca dei canali. Se non è corretto, prova a regolare la posizione dell\'antenna ed esegui nuovamente la ricerca.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Fine"</item>
- <item msgid="2480490326672924828">"Cerca di nuovo"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Nessun canale trovato"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Nessun canale trovato durante la ricerca. Verifica che la TV sia connessa a un\'origine del segnale TV.\n\nSe utilizzi un\'antenna OTA, regolane la posizione o la direzione. Per ottenere risultati ottimali, posizionala in alto, vicino a una finestra ed esegui nuovamente la ricerca."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Nessun canale trovato durante la ricerca. Verifica che il sintonizzatore USB sia collegato e connesso a un\'origine del segnale TV.\n\nSe utilizzi un\'antenna OTA (over-the-air), regolane la posizione o la direzione. Per ottenere risultati ottimali, posizionala in alto, vicino a una finestra ed esegui nuovamente la ricerca."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Nessun canale trovato durante la ricerca. Verifica che il sintonizzatore di rete sia acceso e connesso a un\'origine del segnale TV.\n\nSe utilizzi un\'antenna OTA (over-the-air), regolane la posizione o la direzione. Per ottenere risultati ottimali, posizionala in alto, vicino a una finestra ed esegui nuovamente la ricerca."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Cerca di nuovo"</item>
- <item msgid="2092797862490235174">"Fine"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Cerca canali TV"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Configurazione del sintonizzatore TV"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Configurazione del sintonizzatore TV USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Configurazione del sintonizzatore TV di rete"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"Sintonizzatore TV USB disconnesso."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Sintonizzatore di rete disconnesso."</string>
-</resources>
diff --git a/usbtuner-res/values-iw/strings.xml b/usbtuner-res/values-iw/strings.xml
deleted file mode 100644
index d30fdac5..00000000
--- a/usbtuner-res/values-iw/strings.xml
+++ /dev/null
@@ -1,96 +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="bt_app_name" msgid="5515382901862469770">"טיונר טלוויזיה"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"‏טיונר ה-USB בטלוויזיה"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"טיונר טלוויזיה לרשת (ביטא)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"המתן לסיום העיבוד"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"תוכנת הטיונר עודכנה לאחרונה. סרוק מחדש את הערוצים."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"הפעל סראונד בהגדרות צלילי מערכת כדי להפעיל אודיו"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"אין אפשרות להשמיע אודיו. נסה ערוץ טלוויזיה אחר."</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"הגדרת טיונר ערוצים"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"הגדרת טיונר טלוויזיה"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"‏הגדרת טיונר ערוצים בחיבור USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"הגדרת מקלט הרשת"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"ודא שהטלוויזיה מחוברת למקור אות טלוויזיה.\n\nאם אתה משתמש באנטנה אלחוטית, ייתכן שיהיה עליך לשנות את המיקום או את הכיוון שלה כדי לקלוט כמה שיותר ערוצים. לקבלת התוצאות הטובות ביותר, הצב אותה במקום גבוה ליד חלון."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"‏ודא שטיונר USB מחובר למקור אות בטלוויזיה. \n\n אם אתה משתמש באנטנה אלחוטית, ייתכן שיהיה עליך לכוון את מיקומה או את כיוונה כדי לקלוט כמה שיותר ערוצים. לתוצאות מיטביות, הצב אותה במקום גבוה ליד חלון וסרוק שוב."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"ודא שמקלט הרשת מופעל ומחובר למקור אות של טלוויזיה.\n\nאם אתה משתמש באנטנה לקליטת שידורים אלחוטיים, ייתכן שיהיה עליך לשנות את המיקום או את הכיוון שלה כדי לקלוט כמה שיותר ערוצים. לקבלת התוצאות הטובות ביותר, הצב אותה במקום גבוה ליד חלון."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"המשך"</item>
- <item msgid="727245208787621142">"לא עכשיו"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"האם להפעיל מחדש את הגדרת הערוצים?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"פעולה זו תסיר את הערוצים שנמצאו בטיונר הטלוויזיה ותסרוק שוב ערוצים חדשים.\n\nודא שהטלוויזיה מחוברת למקור אות טלוויזיה.\n\nאם אתה משתמש באנטנה אלחוטית, ייתכן שיהיה עליך לשנות את המיקום או את הכיוון שלה כדי לקלוט כמה שיותר ערוצים. לקבלת התוצאות הטובות ביותר, הצב אותה במקום גבוה ליד חלון."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"‏פעולה זו תסיר את הערוצים שנמצאו מטיונר ה-USB ותתבצע סריקה נוספת לערוצים חדשים.\n\nודא שטיונר USB מחובר למקור אות בטלוויזיה.\n\nאם אתה משתמש באנטנה אלחוטית, ייתכן שיהיה עליך לשנות את מיקומה או את כיוונה כדי לקלוט כמה שיותר ערוצים. לתוצאות מיטביות, הצב אותה במקום גבוה ליד חלון וסרוק שוב."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"הפעולה תסיר את הערוצים שנמצאו ממקלט הרשת ותתבצע שוב סריקה לאיתור ערוצים חדשים.\n\nודא שמקלט הרשת מופעל ומחובר למקור אות של טלוויזיה.\n\nאם אתה משתמש באנטנה לקליטת שידורים אלחוטיים, ייתכן שיהיה עליך לשנות את המיקום או את הכיוון שלה כדי לקלוט כמה שיותר ערוצים. לקבלת התוצאות הטובות ביותר, הצב אותה במקום גבוה ליד חלון."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"המשך"</item>
- <item msgid="235450158666155406">"ביטול"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"בחירת סוג החיבור"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"בחר \'אנטנה\' אם ישנה אנטנה חיצונית שמחוברת לטיונר. בחר \'כבלים\' אם הערוצים מגיעים מספק שירותי כבלים. אם אינך בטוח, שני הסוגים ייסרקו, אך הפעולה עשויה להימשך זמן רב יותר."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"אנטנה"</item>
- <item msgid="2670079958754180142">"כבלים"</item>
- <item msgid="36774059871728525">"לא בטוח"</item>
- <item msgid="6881204453182153978">"פיתוח בלבד"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"הגדרת טיונר טלוויזיה"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"‏הגדרת טיונר ערוצים בחיבור USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"הגדרת מקלט ערוצים לרשת"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"פעולה זו עשויה להימשך מספר דקות"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"הטיונר אינו זמין באופן זמני או שהוא כבר נמצא בשימוש של הקלטה."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="two">‏נמצאו %1$d ערוצים</item>
- <item quantity="many">‏נמצאו %1$d ערוצים</item>
- <item quantity="other">‏נמצאו %1$d ערוצים</item>
- <item quantity="one">נמצא ערוץ אחד</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"הפסק סריקת ערוצים"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="two">‏נמצאו %1$d ערוצים</item>
- <item quantity="many">‏נמצאו %1$d ערוצים</item>
- <item quantity="other">‏נמצאו %1$d ערוצים</item>
- <item quantity="one">נמצא ערוץ אחד</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="two">‏לא רע! במהלך סריקה הערוצים, נמצאו %1$d ערוצים. אם הקליטה לא תקינה, נסה לכוונן את מיקום האנטנה ובצע סריקה נוספת.</item>
- <item quantity="many">‏לא רע! במהלך סריקה הערוצים, נמצאו %1$d ערוצים. אם הקליטה לא תקינה, נסה לכוונן את מיקום האנטנה ובצע סריקה נוספת.</item>
- <item quantity="other">‏לא רע! במהלך סריקה הערוצים, נמצאו %1$d ערוצים. אם הקליטה לא תקינה, נסה לכוונן את מיקום האנטנה ובצע סריקה נוספת.</item>
- <item quantity="one">לא רע! במהלך סריקת הערוצים נמצא ערוץ אחד. אם הקליטה לא תקינה, נסה לכוונן את מיקום האנטנה ובצע סריקה נוספת.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"סיום"</item>
- <item msgid="2480490326672924828">"סרוק שוב"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"לא נמצאו ערוצים"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"בסריקה לא נמצאו ערוצים. ודא שהטלוויזיה מחוברת למקור אות טלוויזיה.\n\nאם אתה משתמש באנטנה אלחוטית, שנה את המיקום או את הכיוון שלה. לקבלת התוצאות הטובות ביותר, הצב אותה במקום גבוה ליד חלון וסרוק שוב."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"‏לא נמצאו ערוצים בסריקה. ודא שטיונר USB מחובר למקור אות בטלוויזיה. \n\n אם אתה משתמש באנטנה אלחוטית, שנה את מיקומה או את כיוונה. לתוצאות מיטביות, הצב אותה במקום גבוה ליד חלון וסרוק שוב."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"בסריקה לא נמצאו ערוצים. ודא שמקלט הרשת מופעל ומחובר למקור אות של טלוויזיה.\n\nאם אתה משתמש באנטנה לקליטת שידורים אלחוטיים, שנה את המיקום או את הכיוון שלה. לקבלת התוצאות הטובות ביותר, הצב אותה במקום גבוה ליד חלון וסרוק שוב."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"סרוק שוב"</item>
- <item msgid="2092797862490235174">"סיום"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"סריקה לאיתור ערוצי טלוויזיה"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"הגדרה של טיונר טלוויזיה"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"‏הגדרה של טיונר USB בטלוויזיה"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"הגדרה של טיונר טלוויזיה לרשת"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"‏טיונר ה-USB שבטלוויזיה מנותק."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"טיונר הרשת מנותק."</string>
-</resources>
diff --git a/usbtuner-res/values-ja/strings.xml b/usbtuner-res/values-ja/strings.xml
deleted file mode 100644
index 53223b41..00000000
--- a/usbtuner-res/values-ja/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"テレビ チューナー"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB テレビ チューナー"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"ネットワーク テレビ チューナー(ベータ版)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"処理が完了するまでこのままお待ちください"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"最近チューナー ソフトウェアが更新されています。チャンネルをもう一度スキャンしてください。"</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"音声を有効にするには、システムのサウンド設定でサラウンド サウンドをオンにしてください"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"音声を再生できません。別のテレビをお試しください"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"チャンネル チューナーのセットアップ"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"テレビ チューナーのセットアップ"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB チャンネル チューナーのセットアップ"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"ネットワーク チューナーのセットアップ"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"テレビがテレビの信号源に接続されていることを確認してください。\n\n無線アンテナを使用している場合は、チャンネルの多くを受信するためにアンテナの場所や方向の調節が必要になる場合があります。最良の結果を得るには、アンテナを窓際の高い位置に設置します。"</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"USB チューナーが電源に接続され、テレビの信号源に接続されていることを確認してください。\n\n無線アンテナを使用している場合は、チャンネルの多くを受信するためにアンテナの場所や方向の調節が必要になる場合があります。最良の結果を得るには、アンテナを窓際の高い位置に設置します。"</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"ネットワーク チューナーの電源がオンになっていて、テレビの信号源に接続されていることを確認してください。\n\n無線アンテナを使用している場合は、チャンネルの多くを受信するためにアンテナの場所や方向の調節が必要になる場合があります。最良の結果を得るには、アンテナを窓際の高い位置に設置します。"</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"次へ"</item>
- <item msgid="727245208787621142">"後で"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"もう一度チャンネルを設定しますか?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"テレビ チューナーで見つかったチャンネルを削除して新しいチャンネルをもう一度スキャンします。\n\nテレビがテレビの信号源に接続されていることを確認してください。\n\n無線アンテナを使用している場合は、チャンネルの多くを受信するためにアンテナの場所や方向の調節が必要になる場合があります。最良の結果を得るには、アンテナを窓際の高い位置に設置します。"</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"USB チューナーで見つかったチャンネルを削除して新しいチャンネルをもう一度スキャンします。\n\nUSB チューナーが電源に接続され、テレビの信号源に接続されていることを確認してください。\n\n無線アンテナを使用している場合は、チャンネルの多くを受信するためにアンテナの場所や方向の調節が必要になる場合があります。最良の結果を得るには、アンテナを窓際の高い位置に設置します。"</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"ネットワーク チューナーで見つかったチャンネルを削除して新しいチャンネルをもう一度スキャンします。\n\nネットワーク チューナーの電源がオンになっていて、テレビの信号源に接続されていることを確認してください。\n\n無線アンテナを使用している場合は、チャンネルの多くを受信するためにアンテナの場所や方向の調節が必要になる場合があります。最良の結果を得るには、アンテナを窓際の高い位置に設置します。"</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"次へ"</item>
- <item msgid="235450158666155406">"キャンセル"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"接続タイプの選択"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"チューナーに外部アンテナが接続されている場合は、[アンテナ] を選択してください。ケーブル サービス プロバイダのチャンネルの場合は、[ケーブル] を選択してください。不明な場合は両方のタイプがスキャンされますが、処理に時間がかかることがあります。"</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"アンテナ"</item>
- <item msgid="2670079958754180142">"ケーブル"</item>
- <item msgid="36774059871728525">"不明"</item>
- <item msgid="6881204453182153978">"開発のみ"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"テレビ チューナーのセットアップ"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB チャンネル チューナーのセットアップ"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"ネットワーク チャンネル チューナーのセットアップ"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"この処理には数分かかることがあります"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"チューナーを一時的に使用できないか、すでに録画に使用しています。"</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d 件のチャンネルが見つかりました</item>
- <item quantity="one">%1$d 件のチャンネルが見つかりました</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"チャンネルのスキャンを停止"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d 件のチャンネルが見つかりました</item>
- <item quantity="one">%1$d 件のチャンネルが見つかりました</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">チャンネルのスキャン中に %1$d 件のチャンネルが見つかりました。この件数が正しくないと思われる場合は、アンテナの位置を調整してもう一度スキャンしてください。</item>
- <item quantity="one">チャンネルのスキャン中に %1$d 件のチャンネルが見つかりました。この件数が正しくないと思われる場合は、アンテナの位置を調整してもう一度スキャンしてください。</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"完了"</item>
- <item msgid="2480490326672924828">"再スキャン"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"チャンネルが見つかりませんでした"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"スキャンの結果、チャンネルは見つかりませんでした。テレビがテレビの信号源に接続されていることを確認してください。\n\n無線アンテナを使用している場合は、アンテナの場所や方向を調節してください。最良の結果を得るには、アンテナを窓際の高い位置に設置してから、もう一度スキャンします。"</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"スキャンの結果、チャンネルは見つかりませんでした。USB チューナーが電源に接続され、テレビの信号源に接続されていることを確認してください。\n\n無線アンテナを使用している場合は、アンテナの場所や方向を調節してください。最良の結果を得るには、アンテナを窓際の高い位置に設置してから、もう一度スキャンします。"</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"スキャンの結果、チャンネルは見つかりませんでした。ネットワーク チューナーの電源がオンになっていて、テレビの信号源に接続されていることを確認してください。\n\n無線アンテナを使用している場合は、アンテナの場所や方向を調節してください。最良の結果を得るには、アンテナを窓際の高い位置に設置してから、もう一度スキャンします。"</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"再スキャン"</item>
- <item msgid="2092797862490235174">"完了"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"テレビのチャンネルをスキャン"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"テレビ チューナーのセットアップ"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB テレビ チューナーのセットアップ"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"ネットワーク テレビ チューナーのセットアップ"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB テレビ チューナーの接続が解除されています。"</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"ネットワーク チューナーの接続が解除されています。"</string>
-</resources>
diff --git a/usbtuner-res/values-ka-rGE/strings.xml b/usbtuner-res/values-ka-rGE/strings.xml
deleted file mode 100644
index 055c4a99..00000000
--- a/usbtuner-res/values-ka-rGE/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"TV-ტუნერი"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB TV-ტუნერი"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"ქსელის TV-ტუნერი (Beta)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"გთხოვთ, მოითმინოთ დამუშავების დასრულებამდე"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"ტუნერის პროგრამული უზრუნველყოფა ახლახან განახლდა. გთხოვთ, ხელახლა დაასკანიროთ არხები."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"აუდიოს ჩასართავად, სისტემის ხმის პარამეტრებში ჩართეთ მოცულობითი ხმა"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"აუდიოს დაკვრა ვერ ხერხდება. გთხოვთ, ცადოთ სხვა არხი"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"არხების ტუნერის დაყენება"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"TV-ტუნერის დაყენება"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"არხების USB ტუნერის დაყენება"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"ქსელის ტუნერის დაყენება"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"დარწმუნდით, რომ თქვენი ტელევიზორი მიერთებულია სატელევიზიო სიგნალის წყაროსთან..\n\nტერესტრიული ანტენის გამოყენების შემთხვევაში, არხების უმეტესობის მისაღებად, მისი განლაგების ან მიმართულების დარეგულება მოგიწევთ. საუკეთესო შედეგის მისაღებად, განათავსეთ ის სიმაღლეზე, ფანჯარასთან ახლოს."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"დარწმუნდით, რომ USB ტუნერი ბუდეშია და მიერთებულია სატელევიზიო სიგნალის წყაროსთან.\n\nტერესტრიული ანტენის გამოყენების შემთხვევაში, არხების უმეტესობის მისაღებად, მისი განლაგების ან მიმართულების დარეგულება მოგიწევთ. საუკეთესო შედეგის მისაღებად, განათავსეთ ის სიმაღლეზე, ფანჯარასთან ახლოს."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"დარწმუნდით, რომ ქსელის ტუნერი ჩართულია და მიერთებულია სატელევიზიო სიგნალის წყაროსთან.\n\nტერესტრიული ანტენის გამოყენების შემთხვევაში, არხების უმეტესობის მისაღებად, მისი განლაგების ან მიმართულების დარეგულირება მოგიწევთ. საუკეთესო შედეგის მისაღებად, განათავსეთ ის სიმაღლეზე, ფანჯარასთან ახლოს."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"გაგრძელება"</item>
- <item msgid="727245208787621142">"ახლა არა"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"გსურთ არხების ხელახლა დაყენება?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"ეს მოქმედება ამოშლის TV ტუნერით ნაპოვნ არხებს და ახალი არხების სკანირება ხელახლა მოხდება.\n\nდარწმუნდით, რომ თქვენი ტელევიზორი მიერთებულია სატელევიზიო სიგნალის წყაროსთან.\n\nტერესტრიული ანტენის გამოყენების შემთხვევაში, არხების უმეტესობის მისაღებად, მისი განლაგების ან მიმართულების დარეგულება მოგიწევთ. საუკეთესო შედეგის მისაღებად, განათავსეთ ის სიმაღლეზე, ფანჯარასთან ახლოს."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"ეს მოქმედება ამოშლის USB ტუნერით ნაპოვნ არხებს და ხელახლა მოხდება ახალი არხების სკანირება.\n\nდარწმუნდით, რომ USB ტუნერი ბუდეშია და მიერთებულია სატელევიზიო სიგნალის წყაროსთან.\n\nტერესტრიული ანტენის გამოყენების შემთხვევაში, არხების უმეტესობის მისაღებად, მისი განლაგების ან მიმართულების დარეგულება მოგიწევთ. საუკეთესო შედეგის მისაღებად, განათავსეთ ის სიმაღლეზე, ფანჯარასთან ახლოს."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"ეს მოქმედება ამოშლის ქსელის ტუნერით ნაპოვნ არხებს და ახალი არხების სკანირება ხელახლა მოხდება.\n\nდარწმუნდით, რომ ქსელის ტუნერი ჩართულია და მიერთებულია სატელევიზიო სიგნალის წყაროსთან.\n\nტერესტრიული ანტენის გამოყენების შემთხვევაში, არხების უმეტესობის მისაღებად, მისი განლაგების ან მიმართულების დარეგულირება მოგიწევთ. საუკეთესო შედეგის მისაღებად, განათავსეთ ის სიმაღლეზე, ფანჯარასთან ახლოს."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"გაგრძელება"</item>
- <item msgid="235450158666155406">"გაუქმება"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"აირჩიეთ კავშირის ტიპი"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"აირჩიეთ ანტენა, თუ ტუნერთან მიერთებულია გარე ანტენა. აირჩიეთ კაბელი, თუ არხებს საკაბელო სერვისის პროვაიდერისგან იღებთ. თუ დარწმუნებული არ ხართ, დასკანირდება ორივე ტიპი, მაგრამ ამას შეიძლება მეტი დრო დასჭირდეს."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"ანტენა"</item>
- <item msgid="2670079958754180142">"კაბელი"</item>
- <item msgid="36774059871728525">"არ ვიცი"</item>
- <item msgid="6881204453182153978">"მხოლოდ დეველოპერებისთვის"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"TV-ტუნერის დაყენება"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"არხების USB ტუნერის დაყენება"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"არხების ქსელის ტუნერის დაყენება"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"ამას შეიძლება რამდენიმე წუთი დასჭირდეს"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"ტუნერი დროებით მიუწვდომელია, ან უკვე გამოიყენება ჩასაწერად."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">მოიძებნა %1$d არხი</item>
- <item quantity="one">მოიძებნა %1$d არხი</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"არხების სკანირების შეწყვეტა"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">მოიძებნა %1$d არხი</item>
- <item quantity="one">მოიძებნა %1$d არხი</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">მშვენიერია! არხების სკანირებისას მოიძებნა %1$d არხი. თუ სათანადო შედეგს ვერ მიიღებთ, ცადეთ ანტენის პოზიციის დარეგულირება და ხელახლა დაასკანირეთ.</item>
- <item quantity="one">მშვენიერია! არხების სკანირებისას მოიძებნა %1$d არხი. თუ სათანადო შედეგს ვერ მიიღებთ, ცადეთ ანტენის პოზიციის დარეგულირება და ხელახლა დაასკანირეთ.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"მზადაა"</item>
- <item msgid="2480490326672924828">"ხელახლა სკანირება"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"არხები ვერ მოიძებნა"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"სკანირებისას არხები ვერ მოიძებნა. დარწმუნდით, რომ თქვენი ტელევიზორი მიერთებულია სატელევიზიო სიგნალის წყაროსთან.\n\nტერესტრიული ანტენის გამოყენების შემთხვევაში, დაარეგულირეთ მისი განლაგება ან მიმართულება. საუკეთესო შედეგის მისაღებად, განათავსეთ ის სიმაღლეზე, ფანჯარასთან ახლოს და ხელახლა დაასკანირეთ."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"სკანირებისას არხები ვერ მოიძებნა. დარწმუნდით, რომ USB ტუნერი ბუდეშია და მიერთებულია სატელევიზიო სიგნალის წყაროსთან. \n\nტერესტრიული ანტენის გამოყენების შემთხვევაში, დაარეგულირეთ მისი განლაგება ან მიმართულება. საუკეთესო შედეგის მისაღებად, განათავსეთ ის სიმაღლეზე, ფანჯარასთან ახლოს და ხელახლა დაასკანირეთ."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"სკანირებისას არხები ვერ მოიძებნა. დარწმუნდით, რომ ქსელის ტუნერი ჩართულია და მიერთებულია სატელევიზიო სიგნალის წყაროსთან. \n\nტერესტრიული ანტენის გამოყენების შემთხვევაში, დაარეგულირეთ მისი განლაგება ან მიმართულება. საუკეთესო შედეგის მისაღებად, განათავსეთ ის სიმაღლეზე, ფანჯარასთან ახლოს და ხელახლა დაასკანირეთ."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"ხელახლა სკანირება"</item>
- <item msgid="2092797862490235174">"მზადაა"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"სკანირება სატელევიზიო არხების აღმოსაჩენად"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"TV-ტუნერის დაყენება"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB TV-ტუნერის დაყენება"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"ქსელის TV-ტუნერის დაყენება"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB TV-ტუნერი გაითიშა."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"ქსელის ტუნერი გაითიშა."</string>
-</resources>
diff --git a/usbtuner-res/values-kk-rKZ/strings.xml b/usbtuner-res/values-kk-rKZ/strings.xml
deleted file mode 100644
index 7ca732b2..00000000
--- a/usbtuner-res/values-kk-rKZ/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"ТД тюнері"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB TД тюнері"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Желілік теледидар тюнері (БЕТА НҰСҚАСЫ)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Өңдеу аяқталғанша күте тұрыңыз"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Тюнердің бағдарламалық құралы жақында жаңартылды. Арналарды қайта іздеңіз."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Аудиомазмұнды қосу үшін жүйенің параметрлерінде көлемдік дыбысты қосыңыз"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Аудиомазмұн ойнатылмайды. Басқа теледидар арнасын қосып көріңіз"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Арна тюнерін орнату"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"ТД тюнерін орнату"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB арна тюнерін орнату"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Желі тюнерін реттеу"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Теледидардың ТД сигнал көзіне қосылғанын тексеріңіз.\n\nСымсыз антеннаны пайдалансаңыз, көптеген арналарды қабылдау үшін оның орнын немесе бағытын реттеу қажет болады. Ең жақсы нәтижеге қол жеткізу үшін оны жоғарырақ және терезеге жақын орналастырыңыз."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"USB тюнерін жалғанғанын және ТД сигнал көзіне қосылғанын тексеріңіз.\n\nСымсыз антеннаны пайдалансаңыз, көптеген арналарды қабылдау үшін оның орнын немесе бағытын реттеу қажет болады. Ең жақсы нәтижеге қол жеткізу үшін оны жоғарырақ және терезеге жақын орналастырыңыз."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"USB тюнері қосылып, теледидардың сигнал көзіне жалғанғанын тексеріңіз.\n\nСымсыз антеннаны пайдалансаңыз, көптеген арналарды қабылдау үшін оның орнын ауыстырыңыз немесе бағытын реттеңіз. Ең жақсы нәтижеге қол жеткізу үшін оны жоғарырақ және терезеге жақын жерге орналастырыңыз."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Жалғастыру"</item>
- <item msgid="727245208787621142">"Қазір емес"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Арналарды қайта орнату қажет пе?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"ТД тюнерінде табылған арналар жойылып, жаңа арналар ізделеді.\n\nТеледидардың ТД сигнал көзіне қосылғанын тексеріңіз.\n\nСымсыз антеннаны пайдалансаңыз, көптеген арналарды қабылдау үшін оның орнын немесе бағытын реттеу қажет болады. Ең жақсы нәтижеге қол жеткізу үшін оны жоғарырақ және терезеге жақын орналастырыңыз."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"USB тюнерінде табылған арналар жойылып, жаңа арналар ізделеді.\n\nUSB тюнері жалғанғанын және ТД сигнал көзіне қосылғанын тексеріңіз.\n\nСымсыз антеннаны пайдалансаңыз, көптеген арналарды қабылдау үшін оның орнын немесе бағытын реттеу қажет болады. Ең жақсы нәтижеге қол жеткізу үшін оны жоғарырақ және терезеге жақын орналастырыңыз."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Желі тюнерінде табылған арналар өшіріліп, жаңа арналар ізделеді.\n\nЖелі тюнері қосылып, теледидардың сигнал көзіне жалғанғанын тексеріңіз.\n\nСымсыз антеннаны пайдалансаңыз, көптеген арналарды қабылдау үшін оның орнын ауыстырыңыз немесе бағытын реттеңіз. Ең жақсы нәтижеге қол жеткізу үшін оны жоғарырақ және терезеге жақын жерге орналастырыңыз."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Жалғастыру"</item>
- <item msgid="235450158666155406">"Бас тарту"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Қосылу түрін таңдау"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Тюнерге қосымша антенна қосылған болса, \"Антенна\" опциясын таңдаңыз. Арналар кабельді қызмет провайдерінен алынған болса, \"Кабель\" опциясын таңдаңыз. Нақты білмесеңіз, екі түрі де ізделеді, бірақ бұған көбірек уақыт кетуі мүмкін."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Антенна"</item>
- <item msgid="2670079958754180142">"Кабель"</item>
- <item msgid="36774059871728525">"Нақты білмеймін"</item>
- <item msgid="6881204453182153978">"Тек әзірлеу"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"ТД тюнерін орнату"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB арна тюнерін орнату"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Желілік арна тюнерін реттеу"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Бұл бірнеше минутты алуы мүмкін"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Тюнер қолжетімсіз немесе жазу барысында қолданылуда."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d арна табылды</item>
- <item quantity="one">%1$d арна табылды</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"АРНА ІЗДЕУДІ ТОҚТАТУ"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d арна табылды</item>
- <item quantity="one">%1$d арна табылды</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Тамаша! Арналарды іздеу кезінде %1$d арна табылды. Жеткіліксіз болса, антеннаның орнын ауыстырып, қайта іздеп көріңіз.</item>
- <item quantity="one">Тамаша! Арналарды іздеу кезінде %1$d арна табылды. Жеткіліксіз болса, антеннаның орнын ауыстырып, қайта іздеп көріңіз.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Орындалды"</item>
- <item msgid="2480490326672924828">"Қайта іздеу"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Арналар табылмады"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Іздеу нәтижесінде ешқандай арна табылмады. Теледидардың ТД сигнал көзіне қосылғанын тексеріңіз.\n\nСымсыз антеннаны пайдалансаңыз, орнын немесе бағытын реттеңіз. Ең жақсы нәтижеге қол жеткізу үшін оны жоғарырақ және терезеге жақын орналастырып, қайта іздеп көріңіз."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Іздеу нәтижесінде арналар табылмады. USB тюнері жалғанғанын және ТД сигнал көзіне қосылғанын тексеріңіз.\n\nСымсыз антеннаны пайдалансаңыз, орнын немесе бағытын реттеңіз. Ең жақсы нәтижеге қол жеткізу үшін оны жоғарырақ және терезеге жақын орналастырып, қайта іздеп көріңіз."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Іздеу нәтижесінде арналар табылмады. Желі тюнері қосылып, теледидардың сигнал көзіне жалғанғанын тексеріңіз.\n\nСымсыз антеннаны пайдалансаңыз, оның орнын ауыстырыңыз немесе бағытын реттеңіз. Ең жақсы нәтижеге қол жеткізу үшін оны жоғарырақ және терезеге жақын жерге орналастырып, қайта іздеп көріңіз."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Қайта іздеу"</item>
- <item msgid="2092797862490235174">"Орындалды"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"ТД арналарын іздеу"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"ТД тюнерін орнату"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB TД тюнерін орнату"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Желілік ТД тюнерін реттеу"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB TД тюнері ажыратылды."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Желі тюнері ажыратылды."</string>
-</resources>
diff --git a/usbtuner-res/values-km-rKH/strings.xml b/usbtuner-res/values-km-rKH/strings.xml
deleted file mode 100644
index 5bca2a02..00000000
--- a/usbtuner-res/values-km-rKH/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"អង្គរាវប៉ុស្តិ៍ទូរទស្សន៍"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"ឧបករណ៍ USB រាវប៉ុស្តិ៍ទូរទស្សន៍"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"កម្មវិធី​រាវ​បណ្តាញ​ទូរទស្សន៍ (បេតា)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"សូមរង់ចាំដើម្បីបញ្ចប់ដំណើរការ"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"កម្មវិធីអង្គរាវប៉ុស្តិ៍បានអាប់ដេតថ្មីៗនេះ។ សូមស្កេនរកប៉ុស្តិ៍ទាំងនេះម្តងទៀត។"</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"បើកដំណើរការសំឡេងជុំវិញនៅក្នុងការកំណត់សំឡេងប្រព័ន្ធដើម្បីបើកដំណើរការអូឌីយ៉ូ"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"មិនអាចចាក់អូឌីយ៉ូបានទេ។ សូមសាកល្បងប្រើទូរទស្សន៍ផ្សេង"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"ការដំឡើងអង្គរាវប៉ុស្តិ៍"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"ការដំឡើងអង្គរាវប៉ុស្តិ៍ទូរទស្សន៍"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"ការដំឡើងឧបករណ៍ USB រាវប៉ុស្តិ៍"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"រៀបចំឧបករណ៍រាវបណ្តាញ"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"សូមផ្ទៀងផ្ទាត់ថាប៉ុស្តិ៍ទូរទស្សន៍របស់អ្នកត្រូវបានភ្ជាប់ទៅប្រភពរលកសញ្ញាទូរទស្សន៍។\n\nប្រសិនបើអ្នកប្រើអង់តែនឥតខ្សែ សូមសម្រួលទីតាំង ឬទិសដៅរបស់វាដើម្បីទទួលបានប៉ុស្តិ៍ច្រើនបំផុត។ ដើម្បីទទួលបានលទ្ធផលល្អបំផុត សូមដាក់វានៅកន្លែងខ្ពស់ និងនៅជិតបង្អួច។"</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"ផ្ទៀងផ្ទាត់ថាអ្នកបានដោតឧបករណ៍ USB រាវប៉ុស្តិ៍ និងបានភ្ជាប់ទៅប្រភពសញ្ញាទូរទស្សន៍\n\nប្រសិនបើអ្នកប្រើអង់តែនឥតខ្សែ អ្នកប្រហែលជាត្រូវកែសម្រួលទីតាំង និងទិសដៅរបស់វាដើម្បីទទួលបានប៉ុស្តិ៍ច្រើនបំផុត។ ដើម្បីទទួលបានលទ្ធផលល្អបំផុត សូមដាក់វានៅកន្លែងដែលខ្ពស់ក្បែរបង្អួច។"</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"ផ្ទៀងផ្ទាត់ថាអ្នកបានបើកឧបករណ៍រាវបណ្តាញនេះ និងបានភ្ជាប់ទៅប្រភពរលកសញ្ញាទូរទស្សន៍។\n\nប្រសិនបើអ្នកប្រើអង់តែនឥតខ្សែ អ្នកត្រូវកែសម្រួលទីតាំង និងទិសដៅរបស់វា ដើម្បីទទួលបានប៉ុស្តិ៍ច្រើនបំផុត។ ដើម្បីទទួលបានលទ្ធផលល្អបំផុត សូមដាក់វានៅកន្លែងដែលខ្ពស់ក្បែរបង្អួច។"</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"បន្ត"</item>
- <item msgid="727245208787621142">"មិនមែនឥឡូវនេះទេ"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"ដំណើរការដំឡើងប៉ុស្តិ៍ឡើងវិញឬទេ?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"វានឹងលុបប៉ុស្តិ៍ដែលបានរកឃើញដោយអង្គរាវប៉ុស្តិ៍ទូរទស្សន៍ចេញ ហើយស្កេនរកប៉ុស្តិ៍ថ្មីម្តងទៀត។\n\nផ្ទៀងផ្ទាត់ថាអ្នកបានដោតទូរទស្សន៍ទៅប្រភពរលកសញ្ញាទូរទស្សន៍\n\nប្រសិនបើអ្នកប្រើអង់តែនឥតខ្សែ អ្នកប្រហែលជាត្រូវសម្រួលទីតាំង និងទិសដៅរបស់វាដើម្បីទទួលបានប៉ុស្តិ៍ច្រើនបំផុត។ ដើម្បីទទួលបានលទ្ធផលល្អបំផុត សូមដាក់វានៅកន្លែងដែលខ្ពស់ក្បែរបង្អួច។"</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"វានឹងលុបប៉ុស្តិ៍ដែលបានរកឃើញដោយឧបករណ៍ USB រាវប៉ុស្តិ៍ចេញ ហើយស្កេនរកប៉ុស្តិ៍ថ្មីម្តងទៀត។\n\nផ្ទៀងផ្ទាត់ថាអ្នកបានដោតឧបករណ៍ USB រាវប៉ុស្តិ៍ និងបានភ្ជាប់ទៅប្រភពសញ្ញាទូរទស្សន៍\n\nប្រសិនបើអ្នកប្រើអង់តែនឥតខ្សែ អ្នកប្រហែលជាត្រូវសម្រួលទីតាំង និងទិសដៅរបស់វាដើម្បីទទួលបានប៉ុស្តិ៍ច្រើនបំផុត។ ដើម្បីទទួលបានលទ្ធផលល្អបំផុត សូមដាក់វានៅកន្លែងដែលខ្ពស់ក្បែរបង្អួច។"</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"វានឹងលុបប៉ុស្តិ៍ដែលបានរកឃើញដោយឧបករណ៍រាវបណ្តាញ ហើយបន្ទាប់មកស្កេនរកបណ្តាញថ្មី។\n\nផ្ទៀងផ្ទាត់ថាអ្នកបានបើកអង្គរាវបណ្តាញនេះ និងបានភ្ជាប់ទៅប្រភពរលកសញ្ញាទូរទស្សន៍។\n\nប្រសិនបើអ្នកប្រើអង់តែនឥតខ្សែ អ្នកត្រូវសម្រួលទីតាំង និងទិសដៅរបស់វា ដើម្បីទទួលបានប៉ុស្តិ៍ច្រើនបំផុត។ ដើម្បីទទួលបានលទ្ធផលល្អបំផុត សូមដាក់វានៅកន្លែងដែលខ្ពស់ក្បែរបង្អួច។"</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"បន្ត"</item>
- <item msgid="235450158666155406">"បោះបង់"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"ជ្រើសប្រភេទនៃការតភ្ជាប់"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"សូមជ្រើសរើសអង់តែន ប្រសិនបើអ្នកភ្ជាប់អង់តែនខាងក្រៅទៅនឹងអង្គរាវប៉ុស្តិ៍។ ឬជ្រើសរើសខ្សែកាប ប្រសិនបើប៉ុស្តិ៍របស់អ្នកផ្តល់ដោយក្រុមហ៊ុនផ្តល់សេវាខ្សែកាប។ ប្រសិនបើអ្នកមិនប្រាកដទេ អ្នកអាចជ្រើសរើសប្រភេទទាំងពីរដើម្បីស្កេន ប៉ុន្តែវាអាចចំណាយពេលយូរ។"</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"អង់តែន"</item>
- <item msgid="2670079958754180142">"ខ្សែកាប"</item>
- <item msgid="36774059871728525">"មិនប្រាកដ"</item>
- <item msgid="6881204453182153978">"ការអភិវឌ្ឍន៍ប៉ុណ្ណោះ"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"ការដំឡើងអង្គរាវប៉ុស្តិ៍ទូរទស្សន៍"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"ការដំឡើងឧបករណ៍ USB រាវប៉ុស្តិ៍ទូរទស្សន៍"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"ការរៀបចំឧបករណ៍រាវបណ្តាញប៉ុស្តិ៍"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"វាអាចចំណាយពេលច្រើននាទី"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"អង្គរាវប៉ុស្តិ៍មិនអាចប្រើបានជាបណ្តោះអាសន្ន ឬបានប្រើសម្រាប់ការថតរួចទៅហើយ។"</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">បានរកឃើញប៉ុស្តិ៍ %1$d</item>
- <item quantity="one">បានរកឃើញប៉ុស្តិ៍ %1$d</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"បញ្ឈប់ការស្កេនរកប៉ុស្តិ៍"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">បានរកឃើញប៉ុស្តិ៍ %1$d</item>
- <item quantity="one">បានរកឃើញប៉ុស្តិ៍ %1$d</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">ល្អណាស់! បានរកឃើញប៉ុស្តិ៍ %1$d អំឡុងពេលស្កេន។ ប្រសិនបើការស្កេននេះរកមិនឃើញប៉ុស្តិ៍ទេ សូមសាកល្បងសម្រួលទីតាំងអង់តែន ហើយស្កេនម្តងទៀត។</item>
- <item quantity="one">ល្អណាស់! បានរកឃើញប៉ុស្តិ៍ %1$d អំឡុងពេលស្កេន។ ប្រសិនបើការស្កេននេះរកមិនឃើញប៉ុស្តិ៍ទេ សូមសាកល្បងសម្រួលទីតាំងអង់តែន ហើយស្កេនម្តងទៀត។</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"រួចរាល់"</item>
- <item msgid="2480490326672924828">"ស្កេនម្តងទៀត"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"រកមិនឃើញប៉ុស្តិ៍ទេ"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"ការស្កេននេះរកមិនឃើញប៉ុស្តិ៍ណាមួយឡើយ។ សូមផ្ទៀងផ្ទាត់ថាប៉ុស្តិ៍ទូរទស្សន៍របស់អ្នកត្រូវបានភ្ជាប់ទៅប្រភពរលកសញ្ញាទូរទស្សន៍។\n\nប្រសិនបើអ្នកប្រើអង់តែនឥតខ្សែ សូមសម្រួលទីតាំង ឬទិសដៅរបស់វា។ ដើម្បីទទួលបានលទ្ធផលល្អបំផុត សូមដាក់វានៅកន្លែងខ្ពស់ និងនៅជិតបង្អួច បន្ទាប់មកធ្វើការស្កេនម្តងទៀត។"</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"ការស្កេនរកមិនឃើញប៉ុស្តិ៍ទេ។ ផ្ទៀងផ្ទាត់ថាអ្នកបានដោតឧបករណ៍ USB រាវប៉ុស្តិ៍ និងបានភ្ជាប់ទៅប្រភពសញ្ញាទូរទស្សន៍។\n\nប្រសិនបើអ្នកប្រើអង់តែនឥតខ្សែ សូមសម្រួលទីតាំង និងទិសដៅរបស់វា។ ដើម្បីទទួលបានលទ្ធផលល្អបំផុត សូមដាក់វានៅកន្លែងដែលខ្ពស់ក្បែរបង្អួច ហើយបន្ទាប់មកស្កេនម្តងទៀត។"</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"ការស្កេនរកមិនឃើញប៉ុស្តិ៍ណាមួយទេ។ ផ្ទៀងផ្ទាត់ថាអ្នកបានបើកឧបករណ៍រាវបណ្តាញនេះ និងបានភ្ជាប់ទៅប្រភពរលកសញ្ញាទូរទស្សន៍។\n\nប្រសិនបើអ្នកប្រើអង់តែនឥតខ្សែ សូមសម្រួលទីតាំង និងទិសដៅរបស់វា។ ដើម្បីទទួលបានលទ្ធផលល្អបំផុត សូមដាក់វានៅកន្លែងដែលខ្ពស់ក្បែរបង្អួច បន្ទាប់មកស្កេនម្តងទៀត។"</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"ស្កេនម្តងទៀត"</item>
- <item msgid="2092797862490235174">"រួចរាល់"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"ស្កេនរកប៉ុស្តិ៍ទូរទស្សន៍"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"ការរៀបចំអង្គរាវរកប៉ុស្តិ៍ទូរទស្សន៍"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"ការរៀបចំអង្គរាវរកប៉ុស្តិ៍ទូរទស្សន៍តាម USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"ការរៀបចំអង្គរាវរកបណ្តាញទូរទស្សន៍"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"អង្គរាវរកប៉ុស្តិ៍ USB សម្រាប់ទូរទស្សន៍ត្រូវបានផ្តាច់។"</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"អង្គរាវរក​បណ្តាញ​ត្រូវ​បាន​ផ្ដាច់។"</string>
-</resources>
diff --git a/usbtuner-res/values-kn-rIN/strings.xml b/usbtuner-res/values-kn-rIN/strings.xml
deleted file mode 100644
index af903462..00000000
--- a/usbtuner-res/values-kn-rIN/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"ಟಿವಿ ಟ್ಯೂನರ್"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB ಟಿವಿ ಟ್ಯೂನರ್"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"ನೆಟ್‌ವರ್ಕ್ ಟಿವಿ ಟ್ಯೂನರ್ (ಬೀಟಾ)‌‌"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"ಪ್ರಕ್ರಿಯೆಗೊಳಿಸುವುದನ್ನು ಪೂರೈಸಲು ದಯವಿಟ್ಟು ಕಾಯಿರಿ"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"ಟ್ಯೂನರ್ ಸಾಫ್ಟ್‌ವೇರ್‍ ಅನ್ನು ಇತ್ತೀಚಿಗೆ ಅಪ್‌ಡೇಟ್ ಮಾಡಲಾಗಿದೆ. ದಯವಿಟ್ಟು ಚಾನಲ್‌ಗಳನ್ನು ಮರು-ಸ್ಕ್ಯಾನ್‌ ಮಾಡಿ."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"ಆಡಿಯೊ ಸಕ್ರಿಯಗೊಳಿಸಲು ಸಿಸ್ಟಂ ಧ್ವನಿ ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ಸರೌಂಡ್ ಧ್ವನಿಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"ಆಡಿಯೊವನ್ನು ಪ್ಲೇ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ. ದಯವಿಟ್ಟು ಬೇರೊಂದು ಟಿವಿಯಲ್ಲಿ ಪ್ರಯತ್ನಿಸಿ"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"ಚಾನಲ್ ಟ್ಯೂನರ್ ಸೆಟಪ್"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"ಟಿವಿ ಟ್ಯೂನರ್ ಸೆಟಪ್"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB ಚಾನಲ್ ಟ್ಯೂನರ್ ಸೆಟಪ್"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"ನೆಟ್‌ವರ್ಕ್ ಟ್ಯೂನರ್ ಸೆಟಪ್"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"ನಿಮ್ಮ ಟಿವಿಯನ್ನು ಟಿವಿ ಸಿಗ್ನಲ್‌ ಮೂಲಕ್ಕೆ ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆಯೆ ಎಂದು ಪರಿಶೀಲಿಸಿ.\n\nನೀವು ಪ್ರಸಾರದ ಮೂಲಕ ಆಂಟೆನಾ ಬಳಸುತ್ತಿದ್ದರೆ, ಹೆಚ್ಚಿನ ಚಾನಲ್‌ಗಳನ್ನು ಸ್ವೀಕರಿಸಲು ನೀವು ಅದರ ಸ್ಥಳ ಅಥವಾ ದಿಕ್ಕನ್ನು ಹೊಂದಿಸಬೇಕಾಗಬಹುದು. ಉತ್ತಮ ಫಲಿತಾಂಶಗಳಿಗೆ, ಇದನ್ನು ಎತ್ತರದಲ್ಲಿ ಮತ್ತು ಕಿಟಕಿಯ ಬಳಿ ಇರಿಸಿ."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"USB ಟ್ಯೂನಲ್ ಅನ್ನು ಪ್ಲಗ್ ಇನ್ ಮಾಡಲಾಗಿದೆಯೇ ಮತ್ತು ಟಿವಿ ಸಿಗ್ನಲ್ ಮೂಲಕ್ಕೆ ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆಯೆ ಎಂದು ಪರಿಶೀಲಿಸಿ.\n\nಪ್ರಸಾರದ ಮೂಲಕ ಆಂಟೆನಾ ಬಳಸುತ್ತಿದ್ದರೆ, ಹೆಚ್ಚಿನ ಚಾನಲ್‌ಗಳನ್ನು ಸ್ವೀಕರಿಸಲು ನೀವು ಅದರ ಸ್ಥಳ ಅಥವಾ ದಿಕ್ಕನ್ನು ಹೊಂದಿಸಬೇಕಾಗಬಹುದು. ಉತ್ತಮ ಫಲಿತಾಂಶಗಳಿಗೆ, ಇದನ್ನು ಎತ್ತರದಲ್ಲಿ ಮತ್ತು ಕಿಟಕಿಯ ಬಳಿ ಇರಿಸಿ ಹಾಗೂ ಮತ್ತೆ ಸ್ಕ್ಯಾನ್ ಮಾಡಿ."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"ನೆಟ್‌ವರ್ಕ್ ಟ್ಯೂನರ್ ಆನ್ ಮಾಡಲಾಗಿದೆಯೇ ಮತ್ತು ಟಿವಿ ಸಿಗ್ನಲ್ ಮೂಲಕ್ಕೆ ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆಯೇ ಎಂದು ಪರಿಶೀಲಿಸಿ.\n\nನೀವು ನೆಟ್‌ವರ್ಕ್ ಸಂಪರ್ಕದ ಮೂಲಕ ಆಂಟೆನಾ ಬಳಸುತ್ತಿದ್ದರೆ, ಹೆಚ್ಚಿನ ಚಾನಲ್‌ಗಳನ್ನು ಸ್ವೀಕರಿಸಲು ನೀವು ಅದರ ಸ್ಥಳ ಅಥವಾ ದಿಕ್ಕನ್ನು ಹೊಂದಿಸಬೇಕಾಗಬಹುದು. ಉತ್ತಮ ಫಲಿತಾಂಶಗಳಿಗೆ, ಅದನ್ನು ಎತ್ತರದಲ್ಲಿ ಮತ್ತು ಕಿಟಕಿಯ ಬಳಿ ಇರಿಸಿ."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"ಮುಂದುವರಿಸು"</item>
- <item msgid="727245208787621142">"ಸದ್ಯಕ್ಕೆ ಬೇಡ"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"ಚಾನಲ್ ಸೆಟಪ್ ಅನ್ನು ಮರುರನ್ ಮಾಡುವುದೇ?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"ಟಿವಿ ಟ್ಯೂನರ್‌ನಿಂದ ಪತ್ತೆ ಮಾಡಲಾದ ಚಾನಲ್‌ಗಳನ್ನು ಇದು ತೆಗೆದುಹಾಕುತ್ತದೆ ಹಾಗೂ ಮತ್ತೆ ಹೊಸ ಚಾನಲ್‌ಗಳಿಗೆ ಸ್ಕ್ಯಾನ್ ಮಾಡಲಾಗುತ್ತದೆ.\n\nನಿಮ್ಮ ಟಿವಿಯನ್ನು ಟಿವಿ ಸಿಗ್ನಲ್‌ ಮೂಲಕ್ಕೆ ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆಯೆ ಎಂದು ಪರಿಶೀಲಿಸಿ.\n\nನೀವು ಪ್ರಸಾರದ ಮೂಲಕ ಆಂಟೆನಾ ಬಳಸುತ್ತಿದ್ದರೆ, ಹೆಚ್ಚಿನ ಚಾನಲ್‌ಗಳನ್ನು ಸ್ವೀಕರಿಸಲು ನೀವು ಅದರ ಸ್ಥಳ ಅಥವಾ ದಿಕ್ಕನ್ನು ಹೊಂದಿಸಬೇಕಾಗಬಹುದು. ಉತ್ತಮ ಫಲಿತಾಂಶಗಳಿಗೆ, ಇದನ್ನು ಎತ್ತರದಲ್ಲಿ ಮತ್ತು ಕಿಟಕಿಯ ಬಳಿ ಇರಿಸಿ."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"USB ಟ್ಯೂನರ್‌ನಿಂದ ಪತ್ತೆ ಮಾಡಲಾದ ಚಾನಲ್‌ಗಳನ್ನು ಇದು ತೆಗೆದುಹಾಕುತ್ತದೆ ಹಾಗೂ ಮತ್ತೆ ಹೊಸ ಚಾನಲ್‌ಗಳಿಗೆ ಸ್ಕ್ಯಾನ್ ಮಾಡಲಾಗುತ್ತದೆ.\n\nUSB ಟ್ಯೂನಲ್ ಅನ್ನು ಪ್ಲಗ್ ಇನ್ ಮಾಡಲಾಗಿದೆಯೇ ಮತ್ತು ಟಿವಿ ಸಿಗ್ನಲ್ ಮೂಲಕ್ಕೆ ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆಯೆ ಎಂದು ಪರಿಶೀಲಿಸಿ.\n\nನೀವು ಪ್ರಸಾರದ ಮೂಲಕ ಆಂಟೆನಾ ಬಳಸುತ್ತಿದ್ದರೆ, ಹೆಚ್ಚಿನ ಚಾನಲ್‌ಗಳನ್ನು ಸ್ವೀಕರಿಸಲು ನೀವು ಅದರ ಸ್ಥಳ ಅಥವಾ ದಿಕ್ಕನ್ನು ಹೊಂದಿಸಬೇಕಾಗಬಹುದು. ಉತ್ತಮ ಫಲಿತಾಂಶಗಳಿಗೆ, ಇದನ್ನು ಎತ್ತರದಲ್ಲಿ ಮತ್ತು ಕಿಟಕಿಯ ಬಳಿ ಇರಿಸಿ ಹಾಗೂ ಮತ್ತೆ ಸ್ಕ್ಯಾನ್ ಮಾಡಿ."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"ನೆಟ್‌ವರ್ಕ್ ಟ್ಯೂನರ್‌ನಿಂದ ಪತ್ತೆ ಮಾಡಲಾದ ಚಾನಲ್‌ಗಳನ್ನು ಇದು ತೆಗೆದುಹಾಕುತ್ತದೆ ಹಾಗೂ ಮತ್ತೆ ಹೊಸ ಚಾನಲ್‌ಗಳಿಗೆ ಸ್ಕ್ಯಾನ್ ಮಾಡುತ್ತದೆ.\n\nನೆಟ್‌ವರ್ಕ್ ಟ್ಯೂನಲ್ ಅನ್ನು ಆನ್ ಮಾಡಲಾಗಿದೆಯೇ ಮತ್ತು ಟಿವಿ ಸಿಗ್ನಲ್ ಮೂಲಕ್ಕೆ ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆಯೇ ಎಂದು ಪರಿಶೀಲಿಸಿ.\n\nನೀವು ನೆಟ್‌ವರ್ಕ್ ಸಂಪರ್ಕದ ಮೂಲಕ ಆಂಟೆನಾ ಬಳಸುತ್ತಿದ್ದರೆ, ಹೆಚ್ಚಿನ ಚಾನಲ್‌ಗಳನ್ನು ಸ್ವೀಕರಿಸಲು ನೀವು ಅದರ ಸ್ಥಳ ಅಥವಾ ದಿಕ್ಕನ್ನು ಹೊಂದಿಸಬೇಕಾಗಬಹುದು. ಉತ್ತಮ ಫಲಿತಾಂಶಗಳಿಗೆ, ಅದನ್ನು ಎತ್ತರದಲ್ಲಿ ಮತ್ತು ಕಿಟಕಿಯ ಬಳಿ ಇರಿಸಿ."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"ಮುಂದುವರಿಸು"</item>
- <item msgid="235450158666155406">"ರದ್ದುಮಾಡಿ"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"ಸಂಪರ್ಕದ ಪ್ರಕಾರ ಆಯ್ಕೆಮಾಡಿ"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"ಟ್ಯೂನರ್‌ಗೆ ಬಾಹ್ಯ ಆಂಟೆನಾವನ್ನು ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದ್ದರೆ ಆಂಟೆನಾ ಆರಿಸಿಕೊಳ್ಳಿ. ನಿಮ್ಮ ಚಾನಲ್‌ಗಳು ಕೇಬಲ್ ಸೇವೆ ಪೂರೈಕೆದಾರರಿಂದ ಬರುತ್ತಿದ್ದರೆ ಕೇಬಲ್ ಆರಿಸಿಕೊಳ್ಳಿ. ನಿಮಗೆ ಯಾವುದು ಎಂದು ಖಚಿತವಿಲ್ಲದಿದ್ದರೆ, ಎರಡೂ ಪ್ರಕಾರಗಳನ್ನು ಸ್ಕ್ಯಾನ್ ಮಾಡಲಾಗುತ್ತದೆ, ಆದರೆ ಇದಕ್ಕೆ ಹೆಚ್ಚು ಸಮಯ ತೆಗೆದುಕೊಳ್ಳಬಹುದು."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"ಆಂಟೆನಾ"</item>
- <item msgid="2670079958754180142">"ಕೇಬಲ್"</item>
- <item msgid="36774059871728525">"ಖಚಿತವಾಗಿಲ್ಲ"</item>
- <item msgid="6881204453182153978">"ಅಭಿವೃದ್ಧಿ ಮಾತ್ರ"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"ಟಿವಿ ಟ್ಯೂನರ್ ಸೆಟಪ್"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB ಚಾನಲ್ ಟ್ಯೂನರ್ ಸೆಟಪ್"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"ನೆಟ್‌ವರ್ಕ್ ಚಾನಲ್ ಟ್ಯೂನರ್ ಸೆಟಪ್"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"ಇದಕ್ಕೆ ಹಲವಾರು ನಿಮಿಷಗಳು ತೆಗೆದುಕೊಳ್ಳಬಹುದು"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"ಟ್ಯೂನರ್ ತಾತ್ಕಾಲಿಕವಾಗಿ ಲಭ್ಯವಿಲ್ಲ ಅಥವಾ ಈಗಾಗಲೇ ರೆಕಾರ್ಡಿಂಗ್‌ಗೆ ಬಳಸಲಾಗಿದೆ."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="one">%1$d ಚಾನಲ್‌ಗಳು ಕಂಡುಬಂದಿವೆ</item>
- <item quantity="other">%1$d ಚಾನಲ್‌ಗಳು ಕಂಡುಬಂದಿವೆ</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"ಚಾನಲ್ ಸ್ಕ್ಯಾನ್ ಮಾಡುವುದನ್ನು ನಿಲ್ಲಿಸಿ"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="one">%1$d ಚಾನಲ್‌ಗಳು ಕಂಡುಬಂದಿವೆ</item>
- <item quantity="other">%1$d ಚಾನಲ್‌ಗಳು ಕಂಡುಬಂದಿವೆ</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="one">ಉತ್ತಮ! ಚಾನಲ್ ಸ್ಕ್ಯಾನ್ ಸಮಯದಲ್ಲಿ %1$d ಚಾನಲ್‌ಗಳು ಕಂಡುಬಂದಿವೆ. ಇದು ಸರಿಯಲ್ಲವೆಂದು ತೋರಿದರೆ, ಆಂಟೆನಾ ಸ್ಥಿತಿಯನ್ನು ಹೊಂದಿಸಲು ಪ್ರಯತ್ನಿಸಿ ಹಾಗೂ ಮತ್ತೆ ಸ್ಕ್ಯಾನ್ ಮಾಡಿ.</item>
- <item quantity="other">ಉತ್ತಮ! ಚಾನಲ್ ಸ್ಕ್ಯಾನ್ ಸಮಯದಲ್ಲಿ %1$d ಚಾನಲ್‌ಗಳು ಕಂಡುಬಂದಿವೆ. ಇದು ಸರಿಯಲ್ಲವೆಂದು ತೋರಿದರೆ, ಆಂಟೆನಾ ಸ್ಥಿತಿಯನ್ನು ಹೊಂದಿಸಲು ಪ್ರಯತ್ನಿಸಿ ಹಾಗೂ ಮತ್ತೆ ಸ್ಕ್ಯಾನ್ ಮಾಡಿ.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"ಮುಗಿದಿದೆ"</item>
- <item msgid="2480490326672924828">"ಮತ್ತೆ ಸ್ಕ್ಯಾನ್ ಮಾಡು"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"ಯಾವುದೇ ಚಾನಲ್‌ಗಳು ಕಂಡುಬಂದಿಲ್ಲ"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"ಸ್ಕ್ಯಾನ್ ಯಾವುದೇ ಚಾನಲ್‌ಗಳನ್ನು ಪತ್ತೆ ಮಾಡಿಲ್ಲ. ನಿಮ್ಮ ಟಿವಿಯನ್ನು ಟಿವಿ ಸಿಗ್ನಲ್‌ ಮೂಲಕ್ಕೆ ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆಯೆ ಎಂದು ಪರಿಶೀಲಿಸಿ.\n\nನೀವು ಪ್ರಸಾರದ ಮೂಲಕ ಆಂಟೆನಾ ಬಳಸುತ್ತಿದ್ದರೆ, ಉತ್ತಮ ಫಲಿತಾಂಶಗಳಿಗೆ, ಇದನ್ನು ಎತ್ತರದಲ್ಲಿ ಮತ್ತು ಕಿಟಕಿಯ ಬಳಿ ಇರಿಸಿ ಹಾಗೂ ಮತ್ತೆ ಸ್ಕ್ಯಾನ್ ಮಾಡಿ."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"ಸ್ಕ್ಯಾನ್ ಯಾವುದೇ ಚಾನಲ್‌ಗಳನ್ನು ಪತ್ತೆ ಮಾಡಿಲ್ಲ. USB ಟ್ಯೂನಲ್ ಅನ್ನು ಪ್ಲಗ್ ಇನ್ ಮಾಡಲಾಗಿದೆಯೇ ಮತ್ತು ಟಿವಿ ಸಿಗ್ನಲ್ ಮೂಲಕ್ಕೆ ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆಯೆ ಎಂದು ಪರಿಶೀಲಿಸಿ.\n\nಪ್ರಸಾರದ ಮೂಲಕ ಆಂಟೆನಾ ಬಳಸುತ್ತಿದ್ದರೆ, ಅದರ ಸ್ಥಳ ಅಥವಾ ದಿಕ್ಕನ್ನು ಸರಿಹೊಂದಿಸಿ. ಉತ್ತಮ ಫಲಿತಾಂಶಗಳಿಗೆ, ಎತ್ತರದಲ್ಲಿ ಮತ್ತು ಕಿಟಕಿಯ ಬಳಿ ಇರಿಸಿ ಹಾಗೂ ಮತ್ತೆ ಸ್ಕ್ಯಾನ್ ಮಾಡಿ."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"ಸ್ಕ್ಯಾನ್ ನಂತರ ಯಾವುದೇ ಚಾನಲ್ ಕಂಡುಬಂದಿಲ್ಲ. ನೆಟ್‌ವರ್ಕ್ ಟ್ಯೂನರ್ ಆನ್ ಮಾಡಲಾಗಿದೆಯೇ ಮತ್ತು ಟಿವಿ ಸಿಗ್ನಲ್ ಮೂಲಕ್ಕೆ ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆಯೇ ಎಂದು ಪರಿಶೀಲಿಸಿ.\n\nನೀವು ನೆಟ್‌ವರ್ಕ್ ಸಂಪರ್ಕದ ಮೂಲಕ ಆಂಟೆನಾ ಬಳಸುತ್ತಿದ್ದರೆ, ಅದರ ಸ್ಥಳ ಅಥವಾ ದಿಕ್ಕನ್ನು ಹೊಂದಿಸಿ. ಉತ್ತಮ ಫಲಿತಾಂಶಗಳಿಗೆ, ಅದನ್ನು ಎತ್ತರದಲ್ಲಿ ಮತ್ತು ಕಿಟಕಿಯ ಬಳಿ ಇರಿಸಿ ಹಾಗೂ ಮತ್ತೊಮ್ಮೆ ಸ್ಕ್ಯಾನ್ ಮಾಡಿ."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"ಮತ್ತೆ ಸ್ಕ್ಯಾನ್ ಮಾಡು"</item>
- <item msgid="2092797862490235174">"ಮುಗಿದಿದೆ"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"ಟಿವಿ ಚಾನಲ್‌ಗಳನ್ನು ಸ್ಕ್ಯಾನ್ ಮಾಡಿ"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"ಟಿವಿ ಟ್ಯೂನರ್ ಸೆಟಪ್"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB ಟಿವಿ ಟ್ಯೂನರ್ ಸೆಟಪ್"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"ನೆಟ್‌ವರ್ಕ್ ಟಿವಿ ಟ್ಯೂನರ್ ಸೆಟಪ್"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB ಟಿವಿ ಟ್ಯೂನರ್ ಸಂಪರ್ಕ ಕಡಿತಗೊಂಡಿದೆ."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"ನೆಟ್‌ವರ್ಕ್ ಟ್ಯೂನರ್ ಸಂಪರ್ಕ ಕಡಿತಗೊಂಡಿದೆ."</string>
-</resources>
diff --git a/usbtuner-res/values-ko/strings.xml b/usbtuner-res/values-ko/strings.xml
deleted file mode 100644
index fa5b6e60..00000000
--- a/usbtuner-res/values-ko/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"TV 튜너"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB TV 튜너"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"네트워크 TV 튜너(BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"처리가 완료될 때까지 기다려 주세요."</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"튜너 소프트웨어가 최근 업데이트되었습니다. 채널을 다시 스캔하세요."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"오디오를 사용하려면 시스템 사운드 설정에서 서라운드 사운드를 사용 설정하세요."</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"오디오를 재생할 수 없습니다. 다른 TV를 사용해 보세요."</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"채널 튜너 설정"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"TV 튜너 설정"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB 채널 튜너 설정"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"네트워크 튜너 설정"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"TV가 TV 신호 소스에 연결되어 있는지 확인하세요.\n\n무선 안테나를 사용하는 경우 대부분의 채널을 수신하려면 위치나 방향을 조정해야 할 수도 있습니다. 안테나를 창가 가까이에 높게 설치하면 가장 좋습니다."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"USB 튜너가 전원 및 TV 신호 소스에 연결되어 있는지 확인하세요.\n\n무선 안테나를 사용 중인 경우 안테나 위치나 방향을 조정하세요. 창가 가까이에 높게 설치하면 가장 좋습니다."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"네트워크 튜너의 전원이 켜져 있고 TV 신호 소스에 연결되어 있는지 확인하세요.\n\n무선 안테나를 사용하는 경우 안테나 위치나 방향을 조정하세요. 최상의 결과를 얻으려면 안테나를 창가 가까이에 높게 설치하세요."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"계속"</item>
- <item msgid="727245208787621142">"나중에"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"채널 설정을 다시 실행하시겠습니까?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"이렇게 하면 TV 튜너에서 찾은 채널을 삭제하고 새로운 채널을 다시 스캔하게 됩니다.\n\nTV가 TV 신호 소스에 연결되어 있는지 확인하세요.\n\n무선 안테나를 사용하는 경우 대부분의 채널을 수신하려면 위치나 방향을 조정해야 할 수 있습니다. 안테나를 창가 가까이에 높게 설치하면 가장 좋습니다."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"이 작업을 수행하면 USB 튜너에서 찾은 채널이 삭제되며 새로운 채널을 다시 스캔합니다.\n\nUSB 튜너가 전원 및 TV 신호 소스에 연결되어 있는지 확인하세요.\n\n무선 안테나를 사용 중인 경우 안테나 위치나 방향을 조정하세요. 창가 가까이에 높게 설치하면 가장 좋습니다."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"네트워크 튜너에서 찾은 채널이 삭제되며 새로운 채널을 다시 스캔합니다.\n\n네트워크 튜너의 전원이 켜져 있고 TV 신호 소스에 연결되어 있는지 확인하세요.\n\n무선 안테나를 사용하는 경우 안테나 위치나 방향을 조정하세요. 최상의 결과를 얻으려면 안테나를 창가 가까이에 높게 설치하고 다시 스캔해보세요."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"계속"</item>
- <item msgid="235450158666155406">"취소"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"연결 유형 선택"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"튜너에 외부 안테나가 연결된 경우 안테나를 선택하고 케이블 서비스 제공업체에서 채널을 제공하는 경우 케이블을 선택하세요. 잘 모르는 경우 두 가지 유형이 모두 스캔되며 시간이 더 오래 걸릴 수 있습니다."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"안테나"</item>
- <item msgid="2670079958754180142">"케이블"</item>
- <item msgid="36774059871728525">"잘 모르겠음"</item>
- <item msgid="6881204453182153978">"개발자 전용"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"TV 튜너 설정"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB 채널 튜너 설정"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"네트워크 채널 튜너 설정"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"이 작업은 몇 분 정도 걸릴 수 있습니다."</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"일시적으로 튜너를 사용할 수 없거나 이미 녹화에 사용하고 있습니다."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">채널 %1$d개 발견</item>
- <item quantity="one">채널 %1$d개 발견</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"채널 스캔 중지"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">채널 %1$d개 발견</item>
- <item quantity="one">채널 %1$d개 발견</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">좋습니다. 채널 스캔 중에 채널 %1$d개를 발견했습니다. 맞지 않는 것 같다면 안테나 위치를 조정한 후 다시 스캔하세요.</item>
- <item quantity="one">좋습니다. 채널 스캔 중에 채널 %1$d개를 발견했습니다. 맞지 않는 것 같다면 안테나 위치를 조정한 후 다시 스캔하세요.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"완료"</item>
- <item msgid="2480490326672924828">"다시 스캔"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"채널 없음"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"스캔 결과 채널을 찾지 못했습니다. TV가 TV 신호 소스에 연결되어 있는지 확인하세요.\n\n무선 안테나를 사용하는 경우 위치나 방향을 조정하세요. 안테나를 창가 가까이에 높게 설치하면 가장 좋습니다."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"스캔하여 채널을 찾을 수 없습니다. USB 튜너가 전원 및 TV 신호 소스에 연결되어 있는지 확인하세요.\n\n무선 안테나를 사용 중인 경우 안테나 위치나 방향을 조정하세요. 창가 가까이에 높게 설치하면 가장 좋습니다. 그런 다음 다시 스캔해 보세요."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"스캔 결과 채널을 찾지 못했습니다. 네트워크 튜너의 전원이 켜져 있고 TV 신호 소스에 연결되어 있는지 확인하세요.\n\n무선 안테나를 사용하는 경우 안테나 위치나 방향을 조정하세요. 최상의 결과를 얻으려면 안테나를 창가 가까이에 높게 설치하고 다시 스캔해보세요."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"다시 스캔"</item>
- <item msgid="2092797862490235174">"완료"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"TV 채널 스캔"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"TV 튜너 설정"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB TV 튜너 설정"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"네트워크 TV 튜너 설정"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB TV 튜너의 연결이 끊겼습니다."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"네트워크 튜너의 연결이 끊겼습니다."</string>
-</resources>
diff --git a/usbtuner-res/values-ky-rKG/strings.xml b/usbtuner-res/values-ky-rKG/strings.xml
deleted file mode 100644
index 18118225..00000000
--- a/usbtuner-res/values-ky-rKG/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"Сыналгы күүлөгүчү"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB сыналгы күүлөгүчү"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Тармактык ТВ-тюнер (БЕТА)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Иштетүүнү бүтүрүү үчүн күтө туруңуз"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Күүлөгүчтүн программасы жакында жаңыртылды. Каналдарды кайрадан издеңиз."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Каналдын үнүн чыгаруу үчүн тутумдун үн жөндөөлөрүнө өтүп, көлөмдүү добушту иштетүү керек"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Аудио ойнотулбай жатат. Башка каналды байкап көрүңүз"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Канал күүлөгүчтү жөндөө"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Сыналгынын күүлөгүчүн жөндөө"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB канал күүлөгүчүнүн жөндөөсү"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Тармактык тюнерди жөндөө"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Сыналдыңыз сыналгынын сигнал булагына туташтырылганын текшериңиз. \n\nЭгер телеантенна колдонулуп жатса, анын ордун же багытын тууралаңыз. Мыкты кармашы үчүн аны бийик жана терезеге жакын жерге коюп, кайра издеңиз."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"USB күүлөгүч сайылып турганын жана сыналгынын сигнал булагына туташтырылганын текшериңиз.\n\nЭгер телеантенна колдонулуп жатса, көпчүлүк каналдарды табуу үчүн анын ордун же багытын тууралашыңыз керек болот. Мыкты кармашы үчүн аны бийик жана терезеге жакын жерге коюп, кайра издеңиз."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Тармактык тюнер сайылып турганын жана сыналгынын сигнал булагына туташтырылганын текшериңиз.\n\nЭгер телеантенна колдонулуп жатса, каналдарды табуу үчүн анын ордун же багытын тууралашыңыз керек болот. Антенна жакшы кармашы үчүн аны бийик жана терезеге жакын жерге коюп, кайра издеп көрүңүз."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Улантуу"</item>
- <item msgid="727245208787621142">"Азыр эмес"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Канал кайра жөндөлсүнбү?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Ушуну менен сыналгынын күүлөгүчүнөн табылган каналдар алынып салынып, жаңы каналдар кайра изделет.\n\nСыналдыңыз сыналгынын сигнал булагына туташтырылганын текшериңиз.\n\nIЭгер телеантенна колдонулуп жатса, көпчүлүк каналдарды табуу үчүн анын ордун же багытын тууралашыңыз керек болот. Мыкты кармашы үчүн аны бийик жана терезеге жакын жерге коюп, кайра издеңиз."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Ушуну менен USB күүлөгүчтөн табылган каналдар алынып салынып, жаңы каналдар кайра изделет.\n\nUSB күүлөгүч сайылып турганын жана сыналгынын сигнал булагына туташтырылганын текшериңиз.\n\nЭгер телеантенна колдонулуп жатса, көпчүлүк каналдарды табуу үчүн анын ордун же багытын тууралашыңыз керек болот. Мыкты кармашы үчүн аны бийик жана терезеге жакын жерге коюп, кайра издеңиз."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Ушуну менен тармактык тюнер аркылуу табылган каналдар алынып салынып, жаңы каналдар кайра изделет.\n\nТармактык тюнер сайылып турганын жана сыналгынын сигнал булагына туташтырылганын текшериңиз.\n\nЭгер телеантенна колдонулуп жатса, каналдарды табуу үчүн анын ордун же багытын тууралашыңыз керек болот. Антенна жакшы кармашы үчүн аны бийик жана терезеге жакын жерге коюп, кайра издеп көрүңүз."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Улантуу"</item>
- <item msgid="235450158666155406">"Токтотуу"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Туташуу түрүн тандаңыз"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Күүлөгүчтө туташтырылган тышкы антенна болсо, Антеннаны тандаңыз. Эгер каналдарыңыз кабелдик кызмат камсыздоочусунан алынса, Кабелди тандаңыз. Эгер так билбесеңиз, эки түрү тең изделет, бирок ал узагыраак созулушу мүмкүн."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Антенна"</item>
- <item msgid="2670079958754180142">"Кабель"</item>
- <item msgid="36774059871728525">"Так айта албайм"</item>
- <item msgid="6881204453182153978">"Иштеп чыгуучулар үчүн гана"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Сыналгы күүлөгүчүн жөндөө"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB канал күүлөгүчүн жөндөө"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Тармактык каналдын тюнерин жөндөө"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Бир нече мүнөт созулушу мүмкүн"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Тюнер убактылуу жеткиликсиз же жаздыруу үчүн колдонулууда."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d канал табылды</item>
- <item quantity="one">%1$d канал табылды</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"КАНАЛ ИЗДӨӨНҮ ТОКТОТУУ"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d канал табылды</item>
- <item quantity="one">%1$d канал табылды</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Сонун! %1$d канал табылды. Керектүү каналдар табылбаса, антеннанын багытын өзгөртүп, кайра издеп көрүңүз.</item>
- <item quantity="one">Сонун! %1$d канал табылды. Керектүү каналдар табылбаса, антеннанын багытын өзгөртүп, кайра издеп көрүңүз.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Бүттү"</item>
- <item msgid="2480490326672924828">"Кайра издөө"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Бир да канал табылган жок"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Издөөдөн эч бир канал табылган жок. Сыналдыңыз сыналгынын сигнал булагына туташтырылганын текшериңиз.\n\nЭгер телеантенна колдонулуп жатса, анын ордун же багытын тууралаңыз. Мыкты кармашы үчүн аны бийик жана терезеге жакын жерге коюп, кайра издеңиз."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Издөөдөн эч бир канал табылган жок. USB күүлөгүч сайылып турганын жана сыналгынын сигнал булагына туташтырылганын текшериңиз.\n\nЭгер телеантенна колдонулуп жатса, анын ордун же багытын тууралаңыз. Мыкты кармашы үчүн аны бийик жана терезеге жакын жерге коюп, кайра издеңиз."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Каналдар табылган жок. Тармактык тюнер сайылып турганын жана сыналгынын сигнал булагына туташтырылганын текшериңиз.\n\nЭгер телеантенна колдонулуп жатса, анын ордун же багытын тууралаңыз. Антенна жакшы кармашы үчүн аны бийик жана терезеге жакын жерге коюп, кайра издеп көрүңүз."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Кайра издөө"</item>
- <item msgid="2092797862490235174">"Бүттү"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Сыналгы каналдарын издөө"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"ТВ-тюнерди жөндөө"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB ТВ-тюнерин жөндөө"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Тармактык ТВ-тюнерди жөндөө"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB TV күүлөгүчү ажыратылды."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Тармак күүлөгүчү ажыратылды."</string>
-</resources>
diff --git a/usbtuner-res/values-lo-rLA/strings.xml b/usbtuner-res/values-lo-rLA/strings.xml
deleted file mode 100644
index 306576a2..00000000
--- a/usbtuner-res/values-lo-rLA/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"ຕົວຮັບສັນຍານໂທລະພາບ"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"ຕົວຮັບສັນຍານໂທລະພາບ USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Network TV Tuner (ເບຕ້າ)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"ກະລຸນາລໍຖ້າເພື່ອປະມວນຜົນໃຫ້ສຳເລັດ"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"ຊອບແວຕົວປັບສັນຍານໄດ້ຮັບການອັບເດດເມື່ອບໍ່ດົນມານີ້ແລ້ວ. ກະລຸນາສະແກນຫາຊ່ອງຄືນໃໝ່."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"ເປີດໃຊ້ສຽງຮອບທິດທາງໃນການຕັ້ງຄ່າລະບົບສຽງເພື່ອເປີດໃຊ້ສຽງ."</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Cannot play audio. Please try another TV"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"ຕັ້ງເຄື່ອງຮັບສັນຍານຊ່ອງ"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"ຕັ້ງຄ່າຕົວຮັບສັນຍານໂທລະພາບ"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"ຕັ້ງເຄື່ອງຮັບສັນຍານຊ່ອງ USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Network tuner setup"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"ໃຫ້ກວດສອບວ່າທ່ານເຊື່ອມຕໍ່ໂທລະພາບຂອງທ່ານຫາແຫລ່ງສັນຍານໂທລະພາບແລ້ວ.\n\nຫາກໃຊ້ເສົາອາກາດ, ໃຫ້ຍ້າຍບ້ອນວາງ ຫຼື ທິດທາງຂອງມັນ. ເພື່ອໃຫ້ໄດ້ຜົນທີ່ດີທີ່ສຸດ, ໃຫ້ວາງໄວ້ບ່ອນສູງໃກ້ໆປ່ອງຢ້ຽມ ແລ້ວລອງສະແກນໃໝ່."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"ກວດສອບເບິ່ງເຄື່ອງຮັບສັນຍານ USB ວ່າ ໄດ້ສຽບສາຍ ແລະ ເຊື່ອມຕໍ່ກັບແຫຼ່ງສັນຍານໂທລະພາບແລ້ວຫຼືຍັງ.\n\nຫາກໃຊ້ເສົາອາກາດ, ໃຫ້ຍ້າຍບ້ອນວາງ ຫຼື ທິດທາງຂອງມັນ. ເພື່ອໃຫ້ໄດ້ຜົນທີ່ດີທີ່ສຸດ, ໃຫ້ວາງໄວ້ບ່ອນສູງໃກ້ໆປ່ອງຢ້ຽມ ແລ້ວລອງສະແກນໃໝ່."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"ກວດສອບເບິ່ງເຄື່ອງຮັບສັນຍານເຄືອຂ່າຍວ່າ ໄດ້ສຽບສາຍ ແລະ ເຊື່ອມຕໍ່ກັບແຫຼ່ງສັນຍານໂທລະພາບແລ້ວຫຼືຍັງ.\n\nຫຼຫາກໃຊ້ເສົາອາກາດ, ໃຫ້ຍ້າຍບ່ອນວາງ ຫຼື ທິດທາງຂອງມັນ. ເພື່ອໃຫ້ໄດ້ຜົນທີ່ດີທີ່ສຸດ, ໃຫ້ວາງໄວ້ບ່ອນສູງໃກ້ໆປ່ອງຢ້ຽມ ແລ້ວລອງສະແກນໃໝ່."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"ສືບຕໍ່"</item>
- <item msgid="727245208787621142">"ບໍ່ແມ່ນຕອນນີ້"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"ຕັ້ງຄ່າຊ່ອງຄືນໃໝ່ບໍ່?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"ນີ້ເປັນການລຶບຊ່ອງທີ່ພົບແລ້ວອອກໄປຈາກຕົວຮັບສັນຍານໂທລະພາບ ແລະ ສະແກນຫາຊ່ອງໃໝ່ອີກຄັ້ງ.\n\nກວດສອບເບິ່ງຕົວຮັບສັນຍານໂທລະພາບວ່າໄດ້ສຽບສາຍ ແລະ ເຊື່ອມຕໍ່ກັບແຫຼ່ງສັນຍານໂທລະພາບແລ້ວຫຼືຍັງ.\n\nຫາກໃຊ້ເສົາອາກາດ, ໃຫ້ຍ້າຍບ້ອນວາງ ຫຼື ທິດທາງຂອງມັນ. ເພື່ອໃຫ້ໄດ້ຜົນທີ່ດີທີ່ສຸດ, ໃຫ້ວາງໄວ້ບ່ອນສູງໃກ້ໆປ່ອງຢ້ຽມ ແລ້ວລອງສະແກນໃໝ່."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"ນີ້ຈະເປັນການລຶບຊ່ອງທີ່ພົບແລ້ວອອກໄປຈາກເຄື່ອງຮັບສັນຍານ USB ແລະ ສະແກນຫາຊ່ອງໃໝ່ອີກ.\n\nໃຫ້ກວດສອບເບິ່ງເຄື່ອງຮັບສັນຍານ USB ວ່າ ໄດ້ສຽບສາຍ ແລະ ເຊື່ອມຕໍ່ກັບແຫຼ່ງສັນຍານໂທລະພາບແລ້ວຫຼືຍັງ.\n\nຫາກໃຊ້ເສົາອາກາດ, ໃຫ້ຍ້າຍບ້ອນວາງ ຫຼື ທິດທາງຂອງມັນ. ເພື່ອໃຫ້ໄດ້ຜົນທີ່ດີທີ່ສຸດ, ໃຫ້ວາງໄວ້ບ່ອນສູງໃກ້ໆປ່ອງຢ້ຽມ ແລ້ວລອງສະແກນໃໝ່."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"ນີ້ຈະເປັນການລຶບຊ່ອງທີ່ພົບແລ້ວອອກໄປຈາກເຄື່ອງຮັບສັນຍານເຄືອຂ່າຍ ແລະ ສະແກນຫາຊ່ອງໃໝ່ອີກຄັ້ງ.\n\nໃຫ້ກວດສອບເບິ່ງເຄື່ອງຮັບສັນຍານເຄືອຂ່າຍວ່າໄດ້ສຽບສາຍ ແລະ ເຊື່ອມຕໍ່ກັບແຫຼ່ງສັນຍານໂທລະພາບແລ້ວຫຼືຍັງ.\n\nຫາກໃຊ້ເສົາອາກາດ, ທ່ານອາດຈະຕ້ອງໄດ້ປັບການຕັ້ງ ຫຼື ທິດທາງຂອງມັນ. ເພື່ອໃຫ້ໄດ້ຜົນດີຂຶ້ນ, ວາງມັນໄວ້ສູງ ແລະ ໃກ້ກັບປ່ອງຢ້ຽມແລ້ວສະແກນໃໝ່ອີກຄັ້ງ."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"ສືບຕໍ່"</item>
- <item msgid="235450158666155406">"ຍົກເລີກ"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"ເລືອກປະເພດການເຊື່ອມຕໍ່"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"ເລືອກເສົາອາກາດ ຖ້າມີເສົາອາກາດແບບແຍກທີ່ເຊື່ອມຕໍ່ກັບເຄື່ອງຮັບສັນຍານ. ເລືອກສາຍເຄເບິ້ນ ຖ້າຊ່ອງຂອງທ່ານມາຈາກຜູ້ໃຫ້ບໍລິການສາຍເຄເບິ້ນ. ຖ້າທ່ານບໍ່ແນ່ໃຈ, ຈະມີການສະແກນທັງສອງປະເພດ, ແຕ່ແບບນີ້ຈະໃຊ້ເວລາດົນກວ່າ."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"ເສົາອາກາດ"</item>
- <item msgid="2670079958754180142">"ສາຍຕໍ່"</item>
- <item msgid="36774059871728525">"ບໍ່ແນ່ໃຈ"</item>
- <item msgid="6881204453182153978">"ການພັດທະນາເທົ່ານັ້ນ"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"ຕັ້ງຄ່າຕົວຮັບສັນຍານໂທລະພາບ"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"ການຕັ້ງເຄື່ອງຮັບສັນຍານຊ່ອງ USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"ຕັ້ງຄ່າຕົວຈູນສັນຍານຊ່ອງເຄືອຂ່າຍ"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"ຂັ້ນຕອນນີ້ອາດຈະໃຊ້ເວລາຫຼາຍນາທີ"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"ຈູນເນີບໍ່ສາມາດໃຊ້ໄດ້ຊົ່ວຄາວ ຫຼື ຖືກໃຊ້ໂດຍການບັນທຶກໃດໜຶ່ງຢູ່ກ່ອນແລ້ວ."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">ພົບ %1$d ຊ່ອງ</item>
- <item quantity="one">ພົບ %1$d ຊ່ອງ</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"ຢຸດການສະແກນຊ່ອງ"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">ພົບ %1$d ຊ່ອງ</item>
- <item quantity="one">ພົບ %1$d ຊ່ອງ</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">ດີຫຼາຍ! ພົບ %1$d ຊ່ອງໃນລະຫວ່າງການສະແກນຫາຊ່ອງ. ຖ້ານີ້ປາກົດວ່າບໍ່ຖືກຕ້ອງ, ໃຫ້ລອງປັບຕຳແໜ່ງເສົາອາກາດ ແລ້ວສະແກນໃໝ່.</item>
- <item quantity="one">ດີຫຼາຍ! ພົບ %1$d ຊ່ອງໃນລະຫວ່າງການສະແກນຫາຊ່ອງ. ຖ້ານີ້ປາກົດວ່າບໍ່ຖືກຕ້ອງ, ໃຫ້ລອງປັບຕຳແໜ່ງເສົາອາກາດ ແລ້ວສະແກນໃໝ່.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"ແລ້ວໆ"</item>
- <item msgid="2480490326672924828">"ສະແກນອີກ"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"ບໍ່ພົບຊ່ອງໃດ"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"ການສະແກນບໍ່ພົບຊ່ອງໃດໆເລີຍ. ໃຫ້ຢັ້ງຢືນວ່າທ່ານເຊື່ອມຕໍ່ໂທລະພາບຂອງທ່ານຫາແຫລ່ງສັນຍານໂທລະພາບແລ້ວ.\n\nຫາກໃຊ້ເສົາອາກາດ, ໃຫ້ຍ້າຍບ້ອນວາງ ຫຼື ທິດທາງຂອງມັນ. ເພື່ອໃຫ້ໄດ້ຜົນທີ່ດີທີ່ສຸດ, ໃຫ້ວາງໄວ້ບ່ອນສູງໃກ້ໆປ່ອງຢ້ຽມ ແລ້ວລອງສະແກນໃໝ່."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"ການສະແກນບໍ່ພົບຊ່ອງໃດ. ໃຫ້ກວດສອບເບິ່ງເຄື່ອງຮັບສັນຍານ USB ວ່າໄດ້ສຽບສາຍ ແລະ ເຊື່ອມຕໍ່ກັບແຫຼ່ງສັນຍານໂທລະພາບແລ້ວຫຼືຍັງ.\n\nຫາກໃຊ້ເສົາອາກາດ, ໃຫ້ຍ້າຍບ້ອນວາງ ຫຼື ທິດທາງຂອງມັນ. ເພື່ອໃຫ້ໄດ້ຜົນທີ່ດີທີ່ສຸດ, ໃຫ້ວາງໄວ້ບ່ອນສູງໃກ້ໆປ່ອງຢ້ຽມ ແລ້ວລອງສະແກນໃໝ່."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"ການສະແກນບໍ່ພົບຊ່ອງໃດ. ໃຫ້ກວດສອບເບິ່ງເຄື່ອງຮັບສັນຍານເຄືອຂ່າຍວ່າໄດ້ສຽບສາຍ ແລະ ເຊື່ອມຕໍ່ກັບແຫຼ່ງສັນຍານໂທລະພາບແລ້ວຫຼືຍັງ.\n\nຫາກໃຊ້ເສົາອາກາດ, ໃຫ້ຍ້າຍບ້ອນວາງ ຫຼື ທິດທາງຂອງມັນ. ເພື່ອໃຫ້ໄດ້ຜົນທີ່ດີທີ່ສຸດ, ໃຫ້ວາງໄວ້ບ່ອນສູງໃກ້ໆປ່ອງຢ້ຽມ ແລ້ວລອງສະແກນໃໝ່."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"ສະແກນອີກ"</item>
- <item msgid="2092797862490235174">"ແລ້ວໆ"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"ສະແກນຫາຊ່ອງໂທລະທັດ"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"ຕັ້ງຄ່າຕົວຮັບສັນຍານໂທລະທັດ"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"ຕັ້ງຄ່າຕົວຮັບສັນຍານໂທລະທັດແບບ USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"ຕັ້ງຄ່າຕົວຮັບສັນຍານໂທລະທັດເຄືອຂ່າຍ"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"ຕັດການເຊື່ອມຕໍ່ USB TV tuner ແລ້ວ."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"ຕັດການເຊື່ອມຕໍ່ຕົວຈູນສັນຍານເຄືອຂ່າຍແລ້ວ."</string>
-</resources>
diff --git a/usbtuner-res/values-lt/strings.xml b/usbtuner-res/values-lt/strings.xml
deleted file mode 100644
index c55020bd..00000000
--- a/usbtuner-res/values-lt/strings.xml
+++ /dev/null
@@ -1,96 +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="bt_app_name" msgid="5515382901862469770">"TV imtuvas"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB TV imtuvas"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Tinklo TV imtuvas (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Palaukite, kol baigsis apdorojimas"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Imtuvo programinė įranga buvo neseniai atnaujinta. Iš naujo nuskaitykite kanalus."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Įgalinkite erdvinį garsą sistemos garso nustatymuose, kad galėtumėte įgalinti garsą"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Negalima paleisti garso įrašo. Bandykite naudoti kitą TV"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Kanalų imtuvo sąranka"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"TV imtuvo sąranka"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB kanalų imtuvo sąranka"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Tinklo imtuvo sąranka"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Įsitikinkite, kad jūsų TV prijungtas prie TV signalo šaltinio.\n\nJei naudojate belaidę anteną, koreguokite jos vietą arba kryptį. Kad pasiektumėte geriausių rezultatų, ją padėkite aukštai prie lango."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Įsitikinkite, kad USB imtuvas prijungtas prie maitinimo ir TV signalo šaltinio.\n\nJei naudojate belaidę anteną, koreguokite jos vietą arba kryptį, kad būtų rodoma kuo daugiau kanalų. Kad pasiektumėte geriausių rezultatų, ją padėkite aukštai prie lango."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Įsitikinkite, kad tinklo imtuvas yra įjungtas ir prijungtas prie TV signalo šaltinio.\n\nJei naudojate belaidę anteną, koreguokite jos vietą arba kryptį. Kad pasiektumėte geriausių rezultatų, ją padėkite aukštai prie lango ir nuskaitykite dar kartą."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Tęsti"</item>
- <item msgid="727245208787621142">"Ne dabar"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Iš naujo vykdyti kanalų sąranką?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Tai atlikus bus pašalinti kanalai, kuriuos aptiko TV imtuvas, ir bus vėl ieškoma naujų kanalų.\n\nNuskaičius nerasta jokių kanalų. Įsitikinkite, kad jūsų TV prijungtas prie TV signalo šaltinio.\n\nJei naudojate belaidę anteną, koreguokite jos vietą arba kryptį. Kad pasiektumėte geriausių rezultatų, ją padėkite aukštai prie lango."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Taip bus pašalinti rasti kanalai iš USB imtuvo ir nauji kanalai nuskaityti dar kartą.\n\nĮsitikinkite, kad USB imtuvas prijungtas prie maitinimo ir TV signalo šaltinio.\n\nJei naudojate belaidę anteną, koreguokite jos vietą arba kryptį, kad būtų rodoma kuo daugiau kanalų. Kad pasiektumėte geriausių rezultatų, ją padėkite aukštai prie lango."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Atlikus šį veiksmą iš tinklo imtuvo bus pašalinti rasti kanalai ir bus dar kartą nuskaitoma ieškant naujų kanalų.\n\nĮsitikinkite, kad tinklo imtuvas yra įjungtas ir prijungtas prie TV signalo šaltinio.\n\nJei naudojate belaidę anteną, koreguokite jos vietą arba kryptį. Kad pasiektumėte geriausių rezultatų, ją padėkite aukštai prie lango ir nuskaitykite dar kartą."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Tęsti"</item>
- <item msgid="235450158666155406">"Atšaukti"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Pasirinkti ryšio tipą"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Pasirinkite „Antena“, jei prie imtuvo prijungta išorinė antena. Pasirinkite „Laidas“, jei kanalus teikia kabelinės paslaugos teikėjas. Jei nesate tikri, bus nuskaityti abiejų tipų kanalai, bet tai gali užtrukti ilgiau."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antena"</item>
- <item msgid="2670079958754180142">"Laidas"</item>
- <item msgid="36774059871728525">"Nežinau"</item>
- <item msgid="6881204453182153978">"Tik kūrimas"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"TV imtuvo sąranka"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB kanalų imtuvo sąranka"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Tinklo kanalų imtuvo sąranka"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Tai gali užtrukti kelias minutes"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Derintuvas laikinai nepasiekiamas arba jau yra naudojamas įrašant."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="one">Rastas %1$d kanalas</item>
- <item quantity="few">Rasti %1$d kanalai</item>
- <item quantity="many">Rasta %1$d kanalo</item>
- <item quantity="other">Rasta %1$d kanalų</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"SUSTABDYTI KANALŲ NUSKAITYMĄ"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="one">Rastas %1$d kanalas</item>
- <item quantity="few">Rasti %1$d kanalai</item>
- <item quantity="many">Rasta %1$d kanalo</item>
- <item quantity="other">Rasta %1$d kanalų</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="one">Puiku! Nuskaitant kanalus rastas %1$d kanalas. Jei tai neatrodo tinkamas rezultatas, pabandykite pakoreguoti antenos padėtį ir nuskaityti dar kartą.</item>
- <item quantity="few">Puiku! Nuskaitant kanalus rasti %1$d kanalai. Jei tai neatrodo tinkamas rezultatas, pabandykite pakoreguoti antenos padėtį ir nuskaityti dar kartą.</item>
- <item quantity="many">Puiku! Nuskaitant kanalus rasta %1$d kanalo. Jei tai neatrodo tinkamas rezultatas, pabandykite pakoreguoti antenos padėtį ir nuskaityti dar kartą.</item>
- <item quantity="other">Puiku! Nuskaitant kanalus rasta %1$d kanalų. Jei tai neatrodo tinkamas rezultatas, pabandykite pakoreguoti antenos padėtį ir nuskaityti dar kartą.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Atlikta"</item>
- <item msgid="2480490326672924828">"Nuskaityti dar kartą"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Nerasta kanalų"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Nuskaičius nerasta jokių kanalų. Įsitikinkite, kad jūsų TV prijungtas prie TV signalo šaltinio.\n\nJei naudojate belaidę anteną, koreguokite jos vietą arba kryptį. Kad pasiektumėte geriausių rezultatų, ją padėkite aukštai prie lango ir nuskaitykite dar kartą."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Nuskaitant nerasta kanalų. Patvirtinkite, ar USB imtuvas prijungtas prie TV signalo šaltinio.\n\nJei naudojate belaidę anteną, koreguokite jos vietą arba kryptį. Kad pasiektumėte geriausių rezultatų, ją padėkite aukštai prie lango ir nuskaitykite dar kartą."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Nuskaičius nerasta jokių kanalų. Įsitikinkite, kad tinklo imtuvas yra įjungtas ir prijungtas prie TV signalo šaltinio.\n\nJei naudojate belaidę anteną, koreguokite jos vietą arba kryptį. Kad pasiektumėte geriausių rezultatų, ją padėkite aukštai prie lango ir nuskaitykite dar kartą."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Nuskaityti dar kartą"</item>
- <item msgid="2092797862490235174">"Atlikta"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Nuskaitykite TV kanalus"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"TV imtuvo sąranka"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB TV imtuvo sąranka"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Tinklo TV imtuvo sąranka"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB TV imtuvas atjungtas."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Tinklo imtuvas atjungtas."</string>
-</resources>
diff --git a/usbtuner-res/values-lv/strings.xml b/usbtuner-res/values-lv/strings.xml
deleted file mode 100644
index df910fab..00000000
--- a/usbtuner-res/values-lv/strings.xml
+++ /dev/null
@@ -1,93 +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="bt_app_name" msgid="5515382901862469770">"TV kanālu meklētājs"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB TV kanālu meklētājs"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Interneta TV kanālu meklētājs (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Lūdzu, uzgaidiet, līdz tiks pabeigta apstrāde!"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Nesen tika atjaunināta kanālu meklētāja programmatūra. Lūdzu, atkārtoti meklējiet kanālus."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Lai ieslēgtu audio, sistēmas skaņas iestatījumos iespējojiet ieskaujošo skaņu."</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Nevar atskaņot audio. Lūdzu, izmantojiet citu televizoru."</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Kanālu meklētāja iestatīšana"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"TV kanālu meklētāja iestatīšana"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB kanālu meklētāja iestatīšana"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Interneta kanālu meklētāja iestatīšana"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Pārbaudiet, vai televizors ir pievienots TV signāla avotam.\n\nJa izmantojat bezvadu antenu, iespējams, būs jāmaina tās novietojums vai virziens, lai uztvertu lielāko daļu kanālu. Lai iegūtu labākos rezultātus, novietojiet antenu augstu pie loga."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Pārbaudiet, vai USB kanālu meklētājs ir pievienots strāvas avotam un TV signāla avotam.\n\nJa izmantojat bezvadu antenu, mainiet tās novietojumu un virzienu. Lai iegūtu labākos rezultātus, novietojiet antenu augstu pie loga un atkārtojiet kanālu meklēšanu."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Pārbaudiet, vai interneta kanālu meklētājs ir ieslēgts un pievienots TV signāla avotam.\n\nJa izmantojat bezvadu antenu, iespējams, jāmaina tās novietojums vai virziens, lai uztvertu lielāko daļu kanālu. Lai iegūtu labākos rezultātus, novietojiet antenu augstu pie loga."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Turpināt"</item>
- <item msgid="727245208787621142">"Vēlāk"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Vai atkārtot kanālu iestatīšanu?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Šādi no TV kanālu meklētāja tiks noņemti atrastie kanāli un tiks vēlreiz meklēti jauni kanāli.\n\nPārbaudiet, vai televizors ir pievienots TV signāla avotam.\n\nJa izmantojat bezvadu antenu, iespējams, būs jāmaina tās novietojums vai virziens, lai uztvertu lielāko daļu kanālu. Lai iegūtu labākos rezultātus, novietojiet antenu augstu pie loga."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Tādējādi tiks noņemti kanāli, kas tika atrasti ar USB kanālu meklētāju, un tiks atkārtota kanālu meklēšana.\n\nPārbaudiet, vai USB kanālu meklētājs ir pievienots strāvas avotam un TV signāla avotam.\n\nJa izmantojat bezvadu antenu, mainiet tās novietojumu un virzienu, lai uztvertu lielāko daļu kanālu. Lai iegūtu labākos rezultātus, novietojiet antenu augstu pie loga."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Šādi no interneta kanālu meklētāja tiks noņemti atrastie kanāli un tiks vēlreiz meklēti jauni kanāli.\n\nPārbaudiet, vai interneta kanālu meklētājs ir ieslēgts un pievienots TV signāla avotam.\n\nJa izmantojat bezvadu antenu, iespējams, jāmaina tās novietojums vai virziens, lai uztvertu lielāko daļu kanālu. Lai iegūtu labākos rezultātus, novietojiet antenu augstu pie loga."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Turpināt"</item>
- <item msgid="235450158666155406">"Atcelt"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Atlasiet savienojuma veidu"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Ja kanālu meklētājam ir ārējā antena, izvēlieties opciju “Antena”. Ja izmantojat kabeļtelevīziju, izvēlieties opciju “Kabelis”. Ja neesat pārliecināts, kanālu meklēšana tiks veikta, izmantojot abas opcijas, taču būs nepieciešams vairāk laika."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antena"</item>
- <item msgid="2670079958754180142">"Kabelis"</item>
- <item msgid="36774059871728525">"Neesmu pārliecināts"</item>
- <item msgid="6881204453182153978">"Tikai izstrāde"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"TV kanālu meklētāja iestatīšana"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB kanālu meklētāja iestatīšana"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Interneta kanālu meklētāja iestatīšana"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Tas var ilgt vairākas minūtes."</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Kanālu meklētājs īslaicīgi nav pieejams, vai arī tas jau tiek izmantots ierakstīšanai."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="zero">Atrasti %1$d kanāli</item>
- <item quantity="one">Atrasts %1$d kanāls</item>
- <item quantity="other">Atrasti %1$d kanāli</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"APTURĒT KANĀLU MEKLĒŠANU"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="zero">Atrasti %1$d kanāli</item>
- <item quantity="one">Atrasts %1$d kanāls</item>
- <item quantity="other">Atrasti %1$d kanāli</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="zero">Lieliski! Tika atrasti %1$d kanāli. Ja jums šķiet, ka kaut kas nav pareizi, noregulējiet antenu un meklējiet kanālus vēlreiz.</item>
- <item quantity="one">Lieliski! Tika atrasts %1$d kanāls. Ja jums šķiet, ka kaut kas nav pareizi, noregulējiet antenu un meklējiet kanālus vēlreiz.</item>
- <item quantity="other">Lieliski! Tika atrasti %1$d kanāli. Ja jums šķiet, ka kaut kas nav pareizi, noregulējiet antenu un meklējiet kanālus vēlreiz.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Gatavs"</item>
- <item msgid="2480490326672924828">"Meklēt vēlreiz"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Netika atrasts neviens kanāls"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Veicot meklēšanu, netika atrasts neviens kanāls. Pārbaudiet, vai televizors pievienots TV signāla avotam.\n\nJa izmantojat bezvadu antenu, mainiet tās novietojumu vai virzienu. Lai iegūtu labākos rezultātus, novietojiet antenu augstu pie loga, bet pēc tam vēlreiz veiciet meklēšanu."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Netika atrasts neviens kanāls. Pārbaudiet, vai USB kanālu meklētājs ir pievienots strāvas avotam un TV signāla avotam.\n\nJa izmantojat bezvadu antenu, mainiet tās novietojumu un virzienu. Lai iegūtu labākos rezultātus, novietojiet antenu augstu pie loga un atkārtojiet kanālu meklēšanu."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Netika atrasts neviens kanāls. Pārbaudiet, vai interneta kanālu meklētājs ir ieslēgts un pievienots TV signāla avotam.\n\nJa izmantojat bezvadu antenu, mainiet tās novietojumu un virzienu. Lai iegūtu labākos rezultātus, novietojiet antenu augstu pie loga un atkārtojiet kanālu meklēšanu."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Meklēt vēlreiz"</item>
- <item msgid="2092797862490235174">"Gatavs"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"TV kanālu meklēšana"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"TV kanālu meklētāja iestatīšana"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB TV kanālu meklētāja iestatīšana"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Interneta TV kanālu meklētāja iestatīšana"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB TV kanālu meklētājs ir atvienots."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Interneta kanālu meklētājs ir atvienots."</string>
-</resources>
diff --git a/usbtuner-res/values-mk-rMK/strings.xml b/usbtuner-res/values-mk-rMK/strings.xml
deleted file mode 100644
index 2db086e1..00000000
--- a/usbtuner-res/values-mk-rMK/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"ТВ приемник"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"ТВ приемник со USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Мрежен ТВ приемник (БЕТА)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Почекајте да заврши обработувањето"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Софтверот на приемникот неодамна е ажуриран. Скенирајте ги каналите повторно."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Овозможете опкружувачки звук во поставките за звуци на системот за да овозможите аудио"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Не може да се пушти аудио. Обидете се со друг ТВ"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Поставување приемник на канали"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Поставување ТВ приемник"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Поставување на USB-приемникот за канали"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Поставување мрежен приемник"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Потврдете дека телевизорот е приклучен и поврзан со изворот на ТВ сигналот.\n\nАко користите безжична антена, можеби ќе треба да ја приспособите поставеноста или насоката за да примате најмногу канали. За најдобри резултати, поставете ја високо и во близина на прозорец."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Потврдете дека USB-приемникот е приклучен и поврзан со изворот на ТВ сигналот.\n\nАко користите безжична антена, можеби ќе треба да ја приспособите поставеноста или насоката за да примате најмногу канали. За најдобри резултати, поставете ја високо и во близина на прозорец."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Потврдете дека мрежниот приемник е вклучен и поврзан со изворот на ТВ сигналот.\n\nАко користите безжична антена, може ќе треба да ја приспособите поставеноста или насоката за да примате најмногу канали. За најдобри резултати, поставете ја високо и во близина на прозорец."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Продолжи"</item>
- <item msgid="727245208787621142">"Не сега"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Да се изврши поставувањето на каналите повторно?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Ова ќе ги отстрани каналите од ТВ приемникот и ќе скенира за нови канали повторно.\n\nПотврдете дека телевизорот е поврзан со изворот на ТВ сигналот.\n\nАко користите безжична антена, приспособете ја поставеноста или насоката. За најдобри резултати, поставете ја високо и во близина на прозорец."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Ова ќе ги отстрани каналите од USB-приемникот и ќе скенира за нови канали повторно.\n\nПотврдете дека USB-приемникот е приклучен и поврзан со изворот на ТВ сигналот.\n\nАко користите безжична антена, можеби ќе треба да ја приспособите поставеноста или насоката за да примате најмногу канали. За најдобри резултати, поставете ја високо и во близина на прозорец."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Ова ќе ги отстрани каналите од мрежниот приемник и ќе скенира за нови канали повторно.\n\nПотврдете дека мрежниот приемник е вклучен и поврзан со изворот на ТВ сигналот.\n\nАко користите безжична антена, може ќе треба да ја приспособите поставеноста или насоката. За најдобри резултати, поставете ја високо и во близина на прозорец."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Продолжи"</item>
- <item msgid="235450158666155406">"Откажи"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Изберете го типот врска"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Изберете „Антена“ ако има надворешна антена поврзана со приемникот. Изберете „Кабел“ ако каналите ви ги обезбедува кабелски оператор. Ако не сте сигурни, и двата типа ќе се скенираат, но тоа може да трае подолго."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Антена"</item>
- <item msgid="2670079958754180142">"Кабел"</item>
- <item msgid="36774059871728525">"Не сум сигурен"</item>
- <item msgid="6881204453182153978">"Само за програмери"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Поставување ТВ приемник"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Поставување на USB-приемникот за канали"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Поставување мрежен приемник на канали"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Ова може да трае неколку минути"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Приемникот е привремено недостапен или веќе се користи за снимање."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="one">Пронајден е %1$d канал</item>
- <item quantity="other">Пронајдени се %1$d канали</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"ЗАПРИ ГО СКЕНИРАЊЕТО КАНАЛИ"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="one">Пронајден е %1$d канал</item>
- <item quantity="other">Пронајдени се %1$d канали</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="one">Убаво! Пронајдени се %1$d канали во текот на скенирањето канали. Ако се чини дека тоа не е во ред, обидете се со приспособување на позицијата на антената и скенирајте повторно.</item>
- <item quantity="other">Убаво! Пронајдени се %1$d канали во текот на скенирањето канали. Ако се чини дека тоа не е во ред, обидете се со приспособување на позицијата на антената и скенирајте повторно.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Готово"</item>
- <item msgid="2480490326672924828">"Скенирај пак"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Не се пронајдени канали"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Скенирањето не пронајде ниеден канал. Потврдете дека телевизорот е поврзан со изворот на ТВ сигналот.\n\nАко користите безжична антена, приспособете ја поставеноста или насоката. За најдобри резултати, поставете ја високо и во близина на прозорец и скенирајте повторно."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Скенирањето не пронајде ниеден канал. Потврдете дека USB-приемникот е приклучен и поврзан со изворот на ТВ сигналот.\n\nАко користите безжична антена, приспособете ја поставеноста или насоката. За најдобри резултати, поставете ја високо и во близина на прозорец и скенирајте повторно."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Скенирањето не пронајде ниеден канал. Потврдете дека мрежниот приемник е вклучен и поврзан со изворот на ТВ сигналот.\n\nАко користите безжична антена, приспособете ја поставеноста или насоката. За најдобри резултати, поставете ја високо и во близина на прозорец, па скенирајте повторно."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Скенирај пак"</item>
- <item msgid="2092797862490235174">"Готово"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Скенирајте ТВ-канали"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Поставување ТВ приемник"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Поставување ТВ приемник со USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Поставување мрежен ТВ приемник"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB-приемникот за TV е исклучен."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Мрежниот приемник е исклучен."</string>
-</resources>
diff --git a/usbtuner-res/values-ml-rIN/strings.xml b/usbtuner-res/values-ml-rIN/strings.xml
deleted file mode 100644
index 2befa74c..00000000
--- a/usbtuner-res/values-ml-rIN/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"ടിവി ട്യൂണർ"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB ടിവി ട്യൂണർ"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"നെറ്റ്‌വർക്ക് ടിവി ട്യൂണർ (ബീറ്റ)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"പ്രോസസ്സുചെയ്യൽ പൂർത്തിയാകുന്നത് വരെ കാത്തിരിക്കുക"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"ട്യൂണർ സോഫ്‌റ്റ്‌വെയർ അടുത്തിടെ അപ്‌ഡേറ്റുചെയ്‌തു. ചാനലുകൾ വീണ്ടും സ്‌കാൻ ചെയ്യുക."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"ഓഡിയോ പ്രവർത്തനക്ഷമമാക്കുന്നതിന് സിസ്റ്റം ശബ്ദ ക്രമീകരണത്തിൽ സറൗണ്ട് ശബ്‌ദം പ്രവർത്തനക്ഷമമാക്കുക"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"ഓഡിയോ പ്ലേ ചെയ്യാൻ കഴിയുന്നില്ല. മറ്റൊരു ടിവിയിൽ ശ്രമിച്ചുനോക്കൂ"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"ചാനൽ ട്യൂണർ സജ്ജമാക്കല്‍‌"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"ടിവി ട്യൂണർ സജ്ജമാക്കല്‍‌"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB ചാനൽ ട്യൂണർ സജ്ജമാക്കല്‍‌"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"നെറ്റ്‌വർക്ക് ട്യൂണർ സജ്ജമാക്കല്‍‌"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"ഒരു ടിവി സിഗ്നൽ ഉറവിടത്തിലേക്ക് നിങ്ങളുടെ ടിവി കണക്റ്റുചെയ്തിട്ടുണ്ടെന്ന് ഉറപ്പാക്കുക.\n\nഉയരത്തിൽ വയ്ക്കേണ്ട തരത്തിലുള്ള ആന്റിനയാണ് നിങ്ങൾ ഉപയോഗിക്കുന്നതെങ്കിൽ, പരമാവധി ചാനലുകൾ ലഭിക്കുന്നതിന്, അതിന്റെ ഇരിപ്പോ ദിശയോ ക്രമീകരിക്കേണ്ടി വരാം. മികച്ച ഫലം ലഭിക്കാൻ, കുറച്ചുകൂടി ഉയരത്തിലും ജാലകത്തിന് അരികിലായും അത് സ്ഥാപിക്കുക."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"USB ട്യൂണർ പ്ലഗിൻ ചെയ്തിട്ടുണ്ടെന്നും ഒരു ടിവി സിഗ്നൽ ഉറവിടത്തിലേക്ക് കണക്റ്റുചെയ്തിട്ടുണ്ടെന്നും ഉറപ്പാക്കുക.\n\nഉയരത്തിൽ വയ്ക്കേണ്ട തരത്തിലുള്ള ആന്റിനയാണ് നിങ്ങൾ ഉപയോഗിക്കുന്നതെങ്കിൽ, പരമാവധി ചാനലുകൾ ലഭിക്കുന്നതിന്, അതിന്റെ ഇരിപ്പോ ദിശയോ ക്രമീകരിക്കേണ്ടി വരാം. മികച്ച ഫലം ലഭിക്കാൻ, കുറച്ചുകൂടി ഉയരത്തിലും ജാലകത്തിന് അരികിലായും അത് സ്ഥാപിക്കുക."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"നെറ്റ്‌വർക്ക് ട്യൂണർ ഓണാക്കിയിട്ടുണ്ടെന്നും ഒരു ടിവി സിഗ്നൽ ഉറവിടത്തിലേക്ക് കണക്റ്റുചെയ്തിട്ടുണ്ടെന്നും ഉറപ്പാക്കുക.\n\nഉയരത്തിൽ വയ്ക്കേണ്ട തരത്തിലുള്ള ആന്റിനയാണ് നിങ്ങൾ ഉപയോഗിക്കുന്നതെങ്കിൽ, പരമാവധി ചാനലുകൾ ലഭിക്കുന്നതിന്, അതിന്റെ ഇരിപ്പോ ദിശയോ ക്രമീകരിക്കേണ്ടി വരാം. മികച്ച ഫലം ലഭിക്കാൻ, കുറച്ചുകൂടി ഉയരത്തിലും ജാലകത്തിന് അരികിലായും അത് സ്ഥാപിക്കുക."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"തുടരുക"</item>
- <item msgid="727245208787621142">"ഇപ്പോൾ വേണ്ട"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"ചാനൽ സജ്ജമാക്കൽ വീണ്ടും റൺ ചെയ്യണോ?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"ടിവി ട്യൂണറിൽ നിന്ന് കണ്ടെത്തിയ ചാനലുകളെ ഇത് നീക്കംചെയ്യും, പുതിയ ചാനലുകൾക്കായി വീണ്ടും സ്കാൻ ചെയ്യും.\n\nഒരു ടിവി സിഗ്നൽ ഉറവിടത്തിലേക്ക് നിങ്ങളുടെ ടിവി കണക്റ്റുചെയ്തിട്ടുണ്ടെന്ന് ഉറപ്പാക്കുക.\n\nഉയരത്തിൽ വയ്ക്കേണ്ട തരത്തിലുള്ള ആന്റിനയാണ് നിങ്ങൾ ഉപയോഗിക്കുന്നതെങ്കിൽ, പരമാവധി ചാനലുകൾ ലഭിക്കുന്നതിന്, അതിന്റെ ഇരിപ്പോ ദിശയോ ക്രമീകരിക്കേണ്ടി വരാം. മികച്ച ഫലം ലഭിക്കാൻ, കുറച്ചുകൂടി ഉയരത്തിലും ജാലകത്തിന് അരികിലായും അത് സ്ഥാപിക്കുക."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"USB ട്യൂണറിൽ നിന്ന് കണ്ടെത്തിയ ചാനലുകളെ ഇത് നീക്കംചെയ്യും, പുതിയ ചാനലുകൾക്കായി വീണ്ടും സ്കാൻ ചെയ്യും.\n\nസ്കാൻ ചെയ്തപ്പോൾ ചാനലുകളൊന്നും കണ്ടെത്തിയില്ല. USB ട്യൂണർ പ്ലഗിൻ ചെയ്തിട്ടുണ്ടെന്നും ഒരു ടിവി സിഗ്നൽ ഉറവിടത്തിലേക്ക് കണക്റ്റുചെയ്തിട്ടുണ്ടെന്നും ഉറപ്പാക്കുക.\n\nഉയരത്തിൽ വയ്ക്കേണ്ട തരത്തിലുള്ള ആന്റിനയാണ് നിങ്ങൾ ഉപയോഗിക്കുന്നതെങ്കിൽ, പരമാവധി ചാനലുകൾ ലഭിക്കുന്നതിന്, അതിന്റെ ഇരിപ്പോ ദിശയോ ക്രമീകരിക്കേണ്ടി വരാം. മികച്ച ഫലം ലഭിക്കാൻ, കുറച്ചുകൂടി ഉയരത്തിലും ജാലകത്തിന് അരികിലായും അത് സ്ഥാപിക്കുക."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"നെറ്റ്‌വർക്ക് ട്യൂണറിൽ നിന്ന് കണ്ടെത്തിയ ചാനലുകളെ ഇത് നീക്കംചെയ്യും, പുതിയ ചാനലുകൾക്കായി വീണ്ടും സ്കാൻ ചെയ്യും.\n\nനെറ്റ്‌വർക്ക് ട്യൂണർ ഓണാക്കിയിട്ടുണ്ടെന്നും ഒരു ടിവി സിഗ്നൽ ഉറവിടത്തിലേക്ക് കണക്റ്റുചെയ്തിട്ടുണ്ടെന്നും ഉറപ്പാക്കുക.\n\nഉയരത്തിൽ വയ്ക്കേണ്ട തരത്തിലുള്ള ആന്റിനയാണ് നിങ്ങൾ ഉപയോഗിക്കുന്നതെങ്കിൽ, പരമാവധി ചാനലുകൾ ലഭിക്കുന്നതിന്, അതിന്റെ ഇരിപ്പോ ദിശയോ ക്രമീകരിക്കേണ്ടി വരാം മികച്ച ഫലം ലഭിക്കാൻ, കുറച്ചുകൂടി ഉയരത്തിലും ജാലകത്തിന് അരികിലായും അത് സ്ഥാപിക്കുക."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"തുടരുക"</item>
- <item msgid="235450158666155406">"റദ്ദാക്കൂ"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"കണക്ഷൻ തരം തിരഞ്ഞെടുക്കുക"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"ട്യൂണറിലേക്ക് കണക്റ്റുചെയ്തിട്ടുള്ള ഒരു ബാഹ്യ ആന്റിന ഉണ്ടെങ്കിൽ, ആന്റിന തിരഞ്ഞെടുക്കുക. കേബിൾ സേവന ദാതാവിൽ നിന്നാണ് ചാനലുകൾ ലഭിക്കുന്നതെങ്കിൽ കേബിൾ തിരഞ്ഞെടുക്കുക. ഏതാണ് ഉള്ളതെന്ന് നിങ്ങൾക്ക് അറിയില്ലെങ്കിൽ, രണ്ട് രീതികളും സ്കാൻ ചെയ്യപ്പെടും, എന്നാലിതിന് സമയമെടുക്കും."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"ആന്റിന"</item>
- <item msgid="2670079958754180142">"കേബിള്‍"</item>
- <item msgid="36774059871728525">"ഉറപ്പില്ല"</item>
- <item msgid="6881204453182153978">"ഡെവലപ്പ്‌മെന്റ് മാത്രം"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"ടിവി ട്യൂണർ സജ്ജമാക്കല്‍‌"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB ചാനൽ ട്യൂണർ സജ്ജമാക്കല്‍‌"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"നെറ്റ്‌വർക്ക് ചാനൽ ട്യൂണർ സജ്ജമാക്കല്‍‌"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"ഇതിന് കുറച്ച് സമയം എടുത്തേക്കാം"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"ട്യൂണർ നിലവിൽ ലഭ്യമല്ല അല്ലെങ്കിൽ ഇതിനകം തന്നെ റെക്കോർഡിംഗ് ഉപയോഗിച്ചുകൊണ്ടിരിക്കുന്നു."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d ചാനലുകൾ കണ്ടെത്തി</item>
- <item quantity="one">%1$d ചാനൽ കണ്ടെത്തി</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"ചാനൽ സ്കാൻ ചെയ്യുന്നത് നിർത്തുക"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d ചാനലുകൾ കണ്ടെത്തി</item>
- <item quantity="one">%1$d ചാനൽ കണ്ടെത്തി</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">കൊള്ളാം! ചാനൽ സ്കാൻ വേളയിൽ %1$d ചാനലുകൾ കണ്ടെത്തി. എന്തോ കുഴപ്പമുണ്ടെന്ന് തോന്നുന്നുവെങ്കിൽ, ആന്റിനയുടെ ദിശ ക്രമീകരിച്ചുകൊണ്ട് വീണ്ടും സ്കാൻ ചെയ്യുക.</item>
- <item quantity="one">കൊള്ളാം! ചാനൽ സ്കാൻ വേളയിൽ %1$d ചാനൽ കണ്ടെത്തി. എന്തോ കുഴപ്പമുണ്ടെന്ന് തോന്നുന്നുവെങ്കിൽ, ആന്റിനയുടെ ദിശ ക്രമീകരിച്ചുകൊണ്ട് വീണ്ടും സ്കാൻ ചെയ്യുക.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"പൂർത്തിയായി"</item>
- <item msgid="2480490326672924828">"വീണ്ടും സ്കാൻ ചെയ്യുക"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"ചാനലുകളൊന്നും കണ്ടെത്തിയില്ല"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"സ്കാൻ ചെയ്തപ്പോൾ ചാനലുകളൊന്നും കണ്ടെത്തിയില്ല. ഒരു ടിവി സിഗ്നൽ ഉറവിടത്തിലേക്ക് നിങ്ങളുടെ ടിവി കണക്റ്റുചെയ്തിട്ടുണ്ടെന്ന് ഉറപ്പാക്കുക.\n\nഉയരത്തിൽ വയ്ക്കേണ്ട തരത്തിലുള്ള ആന്റിനയാണ് നിങ്ങൾ ഉപയോഗിക്കുന്നതെങ്കിൽ അതിന്റെ ഇരിപ്പോ ദിശയോ ക്രമീകരിക്കുക. മികച്ച ഫലം ലഭിക്കാൻ, കുറച്ചുകൂടി ഉയരത്തിലും ജാലകത്തിന് അരികിലായും അത് സ്ഥാപിച്ച് വീണ്ടും സ്കാൻ ചെയ്യുക."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"സ്കാൻ ചെയ്തപ്പോൾ ചാനലുകളൊന്നും കണ്ടെത്തിയില്ല. USB ട്യൂണർ പ്ലഗിൻ ചെയ്തിട്ടുണ്ടെന്നും ഒരു ടിവി സിഗ്നൽ ഉറവിടത്തിലേക്ക് കണക്റ്റുചെയ്തിട്ടുണ്ടെന്നും ഉറപ്പാക്കുക.\n\nഉയരത്തിൽ വയ്ക്കേണ്ട തരത്തിലുള്ള ആന്റിനയാണ് നിങ്ങൾ ഉപയോഗിക്കുന്നതെങ്കിൽ അതിന്റെ ഇരിപ്പോ ദിശയോ ക്രമീകരിക്കുക. മികച്ച ഫലം ലഭിക്കാൻ, കുറച്ചുകൂടി ഉയരത്തിലും ജാലകത്തിന് അരികിലായും അത് സ്ഥാപിച്ച് വീണ്ടും സ്കാൻ ചെയ്യുക."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"സ്കാൻ ചെയ്തപ്പോൾ ചാനലുകളൊന്നും കണ്ടെത്തിയില്ല. നെറ്റ്‌വർക്ക് ട്യൂണർ ഓണാക്കിയിട്ടുണ്ടെന്നും ഒരു ടിവി സിഗ്നൽ ഉറവിടത്തിലേക്ക് കണക്റ്റുചെയ്തിട്ടുണ്ടെന്നും ഉറപ്പാക്കുക.\n\nഉയരത്തിൽ വയ്ക്കേണ്ട തരത്തിലുള്ള ആന്റിനയാണ് നിങ്ങൾ ഉപയോഗിക്കുന്നതെങ്കിൽ അതിന്റെ ഇരിപ്പോ ദിശയോ ക്രമീകരിക്കുക. മികച്ച ഫലം ലഭിക്കാൻ, കുറച്ചുകൂടി ഉയരത്തിലും ജാലകത്തിന് അരികിലായും അത് സ്ഥാപിച്ച് വീണ്ടും സ്കാൻ ചെയ്യുക."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"വീണ്ടും സ്കാൻ ചെയ്യുക"</item>
- <item msgid="2092797862490235174">"പൂർത്തിയായി"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"ടിവി ചാനലുകൾക്കായി സ്കാൻ ചെയ്യുക"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"ടിവി ട്യൂണർ സജ്ജമാക്കല്‍‌"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB ടിവി ട്യൂണർ സജ്ജമാക്കല്‍‌"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"നെറ്റ്‌വർക്ക് ടിവി ട്യൂണർ സജ്ജമാക്കല്‍‌"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB ടിവി ട്യൂണർ വിച്ഛേദിച്ചു."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"നെറ്റ്‌വർക്ക് ട്യൂണർ വിച്ഛേദിച്ചു."</string>
-</resources>
diff --git a/usbtuner-res/values-mn-rMN/strings.xml b/usbtuner-res/values-mn-rMN/strings.xml
deleted file mode 100644
index 996341b4..00000000
--- a/usbtuner-res/values-mn-rMN/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"ТВ тохируулагч"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB ТВ тохируулагч"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Сүлжээний ТВ Тохируулагч (БЭТА)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Боловсруулж дуусах хүртэл хүлээнэ үү"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Тохируулагчийн програмыг саяхан шинэчилсэн байна. Дахин суваг хайна уу."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Аудиог идэвхжүүлэхийн тулд орчны дууны системийг дууны тохиргоонд идэвхжүүлнэ үү"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Аудиог тоглуулах боломжгүй байна. Өөр ТВ дээр оролдож үзнэ үү"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Суваг тохируулагчийн тохиргоо"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"ТВ тохируулагчийн тохиргоо"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB суваг тохируулагчийн тохиргоо"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Сүлжээ тохируулагчийн тохиргоо"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"ТВ-ээ ТВ дохионы үүсвэртэй холбосон эсэхээ батална уу.\n\nХэрэв агаарын антен хэрэглэж байгаа бол олон суваг авахын тулд антены байршил эсвэл чиглэлийг өөрчилнө үү. Антеныг өндөрт, цонхны дэргэд байрлуулах нь илүү үр дүнтэй."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"USB тохируулагчийг залгасан ба ТВ дохионы үүсвэртэй холбосон эсэхээ шалгана уу.\n\nХэрэв агаарын антен хэрэглэж байгаа бол олон суваг авахын тулд антены байршлыг өөрчилнө үү. Антеныг өндөрт, цонхны дэргэд байрлуулах нь илүү үр дүнтэй байдаг."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Сүлжээ тохируулагчийг асааж, ТВ дохионы үүсвэртэй холбосон эсэхээ баталгаажуулна уу.\n\nХэрэв агаарын антен хэрэглэж байгаа бол олон суваг авахын тулд антены байршлыг өөрчилнө үү. Антеныг өндөрт, цонхны дэргэд байрлуулна уу."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Үргэлжлүүлэх"</item>
- <item msgid="727245208787621142">"Одоо биш"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Сувгийн тохируулгыг дахин ажиллуулах уу?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Энэ нь ТВ тохируулагчаас олсон сувгийг устгаад, шинэ суваг хайх болно.\n\nТВ тохируулагчаа ТВ дохионы үүсвэртэй холбосон эсэхээ шалгана уу.\n\nХэрэв агаарын антен хэрэглэж байгаа бол олон суваг авахын тулд антены байршил эсвэл чиглэлийг өөрчилнө үү. Антеныг өндөрт, цонхны дэргэд байрлуулах нь илүү үр дүнтэй."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Энэ нь USB тохируулагчаас олдсон сувгийг устгаад, шинэ суваг хайх болно.\n\nUSB тохируулагчийг залгасан, ТВ дохионы үүсвэртэй холбосон эсэхээ шалгана уу.\n\nХэрэв агаарын антен хэрэглэж байгаа бол олон суваг авахын тулд антены байршлыг өөрчилнө үү. Антеныг өндөрт, цонхны дэргэд байрлуулах нь илүү үр дүнтэй."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Энэ нь сүлжээ тохируулагчаас олдсон сувгийг устгаж, шинэ суваг хайх болно.\n\nСүлжээ тохируулагчийг асааж, ТВ дохио үүсвэртэй холбосон эсэхээ шалгана уу.\n\nХэрэв антен хэрэглэж байгаа бол антены байршлыг өөрчилнө үү. Антеныг өндөрт, цонхны дэргэд байрлуулаад, дахин хайна уу."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Үргэлжлүүлэх"</item>
- <item msgid="235450158666155406">"Цуцлах"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Холболтын төрөл сонгоно уу"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Тохируулагчтай нэмэлт антен холбосон бол Антенаа сонгоно уу. Хэрэв сувгаа кабелийн үйлчилгээ эрхлэгчээс авдаг бол Кабелиа сонгоно уу. Та итгэлгүй байвал энэ хоёр төрлийг хоёуланг нь хайх боловч хэсэг хугацаа зарцуулах болно."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Антен"</item>
- <item msgid="2670079958754180142">"Кабель"</item>
- <item msgid="36774059871728525">"Итгэлгүй байна"</item>
- <item msgid="6881204453182153978">"Зөвхөн хөгжүүлэлтийн хэрэгцээнд"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"ТВ тохируулагчийн тохиргоо"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB суваг тохируулагчийн тохиргоо"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Сүлжээний суваг тохируулагчийн тохиргоо"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Хэдэн минут шаардлагатай"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Суваг солигч одоогоор боломжгүй, эсвэл үүнийг өөр бичлэгт ашиглаж байна."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d суваг оллоо</item>
- <item quantity="one">%1$d суваг оллоо</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"СУВАГ ХАЙХЫГ ЗОГСООХ"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d суваг оллоо</item>
- <item quantity="one">%1$d суваг оллоо</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Сайн байна! Суваг хайх явцад %1$d сувгийг олсон байна. Хэрэв илүү олон суваг хайх бол антенаа хөдөлгөөд, дахин хайна уу.</item>
- <item quantity="one">Сайн байна! Суваг хайх явцад %1$d суваг олдсон. Хэрэв илүү олон суваг хайх бол антенаа хөдөлгөөд, дахин хайна уу.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Дууссан"</item>
- <item msgid="2480490326672924828">"Дахин хайх"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Суваг олсонгүй"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Хайлтын явцад суваг олсонгүй. ТВ-ээ ТВ дохионы үүсвэрт холбосон эсэхээ шалгана уу.\n\nХэрэв агаарын антен хэрэглэж байгаа бол антены байршлыг эсвэл чиглэлийг өөрчилнө үү. Антеныг өндөрт, цонхны дэргэд байрлуулаад, дахин хайх нь илүү үр дүнтэй."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Хайлтын явцад суваг олдсонгүй. USB тохируулагчийг залгасан бөгөөд ТВ дохионы үүсвэртэй холбосон эсэхээ шалгана уу.\n\nХэрэв агаарын антен хэрэглэж байгаа бол антены байршил эсвэл чиглэлийг өөрчилнө үү. Антеныг өндөрт, цонхны дэргэд байрлуулахаад, дахин хайх нь илүү үр дүнтэй."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Хайлтын явцад суваг олдсонгүй. Сүлжээ тохируулагчийг асааж, ТВ дохионы үүсвэртэй холбосон эсэхээ шалгана уу.\n\nХэрэв агаарын антен хэрэглэж байгаа бол антены байршил, эсвэл чиглэлийг өөрчилнө үү. Антеныг өндөрт, цонхны дэргэд байрлуулаад, дахин хайна уу."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Дахин хайх"</item>
- <item msgid="2092797862490235174">"Дууссан"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"TВ-н суваг хайх"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"ТВ тохируулагчийн тохиргоо"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB ТВ тохируулагчийн тохиргоо"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Сүлжээний ТВ тохируулагчийн тохиргоо"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB ТВ тохируулагч салсан байна."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Сүлжээ тааруулагч салсан байна."</string>
-</resources>
diff --git a/usbtuner-res/values-mr-rIN/strings.xml b/usbtuner-res/values-mr-rIN/strings.xml
deleted file mode 100644
index 3b8c9011..00000000
--- a/usbtuner-res/values-mr-rIN/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"टीव्ही ट्यूनर"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB टीव्ही ट्यूनर"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"नेटवर्क टीव्ही ट्यूनर (बीटा)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"कृपया प्रक्रिया पूर्ण होण्‍याची प्रतीक्षा करा"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"ट्यूनर सॉफ्टवेअर अलीकडे अद्यतनित केले आहे. कृपया चॅनेल पुन्हा स्कॅन करा."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"ऑडिओ सक्षम करण्यासाठी सिस्टीम ध्वनी सेटिंग्ज मध्ये सराउंड ध्वनी सक्षम करा"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"ऑडिओ प्ले करू शकत नाही. कृपया दुसरा टीव्ही वापरून पहा"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"चॅनेल ट्यूनर सेटअप"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"टीव्ही ट्यूनर सेटअप"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB चॅनेल ट्यूनर सेटअप"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"नेटवर्क ट्यूनर सेटअप"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"टीव्ही सिग्नल स्रोताशी आपला टीव्ही कनेक्ट केला असल्याची खात्री करा. \n\n बिनतारी अँटेना वापरत असल्‍यास, आपल्‍याला कदाचित सर्वाधिक चॅनेल मिळविण्‍यासाठी त्याचे स्थान किंवा दिशा समायोजित करावी लागेल. उत्कृष्‍ट परिणामांसाठी, त्‍यास उंचावर आणि खिडकी जवळ ठेवा."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"USB ट्यूनर प्लगिन केले आणि TV सिग्नल स्रोताशी कनेक्‍ट केले आहे हे सत्यापित करा.\n\nबिनतारी अँटेना वापरत असल्‍यास, आपल्‍याला कदाचित सर्वाधिक चॅनेल मिळविण्‍यासाठी त्याचे स्थान किंवा दिशा समायोजित करावी लागेल. उत्कृष्‍ट परिणामांसाठी, त्‍यास उंचावर आणि खिडकी जवळ ठेवा."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"नेटवर्क ट्यूनर प्लगिन केले आणि TV सिग्नल स्रोताशी कनेक्‍ट केले आहे हे सत्यापित करा.\n\nबिनतारी अँटेना वापरत असल्‍यास, आपल्‍याला कदाचित सर्वाधिक चॅनेल मिळविण्‍यासाठी त्याचे स्थान किंवा दिशा समायोजित करावी लागेल. उत्कृष्‍ट परिणामांसाठी, त्‍यास उंचावर आणि खिडकी जवळ ठेवा."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"सुरू ठेवा"</item>
- <item msgid="727245208787621142">"सध्या नाही"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"चॅनेल सेटअप वर परत यायचे?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"हे टीव्ही ट्यूनर वरून शोधलेले चॅनेल काढेल आणि नवीन चॅनेलसाठी पुन्हा स्कॅन करेल.\n\n टीव्ही सिग्नल स्रोताशी आपला टीव्ही कनेक्ट केला असल्याचे सत्यापित करा.\n\nबिनतारी अँटेना वापरत असल्‍यास, आपल्‍याला कदाचित सर्वाधिक चॅनेल मिळविण्‍यासाठी त्याचे स्थान किंवा दिशा समायोजित करावी लागेल. उत्कृष्‍ट परिणामांसाठी, त्‍यास उंचावर आणि खिडकी जवळ ठेवा."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"हे USB ट्यूनर वरून शोधलेले चॅनेल काढेल आणि नवीन चॅनेलसाठी पुन्हा स्कॅन करेल.\n\n USB ट्यूनर प्लगिन केले आणि TV सिग्नल स्त्रोताशी कनेक्‍ट केले आहे हे सत्यापित करा.\n\nबिनतारी अँटेना वापरत असल्‍यास, आपल्‍याला कदाचित सर्वाधिक चॅनेल मिळविण्‍यासाठी त्याचे स्थान किंवा दिशा समायोजित करावी लागेल. उत्कृष्‍ट परिणामांसाठी, त्‍यास उंचावर आणि खिडकी जवळ ठेवा."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"हे नेटवर्क ट्यूनर वरून शोधलेले चॅनेल काढेल आणि नवीन चॅनेलसाठी पुन्हा स्कॅन करेल.\n\n नेटवर्क ट्यूनर प्लगिन केले आणि टीव्ही सिग्नल स्रोताशी कनेक्‍ट केले आहे हे सत्यापित करा.\n\nबिनतारी अँटेना वापरत असल्‍यास, आपल्याला कदाचित सर्वाधिक चॅनेल मिळविण्‍यासाठी त्याचे स्थान किंवा दिशा समायोजित करावी लागेल. उत्कृष्‍ट परिणामांसाठी, त्‍यास उंचावर आणि खिडकी जवळ ठेवा."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"सुरू ठेवा"</item>
- <item msgid="235450158666155406">"रद्द करा"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"कनेक्‍शन प्रकार निवडा"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"ट्यूनरशी बाह्य अँटेना कनेक्‍ट केला असल्‍यास अँटेना निवडा. आपले चॅनेल केबल सेवा प्रदात्याकडून येत असल्‍यास केबल निवडा. आपल्‍याला खात्री नसल्‍यास, दोन्ही प्रकार स्कॅन केले जातील परंतु यास वेळ लागेल."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"अँटेना"</item>
- <item msgid="2670079958754180142">"केबल"</item>
- <item msgid="36774059871728525">"खात्री नाही"</item>
- <item msgid="6881204453182153978">"केवळ विकास"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"टीव्ही ट्यूनर सेटअप"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB चॅनेल ट्यूनर सेटअप"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"नेटवर्क चॅनेल ट्यूनर सेटअप"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"यास काही मिनिटे लागू शकतात"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"ट्यूनर तात्पुरते उपलब्ध नाही किंवा रेकॉर्डिंगद्वारे आधीपासून वापरले गेले आहे."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="one">%1$d चॅनेल सापडले</item>
- <item quantity="other">%1$d चॅनेल सापडले</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"चॅनेल स्कॅन करणे थांबवा"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="one">%1$d चॅनेल सापडले</item>
- <item quantity="other">%1$d चॅनेल सापडले</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="one">छान! चॅनेल स्कॅन करताना %1$d चॅनेल सापडले. हे योग्य वाटत नसल्यास, अँटेना स्थिती समायोजित करून पहा आणि पुन्हा स्कॅन करा.</item>
- <item quantity="other">छान! चॅनेल स्कॅन करताना %1$d चॅनेल सापडले. हे योग्य वाटत नसल्यास, अँटेना स्थिती समायोजित करून पहा आणि पुन्हा स्कॅन करा.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"पूर्ण झाले"</item>
- <item msgid="2480490326672924828">"पुन्हा स्कॅन करा"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"कोणतेही चॅनेल आढळले नाही"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"स्कॅन करताना कोणतेही चॅनेल आढळले नाही. टीव्ही सिग्नल स्रोताशी आपला टीव्ही कनेक्ट केला असल्याचे सत्यापित करा.\n\nबिनतारी अँटेना वापरत असल्‍यास, त्याचे स्थान किंवा दिशा समायोजित करा. उत्कृष्‍ट परिणामांसाठी, त्‍यास उंचावर आणि खिडकी जवळ ठेवा आणि पुन्हा स्कॅन करा."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"स्कॅन करताना कोणतेही चॅनेल आढळले नाही. USB ट्यूनर प्लगिन केले आणि TV सिग्नल स्रोताशी कनेक्‍ट केले आहे हे सत्यापित करा.\n\nबिनतारी अँटेना वापरत असल्‍यास, त्याचे स्थान किंवा दिशा समायोजित करा. उत्कृष्‍ट परिणामांसाठी, त्‍यास उंचावर आणि खिडकी जवळ ठेवा आणि पुन्हा स्कॅन करा."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"स्कॅन करताना कोणतेही चॅनेल आढळले नाहीत. नेटवर्क ट्यूनर प्लगिन केले आणि टीव्ही सिग्नल स्रोताशी कनेक्‍ट केले आहे हे सत्यापित करा.\n\nबिनतारी अँटेना वापरत असल्‍यास, त्याचे स्थान किंवा दिशा समायोजित करा. उत्कृष्‍ट परिणामांसाठी, त्‍यास उंचावर आणि खिडकी जवळ ठेवा आणि पुन्हा स्कॅन करा."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"पुन्हा स्कॅन करा"</item>
- <item msgid="2092797862490235174">"पूर्ण झाले"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"टीव्ही चॅनेलसाठी स्कॅन करा"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"टीव्ही ट्यूनर सेटअप"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB टीव्ही ट्यूनर सेटअप"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"नेटवर्क टीव्ही ट्यूनर सेटअप"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB टीव्ही ट्यूनर डिस्कनेक्ट केला."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"नेटवर्क ट्यूनर डिस्कनेक्ट केले."</string>
-</resources>
diff --git a/usbtuner-res/values-ms-rMY/strings.xml b/usbtuner-res/values-ms-rMY/strings.xml
deleted file mode 100644
index c85ce142..00000000
--- a/usbtuner-res/values-ms-rMY/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"Penala TV"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"Penala TV USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Penala TV Rangkaian (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Sila tunggu sehingga proses selesai"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Perisian penala telah dikemas kini baru-baru ini. Sila imbas semula saluran."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Dayakan bunyi keliling dalam tetapan bunyi sistem untuk mendayakan audio"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Tidak dapat memainkan audio. Sila cuba TV lain"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Persediaan penala saluran"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Persediaan Penala TV"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Persediaan penala saluran USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Persediaan penala rangkaian"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Sahkan bahawa TV anda disambungkan pada sumber isyarat TV.\n\nJika menggunakan antena siaran, anda mungkin perlu melaraskan peletakan atau arahnya untuk menerima saluran terbanyak. Untuk mendapatkan hasil yang terbaik, letakkan antena itu di tempat yang tinggi dan berdekatan dengan tingkap."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Sahkan bahawa penala USB dipalamkan dan disambungkan pada sumber isyarat TV.\n\nJika menggunakan antena siaran, anda mungkin perlu melaraskan peletakan atau arahnya untuk menerima saluran terbanyak. Untuk mendapatkan hasil yang terbaik, letakkan antena itu di tempat yang tinggi dan berdekatan dengan tingkap."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Sahkan bahawa penala rangkaian telah dihidupkan dan disambungkan pada sumber isyarat TV\n\nJika menggunakan antena siaran, anda mungkin perlu melaraskan peletakan atau arahnya untuk menerima saluran terbanyak. Untuk mendapatkan hasil yang terbaik, letakkan antena itu di tempat yang tinggi dan berdekatan dengan tingkap."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Teruskan"</item>
- <item msgid="727245208787621142">"Bukan sekarang"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Jalankan semula persediaan saluran?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Tindakan ini akan mengalih keluar saluran yang ditemui daripada penala TV dan mengimbas saluran baharu sekali lagi.\n\nSahkan bahawa penala TV telah disambungkan pada sumber isyarat TV.\n\nJika menggunakan antena siaran, anda mungkin perlu melaraskan peletakan atau arahnya untuk menerima saluran terbanyak. Untuk mendapatkan hasil yang terbaik, letakkan antena itu di tempat yang tinggi dan berdekatan dengan tingkap."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Tindakan ini akan mengalih keluar saluran yang ditemui daripada penala USB dan mengimbas saluran baharu sekali lagi.\n\nSahkan bahawa penala USB telah dipalamkan dan disambungkan pada sumber isyarat TV.\n\nJika menggunakan antena siaran, anda mungkin perlu melaraskan peletakan atau arahnya untuk menerima saluran terbanyak. Untuk mendapatkan hasil yang terbaik, letakkan antena itu di tempat yang tinggi dan berdekatan dengan tingkap."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Tindakan ini akan mengalih keluar saluran yang ditemui daripada penala rangkaian dan mengimbas saluran baharu sekali lagi.\n\nSahkan bahawa penala rangkaian telah dihidupkan dan disambungkan pada sumber isyarat TV.\n\nJika menggunakan antena siaran, anda mungkin perlu melaraskan peletakan atau arahnya untuk menerima saluran terbanyak. Untuk mendapatkan hasil yang terbaik, letakkan antena itu di tempat yang tinggi dan berdekatan dengan tingkap."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Teruskan"</item>
- <item msgid="235450158666155406">"Batal"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Pilih jenis sambungan"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Pilih Antena jika terdapat antena luaran yang disambungkan pada penala. Pilih Kabel jika saluran anda adalah daripada penyedia perkhidmatan kabel. Jika anda tidak pasti, kedua-dua jenis sambungan akan diimbas tetapi proses ini mungkin mengambil masa yang lebih lama."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antena"</item>
- <item msgid="2670079958754180142">"Kabel"</item>
- <item msgid="36774059871728525">"Tidak pasti"</item>
- <item msgid="6881204453182153978">"Pembangunan sahaja"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Persediaan penala TV"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Persediaan penala saluran USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Persediaan penala saluran rangkaian"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Proses ini mungkin mengambil masa beberapa minit"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Penala tidak tersedia buat sementara waktu atau sudah pun digunakan oleh rakaman."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d saluran ditemui</item>
- <item quantity="one">%1$d saluran ditemui</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"HENTIKAN PENGIMBASAN SALURAN"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d saluran ditemui</item>
- <item quantity="one">%1$d saluran ditemui</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Bagus! %1$d saluran ditemui semasa pengimbasan saluran. Jika hasil pengimbasan ini salah, cuba laraskan kedudukan antena dan imbas sekali lagi.</item>
- <item quantity="one">Bagus! %1$d saluran ditemui semasa pengimbasan saluran. Jika hasil pengimbasan ini salah, cuba laraskan kedudukan antena dan imbas sekali lagi.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Selesai"</item>
- <item msgid="2480490326672924828">"Imbas lagi"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Tiada Saluran ditemui"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Pengimbasan ini tidak menemui sebarang saluran. Sahkan bahawa TV anda disambungkan pada sumber isyarat TV.\n\nJika menggunakan antena siaran, laraskan peletakan atau arahnya. Untuk mendapatkan hasil yang terbaik, letakkan antena itu di tempat yang tinggi dan berdekatan dengan tingkap, kemudian imbas sekali lagi."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Pengimbasan tidak menemui sebarang saluran. Sahkan bahawa penala USB dipalamkan dan disambungkan pada sumber isyarat TV.\n\nJika menggunakan antena siaran, laraskan peletakan atau arahnya. Untuk mendapatkan hasil yang terbaik, letakkan antena itu di tempat yang tinggi dan berdekatan dengan tingkap, kemudian imbas sekali lagi."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Pengimbasan ini tidak menemui sebarang saluran. Sahkan bahawa penala rangkaian telah dihidupkan dan disambungkan pada sumber isyarat TV.\n\nJika menggunakan antena siaran, laraskan peletakan atau arahnya. Untuk mendapatkan hasil yang terbaik, letakkan antena itu di tempat yang tinggi dan berdekatan dengan tingkap, kemudian imbas sekali lagi."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Imbas lagi"</item>
- <item msgid="2092797862490235174">"Selesai"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Imbas saluran TV"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Persediaan Penala TV"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Persediaan Penala TV USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Persediaan Penala TV Rangkaian"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"Penala TV USB diputuskan sambungan."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Penala rangkaian diputuskan sambungan."</string>
-</resources>
diff --git a/usbtuner-res/values-my-rMM/strings.xml b/usbtuner-res/values-my-rMM/strings.xml
deleted file mode 100644
index a2cf6c80..00000000
--- a/usbtuner-res/values-my-rMM/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"တီဗီချန်နယ်ချိန်ကိရိယာ"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB တီဗီချန်နယ်ချိန်ကိရိယာ"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"ကွန်ရက်တီဗီ လိုင်းချိန်စနစ် (စမ်းသပ်ဆော့ဖ်ဝဲ)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"စီမံဆောင်ရွက်မှု အဆုံးသတ်ရန် ခဏစောင့်ပါ"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"ချန်နယ်ချိန်ဆော့ဖ်ဝဲကို မကြာသေးမီက အပ်ဒိတ်လုပ်ခဲ့သည်။ ချန်နယ်လိုင်းများကို ပြန်ရှာပါ။"</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"အသံဖွင့်ရန် ပတ်ပတ်လည်အသံစနစ်ဆက်တင်များကို ဖွင့်ပါ"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"အသံဖွင့်၍မရပါ။ အခြားတီဗီတွင် စမ်းကြည့်ပါ"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"ချန်နယ်ချိန်ကိရိယာ ထည့်သွင်းတပ်ဆင်မှု"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"တီဗီချန်နယ်ချိန်ကိရိယာ ပြင်ဆင်သတ်မှတ်မှု"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB ချန်နယ်ချိန်ကိရိယာ ပြင်ဆင်သတ်မှတ်မှု"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"တီဗီလိုင်းဖမ်းကိရိယာ စနစ်ထည့်သွင်းမှု"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"သင့်တီဗီသည် တီဗီချန်နယ်ထုတ်လွှင့်ရာ အရင်းအမြစ်တစ်ခုနှင့် ချိတ်ဆက်ထားကြောင်း အတည်ပြုပါ။\n\nအကယ်၍ ကောင်းကင်အန်တန်နာတိုင်ကို အသုံးပြုနေပါက ချန်နယ်အများစုကို ဖမ်းယူနိုင်ရန် ၎င်း၏အနေအထား (သို့) ဦးတည်ဘက်ကို ချိန်ညှိရန် လိုပါသည်။ ရလဒ်များအကောင်းဆုံး ဖြစ်စေရန် ၎င်းကို ပြတင်းပေါက်နားတွင် မြင့်မြင့်ထားပါ။"</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"USB ချန်နယ်ချိန်ကိရိယာကို တပ်ဆင်ထားပြီး တီဗီချန်နယ်ထုတ်လွှင့်ရာ အရင်းအမြစ်တစ်ခုနှင့် ချိတ်ဆက်ထားကြောင်း အတည်ပြုပါ။\n\nအကယ်၍ ကောင်းကင်အန်တန်နာတိုင်ကို အသုံးပြုနေပါက ချန်နယ်အများစုကို ဖမ်းယူနိုင်ရန် ၎င်း၏အနေအထား (သို့) ဦးတည်ဘက်ကို ချိန်ညှိရန် လိုပါသည်။ ရလဒ်များအကောင်းဆုံး ဖြစ်စေရန် ၎င်းကို ပြတင်းပေါက်နားတွင် မြင့်မြင့်ထားပါ။"</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"တီဗီလိုင်းဖမ်းကိရိယာကို ပါဝါဖွင့်ထားခြင်း ရှိ မရှိနှင့် တီဗီစလောင်းသို့ ချိတ်ဆက်ထားခြင်းရှိမရှိ စစ်ဆေးပါ။\n\nအင်တင်နာကို အသုံးပြုလျှင် ၎င်း၏အနေအထား သို့မဟုတ် ဦးတည်ချက်တို့ကို ချိန်ညှိရန် လိုအပ်ပါသည်။ အကောင်းဆုံးရလဒ်အတွက် ပြတင်းပေါက်အနီး အမြင့်ပိုင်းတွင် ထားပါ။"</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"ရှေ့ဆက်ရန်"</item>
- <item msgid="727245208787621142">"မလုပ်သေးပါ"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"ချန်နယ်စနစ်ထည့်သွင်းမှု ပြန်စမလား။"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"ဤလုပ်ဆောင်ချက်သည် တီဗီချန်နယ်ချိန်ကိရိယာက တွေ့ရှိထားသော ချန်နယ်လိုင်းများကို ဖယ်ရှားလိုက်ပြီး ချန်နယ်အသစ်များကို ထပ်မံရှာဖွေလိမ့်မည်။\n\nသင့်တီဗီသည် တီဗီချန်နယ်ထုတ်လွှင့်ရာ အရင်းအမြစ်တစ်ခုနှင့် ချိတ်ဆက်ထားကြောင်း အတည်ပြုပါ။\n\nအကယ်၍ ကောင်းကင်အန်တန်နာတိုင်ကို အသုံးပြုနေပါက ချန်နယ်အများစုကို ဖမ်းယူနိုင်ရန် ၎င်း၏အနေအထား (သို့) ဦးတည်ဘက်ကို ချိန်ညှိရန် လိုပါသည်။ ရလဒ်များအကောင်းဆုံး ဖြစ်စေရန် ၎င်းကို ပြတင်းပေါက်နားတွင် မြင့်မြင့်ထားပါ။"</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"ဤလုပ်ဆောင်ချက်သည် USB ချန်နယ်ချိန်ကိရိယာက တွေ့ရှိထားသော ချန်နယ်လိုင်းများကို ဖယ်ရှားလိုက်ပြီး ချန်နယ်အသစ်များကို ထပ်မံရှာဖွေလိမ့်မည်။\n\nUSB ချန်နယ်ချိန်ကိရိယာကို တပ်ဆင်ထားပြီး တီဗီချန်နယ်ထုတ်လွှင့်ရာ အရင်းအမြစ်တစ်ခုနှင့် ချိတ်ဆက်ထားကြောင်း အတည်ပြုပါ။\n\nအကယ်၍ ကောင်းကင်အန်တန်နာတိုင်ကို အသုံးပြုနေပါက ချန်နယ်အများစုကို ဖမ်းယူနိုင်ရန် ၎င်း၏အနေအထား (သို့) ဦးတည်ဘက်ကို ချိန်ညှိရန် လိုပါသည်။ ရလဒ်များအကောင်းဆုံး ဖြစ်စေရန် ၎င်းကို ပြတင်းပေါက်နားတွင် မြင့်မြင့်ထားပါ။"</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"၎င်းသည် တီဗီလိုင်းဖမ်းကိရိယာက ရှာဖွေတွေ့ရှိခဲ့သည့် ချန်နယ်များကို ဖယ်ရှားလိုက်မည်ဖြစ်ပြီး ချန်နယ်အသစ်များကို ထပ်မံရှာဖွေသွားပါမည်။\n\nတီဗီလိုင်းဖမ်းကိရိယာကို ပါဝါဖွင့်ထားခြင်း ရှိ မရှိနှင့် တီဗီစလောင်းသို့ ချိတ်ဆက်ထားခြင်းရှိမရှိ စစ်ဆေးပါ။\n\nအင်တင်နာကို အသုံးပြုလျှင် ၎င်း၏အနေအထား သို့မဟုတ် ဦးတည်ချက်တို့ကို ချိန်ညှိရန် လိုအပ်ပါသည်။ အကောင်းဆုံးရလဒ်အတွက် ပြတင်းပေါက်အနီး အမြင့်ပိုင်းတွင် ထားပါ။"</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"ရှေ့ဆက်ရန်"</item>
- <item msgid="235450158666155406">"မလုပ်တော့"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"ချိတ်ဆက်မှု အမျိုးအစားကို ရွေးပါ"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"ချန်နယ်ချိန်ကိရိယာနှင့် ချိတ်ဆက်ထားသော ပြင်ပအန်တန်နာရှိပါက အန်တန်နာကို ရွေးပါ။ ချန်နယ်လိုင်းများအသုံးပြုရန် ကေဘယ်စနစ်သုံး ဝန်ဆောင်မှုပေးသူနှင့် ချိတ်ဆက်ထားပါက ကေဘယ်ကြိုးကို ရွေးပါ။ မသေချာဖြစ်နေလျှင် နှစ်မျိုးလုံးဖြင့် ရှာဖွေနိုင်သော်လည်း အချိန်ပိုကြာနိုင်ပါသည်။"</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"အန်တန်နာ"</item>
- <item msgid="2670079958754180142">"ကေဘယ်ကြိုး"</item>
- <item msgid="36774059871728525">"မသေချာပါ။"</item>
- <item msgid="6881204453182153978">"စမ်းသပ်မှုအတွက်သာ"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"တီဗီချန်နယ်ချိန်ကိရိယာ ပြင်ဆင်သတ်မှတ်မှု"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB ချန်နယ်ချိန်ကိရိယာ ပြင်ဆင်သတ်မှတ်မှု"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"တီဗီလိုင်းဖမ်းကိရိယာ စနစ်ထည့်သွင်းမှု"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"မိနစ်အနည်းငယ် ကြာနိုင်ပါသည်"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"လိုင်းချိန်စက်သည် ယာယီမရနိုင်သေးပါ သို့မဟုတ် ဖမ်းယူခြင်းအတွက် အသုံးပြုနေပြီး ဖြစ်ပါသည်။"</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">ချန်နယ် %1$d လိုင်းတွေ့သည်</item>
- <item quantity="one">ချန်နယ် %1$d လိုင်းတွေ့သည်</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"ချန်နယ်ရှာဖွေမှုကို ရပ်ရန်"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">ချန်နယ် %1$d လိုင်းတွေ့သည်</item>
- <item quantity="one">ချန်နယ် %1$d လိုင်းတွေ့သည်</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">သိပ်ကောင်း။ ချန်နယ်ရှာဖွေရာတွင် ချန်နယ် %1$d လိုင်းတွေ့သည်။ မှားယွင်းနေသည်ဟုထင်လျှင် အန်တန်နာအနေအထားကို ချိန်ညှိပြီး ထပ်ရှာကြည့်ပါ။</item>
- <item quantity="one">သိပ်ကောင်း။ ချန်နယ်ရှာဖွေရာတွင် ချန်နယ် %1$d လိုင်းတွေ့သည်။ မှားယွင်းနေသည်ဟုထင်လျှင် အန်တန်နာအနေအထားကို ချိန်ညှိပြီး ထပ်ရှာကြည့်ပါ။</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"ပြီးသွားပြီ"</item>
- <item msgid="2480490326672924828">"ထပ်ရှာရန်"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"ချန်နယ်တစ်လိုင်းမျှ မတွေ့ပါ"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"ချန်နယ်များရှာဖွေရာတွင် တစ်လိုင်းမျှ ရှာမတွေ့ပါ။ သင့်တီဗီသည် တီဗီချန်နယ်ထုတ်လွှင့်ရာ အရင်းအမြစ်တစ်ခုနှင့် ချိတ်ဆက်ထားကြောင်း အတည်ပြုပါ။\n\n အကယ်၍ ကောင်းကင်အန်တန်နာတိုင်ကို အသုံးပြုနေပါက ၎င်း၏ အနေအထား (သို့) ဦးတည်ဘက်ကို ချိန်ညှိပါ။ ရလဒ်များအကောင်းဆုံးဖြစ်စေရန် ၎င်းကို ပြတင်းပေါက်နားတွင် မြင့်မြင့်ထားပြီး ထပ်ရှာကြည့်ပါ။"</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"ချန်နယ်များရှာဖွေရာတွင် တစ်လိုင်းမျှ ရှာမတွေ့ပါ။ USB ချန်နယ်ချိန်ကိရိယာကို တပ်ဆင်ထား၍ တီဗီချန်နယ်ထုတ်လွှင့်ရာ အရင်းအမြစ်တစ်ခုနှင့် ချိတ်ဆက်ထားကြောင်း အတည်ပြုပါ။\n\n အကယ်၍ ကောင်းကင်အန်တန်နာတိုင်ကို အသုံးပြုနေပါက ၎င်း၏ အနေအထား (သို့) ဦးတည်ဘက်ကို ချိန်ညှိပါ။ ရလဒ်များအကောင်းဆုံးဖြစ်စေရန် ၎င်းကို ပြတင်းပေါက်နားတွင် မြင့်မြင့်ထားပြီး ထပ်ရှာကြည့်ပါ။"</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"မည်သည့် ချန်နယ်မျှ ရှာမတွေ့ပါ။ တီဗီလိုင်းဖမ်းကိရိယာကို ပါဝါဖွင့်ထားခြင်းရှိ မရှိနှင့် တီဗီစလောင်းသို့ ချိတ်ဆက်ထားခြင်းရှိမရှိ စစ်ဆေးပါ။\n\nအင်တင်နာကို အသုံးပြုလျှင် ၎င်း၏အနေအထား သို့မဟုတ် ဦးတည်ချက်တို့ကို ချိန်ညှိရန် လိုအပ်ပါသည်။ အကောင်းဆုံးရလဒ်အတွက် ပြတင်းပေါက်အနီး အမြင့်ပိုင်းတွင် ထားပါ။"</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"ထပ်ရှာရန်"</item>
- <item msgid="2092797862490235174">"ပြီးသွားပြီ"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"TV ချန်နယ်ရှာမည်"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"တီဗီဖမ်းစက် အပြင်အဆင်"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB တီဗီဖမ်းစက် အပြင်အဆင်"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"အင်တာနက် တီဗီဖမ်းစက် အပြင်အဆင်"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB တီဗီလိုင်းချိန်ကိရိယာကို ဖြုတ်လိုက်ပါပြီ။"</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"ကွန်ရက်လိုင်းချိန်ကိရိယာကို ဖြုတ်လိုက်ပါပြီ။"</string>
-</resources>
diff --git a/usbtuner-res/values-nb/strings.xml b/usbtuner-res/values-nb/strings.xml
deleted file mode 100644
index ed45555b..00000000
--- a/usbtuner-res/values-nb/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"TV-tuner"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB-tuneren for TV"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Nettverkstuner for TV (betaversjon)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Vent til behandlingen er fullført"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Programvaren for tuneren er nylig blitt oppdatert. Du må skanne kanalene på nytt."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Slå på surroundlyd i innstillingene for systemlyd for å slå på lyd"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Kan ikke spille av lyd. Prøv en annen TV"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Konfigurasjon av kanaler via tuneren"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Konfigurasjon av TV-tuneren"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Konfigurasjon av kanaler via USB-tuneren"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Konfigurasjon av nettverkstuner"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Bekreft at TV-en din er koblet til en TV-signalkilde.\n\nHvis du bruker en trådløs antenne, kan det hende du må justere posisjonen eller retningen for å motta flest mulig kanaler. For å få de beste resultatene bør du plassere antennen høyt og i nærheten av et vindu."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Bekreft at USB-tuneren er plugget i og koblet til en TV-signalkilde.\n\nHvis du bruker en trådløs antenne, kan det hende du må justere posisjonen eller retningen for å motta flest mulig kanaler. For å få de beste resultatene bør du plassere antennen høyt og i nærheten av et vindu."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Bekreft at nettverkstuneren er slått på og koblet til en TV-signalkilde.\n\nHvis du bruker en trådløs antenne, må du kanskje justere posisjonen eller retningen for å motta flere kanaler. For å få de beste resultatene bør du plassere antennen høyt og i nærheten av et vindu."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Fortsett"</item>
- <item msgid="727245208787621142">"Ikke nå"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Vil du kjøre kanalkonfigureringen på nytt?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Dette fjerner kanalene som ble funnet med TV-tuneren, og skanner etter nye kanaler igjen.\n\nBekreft at TV-en din er koblet til en TV-signalkilde.\n\nHvis du bruker en trådløs antenne, kan det hende du må justere posisjonen eller retningen for å motta flest mulig kanaler. For å få de beste resultatene bør du plassere antennen høyt og i nærheten av et vindu."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Dette fjerner kanaler som er funnet via USB-tuneren, og skanner på nytt etter nye kanaler.\n\nBekreft at USB-tuneren er plugget i og koblet til en TV-signalkilde.\n\nHvis du bruker en trådløs antenne, kan det hende du må justere posisjonen eller retningen for å motta flest mulig kanaler. For å få de beste resultatene bør du plassere antennen høyt og i nærheten av et vindu."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Dette fjerner kanalene som ble funnet av nettverkstuneren, og skanner på nytt etter nye kanaler.\n\nBekreft at nettverkstuneren er slått på og koblet til en TV-signalkilde.\n\nHvis du bruker en trådløs antenne, må du kanskje justere posisjonen eller retningen for å motta flere kanaler. For å få de beste resultatene bør du plassere antennen høyt og i nærheten av et vindu."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Fortsett"</item>
- <item msgid="235450158666155406">"Avbryt"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Velg tilkoblingstypen"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Velg «Antenne» hvis tuneren er koblet til en ekstern antenne. Velg «Kabel» hvis kanalene kommer fra en kabeltjenesteleverandør. Hvis du ikke er sikker, blir begge typene skannet – men det kan ta lengre tid."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antenne"</item>
- <item msgid="2670079958754180142">"Kabel"</item>
- <item msgid="36774059871728525">"Vet ikke"</item>
- <item msgid="6881204453182153978">"Bare for utvikling"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Konfigurasjon av TV-tuneren"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Konfigurasjon av kanaler via USB-tuneren"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Konfigurasjon av tuner for nettverkskanaler"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Dette kan ta flere minutter"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Tuneren er midlertidig utilgjengelig eller brukes allerede av opptak."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d kanaler er funnet</item>
- <item quantity="one">%1$d kanal er funnet</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"STOPP KANALSKANNINGEN"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d kanaler er funnet</item>
- <item quantity="one">%1$d kanal er funnet</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Flott! %1$d kanaler er funnet under kanalskanningen. Hvis dette virker feil, kan du prøve å justere antenneposisjonen og skanne på nytt.</item>
- <item quantity="one">Flott! %1$d kanal er funnet under kanalskanningen. Hvis dette virker feil, kan du prøve å justere antenneposisjonen og skanne på nytt.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Ferdig"</item>
- <item msgid="2480490326672924828">"Skann på nytt"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Fant ingen kanaler"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Ingen kanaler ble funnet under skanningen. Bekreft at TV-en din er koblet til en TV-signalkilde.\n\nHvis du bruker en trådløs antenne, kan du justere posisjonen eller retningen. For å få de beste resultatene bør du plassere antennen høyt og i nærheten av et vindu. Deretter skanner du på nytt."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Fant ingen kanaler under skanningen. Bekreft at USB-tuneren er plugget i og koblet til en TV-signalkilde.\n\nHvis du bruker en trådløs antenne, kan du justere posisjonen eller retningen. For å få de beste resultatene bør du plassere antennen høyt og i nærheten av et vindu. Deretter skanner du på nytt."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Skanningen fant ingen kanaler. Bekreft at nettverkstuneren er slått på og koblet til en TV-signalkilde.\n\nHvis du bruker en trådløs antenne, må du justere posisjonen eller retningen. For å få de beste resultatene bør du plassere antennen høyt og i nærheten av et vindu. Deretter skanner du på nytt."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Skann på nytt"</item>
- <item msgid="2092797862490235174">"Ferdig"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Skann etter TV-kanaler"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Konfigurasjon av TV-tuneren"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Konfigurasjon av USB-tuner for TV"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Konfigurasjon av nettverkstuner for TV"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB-tuneren for TV er frakoblet."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Nettverkstuneren er frakoblet."</string>
-</resources>
diff --git a/usbtuner-res/values-ne-rNP/strings.xml b/usbtuner-res/values-ne-rNP/strings.xml
deleted file mode 100644
index 5f1e3330..00000000
--- a/usbtuner-res/values-ne-rNP/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"TV ट्युनर"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB TV ट्युनर"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"नेटवर्कको TV ट्युनर (बिटा)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"कृपया प्रक्रिया सम्पन्न हुने प्रतीक्षा गर्नुहोस्"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"ट्युनरको सफ्टवेयरलाई हालसालै अद्यावधिक गरिएको छ। कृपया च्यानलहरू पुन:स्क्यान गर्नुहोस्।"</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"अडियोलाई सक्षम पार्न प्रणालीको ध्वनि सम्बन्धी सेटिङहरूमा गई सराउन्ड साउन्डलाई सक्षम पार्नुहोस्"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"अडियो बजाउन सकिँदैन। कृपया अर्को TV को प्रयोग गरी हेर्नुहोस्"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"च्यानल ट्युनरको सेटअप"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"TV ट्युनरको सेटअप"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB च्यानल ट्युनरको सेटअप"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"नेटवर्क ट्युनरको सेटअप"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"तपाईँको TV कुनै TV सिग्नलको स्रोतमा जडान गरिएको छ भनी पुष्टि गर्नुहोस्।\n\nयदि कुनै ओभर-दि-एयर एन्टेनाको प्रयोग भइरहेको छ भने धेरै च्यानलहरू प्राप्त गर्न तपाईँले त्यसको स्थान वा दिशा समायोजन गर्नुपर्ने हुन सक्छ। उत्कृष्ट परिणामहरूका लागि त्यसलाई उच्च स्थानमा र कुनै झ्यालको नजिक राख्नुहोस्।"</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"USB ट्युनर प्लगइन रहेको र कुनै TV सिग्नलको स्रोतमा जडान गरिएको छ भनी पुष्टि गर्नुहोस्।\n\nयदि कुनै ओभर-दि-एयर एन्टेनाको प्रयोग भइरहेको छ भने धेरै च्यानलहरू प्राप्त गर्न तपाईँले त्यसको स्थान वा दिशा समायोजन गर्नुपर्ने हुन सक्छ। उत्कृष्ट परिणामहरूका लागि त्यसलाई उच्च स्थानमा र कुनै झ्यालको नजिक राख्नुहोस्।"</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"नेटवर्क ट्युनर सक्रिय रहेको र कुनै TV सिग्नलको स्रोतमा जडान गरिएको छ भनी पुष्टि गर्नुहोस्।\n\nयदि कुनै ओभर-दि-एयर एन्टेनाको प्रयोग भइरहेको छ भने धेरै च्यानलहरू प्राप्त गर्न तपाईंले त्यसको स्थान वा दिशा समायोजन गर्नुपर्ने हुन सक्छ। उत्कृष्ट परिणामहरूका लागि त्यसलाई उच्च स्थानमा र कुनै झ्यालको नजिक राख्नुहोस्।"</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"जारी राख्नुहोस्"</item>
- <item msgid="727245208787621142">"अहिले होइन"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"च्यानलको सेटअप पुनःसञ्चालन गर्ने हो?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"यसले USB ट्युनरबाट भेट्टिएका च्यानलहरूलाई हटाउनेछ र नयाँ च्यानलहरू भेट्टाउन फेरि स्क्यान गर्नेछ।\n\nतपाईँको TV कुनै TV सिग्नलको स्रोतमा जडान गरिएको छ भनी पुष्टि गर्नुहोस्।\n\nयदि कुनै ओभर-दि-एयर एन्टेनाको प्रयोग भइरहेको छ भने धेरै च्यानलहरू प्राप्त गर्न तपाईँले त्यसको स्थान वा दिशा समायोजन गर्नुपर्ने हुन सक्छ। उत्कृष्ट परिणामहरूका लागि त्यसलाई उच्च स्थानमा र कुनै झ्यालको नजिक राख्नुहोस्।"</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"यसले USB ट्युनरबाट भेट्टिएका च्यानलहरूलाई हटाउनेछ र नयाँ च्यानलहरू भेट्टाउन फेरि स्क्यान गर्नेछ।\n\nUSB ट्युनर प्लगइन गरिएको र कुनै TV सिग्नलको स्रोतमा जडान गरिएको छ भनी पुष्टि गर्नुहोस्।\n\nयदि कुनै ओभर-दि-एयर एन्टेनाको प्रयोग भइरहेको छ भने धेरै च्यानलहरू प्राप्त गर्न तपाईँले त्यसको स्थान वा दिशा समायोजन गर्नुपर्ने हुन सक्छ। उत्कृष्ट परिणामहरूका लागि त्यसलाई उच्च स्थानमा र कुनै झ्यालको नजिक राख्नुहोस्।"</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"यसले नेटवर्क ट्युनर मार्फत भेट्टिएका च्यानलहरूलाई हटाउनेछ र नयाँ च्यानलहरू भेट्टाउन फेरि स्क्यान गर्नेछ।\n\nनेटवर्क ट्युनरलाई सक्रिय गरिएको र कुनै TV सिग्नलको स्रोतमा जडान गरिएको छ भनी पुष्टि गर्नुहोस्।\n\nयदि कुनै ओभर-दि-एयर एन्टेनाको प्रयोग भइरहेको छ भने धेरै च्यानलहरू प्राप्त गर्न तपाईंले त्यसको स्थान वा दिशा समायोजन गर्नुपर्ने हुन सक्छ। उत्कृष्ट परिणामहरूका लागि त्यसलाई उच्च स्थानमा र कुनै झ्यालको नजिक राख्नुहोस्।"</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"जारी राख्नुहोस्"</item>
- <item msgid="235450158666155406">"रद्द गर्नुहोस्"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"जडानको प्रकार चयन गर्नुहोस्"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"यदि उक्त ट्युनरमा कुनै बाह्य एन्टेना जडान गरिएको छ भने एन्टेना छनौट गर्नुहोस्। यदि तपाईंका च्यानलहरू केबल सेवा प्रदायक मार्फत आउँछन् भने केबल छनौट गर्नुहोस्। यदि तपाईं निश्चित हुनुहुन्न भने दुवै प्रकारहरू स्क्यान गरिने छन् तर यसमा लामो समय लाग्न सक्छ।"</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"एन्टेना"</item>
- <item msgid="2670079958754180142">"केबल"</item>
- <item msgid="36774059871728525">"निश्चित छैन"</item>
- <item msgid="6881204453182153978">"विकासका लागि मात्र"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"TV ट्युनरको सेटअप"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB च्यानल ट्युनरको सेटअप"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"नेटवर्कको च्यानल ट्युनरको सेटअप"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"यसमा धेरै मिनेट लाग्न सक्छ"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"ट्युनर अस्थायी रूपले अनुपलब्ध छ वा रेकर्डिङद्वारा पहिले नै प्रयोग गरिएको छ।"</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d च्यानलहरू भेट्टिए</item>
- <item quantity="one">%1$d च्यानल भेट्टियो</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"च्यानल स्क्यान गर्न रोक्नुहोस्"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d च्यानलहरू भेट्टिए</item>
- <item quantity="one">%1$d च्यानल भेट्टियो</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">राम्रो खबर छ! च्यानल स्क्यान गर्दा %1$d च्यानलहरू भेट्टिए। यदि यो सही हो जस्तो लाग्दैन भने एन्टेनाको स्थिति समायोजन गरी हेर्नुहोस् र फेरि स्क्यान गर्नुहोस्।</item>
- <item quantity="one">राम्रो खबर छ! च्यानल स्क्यान गर्दा %1$d च्यानल भेट्टियो। यदि यो सही हो जस्तो लाग्दैन भने एन्टेनाको स्थिति समायोजन गरी हेर्नुहोस् र फेरि स्क्यान गर्नुहोस्।</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"सम्पन्न भयो"</item>
- <item msgid="2480490326672924828">"फेरि स्क्यान गर्नुहोस्"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"कुनै च्यानल भेट्टिएन"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"उक्त स्क्यानले कुनै पनि च्यानल भेट्टाएन। तपाईँको TV कुनै TV सिग्नलको स्रोतमा जडान गरिएको छ भनी पुष्टि गर्नुहोस्।\n\nयदि कुनै ओभर-दि-एयर एन्टेनाको प्रयोग भइरहेको छ भने त्यसको स्थान वा दिशा समायोजन गर्नुहोस्। उत्कृष्ट परिणामहरूका लागि त्यसलाई उच्च स्थानमा र कुनै झ्यालको नजिक राखी फेरि स्क्यान गर्नुहोस्।"</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"उक्त स्क्यानले कुनै पनि च्यानल भेट्टाएन। USB ट्युनर प्लगइन गरिएको र कुनै TV सिग्नलको स्रोतमा जडान गरिएको छ भनी पुष्टि गर्नुहोस्।\n\nयदि कुनै ओभर-दि-एयर एन्टेनाको प्रयोग भइरहेको छ भने त्यसको स्थान वा दिशा समायोजन गर्नुहोस्। उत्कृष्ट परिणामहरूका लागि त्यसलाई उच्च स्थानमा र कुनै झ्यालको नजिक राखी फेरि स्क्यान गर्नुहोस्।"</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"उक्त स्क्यानले कुनै पनि च्यानल भेट्टाएन। नेटवर्क ट्युनरलाई सक्रिय गरिएको र कुनै TV सिग्नलको स्रोतमा जडान गरिएको छ भनी पुष्टि गर्नुहोस्।\n\nयदि कुनै ओभर-दि-एयर एन्टेनाको प्रयोग भइरहेको छ भने त्यसको स्थान वा दिशा समायोजन गर्नुहोस्। उत्कृष्ट परिणामहरूका लागि त्यसलाई उच्च स्थानमा र कुनै झ्यालको नजिक राखी फेरि स्क्यान गर्नुहोस्।"</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"फेरि स्क्यान गर्नुहोस्"</item>
- <item msgid="2092797862490235174">"सम्पन्न भयो"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"TV च्यानलहरू भेट्टाउन स्क्यान गर्नुहोस्"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"TV ट्युनरको सेटअप"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB TV ट्युनरको सेटअप"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"नेटवर्कको TV ट्युनरको सेटअप"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB TV ट्युनरलाई विच्छेद गरियो।"</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"नेटवर्क ट्युनरलाई विच्छेद गरियो।"</string>
-</resources>
diff --git a/usbtuner-res/values-nl/strings.xml b/usbtuner-res/values-nl/strings.xml
deleted file mode 100644
index 34b0d927..00000000
--- a/usbtuner-res/values-nl/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"Tv-tuner"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB-tv-tuner"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Netwerk-tv-tuner (BÈTA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Wacht tot het proces is voltooid"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"De software van de tuner is recent geüpdatet. Scan de kanalen opnieuw."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Schakel surrond sound in via de geluidsinstellingen van het systeem om audio in te schakelen"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Kan audio niet afspelen. Probeer een andere tv."</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Configuratie van kanaaltuner"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Tv-tuner instellen"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Kanaalconfiguratie van USB-tuner"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Netwerktuner instellen"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Controleer of je tv is aangesloten op een tv-signaalbron.\n\nAls je een over-the-air-antenne gebruikt, moet je de positie of richting daarvan mogelijk aanpassen om zo veel mogelijk kanalen te ontvangen. Voor de beste resultaten plaats je de antenne op een hoge plek in de buurt van een raam."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Controleer of de USB-tuner is ingeschakeld en aangesloten op een tv-signaalbron.\n\nAls je een over-the-air-antenne gebruikt, moet je de positie of richting daarvan mogelijk aanpassen om zo veel mogelijk kanalen te ontvangen. Voor de beste resultaten plaats je de antenne op een hoge plek in de buurt van een raam."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Controleer of de netwerktuner is ingeschakeld en aangesloten op een tv-signaalbron.\n\nAls je een over-the-air-antenne gebruikt, moet je de positie of richting daarvan mogelijk aanpassen om zo veel mogelijk kanalen te ontvangen. Voor de beste resultaten plaats je de antenne op een hoge plek in de buurt van een raam."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Doorgaan"</item>
- <item msgid="727245208787621142">"Niet nu"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Kanaalconfiguratie opnieuw uitvoeren?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Hiermee worden de gevonden kanalen verwijderd van de tv-tuner en wordt opnieuw gezocht naar nieuwe kanalen.\n\nControleer of je tv is aangesloten op een tv-signaalbron.\n\nAls je een over-the-air-antenne gebruikt, moet je de positie of richting daarvan mogelijk aanpassen om zo veel mogelijk kanalen te ontvangen. Voor de beste resultaten plaats je de antenne op een hoge plek in de buurt van een raam."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Hiermee worden de gevonden kanalen verwijderd van de USB-tuner en wordt opnieuw gescand naar nieuwe kanalen.\n\nControleer of de USB-tuner is ingeschakeld en aangesloten op een tv-signaalbron.\n\nAls je een over-the-air-antenne gebruikt, moet je de positie of richting daarvan mogelijk aanpassen om zo veel mogelijk kanalen te ontvangen. Voor de beste resultaten plaats je de antenne op een hoge plek in de buurt van een raam."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Hiermee worden de gevonden kanalen verwijderd van de netwerktuner en wordt opnieuw gezocht naar nieuwe kanalen.\n\nControleer of de netwerktuner is ingeschakeld en aangesloten op een tv-signaalbron.\n\nAls je een over-the-air-antenne gebruikt, moet je de positie of richting daarvan mogelijk aanpassen om zo veel mogelijk kanalen te ontvangen. Voor de beste resultaten plaats je de antenne op een hoge plek in de buurt van een raam."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Doorgaan"</item>
- <item msgid="235450158666155406">"Annuleren"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Het verbindingstype selecteren"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Kies Antenne als er een externe antenne is aangesloten op de tuner. Kies Kabel als je kanalen afkomstig zijn van een kabelprovider. Als je het niet zeker weet, worden beide typen gescand. Dit kan echter langer duren."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antenne"</item>
- <item msgid="2670079958754180142">"Kabel"</item>
- <item msgid="36774059871728525">"Niet zeker"</item>
- <item msgid="6881204453182153978">"Alleen ontwikkeling"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Tv-tuner instellen"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Kanaalconfiguratie van USB-tuner"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Netwerkkanaaltuner instellen"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Dit kan enkele minuten duren"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"De tuner is tijdelijk niet beschikbaar of wordt al gebruikt voor een opname."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d kanalen gevonden</item>
- <item quantity="one">%1$d kanaal gevonden</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"KANAALSCAN STOPPEN"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d kanalen gevonden</item>
- <item quantity="one">%1$d kanaal gevonden</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Er zijn %1$d kanalen gevonden tijdens de kanaalscan. Als je denkt dat dit niet klopt, pas je de antennepositie aan en voer je de scan opnieuw uit.</item>
- <item quantity="one">Er is %1$d kanaal gevonden tijdens de kanaalscan. Als je denkt dat dit niet klopt, pas je de antennepositie aan en voer je de scan opnieuw uit.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Gereed"</item>
- <item msgid="2480490326672924828">"Opnieuw scannen"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Geen kanalen gevonden"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Er zijn geen kanalen gevonden tijdens de scan. Controleer of je tv is aangesloten op een tv-signaalbron.\n\nAls je een over-the-air-antenne gebruikt, pas je de positie of richting daarvan aan. Voor de beste resultaten plaats je de antenne op een hoge plek in de buurt van een raam en voer je de scan opnieuw uit."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Er zijn geen kanalen gevonden tijdens de scan. Controleer of de USB-tuner is ingeschakeld en aangesloten op een tv-signaalbron.\n\nAls je een over-the-air-antenne gebruikt, pas je de positie of richting daarvan aan. Voor de beste resultaten plaats je de antenne op een hoge plek in de buurt van een raam en voer je de scan opnieuw uit."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"De scan heeft geen kanalen gevonden. Controleer of de netwerktuner is ingeschakeld en aangesloten op een tv-signaalbron.\n\nAls je een over-the-air-antenne gebruikt, moet je de positie of richting daarvan mogelijk aanpassen. Voor de beste resultaten plaats je de antenne op een hoge plek in de buurt van een raam en voer je de scan opnieuw uit."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Opnieuw scannen"</item>
- <item msgid="2092797862490235174">"Gereed"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Zoeken naar tv-zenders"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Tv-tuner instellen"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB-tv-tuner instellen"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Netwerk-tv-tuner instellen"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB-tv-tuner losgekoppeld."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Netwerktuner losgekoppeld."</string>
-</resources>
diff --git a/usbtuner-res/values-pl/strings.xml b/usbtuner-res/values-pl/strings.xml
deleted file mode 100644
index 9daa3621..00000000
--- a/usbtuner-res/values-pl/strings.xml
+++ /dev/null
@@ -1,96 +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="bt_app_name" msgid="5515382901862469770">"Tuner TV"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"Tuner TV USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Sieciowy tuner TV (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Poczekaj na zakończenie przetwarzania"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Oprogramowanie tunera zostało niedawno zaktualizowane. Przeskanuj ponownie kanały."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Aby włączyć dźwięk, włącz dźwięk przestrzenny w ustawieniach systemowych dźwięku"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Nie można odtworzyć dźwięku. Spróbuj użyć innego telewizora"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Konfiguracja kanałów w tunerze"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Konfiguracja tunera TV"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Konfiguracja kanałów w tunerze USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Konfiguracja tunera sieciowego"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Upewnij się, że telewizor jest podłączony do źródła sygnału telewizyjnego.\n\nJeśli używasz anteny telewizyjnej, może być konieczne wyregulowanie jej położenia lub kierunku, by można było odbierać jak najwięcej kanałów. Aby uzyskać najlepszy sygnał, umieść antenę na podwyższeniu, w pobliżu okna."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Upewnij się, że tuner USB jest podłączony do telewizora oraz do źródła sygnału telewizyjnego.\n\nJeśli używasz anteny telewizyjnej, może być konieczne wyregulowanie jej położenia lub kierunku, by można było odbierać jak najwięcej kanałów. Aby uzyskać najlepszy sygnał, umieść antenę na podwyższeniu, w pobliżu okna."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Upewnij się, że tuner sieciowy jest włączony i został podłączony do źródła sygnału telewizyjnego.\n\nJeśli używasz anteny telewizyjnej, może być konieczne wyregulowanie jej położenia lub kierunku, by można było odbierać jak najwięcej kanałów. Aby uzyskać najlepszy sygnał, umieść antenę na podwyższeniu, w pobliżu okna."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Kontynuuj"</item>
- <item msgid="727245208787621142">"Nie teraz"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Skonfigurować ponownie kanały?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Spowoduje to usunięcie kanałów znalezionych przez tuner TV i ponowne wykonanie skanowania.\n\nUpewnij się, że telewizor jest podłączony do źródła sygnału telewizyjnego.\n\nJeśli używasz anteny telewizyjnej, może być konieczne wyregulowanie jej położenia lub kierunku, by można było odbierać jak najwięcej kanałów. Aby uzyskać najlepszy sygnał, umieść antenę na podwyższeniu, w pobliżu okna."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Spowoduje to usunięcie kanałów znalezionych przez tuner USB i ponowne wykonanie skanowania.\n\nUpewnij się, że tuner USB jest podłączony do telewizora oraz do źródła sygnału telewizyjnego.\n\nJeśli używasz anteny telewizyjnej, może być konieczne wyregulowanie jej położenia lub kierunku, by można było odbierać jak najwięcej kanałów. Aby uzyskać najlepszy sygnał, umieść antenę na podwyższeniu, w pobliżu okna."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Spowoduje to usunięcie kanałów znalezionych przez tuner sieciowy i ponowne wykonanie skanowania.\n\nUpewnij się, że tuner sieciowy jest włączony i został podłączony do źródła sygnału telewizyjnego.\n\nJeśli używasz anteny telewizyjnej, może być konieczne wyregulowanie jej położenia lub kierunku, by można było odbierać jak najwięcej kanałów. Aby uzyskać najlepszy sygnał, umieść antenę na podwyższeniu, w pobliżu okna."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Kontynuuj"</item>
- <item msgid="235450158666155406">"Anuluj"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Wybierz typ połączenia"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Jeśli do tunera jest podłączona zewnętrzna antena, wybierz opcję Antena. Jeśli kanały udostępnia dostawca telewizji kablowej, wybierz opcję Telewizja kablowa. Jeśli nie masz pewności, przeskanowane zostaną oba źródła, ale może to dłużej potrwać."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antena"</item>
- <item msgid="2670079958754180142">"Telewizja kablowa"</item>
- <item msgid="36774059871728525">"Nie mam pewności"</item>
- <item msgid="6881204453182153978">"Tylko dla programistów"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Konfiguracja tunera TV"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Konfiguracja kanałów w tunerze USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Konfiguracja kanałów w tunerze sieciowym"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Może to potrwać kilka minut"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Tuner jest czasowo niedostępny lub właśnie nagrywa."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="few">Znaleziono %1$d kanały</item>
- <item quantity="many">Znaleziono %1$d kanałów</item>
- <item quantity="other">Znaleziono %1$d kanału</item>
- <item quantity="one">Znaleziono %1$d kanał</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"ZATRZYMAJ WYSZUKIWANIE KANAŁÓW"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="few">Znaleziono %1$d kanały</item>
- <item quantity="many">Znaleziono %1$d kanałów</item>
- <item quantity="other">Znaleziono %1$d kanału</item>
- <item quantity="one">Znaleziono %1$d kanał</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="few">Super! Podczas skanowania znaleziono %1$d kanały. Jeśli coś jest nie tak, wyreguluj położenie anteny i ponownie wykonaj skanowanie.</item>
- <item quantity="many">Super! Podczas skanowania znaleziono %1$d kanałów. Jeśli coś jest nie tak, wyreguluj położenie anteny i ponownie wykonaj skanowanie.</item>
- <item quantity="other">Super! Podczas skanowania znaleziono %1$d kanału. Jeśli coś jest nie tak, wyreguluj położenie anteny i ponownie wykonaj skanowanie.</item>
- <item quantity="one">Super! Podczas skanowania znaleziono %1$d kanał. Jeśli coś jest nie tak, wyreguluj położenie anteny i ponownie wykonaj skanowanie.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Gotowe"</item>
- <item msgid="2480490326672924828">"Skanuj ponownie"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Nie znaleziono kanałów"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Podczas skanowania nie znaleziono żadnych kanałów. Upewnij się, że telewizor jest podłączony do źródła sygnału telewizyjnego.\n\nJeśli używasz anteny telewizyjnej, wyreguluj jej położenie lub kierunek. Aby uzyskać najlepszy sygnał, umieść antenę na podwyższeniu, w pobliżu okna, a następnie ponownie wykonaj skanowanie."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Podczas skanowania nie znaleziono żadnych kanałów. Upewnij się, że tuner USB jest podłączony do telewizora oraz do źródła sygnału telewizyjnego.\n\nJeśli używasz anteny telewizyjnej, wyreguluj jej położenie lub kierunek. Aby uzyskać najlepszy sygnał, umieść antenę na podwyższeniu, w pobliżu okna, a następnie ponownie wykonaj skanowanie."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Podczas skanowania nie znaleziono żadnych kanałów. Upewnij się, że tuner sieciowy jest włączony i został podłączony do źródła sygnału telewizyjnego.\n\nJeśli używasz anteny telewizyjnej, wyreguluj jej położenie lub kierunek. Aby uzyskać najlepszy sygnał, umieść antenę na podwyższeniu, w pobliżu okna, a następnie ponownie wykonaj skanowanie."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Skanuj ponownie"</item>
- <item msgid="2092797862490235174">"Gotowe"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Wyszukaj kanały TV"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Konfiguracja tunera TV"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Konfiguracja tunera TV USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Konfiguracja sieciowego tunera TV"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"Telewizyjny tuner USB rozłączony."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Tuner sieciowy rozłączony."</string>
-</resources>
diff --git a/usbtuner-res/values-pt-rPT/strings.xml b/usbtuner-res/values-pt-rPT/strings.xml
deleted file mode 100644
index 02f4dd9d..00000000
--- a/usbtuner-res/values-pt-rPT/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"Sintonizador de TV"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"Sintonizador de TV USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Sintonizador de televisão (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Aguarde enquanto o processamento é terminado"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"O software do sintonizador foi atualizado recentemente. Procure novamente os canais."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Ative o som surround nas definições de som do sistema para ativar o áudio"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Não é possível reproduzir áudio. Experimente outra TV."</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Configuração do sintonizador de canais"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Configuração do sintonizador de TV"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Configuração do sintonizador de canais USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Configuração do sintonizador de rede"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Confirme se a sua TV está ligada a uma fonte de sinal da TV.\n\nSe estiver a utilizar uma antena via rede sem fios, pode ter de ajustar a respetiva posição ou a direção para receber a maioria dos canais. Para obter os melhores resultados, coloque-a num ponto alto e junto a uma janela."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Confirme se o sintonizador USB está ativado e ligado a uma fonte de sinal da TV.\n\nSe estiver a utilizar uma antena via rede sem fios, pode ter de ajustar a respetiva posição ou a direção para receber a maioria dos canais. Para obter os melhores resultados, coloque-a num ponto alto e junto a uma janela."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Confirme se o sintonizador de rede está ligado à alimentação e ligado a uma fonte de sinal de TV.\n\nSe estiver a utilizar uma antena via rede sem fios, pode ter de ajustar a respetiva posição ou direção para receber a maioria dos canais. Para obter os melhores resultados, coloque-a num ponto alto e junto a uma janela."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Continuar"</item>
- <item msgid="727245208787621142">"Agora não"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Pretende executar novamente a configuração do canal?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Esta ação remove os canais encontrados pelo sintonizador de TV e procura novamente canais novos.\n\nConfirme se a sua TV está ligada a uma fonte de sinal da TV.\n\nSe estiver a utilizar uma antena via rede sem fios, pode ter de ajustar a respetiva posição ou a direção para receber a maioria dos canais. Para obter os melhores resultados, coloque-a num ponto alto e junto a uma janela."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Esta ação remove os canais encontrados do sintonizador USB e procura novamente canais novos.\n\nConfirme se o sintonizador USB está ativado e ligado a uma fonte de sinal da TV.\n\nSe estiver a utilizar uma antena via rede sem fios, pode ter de ajustar a respetiva posição ou a direção para receber a maioria dos canais. Para obter os melhores resultados, coloque-a num ponto alto e junto a uma janela."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Esta ação vai remover os canais encontrados do sintonizador de rede e procurar novos canais novamente.\n\nConfirme se o sintonizador de rede está ligado à alimentação e ligado a uma fonte de sinal de TV.\n\nSe estiver a utilizar uma antena via rede sem fios, pode ter de ajustar a respetiva posição ou direção para receber a maioria dos canais. Para obter os melhores resultados, coloque-a num ponto alto e junto a uma janela."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Continuar"</item>
- <item msgid="235450158666155406">"Cancelar"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Selecionar o tipo de ligação"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Escolha Antena se existir uma antena externa ligada ao sintonizador. Escolha Cabo se os canais forem provenientes de um fornecedor de serviços por cabo. Se não tiver a certeza, ambos os tipos são procurados, mas esta ação pode demorar mais tempo."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antena"</item>
- <item msgid="2670079958754180142">"Cabo"</item>
- <item msgid="36774059871728525">"Não tenho a certeza"</item>
- <item msgid="6881204453182153978">"Apenas para desenvolvimento"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Configuração do sintonizador de TV"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Configuração do sintonizador de canais USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Configuração do sintonizador de canais"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Esta operação pode demorar vários minutos"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"O sintonizador está temporariamente indisponível ou já está a ser utilizado pela gravação."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="one">%1$d canal encontrado</item>
- <item quantity="other">%1$d canais encontrados</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"INTERROMPER A PROCURA DE CANAIS"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="one">%1$d canal encontrado</item>
- <item quantity="other">%1$d canais encontrados</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="one">Boa! Foi encontrado %1$d canal durante a procura de canais. Se isso não lhe parecer correto, experimente ajustar a posição da antena e procurar novamente.</item>
- <item quantity="other">Boa! Foram encontrados %1$d canais durante a procura de canais. Se isto não lhe parecer correto, experimente ajustar a posição da antena e procurar novamente.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Concluído"</item>
- <item msgid="2480490326672924828">"Procurar novamente"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Não foram encontrados canais"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"A procura não encontrou quaisquer canais. Confirme se a TV está ligada a uma fonte de sinal da TV.\n\nSe estiver a utilizar uma antena via rede sem fios, ajuste a respetiva posição ou a direção. Para obter os melhores resultados, coloque-a num ponto alto e junto a uma janela e procure novamente."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Não foram encontrados quaisquer canais. Verifique se o sintonizador USB está ativado e ligado a uma fonte de sinal da TV.\n\nSe estiver a utilizar uma antena via rede sem fios, ajuste a respetiva posição ou a direção. Para obter os melhores resultados, coloque-a num ponto alto e junto a uma janela e procure novamente."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"A procura não encontrou quaisquer canais. Confirme se o sintonizador de rede está ligado à alimentação e ligado a uma fonte de sinal de TV.\n\nSe estiver a utilizar uma antena via rede sem fios, ajuste a respetiva posição ou direção. Para obter os melhores resultados, coloque-a num ponto alto e junto a uma janela. Em seguida, procure novamente."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Procurar novamente"</item>
- <item msgid="2092797862490235174">"Concluído"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Procurar canais de TV"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Configuração do sintonizador de TV"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Configuração do sintonizador de TV USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Configuração do sintonizador de TV por cabo"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"Sintonizador de TV USB desligado."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Sintonizador de rede desligado."</string>
-</resources>
diff --git a/usbtuner-res/values-pt/strings.xml b/usbtuner-res/values-pt/strings.xml
deleted file mode 100644
index d123306a..00000000
--- a/usbtuner-res/values-pt/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"Sintonizador de TV"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"Sintonizador de TV USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Sintonizador de rede de TV (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Aguarde a conclusão do processamento"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"O software do sintonizador foi atualizado recentemente. Procure os canais mais uma vez."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Ative o som surround nas configurações de som do sistema para ativar o áudio"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Não foi possível reproduzir o áudio. Tente em outra TV"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Configuração do sintonizador de canais"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Configuração do Sintonizador de TV"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Configuração do sintonizador de canais USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Configuração do sintonizador de rede"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Verifique se sua TV está conecta a uma fonte de sinal de TV.\n\nSe você estiver usando uma antena Over-the-air (OTA), talvez seja necessário ajustar o posicionamento ou a direção dela para receber o máximo de canais. Para ter resultados melhores, coloque-a em um lugar alto e perto de uma janela."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Verifique se o sintonizador USB está conectado a uma fonte de sinal de TV.\n\nSe você usa uma antena Over the air, talvez seja necessário ajustar o posicionamento ou a direção dela para receber o maior número de canais. Para ter resultados melhores, coloque-a em um lugar alto e perto de uma janela."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Verifique se o sintonizador de rede está ligado e conectado a uma fonte de sinal de TV.\n\nSe você estiver usando uma antena OTA (over-the-air), talvez seja necessário ajustar o posicionamento ou a direção dela para receber o máximo de canais. Para ter resultados melhores, coloque-a em um lugar alto e perto de uma janela."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Continuar"</item>
- <item msgid="727245208787621142">"Agora não"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Executar novamente a configuração de canais?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Isso removerá os canais encontrados do Sintonizador de TV e procurará novos canais mais uma vez.\n\nVerifique se sua TV está conectada a uma fonte de sinal de TV.\n\nSe você estiver usando uma antena Over-the-air (OTA), talvez seja necessário ajustar o posicionamento ou a direção dela para receber o máximo de canais. Para ter resultados melhores, coloque-a em um lugar alto e perto de uma janela."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Essa ação remove os canais encontrados pelo sintonizador USB e procura canais novamente.\n\nVerifique se o sintonizador USB está conectado a uma fonte de sinal de TV.\n\n.Se você usa uma antena Over the air, talvez seja necessário ajustar o posicionamento ou a direção dela para receber o maior número de canais. Para ter resultados melhores, coloque-a em um lugar alto e perto de uma janela."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Essa ação removerá os canais encontrados do sintonizador de rede e procurará novos canais mais uma vez.\n\nVerifique se o sintonizador de rede está ligado e conectado a uma fonte de sinal de TV.\n\nSe você estiver usando uma antena OTA (over-the-air), talvez seja necessário ajustar o posicionamento ou a direção dela para receber o máximo de canais. Para ter resultados melhores, coloque-a em um lugar alto e perto de uma janela."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Continuar"</item>
- <item msgid="235450158666155406">"Cancelar"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Selecione o tipo de conexão"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Selecione \"Antena\", se houver uma antena externa conectada ao sintonizador. Selecione \"Cabo\", se seus canais vierem de um provedor de serviços a cabo. Se você não tiver certeza, será feita uma procura usando os dois tipos, mas isso pode demorar mais tempo."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antena"</item>
- <item msgid="2670079958754180142">"Cabo"</item>
- <item msgid="36774059871728525">"Não tenho certeza"</item>
- <item msgid="6881204453182153978">"Somente desenvolvimento"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Configuração do Sintonizador de TV"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Configuração do sintonizador de canais USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Configuração do sintonizador de canais de rede"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Isso pode demorar alguns minutos"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"O sintonizador está temporariamente indisponível ou já está sendo usado pela gravação."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="one">%1$d canal encontrado</item>
- <item quantity="other">%1$d canais encontrados</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"INTERROMPER PROCURA DE CANAIS"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="one">%1$d canal encontrado</item>
- <item quantity="other">%1$d canais encontrados</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="one">Legal! %1$d canal foi encontrado durante a procura de canais. Se você acha que esse número não está correto, tente ajustar a posição da antena e procure novamente.</item>
- <item quantity="other">Legal! %1$d canais foram encontrados durante a procura de canais. Se você acha que esse número não está correto, tente ajustar a posição da antena e procure novamente.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Concluído"</item>
- <item msgid="2480490326672924828">"Procurar novamente"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Nenhum canal encontrado"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"A procura não encontrou nenhum canal. Verifique se sua TV está conectada a uma fonte de sinal de TV.\n\nSe você estiver usando uma antena Over-the-air (OTA), ajuste o posicionamento ou a direção dela. Para ter resultados melhores, coloque-a em um lugar alto e perto de uma janela e procure novamente."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Nenhum canal foi encontrado pela procura. Verifique se o sintonizador USB está conectado a uma fonte de sinal de TV.\n\nSe você estiver usando uma antena Over the air, ajuste o posicionamento ou a direção dela. Para ter resultados melhores, coloque-a em um lugar alto e perto de uma janela e procure novamente."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Nenhum canal foi encontrado pela procura. Verifique se o sintonizador de rede está ligado e conectado a uma fonte de sinal de TV.\n\nSe você estiver usando uma antena OTA (over-the-air), ajuste o posicionamento ou a direção dela. Para ter resultados melhores, coloque-a em um lugar alto e perto de uma janela, depois procure novamente."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Procurar novamente"</item>
- <item msgid="2092797862490235174">"Concluído"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Procurar canais de TV"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Configuração do Sintonizador de TV"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Configuração do Sintonizador de TV USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Configuração do sintonizador de rede de TV"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"Sintonizador de TV USB desconectado."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Sintonizador de rede desconectado."</string>
-</resources>
diff --git a/usbtuner-res/values-ro/strings.xml b/usbtuner-res/values-ro/strings.xml
deleted file mode 100644
index d30101a4..00000000
--- a/usbtuner-res/values-ro/strings.xml
+++ /dev/null
@@ -1,93 +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="bt_app_name" msgid="5515382901862469770">"Tuner TV"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"Tuner TV USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Tuner de rețea TV (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Așteptați ca procesarea să fie finalizată"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Software-ul tunerului a fost actualizat recent. Scanați din nou canalele."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Pentru a activa conținutul audio, activați sunetul surround din setările de sunet ale sistemului"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Conținutul audio nu poate fi redat. Încercați pe un alt televizor."</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Configurarea tunerului de canale"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Configurarea tunerului TV"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Configurarea tunerului de canale USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Configurarea tunerului de rețea"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Asigurați-vă că televizorul este conectat la o sursă de semnal TV.\n\nDacă folosiți o antenă over the air, poate fi necesar să-i ajustați amplasarea sau direcția astfel încât să capteze majoritatea canalelor. Pentru cele mai bune rezultate, amplasați-o la înălțime și în apropierea unei ferestre."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Asigurați-vă că tunerul USB este conectat la o sursă de alimentare și la o sursă de semnal TV.\n\nDacă folosiți o antenă over the air, poate fi necesar să-i ajustați amplasarea sau direcția astfel încât să capteze majoritatea canalelor. Pentru cele mai bune rezultate, amplasați-o la înălțime și în apropierea unei ferestre."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Verificați dacă tunerul de rețea este pornit și conectat la o sursă de semnal TV.\n\nDacă folosiți o antenă over-the-air, ar putea fi necesar să-i ajustați poziția sau direcția pentru a recepționa cât mai multe canale. Pentru rezultate optime, plasați-o sus și lângă o fereastră."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Continuați"</item>
- <item msgid="727245208787621142">"Nu acum"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Configurați din nou canalul?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Astfel, vor fi eliminate canalele găsite de tunerul TV și se vor căuta din nou canale.\n\nAsigurați-vă că televizorul este conectat la o sursă de semnal TV.\n\nDacă folosiți o antenă over the air, poate fi necesar să-i ajustați amplasarea sau direcția astfel încât să capteze majoritatea canalelor. Pentru cele mai bune rezultate, amplasați-o la înălțime și în apropierea unei ferestre."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Astfel, vor fi eliminate canalele găsite de pe tunerul USB și se vor căuta din nou canale.\n\nAsigurați-vă că tunerul USB este conectat la o sursă de alimentare și la o sursă de semnal TV.\n\nDacă folosiți o antenă over the air, poate fi necesar să-i ajustați amplasarea sau direcția pentru a capta majoritatea canalelor. Pentru cele mai bune rezultate, amplasați-o la înălțime și în apropierea unei ferestre."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Astfel veți elimina canalele găsite din tunerul de rețea și veți scana iar pentru a găsi canale noi.\n\nVerificați dacă tunerul de rețea este pornit și conectat la o sursă de semnal TV.\n\nDacă folosiți o antenă over-the-air, ar putea fi necesar să-i ajustați poziția sau direcția pentru a recepționa cât mai multe canale. Pentru rezultate optime, plasați-o sus și lângă o fereastră."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Continuați"</item>
- <item msgid="235450158666155406">"Anulați"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Selectați tipul de conexiune"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Alegeți Antenă dacă există o antenă externă conectată la tuner. Alegeți Cablu în cazul în care canalele provin de la un furnizor de servicii prin cablu. Dacă nu știți sigur care este situația, se vor scana ambele tipuri, însă poate dura mai mult."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antenă"</item>
- <item msgid="2670079958754180142">"Cablu"</item>
- <item msgid="36774059871728525">"Nu știu sigur"</item>
- <item msgid="6881204453182153978">"Numai pentru dezvoltare"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Configurarea tunerului TV"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Configurarea tunerului de canale USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Configurarea tunerului de canale de rețea"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Poate dura câteva minute"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Tunerul nu este disponibil temporar sau este folosit deja de înregistrare."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="few">%1$d canale găsite</item>
- <item quantity="other">%1$d de canale găsite</item>
- <item quantity="one">%1$d canal găsit</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"OPRIȚI SCANAREA CANALELOR"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="few">%1$d canale găsite</item>
- <item quantity="other">%1$d de canale găsite</item>
- <item quantity="one">%1$d canal găsit</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="few">Excelent! La scanarea canalelor s-au găsit %1$d canale. Dacă vi se pare că ceva nu este în regulă, ajustați poziția antenei și repetați scanarea.</item>
- <item quantity="other">Excelent! La scanarea canalelor s-au găsit %1$d de canale. Dacă vi se pare că ceva nu este în regulă, ajustați poziția antenei și repetați scanarea.</item>
- <item quantity="one">Excelent! La scanarea canalelor s-a găsit %1$d canal. Dacă vi se pare că ceva nu este în regulă, ajustați poziția antenei și repetați scanarea.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Terminat"</item>
- <item msgid="2480490326672924828">"Scanați din nou"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Nu s-au găsit canale"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Scanarea nu a găsit niciun canal. Asigurați-vă că televizorul este conectat la o sursă de semnal TV.\n\nDacă folosiți o antenă over the air, ajustați-i amplasarea sau direcția. Pentru cele mai bune rezultate, amplasați-o la înălțime și în apropierea unei ferestre, apoi repetați scanarea."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Nu s-a găsit niciun canal la scanare. Asigurați-vă că tunerul USB este conectat la o sursă de alimentare și la o sursă de semnal TV. \n\nDacă folosiți o antenă over the air, ajustați-i amplasarea sau direcția. Pentru cele mai bune rezultate, amplasați-o la înălțime și în apropierea unei ferestre, apoi repetați scanarea."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Scanarea nu a găsit niciun canal. Verificați dacă tunerul de rețea este pornit și conectat la o sursă de semnal TV.\n\nDacă folosiți o antenă over-the-air, ajustați poziția sau direcția. Pentru rezultate optime, plasați-o sus și lângă o fereastră și scanați din nou."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Scanați din nou"</item>
- <item msgid="2092797862490235174">"Terminat"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Căutați canale TV"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Configurarea tunerului TV"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Configurarea tunerului TV USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Configurarea tunerului de rețea TV"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"Tunerul TV USB este deconectat."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Tunerul de rețea este deconectat."</string>
-</resources>
diff --git a/usbtuner-res/values-ru/strings.xml b/usbtuner-res/values-ru/strings.xml
deleted file mode 100644
index 33f97db0..00000000
--- a/usbtuner-res/values-ru/strings.xml
+++ /dev/null
@@ -1,92 +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="bt_app_name" msgid="5515382901862469770">"ТВ-тюнер"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB-тюнер"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Сетевой ТВ-тюнер (бета)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Дождитесь окончания обработки"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Программное обеспечение тюнера было обновлено. Повторите сканирование."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Включите объемный звук в системных настройках"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Не удается воспроизвести аудио. Выберите другой канал."</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Настройка тюнера"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Настройка ТВ-тюнера"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Настройка USB-тюнера"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Настройка сетевого тюнера"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Убедитесь, что телевизор подключен к источнику сигнала.\n\nЕсли вы используете телеантенну, переместите или перенаправьте ее. Рекомендуем расположить ее высоко и рядом с окном."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Убедитесь, что USB-тюнер включен в сеть и подсоединен к источнику сигнала.\n\nЕсли вы используете телеантенну, переместите или перенаправьте ее. Рекомендуем расположить ее высоко и рядом с окном."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Убедитесь, что сетевой тюнер включен в сеть и подсоединен к источнику ТВ-сигнала.\n\nЕсли вы используете телеантенну, поместите и направьте ее так, чтобы она принимала большинство каналов. Рекомендуем расположить антенну высоко и рядом с окном."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Продолжить"</item>
- <item msgid="727245208787621142">"Не сейчас"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Настроить каналы заново?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Все каналы, найденные с помощью ТВ-тюнера, будут удалены, и поиск начнется заново.\n\nУбедитесь, что телевизор подключен к источнику сигнала.\n\nЕсли вы используете телеантенну, переместите или перенаправьте ее. Рекомендуем расположить ее высоко и рядом с окном."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Все каналы, найденные с помощью USB-тюнера, будут удалены, и сканирование начнется заново.\n\nУбедитесь, что USB-тюнер включен в сеть и подсоединен к источнику сигнала.\n\nЕсли вы используете телеантенну, переместите или перенаправьте ее. Рекомендуем расположить ее высоко и рядом с окном."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Все каналы, найденные с помощью сетевого тюнера, будут удалены, и сканирование начнется заново.\n\nУбедитесь, что сетевой тюнер включен в сеть и подсоединен к источнику ТВ-сигнала.\n\nЕсли вы используете телеантенну, поместите и направьте ее так, чтобы она принимала большинство каналов. Рекомендуем расположить антенну высоко и рядом с окном."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Продолжить"</item>
- <item msgid="235450158666155406">"Отмена"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Выберите тип соединения"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Если к тюнеру подключена внешняя антенна, выберите вариант \"Антенна\". Если вы ищете кабельные каналы, выберите \"Кабельное ТВ\". Если вы затрудняетесь ответить, мы будем искать оба типа каналов, но это займет чуть больше времени."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Антенна"</item>
- <item msgid="2670079958754180142">"Кабельное ТВ"</item>
- <item msgid="36774059871728525">"Неизвестно"</item>
- <item msgid="6881204453182153978">"Только для разработки"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Настройка ТВ-тюнера"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Настройка USB-тюнера"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Настройка каналов сетевого тюнера"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Это может занять несколько минут"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Тюнер временно недоступен или уже используется для записи."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="one">Найден %1$d канал</item>
- <item quantity="few">Найдено %1$d канала</item>
- <item quantity="many">Найдено %1$d каналов</item>
- <item quantity="other">Найдено %1$d канала</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"НЕ СКАНИРОВАТЬ"</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for ut_result_found_title (1448908152026339099) -->
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="one">Отлично! Найден %1$d канал. Если результат вас не устраивает, переместите антенну и повторите сканирование.</item>
- <item quantity="few">Отлично! Найдено %1$d канала. Если результат вас не устраивает, переместите антенну и повторите сканирование.</item>
- <item quantity="many">Отлично! Найдено %1$d каналов. Если результат вас не устраивает, переместите антенну и повторите сканирование.</item>
- <item quantity="other">Отлично! Найдено %1$d канала. Если результат вас не устраивает, переместите антенну и повторите сканирование.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Готово"</item>
- <item msgid="2480490326672924828">"Искать повторно"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Каналы не найдены"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Каналы не найдены. Убедитесь, что телевизор подключен к источнику сигнала.\n\nЕсли вы используете телеантенну, переместите или перенаправьте ее. Рекомендуем расположить ее высоко и рядом с окном."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Каналы не найдены. Убедитесь, что USB-тюнер включен в сеть и подсоединен к источнику сигнала.\n\nЕсли вы используете телеантенну, переместите или перенаправьте ее. Рекомендуем расположить ее высоко и рядом с окном."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Каналы не найдены. Убедитесь, что сетевой тюнер включен в сеть и подсоединен к источнику ТВ-сигнала.\n\nЕсли вы используете телеантенну, переместите или перенаправьте ее (рекомендуем расположить антенну высоко и рядом с окном). Затем повторите сканирование."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Искать повторно"</item>
- <item msgid="2092797862490235174">"Готово"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Сканируйте телеканалы"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Настройка ТВ-тюнера"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Настройка USB-тюнера"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Настройка сетевого ТВ-тюнера"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB-тюнер отключен."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Сетевой тюнер отключен."</string>
-</resources>
diff --git a/usbtuner-res/values-si-rLK/strings.xml b/usbtuner-res/values-si-rLK/strings.xml
deleted file mode 100644
index 577015b6..00000000
--- a/usbtuner-res/values-si-rLK/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"TV සුසරකය"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB TV සුසරකය"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Network TV Tuner (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"සැකසීම අවසන් කිරීමට කරුණාකර රැඳී සිටින්න"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"සුසරක මෘදුකාංගය පසුගියදා යාවත්කාලීන කර ඇත. කරුණාකර නාලිකා නැවත ස්කෑන් කරන්න."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"ශ්‍රව්‍ය සබල කිරීමට හඬ සැකසීම් තුළ අවට හඬ සබල කරන්න"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"ශ්‍රව්‍යය ධාවනය කළ නොහැකිය. කරුණාකර වෙනත් TV එකක් උත්සාහ කරන්න"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"නාලිකා සුසරක පිහිටුවීම"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"TV සුසරක පිහිටුවීම"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB නාලිකා සුසරක පිහිටුවීම"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"ජාල සුසරකය පිහිටුවීම"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"ඔබේ TV, TV සංඥා මූලාශ්‍රයකට සම්බන්ධ කර ඇති බව තහවුරු කර ගන්න.\n\nගුවන-ඔස්සේ ඇන්ටනාවක් භාවිත කරන්නේ නම්, ඔබට බොහොමයක් නාලිකා ලබා ගැනීමට පිහිටීම හෝ දිශාව සීරුමාරු කිරීමට අවශ්‍ය විය හැකිය. හොඳම ප්‍රතිඵල සඳහා, එය ඉහළින් සහ කවුළුවක් ආසන්නයේ තබන්න."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"සුසරකය පේනුගත කර ඇති බව සහ TV සංඥා මූලාශ්‍රයකට සම්බන්ධ කර ඇති බව තහවුරු කර ගන්න.\n\nගුවන-ඔස්සේ ඇන්ටනාවක් භාවිත කරන්නේ නම්, ඔබට බොහොමයක් නාලිකා ලබා ගැනීමට පිහිටීම හෝ දිශාව සීරුමාරු කිරීමට අවශ්‍ය විය හැකිය. හොඳම ප්‍රතිඵල සඳහා, එය ඉහළින් සහ කවුළුවක් ආසන්නයේ තබන්න."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"ජාල සුසරකය බලයට සම්බන්ධ කර ඇති බව සහ TV සංඥා මූලාශ්‍රයකට සම්බන්ධ කර ඇති බව තහවුරු කර ගන්න.\n\nගුවන-ඔස්සේ ඇන්ටනාවක් භාවිත කරන්නේ නම්, ඔබට බොහොමයක් නාලිකා ලබා ගැනීමට පිහිටීම හෝ දිශාව සීරුමාරු කිරීමට අවශ්‍ය විය හැකිය. හොඳම ප්‍රතිඵල සඳහා, එය ඉහළින් සහ කවුළුවක් ආසන්නයේ තබන්න."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"දිගටම කර ගෙන යන්න"</item>
- <item msgid="727245208787621142">"දැන් නොවේ"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"නාලිකා පිහිටුවීම නැවත ධාවනය කරන්නද?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"මෙය TV සුසරකය වෙතින් සොයා ගත් නාලිකා ඉවත් කරනු ඇති අතර නව නාලිකා සඳහා නැවත ස්කෑන් කරනු ඇත.\n\nඔබේ TV, TV සංඥා මූලාශ්‍රයකට සම්බන්ධ කර ඇති බව තහවුරු කර ගන්න.\n\nගුවන-ඔස්සේ ඇන්ටනාවක් භාවිත කරන්නේ නම්, ඔබට බොහොමයක් නාලිකා ලබා ගැනීමට පිහිටීම හෝ දිශාව සීරුමාරු කිරීමට අවශ්‍ය විය හැකිය. හොඳම ප්‍රතිඵල සඳහා, එය ඉහළින් සහ කවුළුවක් ආසන්නයේ තබන්න."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"මෙය USB සුසරකය වෙතින් සොයා ගත් නාලිකා ඉවත් කරනු ඇති අතර නව නාලිකා සඳහා නැවත ස්කෑන් කරනු ඇත.\n\nUSB සුසරකය පේනුගත කර ඇති බව සහ TV සංඥා මූලාශ්‍රයකට සම්බන්ධ කර ඇති බව තහවුරු කර ගන්න.\n\nගුවන-ඔස්සේ ඇන්ටනාවක් භාවිත කරන්නේ නම්, ඔබට බොහොමයක් නාලිකා ලබා ගැනීමට පිහිටීම හෝ දිශාව සීරුමාරු කිරීමට අවශ්‍ය විය හැකිය. හොඳම ප්‍රතිඵල සඳහා, එය ඉහළින් සහ කවුළුවක් ආසන්නයේ තබන්න."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"මෙය ජාල සුසරකය වෙතින් සොයා ගත් නාලිකා ඉවත් කරනු ඇති අතර නව නාලිකා සඳහා නැවත ස්කෑන් කරනු ඇත.\n\nජාල සුසරකය බලයට සම්බන්ධ කර ඇති බව සහ TV සංඥා මූලාශ්‍රයකට සම්බන්ධ කර ඇති බව තහවුරු කර ගන්න.\n\nගුවන-ඔස්සේ ඇන්ටනාවක් භාවිත කරන්නේ නම්, ඔබට බොහොමයක් නාලිකා ලබා ගැනීමට පිහිටීම හෝ දිශාව සීරුමාරු කිරීමට අවශ්‍ය විය හැකිය. හොඳම ප්‍රතිඵල සඳහා, එය ඉහළින් සහ කවුළුවක් ආසන්නයේ තබන්න."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"දිගටම කර ගෙන යන්න"</item>
- <item msgid="235450158666155406">"අවලංගු කරන්න"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"සබැඳුම් ආකාරය තෝරන්න"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"සුසරකයට බාහිර අැන්ටනාවක් සම්බන්ධ කර ඇත්නම්, ඇන්ටනාව තෝරන්න. ඔබේ නාලිකා කේබල් සැපයුම්කරුවෙකු වෙතින් පැමිණෙන්නේ නම් කේබලය තෝරන්න. ඔබට ස්ථිර නැත්නම්, ආකාර දෙකම ස්කෑන් කරනු ඇත, නමුත් මෙයට වැඩි කාලයක් ගත විය හැකිය."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"ඇන්ටනාව"</item>
- <item msgid="2670079958754180142">"කේබලය"</item>
- <item msgid="36774059871728525">"ස්ථිර නැත"</item>
- <item msgid="6881204453182153978">"සංවර්ධනයට පමණි"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"TV සුසරක පිහිටුවීම"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB නාලිකා සුසරක පිහිටුවීම"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"ජාල නාලිකා සුසරකය පිහිටුවීම"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"මෙය මිනිත්තු කිහිපයක් ගත හැකිය"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"සුසරකය තාවකාලිකව ලබා ගත නොහැකිය නැතහොත් දැනටමත් පටිගත කිරීම මගින් භාවිත කරනු ලැබේ."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="one">නාලිකා %1$dක් සොයා ගන්නා ලදී</item>
- <item quantity="other">නාලිකා %1$dක් සොයා ගන්නා ලදී</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"නාලිකා ස්කෑන් කිරීම නවත්වන්න"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="one">නාලිකා %1$dක් සොයා ගන්නා ලදී</item>
- <item quantity="other">නාලිකා %1$dක් සොයා ගන්නා ලදී</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="one">කදිමයි! නාලිකා ස්කෑන් කිරීම අතරතුර නාලිකා %1$dක් සොයා ගන්නා ලදී. මෙය නිවැරදි බව නොපෙනේ නම්, ඇන්ටනාවේ පිහිටීම සීරුමාරු කිරීම උත්සාහ කර නැවත ස්කෑන් කරන්න.</item>
- <item quantity="other">කදිමයි! නාලිකා ස්කෑන් කිරීම අතරතුර නාලිකා %1$dක් සොයා ගන්නා ලදී. මෙය නිවැරදි බව නොපෙනේ නම්, ඇන්ටනාවේ පිහිටීම සීරුමාරු කිරීම උත්සාහ කර නැවත ස්කෑන් කරන්න.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"නිමයි"</item>
- <item msgid="2480490326672924828">"නැවත ස්කෑන් කරන්න"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"නාලිකා හමු නොවීය"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"ස්කෑන් කිරීමට නාලිකා කිසිවක් හමු නොවීය. ඔබේ TV, TV සංඥා මූලාශ්‍රයකට සම්බන්ධ කර ඇති බව තහවුරු කර ගන්න.\n\nගුවන-ඔස්සේ ඇන්ටනාවක් භාවිත කරන්නේ නම්, එහි පිහිටීම හෝ දිශාව සීරුමාරු කරන්න. හොඳම ප්‍රතිඵල සඳහා, එය ඉහළින් සහ කවුළුවක් ආසන්නයේ තබා නැවත ස්කෑන් කරන්න."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"ස්කෑන් කිරීමට නාලිකා කිසිවක් හමු නොවීය. USB සුසරකය පේනුගත කර ඇති බව සහ TV සංඥා මූලාශ්‍රයකට සම්බන්ධ කර ඇති බව තහවුරු කර ගන්න.\n\nගුවන-ඔස්සේ ඇන්ටනාවක් භාවිත කරන්නේ නම්, පිහිටීම හෝ දිශාව සීරුමාරු කරන්න. හොඳම ප්‍රතිඵල සඳහා, එය ඉහළින් සහ කවුළුවක් ආසන්නයේ තබා නැවත ස්කෑන් කරන්න."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"ස්කෑන් කිරීමට නාලිකා කිසිවක් හමු නොවීය. ජාල සුසරකය බලයට සම්බන්ධ කර ඇති බව සහ TV සංඥා මූලාශ්‍රයකට සම්බන්ධ කර ඇති බව තහවුරු කර ගන්න.\n\nගුවන-ඔස්සේ ඇන්ටනාවක් භාවිත කරන්නේ නම්, පිහිටීම හෝ දිශාව සීරුමාරු කරන්න. හොඳම ප්‍රතිඵල සඳහා, එය ඉහළින් සහ කවුළුවක් ආසන්නයේ තබා නැවත ස්කෑන් කරන්න."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"නැවත ස්කෑන් කරන්න"</item>
- <item msgid="2092797862490235174">"නිමයි"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"TV නාලිකා සඳහා ස්කෑන් කරන්න"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"TV සුසරක පිහිටුවීම"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB TV සුසරක පිහිටුවීම"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"ජාල TV සුසරක පිහිටුවීම"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB TV සුසරකය විසන්ධි වුණි."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"ජාල සුසරකය විසන්ධි වුණි."</string>
-</resources>
diff --git a/usbtuner-res/values-sk/strings.xml b/usbtuner-res/values-sk/strings.xml
deleted file mode 100644
index 60448b2b..00000000
--- a/usbtuner-res/values-sk/strings.xml
+++ /dev/null
@@ -1,96 +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="bt_app_name" msgid="5515382901862469770">"Televízny tuner"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"Televízny tuner s rozhraním USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Televízny sieťový tuner (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Počkajte, kým bude spracovanie dokončené"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Softvér tunera bol nedávno aktualizovaný. Znova vyhľadajte kanály."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Ak chcete zapnúť zvuk, v nastaveniach systémového zvuku povoľte priestorový zvuk"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Zvuk nie je možné prehrať. Skúste použiť iný televízor."</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Nastavenie tunera kanálov"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Nastavenie televízneho tunera"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Nastavenie kanálov tunera USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Nastavenie sieťového tunera"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Skontrolujte, či je televízor pripojený k zdroju televízneho signálu.\n\nAk používate vzdušnú anténu, upravte jej umiestnenie alebo orientáciu, aby ste získali čo najviac kanálov. Najlepšie výsledky dosiahnete umiestnením antény dostatočne vysoko a do blízkosti okna."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Skontrolujte, či je tuner USB zapojený a pripojený k zdroju televízneho signálu.\n\nAk používate vzdušnú anténu, upravte jej umiestnenie alebo orientáciu, aby ste získali čo najviac kanálov. Najlepšie výsledky dosiahnete umiestnením antény dostatočne vysoko a do blízkosti okna."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Skontrolujte, či je sieťový tuner zapnutý a pripojený k zdroju televízneho signálu.\n\nAk používate bezdrôtovú anténu, upravte jej umiestnenie alebo orientáciu tak, aby ste získali čo najviac kanálov. Najlepšie výsledky dosiahnete umiestnením antény dostatočne vysoko a do blízkosti okna."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Pokračovať"</item>
- <item msgid="727245208787621142">"Teraz nie"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Znova spustiť nastavenie kanálov?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Táto akcia odstráni nájdené kanály z televízneho tunera a opätovne spustí vyhľadávanie nových kanálov.\n\nSkontrolujte, či je televízor pripojený k zdroju televízneho signálu.\n\nAk používate vzdušnú anténu, upravte jej umiestnenie alebo orientáciu, aby ste získali čo najviac kanálov. Najlepšie výsledky dosiahnete umiestnením antény dostatočne vysoko a do blízkosti okna."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Táto akcia odstráni nájdené kanály z tunera USB a opätovne spustí vyhľadávanie nových kanálov.\n\nSkontrolujte, či je tuner USB zapojený a pripojený k zdroju televízneho signálu.\n\nAk používate vzdušnú anténu, upravte jej umiestnenie alebo orientáciu, aby ste získali čo najviac kanálov. Najlepšie výsledky dosiahnete umiestnením antény dostatočne vysoko a do blízkosti okna."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Táto akcia odstráni nájdené kanály zo sieťového tunera a opätovne spustí vyhľadávanie nových kanálov.\n\nSkontrolujte, či je sieťový tuner zapnutý a pripojený k zdroju televízneho signálu.\n\nAk používate bezdrôtovú anténu, upravte jej umiestnenie alebo orientáciu tak, aby ste získali čo najviac kanálov. Najlepšie výsledky dosiahnete umiestnením antény dostatočne vysoko a do blízkosti okna."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Pokračovať"</item>
- <item msgid="235450158666155406">"Zrušiť"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Výber typu pripojenia"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Ak je k tuneru pripojená externá anténa, vyberte možnosť Anténa. Ak máte kanály od poskytovateľa káblových služieb, vyberte možnosť Kábel. Ak to neviete isto, prehľadajú sa oba typy, ale môže to trvať dlhšie."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Anténa"</item>
- <item msgid="2670079958754180142">"Kábel"</item>
- <item msgid="36774059871728525">"Neviem"</item>
- <item msgid="6881204453182153978">"Iba pre vývojárov"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Nastavenie televízneho tunera"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Nastavenie kanálov tunera USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Nastavenie kanálov sieťového tunera"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Môže to trvať niekoľko minút"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Tuner nie je dočasne kˆ dispozícii alebo sa práve používa na nahrávanie."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="few">Našli sa %1$d kanály</item>
- <item quantity="many">Našlo sa %1$d kanála</item>
- <item quantity="other">Našlo sa %1$d kanálov</item>
- <item quantity="one">Našiel sa %1$d kanál</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"ZASTAVIŤ HĽADANIE KANÁLOV"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="few">Našli sa %1$d kanály</item>
- <item quantity="many">Našlo sa %1$d kanála</item>
- <item quantity="other">Našlo sa %1$d kanálov</item>
- <item quantity="one">Našiel sa %1$d kanál</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="few">Skvelé! Pri hľadaní kanálov sa našli %1$d kanály. Ak sa vám to nezdá, skúste upraviť pozíciu antény a znova spustite hľadanie.</item>
- <item quantity="many">Skvelé! Pri hľadaní kanálov sa našlo %1$d kanála. Ak sa vám to nezdá, skúste upraviť pozíciu antény a znova spustite hľadanie.</item>
- <item quantity="other">Skvelé! Pri hľadaní kanálov sa našlo %1$d kanálov. Ak sa vám to nezdá, skúste upraviť pozíciu antény a znova spustite hľadanie.</item>
- <item quantity="one">Skvelé! Pri hľadaní kanálov sa našiel %1$d kanál. Ak sa vám to nezdá, skúste upraviť pozíciu antény a znova spustite hľadanie.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Hotovo"</item>
- <item msgid="2480490326672924828">"Hľadať znova"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Nenašli sa žiadne kanály"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Nenašli sa žiadne kanály. Skontrolujte, či je televízor pripojený k zdroju televízneho signálu.\n\nAk používate vzdušnú anténu, upravte jej umiestnenie alebo orientáciu. Najlepšie výsledky dosiahnete umiestnením antény dostatočne vysoko a do blízkosti okna. Potom znova spustite hľadanie."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Nenašli sa žiadne kanály. Skontrolujte, či je tuner USB zapojený a pripojený k zdroju televízneho signálu.\n\nAk používate vzdušnú anténu, upravte jej umiestnenie alebo orientáciu. Najlepšie výsledky dosiahnete umiestnením antény dostatočne vysoko a do blízkosti okna. Potom znova spustite hľadanie."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Nenašli sa žiadne kanály. Skontrolujte, či je sieťový tuner zapnutý a pripojený k zdroju televízneho signálu.\n\nAk používate bezdrôtovú anténu, upravte jej umiestnenie alebo orientáciu. Najlepšie výsledky dosiahnete umiestnením antény dostatočne vysoko a do blízkosti okna. Potom znova spustite hľadanie."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Hľadať znova"</item>
- <item msgid="2092797862490235174">"Hotovo"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Vyhľadajte televízne kanály"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Nastavenie televízneho tunera"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Nastavenie televízneho tunera s rozhraním USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Nastavenie televízneho sieťového tunera"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"TV tuner s rozhraním USB bol odpojený."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Sieťový tuner bol odpojený."</string>
-</resources>
diff --git a/usbtuner-res/values-sl/strings.xml b/usbtuner-res/values-sl/strings.xml
deleted file mode 100644
index 2f0812bb..00000000
--- a/usbtuner-res/values-sl/strings.xml
+++ /dev/null
@@ -1,96 +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="bt_app_name" msgid="5515382901862469770">"Sprejemnik TV-kanalov"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"Sprejemnik TV-kanalov USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Omrežni sprejemnik TV-kanalov (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Počakajte, da se dokonča obdelava"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Programska oprema sprejemnika je bila nedavno posodobljena. Znova poiščite kanale."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"V sistemskih nastavitvah zvoka omogočite prostorski zvok, če želite omogočiti zvok"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Zvoka ni mogoče predvajati. Poskusite na drugem televizorju."</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Nastavitev sprejemnika kanalov"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Nastavitev sprejemnika TV-kanalov"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Nastavitev sprejemnika kanalov USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Nastavitev omrežnega sprejemnika"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Preverite, ali je televizor povezan z virom TV-signala.\n\nČe uporabljate anteno za prizemno televizijo, prilagodite njen položaj oziroma njeno usmerjenost. Če želite najboljši rezultat, jo postavite na visok položaj in blizu okna ter iščite kanale znova."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Preverite, ali je sprejemnik USB priključen in povezan z virom TV-signala.\n\nČe uporabljate anteno za prizemno televizijo, morate morda prilagoditi njen položaj oziroma njeno usmerjenost, če želite prejemati največ kanalov. Če želite najboljši rezultat, jo postavite na visok položaj in blizu okna."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Preverite, ali je omrežni sprejemnik vklopljen in povezan z virom TV-signala.\n\nČe uporabljate anteno za prizemno televizijo, prilagodite njen položaj oziroma njeno usmerjenost. Če želite najboljši rezultat, jo postavite na visok položaj in blizu okna."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Naprej"</item>
- <item msgid="727245208787621142">"Ne zdaj"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Ali želite znova zagnati namestitev kanalov?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"S tem bodo odstranjeni kanali, najdeni prek sprejemnika TV-kanalov, in iskanje kanalov se bo začelo znova.\n\nPreverite, ali je televizor povezan z virom TV-signala.\n\nČe uporabljate anteno za prizemno televizijo, prilagodite njen položaj oziroma njeno usmerjenost. Če želite najboljši rezultat, jo postavite na visok položaj in blizu okna ter iščite kanale znova."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"S tem bodo odstranjeni kanali, najdeni prek sprejemnika USB, in iskanje kanalov se bo začelo znova.\n\nPreverite, ali je sprejemnik USB priključen in povezan z virom TV-signala.\n\nČe uporabljate anteno za prizemno televizijo, morate morda prilagoditi njen položaj oziroma njeno usmerjenost, če želite prejemati največ kanalov. Če želite najboljši rezultat, jo postavite na visok položaj in blizu okna."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"S tem bodo odstranjeni kanali, najdeni z omrežnim sprejemnikom kanalov, in vnovič se bo začelo iskanje novih kanalov.\n\nPreverite, ali je omrežni sprejemnik vklopljen in povezan z virom TV-signala.\n\nČe uporabljate anteno za prizemno televizijo, prilagodite njen položaj oziroma njeno usmerjenost. Če želite najboljši rezultat, jo postavite na visok položaj in blizu okna."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Naprej"</item>
- <item msgid="235450158666155406">"Prekliči"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Izbira vrste povezave"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Izberite »Antena«, če je v sprejemnik priključena zunanja antena. Izberite »Kabelska televizija«, če kanale zagotavlja ponudnik storitve kabelske televizije. Če niste prepričani, bo preiskano oboje, vendar lahko traja dlje."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antena"</item>
- <item msgid="2670079958754180142">"Kabelska televizija"</item>
- <item msgid="36774059871728525">"Ne vem"</item>
- <item msgid="6881204453182153978">"Samo za razvoj"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Nastavitev sprejemnika TV-kanalov"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Nastavitev sprejemnika kanalov USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Nastavitev omrežnega sprejemnika kanalov"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"To lahko traja nekaj minut"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Sprejemnik začasno ni na voljo ali se že uporablja za snemanje."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="one">Najden je bil %1$d kanal</item>
- <item quantity="two">Najdena sta bila %1$d kanala</item>
- <item quantity="few">Najdeni so bili %1$d kanali</item>
- <item quantity="other">Najdenih je bilo %1$d kanalov</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"USTAVI ISKANJE KANALOV"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="one">Najden je bil %1$d kanal</item>
- <item quantity="two">Najdena sta bila %1$d kanala</item>
- <item quantity="few">Najdeni so bili %1$d kanali</item>
- <item quantity="other">Najdenih je bilo %1$d kanalov</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="one">Super. Med iskanjem kanalov je bil najden %1$d kanal. Če menite, da to ni pravilno, poskusite prilagoditi položaj antene in iščite znova.</item>
- <item quantity="two">Super. Med iskanjem kanalov sta bila najdena %1$d kanala. Če menite, da to ni pravilno, poskusite prilagoditi položaj antene in iščite znova.</item>
- <item quantity="few">Super. Med iskanjem kanalov so bili najdeni %1$d kanali. Če menite, da to ni pravilno, poskusite prilagoditi položaj antene in iščite znova.</item>
- <item quantity="other">Super. Med iskanjem kanalov je bilo najdenih %1$d kanalov. Če menite, da to ni pravilno, poskusite prilagoditi položaj antene in iščite znova.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Končano"</item>
- <item msgid="2480490326672924828">"Znova išči"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Ni najdenih kanalov"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Pri iskanju kanali niso bili najdeni. Preverite, ali je televizor povezan z virom TV-signala.\n\nČe uporabljate anteno za prizemno televizijo, prilagodite njen položaj oziroma njeno usmerjenost. Če želite najboljši rezultat, jo postavite na visok položaj in blizu okna ter iščite kanale znova."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Pri iskanju kanali niso bili najdeni. Preverite, ali je sprejemnik USB priključen in povezan z virom TV-signala.\n\nČe uporabljate anteno za prizemno televizijo, prilagodite njen položaj oziroma njeno usmerjenost. Če želite najboljši rezultat, jo postavite na visok položaj in blizu okna ter iščite kanale znova."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Pri iskanju ni bil najden noben kanal. Preverite, ali je omrežni sprejemnik vklopljen in povezan z virom TV-signala.\n\nČe uporabljate anteno za prizemno televizijo, prilagodite njen položaj oziroma njeno usmerjenost. Če želite najboljši rezultat, jo postavite na visok položaj in blizu okna ter iščite kanale znova."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Znova išči"</item>
- <item msgid="2092797862490235174">"Končano"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Iskanje TV-kanalov"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Nastavitev sprejemnika TV-kanalov"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Nastavitev sprejemnika TV-kanalov USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Nastavitev omrežnega sprejemnika TV-kanalov"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"Povezava s sprejemnikom za TV-kanale USB je prekinjena."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Povezava z omrežnim sprejemnikom je prekinjena."</string>
-</resources>
diff --git a/usbtuner-res/values-sr/strings.xml b/usbtuner-res/values-sr/strings.xml
deleted file mode 100644
index ce7c4d53..00000000
--- a/usbtuner-res/values-sr/strings.xml
+++ /dev/null
@@ -1,93 +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="bt_app_name" msgid="5515382901862469770">"Тјунер за ТВ"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB тјунер за ТВ"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Мрежни тјунер за ТВ (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Сачекајте да се заврши обрада"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Софтвер тјунера је недавно ажуриран. Претражите канале поново."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Омогући звучни систем у подешавањима звука да бисте омогућили аудио"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Звук не може да се пусти. Испробајте други ТВ уређај"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Подешавање тјунера за канале"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Подешавање тјунера за ТВ"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Подешавање USB тјунера за канале"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Подешавање мрежног тјунера"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Проверите да ли је ТВ повезан са извором ТВ сигнала.\n\nАко користите антену за сигнал преко мреже, можда треба да прилагодите њен положај или смер да бисте имали највише канала. Ако желите најбоље резултате, поставите је високо и близу прозора."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Проверите да ли је USB тјунер прикључен и повезан са извором ТВ сигнала.\n\nАко користите антену за сигнал преко мреже, можда треба да прилагодите њен положај или смер да бисте имали највише канала. Ако желите најбоље резултате, поставите је високо и близу прозора."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Проверите да ли је мрежни тјунер укључен и повезан са извором ТВ сигнала.\n\nАко користите антену за сигнал преко мреже, можда треба да прилагодите њен положај или смер да бисте имали највише канала. Ако желите најбоље резултате, поставите је високо и близу прозора."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Настави"</item>
- <item msgid="727245208787621142">"Не сада"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Желите ли да поново покренете подешавање канала?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"На овај начин уклањате канале пронађене помоћу тјунера за ТВ и поново тражите нове канале.\n\nПроверите да ли је ТВ повезан са извором ТВ сигнала.\n\nАко користите антену за сигнал преко мреже, можда треба да прилагодите њен положај или смер да бисте имали највише канала. Ако желите најбоље резултате, поставите је високо и близу прозора."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"На овај начин уклањате канале пронађене помоћу USB тјунера и поново тражите нове канале.\n\nПроверите да ли је USB тјунер прикључен и повезан са извором ТВ сигнала.\n\nАко користите антену за сигнал преко мреже, можда треба да прилагодите њен положај или смер да бисте имали највише канала. Ако желите најбоље резултате, поставите је високо и близу прозора."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"На овај начин уклањате канале пронађене помоћу мрежног тјунера и поново скенирате нове канале.\n\nПроверите да ли је мрежни тјунер укључен и повезан са извором ТВ сигнала.\n\nАко користите антену за сигнал преко мреже, можда треба да прилагодите њен положај или смер да бисте имали највише канала. Ако желите најбоље резултате, поставите је високо и близу прозора."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Настави"</item>
- <item msgid="235450158666155406">"Откажи"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Изаберите тип везе"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Изаберите „Антена“ ако је спољна антена повезана са тјунером. Изаберите „Кабловска“ ако су вам канали доступни преко добављача услуге кабловске телевизије. Ако нисте сигурни, тражиће се оба типа, али ово може да потраје дуже."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Антена"</item>
- <item msgid="2670079958754180142">"Кабловска"</item>
- <item msgid="36774059871728525">"Нисам сигуран/на"</item>
- <item msgid="6881204453182153978">"Само за програмирање"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Подешавање тјунера за ТВ"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Подешавање USB тјунера за канале"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Подешавање мрежног тјунера за канале"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Ово може да потраје неколико минута"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Тјунер привремено није доступан или се већ користи за снимање."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="one">Пронађен је %1$d канал</item>
- <item quantity="few">Пронађена су %1$d канала</item>
- <item quantity="other">Пронађено је %1$d канала</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"ЗАУСТАВИ ПРЕТРАГУ КАНАЛА"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="one">Пронађен је %1$d канал</item>
- <item quantity="few">Пронађена су %1$d канала</item>
- <item quantity="other">Пронађено је %1$d канала</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="one">Одлично! Пронађен је %1$d канал током претраге канала. Ако сматрате да је ово погрешно, покушајте да прилагодите положај антене и да поново обавите претрагу.</item>
- <item quantity="few">Одлично! Пронађена су %1$d канала током претраге канала. Ако сматрате да је ово погрешно, покушајте да прилагодите положај антене и да поново обавите претрагу.</item>
- <item quantity="other">Одлично! Пронађено је %1$d канала током претраге канала. Ако сматрате да је ово погрешно, покушајте да прилагодите положај антене и да поново обавите претрагу.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Готово"</item>
- <item msgid="2480490326672924828">"Претражи поново"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Није пронађен ниједан канал"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Претрагом није пронађен ниједан канал. Проверите да ли је ТВ повезан са извором ТВ сигнала.\n\nАко користите антену за сигнал преко мреже, прилагодите њен положај или смер. Ако желите најбоље резултате, поставите је високо и близу прозора, па поново обавите претрагу."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Претрагом није пронађен ниједан канал. Проверите да ли је USB тјунер прикључен и повезан са извором ТВ сигнала.\n\nАко користите антену за сигнал преко мреже, прилагодите њен положај или смер. Ако желите најбоље резултате, поставите је високо и близу прозора, па поново обавите претрагу."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Скенирањем није пронађен ниједан канал. Проверите да ли је мрежни тјунер укључен и повезан са извором ТВ сигнала.\n\nАко користите антену за сигнал преко мреже, прилагодите њен положај или смер. Ако желите најбоље резултате, поставите је високо и близу прозора, па поново обавите скенирање."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Претражи поново"</item>
- <item msgid="2092797862490235174">"Готово"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Потражите ТВ канале"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Подешавање тјунера за ТВ"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Подешавање USB тјунера за ТВ"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Подешавање мрежног тјунера за ТВ"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB тјунер за ТВ није прикључен."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Мрежни тјунер није прикључен."</string>
-</resources>
diff --git a/usbtuner-res/values-sv/strings.xml b/usbtuner-res/values-sv/strings.xml
deleted file mode 100644
index 81c37921..00000000
--- a/usbtuner-res/values-sv/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"TV-mottagare"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB-TV-mottagare"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Mottagare för TV över nätverket (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Vänta tills sökningen är klar"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Programvaran för mottagaren har nyligen uppdaterats. Sök igenom kanalerna igen."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Aktivera surroundljud under inställningarna för systemljud om du vill aktivera ljud"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Det går inte att spela upp ljud. Testa en annan TV"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Inställning av kanalmottagare"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Konfiguration av TV-mottagare"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Kanalinställning för USB-mottagare"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Konfiguration av nätverksmottagare"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Verifiera att TV:n är ansluten till en signalkälla på TV:n.\n\nOm du använder en antenn för over the air-uppdateringar justerar du dess placering eller riktning. Placera den högt upp och nära ett fönster och sök på nytt för bästa resultat."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Verifiera att USB-mottagaren är ansluten till en signalkälla på TV:n.\n\nOm du använder en antenn för over the air-uppdateringar kan du behöva justera dess placering eller riktning för att hitta så många kanaler som möjligt. Placera den högt upp och nära ett fönster för bästa resultat."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Verifiera att nätverksmottagaren är på och ansluten till en signalkälla på TV:n.\n\nOm du använder en antenn för over the air-uppdateringar justerar du dess placering eller riktning. Placera den högt upp och nära ett fönster och sök på nytt för bästa resultat."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Fortsätt"</item>
- <item msgid="727245208787621142">"Inte nu"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Vill du göra om kanalinställningen?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Kanalerna som hittats via TV-mottagaren tas bort och en ny kanalsökning startas.\n\nVerifiera att TV:n är ansluten till en signalkälla på TV:n.\n\nOm du använder en antenn för over the air-uppdateringar justerar du dess placering eller riktning. Placera den högt upp och nära ett fönster och sök på nytt för bästa resultat."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Kanalerna som hittats via USB-mottagaren tas bort och en ny kanalsökning startas.\n\nVerifiera att USB-mottagaren är ansluten till en signalkälla på TV:n.\n\nOm du använder en antenn för over the air-uppdateringar kan du behöva justera dess placering eller riktning för att hitta så många kanaler som möjligt. Placera den högt upp och nära ett fönster för bästa resultat."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Åtgärden tar bort alla kanaler som hittades av nätverksmottagaren och söker efter nya kanaler igen.\n\nVerifiera att nätverksmottagaren är på och ansluten till en signalkälla på TV:n.\n\nOm du använder en antenn för over the air-uppdateringar justerar du dess placering eller riktning. Placera den högt upp och nära ett fönster och sök på nytt för bästa resultat."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Fortsätt"</item>
- <item msgid="235450158666155406">"Avbryt"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Välj anslutningstyp"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Välj Antenn om det finns en extern antenn som är ansluten till mottagaren. Välj Kabel om kanalerna kommer från en kabeltjänstleverantör. Om du är osäker genomsöks båda typerna, men detta kan ta längre tid."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antenn"</item>
- <item msgid="2670079958754180142">"Kabel"</item>
- <item msgid="36774059871728525">"Inte säker"</item>
- <item msgid="6881204453182153978">"Endast för utveckling"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Konfiguration av TV-mottagare"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Kanalinställning för USB-mottagare"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Konfiguration av mottagare för nätverkskanaler"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Det här kan ta flera minuter"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Mottagaren är inte tillgänglig just nu eller så spelas andra program in med den."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d kanaler hittades</item>
- <item quantity="one">%1$d kanal hittades</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"STOPPA KANALSÖKNINGEN"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d kanaler hittades</item>
- <item quantity="one">%1$d kanal hittades</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Härligt! %1$d kanaler hittades vid kanalsökningen. Om det här inte verkar stämma kan du testa att justera antennens läge och söka igen.</item>
- <item quantity="one">Härligt! %1$d kanal hittades vid kanalsökningen. Om det här inte verkar stämma kan du testa att justera antennens läge och söka igen.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Klar"</item>
- <item msgid="2480490326672924828">"Sök igen"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Inga kanaler hittades"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Inga kanaler hittades vid sökningen. Verifiera att TV:n är ansluten till en signalkälla på TV:n.\n\nOm du använder en antenn för over the air-uppdateringar justerar du dess placering eller riktning. Placera den högt upp och nära ett fönster och sök på nytt för bästa resultat."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Inga kanaler hittades vid sökningen. Verifiera att USB-mottagaren är ansluten till en signalkälla på TV:n.\n\nOm du använder en antenn för over the air-uppdateringar justerar du dess placering eller riktning. Placera den högt upp och nära ett fönster och sök på nytt för bästa resultat."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Inga kanaler hittades under genomsökningen. Verifiera att nätverksmottagaren är på och ansluten till en signalkälla på TV:n.\n\nOm du använder en antenn för over the air-uppdateringar justerar du dess placering eller riktning. Placera den högt upp och nära ett fönster och sök på nytt för bästa resultat."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Sök igen"</item>
- <item msgid="2092797862490235174">"Klar"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Sök efter TV-kanaler"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Konfiguration av TV-mottagare"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Konfiguration av USB-ansluten TV-mottagare"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Konfiguration av nätverksansluten TV-mottagare"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB-TV-mottagaren har kopplats från."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Nätverksmottagaren har kopplats från."</string>
-</resources>
diff --git a/usbtuner-res/values-sw/strings.xml b/usbtuner-res/values-sw/strings.xml
deleted file mode 100644
index c81614c0..00000000
--- a/usbtuner-res/values-sw/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"Kitafutaji cha vituo vya TV"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"Kitafutaji cha Vituo vya TV cha USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Kitafuta Vituo vya TV ya Mtandao (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Tafadhali subiri ili shughuli imalizike"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Programu ya kitafutaji cha vituo cha USB ilisasishwa hivi majuzi. Tafadhali tafuta vituo tena."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Washa sauti ya mzunguko katika mipangilio ya mfumo wa sauti ili uruhusu sauti"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Haiwezi kucheza sauti. Tafadhali jaribu TV nyingine"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Kuweka mipangilio ya kitafutaji cha vituo"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Kuweka mipangilio ya kitafutaji cha vituo vya TV"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Kuweka mipangilio ya kitafutaji cha vituo cha USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Kuweka mipangilio ya kitafuta vituo vya TV"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Hakikisha TV yako imeunganishwa kwenye chanzo cha TV.\n\nIkiwa unatumia antena ya hewani, rekebisha jinsi ilivyowekwa au mwelekeo wake. Ili kupata matokeo bora zaidi, ipandishe juu na uiweke karibu na dirisha."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Thibitisha kuwa kitafutaji cha vituo cha USB kimechomekwa katika chanzo cha umeme na kuunganishwa katika chanzo cha mawimbi ya TV.\n\nKama unatumia antena ya hewani, rekebisha mkao wake au kule inakoelekea. Kwa matokeo bora zaidi, iweke juu , karibu na dirisha."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Thibitisha kuwa kitafuta vituo kimewashwa na kuunganishwa kwenye chanzo cha mawimbi ya TV. \n\nIkiwa unatumia antena ya hewani, huenda ukahitaji kurekebisha jinsi ilivyowekwa au inakoelekea, ili upate vituo vingi. Kwa matokeo bora zaidi, ipandishe juu na iwe karibu na dirisha."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Endelea"</item>
- <item msgid="727245208787621142">"Si sasa"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Ungependa kuweka mipangilio ya vituo upya?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Hatua hii itaondoa vituo vilivyopatikana kwenye kitafutaji cha vituo vya TV na kutafuta vituo vipya tena.\n\nHakikisha TV yako imeunganishwa kwenye chanzo cha mawimbi ya TV.\n\nIkiwa unatumia antena ya hewani, rekebisha jinsi ilivyowekwa au mwelekeo wake. Ili kupata matokeo bora zaidi, ipandishe juu na uiweke karibu na dirisha kisha utafute tena."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Hatua hii itaondoa vituo vilivyopatikana kwenye kitafutaji cha vituo cha USB na kutafuta vituo tena. \n\nThibitisha kuwa kitafutaji cha vituo cha USB kimechomekwa kwenye chanzo cha umeme na kuunganishwa katika chanzo cha mawimbi ya TV.\n\nKama unatumia antena ya hewani, rekebisha mkao wake au kule inakoelekea. Kwa matokeo bora zaidi, iweke juu, karibu na dirisha."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Hatua hii itaondoa vituo ulivyopata kwenye kitafuta vituo na kutafuta vituo vipya tena.\n\nThibitisha kwamba kitafuta vituo kimewashwa na kuunganishwa kwenye chanzo cha mawimbi ya TV.\n\nIkiwa unatumia antena ya hewani, rekebisha jinsi ilivyowekwa au inakoelekea, ili upate vituo vingi. Kwa matokeo bora zaidi, ipandishe juu na iwe karibu na dirisha."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Endelea"</item>
- <item msgid="235450158666155406">"Ghairi"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Chagua aina ya muunganisho"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Chagua Antena kama unatumia antena ya nje iliyounganishwa kwenye kitafuta vituo. Chagua Kebo kama vituo vyako vinapeperushwa na mtoa huduma za vifaa vinavyotumia kebo. Kama huna uhakika, aina zote zitatafutwa na huenda utafutaji ukachukua muda mrefu."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antena"</item>
- <item msgid="2670079958754180142">"Kebo"</item>
- <item msgid="36774059871728525">"Sina uhakika"</item>
- <item msgid="6881204453182153978">"Kusanidi pekee"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Kuweka mipangilio ya kitafutaji cha vituo vya TV"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Kuweka mipangilio ya kitafutaji cha vituo cha USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Kuweka mipangilio ya kitafuta vituo"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Shughuli hii inaweza kuchukua dakika kadhaa"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Kitafuta vituo hakipatikani kwa sasa au tayari kinatumiwa kurekodi."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">Vituo %1$d vimepatikana</item>
- <item quantity="one">Kituo %1$d kimepatikana</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"SIMAMISHA UTAFUTAJI WA VITUO"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">Vituo %1$d vimepatikana</item>
- <item quantity="one">Kituo %1$d kimepatikana</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Sadakta! Vituo %1$d vimepatikana baada ya kutafuta vituo. Kama hujaridhika, jaribu kurekebisha mkao wa antena kisha utafute tena.</item>
- <item quantity="one">Sadakta! Kituo %1$d kimepatikana baada ya kutafuta vituo. Kama hujaridhika, jaribu kurekebisha mkao wa antena kisha utafute tena.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Nimemaliza"</item>
- <item msgid="2480490326672924828">"Tafuta tena"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Hakuna Vituo vilivyopatikana"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Hakuna vituo vilivyopatikana baada ya kutafuta. Hakikisha TV yako imeunganishwa kwenye chanzo cha mawimbi ya TV.\n\nIkiwa unatumia antena ya hewani, rekebisha jinsi ilivyowekwa au mwelekeo wake. Ili kupata matokeo bora zaidi, ipandishe juu na uiweke karibu na dirisha kisha utafute tena."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Utafutaji haukupata vituo vyovyote. Thibitisha kuwa kitafutaji cha vituo cha USB kimechomekwa kwenye chanzo cha umeme na kuunganishwa katika chanzo cha mawimbi ya TV.\n\nKama unatumia antena ya hewani, rekebisha mkao wake au kule inakoelekea. Kwa matokeo bora zaidi, iweke juu, karibu na dirisha na utafute tena."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Haikupata vituo vyovyote. Thibitisha kwamba kitafuta vituo kimewashwa na kuunganishwa kwenye chanzo cha mawimbi ya TV.\n\nIkiwa unatumia antena ya hewani, rekebisha jinsi ilivyowekwa au inakoelekea. Ili upate matokeo bora zaidi, ipandishe juu na karibu na dirisha kisha utafute tena."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Tafuta tena"</item>
- <item msgid="2092797862490235174">"Nimemaliza"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Tafuta vituo vya TV"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Kuweka mipangilio ya kitafutaji cha vituo vya TV"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Kuweka mipangilio ya Kitafutaji cha Vituo vya TV kupitia USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Kuweka mipangilio ya Kitafutaji cha Vituo vya TV kupitia Mtandao"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"Umeondoa kichagua programu cha USB cha TV."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Umeondoa kitafuta mitandao."</string>
-</resources>
diff --git a/usbtuner-res/values-ta-rIN/strings.xml b/usbtuner-res/values-ta-rIN/strings.xml
deleted file mode 100644
index 1c71722e..00000000
--- a/usbtuner-res/values-ta-rIN/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"டிவி ட்யூனர்"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB டிவி ட்யூனர்"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"நெட்வொர்க் டிவி டியூனர் (பீட்டா)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"செயலாக்கம் முடியும் வரை காத்திருக்கவும்"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"ட்யூனர் மென்பொருள் சமீபத்தில் புதுப்பிக்கப்பட்டது. சேனல்களை மீண்டும் ஸ்கேன் செய்யவும்."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"ஆடியோவை இயக்க, சாதன ஒலி அமைப்புகளில் \"சரவுண்ட் சவுண்ட்\" என்பதை இயக்கவும்"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"ஆடியோவை இயக்க முடியவில்லை. வேறு டிவியைப் பயன்படுத்தவும்"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"சேனல் ட்யூனர் அமைவு"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"டிவி ட்யூனர் அமைவு"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB சேனல் ட்யூனர் அமைவு"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"நெட்வொர்க் டியூனர் அமைவு"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"டிவி சிக்னல் மூலத்துடன் உங்கள் டிவி இணைக்கப்பட்டுள்ளதைச் சரிபார்க்கவும்.\n\nஆன்டெனாவைப் பயன்படுத்தினால், அதிகமான சேனல்களைப் பெறுவதற்காக அதன் இடம் அல்லது திசையைச் சரிசெய்ய வேண்டியிருக்கலாம். இன்னும் சிறப்பான முடிவுகளுக்கு, அதை உயரமான இடத்தில் ஜன்னலுக்கு அருகே வைக்கவும்."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"USB ட்யூனர் செருகப்பட்டுள்ளதையும் டிவி சிக்னல் மூலத்துடன் இணைக்கப்பட்டுள்ளதையும் சரிபார்க்கவும்.\n\nஆன்டெனாவைப் பயன்படுத்தினால், அதிகமான சேனல்களைப் பெறுவதற்காக அதன் இடம் அல்லது திசையைச் சரிசெய்ய வேண்டியிருக்கலாம். இன்னும் சிறப்பான முடிவுகளுக்கு, அதை உயரமான இடத்தில் ஜன்னலுக்கு அருகே வைக்கவும்."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"நெட்வொர்க் டியூனர் ஆன் செய்யப்பட்டுள்ளதையும் டிவி சிக்னல் மூலத்துடன் இணைக்கப்பட்டுள்ளதையும் சரிபார்க்கவும்.\n\nஆன்டெனாவைப் பயன்படுத்தினால், அதிகமான சேனல்களைப் பெறுவதற்காக அதன் இடம் அல்லது திசையைச் சரிசெய்ய வேண்டியிருக்கலாம். இன்னும் சிறப்பான முடிவுகளுக்கு, அதை உயரமான இடத்தில் ஜன்னலுக்கு அருகே வைக்கவும்."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"தொடர்க"</item>
- <item msgid="727245208787621142">"இப்போது வேண்டாம்"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"சேனல் அமைவை மீண்டும் இயக்கவா?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"இவ்வாறு செய்வதால் டிவி ட்யூனர் மூலம் கண்டறிந்த சேனல்கள் அகற்றப்படுவதுடன், புதிய சேனல்களுக்காக மீண்டும் ஸ்கேன் செய்யும்.\n\nடிவி சிக்னல் மூலத்துடன் உங்கள் டிவி இணைக்கப்பட்டுள்ளதைச் சரிபார்க்கவும்.\n\nஆன்டெனாவைப் பயன்படுத்தினால், அதிகமான சேனல்களைப் பெறுவதற்காக அதன் இடம் அல்லது திசையைச் சரிசெய்ய வேண்டியிருக்கலாம். இன்னும் சிறப்பான முடிவுகளுக்கு, அதை உயரமான இடத்தில் ஜன்னலுக்கு அருகே வைக்கவும்."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"இவ்வாறு செய்வதால் USB ட்யூனர் மூலம் கண்டறிந்த சேனல்கள் அகற்றப்படுவதுடன், புதிய சேனல்களுக்காக மீண்டும் ஸ்கேன் செய்யும்.\n\nUSB ட்யூனர் செருகப்பட்டுள்ளதையும் டிவி சிக்னல் மூலத்துடன் இணைக்கப்பட்டுள்ளதையும் சரிபார்க்கவும்.\n\nஆன்டெனாவைப் பயன்படுத்தினால், அதிகமான சேனல்களைப் பெறுவதற்காக அதன் இடம் அல்லது திசையைச் சரிசெய்ய வேண்டியிருக்கலாம். இன்னும் சிறப்பான முடிவுகளுக்கு, அதை உயரமான இடத்தில் ஜன்னலுக்கு அருகே வைக்கவும்."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"இது நெட்வொர்க் டியூனரிலிருந்து கண்டறிந்த சேனல்களை அகற்றும் மற்றும் புதிய சேனல்களுக்காக மீண்டும் ஸ்கேன் செய்யும்.\n\nநெட்வொர்க் டியூனர் ஆன் செய்யப்பட்டுள்ளதையும் டிவி சிக்னல் மூலத்துடன் இணைக்கப்பட்டுள்ளதையும் சரிபார்க்கவும்.\n\nஆன்டெனாவைப் பயன்படுத்தினால், அதிகமான சேனல்களைப் பெறுவதற்காக, அதன் இடம் அல்லது திசையைச் சரிசெய்ய வேண்டியிருக்கலாம். இன்னும் சிறப்பான முடிவுகளுக்கு, உயரமான இடத்தில், ஜன்னலுக்கு அருகில் வைக்கவும்."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"தொடர்க"</item>
- <item msgid="235450158666155406">"ரத்துசெய்"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"இணைப்பு வகையைத் தேர்ந்தெடுக்கவும்"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"ட்யூனருடன் வெளிப்புற ஆன்டெனா இணைக்கப்பட்டிருந்தால், ஆன்டெனாவைத் தேர்வுசெய்யவும். கேபிள் சேவை வழங்குநரிடமிருந்து சேனல்கள் கிடைக்கின்றன என்றால், கேபிளைத் தேர்வுசெய்யவும். எதையும் தேர்வுசெய்யவில்லை எனில், இரு வகைகளிலும் ஸ்கேன் செய்யப்படும். ஆனால் இதற்கு அதிக நேரம் ஆகலாம்."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"ஆன்டெனா"</item>
- <item msgid="2670079958754180142">"கேபிள்"</item>
- <item msgid="36774059871728525">"நிச்சயமாகத் தெரியவில்லை"</item>
- <item msgid="6881204453182153978">"டெவெலப்மென்ட் மட்டும்"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"டிவி ட்யூனர் அமைவு"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB சேனல் ட்யூனர் அமைவு"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"நெட்வொர்க் சேனல் டியூனர் அமைவு"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"இதற்குச் சில நிமிடங்கள் ஆகலாம்"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"ட்யூனர் தற்காலிகமாகக் கிடைக்கவில்லை அல்லது ரெக்கார்டு செய்வதற்காக ஏற்கனவே பயன்படுத்தப்படுகிறது."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d சேனல்கள் கண்டறியப்பட்டன</item>
- <item quantity="one">%1$d சேனல் கண்டறியப்பட்டது</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"சேனலை ஸ்கேன் செய்வதை நிறுத்து"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d சேனல்கள் கண்டறியப்பட்டன</item>
- <item quantity="one">%1$d சேனல் கண்டறியப்பட்டது</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">அருமை! சேனலை ஸ்கேன் செய்யும் போது, %1$d சேனல்கள் கண்டறியப்பட்டன. இவை சரியாகத் தெரியவில்லை என்றால், ஆன்டெனாவின் நிலையை மாற்றி, மீண்டும் ஸ்கேன் செய்யவும்.</item>
- <item quantity="one">அருமை! சேனலை ஸ்கேன் செய்யும் போது, %1$d சேனல் கண்டறியப்பட்டது. இது சரியாகத் தெரியவில்லை என்றால், ஆன்டெனாவின் நிலையை மாற்றி, மீண்டும் ஸ்கேன் செய்யவும்.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"முடிந்தது"</item>
- <item msgid="2480490326672924828">"மீண்டும் ஸ்கேன் செய்"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"சேனல்கள் இல்லை"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"ஸ்கேன் செய்ததில் சேனல்கள் எவையும் கண்டறியப்படவில்லை. டிவி சிக்னல் மூலத்துடன் உங்கள் டிவி இணைக்கப்பட்டுள்ளதைச் சரிபார்க்கவும்.\n\nஆன்டெனாவைப் பயன்படுத்தினால், அதன் இடம் அல்லது திசையைச் சரிசெய்யவும். இன்னும் சிறப்பான முடிவுகளுக்கு, அதை உயரமான இடத்தில் ஜன்னலுக்கு அருகே வைத்து, மீண்டும் ஸ்கேன் செய்யவும்."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"ஸ்கேன் செய்ததில் சேனல்கள் எவையும் கண்டறியப்படவில்லை. USB ட்யூனர் செருகப்பட்டுள்ளதையும் டிவி சிக்னல் மூலத்துடன் இணைக்கப்பட்டுள்ளதையும் சரிபார்க்கவும்.\n\nஆன்டெனாவைப் பயன்படுத்தினால், அதன் இடம் அல்லது திசையைச் சரிசெய்யவும். இன்னும் சிறப்பான முடிவுகளுக்கு, அதை உயரமான இடத்தில் ஜன்னலுக்கு அருகே வைத்து, மீண்டும் ஸ்கேன் செய்யவும்."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"ஸ்கேன் செய்ததில் சேனல்கள் எதுவும் கண்டறியப்படவில்லை. நெட்வொர்க் டியூனர் ஆன் செய்யப்பட்டுள்ளதையும் டிவி சிக்னல் மூலத்துடன் இணைக்கப்பட்டுள்ளதையும் சரிபார்க்கவும்.\n\nஆன்டெனாவைப் பயன்படுத்தினால், அதன் இடம் அல்லது திசையைச் சரிசெய்யவும். இன்னும் சிறப்பான முடிவுகளுக்கு, அதை உயரமான இடத்தில் ஜன்னலுக்கு அருகே வைத்து, மீண்டும் ஸ்கேன் செய்யவும்."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"மீண்டும் ஸ்கேன் செய்"</item>
- <item msgid="2092797862490235174">"முடிந்தது"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"டிவி சேனல்களைத் தேடுக"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"டிவி டியூனர் அமைவு"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB டிவி டியூனர் அமைவு"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"நெட்வொர்க் டிவி டியூனர் அமைவு"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB டிவி ட்யூனர் துண்டிக்கப்பட்டது."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"நெட்வொர்க் ட்யூனர் துண்டிக்கப்பட்டது."</string>
-</resources>
diff --git a/usbtuner-res/values-te-rIN/strings.xml b/usbtuner-res/values-te-rIN/strings.xml
deleted file mode 100644
index fb6bcf37..00000000
--- a/usbtuner-res/values-te-rIN/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"టీవీ ట్యూనర్"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB టీవీ ట్యూనర్"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"నెట్‌వర్క్ టీవీ ట్యూనర్ (బీటా)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"దయచేసి ప్రాసెస్ చేయడం పూర్తయ్యే వరకు వేచి ఉండండి"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"ట్యూనర్ సాఫ్ట్‌వేర్ ఇటీవల నవీకరించబడింది. దయచేసి ఛానెల్‌లను మళ్లీ స్కాన్ చేయండి."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"ఆడియోను ప్రారంభించడానికి సిస్టమ్ శబ్ద సెట్టింగ్‌ల్లో పరిసర వ్యాప్త శబ్దాన్ని ప్రారంభించండి"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"ఆడియోను ప్లే చేయడం సాధ్యపడదు. దయచేసి మరో టీవీలో ప్రయత్నించండి"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"ఛానెల్ ట్యూనర్ సెటప్"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"టీవీ ట్యూనర్ సెటప్"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB ఛానెల్ ట్యూనర్ సెటప్"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"నెట్‍వర్క్ ట్యూనర్ సెటప్"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"టీవీ.సిగ్నల్ సోర్స్‌కు మీ టీవీ కనెక్ట్ చేయబడిందని ధృవపరుచుకోండి.\n\nప్రసారాల కోసం యాంటెన్నాను ఉపయోగిస్తుంటే, మీరు మరిన్ని ఛానెల్‌లను స్వీకరించడానికి దాని స్థానాన్ని లేదా దిశను మార్చాల్సి రావచ్చు. ఉత్తమ ఫలితాల కోసం, దాన్ని ఎత్తులో కిటికీకి దగ్గరగా ఉంచండి."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"USB ట్యూనర్ ప్లగిన్ చేయబడి, టీవీ సిగ్నల్ సోర్స్‌కు కనెక్ట్ చేయబడినట్లు ధృవపరుచుకోండి.\n\nప్రసారాల కోసం యాంటెన్నాను ఉపయోగిస్తుంటే, మీరు మరిన్ని ఛానెల్‌లను స్వీకరించడానికి దాని స్థానాన్ని లేదా దిశను మార్చాల్సి రావచ్చు. ఉత్తమ ఫలితాల కోసం, దాన్ని ఎత్తులో కిటికీకి దగ్గరగా ఉంచండి."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"నెట్‍వర్క్ ట్యూనర్ పవర్ ఆన్ చేయబడి, టీవీ సిగ్నల్ సోర్స్‌కు కనెక్ట్ చేయబడినట్లు ధృవపరుచుకోండి.\n\nప్రసారాల కోసం యాంటెన్నాను ఉపయోగిస్తుంటే, మీరు మరిన్ని ఛానెల్‌లను స్వీకరించడానికి దాని స్థానాన్ని లేదా దిశను మార్చాల్సి రావచ్చు. ఉత్తమ ఫలితాల కోసం, దాన్ని ఎత్తులో కిటికీకి దగ్గరగా ఉంచండి."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"కొనసాగించు"</item>
- <item msgid="727245208787621142">"ఇప్పుడు కాదు"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"ఛానెల్ సెటప్‌ను మళ్లీ అమలు చేయాలా?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"ఇది టీవీ ట్యూనర్ నుండి కనుగొన్న ఛానెల్‌లను తీసివేస్తుంది మరియు మళ్లీ కొత్త ఛానెల్‌ల కోసం స్కాన్ చేస్తుంది.\n\nటీవీ సిగ్నల్ సోర్స్‌కు మీ టీవీ కనెక్ట్ చేయబడిందని ధృవపరుచుకోండి.\n\nప్రసారాల కోసం యాంటెన్నాను ఉపయోగిస్తుంటే, మీరు మరిన్ని ఛానెల్‌లను స్వీకరించడానికి దాని స్థానాన్ని లేదా దిశను మార్చాల్సి రావచ్చు. ఉత్తమ ఫలితాల కోసం, దాన్ని ఎత్తులో కిటికీకి దగ్గరగా ఉంచండి."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"ఇది USB ట్యూనర్ నుండి కనుగొన్న ఛానెల్‌లను తీసివేస్తుంది మరియు మళ్లీ కొత్త ఛానెల్‌ల కోసం స్కాన్ చేస్తుంది.\n\nUSB ట్యూనర్ ప్లగిన్ చేయబడి, టీవీ సిగ్నల్ సోర్స్‌కు కనెక్ట్ చేయబడిందని ధృవపరుచుకోండి.\n\nప్రసారాల కోసం యాంటెన్నాను ఉపయోగిస్తుంటే, మీరు మరిన్ని ఛానెల్‌లను స్వీకరించడానికి దాని స్థానాన్ని లేదా దిశను మార్చాల్సి రావచ్చు. ఉత్తమ ఫలితాల కోసం, దాన్ని ఎత్తులో కిటికీకి దగ్గరగా ఉంచండి."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"ఇది నెట్‍వర్క్ ట్యూనర్ నుండి కనుగొన్న ఛానెల్‌లను తీసివేస్తుంది మరియు మళ్లీ కొత్త ఛానెల్‌ల కోసం స్కాన్ చేస్తుంది.\n\nనెట్‍వర్క్ ట్యూనర్ పవర్ ఆన్ చేయబడి, టీవీ సిగ్నల్ సోర్స్‌కు కనెక్ట్ చేయబడిందని ధృవపరుచుకోండి.\n\nప్రసారాల కోసం యాంటెన్నాను ఉపయోగిస్తుంటే, మీరు మరిన్ని ఛానెల్‌లను స్వీకరించడానికి దాని స్థానాన్ని లేదా దిశను మార్చాలి. ఉత్తమ ఫలితాల కోసం, దాన్ని ఎత్తులో కిటికీకి దగ్గరగా ఉంచండి."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"కొనసాగించు"</item>
- <item msgid="235450158666155406">"రద్దు చేయి"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"కనెక్షన్ రకాన్ని ఎంచుకోండి"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"ట్యూనర్‌కు బాహ్య యాంటెన్నా కనెక్ట్ చేసి ఉంటే యాంటెన్నాను ఎంచుకోండి. మీ ఛానెల్‌‍‍లను కేబుల్ సేవా ప్రదాత అందిస్తుంటే, కేబుల్‌ను ఎంచుకోండి. మీకు ఏ సంగతి ఖచ్చితంగా తెలియకుంటే, రెండు రకాలు స్కాన్ చేయబడతాయి, కానీ దీనికి ఎక్కువ సమయం పట్టవచ్చు."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"యాంటెన్నా"</item>
- <item msgid="2670079958754180142">"కేబుల్"</item>
- <item msgid="36774059871728525">"అంత ఖచ్చితంగా తెలియదు"</item>
- <item msgid="6881204453182153978">"అభివృద్ధి మాత్రమే"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"టీవీ ట్యూనర్ సెటప్"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB ఛానెల్ ట్యూనర్ సెటప్"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"నెట్‍వర్క్ ఛానెల్ ట్యూనర్ సెటప్"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"దీనికి కొన్ని నిమిషాలు పట్టవచ్చు"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"ట్యూనర్ తాత్కాలికంగా అందుబాటులో లేదు లేదా ఇప్పటికే రికార్డింగ్ ద్వారా ఉపయోగించబడుతోంది."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d ఛానెల్‌లు కనుగొనబడ్డాయి</item>
- <item quantity="one">%1$d ఛానెల్ కనుగొనబడింది</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"ఛానెల్ స్కాన్‌ను ఆపివేయి"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d ఛానెల్‌లు కనుగొనబడ్డాయి</item>
- <item quantity="one">%1$d ఛానెల్ కనుగొనబడింది</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">మంచిది! ఛానెల్ స్కాన్‌లో %1$d ఛానెల్‌లు కనుగొనబడ్డాయి. ఇది సరైనదిగా అనిపించకుంటే, యాంటెన్నా స్థానం సర్దుబాటు చేసి, ఆపై మళ్లీ స్కాన్ చేయడం ప్రయత్నించండి.</item>
- <item quantity="one">మంచిది! ఛానెల్ స్కాన్‌లో %1$d ఛానెల్ కనుగొనబడింది. ఇది సరైనదిగా అనిపించకుంటే, యాంటెన్నా స్థానం సర్దుబాటు చేసి, ఆపై మళ్లీ స్కాన్ చేయడం ప్రయత్నించండి.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"పూర్తయింది"</item>
- <item msgid="2480490326672924828">"మళ్లీ స్కాన్ చేయి"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"ఛానెల్‌లు ఏవీ కనుగొనబడలేదు"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"స్కాన్‌లో ఏ ఛానెల్ కనుగొనబడలేదు. టీవీ సిగ్నల్ సోర్స్‌కి మీ టీవీ కనెక్ట్ చేయబడిందని ధృవపరుచుకోండి. \n\nప్రసారాల కోసం యాంటెన్నాను ఉపయోగిస్తుంటే, దాని స్థానాన్ని లేదా దిశను సర్దుబాటు చేయండి. ఉత్తమ ఫలితాల కోసం, దాన్ని ఎత్తులో కిటికీకి దగ్గరగా ఉంచి, ఆపై మళ్లీ స్కాన్ చేయండి."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"స్కాన్‌లో ఛానెల్‌లు ఏవీ కనుగొనబడలేదు. USB ట్యూనర్ ప్లగిన్ చేయబడి, టీవీ సిగ్నల్ సోర్స్‌కు కనెక్ట్ చేయబడిందని ధృవపరుచుకోండి.\n\nప్రసారాల కోసం యాంటెన్నాను ఉపయోగిస్తుంటే, దాని స్థానాన్ని లేదా దిశను సర్దుబాటు చేయండి. ఉత్తమ ఫలితాల కోసం, దాన్ని ఎత్తులో కిటికీకి దగ్గరగా ఉంచి, ఆపై మళ్లీ స్కాన్ చేయండి."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"స్కాన్‌లో ఛానెల్‌లు ఏవీ కనుగొనబడలేదు. నెట్‍వర్క్ ట్యూనర్ పవర్ ఆన్ చేయబడి, టీవీ సిగ్నల్ సోర్స్‌కు కనెక్ట్ చేయబడిందని ధృవపరుచుకోండి.\n\nప్రసారాల కోసం యాంటెన్నాను ఉపయోగిస్తుంటే, దాని స్థానాన్ని లేదా దిశను సర్దుబాటు చేయండి. ఉత్తమ ఫలితాల కోసం, దాన్ని ఎత్తులో కిటికీకి దగ్గరగా ఉంచి, ఆపై మళ్లీ స్కాన్ చేయండి."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"మళ్లీ స్కాన్ చేయి"</item>
- <item msgid="2092797862490235174">"పూర్తయింది"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"టీవీ ఛానెల్‌ల కోసం స్కాన్ చేయండి"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"టీవీ ట్యూనర్ సెటప్"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB టీవీ ట్యూనర్ సెటప్"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"నెట్‍వర్క్ టీవీ ట్యూనర్ సెటప్"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB టీవీ ట్యూనర్ డిస్‌కనెక్ట్ చేయబడింది."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"నెట్‌వర్క్ ట్యూనర్ డిస్‌కనెక్ట్ చేయబడింది."</string>
-</resources>
diff --git a/usbtuner-res/values-th/strings.xml b/usbtuner-res/values-th/strings.xml
deleted file mode 100644
index 703c0f02..00000000
--- a/usbtuner-res/values-th/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"ตัวรับสัญญาณทีวี"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"ตัวรับสัญญาณทีวีแบบ USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"ตัวรับสัญญาณทีวีเครือข่าย (เบต้า)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"โปรดรอให้การดำเนินการหยุดลงสักครู่"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"ซอฟต์แวร์ตัวรับสัญญาณมีการอัปเดตเมื่อเร็วๆ นี้ โปรดสแกนช่องอีกครั้ง"</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"เปิดใช้เสียงเซอร์ราวด์ในการตั้งค่าเสียงของระบบเพื่อเปิดใช้เสียง"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"เล่นเสียงไม่ได้ โปรดลองทีวีเครื่องอื่น"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"ตั้งค่าตัวรับสัญญาณช่อง"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"ตั้งค่าตัวรับสัญญาณทีวี"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"ตั้งค่าตัวรับสัญญาณช่องแบบ USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"ตั้งค่าตัวรับสัญญาณเครือข่าย"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"โปรดตรวจสอบว่าทีวีของคุณเชื่อมต่อกับแหล่งสัญญาณทีวีแล้ว\n\nหากใช้เสาอากาศแบบผ่านอากาศ คุณอาจต้องปรับตำแหน่งและทิศทางเพื่อรับช่องให้ได้มากที่สุด เพื่อผลลัพธ์ที่ดีที่สุด ให้วางไว้บนที่สูงใกล้หน้าต่าง"</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"โปรดตรวจสอบว่าได้เสียบปลั๊กตัวรับสัญญาณแบบ USB และเชื่อมต่อกับแหล่งสัญญาณทีวีแล้ว\n\nหากใช้เสาอากาศแบบผ่านอากาศ คุณอาจต้องปรับตำแหน่งหรือทิศทางเพื่อรับช่องให้ได้มากที่สุด เพื่อผลลัพธ์ที่ดีที่สุด ให้วางไว้บนที่สูงใกล้หน้าต่าง"</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"โปรดตรวจสอบว่าตัวรับสัญญาณเครือข่ายเปิดอยู่และเชื่อมต่อกับแหล่งสัญญาณทีวีแล้ว\n\nหากใช้เสาอากาศแบบผ่านอากาศ (OTA) คุณอาจต้องปรับตำแหน่งหรือทิศทางเพื่อรับช่องให้ได้มากที่สุด เพื่อผลลัพธ์ที่ดีที่สุด ให้วางไว้บนที่สูงใกล้หน้าต่าง"</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"ต่อไป"</item>
- <item msgid="727245208787621142">"ข้ามไปก่อน"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"ต้องการเริ่มการตั้งค่าช่องอีกครั้งใช่ไหม"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"วิธีนี้จะนำช่องที่พบจากตัวรับสัญญาณทีวีออกและสแกนหาช่องใหม่อีกครั้ง\n\nโปรดตรวจสอบว่าทีวีของคุณเชื่อมต่อกับแหล่งสัญญาณทีวีแล้ว\n\nหากใช้เสาอากาศแบบผ่านอากาศ คุณอาจต้องปรับตำแหน่งและทิศทางเพื่อรับช่องให้ได้มากที่สุด เพื่อผลลัพธ์ที่ดีที่สุด ให้วางไว้บนที่สูงใกล้หน้าต่าง"</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"วิธีนี้จะนำช่องที่พบจากตัวรับสัญญาณแบบ USB ออกและสแกนหาช่องใหม่อีกครั้ง\n\nโปรดตรวจสอบว่าได้เสียบปลั๊กตัวรับสัญญาณแบบ USB และเชื่อมต่อกับแหล่งสัญญาณทีวีแล้ว\n\nหากใช้เสาอากาศแบบผ่านอากาศ คุณอาจต้องปรับตำแหน่งหรือทิศทางเพื่อรับช่องให้ได้มากที่สุด เพื่อผลลัพธ์ที่ดีที่สุด ให้วางไว้บนที่สูงใกล้หน้าต่าง"</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"การดำเนินการนี้จะนำช่องที่พบจากตัวรับสัญญาณเครือข่ายออกแล้วสแกนหาช่องใหม่อีกครั้ง\n\nโปรดตรวจสอบว่าตัวรับสัญญาณเครือข่ายเปิดอยู่และเชื่อมต่อกับแหล่งสัญญาณทีวีแล้ว\n\nหากใช้เสาอากาศแบบผ่านอากาศ (OTA) คุณอาจต้องปรับตำแหน่งหรือทิศทางเพื่อรับช่องให้ได้มากที่สุด เพื่อผลลัพธ์ที่ดีที่สุด ให้วางไว้บนที่สูงใกล้หน้าต่าง"</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"ต่อไป"</item>
- <item msgid="235450158666155406">"ยกเลิก"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"เลือกประเภทการเชื่อมต่อ"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"เลือกเสาอากาศหากมีการเชื่อมต่อเสาอากาศภายนอกกับตัวรับสัญญาณ เลือกเคเบิลหากช่องของคุณมาจากผู้ให้บริการเคเบิล หากคุณไม่แน่ใจ ระบบจะสแกนทั้ง 2 แบบซึ่งอาจใช้เวลานานขึ้น"</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"เสาอากาศ"</item>
- <item msgid="2670079958754180142">"เคเบิล"</item>
- <item msgid="36774059871728525">"ไม่แน่ใจ"</item>
- <item msgid="6881204453182153978">"เฉพาะการพัฒนาเท่านั้น"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"ตั้งค่าตัวรับสัญญาณทีวี"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"ตั้งค่าตัวรับสัญญาณช่องแบบ USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"ตั้งค่าตัวรับสัญญาณช่องเครือข่าย"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"อาจใช้เวลาหลายนาที"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"ตัวรับสัญญาณไม่สามารถใช้ได้ชั่วคราว หรือถูกใช้ในการบันทึกแล้ว"</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">พบ %1$d ช่อง</item>
- <item quantity="one">พบ %1$d ช่อง</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"หยุดการสแกนช่อง"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">พบ %1$d ช่อง</item>
- <item quantity="one">พบ %1$d ช่อง</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">เยี่ยมมาก! สแกนพบ %1$d ช่อง หากคุณคิดว่าไม่ถูกต้อง ให้ลองปรับตำแหน่งเสาอากาศแล้วสแกนอีกครั้ง</item>
- <item quantity="one">เยี่ยมมาก! สแกนพบ %1$d ช่อง หากคุณคิดว่าไม่ถูกต้อง ให้ลองปรับตำแหน่งเสาอากาศแล้วสแกนอีกครั้ง</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"เสร็จสิ้น"</item>
- <item msgid="2480490326672924828">"สแกนอีกครั้ง"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"ไม่พบช่อง"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"สแกนไม่พบช่องใดเลย โปรดตรวจสอบว่าทีวีของคุณเชื่อมต่อกับแหล่งสัญญาณทีวีแล้ว\n\nหากคุณใช้เสาอากาศแบบผ่านอากาศ ให้ปรับตำแหน่งและทิศทาง เพื่อผลลัพธ์ที่ดีที่สุด ให้วางไว้บนที่สูงใกล้หน้าต่างแล้วสแกนอีกครั้ง"</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"สแกนไม่พบช่องใดเลย โปรดตรวจสอบว่าได้เสียบปลั๊กตัวรับสัญญาณแบบ USB และเชื่อมต่อกับแหล่งสัญญาณทีวีแล้ว\n\nหากคุณใช้เสาอากาศแบบผ่านอากาศ ให้ปรับตำแหน่งหรือทิศทาง เพื่อให้ได้ผลลัพธ์ที่ดีที่สุด ให้วางไว้บนที่สูงใกล้หน้าต่างแล้วสแกนอีกครั้ง"</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"สแกนไม่พบช่องใดเลย โปรดตรวจสอบว่าตัวรับสัญญาณเครือข่ายเปิดอยู่และเชื่อมต่อกับแหล่งสัญญาณทีวีแล้ว\n\nหากใช้เสาอากาศแบบผ่านอากาศ (OTA) ให้ปรับตำแหน่งหรือทิศทาง เพื่อผลลัพธ์ที่ดีที่สุด ให้วางไว้บนที่สูงใกล้หน้าต่างแล้วสแกนอีกครั้ง"</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"สแกนอีกครั้ง"</item>
- <item msgid="2092797862490235174">"เสร็จสิ้น"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"สแกนหาช่องทีวี"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"ตั้งค่าตัวรับสัญญาณทีวี"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"ตั้งค่าตัวรับสัญญาณทีวีแบบ USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"ตั้งค่าตัวรับสัญญาณทีวีเครือข่าย"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"ยกเลิกการเชื่อมต่อตัวรับสัญญาณทีวีผ่าน USB แล้ว"</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"ยกเลิกการเชื่อมต่อตัวรับสัญญาณเครือข่ายแล้ว"</string>
-</resources>
diff --git a/usbtuner-res/values-tl/strings.xml b/usbtuner-res/values-tl/strings.xml
deleted file mode 100644
index 52c99eac..00000000
--- a/usbtuner-res/values-tl/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"TV Tuner"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB TV Tuner"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Network TV Tuner (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Mangyaring maghintay na matapos ang pagpoproseso"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Na-update kamakailan ang software ng tuner. Paki-scan muli ang mga channel."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"I-enable ang surround sound sa mga setting ng tunog ng system upang ma-enable ang audio"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Hindi ma-play ang audio. Mangyaring sumubok ng ibang TV"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Setup ng channel tuner"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Setup ng TV Tuner"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Setup ng USB channel tuner"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Pag-set up ng network tuner"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"I-verify na nakakonekta ang iyong TV sa isang TV signal source.\n\nKung gumagamit ng over-the-air antenna, maaaring kailanganin mong ayusin ang pagkakalagay o direksyon nito upang makuha ang pinakamaraming channel. Para sa mga pinakamainam na resulta, ilagay ito sa mataas na lugar at malapit sa isang bintana."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"I-verify na nakasaksak at nakakonekta ang USB tuner sa isang TV source signal.\n\nKung gumagamit ka ng isang over-the-air antenna, maaaring kailanganin mong ayusin ang pagkakalagay o direksyon nito upang makuha ang pinakamaraming channel. Para sa mga pinakamainam na resulta, ilagay ito sa isang mataas na lugar at malapit sa isang bintana."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"I-verify na naka-on ang network tuner at nakakonekta sa isang pinagmumulan ng TV signal.\n\nKung gumagamit ng over-the-air na antenna, maaaring kailanganin mong ayusin ang pagkakalagay o direksyon nito upang masagap ang karamihan ng mga channel. Para sa mga pinakamainam na resulta, ilagay ito sa mataas na lugar at malapit sa bintana."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Magpatuloy"</item>
- <item msgid="727245208787621142">"Hindi ngayon"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Gusto mo bang muling patakbuhin ang pag-set up ng channel?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Aalisin nito ang mga nakitang channel mula sa TV tuner at mag-scan muli para sa mga bagong channel.\n\nI-verify na nakakonekta ang iyong TV sa isang TV signal source.\n\nKung gumagamit ka ng over-the-air antenna, maaaring kailanganin mong ayusin ang pagkakalagay o direksyon nito upang makuha ang pinakamaraming channel. Para sa mga pinakamainam na resulta, ilagay ito sa mataas na lugar at malapit sa isang bintana."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Aalisin nito ang mga channel na nahanap mula sa USB tuner at mag-scan muli para sa mga bagong channel.\n\nI-verify na nakasaksak at nakakonekta ang USB tuner sa isang TV source signal.\n\nKung gumagamit ng over-the-air antenna, maaaring kailanganin mong ayusin ang pagkakalagay o direksyon nito upang makuha ang pinakamaraming channel. Para sa mga pinakamainam na resulta, ilagay ito sa isang mataas na lugar at malapit sa isang bintana."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Aalisin nito ang mga nahanap na channel sa network tuner at muling magsa-scan ng mga bagong channel.\n\nI-verify na naka-on ang network tuner at nakakonekta sa isang pinagmumulan ng TV signal.\n\nKung gumagamit ng over-the-air na antenna, maaaring kailanganin mong ayusin ang pagkakalagay o direksyon nito upang masagap ang karamihan ng mga channel. Para sa mga pinakamainam na resulta, ilagay ito sa mataas na lugar at malapit sa bintana."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Magpatuloy"</item>
- <item msgid="235450158666155406">"Kanselahin"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Piliin ang uri ng koneksyon"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Piliin ang Antenna kung may nakakonektang external na antenna sa tuner. Piliin ang Cable kung nanggagaling ang iyong mga channel sa isang service provider ng cable. Kung hindi ka sigurado, iii-scan ang parehong nabanggit na uri, ngunit maaaring mas magtagal ito."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antenna"</item>
- <item msgid="2670079958754180142">"Cable"</item>
- <item msgid="36774059871728525">"Hindi sigurado"</item>
- <item msgid="6881204453182153978">"Development lang"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Setup ng TV tuner"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Setup ng USB channel tuner"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Pag-set up ng network channel tuner"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Maaari itong tumagal ng ilang minuto"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Pansamantalang hindi available ang Tuner o ginagamit na ito ng recording."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="one">Nakahanap ng %1$d channel</item>
- <item quantity="other">Nakahanap ng %1$d na channel</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"IHINTO ANG PAG-SCAN NG CHANNEL"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="one">Nakahanap ng %1$d channel</item>
- <item quantity="other">Nakahanap ng %1$d na channel</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="one">Ayos! Nakahanap ng %1$d channel noong nag-ii-scan ng channel. Kung sa palagay mo ay kulang pa ito, subukang ayusin ang posisyon ng antenna at muling mag-scan.</item>
- <item quantity="other">Ayos! Nakahanap ng %1$d na channel noong nag-ii-scan ng channel. Kung sa palagay mo ay kulang pa ito, subukang ayusin ang posisyon ng antenna at muling mag-scan.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Tapos Na"</item>
- <item msgid="2480490326672924828">"Muling mag-scan"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Walang nakitang Mga Channel"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Hindi nakahanap ng anumang mga channel ang pag-scan. I-verify na nakakonekta ang iyong TV sa isang TV signal source.\n\nKung gumagamit ka ng over-the-air antenna, ayusin ang pagkakalagay o direksyon nito. Para sa mga pinakamainam na resulta, ilagay ito sa mataas na lugar malapit sa isang bintana at mag-scan muli."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Walang nahanap na anumang mga channel noong nag-scan. I-verify na nakasaksak at nakakonekta ang USB tuner sa isang TV signal source.\n\nKung gumagamit ka ng over-the-air antenna, ayusin ang pagkakalagay o direksyon nito. Para sa mga pinakamainam na resulta, ilagay ito sa isang mataas na lugar at malapit sa isang bintana mag-scan muli."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Walang nahanap na channel sa pag-scan. I-verify na naka-on ang network tuner at nakakonekta sa isang pinagmumulan ng TV signal.\n\nKung gumagamit ng over-the-air na antenna, ayusin ang pagkakalagay o direksyon nito. Para sa mga pinakamainam na resulta, ilagay ito sa mataas na lugar at malapit sa bintana at muling mag-scan."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Muling mag-scan"</item>
- <item msgid="2092797862490235174">"Tapos Na"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Mag-scan ng mga channel sa TV"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Setup ng TV Tuner"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Setup ng USB TV Tuner"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Setup ng Network TV Tuner"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"Nadiskonekta ang USB TV tuner."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Nadiskonekta ang network tuner."</string>
-</resources>
diff --git a/usbtuner-res/values-tr/strings.xml b/usbtuner-res/values-tr/strings.xml
deleted file mode 100644
index 5196bfa3..00000000
--- a/usbtuner-res/values-tr/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"TV Kanal Ayarlayıcı"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB TV Kanal Ayarlayıcı"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Ağ TV Kanal Ayarlayıcı (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Lütfen işlemin tamamlanmasını bekleyin"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Kanal ayarlayıcı yazılımı yakın zamanda güncellendi. Lütfen kanalları yeniden tarayın."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Sesi etkinleştirmek için sistemin ses ayarlarında surround sesi etkinleştirin"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Ses çalınamıyor. Lütfen başka bir TV deneyin"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Kanal ayarlayıcı kurulumu"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"TV Kanal Ayarlayıcı kurulumu"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB kanal ayarlayıcı kurulumu"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Ağ kanal ayarlayıcı kurulumu"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"TV\'nizin bir TV sinyal kaynağına bağlı olduğunu doğrulayın.\n\nHavadan yayın alan bir anten kullanıyorsanız en çok sayıda kanalı almak için antenin yerini veya yönünü ayarlamanız gerekebilir. En iyi sonucu elde etmek için anteni yüksek ve pencereye yakın bir yere yerleştirin."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"USB kanal ayarlayıcının takılı olduğunu ve bir TV sinyali kaynağına bağlandığını doğrulayın.\n\nHavadan yayın alan bir anten kullanıyorsanız en çok sayıda kanalı almak için antenin yerini veya yönünü ayarlamanız gerekebilir. En iyi sonucu elde etmek için anteni yüksek ve pencereye yakın bir yere yerleştirin."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Ağ kanal ayarlayıcının açık olduğunu ve TV sinyali alabileceği bir kaynağa bağlandığını doğrulayın.\n\nHavadan gelen yayını alacak bir anten kullanıyorsanız en fazla sayıda kanalı almak için antenin yerini veya yönünü ayarlamanız gerekebilir. En iyi sonuçları elde etmek için anteni yüksek ve pencereye yakın bir yere yerleştirin."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Devam"</item>
- <item msgid="727245208787621142">"Şimdi değil"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Kanal kurulumu yeniden çalıştırılsın mı?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Bu işlem, TV kanal ayarlayıcının bulduğu kanalları kaldıracak ve tekrar yeni kanallar arayacak.\n\nTV\'nizin bir TV sinyal kaynağına bağlı olduğunu doğrulayın.\n\nHavadan yayın alan bir anten kullanıyorsanız en çok sayıda kanalı almak için antenin yerini veya yönünü ayarlamanız gerekebilir. En iyi sonucu elde etmek için anteni yüksek ve pencereye yakın bir yere yerleştirin."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Bu işlem, USB kanal ayarlayıcıdan bulunan kanalları kaldıracak ve yeni kanallar için tekrar tarama yapacaktır.\n\nUSB kanal ayarlayıcının takılı olduğunu ve bir TV sinyali kaynağına bağlandığını doğrulayın.\n\nHavadan yayın alan bir anten kullanıyorsanız en çok sayıda kanalı almak için antenin yerini veya yönünü ayarlamanız gerekebilir. En iyi sonucu elde etmek için anteni yüksek ve pencereye yakın bir yere yerleştirin."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Bu işlem, bulunan kanalları ağ kanal ayarlayıcıdan kaldıracak ve yeni kanalların bulunması için tekrar tarama yapacak.\n\nAğ kanal ayarlayıcının açık olduğunu ve TV sinyali alabileceği bir kaynağa bağlandığını doğrulayın.\n\nHavadan gelen yayını alacak bir anten kullanıyorsanız en fazla sayıda kanalı almak için antenin yerini veya yönünü ayarlamanız gerekebilir. En iyi sonucu elde etmek için anteni yüksek ve pencereye yakın bir yere yerleştirin."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Devam"</item>
- <item msgid="235450158666155406">"İptal"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Bağlantı türünü seçin"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Kanal ayarlayıcıya bağlı harici bir anteniniz varsa Anten\'i seçin. Kanallarınız bir kablolu yayın hizmeti sağlayıcısından geliyorsa Kablolu\'yu seçin. Emin değilseniz her iki tür de taranacaktır, ancak işlem daha uzun sürebilir."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Anten"</item>
- <item msgid="2670079958754180142">"Kablolu"</item>
- <item msgid="36774059871728525">"Emin değilim"</item>
- <item msgid="6881204453182153978">"Yalnızca geliştirme"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"TV kanal ayarlayıcı kurulumu"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB kanal ayarlayıcı kurulumu"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Ağ kanal ayarlayıcı kurulumu"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Bu işlem birkaç dakika sürebilir"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Kanal ayarlayıcı geçici olarak kullanılamıyor veya şu anda kayıt yapmak için kullanılıyor."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d kanal bulundu</item>
- <item quantity="one">%1$d kanal bulundu</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"KANAL TARAMASINI DURDUR"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d kanal bulundu</item>
- <item quantity="one">%1$d kanal bulundu</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Başarılı! Kanal tarama sırasında %1$d kanal bulundu. Bu sonuç doğru görünmüyorsa antenin konumunu ayarlayıp tekrar taramayı deneyin.</item>
- <item quantity="one">Başarılı! Kanal tarama sırasında %1$d kanal bulundu. Bu sonuç doğru görünmüyorsa antenin konumunu ayarlayıp tekrar taramayı deneyin.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Bitti"</item>
- <item msgid="2480490326672924828">"Yeniden tara"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Hiçbir kanal bulunamadı"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Tarama işleminde hiçbir kanal bulunamadı. TV\'nizin bir TV sinyal kaynağına bağlı olduğunu doğrulayın.\n\nHavadan yayın alan bir anten kullanıyorsanız yerini veya yönünü ayarlayın. En iyi sonucu elde etmek için anteni yüksek ve pencereye yakın bir yere yerleştirerek tekrar tarayın."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Tarama işlemi herhangi bir kanal bulamadı. USB kanal ayarlayıcının takılı olduğunu ve bir TV sinyali kaynağına bağlandığını doğrulayın.\n\nHavadan yayın alan bir anten kullanıyorsanız yerini veya yönünü ayarlayın. En iyi sonucu elde etmek için anteni yüksek ve pencereye yakın bir yere yerleştirerek tekrar tarayın."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Taramada herhangi bir kanal bulunamadı. Ağ kanal ayarlayıcının açık olduğunu ve TV sinyali alabileceği bir kaynağa bağlandığını doğrulayın.\n\nHavadan gelen yayınları alacak bir anten kullanıyorsanız antenin yerini veya yönünü ayarlayın. En iyi sonucu elde etmek için anteni yüksek ve pencereye yakın bir yere yerleştirin."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Yeniden tara"</item>
- <item msgid="2092797862490235174">"Bitti"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"TV kanallarını tarayın"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"TV Kanal Ayarlayıcı kurulumu"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB TV Kanal Ayarlayıcı kurulumu"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Ağ TV Kanal Ayarlayıcı kurulumu"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB TV kanal ayarlayıcı bağlantısı kesildi."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Ağ kanal ayarlayıcı bağlantısı kesildi."</string>
-</resources>
diff --git a/usbtuner-res/values-uk/strings.xml b/usbtuner-res/values-uk/strings.xml
deleted file mode 100644
index 50c84efc..00000000
--- a/usbtuner-res/values-uk/strings.xml
+++ /dev/null
@@ -1,96 +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="bt_app_name" msgid="5515382901862469770">"ТВ-тюнер"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"ТВ-тюнер USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Мережевий ТВ-тюнер (БЕТА-ВЕРСІЯ)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Зачекайте, доки завершиться пошук"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Програмне забезпечення тюнера нещодавно оновлено. Проскануйте канали знову."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Увімкнути об’ємний звук у налаштуваннях системи, щоб слухати аудіо"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Не вдається відтворити відео. Спробуйте на іншому телевізорі"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Налаштування тюнера"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Налаштування ТВ-тюнера"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Налаштування USB-тюнера"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Налаштування мережевого тюнера"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Переконайтеся, що ви під’єднали телевізор до джерела вхідного телевізійного сигналу.\n\nЯкщо ви користуєтесь ефірною антеною, змініть її положення,щоб знайти більше каналів. Розмістіть антену вище та біля вікна."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Переконайтеся, що ви підключили USB-тюнер і під’єднали джерело вхідного телевізійного сигналу.\n\nЯкщо ви користуєтесь ефірною антеною, змініть її положення, щоб знайти більше каналів. Розмістіть антену вище та біля вікна."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Переконайтеся, що мережевий тюнер увімкнено та під’єднано до джерела телевізійного сигналу.\n\nЯкщо у вас ефірна антена, змініть її положення. Розмістіть антену вище та біля вікна й повторіть спробу."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Продовжити"</item>
- <item msgid="727245208787621142">"Не зараз"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Відновити налаштування каналу?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Канали, знайдені за допомогою ТВ-тюнера, буде видалено. Пошук почнеться знову.\n\nПереконайтеся, що ви під’єднали телевізор до джерела вхідного телевізійного сигналу.\n\nЯкщо ви користуєтесь ефірною антеною, змініть її положення, щоб знайти більше каналів. Розмістіть антену вище та біля вікна й повторіть спробу."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Канали, знайдені за допомогою USB-тюнера, буде видалено. Пошук почнеться знову.\n\nПереконайтеся, що ви підключили USB-тюнер і під’єднали джерело вхідного телевізійного сигналу.\n\nЯкщо ви користуєтесь ефірною антеною, змініть її положення, щоб знайти більше каналів. Розмістіть антену вище та біля вікна."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Канали, знайдені мережевим тюнером, буде видалено. Пошук почнеться знову.\n\nПереконайтеся, що мережевий тюнер увімкнено та під’єднано до джерела телевізійного сигналу.\n\nЯкщо у вас ефірна антена, змініть її положення. Розмістіть антену вище та біля вікна й повторіть спробу."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Продовжити"</item>
- <item msgid="235450158666155406">"Скасувати"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Вибір типу з’єднання"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Виберіть \"Антена\", якщо до тюнера під’єднано зовнішню антену. Виберіть \"Кабель\", якщо у вас кабельне телебачення. Якщо вибрати \"Не знаю\", тюнер шукатиме обидва типи сигналу, але це займе більше часу."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Антена"</item>
- <item msgid="2670079958754180142">"Кабель"</item>
- <item msgid="36774059871728525">"Не знаю"</item>
- <item msgid="6881204453182153978">"Лише для розробки"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Налаштування ТВ-тюнера"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Налаштування USB-тюнера"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Налаштування мережевого тюнера"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Це може зайняти декілька хвилин"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Тюнер тимчасово недоступний або вже використовується для запису."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="one">Знайдено %1$d канал</item>
- <item quantity="few">Знайдено %1$d канали</item>
- <item quantity="many">Знайдено %1$d каналів</item>
- <item quantity="other">Знайдено %1$d каналу</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"ПРИПИНИТИ ПОШУК КАНАЛІВ"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="one">Знайдено %1$d канал</item>
- <item quantity="few">Знайдено %1$d канали</item>
- <item quantity="many">Знайдено %1$d каналів</item>
- <item quantity="other">Знайдено %1$d каналу</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="one"> Під час сканування знайдено %1$d канал. Якщо має бути більше каналів, змініть положення антени та повторіть спробу.</item>
- <item quantity="few"> Під час сканування знайдено %1$d канали. Якщо має бути більше каналів, змініть положення антени та повторіть спробу.</item>
- <item quantity="many"> Під час сканування знайдено %1$d каналів. Якщо має бути більше каналів, змініть положення антени та повторіть спробу.</item>
- <item quantity="other"> Під час сканування знайдено %1$d каналу. Якщо має бути більше каналів, змініть положення антени та повторіть спробу.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Готово"</item>
- <item msgid="2480490326672924828">"Шукати знову"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Канали не знайдено"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Канали не знайдено. Переконайтеся, що ви під’єднали телевізор до джерела вхідного телевізійного сигналу.\n\nЯкщо ви користуєтесь ефірною антеною, змініть її положення. Розмістіть антену вище та біля вікна й повторіть спробу."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Канали не знайдено. Переконайтеся, що ви підключили USB-тюнер і під’єднали джерело вхідного телевізійного сигналу.\n\nЯкщо ви користуєтесь ефірною антеною, змініть її положення. Розмістіть антену вище та біля вікна й повторіть спробу."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Каналів не знайдено. Переконайтеся, що мережевий тюнер увімкнено та під’єднано до джерела телевізійного сигналу.\n\nЯкщо у вас ефірна антена, змініть її положення. Розмістіть антену вище та біля вікна й повторіть спробу."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Шукати знову"</item>
- <item msgid="2092797862490235174">"Готово"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Пошук телевізійних каналів"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Налаштування ТВ-тюнера"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Налаштування ТВ-тюнера USB"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Налаштування мережевого ТВ-тюнера"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"ТВ-тюнер USB від’єднано."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Мережевий тюнер від’єднано."</string>
-</resources>
diff --git a/usbtuner-res/values-ur-rPK/strings.xml b/usbtuner-res/values-ur-rPK/strings.xml
deleted file mode 100644
index 49dc9e34..00000000
--- a/usbtuner-res/values-ur-rPK/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"‏TV ٹیونر"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"‏USB TV ٹیونر"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"‏نیٹ ورک TV ٹیونر (بی ٹا)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"براہ کرم کارروائی ختم ہونے کا انتظار کریں"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"ٹیونر سافٹ ویئر حال ہی میں اپ ڈیٹ کیا گیا ہے۔ براہ کرم چینلز کو دوبارہ اسکین کریں۔"</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"آڈیو کو فعال کرنے کیلئے سسٹم کی آواز کی ترتیبات میں محیط آواز فعال کریں"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"آڈیو نہیں چل رہی۔ براہ کرم کوئی اور ٹی وی آزمائیں"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"چینل ٹیونر سیٹ اپ"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"‏TV ٹیونر سیٹ اپ"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"‏USB چینل ٹیونر سیٹ اپ"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"نیٹ ورک ٹیونر سیٹ اپ"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"‏اپنے TV کے کسی TV سگنل ماخذ سے منسلک ہونے کی توثیق کریں۔\n\nاگر وائرلیس نیٹ ورک کنکشن انٹینا استعمال کر رہے ہیں تو زیادہ تر چینلز موصول کرنے کیلئے آپ کو اس کا مقام یا سمت ایڈجسٹ کرنے کی ضرورت پیش آ سکتی ہے۔ بہترین نتائج کیلئے اسے اونچی جگہ اور کھڑکی کے پاس رکھیں۔"</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"‏USB ٹیونر کے پلگ ان ہونے اور TV سگنل ماخذ سے منسلک ہونے کی توثیق کریں۔\n\nاگر وائرلیس نیٹ ورک کنکشن انٹینا استعمال کر رہے ہیں تو زیادہ تر چینلز موصول کرنے کیلئے آپ کو اس کا مقام یا سمت ایڈجسٹ کرنے کی ضرورت پیش آ سکتی ہے۔ بہترین نتائج کیلئے اسے اونچی جگہ اور کھڑکی کے پاس رکھیں۔"</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"‏نیٹ ورک ٹیونر کے آن ہونے اور TV سگنل ماخذ سے منسلک ہونے کی توثیق کریں۔\n\nاگر وائرلیس نیٹ ورک کنکشن انٹینا استعمال کر رہے ہیں تو زیادہ تر چینلز موصول کرنے کیلئے آپ کو اس کا مقام یا سمت ایڈجسٹ کرنے کی ضرورت پیش آ سکتی ہے۔ بہترین نتائج کیلئے اسے اونچی جگہ اور کھڑکی کے پاس رکھیں۔"</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"جاری رکھیں"</item>
- <item msgid="727245208787621142">"ابھی نہیں"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"چینل سیٹ اپ دوبارہ چلائیں؟"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"‏یہ TV ٹیونر سے ملے چینلز ہٹا دے گا اور دوبارہ نئے چینلز کیلئے اسکین کرے گا۔\n\nاپنے TV کے کسی سگنل ماخذ سے منسلک ہونے کی توثیق کریں۔\n\nاگر وائرلیس نیٹ ورک کنکشن انٹینا استعمال کر رہے ہیں تو زیادہ تر چینلز موصول کرنے کیلئے آپ کو اس کا مقام یا سمت ایڈجسٹ کرنے کی ضرورت پیش آ سکتی ہے۔ بہترین نتائج کیلئے اسے اونچی جگہ اور کھڑکی کے پاس رکھیں۔"</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"‏یہ USB ٹیونر سے ملے چینلز ہٹا دے گا اور دوبارہ نئے چینلز کیلئے اسکین کرے گا۔\n\nTV ٹیونر کے پلگ ان ہونے اور سگنل ماخذ سے منسلک ہونے کی توثیق کریں۔\n\nاگر وائرلیس نیٹ ورک کنکشن انٹینا استعمال کر رہے ہیں تو زیادہ تر چینلز موصول کرنے کیلئے آپ کو اس کا مقام یا سمت ایڈجسٹ کرنے کی ضرورت پیش آ سکتی ہے۔ بہترین نتائج کیلئے اسے اونچی جگہ اور کھڑکی کے پاس رکھیں۔"</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"‏یہ نیٹ ورک ٹیونر سے ملنے والے چیلنز ہٹا دے گا اور دوبارہ نئے چینلز کیلئے اسکین کرے گا۔\n\nTV ٹیونر کے آن ہونے اور سگنل ماخذ سے منسلک ہونے کی توثیق کریں۔\n\nاگر وائرلیس نیٹ ورک کنکشن انٹینا استعمال کر رہے ہیں، تو زیادہ تر چینلز موصول کرنے کیلئے آپ کو اس کی مقام بندی یا سمت ایڈجسٹ کرنے کی ضرورت پیش آ سکتی ہے۔ بہترین نتائج کیلئے اسے اونچی جگہ اور کھڑکی کے پاس رکھیں۔"</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"جاری رکھیں"</item>
- <item msgid="235450158666155406">"منسوخ کریں"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"کنکشن کی قسم منتخب کریں"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"اگر ٹیونر سے کوئی بیرونی انٹینا منسلک ہے تو انٹینا کا انتخاب کریں۔ اگر آپ کے چینلز کسی کیبل سروس فراہم کنندہ کی طرف سے موصول ہوتے ہیں تو کیبل کا انتخاب کریں۔ اگر آپ پُر یقین نہیں ہیں تو دونوں اقسام کو اسکین کیا جائے گا، مگر اس میں زیادہ وقت لگ سکتا ہے۔"</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"انٹینا"</item>
- <item msgid="2670079958754180142">"کیبل"</item>
- <item msgid="36774059871728525">"یقین نہیں ہے"</item>
- <item msgid="6881204453182153978">"صرف ڈیولپمنٹ"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"‏TV ٹیونر سیٹ اپ"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"‏USB چینل ٹیونر سیٹ اپ"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"نیٹ ورک چینل ٹیونر سیٹ اپ"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"اس میں کئی منٹ لگ سکتے ہیں"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"ٹیونر عارضی طور پر غیر دستیاب ہے یا پہلے سے ریکارڈنگ کی وجہ سے استعمال ہو گیا ہے۔"</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">‏%1$d چینلز ملے</item>
- <item quantity="one">‏%1$d چینل ملا</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"چینل اسکین روکیں"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">‏‎%1$d چینلز ملے</item>
- <item quantity="one">‏%1$d چینل ملا</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">‏خوب! چینل اسکین کے دوران ‎%1$d چینلز ملے۔ اگر یہ ٹھیک نہیں لگ رہا تو انٹینا کی پوزیشن ایڈجسٹ کرنے کی کوشش کریں اور دوبارہ اسکین کریں۔</item>
- <item quantity="one">‏خوب! چینل اسکین کے دوران ‎%1$d چینل ملا۔ اگر یہ ٹھیک نہیں لگ رہا تو انٹینا کی پوزیشن ایڈجسٹ کرنے کی کوشش کریں اور دوبارہ اسکین کریں۔</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"ہو گیا"</item>
- <item msgid="2480490326672924828">"دوبارہ اسکین کریں"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"کوئی چینلز نہیں ملے"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"‏اسکین سے کوئی چینلز نہیں ملے۔ اپنے TV کے ایک TV سگنل ماخذ سے منسلک ہونے کی توثیق کریں۔\n\nاگر وائرلیس نیٹ ورک کنکشن انٹینا استعمال کر رہے ہیں تو اس کا مقام یا سمت ایڈجسٹ کریں۔ بہترین نتائج کیلئے اسے اونچی جگہ اور کھڑکی کے پاس رکھیں اور دوبارہ اسکین کریں۔"</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"‏اسکین سے کوئی چینلز نہیں ملے۔ USB ٹیونر کے پلگ ان ہونے اور TV سگنل ماخذ سے منسلک ہونے کی توثیق کریں۔\n\nاگر وائرلیس نیٹ ورک کنکشن انٹینا استعمال کر رہے ہیں تو اس کا مقام یا سمت ایڈجسٹ کریں۔ بہترین نتائج کیلئے اسے اونچی جگہ اور کھڑکی کے پاس رکھیں اور دوبارہ اسکین کریں۔"</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"‏اسکین سے کوئی چینلز نہیں ملے۔ توثیق کریں کہ نیٹ ورک ٹیونر آن ہے اور TV سگنل ماخذ سے منسلک ہے۔\n\nاگر وائرلیس نیٹ ورک کنکشن انٹینا استعمال کر رہے ہیں، تو اس کا مقام یا سمت ایڈجسٹ کریں۔ بہترین نتائج کیلئے، اسے اونچی جگہ اور کھڑکی کے قریب رکھیں اور دوبارہ اسکین کریں۔"</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"دوبارہ اسکین کریں"</item>
- <item msgid="2092797862490235174">"ہو گیا"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"‏TV چینلز کے لیے اسکین کریں"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"‏TV ٹیونر سیٹ اپ"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"‏USB TV ٹیونر سیٹ اپ"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"‏نیٹ ورک TV ٹیونر سیٹ اپ"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"‏USB TV ٹیونر غیر منسلک ہے۔"</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"نیٹ ورک ٹیونر غیر منسلک ہے۔"</string>
-</resources>
diff --git a/usbtuner-res/values-uz-rUZ/strings.xml b/usbtuner-res/values-uz-rUZ/strings.xml
deleted file mode 100644
index 2709dec0..00000000
--- a/usbtuner-res/values-uz-rUZ/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"TV-tyuner"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB TV-tyuner"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Tarmoq TV-tyuneri (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Iltimos, jarayon tugashini kuting"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Tyunerning dasturiy ta’minoti yaqinda yangilandi. Kanallarni qaytadan qidiring."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Audioni yoqish uchun tizim ovozi sozlamalari orqali qamrovli ovozni yoqing"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Audioni ijro ettirib bo‘lmadi. Boshqa kanalni sinab ko‘ring."</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Tyunerni sozlash"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"TV-tyunerni sozlash"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB-tyunerni sozlash"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Tarmoq tyunerini sozlash"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Televizor signali manbasiga ulangan ekanligini tekshiring.\n\nAgar havo orqali to‘lqin tutuvchi antennadan foydalanayotgan bo‘lsangiz, ko‘proq kanal topilishi uchun uning joyi va yo‘nalishini sozlang. Eng yaxshi natijaga erishish uchun uni yuqoriroq va oynaga yaqin joyga o‘rnating."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"USB-tyuner suqilgan hamda televizor signali manbasiga ulangan ekanligini tekshiring.\n\nAgar havo orqali to‘lqin tutuvchi antennadan foydalanayotgan bo‘lsangiz, ko‘proq kanal topilishi uchun uning joyi va yo‘nalishini sozlang. Eng yaxshi natijaga erishish uchun uni yuqoriroq va oynaga yaqin joyga o‘rnating."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Tarmoq tyuneri yoqilgan hamda televizor signali manbasiga ulangan ekanligini tekshiring.\n\nAgar havo orqali to‘lqin tutuvchi antennadan foydalanayotgan bo‘lsangiz, ko‘proq kanal topilishi uchun uning joyi va yo‘nalishini sozlang. Eng yaxshi natijaga erishish uchun uni yuqoriroq va oynaga yaqin joyga o‘rnating."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Davom etish"</item>
- <item msgid="727245208787621142">"Hozir emas"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Kanallar qaytadan sozlansinmi?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Buning natijasida TV-tyuner orqali topilgan kanallar o‘chirib tashlanadi va kanallar boshqatdan qidiriladi.\n\nTelevizor signal manbasiga ulangan ekanligini tekshiring.\n\nAgar havo orqali to‘lqin tutuvchi antennadan foydalanayotgan bo‘lsangiz, uning joyi va yo‘nalishini sozlang. Eng yaxshi natijaga erishish uchun uni yuqoriroq va oynaga yaqin joyga o‘rnating hamda qaytadan qidiring."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Buning natijasida USB-tyuner orqali topilgan kanallar o‘chirib tashlanadi va kanallar boshqatdan qidiriladi.\n\nUSB-tyuner suqilgan hamda televizor signali manbasiga ulangan ekanligini tekshiring.\n\nAgar havo orqali to‘lqin tutuvchi antennadan foydalanayotgan bo‘lsangiz, uning joyi va yo‘nalishini sozlang. Eng yaxshi natijaga erishish uchun uni yuqoriroq va oynaga yaqin joyga o‘rnating hamda qaytadan qidiring."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Buning natijasida tarmoq tyuneri orqali topilgan kanallar o‘chirib tashlanadi va kanallar boshqatdan qidiriladi.\n\nTarmoq tyuneri yoqilgan hamda televizor signali manbasiga ulangan ekanligini tekshiring.\n\nAgar havo orqali to‘lqin tutuvchi antennadan foydalanayotgan bo‘lsangiz, uning joyi va yo‘nalishini sozlang. Eng yaxshi natijaga erishish uchun uni yuqoriroq va oynaga yaqin joyga o‘rnating hamda qaytadan qidiring."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Davom etish"</item>
- <item msgid="235450158666155406">"Bekor qilish"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Aloqa turini tanlang"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Agar tyunerga tashqi antenna ulangan bo‘lsa, “Antenna” variantini tanlang. Agar kanallar kabel televideniye ta’minotchisidan olinadigan bo‘lsa, “Kabel TV” variantini tanlang. Agar qaysi biri ekanligini aniq bilmasangiz, har ikkala tur ham qidiriladi, shuning uchun uzoqroq vaqt ketishi mumkin."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Antenna"</item>
- <item msgid="2670079958754180142">"Kabel"</item>
- <item msgid="36774059871728525">"Aniq bilmayman"</item>
- <item msgid="6881204453182153978">"Faqat dasturchilar uchun"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"TV-tyunerni sozlash"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB-tyunerni sozlash"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Tarmoq tyuneri kanallarini sozlash"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Bu bir necha daqiqa vaqt olishi mumkin"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Tyunerdan vaqtinchalik foydalanib bo‘lmaydi yoki allaqachon yozib olishda foydalanilmoqda."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">%1$d ta kanal topildi</item>
- <item quantity="one">%1$d ta kanal topildi</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"KANAL QIDIRUVINI TO‘XTATISH"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">%1$d ta kanal topildi</item>
- <item quantity="one">%1$d ta kanal topildi</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Ajoyib! Kanal qidirish natijasida %1$d ta kanal topildi. Agar yanada ko‘proq kanal topilishi kerak deb hisoblasangiz, antenna joylashuvini sozlang va qaytadan qidiring.</item>
- <item quantity="one">Ajoyib! Kanal qidirish natijasida %1$d ta kanal topildi. Agar yanada ko‘proq kanal topilishi kerak deb hisoblasangiz, antenna joylashuvini sozlang va qaytadan qidiring.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Tayyor"</item>
- <item msgid="2480490326672924828">"Yana qidirish"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Hech qanday kanal topilmadi"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Qidiruv natijasida hech qanday kanal topilmadi. Televizor signal manbasiga ulangan ekanligini tekshiring.\n\nAgar havo orqali to‘lqin tutuvchi antennadan foydalanayotgan bo‘lsangiz, uning joyi va yo‘nalishini sozlang. Eng yaxshi natijaga erishish uchun uni yuqoriroq va oynaga yaqin joyga o‘rnating hamda qaytadan qidiring."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Qidiruv natijasida hech qanday kanal topilmadi. USB-tyuner suqilgan hamda televizor signali manbasiga ulangan ekanligini tekshiring.\n\nAgar havo orqali to‘lqin tutuvchi antennadan foydalanayotgan bo‘lsangiz, uning joyi va yo‘nalishini sozlang. Eng yaxshi natijaga erishish uchun uni yuqoriroq va oynaga yaqin joyga o‘rnating hamda qaytadan qidiring."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Qidiruv natijasida hech qanday kanal topilmadi. Tarmoq tyuneri yoqilgan hamda televizor signali manbasiga ulangan ekanligini tekshiring.\n\nAgar havo orqali to‘lqin tutuvchi antennadan foydalanayotgan bo‘lsangiz, uning joyi va yo‘nalishini sozlang. Eng yaxshi natijaga erishish uchun uni yuqoriroq va oynaga yaqin joyga o‘rnating hamda qaytadan qidiring."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Yana qidirish"</item>
- <item msgid="2092797862490235174">"Tayyor"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Telekanallarni qidiring"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"TV-tyunerni sozlash"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB TV-tyunerni sozlang"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Tarmoq TV-tyunerini sozlash"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB-tyuner o‘chirib qo‘yildi."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Tarmoq tyuneri uzib qo‘yildi."</string>
-</resources>
diff --git a/usbtuner-res/values-vi/strings.xml b/usbtuner-res/values-vi/strings.xml
deleted file mode 100644
index 6e6cef72..00000000
--- a/usbtuner-res/values-vi/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"Bộ dò TV"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"Bộ dò TV USB"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Bộ dò TV mạng (BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Vui lòng đợi để hoàn tất xử lý"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Phần mềm bộ dò đã được cập nhật gần đây. Vui lòng quét lại các kênh."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Bật tính năng âm thanh vòm trong cài đặt âm thanh hệ thống để bật âm thanh"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Không thể phát âm thanh. Vui lòng thử TV khác"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Thiết lập bộ dò kênh"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Thiết lập bộ dò TV"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Thiết lập bộ dò kênh USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Thiết lập bộ dò mạng"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Hãy xác minh rằng TV của bạn đã được kết nối với nguồn tín hiệu TV.\n\nNếu sử dụng ăng-ten không dây thì bạn có thể cần phải điều chỉnh vị trí hoặc hướng của ăng-ten đó để nhận nhiều kênh nhất. Để có kết quả tốt nhất, hãy đặt ăng-ten lên cao và gần cửa sổ."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Xác minh rằng bộ dò USB đã được cắm và được kết nối với nguồn tín hiệu TV.\n\nNếu sử dụng ăng-ten không dây, có thể bạn cần phải điều chỉnh vị trí hoặc hướng của ăng-ten đó để nhận được nhiều kênh nhất. Để có kết quả tốt nhất, hãy đặt ăng-ten lên cao và gần cửa sổ."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Hãy xác minh là bộ dò mạng đã được bật nguồn và kết nối với một nguồn tín hiệu TV.\n\nNếu sử dụng ăng-ten không dây, hãy điều chỉnh vị trí hoặc hướng của ăng-ten để nhận hầu hết kênh. Để có kết quả tốt nhất, hãy đặt ăng-ten ở vị trí cao và gần cửa sổ."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Tiếp tục"</item>
- <item msgid="727245208787621142">"Không phải bây giờ"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Chạy lại quá trình thiết lập kênh?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Điều này sẽ xóa các kênh được tìm thấy khỏi bộ dò TV và quét các kênh mới lần nữa.\n\nHãy xác minh rằng TV của bạn đã được kết nối với nguồn tín hiệu TV.\n\nNếu sử dụng ăng-ten không dây thì bạn có thể cần phải điều chỉnh vị trí hoặc hướng của ăng-ten đó để nhận nhiều kênh nhất. Để có kết quả tốt nhất, hãy đặt ăng-ten lên cao và gần cửa sổ."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Quá trình này sẽ xóa các kênh bộ dò USB đã tìm thấy và quét lại để tìm các kênh mới.\n\nHãy xác minh rằng bộ dò USB đã được cắm và kết nối với nguồn tín hiệu TV.\n\nNếu sử dụng ăng-ten không dây, bạn có thể cần phải điều chỉnh vị trí hoặc hướng của ăng-ten đó để nhận được nhiều kênh nhất. Để có kết quả tốt nhất, hãy đặt ăng-ten lên cao và gần cửa sổ."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Điều này sẽ xóa các kênh được tìm thấy từ bộ dò mạng và quét các kênh mới một lần nữa.\n\nHãy xác minh là bộ dò mạng đã được bật nguồn và kết nối với một nguồn tín hiệu TV.\n\nNếu sử dụng ăng-ten không dây, hãy điều chỉnh vị trí hoặc hướng của ăng-ten để nhận hầu hết kênh. Để có kết quả tốt nhất, hãy đặt ăng-ten ở vị trí cao và gần cửa sổ."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Tiếp tục"</item>
- <item msgid="235450158666155406">"Hủy"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Chọn loại kết nối"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Chọn Ăng-ten nếu có ăng-ten bên ngoài được kết nối với bộ dò. Chọn Cáp nếu kênh của bạn đến từ nhà cung cấp dịch vụ cáp. Nếu bạn không chắc chắn thì cả hai loại sẽ đều được quét. Tuy nhiên, quá trình này có thể mất nhiều thời gian hơn."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"Ăng-ten"</item>
- <item msgid="2670079958754180142">"Cáp"</item>
- <item msgid="36774059871728525">"Không chắc chắn"</item>
- <item msgid="6881204453182153978">"Chỉ phát triển"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Thiết lập bộ dò TV"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Thiết lập bộ dò kênh USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Thiết lập bộ do kênh mạng"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Quá trình này có thể mất vài phút"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Bộ dò tạm thời không có sẵn hoặc đã được sử dụng để ghi."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">Đã tìm thấy %1$d kênh</item>
- <item quantity="one">Đã tìm thấy %1$d kênh</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"NGỪNG QUÉT KÊNH"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">Đã tìm thấy %1$d kênh</item>
- <item quantity="one">Đã tìm thấy %1$d kênh</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">Tuyệt! Đã tìm thấy %1$d kênh trong quá trình quét kênh. Nếu điều này có vẻ không ổn, hãy thử điều chỉnh vị trí ăng-ten và quét lại.</item>
- <item quantity="one">Tuyệt! Đã tìm thấy %1$d kênh trong quá trình quét kênh. Nếu điều này có vẻ không ổn, hãy thử điều chỉnh vị trí ăng-ten và quét lại.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Xong"</item>
- <item msgid="2480490326672924828">"Quét lại"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Không tìm thấy kênh nào"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Quá trình quét không tìm thấy bất kỳ kênh nào. Hãy xác minh rằng TV của bạn đã được kết nối với nguồn tín hiệu TV.\n\nNếu sử dụng ăng-ten không dây, hãy điều chỉnh vị trí hoặc hướng của ăng-ten đó. Để có kết quả tốt nhất, hãy đặt ăng-ten lên cao và gần cửa sổ rồi quét lại."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Quá trình quét không tìm thấy bất kỳ kênh nào. Hãy xác minh rằng bộ dò USB đã được cắm và kết nối với nguồn tín hiệu TV.\n\nNếu sử dụng ăng-ten không dây, hãy điều chỉnh vị trí hoặc hướng của ăng-ten đó. Để có kết quả tốt nhất, hãy đặt ăng-ten lên cao và gần cửa sổ rồi quét lại."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Quá trình quét không tìm thấy bất kỳ kênh nào. Hãy xác minh là bộ dò mạng đã được bật nguồn và kết nối với một nguồn tín hiệu TV.\n\nNếu sử dụng ăng-ten không dây, hãy điều chỉnh vị trí hoặc hướng của ăng-ten. Để có kết quả tốt nhất, hãy đặt ăng-ten ở vị trí cao và gần cửa sổ và quét lại."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Quét lại"</item>
- <item msgid="2092797862490235174">"Xong"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Quét tìm các kênh TV"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Thiết lập bộ dò TV"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Thiết lập bộ dò TV USB."</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Thiết lập bộ dò TV mạng"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"Đã ngắt kết nối bộ dò truyền hình USB."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Đã ngắt kết nối bộ dò mạng."</string>
-</resources>
diff --git a/usbtuner-res/values-zh-rCN/strings.xml b/usbtuner-res/values-zh-rCN/strings.xml
deleted file mode 100644
index cf2b477f..00000000
--- a/usbtuner-res/values-zh-rCN/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"电视调谐器"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB 电视调谐器"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"网络电视调谐器(测试版)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"请耐心等待处理完毕"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"调谐器软件近期已更新。请重新扫描频道。"</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"在系统声音设置中启用环绕声即可启用音频"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"无法播放音频,请试试其他电视频道"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"频道调谐器设置"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"电视调谐器设置"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB 频道调谐器设置"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"网络调谐器设置"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"请检查您的电视是否已连接到电视信号源。\n\n如果您使用的是无线电视,则可能需要调节天线的位置或方向,以便接收尽可能多的频道。要获得最佳效果,请将天线的位置调高并靠近窗户。"</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"请检查 USB 调谐器是否已插好并连接到电视信号源。\n\n如果您使用的是无线电视,则可能需要调节天线的位置或方向,以便接收尽可能多的频道。要获得最佳效果,请将天线的位置调高并靠近窗户。"</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"请检查网络调谐器是否已接通电源并连接到电视信号源。\n\n如果您使用的是无线电视,则可能需要调节天线的位置或方向,以便接收尽可能多的频道。要获得最佳效果,请将天线的位置调高并靠近窗户。"</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"继续"</item>
- <item msgid="727245208787621142">"以后再说"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"要重新进行频道设置吗?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"此操作将移除通过电视调谐器找到的频道,并重新扫描新频道。\n\n请检查您的电视是否已连接到电视信号源。\n\n如果您使用的是无线电视,则可能需要调节天线的位置或方向,以便接收尽可能多的频道。要获得最佳效果,请将天线的位置调高并靠近窗户。"</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"此操作将移除通过 USB 调谐器找到的频道,并重新扫描新频道。\n\n请检查 USB 调谐器是否已插好并连接到电视信号源。\n\n如果您使用的是无线电视,则可能需要调节天线的位置或方向,以便接收尽可能多的频道。要获得最佳效果,请将天线的位置调高并靠近窗户。"</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"此操作将移除通过网络调谐器找到的频道,并重新扫描新频道。\n\n请检查网络调谐器是否已接通电源并连接到电视信号源。\n\n如果您使用的是无线电视,则可能需要调节天线的位置或方向,以便接收尽可能多的频道。要获得最佳效果,请将天线的位置调高并靠近窗户。"</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"继续"</item>
- <item msgid="235450158666155406">"取消"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"选择连接类型"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"如果调谐器连接了外部天线,请选择“天线”。如果您的频道由有线电视服务商提供,请选择“有线电视”。如果您不确定,则系统会扫描以上两种类型,不过这可能会花费更长的时间。"</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"天线"</item>
- <item msgid="2670079958754180142">"有线电视"</item>
- <item msgid="36774059871728525">"不确定"</item>
- <item msgid="6881204453182153978">"仅限开发用途"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"电视调谐器设置"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB 频道调谐器设置"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"网络频道调谐器设置"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"此过程可能需要几分钟时间"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"调谐器暂时无法使用或已用于录制。"</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">找到 %1$d 个频道</item>
- <item quantity="one">找到 %1$d 个频道</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"停止频道扫描"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">找到 %1$d 个频道</item>
- <item quantity="one">找到 %1$d 个频道</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">很好!系统在频道扫描过程中找到 %1$d 个频道。如果您觉得数量有误,请尝试调整天线的位置,然后再扫描一次。</item>
- <item quantity="one">很好!系统在频道扫描过程中找到 %1$d 个频道。如果您觉得数量有误,请尝试调整天线的位置,然后再扫描一次。</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"完成"</item>
- <item msgid="2480490326672924828">"重新扫描"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"未找到任何频道"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"扫描后未找到任何频道。请检查您的电视是否已连接到电视信号源。\n\n如果您使用的是无线电视,请调节天线的位置或方向。要获得最佳效果,请将天线的位置调高并靠近窗户,然后再扫描一次。"</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"扫描后未找到任何频道。请检查 USB 调谐器是否已插好并连接到电视信号源。\n\n如果您使用的是无线电视,请调节天线的位置或方向。要获得最佳效果,请将天线的位置调高并靠近窗户,然后再扫描一次。"</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"扫描后未找到任何频道。请检查网络调谐器是否已接通电源并连接到电视信号源。\n\n如果您使用的是无线电视,请调节天线的位置或方向。要获得最佳效果,请将天线的位置调高并靠近窗户,然后再扫描一次。"</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"重新扫描"</item>
- <item msgid="2092797862490235174">"完成"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"扫描电视频道"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"电视调谐器设置"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB 电视调谐器设置"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"网络电视调谐器设置"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB 电视调谐器已断开连接。"</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"网络调谐器已断开连接。"</string>
-</resources>
diff --git a/usbtuner-res/values-zh-rHK/strings.xml b/usbtuner-res/values-zh-rHK/strings.xml
deleted file mode 100644
index f2b89519..00000000
--- a/usbtuner-res/values-zh-rHK/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"電視調諧器"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB 電視調諧器"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"網絡電視調諧器 (測試版)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"請等待系統完成處理程序"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"調諧器軟件最近已更新。請重新掃瞄頻道。"</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"在系統音效設定中啟用環迴立體聲功能即可啟用音效"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"無法播放音效,請嘗試使用其他電視頻道"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"頻道調諧器設定"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"電視調諧器設定"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB 頻道調諧器設定"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"網絡調諧器設定"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"請確定電視已連接至電視訊號來源。\n\n如果您使用無線天線,可能需要調整天線的位置或方向,以接收最多頻道。您亦可將天線放在較高位置並靠近窗戶,以獲得最佳效果。"</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"請確定 USB 調諧器已接駁並連接至電視訊號來源。\n\n如果您使用無線天線,可能需要調整天線的位置或方向,以接收最多頻道。您亦可以將天線放在較高位置並靠近窗戶,以獲取最佳效果。"</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"請確定網絡調諧器已開啟電源,並連接至電視訊號來源。\n\n如果您使用無線天線,可能需要調整天線的位置或方向,以接收最多頻道。要取得最佳效果,請將天線放在較高位置並靠近窗戶,然後重新掃瞄。"</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"繼續"</item>
- <item msgid="727245208787621142">"暫時不要"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"要重新設定頻道嗎?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"這項操作將移除電視調諧器找到的頻道,並重新掃瞄新的頻道。\n\n請確定電視已連接至電視訊號來源。\n\n如果您使用無線天線,可能需要調整天線的位置或方向,以接收最多頻道。您亦可將天線放在較高位置並靠近窗戶,以獲得最佳效果。"</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"這項操作將移除 USB 調諧器找到的頻道,並會重新掃瞄新頻道。\n\n請確定 USB 調諧器已接駁並連接至電視訊號來源。\n\n如果您使用無線天線,可能需要調整天線的位置或方向,以接收最多頻道。您亦可將天線放在較高位置並靠近窗戶,以獲取最佳效果。"</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"這項操作將移除網絡調諧器找到的頻道,並再次掃瞄新頻道。\n\n請確定網絡調諧器已開啟電源並連接至電視訊號來源。\n\n如果您使用無線天線,請調整天線的位置或方向。要取得最佳效果,請將天線放在較高位置並靠近窗戶,然後重新掃瞄。"</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"繼續"</item>
- <item msgid="235450158666155406">"取消"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"選擇連接類型"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"如果調諧器連接至外置天線,請選擇 [天線]。如果頻道由有線服務供應商提供,請選擇 [有線電視]。如果您不確定,系統會同時掃瞄這兩種類型的頻道,但可能需時較長。"</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"天線"</item>
- <item msgid="2670079958754180142">"有線電視"</item>
- <item msgid="36774059871728525">"不確定"</item>
- <item msgid="6881204453182153978">"只限開發用途"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"電視調諧器設定"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB 頻道調諧器設定"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"網絡頻道調諧器設定"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"可能需時數分鐘"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"調諧器暫時無法使用,或已用於錄影。"</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">已找到 %1$d 個頻道</item>
- <item quantity="one">已找到 %1$d 個頻道</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"停止頻道掃瞄"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">已找到 %1$d 個頻道</item>
- <item quantity="one">已找到 %1$d 個頻道</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">太好了!掃瞄頻道時找到 %1$d 個頻道。如果掃瞄結果看來不正確,請嘗試調整天線位置並重新掃瞄。</item>
- <item quantity="one">太好了!掃瞄頻道時找到 %1$d 個頻道。如果掃瞄結果看來不正確,請嘗試調整天線位置並重新掃瞄。</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"完成"</item>
- <item msgid="2480490326672924828">"重新掃瞄"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"找不到頻道"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"掃瞄後找不到頻道。請確定電視已連接至電視訊號來源。\n\n如果您使用無線天線,請調整天線的位置或方向。您亦可將天線放在較高位置並靠近窗戶,然後重新掃瞄,以獲得最佳效果。"</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"掃瞄找不到頻道。請確定 USB 調諧器已接駁並連接至電視訊號來源。\n\n如果您使用無線天線,請調整天線的位置或方向。您亦可將天線放在較高位置並靠近窗戶,然後重新掃瞄,以獲取最佳效果。"</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"掃瞄後找不到任何頻道。請確定網絡調諧器已開啟電源,並連接至電視訊號來源。\n\n如果您使用無線天線,請調整天線的位置或方向。要取得最佳效果,請將天線放在較高位置並靠近窗戶,然後重新掃瞄。"</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"重新掃瞄"</item>
- <item msgid="2092797862490235174">"完成"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"掃瞄電視頻道"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"電視調諧器設定"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB 電視調諧器設定"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"網絡電視調諧器設定"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB 電視調諧器已中斷連線。"</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"網絡調諧器已中斷連線。"</string>
-</resources>
diff --git a/usbtuner-res/values-zh-rTW/strings.xml b/usbtuner-res/values-zh-rTW/strings.xml
deleted file mode 100644
index 86624ef5..00000000
--- a/usbtuner-res/values-zh-rTW/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"電視調諧器"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"USB 電視調諧器"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"網路電視調諧器 (測試版)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"請等待處理程序完成"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"調諧器軟體最近已更新。請重新掃描頻道。"</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"前往系統音效設定開啟環繞音效即可啟用音訊"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"無法播放音訊,請改用其他電視頻道"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"頻道調諧器設定"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"電視調諧器設定"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"USB 頻道調諧器設定"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"網路調諧器設定"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"請確認你的電視已連接到電視訊號來源。\n\n如果你使用無線電視天線,可能需要調整天線的位置和方向,以便接收最多頻道。為達最佳效果,請將天線放在靠近窗戶的較高位置。"</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"請確認 USB 調諧器已插入並連接到電視訊號來源。\n\n如果你使用無線電視天線,可能需要調整天線的位置和方向,以便接收最多頻道。為達最佳效果,請將天線放在靠近窗戶的較高位置。"</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"請確認你的網路調諧器已開啟電源,並連接到電視訊號來源。\n\n如果你使用無線電視天線,可能需要調整天線的位置和方向,以便接收最多頻道。為達最佳效果,請將天線放在靠近窗戶的較高位置。"</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"繼續"</item>
- <item msgid="727245208787621142">"暫時不要"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"要重新設定頻道嗎?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"這項動作將移除電視調諧器找到的頻道,並再次掃描新的頻道。\n\n請確認你的電視已連接到電視訊號來源。\n\n如果你使用無線電視天線,可能需要調整天線的位置和方向,以便接收最多頻道。為達最佳效果,請將天線放在靠近窗戶的較高位置。"</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"這項動作將移除 USB 調諧器找到的頻道,並再次掃描新的頻道。\n\n請確認 USB 調諧器已插入並連接到電視訊號來源。\n\n如果你使用無線電視天線,可能需要調整天線的位置和方向,以便接收最多頻道。為達最佳效果,請將天線放在靠近窗戶的較高位置。"</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"這個動作將移除網路調諧器找到的頻道,並再次掃描新的頻道。\n\n請確認你的網路調諧器已開啟電源,並連接到電視訊號來源。\n\n如果你使用無線電視天線,可能需要調整天線的位置和方向,以便接收最多頻道。為達最佳效果,請將天線放在靠近窗戶的較高位置。"</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"繼續"</item>
- <item msgid="235450158666155406">"取消"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"選取連接類型"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"如果調諧器連接外部天線,請選擇 [天線]。如果你的頻道來自有線電視服務供應商,請選擇 [有線電視]。如果你不確定,系統會掃描這兩種類型,但是可能需要較長的時間。"</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"天線"</item>
- <item msgid="2670079958754180142">"有線電視"</item>
- <item msgid="36774059871728525">"不確定"</item>
- <item msgid="6881204453182153978">"僅供開發之用"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"電視調諧器設定"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"USB 頻道調諧器設定"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"網路頻道調諧器設定"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"這可能需要幾分鐘的時間"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"調諧器暫時無法使用,或是已用於錄製。"</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="other">找到 %1$d 個頻道</item>
- <item quantity="one">找到 %1$d 個頻道</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"停止頻道掃描作業"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="other">找到 %1$d 個頻道</item>
- <item quantity="one">找到 %1$d 個頻道</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="other">太好了!頻道掃描作業結束後找到 %1$d 個頻道。如果數量不太正確,請嘗試調整天線的位置,然後再掃描一次。</item>
- <item quantity="one">太好了!頻道掃描作業結束後找到 %1$d 個頻道。如果數量不太正確,請嘗試調整天線的位置,然後再掃描一次。</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"完成"</item>
- <item msgid="2480490326672924828">"重新掃描"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"找不到任何頻道"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"掃描後並未發現任何頻道。請確認你的電視已連接到電視訊號來源。\n\n如果你使用無線電視天線,請調整天線的位置和方向。為達最佳效果,請將天線放在靠近窗戶的較高位置,然後再掃描一次。"</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"掃描後並未發現任何頻道。請確認 USB 調諧器已插入並連接到電視訊號來源。\n\n如果你使用無線電視天線,請調整天線的位置和方向。為達最佳效果,請將天線放在靠近窗戶的較高位置,然後再掃描一次。"</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"掃描後並未發現任何頻道。請確認你的網路調諧器已開啟電源,並連接到電視訊號來源。\n\n如果你使用無線電視天線,請調整天線的位置和方向。為達最佳效果,請將天線放在靠近窗戶的較高位置,然後再掃描一次。"</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"重新掃描"</item>
- <item msgid="2092797862490235174">"完成"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"掃描電視頻道"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"電視調諧器設定"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"USB 電視調諧器設定"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"網路電視調諧器設定"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"USB 電視調諧器已中斷連線。"</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"網路調諧器已中斷連線。"</string>
-</resources>
diff --git a/usbtuner-res/values-zu/strings.xml b/usbtuner-res/values-zu/strings.xml
deleted file mode 100644
index fa8df8d3..00000000
--- a/usbtuner-res/values-zu/strings.xml
+++ /dev/null
@@ -1,90 +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="bt_app_name" msgid="5515382901862469770">"Ishuna ye-TV"</string>
- <string name="ut_app_name" msgid="8557698013780762454">"Ishuna ye-USB TV"</string>
- <string name="nt_app_name" msgid="4627006858832620833">"Ishuna yenethiwekhi ye-TV (i-BETA)"</string>
- <string name="ut_setup_cancel" msgid="5318292052302751909">"Sicela ulinde ukuze uqede ukucubungula"</string>
- <string name="ut_rescan_needed" msgid="2273655435759849436">"Isofthiwe yeshuna ibuyekezwe kamuva. Sicela uphinde uskene iziteshi."</string>
- <string name="ut_surround_sound_disabled" msgid="6465044734143962900">"Nika amandla umsindo ozungezile kuzilungiselelo zomsindo wesistimu"</string>
- <string name="audio_passthrough_not_supported" msgid="8766302073295760976">"Ayikwazi ukudlala umsindo. Sicela uzame enye i-TV"</string>
- <string name="ut_setup_breadcrumb" msgid="2810318605327367247">"Ukusethwa kweshuna yesiteshi"</string>
- <string name="bt_setup_new_title" msgid="8447554965697762891">"Ukusethwa kweshuna ye-TV"</string>
- <string name="ut_setup_new_title" msgid="2118880835101453405">"Ukusetha kweshuna yesiteshi se-USB"</string>
- <string name="nt_setup_new_title" msgid="2996573474450060002">"Ukusethwa kweshuna yenethiwekhi"</string>
- <string name="bt_setup_new_description" msgid="256690722062003128">"Qinisekisa ukuthi i-TV yakho ixhunywe kumthombo wesignali ye-TV.\n\nUma usebenzisa i-antenna esemoyeni, kungenzeka ukuthi kumele ulungise ukubekwa noma ukubheka ukuze uthole iziteshi eziningi. Ukuze uthole imiphumela ehamba phambili, beka phezulu naseduze kwewindi."</string>
- <string name="ut_setup_new_description" msgid="2610122936163002137">"Qinisekisa ukuthi ishuna ye-USB ixhunyiwe futhi ixhumeke kumthombo wesignali ye-TV.\n\nUma usebenzisa i-antenna esemoyeni, kuzomele ulungise ukubekwa kwayo noma ukubheka kwayo ukuze uthole iziteshi eziningi. Ukuze uthole imiphumela ehamba phambili, yibeke phezulu eduze kwewindi."</string>
- <string name="nt_setup_new_description" msgid="8315318180352515751">"Qinisekisa ukuthi ishuna yenethiwekhi ivuliwe yaphinde yaxhunywa kumthombo wesignali ye-TV.\n\nUma usebenzisa i-antenna esemoyeni, kungenzeka ukuthi kumele ulungise ukubekwa kwayo noma indawo ebhekiwe ukuze uthole iziteshi eziningi. Ukuze uthole imiphumela ehamba phambili, ibeke ngaphezulu naseduze kwewindi."</string>
- <string-array name="ut_setup_new_choices">
- <item msgid="8728069574888601683">"Qhubeka"</item>
- <item msgid="727245208787621142">"Hhayi manje"</item>
- </string-array>
- <string name="bt_setup_again_title" msgid="884713873101099572">"Phinda uqalise ukusethwa kwesiteshi?"</string>
- <string name="bt_setup_again_description" msgid="1247792492948741337">"Lokhu kuzosusa iziteshi ezitholwe kusukela kushuna ye-TV kuphinde kuskenele iziteshi ezintsha.\n\nQinisekisa ukuthi i-TV yakho ixhunywe kumthombo wesignali ye-TV.\n\nUma usebenzisa i-antenna esemoyeni, kungenzeka ukuthi kumele ulungise ukubekwa noma ukubheka ukuze uthole iziteshi eziningi. Ukuze uthole imiphumela ehamba phambili, beka phezulu naseduze kwewindi."</string>
- <string name="ut_setup_again_description" msgid="7837706010887799255">"Lokhu kuzosusa iziteshi ezitholakele kusukela kushuna ye-USB kuphinde kuskenele iziteshi ezintsha futhi.\n\nQinisekisa ukuthi ishuna ye-USB ixhunyiwe futhi ixhumeke kumthombo wesignali ye-TV.\n\nUma usebenzisa i-antenna esemoyeni, kuzomele ulungise ukubekwa kwayo noma ukubheka kwayo ukuze uthole iziteshi eziningi. Ukuze uthole imiphumela ehamba phambili, yibeke ngaphezulu naseduze kwewindi."</string>
- <string name="nt_setup_again_description" msgid="681642895365018072">"Lokhu kuzosusa iziteshi ezitholakele kusukela kushuna yenethiwekhi kuphinde kuskenele iziteshi ezintsha futhi.\n\nQinisekisa ukuthi ishuna yenethiwekhi ixhunyiwe futhi ixhumeke kumthombo wesignali ye-TV.\n\nUma usebenzisa i-antenna esemoyeni, kuzomele ulungise ukubekwa kwayo noma ukubheka kwayo ukuze uthole iziteshi eziningi. Ukuze uthole imiphumela ehamba phambili, yibeke ngaphezulu naseduze kwewindi."</string>
- <string-array name="ut_setup_again_choices">
- <item msgid="2557527790311851317">"Qhubeka"</item>
- <item msgid="235450158666155406">"Khansela"</item>
- </string-array>
- <string name="ut_connection_title" msgid="8435949189164677545">"Khetha uhlobo lokuxhumeka"</string>
- <string name="ut_connection_description" msgid="7234582943233286192">"Khetha i-Antenna uma ngabe kukhona i-antenna yangaphandle exhunywe kusishuni. Khetha ikhebuli uma ngabe iziteshi zakho ziza kusukela kumhlinzeki wesevisi yekhebuli. Uma ungaqinisekanga, zombili izinhlobo zizoskenwa, kodwa lokhu kungathatha isikhathi eside."</string>
- <string-array name="ut_connection_choices">
- <item msgid="1499878461856892555">"I-Antenna"</item>
- <item msgid="2670079958754180142">"Ikhebuli"</item>
- <item msgid="36774059871728525">"Angiqinisekile"</item>
- <item msgid="6881204453182153978">"Ukuthuthukiswa kuphela"</item>
- </string-array>
- <string name="bt_channel_scan" msgid="3291924771702347469">"Ukusethwa kweshuna ye-TV"</string>
- <string name="ut_channel_scan" msgid="6100090671500464604">"Ukusetha kweshuna yesiteshi se-USB"</string>
- <string name="nt_channel_scan" msgid="30206992732534178">"Ukusethwa kweshuna yesiteshi senethiwekhi"</string>
- <string name="ut_channel_scan_time" msgid="1844845425359642393">"Lokhu kungathatha amaminithi athile"</string>
- <string name="ut_channel_scan_tuner_unavailable" msgid="3135723754380409658">"Ishuna okwamanje ayitholakali noma isivele isetshenziswa ngokurekhodwa."</string>
- <plurals name="ut_channel_scan_message" formatted="false" msgid="3131606783282632056">
- <item quantity="one">%1$d iziteshi ezitholakele</item>
- <item quantity="other">%1$d iziteshi ezitholakele</item>
- </plurals>
- <string name="ut_stop_channel_scan" msgid="566811986747774193">"MISA UKUSKENA KWESITESHI"</string>
- <plurals name="ut_result_found_title" formatted="false" msgid="1448908152026339099">
- <item quantity="one">%1$d iziteshi ezitholakele</item>
- <item quantity="other">%1$d iziteshi ezitholakele</item>
- </plurals>
- <plurals name="ut_result_found_description" formatted="false" msgid="4132691388395648565">
- <item quantity="one">Kuhle! %1$d iziteshi ezitholakele ngesikhathi sokuskena kwesiteshi. Uma lokhu kungabonakali kulungile, zama ukulungisa ukuma kwe-antenna uphinde uskene futhi.</item>
- <item quantity="other">Kuhle! %1$d iziteshi ezitholakele ngesikhathi sokuskena kwesiteshi. Uma lokhu kungabonakali kulungile, zama ukulungisa ukuma kwe-antenna uphinde uskene futhi.</item>
- </plurals>
- <string-array name="ut_result_found_choices">
- <item msgid="3220617441427115421">"Kwenziwe"</item>
- <item msgid="2480490326672924828">"Skena futhi"</item>
- </string-array>
- <string name="ut_result_not_found_title" msgid="4649533929056795595">"Azikho iziteshi ezitholiwe"</string>
- <string name="bt_result_not_found_description" msgid="7378208337325024042">"Ukuskena akuzange kuthole iziteshi. Qinisekisa ukuthi i-TV yakho ixhumeke kumthombo wesignali we-TV.\n\nUma usebenzisa i-antenna esemoyeni, kungenzeka ukuthi kumele ulungise ukubekwa noma ukubheka ukuze uthole iziteshi eziningi. Ukuze uthole imiphumela ehamba phambili, beka phezulu naseduze kwewindi."</string>
- <string name="ut_result_not_found_description" msgid="1080746285957681414">"Iskena asizange sithole noma yiziphi iziteshi. Qinisekisa ukuthi ishuna ye-USB ixhunyiwe futhi ixhumeke kumthombo wesignali ye-TV.\n\nUma usebenzisa i-antenna esemoyeni, lungisa ukubekwa kwayo noma ukuma kwayo. Ukuze uthole imiphumela ehamba phambili, yibeke phezulu naseduze kwewindi uphinde uskene."</string>
- <string name="nt_result_not_found_description" msgid="2177919867285510855">"Iskena asizange sithole noma iziphi iziteshi. Qinisekisa ukuthi ishuna ye-USB ixhunyiwe futhi ixhumeke kumthombo wesignali ye-TV.\n\nUma usebenzisa i-antenna esemoyeni, lungisa ukubekwa kwayo noma ukuma kwayo. Ukuze uthole imiphumela ehamba phambili, yibeke phezulu naseduze kwewindi uphinde uskene."</string>
- <string-array name="ut_result_not_found_choices">
- <item msgid="5436884968471542030">"Skena futhi"</item>
- <item msgid="2092797862490235174">"Kwenziwe"</item>
- </string-array>
- <string name="ut_setup_notification_content_title" msgid="3439301313253273422">"Skenela iziteshi ze-TV"</string>
- <string name="bt_setup_notification_content_text" msgid="7578820978070596694">"Ukusethwa kweshuna ye-TV"</string>
- <string name="ut_setup_notification_content_text" msgid="1656697878628557384">"Ukusetha kweshuna ye-USB TV"</string>
- <string name="nt_setup_notification_content_text" msgid="1186152789699583895">"Ukusethwa kweshuna yenethiwekhi ye-TV"</string>
- <string name="msg_usb_tuner_disconnected" msgid="1206606328815245830">"Ishuna ye-USB TV inqanyuliwe."</string>
- <string name="msg_network_tuner_disconnected" msgid="7103193099674978964">"Ishuna yenethiwekhi inqanyuliwe."</string>
-</resources>
diff --git a/version.mk b/version.mk
index b76c39ed..57f3a43b 100644
--- a/version.mk
+++ b/version.mk
@@ -1,4 +1,4 @@
-#
+#####################################################
# Copyright (C) 2015 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -48,17 +48,17 @@
base_version_major := 1
# Change this for each branch
-base_version_minor := 15
+base_version_minor := 17
# code_version_major will overflow at 22
code_version_major := $(shell echo $$(($(base_version_major)+3)))
# x86 and arm sometimes don't match.
-code_version_build := 007
+code_version_build := 001
#####################################################
#####################################################
# Collect automatic version code parameters
-ifeq ($(strip $(HAS_BUILD_NUMBER)),false)
+ifneq "" "$(filter eng.%,$(BUILD_NUMBER))"
# This is an eng build
base_version_buildtype := 0
else
@@ -94,12 +94,12 @@ version_code_package := $(code_version_major)$(base_version_minor)$(code_version
# and hh is the git hash
# On eng builds, the BUILD_NUMBER has the user and timestamp inline
ifdef TARGET_BUILD_APPS
-ifeq ($(strip $(HAS_BUILD_NUMBER)),false)
+ifneq "" "$(filter eng.%,$(BUILD_NUMBER))"
git_hash := $(shell git --git-dir $(LOCAL_PATH)/.git log -n 1 --pretty=format:%h)
date_string := $(shell date +%Y-%m-%d)
version_name_package := $(base_version_major).$(base_version_minor).$(code_version_build) (eng.$(USER).$(git_hash).$(date_string)-$(base_version_arch)$(base_version_density))
else
- version_name_package := $(base_version_major).$(base_version_minor).$(code_version_build) ($(BUILD_NUMBER_FROM_FILE)-$(base_version_arch)$(base_version_density))
+ version_name_package := $(base_version_major).$(base_version_minor).$(code_version_build) ($(BUILD_NUMBER)-$(base_version_arch)$(base_version_density))
endif
else # !TARGET_BUILD_APPS
version_name_package := $(base_version_major).$(base_version_minor).$(code_version_build)