diff options
author | Dongwon Kang <dwkang@google.com> | 2014-08-06 00:26:00 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-08-04 23:56:40 +0000 |
commit | 333d3ee2cde4eebf4cf86e841cd7e844ad0da94c (patch) | |
tree | eb84f02db0119f7faee46d1a86843e1c92418da3 | |
parent | ec9bd88036279df7421321b2e9945adf8f89db4b (diff) | |
parent | 5642ac621a38ef0b69bfb42514d28d78b9569d11 (diff) | |
download | TV-333d3ee2cde4eebf4cf86e841cd7e844ad0da94c.tar.gz |
Merge "Add sample TV input for 2way pairing use case." into lmp-dev
9 files changed, 387 insertions, 3 deletions
diff --git a/sample/AndroidManifest.xml b/sample/AndroidManifest.xml index add8d7fc..d1a95872 100644 --- a/sample/AndroidManifest.xml +++ b/sample/AndroidManifest.xml @@ -73,5 +73,31 @@ <meta-data android:name="android.media.tv.input" android:resource="@xml/externalfiletvinputservice" /> </service> + <activity + android:name="com.example.android.sampleproxyservice.SampleProxySetupActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + </intent-filter> + </activity> + <service android:name="com.example.android.sampleproxyservice.FakeHdmiTvInputService" + android:permission="android.permission.BIND_TV_INPUT" + android:label="@string/fake_hdmi_input_label" + android:process=":remoteProxyTvInput"> + <intent-filter> + <action android:name="android.media.tv.TvInputService" /> + </intent-filter> + <meta-data android:name="android.media.tv.input" + android:resource="@xml/sampleproxyservice" /> + </service> + <service android:name="com.example.android.sampleproxyservice.SampleProxyService" + android:permission="android.permission.BIND_TV_INPUT" + android:label="@string/proxy_input_label" + android:process=":remoteProxyTvInput"> + <intent-filter> + <action android:name="android.media.tv.TvInputService" /> + </intent-filter> + <meta-data android:name="android.media.tv.input" + android:resource="@xml/sampleproxyservice" /> + </service> </application> </manifest> diff --git a/sample/res/values/strings.xml b/sample/res/values/strings.xml index 64add5d1..60bda09d 100644 --- a/sample/res/values/strings.xml +++ b/sample/res/values/strings.xml @@ -19,6 +19,8 @@ <string name="local_input_label">Sample Input 1: Overlay View Test</string> <string name="hls_input_label">Sample Input 2: HLS Streaming</string> <string name="external_input_label">Sample Input 3: External Files</string> + <string name="fake_hdmi_input_label">Sample Input 4: Fake HDMI</string> + <string name="proxy_input_label">Sample Input 5: Hardware Proxy </string> <string name="overlay_view_test">Overlay View Test: press U to toggle the menu</string> <string name="webview_toggle_button">Toggle WebView</string> <string name="overlay_view_close_button">Close OverlayView</string> diff --git a/sample/res/xml/sampleproxyservice.xml b/sample/res/xml/sampleproxyservice.xml new file mode 100644 index 00000000..c728426b --- /dev/null +++ b/sample/res/xml/sampleproxyservice.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * 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.example.android.sampleproxyservice.SampleProxySetupActivity" /> diff --git a/sample/src/com/example/android/sampleproxyservice/ExternalSettopBox.java b/sample/src/com/example/android/sampleproxyservice/ExternalSettopBox.java new file mode 100644 index 00000000..f8591f4e --- /dev/null +++ b/sample/src/com/example/android/sampleproxyservice/ExternalSettopBox.java @@ -0,0 +1,75 @@ +package com.example.android.sampleproxyservice; + +import android.media.MediaPlayer; +import android.util.Log; +import android.view.Surface; + +import java.io.IOException; + +/** + * Represents a external set-top box connected to a pass-through TV input. + */ +public class ExternalSettopBox { + private static final String TAG = ExternalSettopBox.class.getSimpleName(); + private static final boolean DEBUG = true; + + private static final String DEFAULT_URL = + "http://devimages.apple.com/iphone/samples/bipbop/gear1/prog_index.m3u8"; + private static ExternalSettopBox sInstance; + + private MediaPlayer mPlayer; + private String mUrl; + private Surface mSurface; + + private ExternalSettopBox() { + mUrl = DEFAULT_URL; + } + + public synchronized static ExternalSettopBox getInstance() { + if (sInstance == null) { + sInstance = new ExternalSettopBox(); + } + return sInstance; + } + + public synchronized void setSurface(Surface surface) { + if (DEBUG) Log.d(TAG, "setSurface(" + surface + ")"); + mSurface = surface; + resetPlayback(); + } + + public synchronized void setVolume(float volume) { + if (DEBUG) Log.d(TAG, "setVolume(" + volume + ")"); + mPlayer.setVolume(volume, volume); + } + + public synchronized void tune(String url) { + if (DEBUG) Log.d(TAG, "tune(" + url + ")"); + mUrl = url; + resetPlayback(); + } + + private void resetPlayback() { + if (DEBUG) Log.d(TAG, "resetPlayback(" + mUrl + ")"); + if (mPlayer != null) { + if (DEBUG) Log.d(TAG, "Stopping"); + mPlayer.stop(); + mPlayer.release(); + mPlayer = null; + } + try { + if (mSurface != null) { + if (DEBUG) Log.d(TAG, "Starting " + mUrl); + mPlayer = new MediaPlayer(); + mPlayer.setSurface(mSurface); + mPlayer.setDataSource(mUrl); + mPlayer.prepare(); + mPlayer.setLooping(true); + mPlayer.start(); + } + } catch (IllegalArgumentException | SecurityException | IllegalStateException + | IOException e) { + Log.e(TAG, "Failed to play: " + mUrl); + } + } +} diff --git a/sample/src/com/example/android/sampleproxyservice/FakeHdmiTvInputService.java b/sample/src/com/example/android/sampleproxyservice/FakeHdmiTvInputService.java new file mode 100644 index 00000000..04acbea3 --- /dev/null +++ b/sample/src/com/example/android/sampleproxyservice/FakeHdmiTvInputService.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.sampleproxyservice; + +import android.media.tv.TvInputService; +import android.net.Uri; +import android.view.Surface; + +/** + * Represents a simple HDMI input. + */ +public class FakeHdmiTvInputService extends TvInputService { + private ExternalSettopBox mExternalSettopBox = ExternalSettopBox.getInstance(); + + @Override + public Session onCreateSession(String inputId) { + return new FakeHdmiSession(); + } + + private class FakeHdmiSession extends Session { + @Override + public void onRelease() { + } + + @Override + public void onSetCaptionEnabled(boolean enabled) { + // No-op. + } + + @Override + public void onSetStreamVolume(float volume) { + mExternalSettopBox.setVolume(volume); + } + + @Override + public boolean onSetSurface(Surface surface) { + mExternalSettopBox.setSurface(surface); + return true; + } + + @Override + public boolean onTune(Uri channel) { + // No-op. + return false; + } + } +} diff --git a/sample/src/com/example/android/sampleproxyservice/SampleProxyService.java b/sample/src/com/example/android/sampleproxyservice/SampleProxyService.java new file mode 100644 index 00000000..e0914ab0 --- /dev/null +++ b/sample/src/com/example/android/sampleproxyservice/SampleProxyService.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.sampleproxyservice; + +import android.content.Context; +import android.database.Cursor; +import android.media.tv.TvContract; +import android.media.tv.TvInputInfo; +import android.media.tv.TvInputManager; +import android.media.tv.TvInputService; +import android.net.Uri; +import android.util.Log; + +import java.util.List; + +/** + * Demonstrates how to implement a TV input service which represents a external device connected to + * a hardware TV input. + */ +public class SampleProxyService extends TvInputService { + private static final String TAG = SampleProxyService.class.getSimpleName(); + private static final boolean DEBUG = true; + + private String mHardwareTvInputId; + private TvInputManager mTvInputManager; + + @Override + public void onCreate() { + if (DEBUG) Log.d(TAG, "onCreate()"); + super.onCreate(); + mTvInputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE); + } + + @Override + public Session onCreateSession(String inputId) { + return new SampleSession(); + } + + private class SampleSession extends HardwareSession { + private ExternalSettopBox mExternalSettopBox = ExternalSettopBox.getInstance(); + + @Override + public boolean onTune(Uri channel) { + if (DEBUG) Log.d(TAG, "onTune(" + channel + ")"); + String[] projection = { TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA }; + + Cursor cursor = null; + try { + cursor = getContentResolver().query(channel, projection, null, null, null); + if (cursor == null || cursor.getCount() == 0) { + if (DEBUG) Log.d(TAG, "Can't find channel in TvProvider."); + return false; + } + cursor.moveToNext(); + String url = cursor.getString(0); + if (DEBUG) Log.d(TAG, "Tuning to: " + url); + mExternalSettopBox.tune(url); + } finally { + if (cursor != null) { + cursor.close(); + } + } + return true; + } + + @Override + public void onRelease() { + // Do nothing. + } + + @Override + public void onSetCaptionEnabled(boolean enabled) { + // Do nothing. + } + + @Override + public void onSetStreamVolume(float volume) { + // Do Nothing. + } + + @Override + public String getHardwareInputId() { + if (mHardwareTvInputId != null) { + return mHardwareTvInputId; + } + List<TvInputInfo> inputs = mTvInputManager.getTvInputList(); + // TODO: This should be set in the setup phase. + String hdmiServiceName = FakeHdmiTvInputService.class.getSimpleName(); + for (TvInputInfo info : inputs) { + if (info.getComponent().toString().contains(hdmiServiceName)) { + mHardwareTvInputId = info.getId(); + return mHardwareTvInputId; + } + } + if (DEBUG) Log.e(TAG, "Cannot find input for: " + hdmiServiceName); + return null; + } + } +} diff --git a/sample/src/com/example/android/sampleproxyservice/SampleProxySetupActivity.java b/sample/src/com/example/android/sampleproxyservice/SampleProxySetupActivity.java new file mode 100644 index 00000000..4d1e7ca1 --- /dev/null +++ b/sample/src/com/example/android/sampleproxyservice/SampleProxySetupActivity.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.sampleproxyservice; + +import android.app.Activity; +import android.content.ContentValues; +import android.database.Cursor; +import android.media.tv.TvContract; +import android.media.tv.TvContract.Channels; +import android.media.tv.TvInputInfo; +import android.net.Uri; +import android.os.Bundle; + +public class SampleProxySetupActivity extends Activity { + private static final String TAG = SampleProxySetupActivity.class.getSimpleName(); + private static final boolean DEBUG = true; + + private static final String CHANNEL_NUMBER_1 = "1"; + private static final String CHANNEL_NAME_1 = "BIPBOP"; + private static final String CHANNEL_URL_1 = + "http://devimages.apple.com/iphone/samples/bipbop/gear1/prog_index.m3u8"; + + private static final String CHANNEL_NUMBER_2 = "2"; + private static final String CHANNEL_NAME_2 = "BUNNY"; + private static final String CHANNEL_URL_2 = + "http://playertest.longtailvideo.com/adaptive/bbbfull/bbbfull.m3u8"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + String inputId = getIntent().getStringExtra(TvInputInfo.EXTRA_INPUT_ID); + createSampleChannels(inputId); + setResult(Activity.RESULT_OK); + finish(); + } + + private void createSampleChannels(String inputId) { + Uri uri = TvContract.buildChannelsUriForInput(inputId); + String[] projection = { TvContract.Channels._ID }; + + Cursor cursor = null; + try { + while (true) { + cursor = getContentResolver().query(uri, projection, null, null, null); + if (cursor != null && cursor.getCount() > 0) { + return; + } + if (cursor != null) { + cursor.close(); + cursor = null; + } + ContentValues values = new ContentValues(); + values.put(Channels.COLUMN_INPUT_ID, inputId); + + values.put(Channels.COLUMN_DISPLAY_NUMBER, CHANNEL_NUMBER_1); + values.put(Channels.COLUMN_DISPLAY_NAME, CHANNEL_NAME_1); + values.put(Channels.COLUMN_INTERNAL_PROVIDER_DATA, CHANNEL_URL_1); + getContentResolver().insert(TvContract.Channels.CONTENT_URI, values); + + values.put(Channels.COLUMN_DISPLAY_NUMBER, CHANNEL_NUMBER_2); + values.put(Channels.COLUMN_DISPLAY_NAME, CHANNEL_NAME_2); + values.put(Channels.COLUMN_INTERNAL_PROVIDER_DATA, CHANNEL_URL_2); + getContentResolver().insert(TvContract.Channels.CONTENT_URI, values); + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + } +} diff --git a/sample/src/com/example/sampletvinput/BaseTvInputService.java b/sample/src/com/example/sampletvinput/BaseTvInputService.java index 14711749..39d79e67 100644 --- a/sample/src/com/example/sampletvinput/BaseTvInputService.java +++ b/sample/src/com/example/sampletvinput/BaseTvInputService.java @@ -76,7 +76,7 @@ abstract public class BaseTvInputService extends TvInputService { } @Override - public TvInputService.Session onCreateSession(String inputId) { + public Session onCreateSession(String inputId) { if (DEBUG) Log.d(TAG, "onCreateSession(inputId=" + inputId + ")"); return new BaseTvInputSessionImpl(); } diff --git a/sample/src/com/example/sampletvinput/LocalTvInputService.java b/sample/src/com/example/sampletvinput/LocalTvInputService.java index 7e2fbefb..0a69c77f 100644 --- a/sample/src/com/example/sampletvinput/LocalTvInputService.java +++ b/sample/src/com/example/sampletvinput/LocalTvInputService.java @@ -16,8 +16,8 @@ package com.example.sampletvinput; -import android.media.tv.TvInputService; import android.media.tv.TvContentRating; +import android.media.tv.TvInputService; import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -52,7 +52,7 @@ public class LocalTvInputService extends BaseTvInputService { private static final String WEBVIEW_SITE = "http://www.android.com"; @Override - public TvInputService.Session onCreateSession(String inputId) { + public Session onCreateSession(String inputId) { if (DEBUG) Log.d(TAG, "onCreateSession(inputId=" + inputId + ")"); TvInputService.Session impl = new LocalTvInputSessionImpl(); impl.setOverlayViewEnabled(true); |