1a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo/* 2a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * Copyright (C) 2016 The Android Open Source Project 3a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * 4a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * Licensed under the Apache License, Version 2.0 (the "License"); 5a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * you may not use this file except in compliance with the License. 6a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * You may obtain a copy of the License at 7a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * 8a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * http://www.apache.org/licenses/LICENSE-2.0 9a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * 10a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * Unless required by applicable law or agreed to in writing, software 11a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * distributed under the License is distributed on an "AS IS" BASIS, 12a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * See the License for the specific language governing permissions and 14a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * limitations under the License. 15a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo */ 16a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 17a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seopackage android.media.tv; 18a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 19a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seoimport android.annotation.NonNull; 204eee6a73e476cd2d82a69f3a535628901047f140Jae Seoimport android.annotation.Nullable; 21a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seoimport android.annotation.SystemApi; 22a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seoimport android.content.Context; 23e3c11e842937f50f54c9d82363f33338dc9e261bJae Seoimport android.media.tv.TvInputManager; 24a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seoimport android.net.Uri; 25a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seoimport android.os.Bundle; 26a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seoimport android.os.Handler; 27a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seoimport android.os.Looper; 28a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seoimport android.text.TextUtils; 29a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seoimport android.util.Log; 30a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seoimport android.util.Pair; 31a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 32a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seoimport java.util.ArrayDeque; 33a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seoimport java.util.Queue; 34a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 35a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo/** 36a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * The public interface object used to interact with a specific TV input service for TV program 37a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * recording. 38a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo */ 39a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seopublic class TvRecordingClient { 40a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo private static final String TAG = "TvRecordingClient"; 41a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo private static final boolean DEBUG = false; 42a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 43a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo private final RecordingCallback mCallback; 44a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo private final Handler mHandler; 45a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 46a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo private final TvInputManager mTvInputManager; 47a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo private TvInputManager.Session mSession; 48a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo private MySessionCallback mSessionCallback; 49a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 50e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo private boolean mIsRecordingStarted; 51e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo private boolean mIsTuned; 52a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo private final Queue<Pair<String, Bundle>> mPendingAppPrivateCommands = new ArrayDeque<>(); 53a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 54a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo /** 55a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * Creates a new TvRecordingClient object. 56a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * 57e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * @param context The application context to create a TvRecordingClient with. 58a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * @param tag A short name for debugging purposes. 59a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * @param callback The callback to receive recording status changes. 60a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * @param handler The handler to invoke the callback on. 61a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo */ 62a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo public TvRecordingClient(Context context, String tag, @NonNull RecordingCallback callback, 63a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo Handler handler) { 64a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mCallback = callback; 65a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mHandler = handler == null ? new Handler(Looper.getMainLooper()) : handler; 66a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mTvInputManager = (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE); 67a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 68a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 69a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo /** 70e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * Tunes to a given channel for TV program recording. The first tune request will create a new 7125c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo * recording session for the corresponding TV input and establish a connection between the 72e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * application and the session. If recording has already started in the current recording 73e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * session, this method throws an exception. 74e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * 75e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * <p>The application may call this method before starting or after stopping recording, but not 76e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * during recording. 77a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * 78a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * <p>The recording session will respond by calling 79b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang * {@link RecordingCallback#onTuned(Uri)} if the tune request was fulfilled, or 80e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * {@link RecordingCallback#onError(int)} otherwise. 81a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * 82a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * @param inputId The ID of the TV input for the given channel. 83a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * @param channelUri The URI of a channel. 84b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang * @throws IllegalStateException If recording is already started. 85a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo */ 86e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo public void tune(String inputId, Uri channelUri) { 87e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo tune(inputId, channelUri, null); 88a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 89a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 90a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo /** 91e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * Tunes to a given channel for TV program recording. The first tune request will create a new 9225c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo * recording session for the corresponding TV input and establish a connection between the 93e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * application and the session. If recording has already started in the current recording 94d48d029da43babf265fccbf5d84a06b4b275f72cJiabin * session, this method throws an exception. This can be used to provide domain-specific 95d48d029da43babf265fccbf5d84a06b4b275f72cJiabin * features that are only known between certain client and their TV inputs. 96e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * 97e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * <p>The application may call this method before starting or after stopping recording, but not 98e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * during recording. 99a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * 100a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * <p>The recording session will respond by calling 101d48d029da43babf265fccbf5d84a06b4b275f72cJiabin * {@link RecordingCallback#onTuned(Uri)} if the tune request was fulfilled, or 102e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * {@link RecordingCallback#onError(int)} otherwise. 103a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * 104a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * @param inputId The ID of the TV input for the given channel. 105a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * @param channelUri The URI of a channel. 106d48d029da43babf265fccbf5d84a06b4b275f72cJiabin * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped 107d48d029da43babf265fccbf5d84a06b4b275f72cJiabin * name, i.e. prefixed with a package name you own, so that different developers will 108d48d029da43babf265fccbf5d84a06b4b275f72cJiabin * not create conflicting keys. 109b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang * @throws IllegalStateException If recording is already started. 110a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo */ 111e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo public void tune(String inputId, Uri channelUri, Bundle params) { 112e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo if (DEBUG) Log.d(TAG, "tune(" + channelUri + ")"); 113a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo if (TextUtils.isEmpty(inputId)) { 114a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo throw new IllegalArgumentException("inputId cannot be null or an empty string"); 115a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 116e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo if (mIsRecordingStarted) { 117e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo throw new IllegalStateException("tune failed - recording already started"); 118e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo } 119a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo if (mSessionCallback != null && TextUtils.equals(mSessionCallback.mInputId, inputId)) { 120a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo if (mSession != null) { 121e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo mSession.tune(channelUri, params); 122a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } else { 123a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mSessionCallback.mChannelUri = channelUri; 124a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mSessionCallback.mConnectionParams = params; 125a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 126a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } else { 127a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo resetInternal(); 128a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mSessionCallback = new MySessionCallback(inputId, channelUri, params); 129a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo if (mTvInputManager != null) { 130a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mTvInputManager.createRecordingSession(inputId, mSessionCallback, mHandler); 131a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 132a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 133a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 134a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 135a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo /** 136e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * Releases the resources in the current recording session immediately. This may be called at 137e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * any time, however if the session is already released, it does nothing. 138a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo */ 139e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo public void release() { 140e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo if (DEBUG) Log.d(TAG, "release()"); 141a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo resetInternal(); 142a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 143a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 144a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo private void resetInternal() { 145a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mSessionCallback = null; 146a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mPendingAppPrivateCommands.clear(); 147a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo if (mSession != null) { 148a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mSession.release(); 149a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mSession = null; 150a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 151a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 152a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 153a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo /** 154e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * Starts TV program recording in the current recording session. Recording is expected to start 155e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * immediately when this method is called. If the current recording session has not yet tuned to 156e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * any channel, this method throws an exception. 157a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * 158b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang * <p>The application may supply the URI for a TV program for filling in program specific data 159b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang * fields in the {@link android.media.tv.TvContract.RecordedPrograms} table. 1600cb5244e52590214ddc16dd5fc1030b5baf04726Dongwon Kang * A non-null {@code programUri} implies the started recording should be of that specific 161308fe5741f05062d20664afb40e9735cf3458510Dongwon Kang * program, whereas null {@code programUri} does not impose such a requirement and the 162e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * recording can span across multiple TV programs. In either case, the application must call 163e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * {@link TvRecordingClient#stopRecording()} to stop the recording. 1644eee6a73e476cd2d82a69f3a535628901047f140Jae Seo * 165e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * <p>The recording session will respond by calling {@link RecordingCallback#onError(int)} if 166e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * the start request cannot be fulfilled. 1674eee6a73e476cd2d82a69f3a535628901047f140Jae Seo * 168b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang * @param programUri The URI for the TV program to record, built by 169e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * {@link TvContract#buildProgramUri(long)}. Can be {@code null}. 170b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang * @throws IllegalStateException If {@link #tune} request hasn't been handled yet. 171a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo */ 172b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang public void startRecording(@Nullable Uri programUri) { 173e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo if (!mIsTuned) { 174e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo throw new IllegalStateException("startRecording failed - not yet tuned"); 175e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo } 176a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo if (mSession != null) { 177b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang mSession.startRecording(programUri); 178e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo mIsRecordingStarted = true; 179a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 180a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 181a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 182a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo /** 183e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * Stops TV program recording in the current recording session. Recording is expected to stop 184e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * immediately when this method is called. If recording has not yet started in the current 185e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * recording session, this method does nothing. 186a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * 187e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * <p>The recording session is expected to create a new data entry in the 188e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * {@link android.media.tv.TvContract.RecordedPrograms} table that describes the newly 189e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * recorded program and pass the URI to that entry through to 190e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * {@link RecordingCallback#onRecordingStopped(Uri)}. 191e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * If the stop request cannot be fulfilled, the recording session will respond by calling 192e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * {@link RecordingCallback#onError(int)}. 193a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo */ 194a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo public void stopRecording() { 195e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo if (!mIsRecordingStarted) { 196e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo Log.w(TAG, "stopRecording failed - recording not yet started"); 197e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo } 198a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo if (mSession != null) { 199a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mSession.stopRecording(); 200a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 201a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 202a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 203a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo /** 204f714e62c12d99d816d70d09da60b6885a1368cefDongwon Kang * Sends a private command to the underlying TV input. This can be used to provide 205f714e62c12d99d816d70d09da60b6885a1368cefDongwon Kang * domain-specific features that are only known between certain clients and their TV inputs. 206a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * 207a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * @param action The name of the private command to send. This <em>must</em> be a scoped name, 208a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * i.e. prefixed with a package name you own, so that different developers will not 209a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * create conflicting commands. 210a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * @param data An optional bundle to send with the command. 211a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo */ 212a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo public void sendAppPrivateCommand(@NonNull String action, Bundle data) { 213a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo if (TextUtils.isEmpty(action)) { 214a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo throw new IllegalArgumentException("action cannot be null or an empty string"); 215a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 216a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo if (mSession != null) { 217a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mSession.sendAppPrivateCommand(action, data); 218a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } else { 219a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo Log.w(TAG, "sendAppPrivateCommand - session not yet created (action \"" + action 220a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo + "\" pending)"); 221a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mPendingAppPrivateCommands.add(Pair.create(action, data)); 222a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 223a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 224a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 225a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo /** 226a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * Callback used to receive various status updates on the 227a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * {@link android.media.tv.TvInputService.RecordingSession} 228a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo */ 229397b1447ab4533f8cb87512689524f8d860bb0a5Dongwon Kang public abstract static class RecordingCallback { 230a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo /** 23125c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo * This is called when an error occurred while establishing a connection to the recording 23225c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo * session for the corresponding TV input. 23325c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo * 23425c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo * @param inputId The ID of the TV input bound to the current TvRecordingClient. 23525c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo */ 23625c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo public void onConnectionFailed(String inputId) { 23725c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo } 23825c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo 23925c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo /** 24025c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo * This is called when the connection to the current recording session is lost. 24125c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo * 24225c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo * @param inputId The ID of the TV input bound to the current TvRecordingClient. 24325c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo */ 24425c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo public void onDisconnected(String inputId) { 24525c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo } 24625c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo 24725c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo /** 248e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * This is called when the recording session has been tuned to the given channel and is 249e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * ready to start recording. 250b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang * 251b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang * @param channelUri The URI of a channel. 252a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo */ 253b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang public void onTuned(Uri channelUri) { 254a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 255a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 256a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo /** 257e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * This is called when the current recording session has stopped recording and created a 258e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * new data entry in the {@link TvContract.RecordedPrograms} table that describes the newly 259e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * recorded program. 260a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * 261e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * @param recordedProgramUri The URI for the newly recorded program. 262a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo */ 263a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo public void onRecordingStopped(Uri recordedProgramUri) { 264a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 265a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 266a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo /** 267e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * This is called when an issue has occurred. It may be called at any time after the current 268e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo * recording session is created until it is released. 269a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * 270a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * @param error The error code. Should be one of the followings. 271a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * <ul> 272a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * <li>{@link TvInputManager#RECORDING_ERROR_UNKNOWN} 273a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * <li>{@link TvInputManager#RECORDING_ERROR_INSUFFICIENT_SPACE} 274a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * <li>{@link TvInputManager#RECORDING_ERROR_RESOURCE_BUSY} 275a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * </ul> 276a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo */ 277a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo public void onError(@TvInputManager.RecordingError int error) { 278a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 279a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 280a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo /** 281a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * This is invoked when a custom event from the bound TV input is sent to this client. 282a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * 283a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * @param inputId The ID of the TV input bound to this client. 284a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * @param eventType The type of the event. 285a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * @param eventArgs Optional arguments of the event. 286a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo * @hide 287a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo */ 288a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo @SystemApi 289a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo public void onEvent(String inputId, String eventType, Bundle eventArgs) { 290a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 291a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 292a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 293a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo private class MySessionCallback extends TvInputManager.SessionCallback { 294a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo final String mInputId; 295a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo Uri mChannelUri; 296a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo Bundle mConnectionParams; 297a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 298a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo MySessionCallback(String inputId, Uri channelUri, Bundle connectionParams) { 299a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mInputId = inputId; 300a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mChannelUri = channelUri; 301a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mConnectionParams = connectionParams; 302a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 303a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 304a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo @Override 305a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo public void onSessionCreated(TvInputManager.Session session) { 306a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo if (DEBUG) { 307a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo Log.d(TAG, "onSessionCreated()"); 308a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 309a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo if (this != mSessionCallback) { 310a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo Log.w(TAG, "onSessionCreated - session already created"); 311a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo // This callback is obsolete. 312a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo if (session != null) { 313a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo session.release(); 314a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 315a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo return; 316a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 317a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mSession = session; 318a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo if (session != null) { 319a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo // Sends the pending app private commands. 320a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo for (Pair<String, Bundle> command : mPendingAppPrivateCommands) { 321a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mSession.sendAppPrivateCommand(command.first, command.second); 322a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 323a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mPendingAppPrivateCommands.clear(); 324e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo mSession.tune(mChannelUri, mConnectionParams); 325a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } else { 326a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mSessionCallback = null; 32725c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo if (mCallback != null) { 32825c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo mCallback.onConnectionFailed(mInputId); 32925c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo } 330a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 331a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 332a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 333a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo @Override 334b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang void onTuned(TvInputManager.Session session, Uri channelUri) { 33582621b6c09bc2e76e9fd52b921cfd2f5166c0945Youngsang Cho if (DEBUG) { 336e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo Log.d(TAG, "onTuned()"); 33782621b6c09bc2e76e9fd52b921cfd2f5166c0945Youngsang Cho } 33882621b6c09bc2e76e9fd52b921cfd2f5166c0945Youngsang Cho if (this != mSessionCallback) { 339e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo Log.w(TAG, "onTuned - session not created"); 34082621b6c09bc2e76e9fd52b921cfd2f5166c0945Youngsang Cho return; 34182621b6c09bc2e76e9fd52b921cfd2f5166c0945Youngsang Cho } 342e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo mIsTuned = true; 343b55c7517ba4b2c2959a0bc4d37536e7e3c8283c9Dongwon Kang mCallback.onTuned(channelUri); 34482621b6c09bc2e76e9fd52b921cfd2f5166c0945Youngsang Cho } 34582621b6c09bc2e76e9fd52b921cfd2f5166c0945Youngsang Cho 34682621b6c09bc2e76e9fd52b921cfd2f5166c0945Youngsang Cho @Override 347a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo public void onSessionReleased(TvInputManager.Session session) { 348a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo if (DEBUG) { 349a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo Log.d(TAG, "onSessionReleased()"); 350a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 351a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo if (this != mSessionCallback) { 352a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo Log.w(TAG, "onSessionReleased - session not created"); 353a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo return; 354a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 355e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo mIsTuned = false; 356e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo mIsRecordingStarted = false; 35725c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo mSessionCallback = null; 35825c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo mSession = null; 35925c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo if (mCallback != null) { 36025c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo mCallback.onDisconnected(mInputId); 36125c9c5edab42d6c9e9e0469ab04fb7ff87704d1cJae Seo } 362a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 363a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 364a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo @Override 365a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo public void onRecordingStopped(TvInputManager.Session session, Uri recordedProgramUri) { 366a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo if (DEBUG) { 3674eee6a73e476cd2d82a69f3a535628901047f140Jae Seo Log.d(TAG, "onRecordingStopped(recordedProgramUri= " + recordedProgramUri + ")"); 368a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 369a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo if (this != mSessionCallback) { 370a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo Log.w(TAG, "onRecordingStopped - session not created"); 371a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo return; 372a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 373e3c11e842937f50f54c9d82363f33338dc9e261bJae Seo mIsRecordingStarted = false; 374a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mCallback.onRecordingStopped(recordedProgramUri); 375a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 376a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 377a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo @Override 378a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo public void onError(TvInputManager.Session session, int error) { 379a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo if (DEBUG) { 3804eee6a73e476cd2d82a69f3a535628901047f140Jae Seo Log.d(TAG, "onError(error=" + error + ")"); 381a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 382a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo if (this != mSessionCallback) { 383a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo Log.w(TAG, "onError - session not created"); 384a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo return; 385a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 386a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mCallback.onError(error); 387a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 388a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo 389a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo @Override 390a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo public void onSessionEvent(TvInputManager.Session session, String eventType, 391a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo Bundle eventArgs) { 392a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo if (DEBUG) { 3934eee6a73e476cd2d82a69f3a535628901047f140Jae Seo Log.d(TAG, "onSessionEvent(eventType=" + eventType + ", eventArgs=" + eventArgs 3944eee6a73e476cd2d82a69f3a535628901047f140Jae Seo + ")"); 395a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 396a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo if (this != mSessionCallback) { 397a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo Log.w(TAG, "onSessionEvent - session not created"); 398a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo return; 399a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 400a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo if (mCallback != null) { 401a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo mCallback.onEvent(mInputId, eventType, eventArgs); 402a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 403a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 404a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo } 405a826d0172aae5e91d633ffe606059a2355fbf7e5Jae Seo} 406