diff options
Diffstat (limited to 'tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample')
8 files changed, 805 insertions, 0 deletions
diff --git a/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/AndroidManifest.xml b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/AndroidManifest.xml new file mode 100644 index 00000000..dddd8a4b --- /dev/null +++ b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/AndroidManifest.xml @@ -0,0 +1,45 @@ +<?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.network" 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="27" android:minSdkVersion="23"/> + <application + android:name=".app.SampleNetworkTuner" + android:icon="@mipmap/ic_launcher" + android:label="@string/sample_network_tuner_app_name" + /> +</manifest> diff --git a/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/README.md b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/README.md new file mode 100644 index 00000000..9df86c53 --- /dev/null +++ b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/README.md @@ -0,0 +1,37 @@ +# SampleNetworkTuner + +SampleNetworkTuner 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.network"> + <permission name="android.permission.DVB_DEVICE"/> +</privapp-permissions> +``` + +### Push to system/priv-app + +```bash +adb shell mkdir /system/priv-app/SampleNetworkTuner +adb push <path to apk> /system/priv-app/SampleNetworkTuner/SampleNetworkTuner.apk +``` diff --git a/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/app/SampleNetworkTuner.java b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/app/SampleNetworkTuner.java new file mode 100644 index 00000000..eb5b2ad4 --- /dev/null +++ b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/app/SampleNetworkTuner.java @@ -0,0 +1,88 @@ +/* + * 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.network.app; + +import android.content.ComponentName; +import android.media.tv.TvContract; +import com.android.tv.common.BaseApplication; +import com.android.tv.common.singletons.HasSingletons; +import com.android.tv.tuner.modules.TunerSingletonsModule; +import com.android.tv.tuner.sample.network.singletons.SampleNetworkSingletons; +import com.android.tv.tuner.sample.network.tvinput.SampleNetworkTunerTvInputService; +import com.android.tv.tuner.tvinput.factory.TunerSessionFactory; +import com.android.tv.tuner.tvinput.factory.TunerSessionFactoryImpl; +import dagger.android.AndroidInjector; +import com.android.tv.common.flags.CloudEpgFlags; +import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags; +import javax.inject.Inject; + +/** The top level application for Sample DVB Tuner. */ +public class SampleNetworkTuner extends BaseApplication + implements SampleNetworkSingletons, HasSingletons<SampleNetworkSingletons> { + + private String mEmbeddedInputId; + @Inject CloudEpgFlags mCloudEpgFlags; + @Inject ConcurrentDvrPlaybackFlags mConcurrentDvrPlaybackFlags; + @Inject TunerSessionFactoryImpl mTunerSessionFactory; + + @Override + public void onCreate() { + super.onCreate(); + } + + @Override + protected AndroidInjector<SampleNetworkTuner> applicationInjector() { + return DaggerSampleNetworkTunerComponent.builder() + .sampleNetworkTunerModule(new SampleNetworkTunerModule(this)) + .tunerSingletonsModule(new TunerSingletonsModule(this)) + .build(); + } + + @Override + public synchronized String getEmbeddedTunerInputId() { + if (mEmbeddedInputId == null) { + mEmbeddedInputId = + TvContract.buildInputId( + new ComponentName(this, SampleNetworkTunerTvInputService.class)); + } + return mEmbeddedInputId; + } + + @Override + public CloudEpgFlags getCloudEpgFlags() { + return mCloudEpgFlags; + } + + @Override + public BuildType getBuildType() { + return BuildType.ENG; + } + + @Override + public ConcurrentDvrPlaybackFlags getConcurrentDvrPlaybackFlags() { + return mConcurrentDvrPlaybackFlags; + } + + @Override + public SampleNetworkSingletons singletons() { + return this; + } + + public TunerSessionFactory getTunerSessionFactory() { + return mTunerSessionFactory; + } +} diff --git a/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/app/SampleNetworkTunerComponent.java b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/app/SampleNetworkTunerComponent.java new file mode 100644 index 00000000..b10105b3 --- /dev/null +++ b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/app/SampleNetworkTunerComponent.java @@ -0,0 +1,26 @@ +/* + * 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.network.app; + +import dagger.Component; +import dagger.android.AndroidInjectionModule; +import dagger.android.AndroidInjector; +import javax.inject.Singleton; + +/** Dagger component for {@link SampleNetworkTuner}. */ +@Singleton +@Component(modules = {AndroidInjectionModule.class, SampleNetworkTunerModule.class}) +public interface SampleNetworkTunerComponent extends AndroidInjector<SampleNetworkTuner> {} diff --git a/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/app/SampleNetworkTunerModule.java b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/app/SampleNetworkTunerModule.java new file mode 100644 index 00000000..d974e20a --- /dev/null +++ b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/app/SampleNetworkTunerModule.java @@ -0,0 +1,52 @@ +/* + * 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.network.app; + +import com.android.tv.common.flags.impl.DefaultFlagsModule; +import com.android.tv.tuner.api.TunerFactory; +import com.android.tv.tuner.builtin.BuiltInTunerHalFactory; +import com.android.tv.tuner.modules.TunerModule; +import com.android.tv.tuner.sample.network.setup.SampleNetworkTunerSetupActivity; +import com.android.tv.tuner.sample.network.tvinput.SampleNetworkTunerTvInputService; +import com.android.tv.tuner.tvinput.factory.TunerSessionFactory; +import dagger.Module; +import dagger.Provides; + +/** Dagger module for {@link SampleNetworkTuner}. */ +@Module( + includes = { + DefaultFlagsModule.class, + SampleNetworkTunerTvInputService.Module.class, + SampleNetworkTunerSetupActivity.Module.class, + TunerModule.class, + }) +class SampleNetworkTunerModule { + private final SampleNetworkTuner mSampleNetworkTuner; + + SampleNetworkTunerModule(SampleNetworkTuner sampleNetworkTuner) { + mSampleNetworkTuner = sampleNetworkTuner; + } + + @Provides + public TunerSessionFactory providesTunerSessionFactory() { + return mSampleNetworkTuner.getTunerSessionFactory(); + } + + @Provides + TunerFactory providesTunerFactory() { + return BuiltInTunerHalFactory.INSTANCE; + } +} diff --git a/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/setup/SampleNetworkTunerSetupActivity.java b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/setup/SampleNetworkTunerSetupActivity.java new file mode 100644 index 00000000..fd783c4f --- /dev/null +++ b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/setup/SampleNetworkTunerSetupActivity.java @@ -0,0 +1,500 @@ +/* + * 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.network.setup; + +import android.app.FragmentManager; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Intent; +import android.content.pm.PackageManager; +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.feature.CommonFeatures; +import com.android.tv.common.singletons.HasSingletons; +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.sample.network.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.LocationFragment; +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.android.tv.tuner.singletons.TunerSingletons; +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 dagger.android.ContributesAndroidInjector; +import java.util.ArrayList; +import java.util.List; + +/** An activity that serves Live TV tuner setup process. */ +public class SampleNetworkTunerSetupActivity extends BaseTunerSetupActivity { + private static final String TAG = "SampleNetworkTunerSetupActivity"; + 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 = this::cancelFetchLineup; + private String embeddedInputId; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (DEBUG) { + Log.d(TAG, "onCreate"); + } + embeddedInputId = + HasSingletons.get(TunerSingletons.class, getApplicationContext()) + .getEmbeddedTunerInputId(); + new QueryEpgInputTask(embeddedInputId).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + @Override + protected void executeGetTunerTypeAndCountAsyncTask() { + new AsyncTask<Void, Void, Integer>() { + @Override + protected Integer doInBackground(Void... arg0) { + return mTunerFactory.getTunerTypeAndCount(SampleNetworkTunerSetupActivity.this) + .first; + } + + @Override + protected void onPostExecute(Integer result) { + if (!SampleNetworkTunerSetupActivity.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); + boolean needLocation = + CommonFeatures.ENABLE_CLOUD_EPG_REGION.isEnabled( + getApplicationContext()) + && TextUtils.isEmpty(postalCode); + if (needLocation + && checkSelfPermission( + android.Manifest.permission.ACCESS_COARSE_LOCATION) + != PackageManager.PERMISSION_GRANTED) { + showLocationFragment(); + } else if (mNeedToShowPostalCodeFragment || needLocation) { + // 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 LocationFragment.ACTION_CATEGORY: + switch (actionId) { + case LocationFragment.ACTION_ALLOW_PERMISSION: + String postalCode = + params == null + ? null + : params.getString(LocationFragment.KEY_POSTAL_CODE); + if (postalCode == null) { + showPostalCodeFragment(); + } else { + this.postalCode = postalCode; + restartFetchLineupTask(); + showConnectionTypeFragment(); + } + break; + default: + cancelFetchLineup(); + showConnectionTypeFragment(); + } + 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 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) + || checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) + != PackageManager.PERMISSION_GRANTED) { + 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"); + } + } + SampleNetworkTunerSetupActivity.this.lineups = lineups; + if (currentLineupFragment != null) { + if (lineups == null || lineups.isEmpty()) { + currentLineupFragment.onLineupNotFound(); + } else { + lineupMatchCountPair = + TunerSetupUtils.lineupChannelMatchCount( + SampleNetworkTunerSetupActivity.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 + || (SampleNetworkTunerSetupActivity.this.epgInput != null + && TextUtils.equals( + lineup.getId(), + SampleNetworkTunerSetupActivity.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 (SampleNetworkTunerSetupActivity.this.epgInput != null) { + values.put( + EpgContract.EpgInputs.COLUMN_ID, + SampleNetworkTunerSetupActivity.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(); + } + } + + /** + * Exports {@link SampleNetworkTunerSetupActivity} for Dagger codegen to create the appropriate + * injector. + */ + @dagger.Module + public abstract static class Module { + @ContributesAndroidInjector + abstract SampleNetworkTunerSetupActivity + contributeSampleNetworkTunerSetupActivityInjector(); + } + + 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/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/singletons/SampleNetworkSingletons.java b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/singletons/SampleNetworkSingletons.java new file mode 100644 index 00000000..00a6e27a --- /dev/null +++ b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/singletons/SampleNetworkSingletons.java @@ -0,0 +1,23 @@ +/* + * 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.network.singletons; + +import com.android.tv.common.BaseSingletons; +import com.android.tv.tuner.singletons.TunerSingletons; + +/** Singletons for SampleNetworkTuner. */ +public interface SampleNetworkSingletons extends BaseSingletons, TunerSingletons {} diff --git a/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/tvinput/SampleNetworkTunerTvInputService.java b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/tvinput/SampleNetworkTunerTvInputService.java new file mode 100644 index 00000000..de5ff229 --- /dev/null +++ b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/tvinput/SampleNetworkTunerTvInputService.java @@ -0,0 +1,34 @@ +/* + * 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.network.tvinput; + +import com.android.tv.tuner.tvinput.BaseTunerTvInputService; +import dagger.android.ContributesAndroidInjector; + +/** Sample DVB Tuner {@link android.media.tv.TvInputService}. */ +public class SampleNetworkTunerTvInputService extends BaseTunerTvInputService { + + /** + * Exports {@link SampleNetworkTunerTvInputService} for Dagger codegen to create the appropriate + * injector. + */ + @dagger.Module + public abstract static class Module { + @ContributesAndroidInjector + abstract SampleNetworkTunerTvInputService + contributesSampleNetworkTunerTvInputServiceInjector(); + } +} |