resolved conflicts for merge of ebe661c4 to master
Change-Id: Ic25dc762ee43654b439de3ec237ead007bee3df4
This commit is contained in:
@@ -75,6 +75,8 @@ import android.location.LocationManager;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaRouter;
|
||||
import android.media.session.MediaSessionManager;
|
||||
import android.media.tv.ITvInputManager;
|
||||
import android.media.tv.TvInputManager;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.IConnectivityManager;
|
||||
import android.net.EthernetManager;
|
||||
@@ -119,8 +121,6 @@ import android.service.fingerprint.FingerprintManager;
|
||||
import android.service.fingerprint.FingerprintManagerReceiver;
|
||||
import android.service.fingerprint.FingerprintService;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.tv.ITvInputManager;
|
||||
import android.tv.TvInputManager;
|
||||
import android.content.ClipboardManager;
|
||||
import android.util.AndroidRuntimeException;
|
||||
import android.util.ArrayMap;
|
||||
|
||||
@@ -2751,11 +2751,11 @@ public abstract class Context {
|
||||
|
||||
/**
|
||||
* Use with {@link #getSystemService} to retrieve a
|
||||
* {@link android.tv.TvInputManager} for interacting with TV inputs on the
|
||||
* device.
|
||||
* {@link android.media.tv.TvInputManager} for interacting with TV inputs
|
||||
* on the device.
|
||||
*
|
||||
* @see #getSystemService
|
||||
* @see android.tv.TvInputManager
|
||||
* @see android.media.tv.TvInputManager
|
||||
*/
|
||||
public static final String TV_INPUT_SERVICE = "tv_input";
|
||||
|
||||
|
||||
@@ -1,741 +0,0 @@
|
||||
/*
|
||||
* 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 android.provider;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.net.Uri;
|
||||
import android.tv.TvInputService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The contract between the TV provider and applications. Contains definitions for the supported
|
||||
* URIs and columns.
|
||||
* </p>
|
||||
* <h3>Overview</h3>
|
||||
* <p>
|
||||
* TvContract defines a basic database of TV content metadata such as channel and program
|
||||
* information. The information is stored in {@link Channels} and {@link Programs} tables.
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>A row in the {@link Channels} table represents information about a TV channel. The data
|
||||
* format can vary greatly from standard to standard or according to service provider, thus
|
||||
* the columns here are mostly comprised of basic entities that are usually seen to users
|
||||
* regardless of standard such as channel number and name.</li>
|
||||
* <li>A row in the {@link Programs} table represents a set of data describing a TV program such
|
||||
* as program title and start time.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public final class TvContract {
|
||||
/** The authority for the TV provider. */
|
||||
public static final String AUTHORITY = "com.android.tv";
|
||||
|
||||
private static final String PATH_CHANNEL = "channel";
|
||||
private static final String PATH_PROGRAM = "program";
|
||||
private static final String PATH_INPUT = "input";
|
||||
|
||||
/**
|
||||
* An optional query, update or delete URI parameter that allows the caller to specify start
|
||||
* time (in milliseconds since the epoch) to filter programs.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final String PARAM_START_TIME = "start_time";
|
||||
|
||||
/**
|
||||
* An optional query, update or delete URI parameter that allows the caller to specify end time
|
||||
* (in milliseconds since the epoch) to filter programs.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final String PARAM_END_TIME = "end_time";
|
||||
|
||||
/**
|
||||
* A query, update or delete URI parameter that allows the caller to operate on all or
|
||||
* browsable-only channels. If set to "true", the rows that contain non-browsable channels are
|
||||
* not affected.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final String PARAM_BROWSABLE_ONLY = "browsable_only";
|
||||
|
||||
/**
|
||||
* Builds a URI that points to a specific channel.
|
||||
*
|
||||
* @param channelId The ID of the channel to point to.
|
||||
*/
|
||||
public static final Uri buildChannelUri(long channelId) {
|
||||
return ContentUris.withAppendedId(Channels.CONTENT_URI, channelId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a URI that points to all browsable channels from a given TV input.
|
||||
*
|
||||
* @param name {@link ComponentName} of the {@link android.tv.TvInputService} that implements
|
||||
* the given TV input.
|
||||
*/
|
||||
public static final Uri buildChannelsUriForInput(ComponentName name) {
|
||||
return buildChannelsUriForInput(name, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a URI that points to all or browsable-only channels from a given TV input.
|
||||
*
|
||||
* @param name {@link ComponentName} of the {@link android.tv.TvInputService} that implements
|
||||
* the given TV input.
|
||||
* @param browsableOnly If set to {@code true} the URI points to only browsable channels. If set
|
||||
* to {@code false} the URI points to all channels regardless of whether they are
|
||||
* browsable or not.
|
||||
*/
|
||||
public static final Uri buildChannelsUriForInput(ComponentName name, boolean browsableOnly) {
|
||||
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY)
|
||||
.appendPath(PATH_INPUT).appendPath(name.getPackageName())
|
||||
.appendPath(name.getClassName()).appendPath(PATH_CHANNEL)
|
||||
.appendQueryParameter(PARAM_BROWSABLE_ONLY, String.valueOf(browsableOnly)).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a URI that points to a specific program.
|
||||
*
|
||||
* @param programId The ID of the program to point to.
|
||||
*/
|
||||
public static final Uri buildProgramUri(long programId) {
|
||||
return ContentUris.withAppendedId(Programs.CONTENT_URI, programId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a URI that points to all programs on a given channel.
|
||||
*
|
||||
* @param channelUri The URI of the channel to return programs for.
|
||||
*/
|
||||
public static final Uri buildProgramsUriForChannel(Uri channelUri) {
|
||||
if (!PATH_CHANNEL.equals(channelUri.getPathSegments().get(0))) {
|
||||
throw new IllegalArgumentException("Not a channel: " + channelUri);
|
||||
}
|
||||
String channelId = String.valueOf(ContentUris.parseId(channelUri));
|
||||
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY)
|
||||
.appendPath(PATH_CHANNEL).appendPath(channelId).appendPath(PATH_PROGRAM).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a URI that points to programs on a specific channel whose schedules overlap with the
|
||||
* given time frame.
|
||||
*
|
||||
* @param channelUri The URI of the channel to return programs for.
|
||||
* @param startTime The start time used to filter programs. The returned programs should have
|
||||
* {@link Programs#COLUMN_END_TIME_UTC_MILLIS} that is greater than this time.
|
||||
* @param endTime The end time used to filter programs. The returned programs should have
|
||||
* {@link Programs#COLUMN_START_TIME_UTC_MILLIS} that is less than this time.
|
||||
*/
|
||||
public static final Uri buildProgramsUriForChannel(Uri channelUri, long startTime,
|
||||
long endTime) {
|
||||
Uri uri = buildProgramsUriForChannel(channelUri);
|
||||
return uri.buildUpon().appendQueryParameter(PARAM_START_TIME, String.valueOf(startTime))
|
||||
.appendQueryParameter(PARAM_END_TIME, String.valueOf(endTime)).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a URI that points to a specific program the user watched.
|
||||
*
|
||||
* @param watchedProgramId The ID of the watched program to point to.
|
||||
* @hide
|
||||
*/
|
||||
public static final Uri buildWatchedProgramUri(long watchedProgramId) {
|
||||
return ContentUris.withAppendedId(WatchedPrograms.CONTENT_URI, watchedProgramId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the {@link Channels#COLUMN_PACKAGE_NAME} from a given URI.
|
||||
*
|
||||
* @param channelsUri A URI constructed by {@link #buildChannelsUriForInput(ComponentName)} or
|
||||
* {@link #buildChannelsUriForInput(ComponentName, boolean)}.
|
||||
* @hide
|
||||
*/
|
||||
public static final String getPackageName(Uri channelsUri) {
|
||||
final List<String> paths = channelsUri.getPathSegments();
|
||||
if (paths.size() < 4) {
|
||||
throw new IllegalArgumentException("Not channels: " + channelsUri);
|
||||
}
|
||||
if (!PATH_INPUT.equals(paths.get(0)) || !PATH_CHANNEL.equals(paths.get(3))) {
|
||||
throw new IllegalArgumentException("Not channels: " + channelsUri);
|
||||
}
|
||||
return paths.get(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the {@link Channels#COLUMN_SERVICE_NAME} from a given URI.
|
||||
*
|
||||
* @param channelsUri A URI constructed by {@link #buildChannelsUriForInput(ComponentName)} or
|
||||
* {@link #buildChannelsUriForInput(ComponentName, boolean)}.
|
||||
* @hide
|
||||
*/
|
||||
public static final String getServiceName(Uri channelsUri) {
|
||||
final List<String> paths = channelsUri.getPathSegments();
|
||||
if (paths.size() < 4) {
|
||||
throw new IllegalArgumentException("Not channels: " + channelsUri);
|
||||
}
|
||||
if (!PATH_INPUT.equals(paths.get(0)) || !PATH_CHANNEL.equals(paths.get(3))) {
|
||||
throw new IllegalArgumentException("Not channels: " + channelsUri);
|
||||
}
|
||||
return paths.get(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the {@link Channels#_ID} from a given URI.
|
||||
*
|
||||
* @param programsUri A URI constructed by {@link #buildProgramsUriForChannel(Uri)} or
|
||||
* {@link #buildProgramsUriForChannel(Uri, long, long)}.
|
||||
* @hide
|
||||
*/
|
||||
public static final String getChannelId(Uri programsUri) {
|
||||
final List<String> paths = programsUri.getPathSegments();
|
||||
if (paths.size() < 3) {
|
||||
throw new IllegalArgumentException("Not programs: " + programsUri);
|
||||
}
|
||||
if (!PATH_CHANNEL.equals(paths.get(0)) || !PATH_PROGRAM.equals(paths.get(2))) {
|
||||
throw new IllegalArgumentException("Not programs: " + programsUri);
|
||||
}
|
||||
return paths.get(1);
|
||||
}
|
||||
|
||||
|
||||
private TvContract() {}
|
||||
|
||||
/**
|
||||
* Common base for the tables of TV channels/programs.
|
||||
*/
|
||||
public interface BaseTvColumns extends BaseColumns {
|
||||
/**
|
||||
* The name of the package that owns a row in each table.
|
||||
* <p>
|
||||
* The TV provider fills it in with the name of the package that provides the initial data
|
||||
* of that row. If the package is later uninstalled, the rows it owns are automatically
|
||||
* removed from the tables.
|
||||
* </p><p>
|
||||
* Type: TEXT
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_PACKAGE_NAME = "package_name";
|
||||
}
|
||||
|
||||
/** Column definitions for the TV channels table. */
|
||||
public static final class Channels implements BaseTvColumns {
|
||||
|
||||
/** The content:// style URI for this table. */
|
||||
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
|
||||
+ PATH_CHANNEL);
|
||||
|
||||
/** The MIME type of a directory of TV channels. */
|
||||
public static final String CONTENT_TYPE =
|
||||
"vnd.android.cursor.dir/vnd.com.android.tv.channels";
|
||||
|
||||
/** The MIME type of a single TV channel. */
|
||||
public static final String CONTENT_ITEM_TYPE =
|
||||
"vnd.android.cursor.item/vnd.com.android.tv.channels";
|
||||
|
||||
/** A generic channel type. */
|
||||
public static final int TYPE_OTHER = 0x0;
|
||||
|
||||
/** The special channel type used for pass-through inputs such as HDMI. */
|
||||
public static final int TYPE_PASSTHROUGH = 0x00010000;
|
||||
|
||||
/** The channel type for DVB-T (terrestrial). */
|
||||
public static final int TYPE_DVB_T = 0x00020000;
|
||||
|
||||
/** The channel type for DVB-T2 (terrestrial). */
|
||||
public static final int TYPE_DVB_T2 = 0x00020001;
|
||||
|
||||
/** The channel type for DVB-S (satellite). */
|
||||
public static final int TYPE_DVB_S = 0x00020100;
|
||||
|
||||
/** The channel type for DVB-S2 (satellite). */
|
||||
public static final int TYPE_DVB_S2 = 0x00020101;
|
||||
|
||||
/** The channel type for DVB-C (cable). */
|
||||
public static final int TYPE_DVB_C = 0x00020200;
|
||||
|
||||
/** The channel type for DVB-C2 (cable). */
|
||||
public static final int TYPE_DVB_C2 = 0x00020201;
|
||||
|
||||
/** The channel type for DVB-H (handheld). */
|
||||
public static final int TYPE_DVB_H = 0x00020300;
|
||||
|
||||
/** The channel type for DVB-SH (satellite). */
|
||||
public static final int TYPE_DVB_SH = 0x00020400;
|
||||
|
||||
/** The channel type for ATSC (terrestrial). */
|
||||
public static final int TYPE_ATSC_T = 0x00030000;
|
||||
|
||||
/** The channel type for ATSC (cable). */
|
||||
public static final int TYPE_ATSC_C = 0x00030200;
|
||||
|
||||
/** The channel type for ATSC-M/H (mobile/handheld). */
|
||||
public static final int TYPE_ATSC_M_H = 0x00030200;
|
||||
|
||||
/** The channel type for ISDB-T (terrestrial). */
|
||||
public static final int TYPE_ISDB_T = 0x00040000;
|
||||
|
||||
/** The channel type for ISDB-Tb (Brazil). */
|
||||
public static final int TYPE_ISDB_TB = 0x00040100;
|
||||
|
||||
/** The channel type for ISDB-S (satellite). */
|
||||
public static final int TYPE_ISDB_S = 0x00040200;
|
||||
|
||||
/** The channel type for ISDB-C (cable). */
|
||||
public static final int TYPE_ISDB_C = 0x00040300;
|
||||
|
||||
/** The channel type for 1seg (handheld). */
|
||||
public static final int TYPE_1SEG = 0x00040400;
|
||||
|
||||
/** The channel type for DTMB (terrestrial). */
|
||||
public static final int TYPE_DTMB = 0x00050000;
|
||||
|
||||
/** The channel type for CMMB (handheld). */
|
||||
public static final int TYPE_CMMB = 0x00050100;
|
||||
|
||||
/** The channel type for T-DMB (terrestrial). */
|
||||
public static final int TYPE_T_DMB = 0x00060000;
|
||||
|
||||
/** The channel type for S-DMB (satellite). */
|
||||
public static final int TYPE_S_DMB = 0x00060100;
|
||||
|
||||
/** A generic service type. */
|
||||
public static final int SERVICE_TYPE_OTHER = 0x0;
|
||||
|
||||
/** The service type for regular TV channels. */
|
||||
public static final int SERVICE_TYPE_TV = 0x1;
|
||||
|
||||
/** The service type for radio channels. */
|
||||
public static final int SERVICE_TYPE_RADIO = 0x2;
|
||||
|
||||
/**
|
||||
* The name of the {@link TvInputService} subclass that provides this TV channel. This
|
||||
* should be a fully qualified class name (such as, "com.example.project.TvInputService").
|
||||
* <p>
|
||||
* This is a required field.
|
||||
* </p><p>
|
||||
* Type: TEXT
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_SERVICE_NAME = "service_name";
|
||||
|
||||
/**
|
||||
* The predefined type of this TV channel.
|
||||
* <p>
|
||||
* This is primarily used to indicate which broadcast standard (e.g. ATSC, DVB or ISDB) the
|
||||
* current channel conforms to, with an exception being {@link #TYPE_PASSTHROUGH}, which is
|
||||
* a special channel type used only by pass-through inputs such as HDMI. The value should
|
||||
* match to one of the followings: {@link #TYPE_OTHER}, {@link #TYPE_PASSTHROUGH},
|
||||
* {@link #TYPE_DVB_T}, {@link #TYPE_DVB_T2}, {@link #TYPE_DVB_S}, {@link #TYPE_DVB_S2},
|
||||
* {@link #TYPE_DVB_C}, {@link #TYPE_DVB_C2}, {@link #TYPE_DVB_H}, {@link #TYPE_DVB_SH},
|
||||
* {@link #TYPE_ATSC_T}, {@link #TYPE_ATSC_C}, {@link #TYPE_ATSC_M_H}, {@link #TYPE_ISDB_T},
|
||||
* {@link #TYPE_ISDB_TB}, {@link #TYPE_ISDB_S}, {@link #TYPE_ISDB_C} {@link #TYPE_1SEG},
|
||||
* {@link #TYPE_DTMB}, {@link #TYPE_CMMB}, {@link #TYPE_T_DMB}, {@link #TYPE_S_DMB}
|
||||
* </p><p>
|
||||
* This is a required field.
|
||||
* </p><p>
|
||||
* Type: INTEGER
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_TYPE = "type";
|
||||
|
||||
/**
|
||||
* The predefined service type of this TV channel.
|
||||
* <p>
|
||||
* This is primarily used to indicate whether the current channel is a regular TV channel or
|
||||
* a radio-like channel. Use the same coding for {@code service_type} in the underlying
|
||||
* broadcast standard if it is defined there (e.g. ATSC A/53, ETSI EN 300 468 and ARIB
|
||||
* STD-B10). Otherwise use one of the followings: {@link #SERVICE_TYPE_OTHER},
|
||||
* {@link #SERVICE_TYPE_TV}, {@link #SERVICE_TYPE_RADIO}
|
||||
* </p><p>
|
||||
* This is a required field.
|
||||
* </p><p>
|
||||
* Type: INTEGER
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_SERVICE_TYPE = "service_type";
|
||||
|
||||
/**
|
||||
* The original network ID of this TV channel.
|
||||
* <p>
|
||||
* This is used to identify the originating delivery system, if applicable. Use the same
|
||||
* coding for {@code origianal_network_id} in the underlying broadcast standard if it is
|
||||
* defined there (e.g. ETSI EN 300 468/TR 101 211 and ARIB STD-B10). If channels cannot be
|
||||
* globally identified by 2-tuple {{@link #COLUMN_TRANSPORT_STREAM_ID},
|
||||
* {@link #COLUMN_SERVICE_ID}}, one must carefully assign a value to this field to form a
|
||||
* unique 3-tuple identification {{@link #COLUMN_ORIGINAL_NETWORK_ID},
|
||||
* {@link #COLUMN_TRANSPORT_STREAM_ID}, {@link #COLUMN_SERVICE_ID}} for its channels.
|
||||
* </p><p>
|
||||
* This is a required field if the channel cannot be uniquely identified by a 2-tuple
|
||||
* {{@link #COLUMN_TRANSPORT_STREAM_ID}, {@link #COLUMN_SERVICE_ID}}.
|
||||
* </p><p>
|
||||
* Type: INTEGER
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id";
|
||||
|
||||
/**
|
||||
* The transport stream ID of this channel.
|
||||
* <p>
|
||||
* This is used to identify the Transport Stream that contains the current channel from any
|
||||
* other multiplex within a network, if applicable. Use the same coding for
|
||||
* {@code transport_stream_id} defined in ISO/IEC 13818-1 if the channel is transmitted via
|
||||
* the MPEG Transport Stream as is the case for many digital broadcast standards.
|
||||
* </p><p>
|
||||
* This is a required field if the current channel is transmitted via the MPEG Transport
|
||||
* Stream.
|
||||
* </p><p>
|
||||
* Type: INTEGER
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id";
|
||||
|
||||
/**
|
||||
* The service ID of this channel.
|
||||
* <p>
|
||||
* This is used to identify the current service (roughly equivalent to channel) from any
|
||||
* other service within the Transport Stream, if applicable. Use the same coding for
|
||||
* {@code service_id} in the underlying broadcast standard if it is defined there (e.g. ETSI
|
||||
* EN 300 468 and ARIB STD-B10) or {@code program_number} (which usually has the same value
|
||||
* as {@code service_id}) in ISO/IEC 13818-1 if the channel is transmitted via the MPEG
|
||||
* Transport Stream.
|
||||
* </p><p>
|
||||
* This is a required field if the current channel is transmitted via the MPEG Transport
|
||||
* Stream.
|
||||
* </p><p>
|
||||
* Type: INTEGER
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_SERVICE_ID = "service_id";
|
||||
|
||||
/**
|
||||
* The channel number that is displayed to the user.
|
||||
* <p>
|
||||
* The format can vary depending on broadcast standard and product specification.
|
||||
* </p><p>
|
||||
* Type: TEXT
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_DISPLAY_NUMBER = "display_number";
|
||||
|
||||
/**
|
||||
* The channel name that is displayed to the user.
|
||||
* <p>
|
||||
* A call sign is a good candidate to use for this purpose but any name that helps the user
|
||||
* recognize the current channel will be enough. Can also be empty depending on broadcast
|
||||
* standard.
|
||||
* </p><p>
|
||||
* Type: TEXT
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_DISPLAY_NAME = "display_name";
|
||||
|
||||
/**
|
||||
* The description of this TV channel.
|
||||
* <p>
|
||||
* Can be empty initially.
|
||||
* </p><p>
|
||||
* Type: TEXT
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_DESCRIPTION = "description";
|
||||
|
||||
/**
|
||||
* The flag indicating whether this TV channel is browsable or not.
|
||||
* <p>
|
||||
* A value of 1 indicates the channel is included in the channel list that applications use
|
||||
* to browse channels, a value of 0 indicates the channel is not included in the list. If
|
||||
* not specified, this value is set to 1 (browsable) by default.
|
||||
* </p><p>
|
||||
* Type: INTEGER (boolean)
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_BROWSABLE = "browsable";
|
||||
|
||||
/**
|
||||
* The flag indicating whether this TV channel is searchable or not.
|
||||
* <p>
|
||||
* In some regions, it is not allowed to surface search results for a given channel without
|
||||
* broadcaster's consent. This is used to impose such restriction. A value of 1 indicates
|
||||
* the channel is searchable and can be included in search results, a value of 0 indicates
|
||||
* the channel and its TV programs are hidden from search. If not specified, this value is
|
||||
* set to 1 (searchable) by default.
|
||||
* </p>
|
||||
* <p>
|
||||
* Type: INTEGER (boolean)
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_SEARCHABLE = "searchable";
|
||||
|
||||
/**
|
||||
* The flag indicating whether this TV channel is locked or not.
|
||||
* <p>
|
||||
* This is primarily used for alternative parental control to prevent unauthorized users
|
||||
* from watching the current channel regardless of the content rating. A value of 1
|
||||
* indicates the channel is locked and the user is required to enter passcode to unlock it
|
||||
* in order to watch the current program from the channel, a value of 0 indicates the
|
||||
* channel is not locked thus the user is not prompted to enter passcode If not specified,
|
||||
* this value is set to 0 (not locked) by default.
|
||||
* </p><p>
|
||||
* Type: INTEGER (boolean)
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_LOCKED = "locked";
|
||||
|
||||
/**
|
||||
* Generic data used by individual TV input services.
|
||||
* <p>
|
||||
* Type: BLOB
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_DATA = "data";
|
||||
|
||||
|
||||
/**
|
||||
* The version number of this row entry used by TV input services.
|
||||
* <p>
|
||||
* This is best used by sync adapters to identify the rows to update. The number can be
|
||||
* defined by individual TV input services. One may assign the same value as
|
||||
* {@code version_number} that appears in ETSI EN 300 468 or ATSC A/65, if the data are
|
||||
* coming from a TV broadcast.
|
||||
* </p><p>
|
||||
* Type: INTEGER
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_VERSION_NUMBER = "version_number";
|
||||
|
||||
private Channels() {}
|
||||
}
|
||||
|
||||
/** Column definitions for the TV programs table. */
|
||||
public static final class Programs implements BaseTvColumns {
|
||||
|
||||
/** The content:// style URI for this table. */
|
||||
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
|
||||
+ PATH_PROGRAM);
|
||||
|
||||
/** The MIME type of a directory of TV programs. */
|
||||
public static final String CONTENT_TYPE =
|
||||
"vnd.android.cursor.dir/vnd.com.android.tv.programs";
|
||||
|
||||
/** The MIME type of a single TV program. */
|
||||
public static final String CONTENT_ITEM_TYPE =
|
||||
"vnd.android.cursor.item/vnd.com.android.tv.programs";
|
||||
|
||||
/**
|
||||
* The ID of the TV channel that contains this TV program.
|
||||
* <p>
|
||||
* This is a part of the channel URI and matches to {@link BaseColumns#_ID}.
|
||||
* </p><p>
|
||||
* Type: INTEGER (long)
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_CHANNEL_ID = "channel_id";
|
||||
|
||||
/**
|
||||
* The title of this TV program.
|
||||
* <p>
|
||||
* Type: TEXT
|
||||
* </p>
|
||||
**/
|
||||
public static final String COLUMN_TITLE = "title";
|
||||
|
||||
/**
|
||||
* The start time of this TV program, in milliseconds since the epoch.
|
||||
* <p>
|
||||
* Type: INTEGER (long)
|
||||
* </p>
|
||||
*/
|
||||
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>
|
||||
* Type: INTEGER (long)
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
|
||||
|
||||
/**
|
||||
* The comma-separated genre string of this TV program.
|
||||
* <p>
|
||||
* Use the same language appeared in the underlying broadcast standard, if applicable. (For
|
||||
* example, one can refer to the genre strings used in Genre Descriptor of ATSC A/65 or
|
||||
* Content Descriptor of ETSI EN 300 468, if appropriate.) Otherwise, use one of the
|
||||
* following genres:
|
||||
* <ul>
|
||||
* <li>Family/Kids</li>
|
||||
* <li>Sports</li>
|
||||
* <li>Shopping</li>
|
||||
* <li>Movies</li>
|
||||
* <li>Comedy</li>
|
||||
* <li>Travel</li>
|
||||
* <li>Drama</li>
|
||||
* <li>Education</li>
|
||||
* <li>Animal/Wildlife</li>
|
||||
* <li>News</li>
|
||||
* <li>Gaming</li>
|
||||
* <li>Others</li>
|
||||
* </ul>
|
||||
* </p><p>
|
||||
* Type: TEXT
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_GENRE = "genre";
|
||||
|
||||
/**
|
||||
* The description of this TV program that is displayed to the user by default.
|
||||
* <p>
|
||||
* The maximum length of this field is 256 characters.
|
||||
* </p><p>
|
||||
* Type: TEXT
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_DESCRIPTION = "description";
|
||||
|
||||
/**
|
||||
* The detailed, lengthy description of this TV program that is displayed only when the user
|
||||
* wants to see more information.
|
||||
* <p>
|
||||
* TV input services should leave this field empty if they have no additional
|
||||
* details beyond {@link #COLUMN_DESCRIPTION}.
|
||||
* </p><p>
|
||||
* Type: TEXT
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_LONG_DESCRIPTION = "long_description";
|
||||
|
||||
/**
|
||||
* The comma-separated audio languages of this TV program.
|
||||
* <p>
|
||||
* This is used to describe available audio languages included in the program. Use
|
||||
* 3-character language code as specified by ISO 639-2.
|
||||
* </p><p>
|
||||
* Type: TEXT
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_AUDIO_LANGUAGE = "audio_language";
|
||||
|
||||
/**
|
||||
* Generic data used by TV input services.
|
||||
* <p>
|
||||
* Type: BLOB
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_DATA = "data";
|
||||
|
||||
/**
|
||||
* The version number of this row entry used by TV input services.
|
||||
* <p>
|
||||
* This is best used by sync adapters to identify the rows to update. The number can be
|
||||
* defined by individual TV input services. One may assign the same value as
|
||||
* {@code version_number} in ETSI EN 300 468 or ATSC A/65, if the data are coming from a TV
|
||||
* broadcast.
|
||||
* </p><p>
|
||||
* Type: INTEGER
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_VERSION_NUMBER = "version_number";
|
||||
|
||||
private Programs() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Column definitions for the TV programs that the user watched. Applications do not have access
|
||||
* to this table.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final class WatchedPrograms implements BaseColumns {
|
||||
|
||||
/** The content:// style URI for this table. */
|
||||
public static final Uri CONTENT_URI =
|
||||
Uri.parse("content://" + AUTHORITY + "/watched_program");
|
||||
|
||||
/** The MIME type of a directory of watched programs. */
|
||||
public static final String CONTENT_TYPE =
|
||||
"vnd.android.cursor.dir/vnd.com.android.tv.watched_programs";
|
||||
|
||||
/** The MIME type of a single item in this table. */
|
||||
public static final String CONTENT_ITEM_TYPE =
|
||||
"vnd.android.cursor.item/vnd.com.android.tv.watched_programs";
|
||||
|
||||
/**
|
||||
* The UTC time that the user started watching this TV program, in milliseconds since the
|
||||
* epoch.
|
||||
* <p>
|
||||
* Type: INTEGER (long)
|
||||
* </p>
|
||||
*/
|
||||
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>
|
||||
* Type: INTEGER (long)
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_WATCH_END_TIME_UTC_MILLIS = "watch_end_time_utc_millis";
|
||||
|
||||
/**
|
||||
* The channel ID that contains this TV program.
|
||||
* <p>
|
||||
* Type: INTEGER (long)
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_CHANNEL_ID = "channel_id";
|
||||
|
||||
/**
|
||||
* The title of this TV program.
|
||||
* <p>
|
||||
* Type: TEXT
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_TITLE = "title";
|
||||
|
||||
/**
|
||||
* The start time of this TV program, in milliseconds since the epoch.
|
||||
* <p>
|
||||
* Type: INTEGER (long)
|
||||
* </p>
|
||||
*/
|
||||
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>
|
||||
* Type: INTEGER (long)
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
|
||||
|
||||
/**
|
||||
* The description of this TV program.
|
||||
* <p>
|
||||
* Type: TEXT
|
||||
* </p>
|
||||
*/
|
||||
public static final String COLUMN_DESCRIPTION = "description";
|
||||
|
||||
private WatchedPrograms() {}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* 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 android.tv;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.os.Bundle;
|
||||
import android.tv.ITvInputSession;
|
||||
import android.view.InputChannel;
|
||||
|
||||
/**
|
||||
* Interface a client of the ITvInputManager implements, to identify itself and receive information
|
||||
* about changes to the state of each TV input service.
|
||||
* @hide
|
||||
*/
|
||||
oneway interface ITvInputClient {
|
||||
void onSessionCreated(in String inputId, IBinder token, in InputChannel channel, int seq);
|
||||
void onAvailabilityChanged(in String inputId, boolean isAvailable);
|
||||
void onSessionReleased(int seq);
|
||||
void onSessionEvent(in String name, in Bundle args, int seq);
|
||||
void onVideoSizeChanged(int width, int height, int seq);
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* 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 android.tv;
|
||||
|
||||
import android.tv.TvStreamConfig;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Surface;
|
||||
|
||||
/**
|
||||
* TvInputService representing a physical port should connect to HAL through this interface.
|
||||
* Framework will take care of communication among system services including TvInputManagerService,
|
||||
* HdmiControlService, AudioService, etc.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
interface ITvInputHardware {
|
||||
/**
|
||||
* Make the input render on the surface according to the config. In case of HDMI, this will
|
||||
* trigger CEC commands for adjusting active HDMI source. Returns true on success.
|
||||
*/
|
||||
boolean setSurface(in Surface surface, in TvStreamConfig config);
|
||||
/**
|
||||
* Set volume for this stream via AudioGain. (TBD)
|
||||
*/
|
||||
void setVolume(float volume);
|
||||
|
||||
/**
|
||||
* Dispatch key event to HDMI service. The events would be automatically converted to
|
||||
* HDMI CEC commands. If the hardware is not representing an HDMI port, this method will fail.
|
||||
*/
|
||||
boolean dispatchKeyEventToHdmi(in KeyEvent event);
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* 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 android.tv;
|
||||
|
||||
import android.tv.TvStreamConfig;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
oneway interface ITvInputHardwareCallback {
|
||||
void onReleased();
|
||||
void onStreamConfigChanged(in TvStreamConfig[] configs);
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* 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 android.tv;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.graphics.Rect;
|
||||
import android.net.Uri;
|
||||
import android.tv.ITvInputHardware;
|
||||
import android.tv.ITvInputHardwareCallback;
|
||||
import android.tv.ITvInputClient;
|
||||
import android.tv.TvInputHardwareInfo;
|
||||
import android.tv.TvInputInfo;
|
||||
import android.view.Surface;
|
||||
|
||||
/**
|
||||
* Interface to the TV input manager service.
|
||||
* @hide
|
||||
*/
|
||||
interface ITvInputManager {
|
||||
List<TvInputInfo> getTvInputList(int userId);
|
||||
|
||||
boolean getAvailability(in ITvInputClient client, in String inputId, int userId);
|
||||
|
||||
void registerCallback(in ITvInputClient client, in String inputId, int userId);
|
||||
void unregisterCallback(in ITvInputClient client, in String inputId, int userId);
|
||||
|
||||
void createSession(in ITvInputClient client, in String inputId, int seq, int userId);
|
||||
void releaseSession(in IBinder sessionToken, int userId);
|
||||
|
||||
void setSurface(in IBinder sessionToken, in Surface surface, int userId);
|
||||
void setVolume(in IBinder sessionToken, float volume, int userId);
|
||||
void tune(in IBinder sessionToken, in Uri channelUri, int userId);
|
||||
|
||||
void createOverlayView(in IBinder sessionToken, in IBinder windowToken, in Rect frame,
|
||||
int userId);
|
||||
void relayoutOverlayView(in IBinder sessionToken, in Rect frame, int userId);
|
||||
void removeOverlayView(in IBinder sessionToken, int userId);
|
||||
|
||||
// For TV input hardware binding
|
||||
List<TvInputHardwareInfo> getHardwareList();
|
||||
ITvInputHardware acquireTvInputHardware(int deviceId, in ITvInputHardwareCallback callback,
|
||||
int userId);
|
||||
void releaseTvInputHardware(int deviceId, in ITvInputHardware hardware, int userId);
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* 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 android.tv;
|
||||
|
||||
import android.tv.ITvInputServiceCallback;
|
||||
import android.tv.ITvInputSessionCallback;
|
||||
import android.view.InputChannel;
|
||||
|
||||
/**
|
||||
* Top-level interface to a TV input component (implemented in a Service).
|
||||
* @hide
|
||||
*/
|
||||
oneway interface ITvInputService {
|
||||
void registerCallback(ITvInputServiceCallback callback);
|
||||
void unregisterCallback(in ITvInputServiceCallback callback);
|
||||
void createSession(in InputChannel channel, ITvInputSessionCallback callback);
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* 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 android.tv;
|
||||
|
||||
import android.content.ComponentName;
|
||||
|
||||
/**
|
||||
* Helper interface for ITvInputService to allow the TV input to notify the client when its status
|
||||
* has been changed.
|
||||
* @hide
|
||||
*/
|
||||
oneway interface ITvInputServiceCallback {
|
||||
void onAvailabilityChanged(in String inputId, boolean isAvailable);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* 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 android.tv;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.net.Uri;
|
||||
import android.view.Surface;
|
||||
|
||||
/**
|
||||
* Sub-interface of ITvInputService which is created per session and has its own context.
|
||||
* @hide
|
||||
*/
|
||||
oneway interface ITvInputSession {
|
||||
void release();
|
||||
|
||||
void setSurface(in Surface surface);
|
||||
// TODO: Remove this once it becomes irrelevant for applications to handle audio focus. The plan
|
||||
// is to introduce some new concepts that will solve a number of problems in audio policy today.
|
||||
void setVolume(float volume);
|
||||
void tune(in Uri channelUri);
|
||||
|
||||
void createOverlayView(in IBinder windowToken, in Rect frame);
|
||||
void relayoutOverlayView(in Rect frame);
|
||||
void removeOverlayView();
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* 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 android.tv;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.tv.ITvInputSession;
|
||||
|
||||
/**
|
||||
* Helper interface for ITvInputSession to allow the TV input to notify the system service when a
|
||||
* new session has been created.
|
||||
* @hide
|
||||
*/
|
||||
oneway interface ITvInputSessionCallback {
|
||||
void onSessionCreated(ITvInputSession session);
|
||||
void onSessionEvent(in String name, in Bundle args);
|
||||
void onVideoSizeChanged(int width, int height);
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
/*
|
||||
* 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 android.tv;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.net.Uri;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.tv.TvInputManager.Session;
|
||||
import android.tv.TvInputService.TvInputSessionImpl;
|
||||
import android.util.Log;
|
||||
import android.view.InputChannel;
|
||||
import android.view.InputEvent;
|
||||
import android.view.InputEventReceiver;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
|
||||
import com.android.internal.os.HandlerCaller;
|
||||
import com.android.internal.os.SomeArgs;
|
||||
|
||||
/**
|
||||
* Implements the internal ITvInputSession interface to convert incoming calls on to it back to
|
||||
* calls on the public TvInputSession interface, scheduling them on the main thread of the process.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class ITvInputSessionWrapper extends ITvInputSession.Stub implements HandlerCaller.Callback {
|
||||
private static final String TAG = "TvInputSessionWrapper";
|
||||
|
||||
private static final int DO_RELEASE = 1;
|
||||
private static final int DO_SET_SURFACE = 2;
|
||||
private static final int DO_SET_VOLUME = 3;
|
||||
private static final int DO_TUNE = 4;
|
||||
private static final int DO_CREATE_OVERLAY_VIEW = 5;
|
||||
private static final int DO_RELAYOUT_OVERLAY_VIEW = 6;
|
||||
private static final int DO_REMOVE_OVERLAY_VIEW = 7;
|
||||
|
||||
private final HandlerCaller mCaller;
|
||||
|
||||
private TvInputSessionImpl mTvInputSessionImpl;
|
||||
private InputChannel mChannel;
|
||||
private TvInputEventReceiver mReceiver;
|
||||
|
||||
public ITvInputSessionWrapper(Context context, TvInputSessionImpl sessionImpl,
|
||||
InputChannel channel) {
|
||||
mCaller = new HandlerCaller(context, null, this, true /* asyncHandler */);
|
||||
mTvInputSessionImpl = sessionImpl;
|
||||
mChannel = channel;
|
||||
if (channel != null) {
|
||||
mReceiver = new TvInputEventReceiver(channel, context.getMainLooper());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeMessage(Message msg) {
|
||||
if (mTvInputSessionImpl == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (msg.what) {
|
||||
case DO_RELEASE: {
|
||||
mTvInputSessionImpl.release();
|
||||
mTvInputSessionImpl = null;
|
||||
if (mReceiver != null) {
|
||||
mReceiver.dispose();
|
||||
mReceiver = null;
|
||||
}
|
||||
if (mChannel != null) {
|
||||
mChannel.dispose();
|
||||
mChannel = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DO_SET_SURFACE: {
|
||||
mTvInputSessionImpl.setSurface((Surface) msg.obj);
|
||||
return;
|
||||
}
|
||||
case DO_SET_VOLUME: {
|
||||
mTvInputSessionImpl.setVolume((Float) msg.obj);
|
||||
return;
|
||||
}
|
||||
case DO_TUNE: {
|
||||
mTvInputSessionImpl.tune((Uri) msg.obj);
|
||||
return;
|
||||
}
|
||||
case DO_CREATE_OVERLAY_VIEW: {
|
||||
SomeArgs args = (SomeArgs) msg.obj;
|
||||
mTvInputSessionImpl.createOverlayView((IBinder) args.arg1, (Rect) args.arg2);
|
||||
args.recycle();
|
||||
return;
|
||||
}
|
||||
case DO_RELAYOUT_OVERLAY_VIEW: {
|
||||
mTvInputSessionImpl.relayoutOverlayView((Rect) msg.obj);
|
||||
return;
|
||||
}
|
||||
case DO_REMOVE_OVERLAY_VIEW: {
|
||||
mTvInputSessionImpl.removeOverlayView(true);
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
Log.w(TAG, "Unhandled message code: " + msg.what);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_RELEASE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSurface(Surface surface) {
|
||||
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_SURFACE, surface));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setVolume(float volume) {
|
||||
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_VOLUME, volume));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tune(Uri channelUri) {
|
||||
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_TUNE, channelUri));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createOverlayView(IBinder windowToken, Rect frame) {
|
||||
mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_OVERLAY_VIEW, windowToken,
|
||||
frame));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void relayoutOverlayView(Rect frame) {
|
||||
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_RELAYOUT_OVERLAY_VIEW, frame));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeOverlayView() {
|
||||
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_OVERLAY_VIEW));
|
||||
}
|
||||
|
||||
private final class TvInputEventReceiver extends InputEventReceiver {
|
||||
public TvInputEventReceiver(InputChannel inputChannel, Looper looper) {
|
||||
super(inputChannel, looper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputEvent(InputEvent event) {
|
||||
if (mTvInputSessionImpl == null) {
|
||||
// The session has been finished.
|
||||
finishInputEvent(event, false);
|
||||
return;
|
||||
}
|
||||
|
||||
int handled = mTvInputSessionImpl.dispatchInputEvent(event, this);
|
||||
if (handled != Session.DISPATCH_IN_PROGRESS) {
|
||||
finishInputEvent(event, handled == Session.DISPATCH_HANDLED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 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 android.tv;
|
||||
|
||||
parcelable TvInputHardwareInfo;
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* 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 android.tv;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Simple container for information about TV input hardware.
|
||||
* Not for third-party developers.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class TvInputHardwareInfo implements Parcelable {
|
||||
static final String TAG = "TvInputHardwareInfo";
|
||||
|
||||
// Match hardware/libhardware/include/hardware/tv_input.h
|
||||
public static final int TV_INPUT_TYPE_HDMI = 1;
|
||||
public static final int TV_INPUT_TYPE_BUILT_IN_TUNER = 2;
|
||||
public static final int TV_INPUT_TYPE_PASSTHROUGH = 3;
|
||||
|
||||
public static final Parcelable.Creator<TvInputHardwareInfo> CREATOR =
|
||||
new Parcelable.Creator<TvInputHardwareInfo>() {
|
||||
@Override
|
||||
public TvInputHardwareInfo createFromParcel(Parcel source) {
|
||||
try {
|
||||
TvInputHardwareInfo info = new TvInputHardwareInfo();
|
||||
info.readFromParcel(source);
|
||||
return info;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Exception creating TvInputHardwareInfo from parcel", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TvInputHardwareInfo[] newArray(int size) {
|
||||
return new TvInputHardwareInfo[size];
|
||||
}
|
||||
};
|
||||
|
||||
private int mDeviceId;
|
||||
private int mType;
|
||||
// TODO: Add audio port & audio address for audio service.
|
||||
// TODO: Add HDMI handle for HDMI service.
|
||||
|
||||
public TvInputHardwareInfo() { }
|
||||
|
||||
public TvInputHardwareInfo(int deviceId, int type) {
|
||||
mDeviceId = deviceId;
|
||||
mType = type;
|
||||
}
|
||||
|
||||
public int getDeviceId() {
|
||||
return mDeviceId;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
// Parcelable
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(mDeviceId);
|
||||
dest.writeInt(mType);
|
||||
}
|
||||
|
||||
public void readFromParcel(Parcel source) {
|
||||
mDeviceId = source.readInt();
|
||||
mType = source.readInt();
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* 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 android.tv;
|
||||
|
||||
parcelable TvInputInfo;
|
||||
@@ -1,163 +0,0 @@
|
||||
/*
|
||||
* 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 android.tv;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* This class is used to specify meta information of a TV input.
|
||||
*/
|
||||
public final class TvInputInfo implements Parcelable {
|
||||
private final ResolveInfo mService;
|
||||
private final String mId;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param service The ResolveInfo returned from the package manager about this TV input service.
|
||||
* @hide
|
||||
*/
|
||||
public TvInputInfo(ResolveInfo service) {
|
||||
mService = service;
|
||||
ServiceInfo si = service.serviceInfo;
|
||||
mId = generateInputIdForComponentName(new ComponentName(si.packageName, si.name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a unique ID for this TV input. The ID is generated from the package and class name
|
||||
* implementing the TV input service.
|
||||
*/
|
||||
public String getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the .apk package that implements this TV input service.
|
||||
*/
|
||||
public String getPackageName() {
|
||||
return mService.serviceInfo.packageName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the class name of the service component that implements this TV input service.
|
||||
*/
|
||||
public String getServiceName() {
|
||||
return mService.serviceInfo.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the component of the service that implements this TV input.
|
||||
*/
|
||||
public ComponentName getComponent() {
|
||||
return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the user-displayed label for this TV input service.
|
||||
*
|
||||
* @param pm Supplies a PackageManager used to load the TV input's resources.
|
||||
* @return a CharSequence containing the TV input's label. If the TV input does not have
|
||||
* a label, its name is returned.
|
||||
*/
|
||||
public CharSequence loadLabel(PackageManager pm) {
|
||||
return mService.loadLabel(pm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mId.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(o instanceof TvInputInfo)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TvInputInfo obj = (TvInputInfo) o;
|
||||
return mId.equals(obj.mId)
|
||||
&& mService.serviceInfo.packageName.equals(obj.mService.serviceInfo.packageName)
|
||||
&& mService.serviceInfo.name.equals(obj.mService.serviceInfo.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TvInputInfo{id=" + mId
|
||||
+ ", pkg=" + mService.serviceInfo.packageName
|
||||
+ ", service=" + mService.serviceInfo.name + "}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to package this object into a {@link Parcel}.
|
||||
*
|
||||
* @param dest The {@link Parcel} to be written.
|
||||
* @param flags The flags used for parceling.
|
||||
*/
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(mId);
|
||||
mService.writeToParcel(dest, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to generate an input id from a ComponentName.
|
||||
*
|
||||
* @param name the component name for generating an input id.
|
||||
* @return the generated input id for the given {@code name}.
|
||||
* @hide
|
||||
*/
|
||||
public static final String generateInputIdForComponentName(ComponentName name) {
|
||||
return name.flattenToShortString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to make this class parcelable.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final Parcelable.Creator<TvInputInfo> CREATOR =
|
||||
new Parcelable.Creator<TvInputInfo>() {
|
||||
@Override
|
||||
public TvInputInfo createFromParcel(Parcel in) {
|
||||
return new TvInputInfo(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TvInputInfo[] newArray(int size) {
|
||||
return new TvInputInfo[size];
|
||||
}
|
||||
};
|
||||
|
||||
private TvInputInfo(Parcel in) {
|
||||
mId = in.readString();
|
||||
mService = ResolveInfo.CREATOR.createFromParcel(in);
|
||||
}
|
||||
}
|
||||
@@ -1,850 +0,0 @@
|
||||
/*
|
||||
* 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 android.tv;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.util.Pools.Pool;
|
||||
import android.util.Pools.SimplePool;
|
||||
import android.util.SparseArray;
|
||||
import android.view.InputChannel;
|
||||
import android.view.InputEvent;
|
||||
import android.view.InputEventSender;
|
||||
import android.view.Surface;
|
||||
import android.view.View;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Central system API to the overall TV input framework (TIF) architecture, which arbitrates
|
||||
* interaction between applications and the selected TV inputs.
|
||||
*/
|
||||
public final class TvInputManager {
|
||||
private static final String TAG = "TvInputManager";
|
||||
|
||||
private final ITvInputManager mService;
|
||||
|
||||
// A mapping from an input to the list of its TvInputListenerRecords.
|
||||
private final Map<String, List<TvInputListenerRecord>> mTvInputListenerRecordsMap =
|
||||
new HashMap<String, List<TvInputListenerRecord>>();
|
||||
|
||||
// A mapping from the sequence number of a session to its SessionCallbackRecord.
|
||||
private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap =
|
||||
new SparseArray<SessionCallbackRecord>();
|
||||
|
||||
// A sequence number for the next session to be created. Should be protected by a lock
|
||||
// {@code mSessionCallbackRecordMap}.
|
||||
private int mNextSeq;
|
||||
|
||||
private final ITvInputClient mClient;
|
||||
|
||||
private final int mUserId;
|
||||
|
||||
/**
|
||||
* Interface used to receive the created session.
|
||||
*/
|
||||
public abstract static class SessionCallback {
|
||||
/**
|
||||
* This is called after {@link TvInputManager#createSession} has been processed.
|
||||
*
|
||||
* @param session A {@link TvInputManager.Session} instance created. This can be
|
||||
* {@code null} if the creation request failed.
|
||||
*/
|
||||
public void onSessionCreated(Session session) {
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when {@link TvInputManager.Session} is released.
|
||||
* This typically happens when the process hosting the session has crashed or been killed.
|
||||
*
|
||||
* @param session A {@link TvInputManager.Session} instance released.
|
||||
*/
|
||||
public void onSessionReleased(Session session) {
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called at the beginning of the playback of a channel and later when the size of
|
||||
* the video has been changed.
|
||||
*
|
||||
* @param session A {@link TvInputManager.Session} associated with this callback
|
||||
* @param width the width of the video
|
||||
* @param height the height of the video
|
||||
* @hide
|
||||
*/
|
||||
public void onVideoSizeChanged(Session session, int width, int height) {
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when a custom event has been sent from this session.
|
||||
*
|
||||
* @param session A {@link TvInputManager.Session} associated with this callback
|
||||
* @param eventType The type of the event.
|
||||
* @param eventArgs Optional arguments of the event.
|
||||
* @hide
|
||||
*/
|
||||
public void onSessionEvent(Session session, String eventType, Bundle eventArgs) {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SessionCallbackRecord {
|
||||
private final SessionCallback mSessionCallback;
|
||||
private final Handler mHandler;
|
||||
private Session mSession;
|
||||
|
||||
public SessionCallbackRecord(SessionCallback sessionCallback,
|
||||
Handler handler) {
|
||||
mSessionCallback = sessionCallback;
|
||||
mHandler = handler;
|
||||
}
|
||||
|
||||
public void postSessionCreated(final Session session) {
|
||||
mSession = session;
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mSessionCallback.onSessionCreated(session);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void postSessionReleased() {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mSessionCallback.onSessionReleased(mSession);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void postVideoSizeChanged(final int width, final int height) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mSessionCallback.onVideoSizeChanged(mSession, width, height);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void postSessionEvent(final String eventType, final Bundle eventArgs) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mSessionCallback.onSessionEvent(mSession, eventType, eventArgs);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface used to monitor status of the TV input.
|
||||
*/
|
||||
public abstract static class TvInputListener {
|
||||
/**
|
||||
* This is called when the availability status of a given TV input is changed.
|
||||
*
|
||||
* @param inputId the id of the TV input.
|
||||
* @param isAvailable {@code true} if the given TV input is available to show TV programs.
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
public void onAvailabilityChanged(String inputId, boolean isAvailable) {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class TvInputListenerRecord {
|
||||
private final TvInputListener mListener;
|
||||
private final Handler mHandler;
|
||||
|
||||
public TvInputListenerRecord(TvInputListener listener, Handler handler) {
|
||||
mListener = listener;
|
||||
mHandler = handler;
|
||||
}
|
||||
|
||||
public TvInputListener getListener() {
|
||||
return mListener;
|
||||
}
|
||||
|
||||
public void postAvailabilityChanged(final String inputId, final boolean isAvailable) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mListener.onAvailabilityChanged(inputId, isAvailable);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public TvInputManager(ITvInputManager service, int userId) {
|
||||
mService = service;
|
||||
mUserId = userId;
|
||||
mClient = new ITvInputClient.Stub() {
|
||||
@Override
|
||||
public void onSessionCreated(String inputId, IBinder token, InputChannel channel,
|
||||
int seq) {
|
||||
synchronized (mSessionCallbackRecordMap) {
|
||||
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
|
||||
if (record == null) {
|
||||
Log.e(TAG, "Callback not found for " + token);
|
||||
return;
|
||||
}
|
||||
Session session = null;
|
||||
if (token != null) {
|
||||
session = new Session(token, channel, mService, mUserId, seq,
|
||||
mSessionCallbackRecordMap);
|
||||
}
|
||||
record.postSessionCreated(session);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionReleased(int seq) {
|
||||
synchronized (mSessionCallbackRecordMap) {
|
||||
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
|
||||
mSessionCallbackRecordMap.delete(seq);
|
||||
if (record == null) {
|
||||
Log.e(TAG, "Callback not found for seq:" + seq);
|
||||
return;
|
||||
}
|
||||
record.mSession.releaseInternal();
|
||||
record.postSessionReleased();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoSizeChanged(int width, int height, int seq) {
|
||||
synchronized (mSessionCallbackRecordMap) {
|
||||
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
|
||||
if (record == null) {
|
||||
Log.e(TAG, "Callback not found for seq " + seq);
|
||||
return;
|
||||
}
|
||||
record.postVideoSizeChanged(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionEvent(String eventType, Bundle eventArgs, int seq) {
|
||||
synchronized (mSessionCallbackRecordMap) {
|
||||
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
|
||||
if (record == null) {
|
||||
Log.e(TAG, "Callback not found for seq " + seq);
|
||||
return;
|
||||
}
|
||||
record.postSessionEvent(eventType, eventArgs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAvailabilityChanged(String inputId, boolean isAvailable) {
|
||||
synchronized (mTvInputListenerRecordsMap) {
|
||||
List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
|
||||
if (records == null) {
|
||||
// Silently ignore - no listener is registered yet.
|
||||
return;
|
||||
}
|
||||
int recordsCount = records.size();
|
||||
for (int i = 0; i < recordsCount; i++) {
|
||||
records.get(i).postAvailabilityChanged(inputId, isAvailable);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the complete list of TV inputs on the system.
|
||||
*
|
||||
* @return List of {@link TvInputInfo} for each TV input that describes its meta information.
|
||||
*/
|
||||
public List<TvInputInfo> getTvInputList() {
|
||||
try {
|
||||
return mService.getTvInputList(mUserId);
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the availability of a given TV input.
|
||||
*
|
||||
* @param inputId the id of the TV input.
|
||||
* @throws IllegalArgumentException if the argument is {@code null}.
|
||||
* @throws IllegalStateException If there is no {@link TvInputListener} registered on the given
|
||||
* TV input.
|
||||
*/
|
||||
public boolean getAvailability(String inputId) {
|
||||
if (inputId == null) {
|
||||
throw new IllegalArgumentException("id cannot be null");
|
||||
}
|
||||
synchronized (mTvInputListenerRecordsMap) {
|
||||
List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
|
||||
if (records == null || records.size() == 0) {
|
||||
throw new IllegalStateException("At least one listener should be registered.");
|
||||
}
|
||||
}
|
||||
try {
|
||||
return mService.getAvailability(mClient, inputId, mUserId);
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a {@link TvInputListener} for a given TV input.
|
||||
*
|
||||
* @param inputId the id of the TV input.
|
||||
* @param listener a listener used to monitor status of the given TV input.
|
||||
* @param handler a {@link Handler} that the status change will be delivered to.
|
||||
* @throws IllegalArgumentException if any of the arguments is {@code null}.
|
||||
*/
|
||||
public void registerListener(String inputId, TvInputListener listener, Handler handler) {
|
||||
if (inputId == null) {
|
||||
throw new IllegalArgumentException("id cannot be null");
|
||||
}
|
||||
if (listener == null) {
|
||||
throw new IllegalArgumentException("listener cannot be null");
|
||||
}
|
||||
if (handler == null) {
|
||||
throw new IllegalArgumentException("handler cannot be null");
|
||||
}
|
||||
synchronized (mTvInputListenerRecordsMap) {
|
||||
List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
|
||||
if (records == null) {
|
||||
records = new ArrayList<TvInputListenerRecord>();
|
||||
mTvInputListenerRecordsMap.put(inputId, records);
|
||||
try {
|
||||
mService.registerCallback(mClient, inputId, mUserId);
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
records.add(new TvInputListenerRecord(listener, handler));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the existing {@link TvInputListener} for a given TV input.
|
||||
*
|
||||
* @param inputId the id of the TV input.
|
||||
* @param listener the existing listener to remove for the given TV input.
|
||||
* @throws IllegalArgumentException if any of the arguments is {@code null}.
|
||||
*/
|
||||
public void unregisterListener(String inputId, final TvInputListener listener) {
|
||||
if (inputId == null) {
|
||||
throw new IllegalArgumentException("id cannot be null");
|
||||
}
|
||||
if (listener == null) {
|
||||
throw new IllegalArgumentException("listener cannot be null");
|
||||
}
|
||||
synchronized (mTvInputListenerRecordsMap) {
|
||||
List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
|
||||
if (records == null) {
|
||||
Log.e(TAG, "No listener found for " + inputId);
|
||||
return;
|
||||
}
|
||||
for (Iterator<TvInputListenerRecord> it = records.iterator(); it.hasNext();) {
|
||||
TvInputListenerRecord record = it.next();
|
||||
if (record.getListener() == listener) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
if (records.isEmpty()) {
|
||||
try {
|
||||
mService.unregisterCallback(mClient, inputId, mUserId);
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
mTvInputListenerRecordsMap.remove(inputId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Session} for a given TV input.
|
||||
* <p>
|
||||
* The number of sessions that can be created at the same time is limited by the capability of
|
||||
* the given TV input.
|
||||
* </p>
|
||||
*
|
||||
* @param inputId the id of the TV input.
|
||||
* @param callback a callback used to receive the created session.
|
||||
* @param handler a {@link Handler} that the session creation will be delivered to.
|
||||
* @throws IllegalArgumentException if any of the arguments is {@code null}.
|
||||
*/
|
||||
public void createSession(String inputId, final SessionCallback callback,
|
||||
Handler handler) {
|
||||
if (inputId == null) {
|
||||
throw new IllegalArgumentException("id cannot be null");
|
||||
}
|
||||
if (callback == null) {
|
||||
throw new IllegalArgumentException("callback cannot be null");
|
||||
}
|
||||
if (handler == null) {
|
||||
throw new IllegalArgumentException("handler cannot be null");
|
||||
}
|
||||
SessionCallbackRecord record = new SessionCallbackRecord(callback, handler);
|
||||
synchronized (mSessionCallbackRecordMap) {
|
||||
int seq = mNextSeq++;
|
||||
mSessionCallbackRecordMap.put(seq, record);
|
||||
try {
|
||||
mService.createSession(mClient, inputId, seq, mUserId);
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** The Session provides the per-session functionality of TV inputs. */
|
||||
public static final class Session {
|
||||
static final int DISPATCH_IN_PROGRESS = -1;
|
||||
static final int DISPATCH_NOT_HANDLED = 0;
|
||||
static final int DISPATCH_HANDLED = 1;
|
||||
|
||||
private static final long INPUT_SESSION_NOT_RESPONDING_TIMEOUT = 2500;
|
||||
|
||||
private final ITvInputManager mService;
|
||||
private final int mUserId;
|
||||
private final int mSeq;
|
||||
|
||||
// For scheduling input event handling on the main thread. This also serves as a lock to
|
||||
// protect pending input events and the input channel.
|
||||
private final InputEventHandler mHandler = new InputEventHandler(Looper.getMainLooper());
|
||||
|
||||
private final Pool<PendingEvent> mPendingEventPool = new SimplePool<PendingEvent>(20);
|
||||
private final SparseArray<PendingEvent> mPendingEvents = new SparseArray<PendingEvent>(20);
|
||||
private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap;
|
||||
|
||||
private IBinder mToken;
|
||||
private TvInputEventSender mSender;
|
||||
private InputChannel mChannel;
|
||||
|
||||
/** @hide */
|
||||
private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId,
|
||||
int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
|
||||
mToken = token;
|
||||
mChannel = channel;
|
||||
mService = service;
|
||||
mUserId = userId;
|
||||
mSeq = seq;
|
||||
mSessionCallbackRecordMap = sessionCallbackRecordMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases this session.
|
||||
*
|
||||
* @throws IllegalStateException if the session has been already released.
|
||||
*/
|
||||
public void release() {
|
||||
if (mToken == null) {
|
||||
throw new IllegalStateException("the session has been already released");
|
||||
}
|
||||
try {
|
||||
mService.releaseSession(mToken, mUserId);
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
releaseInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link android.view.Surface} for this session.
|
||||
*
|
||||
* @param surface A {@link android.view.Surface} used to render video.
|
||||
* @throws IllegalStateException if the session has been already released.
|
||||
* @hide
|
||||
*/
|
||||
public void setSurface(Surface surface) {
|
||||
if (mToken == null) {
|
||||
throw new IllegalStateException("the session has been already released");
|
||||
}
|
||||
// surface can be null.
|
||||
try {
|
||||
mService.setSurface(mToken, surface, mUserId);
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the relative volume of this session to handle a change of audio focus.
|
||||
*
|
||||
* @param volume A volume value between 0.0f to 1.0f.
|
||||
* @throws IllegalArgumentException if the volume value is out of range.
|
||||
* @throws IllegalStateException if the session has been already released.
|
||||
*/
|
||||
public void setVolume(float volume) {
|
||||
if (mToken == null) {
|
||||
throw new IllegalStateException("the session has been already released");
|
||||
}
|
||||
try {
|
||||
if (volume < 0.0f || volume > 1.0f) {
|
||||
throw new IllegalArgumentException("volume should be between 0.0f and 1.0f");
|
||||
}
|
||||
mService.setVolume(mToken, volume, mUserId);
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tunes to a given channel.
|
||||
*
|
||||
* @param channelUri The URI of a channel.
|
||||
* @throws IllegalArgumentException if the argument is {@code null}.
|
||||
* @throws IllegalStateException if the session has been already released.
|
||||
*/
|
||||
public void tune(Uri channelUri) {
|
||||
if (channelUri == null) {
|
||||
throw new IllegalArgumentException("channelUri cannot be null");
|
||||
}
|
||||
if (mToken == null) {
|
||||
throw new IllegalStateException("the session has been already released");
|
||||
}
|
||||
try {
|
||||
mService.tune(mToken, channelUri, mUserId);
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an overlay view. Once the overlay view is created, {@link #relayoutOverlayView}
|
||||
* should be called whenever the layout of its containing view is changed.
|
||||
* {@link #removeOverlayView()} should be called to remove the overlay view.
|
||||
* Since a session can have only one overlay view, this method should be called only once
|
||||
* or it can be called again after calling {@link #removeOverlayView()}.
|
||||
*
|
||||
* @param view A view playing TV.
|
||||
* @param frame A position of the overlay view.
|
||||
* @throws IllegalArgumentException if any of the arguments is {@code null}.
|
||||
* @throws IllegalStateException if {@code view} is not attached to a window or
|
||||
* if the session has been already released.
|
||||
*/
|
||||
void createOverlayView(View view, Rect frame) {
|
||||
if (view == null) {
|
||||
throw new IllegalArgumentException("view cannot be null");
|
||||
}
|
||||
if (frame == null) {
|
||||
throw new IllegalArgumentException("frame cannot be null");
|
||||
}
|
||||
if (view.getWindowToken() == null) {
|
||||
throw new IllegalStateException("view must be attached to a window");
|
||||
}
|
||||
if (mToken == null) {
|
||||
throw new IllegalStateException("the session has been already released");
|
||||
}
|
||||
try {
|
||||
mService.createOverlayView(mToken, view.getWindowToken(), frame, mUserId);
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Relayouts the current overlay view.
|
||||
*
|
||||
* @param frame A new position of the overlay view.
|
||||
* @throws IllegalArgumentException if the arguments is {@code null}.
|
||||
* @throws IllegalStateException if the session has been already released.
|
||||
*/
|
||||
void relayoutOverlayView(Rect frame) {
|
||||
if (frame == null) {
|
||||
throw new IllegalArgumentException("frame cannot be null");
|
||||
}
|
||||
if (mToken == null) {
|
||||
throw new IllegalStateException("the session has been already released");
|
||||
}
|
||||
try {
|
||||
mService.relayoutOverlayView(mToken, frame, mUserId);
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the current overlay view.
|
||||
*
|
||||
* @throws IllegalStateException if the session has been already released.
|
||||
*/
|
||||
void removeOverlayView() {
|
||||
if (mToken == null) {
|
||||
throw new IllegalStateException("the session has been already released");
|
||||
}
|
||||
try {
|
||||
mService.removeOverlayView(mToken, mUserId);
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an input event to this session.
|
||||
*
|
||||
* @param event {@link InputEvent} to dispatch.
|
||||
* @param token A token used to identify the input event later in the callback.
|
||||
* @param callback A callback used to receive the dispatch result.
|
||||
* @param handler {@link Handler} that the dispatch result will be delivered to.
|
||||
* @return Returns {@link #DISPATCH_HANDLED} if the event was handled. Returns
|
||||
* {@link #DISPATCH_NOT_HANDLED} if the event was not handled. Returns
|
||||
* {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the callback will
|
||||
* be invoked later.
|
||||
* @throws IllegalArgumentException if any of the necessary arguments is {@code null}.
|
||||
* @hide
|
||||
*/
|
||||
public int dispatchInputEvent(InputEvent event, Object token,
|
||||
FinishedInputEventCallback callback, Handler handler) {
|
||||
if (event == null) {
|
||||
throw new IllegalArgumentException("event cannot be null");
|
||||
}
|
||||
if (callback != null && handler == null) {
|
||||
throw new IllegalArgumentException("handler cannot be null");
|
||||
}
|
||||
synchronized (mHandler) {
|
||||
if (mChannel == null) {
|
||||
return DISPATCH_NOT_HANDLED;
|
||||
}
|
||||
PendingEvent p = obtainPendingEventLocked(event, token, callback, handler);
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
// Already running on the main thread so we can send the event immediately.
|
||||
return sendInputEventOnMainLooperLocked(p);
|
||||
}
|
||||
|
||||
// Post the event to the main thread.
|
||||
Message msg = mHandler.obtainMessage(InputEventHandler.MSG_SEND_INPUT_EVENT, p);
|
||||
msg.setAsynchronous(true);
|
||||
mHandler.sendMessage(msg);
|
||||
return DISPATCH_IN_PROGRESS;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback that is invoked when an input event that was dispatched to this session has been
|
||||
* finished.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public interface FinishedInputEventCallback {
|
||||
/**
|
||||
* Called when the dispatched input event is finished.
|
||||
*
|
||||
* @param token a token passed to {@link #dispatchInputEvent}.
|
||||
* @param handled {@code true} if the dispatched input event was handled properly.
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
public void onFinishedInputEvent(Object token, boolean handled);
|
||||
}
|
||||
|
||||
// Must be called on the main looper
|
||||
private void sendInputEventAndReportResultOnMainLooper(PendingEvent p) {
|
||||
synchronized (mHandler) {
|
||||
int result = sendInputEventOnMainLooperLocked(p);
|
||||
if (result == DISPATCH_IN_PROGRESS) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
invokeFinishedInputEventCallback(p, false);
|
||||
}
|
||||
|
||||
private int sendInputEventOnMainLooperLocked(PendingEvent p) {
|
||||
if (mChannel != null) {
|
||||
if (mSender == null) {
|
||||
mSender = new TvInputEventSender(mChannel, mHandler.getLooper());
|
||||
}
|
||||
|
||||
final InputEvent event = p.mEvent;
|
||||
final int seq = event.getSequenceNumber();
|
||||
if (mSender.sendInputEvent(seq, event)) {
|
||||
mPendingEvents.put(seq, p);
|
||||
Message msg = mHandler.obtainMessage(InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p);
|
||||
msg.setAsynchronous(true);
|
||||
mHandler.sendMessageDelayed(msg, INPUT_SESSION_NOT_RESPONDING_TIMEOUT);
|
||||
return DISPATCH_IN_PROGRESS;
|
||||
}
|
||||
|
||||
Log.w(TAG, "Unable to send input event to session: " + mToken + " dropping:"
|
||||
+ event);
|
||||
}
|
||||
return DISPATCH_NOT_HANDLED;
|
||||
}
|
||||
|
||||
void finishedInputEvent(int seq, boolean handled, boolean timeout) {
|
||||
final PendingEvent p;
|
||||
synchronized (mHandler) {
|
||||
int index = mPendingEvents.indexOfKey(seq);
|
||||
if (index < 0) {
|
||||
return; // spurious, event already finished or timed out
|
||||
}
|
||||
|
||||
p = mPendingEvents.valueAt(index);
|
||||
mPendingEvents.removeAt(index);
|
||||
|
||||
if (timeout) {
|
||||
Log.w(TAG, "Timeout waiting for seesion to handle input event after "
|
||||
+ INPUT_SESSION_NOT_RESPONDING_TIMEOUT + " ms: " + mToken);
|
||||
} else {
|
||||
mHandler.removeMessages(InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p);
|
||||
}
|
||||
}
|
||||
|
||||
invokeFinishedInputEventCallback(p, handled);
|
||||
}
|
||||
|
||||
// Assumes the event has already been removed from the queue.
|
||||
void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) {
|
||||
p.mHandled = handled;
|
||||
if (p.mHandler.getLooper().isCurrentThread()) {
|
||||
// Already running on the callback handler thread so we can send the callback
|
||||
// immediately.
|
||||
p.run();
|
||||
} else {
|
||||
// Post the event to the callback handler thread.
|
||||
// In this case, the callback will be responsible for recycling the event.
|
||||
Message msg = Message.obtain(p.mHandler, p);
|
||||
msg.setAsynchronous(true);
|
||||
msg.sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
private void flushPendingEventsLocked() {
|
||||
mHandler.removeMessages(InputEventHandler.MSG_FLUSH_INPUT_EVENT);
|
||||
|
||||
final int count = mPendingEvents.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
int seq = mPendingEvents.keyAt(i);
|
||||
Message msg = mHandler.obtainMessage(InputEventHandler.MSG_FLUSH_INPUT_EVENT, seq, 0);
|
||||
msg.setAsynchronous(true);
|
||||
msg.sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
private PendingEvent obtainPendingEventLocked(InputEvent event, Object token,
|
||||
FinishedInputEventCallback callback, Handler handler) {
|
||||
PendingEvent p = mPendingEventPool.acquire();
|
||||
if (p == null) {
|
||||
p = new PendingEvent();
|
||||
}
|
||||
p.mEvent = event;
|
||||
p.mToken = token;
|
||||
p.mCallback = callback;
|
||||
p.mHandler = handler;
|
||||
return p;
|
||||
}
|
||||
|
||||
private void recyclePendingEventLocked(PendingEvent p) {
|
||||
p.recycle();
|
||||
mPendingEventPool.release(p);
|
||||
}
|
||||
|
||||
private void releaseInternal() {
|
||||
mToken = null;
|
||||
synchronized (mHandler) {
|
||||
if (mChannel != null) {
|
||||
if (mSender != null) {
|
||||
flushPendingEventsLocked();
|
||||
mSender.dispose();
|
||||
mSender = null;
|
||||
}
|
||||
mChannel.dispose();
|
||||
mChannel = null;
|
||||
}
|
||||
}
|
||||
synchronized (mSessionCallbackRecordMap) {
|
||||
mSessionCallbackRecordMap.remove(mSeq);
|
||||
}
|
||||
}
|
||||
|
||||
private final class InputEventHandler extends Handler {
|
||||
public static final int MSG_SEND_INPUT_EVENT = 1;
|
||||
public static final int MSG_TIMEOUT_INPUT_EVENT = 2;
|
||||
public static final int MSG_FLUSH_INPUT_EVENT = 3;
|
||||
|
||||
InputEventHandler(Looper looper) {
|
||||
super(looper, null, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_SEND_INPUT_EVENT: {
|
||||
sendInputEventAndReportResultOnMainLooper((PendingEvent) msg.obj);
|
||||
return;
|
||||
}
|
||||
case MSG_TIMEOUT_INPUT_EVENT: {
|
||||
finishedInputEvent(msg.arg1, false, true);
|
||||
return;
|
||||
}
|
||||
case MSG_FLUSH_INPUT_EVENT: {
|
||||
finishedInputEvent(msg.arg1, false, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class TvInputEventSender extends InputEventSender {
|
||||
public TvInputEventSender(InputChannel inputChannel, Looper looper) {
|
||||
super(inputChannel, looper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputEventFinished(int seq, boolean handled) {
|
||||
finishedInputEvent(seq, handled, false);
|
||||
}
|
||||
}
|
||||
|
||||
private final class PendingEvent implements Runnable {
|
||||
public InputEvent mEvent;
|
||||
public Object mToken;
|
||||
public FinishedInputEventCallback mCallback;
|
||||
public Handler mHandler;
|
||||
public boolean mHandled;
|
||||
|
||||
public void recycle() {
|
||||
mEvent = null;
|
||||
mToken = null;
|
||||
mCallback = null;
|
||||
mHandler = null;
|
||||
mHandled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
mCallback.onFinishedInputEvent(mToken, mHandled);
|
||||
|
||||
synchronized (mHandler) {
|
||||
recyclePendingEventLocked(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,598 +0,0 @@
|
||||
/*
|
||||
* 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 android.tv;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteCallbackList;
|
||||
import android.os.RemoteException;
|
||||
import android.tv.TvInputManager.Session;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.InputChannel;
|
||||
import android.view.InputDevice;
|
||||
import android.view.InputEvent;
|
||||
import android.view.InputEventReceiver;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.os.SomeArgs;
|
||||
|
||||
/**
|
||||
* A base class for implementing television input service.
|
||||
*/
|
||||
public abstract class TvInputService extends Service {
|
||||
// STOPSHIP: Turn debugging off.
|
||||
private static final boolean DEBUG = true;
|
||||
private static final String TAG = "TvInputService";
|
||||
|
||||
/**
|
||||
* This is the interface name that a service implementing a TV input should say that it support
|
||||
* -- that is, this is the action it uses for its intent filter. To be supported, the service
|
||||
* must also require the {@link android.Manifest.permission#BIND_TV_INPUT} permission so that
|
||||
* other applications cannot abuse it.
|
||||
*/
|
||||
public static final String SERVICE_INTERFACE = "android.tv.TvInputService";
|
||||
|
||||
private String mId;
|
||||
private final Handler mHandler = new ServiceHandler();
|
||||
private final RemoteCallbackList<ITvInputServiceCallback> mCallbacks =
|
||||
new RemoteCallbackList<ITvInputServiceCallback>();
|
||||
private boolean mAvailable;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
mId = TvInputInfo.generateInputIdForComponentName(
|
||||
new ComponentName(getPackageName(), getClass().getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final IBinder onBind(Intent intent) {
|
||||
return new ITvInputService.Stub() {
|
||||
@Override
|
||||
public void registerCallback(ITvInputServiceCallback cb) {
|
||||
if (cb != null) {
|
||||
mCallbacks.register(cb);
|
||||
// The first time a callback is registered, the service needs to report its
|
||||
// availability status so that the system can know its initial value.
|
||||
try {
|
||||
cb.onAvailabilityChanged(mId, mAvailable);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "error in onAvailabilityChanged", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterCallback(ITvInputServiceCallback cb) {
|
||||
if (cb != null) {
|
||||
mCallbacks.unregister(cb);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createSession(InputChannel channel, ITvInputSessionCallback cb) {
|
||||
if (channel == null) {
|
||||
Log.w(TAG, "Creating session without input channel");
|
||||
}
|
||||
if (cb == null) {
|
||||
return;
|
||||
}
|
||||
SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = channel;
|
||||
args.arg2 = cb;
|
||||
mHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args).sendToTarget();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to notify an availability change of this TV input service.
|
||||
*
|
||||
* @param available {@code true} if the input service is available to show TV programs.
|
||||
*/
|
||||
public final void setAvailable(boolean available) {
|
||||
if (available != mAvailable) {
|
||||
mAvailable = available;
|
||||
mHandler.obtainMessage(ServiceHandler.DO_BROADCAST_AVAILABILITY_CHANGE, available)
|
||||
.sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of callbacks that are registered.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public final int getRegisteredCallbackCount() {
|
||||
return mCallbacks.getRegisteredCallbackCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a concrete implementation of {@link TvInputSessionImpl}.
|
||||
* <p>
|
||||
* May return {@code null} if this TV input service fails to create a session for some reason.
|
||||
* </p>
|
||||
*/
|
||||
public abstract TvInputSessionImpl onCreateSession();
|
||||
|
||||
/**
|
||||
* Base class for derived classes to implement to provide {@link TvInputManager.Session}.
|
||||
*/
|
||||
public abstract class TvInputSessionImpl implements KeyEvent.Callback {
|
||||
private final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState();
|
||||
private final WindowManager mWindowManager;
|
||||
private WindowManager.LayoutParams mWindowParams;
|
||||
private Surface mSurface;
|
||||
private View mOverlayView;
|
||||
private boolean mOverlayViewEnabled;
|
||||
private IBinder mWindowToken;
|
||||
private Rect mOverlayFrame;
|
||||
private ITvInputSessionCallback mSessionCallback;
|
||||
|
||||
public TvInputSessionImpl() {
|
||||
mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables the overlay view. By default, the overlay view is disabled. Must be
|
||||
* called explicitly after the session is created to enable the overlay view.
|
||||
*
|
||||
* @param enable {@code true} if you want to enable the overlay view. {@code false}
|
||||
* otherwise.
|
||||
*/
|
||||
public void setOverlayViewEnabled(final boolean enable) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (enable == mOverlayViewEnabled) {
|
||||
return;
|
||||
}
|
||||
mOverlayViewEnabled = enable;
|
||||
if (enable) {
|
||||
if (mWindowToken != null) {
|
||||
createOverlayView(mWindowToken, mOverlayFrame);
|
||||
}
|
||||
} else {
|
||||
removeOverlayView(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an event to the application using this session.
|
||||
*
|
||||
* @param eventType The type of the event.
|
||||
* @param eventArgs Optional arguments of the event.
|
||||
* @hide
|
||||
*/
|
||||
public void dispatchSessionEvent(final String eventType, final Bundle eventArgs) {
|
||||
if (eventType == null) {
|
||||
throw new IllegalArgumentException("eventType should not be null.");
|
||||
}
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (DEBUG) Log.d(TAG, "dispatchSessionEvent(" + eventType + ")");
|
||||
mSessionCallback.onSessionEvent(eventType, eventArgs);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "error in sending event (event=" + eventType + ")");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the change on the size of the video. This is expected to be called at the
|
||||
* beginning of the playback and later when the size has been changed.
|
||||
*
|
||||
* @param width The width of the video.
|
||||
* @param height The height of the video.
|
||||
* @hide
|
||||
*/
|
||||
public void dispatchVideoSizeChanged(final int width, final int height) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (DEBUG) Log.d(TAG, "dispatchVideoSizeChanged");
|
||||
mSessionCallback.onVideoSizeChanged(width, height);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "error in dispatchVideoSizeChanged");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the session is released.
|
||||
*/
|
||||
public abstract void onRelease();
|
||||
|
||||
/**
|
||||
* Sets the {@link Surface} for the current input session on which the TV input renders
|
||||
* video.
|
||||
*
|
||||
* @param surface {@link Surface} an application passes to this TV input session.
|
||||
* @return {@code true} if the surface was set, {@code false} otherwise.
|
||||
*/
|
||||
public abstract boolean onSetSurface(Surface surface);
|
||||
|
||||
/**
|
||||
* Sets the relative volume of the current TV input session to handle the change of audio
|
||||
* focus by setting.
|
||||
*
|
||||
* @param volume Volume scale from 0.0 to 1.0.
|
||||
*/
|
||||
public abstract void onSetVolume(float volume);
|
||||
|
||||
/**
|
||||
* Tunes to a given channel.
|
||||
*
|
||||
* @param channelUri The URI of the channel.
|
||||
* @return {@code true} the tuning was successful, {@code false} otherwise.
|
||||
*/
|
||||
public abstract boolean onTune(Uri channelUri);
|
||||
|
||||
/**
|
||||
* Called when an application requests to create an overlay view. Each session
|
||||
* implementation can override this method and return its own view.
|
||||
*
|
||||
* @return a view attached to the overlay window
|
||||
*/
|
||||
public View onCreateOverlayView() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of {@link android.view.KeyEvent.Callback#onKeyDown(int, KeyEvent)
|
||||
* KeyEvent.Callback.onKeyDown()}: always returns false (doesn't handle the event).
|
||||
* <p>
|
||||
* Override this to intercept key down events before they are processed by the application.
|
||||
* If you return true, the application will not process the event itself. If you return
|
||||
* false, the normal application processing will occur as if the TV input had not seen the
|
||||
* event at all.
|
||||
*
|
||||
* @param keyCode The value in event.getKeyCode().
|
||||
* @param event Description of the key event.
|
||||
* @return If you handled the event, return {@code true}. If you want to allow the event to
|
||||
* be handled by the next receiver, return {@code false}.
|
||||
*/
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of
|
||||
* {@link android.view.KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
|
||||
* KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle the event).
|
||||
* <p>
|
||||
* Override this to intercept key long press events before they are processed by the
|
||||
* application. If you return true, the application will not process the event itself. If
|
||||
* you return false, the normal application processing will occur as if the TV input had not
|
||||
* seen the event at all.
|
||||
*
|
||||
* @param keyCode The value in event.getKeyCode().
|
||||
* @param event Description of the key event.
|
||||
* @return If you handled the event, return {@code true}. If you want to allow the event to
|
||||
* be handled by the next receiver, return {@code false}.
|
||||
*/
|
||||
@Override
|
||||
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of
|
||||
* {@link android.view.KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent)
|
||||
* KeyEvent.Callback.onKeyMultiple()}: always returns false (doesn't handle the event).
|
||||
* <p>
|
||||
* Override this to intercept special key multiple events before they are processed by the
|
||||
* application. If you return true, the application will not itself process the event. If
|
||||
* you return false, the normal application processing will occur as if the TV input had not
|
||||
* seen the event at all.
|
||||
*
|
||||
* @param keyCode The value in event.getKeyCode().
|
||||
* @param count The number of times the action was made.
|
||||
* @param event Description of the key event.
|
||||
* @return If you handled the event, return {@code true}. If you want to allow the event to
|
||||
* be handled by the next receiver, return {@code false}.
|
||||
*/
|
||||
@Override
|
||||
public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of {@link android.view.KeyEvent.Callback#onKeyUp(int, KeyEvent)
|
||||
* KeyEvent.Callback.onKeyUp()}: always returns false (doesn't handle the event).
|
||||
* <p>
|
||||
* Override this to intercept key up events before they are processed by the application. If
|
||||
* you return true, the application will not itself process the event. If you return false,
|
||||
* the normal application processing will occur as if the TV input had not seen the event at
|
||||
* all.
|
||||
*
|
||||
* @param keyCode The value in event.getKeyCode().
|
||||
* @param event Description of the key event.
|
||||
* @return If you handled the event, return {@code true}. If you want to allow the event to
|
||||
* be handled by the next receiver, return {@code false}.
|
||||
*/
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this method to handle touch screen motion events on the current input session.
|
||||
*
|
||||
* @param event The motion event being received.
|
||||
* @return If you handled the event, return {@code true}. If you want to allow the event to
|
||||
* be handled by the next receiver, return {@code false}.
|
||||
* @see View#onTouchEvent
|
||||
*/
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this method to handle trackball events on the current input session.
|
||||
*
|
||||
* @param event The motion event being received.
|
||||
* @return If you handled the event, return {@code true}. If you want to allow the event to
|
||||
* be handled by the next receiver, return {@code false}.
|
||||
* @see View#onTrackballEvent
|
||||
*/
|
||||
public boolean onTrackballEvent(MotionEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this method to handle generic motion events on the current input session.
|
||||
*
|
||||
* @param event The motion event being received.
|
||||
* @return If you handled the event, return {@code true}. If you want to allow the event to
|
||||
* be handled by the next receiver, return {@code false}.
|
||||
* @see View#onGenericMotionEvent
|
||||
*/
|
||||
public boolean onGenericMotionEvent(MotionEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when the application would like to stop using the current input
|
||||
* session.
|
||||
*/
|
||||
void release() {
|
||||
onRelease();
|
||||
if (mSurface != null) {
|
||||
mSurface.release();
|
||||
mSurface = null;
|
||||
}
|
||||
removeOverlayView(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #onSetSurface}.
|
||||
*/
|
||||
void setSurface(Surface surface) {
|
||||
onSetSurface(surface);
|
||||
if (mSurface != null) {
|
||||
mSurface.release();
|
||||
}
|
||||
mSurface = surface;
|
||||
// TODO: Handle failure.
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #onSetVolume}.
|
||||
*/
|
||||
void setVolume(float volume) {
|
||||
onSetVolume(volume);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #onTune}.
|
||||
*/
|
||||
void tune(Uri channelUri) {
|
||||
onTune(channelUri);
|
||||
// TODO: Handle failure.
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an overlay view. This calls {@link #onCreateOverlayView} to get a view to attach
|
||||
* to the overlay window.
|
||||
*
|
||||
* @param windowToken A window token of an application.
|
||||
* @param frame A position of the overlay view.
|
||||
*/
|
||||
void createOverlayView(IBinder windowToken, Rect frame) {
|
||||
if (mOverlayView != null) {
|
||||
mWindowManager.removeView(mOverlayView);
|
||||
mOverlayView = null;
|
||||
}
|
||||
if (DEBUG) Log.d(TAG, "create overlay view(" + frame + ")");
|
||||
mWindowToken = windowToken;
|
||||
mOverlayFrame = frame;
|
||||
if (!mOverlayViewEnabled) {
|
||||
return;
|
||||
}
|
||||
mOverlayView = onCreateOverlayView();
|
||||
if (mOverlayView == null) {
|
||||
return;
|
||||
}
|
||||
// TvView's window type is TYPE_APPLICATION_MEDIA and we want to create
|
||||
// an overlay window above the media window but below the application window.
|
||||
int type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
|
||||
// We make the overlay view non-focusable and non-touchable so that
|
||||
// the application that owns the window token can decide whether to consume or
|
||||
// dispatch the input events.
|
||||
int flag = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
|
||||
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
|
||||
mWindowParams = new WindowManager.LayoutParams(
|
||||
frame.right - frame.left, frame.bottom - frame.top,
|
||||
frame.left, frame.top, type, flag, PixelFormat.TRANSPARENT);
|
||||
mWindowParams.privateFlags |=
|
||||
WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
|
||||
mWindowParams.gravity = Gravity.START | Gravity.TOP;
|
||||
mWindowParams.token = windowToken;
|
||||
mWindowManager.addView(mOverlayView, mWindowParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Relayouts the current overlay view.
|
||||
*
|
||||
* @param frame A new position of the overlay view.
|
||||
*/
|
||||
void relayoutOverlayView(Rect frame) {
|
||||
if (DEBUG) Log.d(TAG, "relayoutOverlayView(" + frame + ")");
|
||||
mOverlayFrame = frame;
|
||||
if (!mOverlayViewEnabled || mOverlayView == null) {
|
||||
return;
|
||||
}
|
||||
mWindowParams.x = frame.left;
|
||||
mWindowParams.y = frame.top;
|
||||
mWindowParams.width = frame.right - frame.left;
|
||||
mWindowParams.height = frame.bottom - frame.top;
|
||||
mWindowManager.updateViewLayout(mOverlayView, mWindowParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the current overlay view.
|
||||
*/
|
||||
void removeOverlayView(boolean clearWindowToken) {
|
||||
if (DEBUG) Log.d(TAG, "removeOverlayView(" + mOverlayView + ")");
|
||||
if (clearWindowToken) {
|
||||
mWindowToken = null;
|
||||
mOverlayFrame = null;
|
||||
}
|
||||
if (mOverlayView != null) {
|
||||
mWindowManager.removeView(mOverlayView);
|
||||
mOverlayView = null;
|
||||
mWindowParams = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes care of dispatching incoming input events and tells whether the event was handled.
|
||||
*/
|
||||
int dispatchInputEvent(InputEvent event, InputEventReceiver receiver) {
|
||||
if (DEBUG) Log.d(TAG, "dispatchInputEvent(" + event + ")");
|
||||
if (event instanceof KeyEvent) {
|
||||
if (((KeyEvent) event).dispatch(this, mDispatcherState, this)) {
|
||||
return Session.DISPATCH_HANDLED;
|
||||
}
|
||||
} else if (event instanceof MotionEvent) {
|
||||
MotionEvent motionEvent = (MotionEvent) event;
|
||||
final int source = motionEvent.getSource();
|
||||
if (motionEvent.isTouchEvent()) {
|
||||
if (onTouchEvent(motionEvent)) {
|
||||
return Session.DISPATCH_HANDLED;
|
||||
}
|
||||
} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
|
||||
if (onTrackballEvent(motionEvent)) {
|
||||
return Session.DISPATCH_HANDLED;
|
||||
}
|
||||
} else {
|
||||
if (onGenericMotionEvent(motionEvent)) {
|
||||
return Session.DISPATCH_HANDLED;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mOverlayView == null || !mOverlayView.isAttachedToWindow()) {
|
||||
return Session.DISPATCH_NOT_HANDLED;
|
||||
}
|
||||
if (!mOverlayView.hasWindowFocus()) {
|
||||
mOverlayView.getViewRootImpl().windowFocusChanged(true, true);
|
||||
}
|
||||
mOverlayView.getViewRootImpl().dispatchInputEvent(event, receiver);
|
||||
return Session.DISPATCH_IN_PROGRESS;
|
||||
}
|
||||
|
||||
private void setSessionCallback(ITvInputSessionCallback callback) {
|
||||
mSessionCallback = callback;
|
||||
}
|
||||
}
|
||||
|
||||
private final class ServiceHandler extends Handler {
|
||||
private static final int DO_CREATE_SESSION = 1;
|
||||
private static final int DO_BROADCAST_AVAILABILITY_CHANGE = 2;
|
||||
|
||||
@Override
|
||||
public final void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case DO_CREATE_SESSION: {
|
||||
SomeArgs args = (SomeArgs) msg.obj;
|
||||
InputChannel channel = (InputChannel) args.arg1;
|
||||
ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg2;
|
||||
try {
|
||||
TvInputSessionImpl sessionImpl = onCreateSession();
|
||||
if (sessionImpl == null) {
|
||||
// Failed to create a session.
|
||||
cb.onSessionCreated(null);
|
||||
} else {
|
||||
sessionImpl.setSessionCallback(cb);
|
||||
ITvInputSession stub = new ITvInputSessionWrapper(TvInputService.this,
|
||||
sessionImpl, channel);
|
||||
cb.onSessionCreated(stub);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "error in onSessionCreated");
|
||||
}
|
||||
args.recycle();
|
||||
return;
|
||||
}
|
||||
case DO_BROADCAST_AVAILABILITY_CHANGE: {
|
||||
boolean isAvailable = (Boolean) msg.obj;
|
||||
int n = mCallbacks.beginBroadcast();
|
||||
try {
|
||||
for (int i = 0; i < n; i++) {
|
||||
mCallbacks.getBroadcastItem(i).onAvailabilityChanged(mId, isAvailable);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Unexpected exception", e);
|
||||
} finally {
|
||||
mCallbacks.finishBroadcast();
|
||||
}
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
Log.w(TAG, "Unhandled message code: " + msg.what);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 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 android.tv;
|
||||
|
||||
parcelable TvStreamConfig;
|
||||
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
* 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 android.tv;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class TvStreamConfig implements Parcelable {
|
||||
static final String TAG = TvStreamConfig.class.getSimpleName();
|
||||
|
||||
public final static int STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE = 1;
|
||||
public final static int STREAM_TYPE_BUFFER_PRODUCER = 2;
|
||||
|
||||
private int mStreamId;
|
||||
private int mType;
|
||||
// TODO: Revisit if max widht/height really make sense.
|
||||
private int mMaxWidth;
|
||||
private int mMaxHeight;
|
||||
/**
|
||||
* Generations are incremented once framework receives STREAM_CONFIGURATION_CHANGED event from
|
||||
* HAL module. Framework should throw away outdated configurations and get new configurations
|
||||
* via tv_input_device::get_stream_configurations().
|
||||
*/
|
||||
private int mGeneration;
|
||||
|
||||
public static final Parcelable.Creator<TvStreamConfig> CREATOR =
|
||||
new Parcelable.Creator<TvStreamConfig>() {
|
||||
@Override
|
||||
public TvStreamConfig createFromParcel(Parcel source) {
|
||||
try {
|
||||
return new Builder().
|
||||
streamId(source.readInt()).
|
||||
type(source.readInt()).
|
||||
maxWidth(source.readInt()).
|
||||
maxHeight(source.readInt()).
|
||||
generation(source.readInt()).build();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Exception creating TvStreamConfig from parcel", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TvStreamConfig[] newArray(int size) {
|
||||
return new TvStreamConfig[size];
|
||||
}
|
||||
};
|
||||
|
||||
private TvStreamConfig() {}
|
||||
|
||||
public int getStreamId() {
|
||||
return mStreamId;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
public int getMaxWidth() {
|
||||
return mMaxWidth;
|
||||
}
|
||||
|
||||
public int getMaxHeight() {
|
||||
return mMaxHeight;
|
||||
}
|
||||
|
||||
public int getGeneration() {
|
||||
return mGeneration;
|
||||
}
|
||||
|
||||
// Parcelable
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(mStreamId);
|
||||
dest.writeInt(mType);
|
||||
dest.writeInt(mMaxWidth);
|
||||
dest.writeInt(mMaxHeight);
|
||||
dest.writeInt(mGeneration);
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper class for creating a TvStreamConfig object.
|
||||
*/
|
||||
public static final class Builder {
|
||||
private Integer mStreamId;
|
||||
private Integer mType;
|
||||
private Integer mMaxWidth;
|
||||
private Integer mMaxHeight;
|
||||
private Integer mGeneration;
|
||||
|
||||
public Builder() {
|
||||
}
|
||||
|
||||
public Builder streamId(int streamId) {
|
||||
mStreamId = streamId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder type(int type) {
|
||||
mType = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder maxWidth(int maxWidth) {
|
||||
mMaxWidth = maxWidth;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder maxHeight(int maxHeight) {
|
||||
mMaxHeight = maxHeight;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder generation(int generation) {
|
||||
mGeneration = generation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TvStreamConfig build() {
|
||||
if (mStreamId == null || mType == null || mMaxWidth == null || mMaxHeight == null
|
||||
|| mGeneration == null) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
TvStreamConfig config = new TvStreamConfig();
|
||||
config.mStreamId = mStreamId;
|
||||
config.mType = mType;
|
||||
config.mMaxWidth = mMaxWidth;
|
||||
config.mMaxHeight = mMaxHeight;
|
||||
config.mGeneration = mGeneration;
|
||||
return config;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,402 +0,0 @@
|
||||
/*
|
||||
* 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 android.tv;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.text.TextUtils;
|
||||
import android.tv.TvInputManager.Session;
|
||||
import android.tv.TvInputManager.Session.FinishedInputEventCallback;
|
||||
import android.tv.TvInputManager.SessionCallback;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.InputEvent;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.ViewRootImpl;
|
||||
|
||||
/**
|
||||
* View playing TV
|
||||
*/
|
||||
public class TvView extends SurfaceView {
|
||||
// STOPSHIP: Turn debugging off.
|
||||
private static final boolean DEBUG = true;
|
||||
private static final String TAG = "TvView";
|
||||
|
||||
private final Handler mHandler = new Handler();
|
||||
private TvInputManager.Session mSession;
|
||||
private Surface mSurface;
|
||||
private boolean mOverlayViewCreated;
|
||||
private Rect mOverlayViewFrame;
|
||||
private final TvInputManager mTvInputManager;
|
||||
private SessionCallback mSessionCallback;
|
||||
private OnUnhandledInputEventListener mOnUnhandledInputEventListener;
|
||||
|
||||
private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||
Log.d(TAG, "surfaceChanged(holder=" + holder + ", format=" + format + ", width=" + width
|
||||
+ ", height=" + height + ")");
|
||||
if (holder.getSurface() == mSurface) {
|
||||
return;
|
||||
}
|
||||
mSurface = holder.getSurface();
|
||||
setSessionSurface(mSurface);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
mSurface = holder.getSurface();
|
||||
setSessionSurface(mSurface);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
mSurface = null;
|
||||
setSessionSurface(null);
|
||||
}
|
||||
};
|
||||
|
||||
private final FinishedInputEventCallback mFinishedInputEventCallback =
|
||||
new FinishedInputEventCallback() {
|
||||
@Override
|
||||
public void onFinishedInputEvent(Object token, boolean handled) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onFinishedInputEvent(token=" + token + ", handled=" + handled + ")");
|
||||
}
|
||||
if (handled) {
|
||||
return;
|
||||
}
|
||||
// TODO: Re-order unhandled events.
|
||||
InputEvent event = (InputEvent) token;
|
||||
if (dispatchUnhandledInputEvent(event)) {
|
||||
return;
|
||||
}
|
||||
ViewRootImpl viewRootImpl = getViewRootImpl();
|
||||
if (viewRootImpl != null) {
|
||||
viewRootImpl.dispatchUnhandledInputEvent(event);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public TvView(Context context) {
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
public TvView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public TvView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
getHolder().addCallback(mSurfaceHolderCallback);
|
||||
mTvInputManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a TV input to this view. {@link SessionCallback#onSessionCreated} will be
|
||||
* called to send the result of this binding with {@link TvInputManager.Session}.
|
||||
* If a TV input is already bound, the input will be unbound from this view and its session
|
||||
* will be released.
|
||||
*
|
||||
* @param inputId the id of TV input which will be bound to this view.
|
||||
* @param callback called when TV input is bound. The callback sends
|
||||
* {@link TvInputManager.Session}
|
||||
* @throws IllegalArgumentException if any of the arguments is {@code null}.
|
||||
*/
|
||||
public void bindTvInput(String inputId, SessionCallback callback) {
|
||||
if (TextUtils.isEmpty(inputId)) {
|
||||
throw new IllegalArgumentException("inputId cannot be null or an empty string");
|
||||
}
|
||||
if (callback == null) {
|
||||
throw new IllegalArgumentException("callback cannot be null");
|
||||
}
|
||||
if (mSession != null) {
|
||||
release();
|
||||
}
|
||||
// When bindTvInput is called multiple times before the callback is called,
|
||||
// only the callback of the last bindTvInput call will be actually called back.
|
||||
// The previous callbacks will be ignored. For the logic, mSessionCallback
|
||||
// is newly assigned for every bindTvInput call and compared with
|
||||
// MySessionCreateCallback.this.
|
||||
mSessionCallback = new MySessionCallback(callback);
|
||||
mTvInputManager.createSession(inputId, mSessionCallback, mHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbinds a TV input currently bound. Its corresponding {@link TvInputManager.Session}
|
||||
* is released.
|
||||
*/
|
||||
public void unbindTvInput() {
|
||||
if (mSession != null) {
|
||||
release();
|
||||
}
|
||||
mSessionCallback = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an unhandled input event to the next receiver.
|
||||
* <p>
|
||||
* Except system keys, TvView always consumes input events in the normal flow. This is called
|
||||
* asynchronously from where the event is dispatched. It gives the host application a chance to
|
||||
* dispatch the unhandled input events.
|
||||
*
|
||||
* @param event The input event.
|
||||
* @return {@code true} if the event was handled by the view, {@code false} otherwise.
|
||||
*/
|
||||
public boolean dispatchUnhandledInputEvent(InputEvent event) {
|
||||
if (mOnUnhandledInputEventListener != null) {
|
||||
if (mOnUnhandledInputEventListener.onUnhandledInputEvent(event)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return onUnhandledInputEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an unhandled input event was also not handled by the user provided callback. This
|
||||
* is the last chance to handle the unhandled input event in the TvView.
|
||||
*
|
||||
* @param event The input event.
|
||||
* @return If you handled the event, return {@code true}. If you want to allow the event to be
|
||||
* handled by the next receiver, return {@code false}.
|
||||
*/
|
||||
public boolean onUnhandledInputEvent(InputEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback to be invoked when an input event was not handled by the bound TV input.
|
||||
*
|
||||
* @param listener The callback to invoke when the unhandled input event was received.
|
||||
*/
|
||||
public void setOnUnhandledInputEventListener(OnUnhandledInputEventListener listener) {
|
||||
mOnUnhandledInputEventListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||
if (super.dispatchKeyEvent(event)) {
|
||||
return true;
|
||||
}
|
||||
if (DEBUG) Log.d(TAG, "dispatchKeyEvent(" + event + ")");
|
||||
if (mSession == null) {
|
||||
return false;
|
||||
}
|
||||
InputEvent copiedEvent = event.copy();
|
||||
int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
|
||||
mHandler);
|
||||
return ret != Session.DISPATCH_NOT_HANDLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent event) {
|
||||
if (super.dispatchTouchEvent(event)) {
|
||||
return true;
|
||||
}
|
||||
if (DEBUG) Log.d(TAG, "dispatchTouchEvent(" + event + ")");
|
||||
if (mSession == null) {
|
||||
return false;
|
||||
}
|
||||
InputEvent copiedEvent = event.copy();
|
||||
int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
|
||||
mHandler);
|
||||
return ret != Session.DISPATCH_NOT_HANDLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTrackballEvent(MotionEvent event) {
|
||||
if (super.dispatchTrackballEvent(event)) {
|
||||
return true;
|
||||
}
|
||||
if (DEBUG) Log.d(TAG, "dispatchTrackballEvent(" + event + ")");
|
||||
if (mSession == null) {
|
||||
return false;
|
||||
}
|
||||
InputEvent copiedEvent = event.copy();
|
||||
int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
|
||||
mHandler);
|
||||
return ret != Session.DISPATCH_NOT_HANDLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchGenericMotionEvent(MotionEvent event) {
|
||||
if (super.dispatchGenericMotionEvent(event)) {
|
||||
return true;
|
||||
}
|
||||
if (DEBUG) Log.d(TAG, "dispatchGenericMotionEvent(" + event + ")");
|
||||
if (mSession == null) {
|
||||
return false;
|
||||
}
|
||||
InputEvent copiedEvent = event.copy();
|
||||
int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
|
||||
mHandler);
|
||||
return ret != Session.DISPATCH_NOT_HANDLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
createSessionOverlayView();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
removeSessionOverlayView();
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@Override
|
||||
protected void updateWindow(boolean force, boolean redrawNeeded) {
|
||||
super.updateWindow(force, redrawNeeded);
|
||||
relayoutSessionOverlayView();
|
||||
}
|
||||
|
||||
private void release() {
|
||||
setSessionSurface(null);
|
||||
removeSessionOverlayView();
|
||||
mSession.release();
|
||||
mSession = null;
|
||||
}
|
||||
|
||||
private void setSessionSurface(Surface surface) {
|
||||
if (mSession == null) {
|
||||
return;
|
||||
}
|
||||
mSession.setSurface(surface);
|
||||
}
|
||||
|
||||
private void createSessionOverlayView() {
|
||||
if (mSession == null || !isAttachedToWindow()
|
||||
|| mOverlayViewCreated) {
|
||||
return;
|
||||
}
|
||||
mOverlayViewFrame = getViewFrameOnScreen();
|
||||
mSession.createOverlayView(this, mOverlayViewFrame);
|
||||
mOverlayViewCreated = true;
|
||||
}
|
||||
|
||||
private void removeSessionOverlayView() {
|
||||
if (mSession == null || !mOverlayViewCreated) {
|
||||
return;
|
||||
}
|
||||
mSession.removeOverlayView();
|
||||
mOverlayViewCreated = false;
|
||||
mOverlayViewFrame = null;
|
||||
}
|
||||
|
||||
private void relayoutSessionOverlayView() {
|
||||
if (mSession == null || !isAttachedToWindow()
|
||||
|| !mOverlayViewCreated) {
|
||||
return;
|
||||
}
|
||||
Rect viewFrame = getViewFrameOnScreen();
|
||||
if (viewFrame.equals(mOverlayViewFrame)) {
|
||||
return;
|
||||
}
|
||||
mSession.relayoutOverlayView(viewFrame);
|
||||
mOverlayViewFrame = viewFrame;
|
||||
}
|
||||
|
||||
private Rect getViewFrameOnScreen() {
|
||||
int[] location = new int[2];
|
||||
getLocationOnScreen(location);
|
||||
return new Rect(location[0], location[1],
|
||||
location[0] + getWidth(), location[1] + getHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be invoked when the unhandled input event is received.
|
||||
*/
|
||||
public interface OnUnhandledInputEventListener {
|
||||
/**
|
||||
* Called when an input event was not handled by the bound TV input.
|
||||
* <p>
|
||||
* This is called asynchronously from where the event is dispatched. It gives the host
|
||||
* application a chance to handle the unhandled input events.
|
||||
*
|
||||
* @param event The input event.
|
||||
* @return If you handled the event, return {@code true}. If you want to allow the event to
|
||||
* be handled by the next receiver, return {@code false}.
|
||||
*/
|
||||
boolean onUnhandledInputEvent(InputEvent event);
|
||||
}
|
||||
|
||||
private class MySessionCallback extends SessionCallback {
|
||||
final SessionCallback mExternalCallback;
|
||||
|
||||
MySessionCallback(SessionCallback externalCallback) {
|
||||
mExternalCallback = externalCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionCreated(Session session) {
|
||||
if (this != mSessionCallback) {
|
||||
// This callback is obsolete.
|
||||
if (session != null) {
|
||||
session.release();
|
||||
}
|
||||
return;
|
||||
}
|
||||
mSession = session;
|
||||
if (session != null) {
|
||||
// mSurface may not be ready yet as soon as starting an application.
|
||||
// In the case, we don't send Session.setSurface(null) unnecessarily.
|
||||
// setSessionSurface will be called in surfaceCreated.
|
||||
if (mSurface != null) {
|
||||
setSessionSurface(mSurface);
|
||||
}
|
||||
createSessionOverlayView();
|
||||
}
|
||||
if (mExternalCallback != null) {
|
||||
mExternalCallback.onSessionCreated(session);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionReleased(Session session) {
|
||||
mSession = null;
|
||||
if (mExternalCallback != null) {
|
||||
mExternalCallback.onSessionReleased(session);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoSizeChanged(Session session, int width, int height) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onVideoSizeChanged(" + width + ", " + height + ")");
|
||||
}
|
||||
if (mExternalCallback != null) {
|
||||
mExternalCallback.onVideoSizeChanged(session, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionEvent(TvInputManager.Session session, String eventType,
|
||||
Bundle eventArgs) {
|
||||
if (mExternalCallback != null) {
|
||||
mExternalCallback.onSessionEvent(session, eventType, eventArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user