/* * NetcastTVService * Connect SDK * * Copyright (c) 2014 LG Electronics. * Created by Hyun Kook Khang on 19 Jan 2014 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.connectsdk.service; import android.graphics.PointF; import android.util.Log; import com.connectsdk.core.AppInfo; import com.connectsdk.core.ChannelInfo; import com.connectsdk.core.ExternalInputInfo; import com.connectsdk.core.ImageInfo; import com.connectsdk.core.MediaInfo; import com.connectsdk.core.Util; import com.connectsdk.device.ConnectableDevice; import com.connectsdk.discovery.DiscoveryFilter; import com.connectsdk.discovery.DiscoveryManager; import com.connectsdk.discovery.DiscoveryManager.PairingLevel; import com.connectsdk.etc.helper.DeviceServiceReachability; import com.connectsdk.etc.helper.HttpConnection; import com.connectsdk.etc.helper.HttpMessage; import com.connectsdk.service.capability.CapabilityMethods; import com.connectsdk.service.capability.ExternalInputControl; import com.connectsdk.service.capability.KeyControl; import com.connectsdk.service.capability.Launcher; import com.connectsdk.service.capability.MediaControl; import com.connectsdk.service.capability.MediaPlayer; import com.connectsdk.service.capability.MouseControl; import com.connectsdk.service.capability.PowerControl; import com.connectsdk.service.capability.TVControl; import com.connectsdk.service.capability.TextInputControl; import com.connectsdk.service.capability.VolumeControl; import com.connectsdk.service.capability.listeners.ResponseListener; import com.connectsdk.service.command.NotSupportedServiceSubscription; import com.connectsdk.service.command.ServiceCommand; import com.connectsdk.service.command.ServiceCommandError; import com.connectsdk.service.command.ServiceSubscription; import com.connectsdk.service.command.URLServiceSubscription; import com.connectsdk.service.config.NetcastTVServiceConfig; import com.connectsdk.service.config.ServiceConfig; import com.connectsdk.service.config.ServiceDescription; import com.connectsdk.service.netcast.NetcastAppNumberParser; import com.connectsdk.service.netcast.NetcastApplicationsParser; import com.connectsdk.service.netcast.NetcastChannelParser; import com.connectsdk.service.netcast.NetcastHttpServer; import com.connectsdk.service.netcast.NetcastVirtualKeycodes; import com.connectsdk.service.netcast.NetcastVolumeParser; import com.connectsdk.service.sessions.LaunchSession; import com.connectsdk.service.sessions.LaunchSession.LaunchSessionType; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.xml.sax.SAXException; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; public class NetcastTVService extends DeviceService implements Launcher, MediaControl, MediaPlayer, TVControl, VolumeControl, ExternalInputControl, MouseControl, TextInputControl, PowerControl, KeyControl { public static final String ID = "Netcast TV"; public static final String UDAP_PATH_PAIRING = "/udap/api/pairing"; public static final String UDAP_PATH_DATA = "/udap/api/data"; public static final String UDAP_PATH_COMMAND = "/udap/api/command"; public static final String UDAP_PATH_EVENT = "/udap/api/event"; public static final String UDAP_PATH_APPTOAPP_DATA = "/udap/api/apptoapp/data/"; public static final String UDAP_PATH_APPTOAPP_COMMAND = "/udap/api/apptoapp/command/"; public static final String ROAP_PATH_APP_STORE = "/roap/api/command/"; public static final String UDAP_API_PAIRING = "pairing"; public static final String UDAP_API_COMMAND = "command"; public static final String UDAP_API_EVENT = "event"; public final static String TARGET_CHANNEL_LIST = "channel_list"; public final static String TARGET_CURRENT_CHANNEL = "cur_channel"; public final static String TARGET_VOLUME_INFO = "volume_info"; public final static String TARGET_APPLIST_GET = "applist_get"; public final static String TARGET_APPNUM_GET = "appnum_get"; public final static String TARGET_3D_MODE = "3DMode"; public final static String TARGET_IS_3D = "is_3D"; public static final String SMART_SHARE = "SmartShare™"; enum State { NONE, INITIAL, CONNECTING, PAIRING, PAIRED, DISCONNECTING } NetcastHttpServer httpServer; DLNAService dlnaService; DIALService dialService; LaunchSession inputPickerSession; List<AppInfo> applications; List<URLServiceSubscription<?>> subscriptions; StringBuilder keyboardString; State state = State.INITIAL; PointF mMouseDistance; Boolean mMouseIsMoving; public NetcastTVService(ServiceDescription serviceDescription, ServiceConfig serviceConfig) { super(serviceDescription, serviceConfig); pairingType = PairingType.PIN_CODE; if (serviceDescription.getPort() != 8080) serviceDescription.setPort(8080); applications = new ArrayList<AppInfo>(); subscriptions = new ArrayList<URLServiceSubscription<?>>(); keyboardString = new StringBuilder(); state = State.INITIAL; inputPickerSession = null; } public static DiscoveryFilter discoveryFilter() { return new DiscoveryFilter(ID, "urn:schemas-upnp-org:device:MediaRenderer:1"); } @Override public CapabilityPriorityLevel getPriorityLevel(Class<? extends CapabilityMethods> clazz) { if (clazz.equals(MediaPlayer.class)) { return getMediaPlayerCapabilityLevel(); } else if (clazz.equals(MediaControl.class)) { return getMediaControlCapabilityLevel(); } else if (clazz.equals(Launcher.class)) { return getLauncherCapabilityLevel(); } else if (clazz.equals(TVControl.class)) { return getTVControlCapabilityLevel(); } else if (clazz.equals(VolumeControl.class)) { return getVolumeControlCapabilityLevel(); } else if (clazz.equals(ExternalInputControl.class)) { return getExternalInputControlPriorityLevel(); } else if (clazz.equals(MouseControl.class)) { return getMouseControlCapabilityLevel(); } else if (clazz.equals(TextInputControl.class)) { return getTextInputControlCapabilityLevel(); } else if (clazz.equals(PowerControl.class)) { return getPowerControlCapabilityLevel(); } else if (clazz.equals(KeyControl.class)) { return getKeyControlCapabilityLevel(); } return CapabilityPriorityLevel.NOT_SUPPORTED; } @Override public void setServiceDescription(ServiceDescription serviceDescription) { super.setServiceDescription(serviceDescription); if (serviceDescription.getPort() != 8080) serviceDescription.setPort(8080); } @Override public void connect() { if (state != State.INITIAL) { Log.w(Util.T, "already connecting; not trying to connect again: " + state); return; // don't try to connect again while connected } if (!(serviceConfig instanceof NetcastTVServiceConfig)) { ServiceConfig.ServiceConfigListener serviceConfigListener = serviceConfig.getListener(); serviceConfig = new NetcastTVServiceConfig(serviceConfig.getServiceUUID()); serviceConfig.setListener(serviceConfigListener); } if (DiscoveryManager.getInstance().getPairingLevel() == PairingLevel.ON) { if (((NetcastTVServiceConfig) serviceConfig).getPairingKey() != null && ((NetcastTVServiceConfig)serviceConfig).getPairingKey().length() != 0) { sendPairingKey(((NetcastTVServiceConfig) serviceConfig).getPairingKey()); } else { showPairingKeyOnTV(); } Util.runInBackground(new Runnable() { @Override public void run() { httpServer = new NetcastHttpServer(NetcastTVService.this, getServiceDescription().getPort(), mTextChangedListener); httpServer.setSubscriptions(subscriptions); httpServer.start(); } }); } else { hConnectSuccess(); } } @Override public void disconnect() { endPairing(null); connected = false; if (mServiceReachability != null) mServiceReachability.stop(); Util.runOnUI(new Runnable() { @Override public void run() { if (listener != null) listener.onDisconnect(NetcastTVService.this, null); } }); if (httpServer != null) { httpServer.stop(); httpServer = null; } state = State.INITIAL; } @Override public boolean isConnectable() { return true; } @Override public boolean isConnected() { return connected; } private void hConnectSuccess() { // TODO: Fix this for Netcast. Right now it is using the InetAddress reachable function. Need to use an HTTP Method. // mServiceReachability = DeviceServiceReachability.getReachability(serviceDescription.getIpAddress(), this); // mServiceReachability.start(); connected = true; // Pairing was successful, so report connected and ready reportConnected(true); } @Override public void onLoseReachability(DeviceServiceReachability reachability) { if (connected) { disconnect(); } else { if (mServiceReachability != null) mServiceReachability.stop(); } } public void hostByeBye () { disconnect(); } //============= Auth ============================== public void showPairingKeyOnTV() { state = State.CONNECTING; ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { if (listener != null) listener.onPairingRequired(NetcastTVService.this, pairingType, null); } @Override public void onError(ServiceCommandError error) { state = State.INITIAL; if (listener != null) listener.onConnectionFailure(NetcastTVService.this, error); } }; String requestURL = getUDAPRequestURL(UDAP_PATH_PAIRING); Map <String,String> params = new HashMap<String,String>(); params.put("name", "showKey"); String httpMessage = getUDAPMessageBody(UDAP_API_PAIRING, params); ServiceCommand<ResponseListener<Object>> command = new ServiceCommand<ResponseListener<Object>>(this, requestURL, httpMessage, responseListener); command.send(); } @Override public void cancelPairing() { removePairingKeyOnTV(); state = State.INITIAL; } public void removePairingKeyOnTV() { ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { } @Override public void onError(ServiceCommandError error) { } }; String requestURL = getUDAPRequestURL(UDAP_PATH_PAIRING); Map <String,String> params = new HashMap<String,String>(); params.put("name", "CancelAuthKeyReq"); String httpMessage = getUDAPMessageBody(UDAP_API_PAIRING, params); ServiceCommand<ResponseListener<Object>> command = new ServiceCommand<ResponseListener<Object>>(this, requestURL, httpMessage, responseListener); command.send(); } @Override public void sendPairingKey(final String pairingKey) { state = State.PAIRING; if (!(serviceConfig instanceof NetcastTVServiceConfig)) { serviceConfig = new NetcastTVServiceConfig(serviceConfig.getServiceUUID()); } ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { state = State.PAIRED; ((NetcastTVServiceConfig)serviceConfig).setPairingKey(pairingKey); hConnectSuccess(); } @Override public void onError(ServiceCommandError error) { state = State.INITIAL; if (listener != null) listener.onConnectionFailure(NetcastTVService.this, error); } }; String requestURL = getUDAPRequestURL(UDAP_PATH_PAIRING); Map <String,String> params = new HashMap<String,String>(); params.put("name", "hello"); params.put("value", pairingKey); params.put("port", String.valueOf(serviceDescription.getPort())); String httpMessage = getUDAPMessageBody(UDAP_API_PAIRING, params); ServiceCommand<ResponseListener<Object>> command = new ServiceCommand<ResponseListener<Object>>(this, requestURL, httpMessage, responseListener); command.send(); } private void endPairing(ResponseListener<Object> listener) { String requestURL = getUDAPRequestURL(UDAP_PATH_PAIRING); Map <String,String> params = new HashMap<String,String>(); params.put("name", "byebye"); params.put("port", String.valueOf(serviceDescription.getPort())); String httpMessage = getUDAPMessageBody(UDAP_API_PAIRING, params); ServiceCommand<ResponseListener<Object>> command = new ServiceCommand<ResponseListener<Object>>(this, requestURL, httpMessage, listener); command.send(); } /****************** LAUNCHER *****************/ public Launcher getLauncher() { return this; } @Override public CapabilityPriorityLevel getLauncherCapabilityLevel() { return CapabilityPriorityLevel.HIGH; } class NetcastTVLaunchSessionR extends LaunchSession { String appName; NetcastTVService service; NetcastTVLaunchSessionR (NetcastTVService service, String auid, String appName) { this.service = service; appId = auid; } NetcastTVLaunchSessionR (NetcastTVService service, JSONObject obj) throws JSONException { this.service = service; fromJSONObject(obj); } public void close(ResponseListener<Object> responseListener) { } @Override public JSONObject toJSONObject() throws JSONException { JSONObject obj = super.toJSONObject(); obj.put("type", "netcasttv"); obj.put("appName", appName); return obj; } @Override public void fromJSONObject(JSONObject obj) throws JSONException { super.fromJSONObject(obj); appName = obj.optString("appName"); } } public void getApplication(final String appName, final AppInfoListener listener) { ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { final String strObj = (String) response; AppInfo appId = new AppInfo() {{ setId(decToHex(strObj)); }}; Util.postSuccess(listener, appId); } @Override public void onError(ServiceCommandError error) { if (listener != null) Util.postError(listener, error); } }; String uri = UDAP_PATH_APPTOAPP_DATA + appName; String requestURL = getUDAPRequestURL(uri); ServiceCommand<ResponseListener<Object>> command = new ServiceCommand<ResponseListener<Object>>(this, requestURL, null, responseListener); command.setHttpMethod(ServiceCommand.TYPE_GET); command.send(); } @Override public void launchApp(final String appId, final AppLaunchListener listener) { getAppInfoForId(appId, new AppInfoListener() { @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } @Override public void onSuccess(AppInfo appInfo) { launchAppWithInfo(appInfo, listener); } }); } private void getAppInfoForId(final String appId, final AppInfoListener listener) { getAppList(new AppListListener() { @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } @Override public void onSuccess(List<AppInfo> object) { for (AppInfo info : object) { if (info.getName().equalsIgnoreCase(appId)) { Util.postSuccess(listener, info); return; } } Util.postError(listener, new ServiceCommandError(0, "Unable to find the App with id", null)); } }); } private void launchApplication(final String appName, final String auid, final String contentId, final Launcher.AppLaunchListener listener) { JSONObject jsonObj = new JSONObject(); try { jsonObj.put("id", auid); jsonObj.put("title", appName); } catch (JSONException e) { e.printStackTrace(); } ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { LaunchSession launchSession = LaunchSession.launchSessionForAppId(auid); launchSession.setAppName(appName); launchSession.setService(NetcastTVService.this); launchSession.setSessionType(LaunchSessionType.App); Util.postSuccess(listener, launchSession); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; String requestURL = getUDAPRequestURL(UDAP_PATH_APPTOAPP_COMMAND); Map <String,String> params = new HashMap<String,String>(); params.put("name", "AppExecute"); params.put("auid", auid); if (appName != null) { params.put("appname", appName); } if (contentId != null) { params.put("contentid", contentId); } String httpMessage = getUDAPMessageBody(UDAP_API_COMMAND, params); ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, requestURL, httpMessage, responseListener); request.send(); } @Override public void launchAppWithInfo(AppInfo appInfo, Launcher.AppLaunchListener listener) { launchAppWithInfo(appInfo, null, listener); } @Override public void launchAppWithInfo(AppInfo appInfo, Object params, Launcher.AppLaunchListener listener) { String appName = HttpMessage.encode(appInfo.getName()); String appId = appInfo.getId(); String contentId = null; JSONObject mParams = null; if (params instanceof JSONObject) mParams = (JSONObject) params; if (mParams != null) { try { contentId = (String) mParams.get("contentId"); } catch (JSONException e) { e.printStackTrace(); } } launchApplication(appName, appId, contentId, listener); } @Override public void launchBrowser(String url, final Launcher.AppLaunchListener listener) { if (!(url == null || url.length() == 0)) Log.w(Util.T, "Netcast TV does not support deeplink for Browser"); final String appName = "Internet"; getApplication(appName, new AppInfoListener() { @Override public void onSuccess(AppInfo appInfo) { String contentId = null; launchApplication(appName, appInfo.getId(), contentId, listener); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }); } @Override public void launchYouTube(String contentId, Launcher.AppLaunchListener listener) { launchYouTube(contentId, (float)0.0, listener); } @Override public void launchYouTube(final String contentId, float startTime, final AppLaunchListener listener) { if (getDIALService() != null) { getDIALService().getLauncher().launchYouTube(contentId, startTime, listener); return; } if (startTime <= 0.0) { getApplication("YouTube", new AppInfoListener() { @Override public void onSuccess(AppInfo appInfo) { launchApplication(appInfo.getName(), appInfo.getId(), contentId, listener); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }); } else { Util.postError(listener, new ServiceCommandError(0, "Cannot reach DIAL service for launching with provided start time", null)); } } @Override public void launchHulu(final String contentId, final Launcher.AppLaunchListener listener) { final String appName = "Hulu"; getApplication(appName, new AppInfoListener() { @Override public void onSuccess(AppInfo appInfo) { launchApplication(appName, appInfo.getId(), contentId, listener); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }); } @Override public void launchNetflix(final String contentId, final Launcher.AppLaunchListener listener) { if (!serviceDescription.getModelNumber().equals("4.0")) { launchApp("Netflix", listener); return; } final String appName = "Netflix"; getApplication(appName, new AppInfoListener() { @Override public void onSuccess(final AppInfo appInfo) { JSONObject jsonObj = new JSONObject(); try { jsonObj.put("id", appInfo.getId()); jsonObj.put("name", appName); } catch (JSONException e) { e.printStackTrace(); } ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { LaunchSession launchSession = LaunchSession.launchSessionForAppId(appInfo.getId()); launchSession.setAppName(appName); launchSession.setService(NetcastTVService.this); launchSession.setSessionType(LaunchSessionType.App); Util.postSuccess(listener, launchSession); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; String requestURL = getUDAPRequestURL(UDAP_PATH_APPTOAPP_COMMAND); Map <String,String> params = new HashMap<String,String>(); params.put("name", "SearchCMDPlaySDPContent"); params.put("content_type", "1"); params.put("conts_exec_type", "20"); params.put("conts_plex_type_flag", "N"); params.put("conts_search_id", "2023237"); params.put("conts_age", "18"); params.put("exec_id", "netflix"); params.put("item_id", "-Q m=http%3A%2F%2Fapi.netflix.com%2Fcatalog%2Ftitles%2Fmovies%2F" + contentId + "&source_type=4&trackId=6054700&trackUrl=https%3A%2F%2Fapi.netflix.com%2FAPI_APP_ID_6261%3F%23Search%3F"); params.put("app_type", ""); String httpMessage = getUDAPMessageBody(UDAP_API_COMMAND, params); ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(NetcastTVService.this, requestURL, httpMessage, responseListener); request.send(); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }); } @Override public void launchAppStore(final String appId, final AppLaunchListener listener) { if (!serviceDescription.getModelNumber().equals("4.0")) { launchApp("LG Smart World", listener); // TODO: this will not work in Korea, use Korean name instead return; } String targetPath = getUDAPRequestURL(ROAP_PATH_APP_STORE); Map<String, String> params = new HashMap<String, String>(); params.put("name", "SearchCMDPlaySDPContent"); params.put("content_type", "4"); params.put("conts_exec_type", ""); params.put("conts_plex_type_flag", ""); params.put("conts_search_id", ""); params.put("conts_age", "12"); params.put("exec_id", ""); params.put("item_id", HttpMessage.encode(appId)); params.put("app_type", "S"); String httpMessage = getUDAPMessageBody(UDAP_API_COMMAND, params); ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { LaunchSession launchSession = LaunchSession.launchSessionForAppId(appId); launchSession.setAppName("LG Smart World"); // TODO: this will not work in Korea, use Korean name instead launchSession.setService(NetcastTVService.this); launchSession.setSessionType(LaunchSessionType.App); Util.postSuccess(listener, launchSession); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; ServiceCommand<ResponseListener<Object>> command = new ServiceCommand<ResponseListener<Object>>(this, targetPath, httpMessage, responseListener); command.send(); } @Override public void closeApp(LaunchSession launchSession, ResponseListener<Object> listener) { String requestURL = getUDAPRequestURL(UDAP_PATH_APPTOAPP_COMMAND); Map <String,String> params = new HashMap<String,String>(); params.put("name", "AppTerminate"); params.put("auid", launchSession.getAppId()); if (launchSession.getAppName() != null) params.put("appname", HttpMessage.encode(launchSession.getAppName())); String httpMessage = getUDAPMessageBody(UDAP_API_COMMAND, params); ServiceCommand<ResponseListener<Object>> command = new ServiceCommand<ResponseListener<Object>>(launchSession.getService(), requestURL, httpMessage, listener); command.send(); } private void getTotalNumberOfApplications(int type, final AppCountListener listener) { ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { String strObj = (String) response; int applicationNumber = parseAppNumberXmlToJSON(strObj); Util.postSuccess(listener, applicationNumber); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; String requestURL = getUDAPRequestURL(UDAP_PATH_DATA, TARGET_APPNUM_GET, String.valueOf(type)); ServiceCommand<ResponseListener<Object>> command = new ServiceCommand<ResponseListener<Object>>(this, requestURL, null, responseListener); command.setHttpMethod(ServiceCommand.TYPE_GET); command.send(); } private void getApplications(int type, int number, final AppListListener listener) { ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { String strObj = (String) response; JSONArray applicationArray = parseApplicationsXmlToJSON(strObj); List<AppInfo> appList = new ArrayList<AppInfo>(); for (int i = 0; i < applicationArray.length(); i++) { try { final JSONObject appJSON = applicationArray.getJSONObject(i); AppInfo appInfo = new AppInfo() {{ setId(appJSON.getString("id")); setName(appJSON.getString("title")); }}; appList.add(appInfo); } catch (JSONException e) { e.printStackTrace(); } } Util.postSuccess(listener, appList); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; String requestURL = getUDAPRequestURL(UDAP_PATH_DATA, TARGET_APPLIST_GET, String.valueOf(type), "0", String.valueOf(number)); ServiceCommand<ResponseListener<Object>> command = new ServiceCommand<ResponseListener<Object>>(this, requestURL, null, responseListener); command.setHttpMethod(ServiceCommand.TYPE_GET); command.send(); } @Override public void getAppList(final AppListListener listener) { applications.clear(); getTotalNumberOfApplications(2, new AppCountListener() { @Override public void onSuccess(final Integer count) { getApplications(2, count, new AppListListener() { @Override public void onSuccess(List<AppInfo> apps) { applications.addAll(apps); getTotalNumberOfApplications(3, new AppCountListener() { @Override public void onSuccess(final Integer count) { getApplications(3, count, new AppListListener() { @Override public void onSuccess(List<AppInfo> apps) { applications.addAll(apps); Util.postSuccess(listener, applications); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }); } @Override public void getRunningApp(AppInfoListener listener) { // Do nothing - Not Supported Util.postError(listener, ServiceCommandError.notSupported()); } @Override public ServiceSubscription<AppInfoListener> subscribeRunningApp(AppInfoListener listener) { // Do nothing - Not Supported Util.postError(listener, ServiceCommandError.notSupported()); return new NotSupportedServiceSubscription<AppInfoListener>(); } @Override public void getAppState(final LaunchSession launchSession, final AppStateListener listener) { String requestURL = String.format(Locale.US, "%s%s", getUDAPRequestURL(UDAP_PATH_APPTOAPP_DATA), String.format(Locale.US, "/%s/status", launchSession.getAppId())); ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } @Override public void onSuccess(Object object) { String response = (String) object; AppState appState; if (response.equalsIgnoreCase("NONE")) appState = new AppState(false, false); else if (response.equalsIgnoreCase("LOAD")) appState = new AppState(false, true); else if (response.equalsIgnoreCase("RUN_NF")) appState = new AppState(true, false); else if (response.equalsIgnoreCase("TERM")) appState = new AppState(false, true); else appState = new AppState(false, false); Util.postSuccess(listener, appState); } }; ServiceCommand<ResponseListener<Object>> command = new ServiceCommand<ResponseListener<Object>>(this, requestURL, null, responseListener); command.setHttpMethod(ServiceCommand.TYPE_GET); command.send(); } @Override public ServiceSubscription<AppStateListener> subscribeAppState(LaunchSession launchSession, AppStateListener listener) { Util.postError(listener, ServiceCommandError.notSupported()); return null; } /****************** TV CONTROL *****************/ @Override public TVControl getTVControl() { return this; } @Override public CapabilityPriorityLevel getTVControlCapabilityLevel() { return CapabilityPriorityLevel.HIGH; } @Override public void getChannelList(final ChannelListListener listener) { String requestURL = getUDAPRequestURL(UDAP_PATH_DATA, TARGET_CHANNEL_LIST); ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { String strObj = (String)response; try { SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); InputStream stream = new ByteArrayInputStream(strObj.getBytes("UTF-8")); SAXParser saxParser = saxParserFactory.newSAXParser(); NetcastChannelParser parser = new NetcastChannelParser(); saxParser.parse(stream, parser); JSONArray channelArray = parser.getJSONChannelArray(); ArrayList<ChannelInfo> channelList = new ArrayList<ChannelInfo>(); for (int i = 0; i < channelArray.length(); i++) { JSONObject rawData; try { rawData = (JSONObject) channelArray.get(i); ChannelInfo channel = NetcastChannelParser.parseRawChannelData(rawData); channelList.add(channel); } catch (JSONException e) { e.printStackTrace(); } } Util.postSuccess(listener, channelList); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, requestURL, null, responseListener); request.setHttpMethod(ServiceCommand.TYPE_GET); request.send(); } @Override public void channelUp(ResponseListener<Object> listener) { sendVirtualKeyCode(NetcastVirtualKeycodes.CHANNEL_UP.getCode(), listener); } @Override public void channelDown(ResponseListener<Object> listener) { sendVirtualKeyCode(NetcastVirtualKeycodes.CHANNEL_DOWN.getCode(), listener); } @Override public void setChannel(final ChannelInfo channelInfo, final ResponseListener<Object> listener) { getChannelList(new ChannelListListener() { @Override public void onSuccess(List<ChannelInfo> channelList) { String requestURL = getUDAPRequestURL(UDAP_PATH_COMMAND); Map<String, String> params = new HashMap<String, String>(); for (int i = 0; i < channelList.size(); i++) { ChannelInfo ch = channelList.get(i); JSONObject rawData = ch.getRawData(); try { String major = channelInfo.getNumber().split("-")[0]; String minor = channelInfo.getNumber().split("-")[1]; int majorNumber = ch.getMajorNumber(); int minorNumber = ch.getMinorNumber(); String sourceIndex = (String) rawData.get("sourceIndex"); int physicalNum = (Integer) rawData.get("physicalNumber"); if (Integer.valueOf(major) == majorNumber && Integer.valueOf(minor) == minorNumber) { params.put("name", "HandleChannelChange"); params.put("major", major); params.put("minor", minor); params.put("sourceIndex", sourceIndex); params.put("physicalNum", String.valueOf(physicalNum)); break; } } catch (JSONException e) { e.printStackTrace(); } } String httpMessage = getUDAPMessageBody(UDAP_API_COMMAND, params); ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(NetcastTVService.this, requestURL, httpMessage, listener); request.send(); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }); } @Override public void getCurrentChannel(final ChannelListener listener) { String requestURL = getUDAPRequestURL(UDAP_PATH_DATA, TARGET_CURRENT_CHANNEL); ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { String strObj = (String)response; try { SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); InputStream stream = new ByteArrayInputStream(strObj.getBytes("UTF-8")); SAXParser saxParser = saxParserFactory.newSAXParser(); NetcastChannelParser parser = new NetcastChannelParser(); saxParser.parse(stream, parser); JSONArray channelArray = parser.getJSONChannelArray(); if (channelArray.length() > 0) { JSONObject rawData = (JSONObject) channelArray.get(0); ChannelInfo channel = NetcastChannelParser.parseRawChannelData(rawData); Util.postSuccess(listener, channel); } } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (JSONException e) { e.printStackTrace(); } } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, requestURL, null, responseListener); request.send(); } @Override public ServiceSubscription<ChannelListener> subscribeCurrentChannel(final ChannelListener listener) { getCurrentChannel(listener); // This is for the initial Current TV Channel Info. URLServiceSubscription<ChannelListener> request = new URLServiceSubscription<ChannelListener>(this, "ChannelChanged", null, null); request.setHttpMethod(ServiceCommand.TYPE_GET); request.addListener(listener); addSubscription(request); return request; } @Override public void getProgramInfo(ProgramInfoListener listener) { // Do nothing - Not Supported Util.postError(listener, ServiceCommandError.notSupported()); } @Override public ServiceSubscription<ProgramInfoListener> subscribeProgramInfo(ProgramInfoListener listener) { // Do nothing - Not Supported Util.postError(listener, ServiceCommandError.notSupported()); return null; } @Override public void getProgramList(ProgramListListener listener) { // Do nothing - Not Supported Util.postError(listener, ServiceCommandError.notSupported()); } @Override public ServiceSubscription<ProgramListListener> subscribeProgramList(ProgramListListener listener) { // Do nothing - Not Supported Util.postError(listener, ServiceCommandError.notSupported()); return null; } @Override public void set3DEnabled(final boolean enabled, final ResponseListener<Object> listener) { get3DEnabled(new State3DModeListener() { @Override public void onSuccess(Boolean isEnabled) { if (enabled != isEnabled) { sendVirtualKeyCode(NetcastVirtualKeycodes.VIDEO_3D.getCode(), listener); } } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }); } @Override public void get3DEnabled(final State3DModeListener listener) { ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { String strObj = (String) response; String upperStr = strObj.toUpperCase(Locale.ENGLISH); Util.postSuccess(listener, upperStr.contains("TRUE")); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; String requestURL = getUDAPRequestURL(UDAP_PATH_DATA, TARGET_IS_3D); ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, requestURL, null, responseListener); request.setHttpMethod(ServiceCommand.TYPE_GET); request.send(); } @Override public ServiceSubscription<State3DModeListener> subscribe3DEnabled(final State3DModeListener listener) { get3DEnabled(listener); URLServiceSubscription<State3DModeListener> request = new URLServiceSubscription<State3DModeListener>(this, TARGET_3D_MODE, null, null); request.setHttpMethod(ServiceCommand.TYPE_GET); request.addListener(listener); addSubscription(request); return request; } /************** VOLUME **************/ @Override public VolumeControl getVolumeControl() { return this; } @Override public CapabilityPriorityLevel getVolumeControlCapabilityLevel() { return CapabilityPriorityLevel.HIGH; } @Override public void volumeUp(ResponseListener<Object> listener) { sendVirtualKeyCode(NetcastVirtualKeycodes.VOLUME_UP.getCode(), listener); } @Override public void volumeDown(ResponseListener<Object> listener) { sendVirtualKeyCode(NetcastVirtualKeycodes.VOLUME_DOWN.getCode(), listener); } @Override public void setVolume(float volume, ResponseListener<Object> listener) { if (getDLNAService() != null) { getDLNAService().setVolume(volume, listener); } else Util.postError(listener, ServiceCommandError.notSupported()); } @Override public void getVolume(final VolumeListener listener) { getVolumeStatus(new VolumeStatusListener() { @Override public void onSuccess(VolumeStatus status) { Util.postSuccess(listener, status.volume); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }); } @Override public void setMute(final boolean isMute, final ResponseListener<Object> listener) { getVolumeStatus(new VolumeStatusListener() { @Override public void onSuccess(VolumeStatus status) { if (isMute != status.isMute) { sendVirtualKeyCode(NetcastVirtualKeycodes.MUTE.getCode(), listener); } } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }); } @Override public void getMute(final MuteListener listener) { getVolumeStatus(new VolumeStatusListener() { @Override public void onSuccess(VolumeStatus status) { Util.postSuccess(listener, status.isMute); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }); } @Override public ServiceSubscription<VolumeListener> subscribeVolume(VolumeListener listener) { if (getDLNAService() != null) { return getDLNAService().subscribeVolume(listener); } else Util.postError(listener, ServiceCommandError.notSupported()); return null; } @Override public ServiceSubscription<MuteListener> subscribeMute(MuteListener listener) { if (getDLNAService() != null) { return getDLNAService().subscribeMute(listener); } else Util.postError(listener, ServiceCommandError.notSupported()); return null; } private void getVolumeStatus(final VolumeStatusListener listener) { ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { String strObj = (String) response; JSONObject volumeStatus = parseVolumeXmlToJSON(strObj); try { boolean isMute = (Boolean) volumeStatus.get("mute"); int volume = (Integer) volumeStatus.get("level"); Util.postSuccess(listener, new VolumeStatus(isMute, volume)); } catch (JSONException e) { e.printStackTrace(); } } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; String requestURL = getUDAPRequestURL(UDAP_PATH_DATA, TARGET_VOLUME_INFO); ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, requestURL, null, responseListener); request.setHttpMethod(ServiceCommand.TYPE_GET); request.send(); } /************** EXTERNAL INPUT **************/ @Override public ExternalInputControl getExternalInput() { return this; } @Override public CapabilityPriorityLevel getExternalInputControlPriorityLevel() { return CapabilityPriorityLevel.HIGH; } @Override public void launchInputPicker(final AppLaunchListener listener) { final String appName = "Input List"; final String encodedStr = HttpMessage.encode(appName); getApplication(encodedStr, new AppInfoListener() { @Override public void onSuccess(final AppInfo appInfo) { Launcher.AppLaunchListener launchListener = new Launcher.AppLaunchListener() { @Override public void onSuccess(LaunchSession session) { if (inputPickerSession == null) { inputPickerSession = session; } Util.postSuccess(listener, session); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; launchApplication(appName, appInfo.getId(), null, launchListener); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }); } @Override public void closeInputPicker(LaunchSession launchSession, ResponseListener<Object> listener) { sendVirtualKeyCode(NetcastVirtualKeycodes.EXIT.getCode(), listener); } @Override public void getExternalInputList(ExternalInputListListener listener) { // Do nothing - not Supported Util.postError(listener, ServiceCommandError.notSupported()); } @Override public void setExternalInput(ExternalInputInfo input, ResponseListener<Object> listener) { // Do nothing - not Supported Util.postError(listener, ServiceCommandError.notSupported()); } /****************** MEDIA PLAYER *****************/ @Override public MediaPlayer getMediaPlayer() { return this; } @Override public CapabilityPriorityLevel getMediaPlayerCapabilityLevel() { return CapabilityPriorityLevel.HIGH; } @Override public void getMediaInfo(final MediaInfoListener listener) { if (getDLNAService() != null) { getDLNAService().getMediaInfo(listener); } else { if (listener != null) Util.postError(listener, ServiceCommandError.notSupported()); } } @Override public ServiceSubscription<MediaInfoListener> subscribeMediaInfo( MediaInfoListener listener) { if (getDLNAService() != null) { return getDLNAService().subscribeMediaInfo(listener); } else { if (listener != null) Util.postError(listener, ServiceCommandError.notSupported()); return null; } } @Override public void displayImage(final String url, final String mimeType, final String title, final String description, final String iconSrc, final MediaPlayer.LaunchListener listener) { if (getDLNAService() != null) { final MediaPlayer.LaunchListener launchListener = new LaunchListener() { @Override public void onError(ServiceCommandError error) { if (listener != null) Util.postError(listener, error); } @Override public void onSuccess(MediaLaunchObject object) { object.launchSession.setAppId(SMART_SHARE); object.launchSession.setAppName(SMART_SHARE); object.mediaControl = NetcastTVService.this.getMediaControl(); if (listener != null) Util.postSuccess(listener, object); } }; getDLNAService().displayImage(url, mimeType, title, description, iconSrc, launchListener); } else { System.err.println("DLNA Service is not ready yet"); } } @Override public void displayImage(MediaInfo mediaInfo, LaunchListener listener) { String mediaUrl = null; String mimeType = null; String title = null; String desc = null; String iconSrc = null; if (mediaInfo != null) { mediaUrl = mediaInfo.getUrl(); mimeType = mediaInfo.getMimeType(); title = mediaInfo.getTitle(); desc = mediaInfo.getDescription(); if (mediaInfo.getImages() != null && mediaInfo.getImages().size() > 0) { ImageInfo imageInfo = mediaInfo.getImages().get(0); iconSrc = imageInfo.getUrl(); } } displayImage(mediaUrl, mimeType, title, desc, iconSrc, listener); } @Override public void playMedia(String url, String mimeType, String title, String description, String iconSrc, boolean shouldLoop, MediaPlayer.LaunchListener listener) { MediaInfo mediaInfo = new MediaInfo.Builder(url, mimeType) .setTitle(title) .setDescription(description) .setIcon(iconSrc) .build(); playMedia(mediaInfo, shouldLoop, listener); } @Override public void playMedia(MediaInfo mediaInfo, boolean shouldLoop, final MediaPlayer.LaunchListener listener) { if (getDLNAService() != null) { final MediaPlayer.LaunchListener launchListener = new LaunchListener() { @Override public void onError(ServiceCommandError error) { if (listener != null) Util.postError(listener, error); } @Override public void onSuccess(MediaLaunchObject object) { object.launchSession.setAppId(SMART_SHARE); object.launchSession.setAppName(SMART_SHARE); object.mediaControl = NetcastTVService.this.getMediaControl(); if (listener != null) Util.postSuccess(listener, object); } }; getDLNAService().playMedia(mediaInfo, shouldLoop, launchListener); } else { System.err.println("DLNA Service is not ready yet"); } } @Override public void closeMedia(LaunchSession launchSession, ResponseListener<Object> listener) { if (getDLNAService() == null) { Util.postError(listener, new ServiceCommandError(0, "Service is not connected", null)); return; } getDLNAService().closeMedia(launchSession, listener); } /****************** MEDIA CONTROL *****************/ @Override public MediaControl getMediaControl() { if (DiscoveryManager.getInstance().getPairingLevel() == PairingLevel.OFF) return this.getDLNAService(); else return this; } @Override public CapabilityPriorityLevel getMediaControlCapabilityLevel() { return CapabilityPriorityLevel.HIGH; } @Override public void play(ResponseListener<Object> listener) { sendVirtualKeyCode(NetcastVirtualKeycodes.PLAY.getCode(), listener); } @Override public void pause(ResponseListener<Object> listener) { sendVirtualKeyCode(NetcastVirtualKeycodes.PAUSE.getCode(), listener); } @Override public void stop(final ResponseListener<Object> listener) { sendVirtualKeyCode(NetcastVirtualKeycodes.STOP.getCode(), listener); } @Override public void rewind(ResponseListener<Object> listener) { Util.postError(listener, ServiceCommandError.notSupported()); } @Override public void fastForward(ResponseListener<Object> listener) { Util.postError(listener, ServiceCommandError.notSupported()); } @Override public void previous(ResponseListener<Object> listener) { sendVirtualKeyCode(NetcastVirtualKeycodes.SKIP_BACKWARD.getCode(), listener); } @Override public void next(ResponseListener<Object> listener) { sendVirtualKeyCode(NetcastVirtualKeycodes.SKIP_FORWARD.getCode(), listener); } @Override public void seek(long position, ResponseListener<Object> listener) { if (getDLNAService() != null) { getDLNAService().seek(position, listener); } else { if (listener != null) Util.postError(listener, ServiceCommandError.notSupported()); } } @Override public void getDuration(DurationListener listener) { if (getDLNAService() != null) { getDLNAService().getDuration(listener); } else { if (listener != null) Util.postError(listener, ServiceCommandError.notSupported()); } } @Override public void getPosition(PositionListener listener) { if (getDLNAService() != null) { getDLNAService().getPosition(listener); } else { if (listener != null) Util.postError(listener, ServiceCommandError.notSupported()); } } @Override public void getPlayState(PlayStateListener listener) { if (getDLNAService() != null) { getDLNAService().getPlayState(listener); } else { if (listener != null) Util.postError(listener, ServiceCommandError.notSupported()); } } @Override public ServiceSubscription<PlayStateListener> subscribePlayState(PlayStateListener listener) { if (getDLNAService() != null) { return getDLNAService().subscribePlayState(listener); } else Util.postError(listener, ServiceCommandError.notSupported()); return null; } /************** MOUSE CONTROL **************/ @Override public MouseControl getMouseControl() { return this; } @Override public CapabilityPriorityLevel getMouseControlCapabilityLevel() { return CapabilityPriorityLevel.HIGH; } private void setMouseCursorVisible(boolean visible, ResponseListener<Object> listener) { String requestURL = getUDAPRequestURL(UDAP_PATH_EVENT); Map <String,String> params = new HashMap<String,String>(); params.put("name", "CursorVisible"); params.put("value", visible? "true" : "false"); params.put("mode", "auto"); String httpMessage = getUDAPMessageBody(UDAP_API_EVENT, params); ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, requestURL, httpMessage, listener); request.send(); } @Override public void connectMouse() { ResponseListener<Object> listener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { Log.d(Util.T, "Netcast TV's mouse has been connected"); mMouseDistance = new PointF(0, 0); mMouseIsMoving = false; } @Override public void onError(ServiceCommandError error) { Log.w(Util.T, "Netcast TV's mouse connection has been failed"); } }; setMouseCursorVisible(true, listener); } @Override public void disconnectMouse() { setMouseCursorVisible(false, null); } @Override public void click() { ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { } @Override public void onError(ServiceCommandError error) { Log.w(Util.T, "Netcast TV's mouse click has been failed"); } }; String requestURL = getUDAPRequestURL(UDAP_PATH_COMMAND); Map <String,String> params = new HashMap<String,String>(); params.put("name", "HandleTouchClick"); String httpMessage = getUDAPMessageBody(UDAP_API_COMMAND, params); ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, requestURL, httpMessage, responseListener); request.send(); } @Override public void move(double dx, double dy) { mMouseDistance.x += dx; mMouseDistance.y += dy; if (!mMouseIsMoving) { mMouseIsMoving = true; this.moveMouse(); } } private void moveMouse() { String requestURL = getUDAPRequestURL(UDAP_PATH_COMMAND); int x = (int)mMouseDistance.x; int y = (int)mMouseDistance.y; Map <String,String> params = new HashMap<String,String>(); params.put("name", "HandleTouchMove"); params.put("x", String.valueOf(x)); params.put("y", String.valueOf(y)); mMouseDistance.x = mMouseDistance.y = 0; final NetcastTVService mouseService = this; ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { if (mMouseDistance.x > 0 || mMouseDistance.y > 0) mouseService.moveMouse(); else mMouseIsMoving = false; } @Override public void onError(ServiceCommandError error) { Log.w(Util.T, "Netcast TV's mouse move has failed"); mMouseIsMoving = false; } }; String httpMessage = getUDAPMessageBody(UDAP_API_COMMAND, params); ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, requestURL, httpMessage, responseListener); request.send(); } @Override public void move(PointF diff) { move(diff.x, diff.y); } @Override public void scroll(double dx, double dy) { ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { } @Override public void onError(ServiceCommandError error) { Log.w(Util.T, "Netcast TV's mouse scroll has been failed"); } }; String requestURL = getUDAPRequestURL(UDAP_PATH_COMMAND); Map <String,String> params = new HashMap<String,String>(); params.put("name", "HandleTouchWheel"); if (dy > 0) params.put("value", "up"); else params.put("value", "down"); String httpMessage = getUDAPMessageBody(UDAP_API_COMMAND, params); ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, requestURL, httpMessage, responseListener); request.send(); } @Override public void scroll(PointF diff) { scroll(diff.x, diff.y); } /************** KEYBOARD CONTROL **************/ @Override public TextInputControl getTextInputControl() { return this; } @Override public CapabilityPriorityLevel getTextInputControlCapabilityLevel() { return CapabilityPriorityLevel.HIGH; } @Override public ServiceSubscription<TextInputStatusListener> subscribeTextInputStatus(final TextInputStatusListener listener) { keyboardString = new StringBuilder(); URLServiceSubscription<TextInputStatusListener> request = new URLServiceSubscription<TextInputStatusListener>(this, "KeyboardVisible", null, null); request.addListener(listener); addSubscription(request); return request; } @Override public void sendText(final String input) { Log.d(Util.T, "Add to Queue: " + input); keyboardString.append(input); handleKeyboardInput("Editing", keyboardString.toString()); } @Override public void sendEnter() { ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { } @Override public void onError(ServiceCommandError error) { Log.w(Util.T, "Netcast TV's enter key has been failed"); } }; handleKeyboardInput("EditEnd", keyboardString.toString()); sendVirtualKeyCode(NetcastVirtualKeycodes.RED.getCode(), responseListener); // Send RED Key to enter the "ENTER" button } @Override public void sendDelete() { if (keyboardString.length() > 1) { keyboardString.deleteCharAt(keyboardString.length()-1); } else { keyboardString = new StringBuilder(); } handleKeyboardInput("Editing", keyboardString.toString()); } private ResponseListener<String> mTextChangedListener = new ResponseListener<String>() { @Override public void onError(ServiceCommandError error) { } @Override public void onSuccess(String newValue) { keyboardString = new StringBuilder(newValue); } }; private void handleKeyboardInput(final String state, final String buffer) { ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { } @Override public void onError(ServiceCommandError error) { Log.w(Util.T, "Netcast TV's keyboard input has been failed"); } }; String requestURL = getUDAPRequestURL(UDAP_PATH_EVENT); Map <String,String> params = new HashMap<String,String>(); params.put("name", "TextEdited"); params.put("state", state); params.put("value", buffer); String httpMessage = getUDAPMessageBody(UDAP_API_EVENT, params); ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, requestURL, httpMessage, responseListener); request.send(); } /************** KEY CONTROL **************/ @Override public KeyControl getKeyControl() { return this; } @Override public CapabilityPriorityLevel getKeyControlCapabilityLevel() { return CapabilityPriorityLevel.HIGH; } @Override public void up(final ResponseListener<Object> listener) { sendVirtualKeyCode(NetcastVirtualKeycodes.KEY_UP.getCode(), listener); } @Override public void down(final ResponseListener<Object> listener) { sendVirtualKeyCode(NetcastVirtualKeycodes.KEY_DOWN.getCode(), listener); } @Override public void left(final ResponseListener<Object> listener) { sendVirtualKeyCode(NetcastVirtualKeycodes.KEY_LEFT.getCode(), listener); } @Override public void right(final ResponseListener<Object> listener) { sendVirtualKeyCode(NetcastVirtualKeycodes.KEY_RIGHT.getCode(), listener); } @Override public void ok(final ResponseListener<Object> listener) { sendVirtualKeyCode(NetcastVirtualKeycodes.OK.getCode(), listener); } @Override public void back(final ResponseListener<Object> listener) { sendVirtualKeyCode(NetcastVirtualKeycodes.BACK.getCode(), listener); } @Override public void home(final ResponseListener<Object> listener) { sendVirtualKeyCode(NetcastVirtualKeycodes.HOME.getCode(), listener); } /************** POWER CONTROL **************/ @Override public PowerControl getPowerControl() { return this; } @Override public CapabilityPriorityLevel getPowerControlCapabilityLevel() { return CapabilityPriorityLevel.HIGH; } @Override public void powerOff(ResponseListener<Object> listener) { ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { } @Override public void onError(ServiceCommandError error) { Log.w(Util.T, "Netcast TV's power off has been failed"); } }; sendVirtualKeyCode(NetcastVirtualKeycodes.POWER.getCode(), responseListener); } @Override public void powerOn(ResponseListener<Object> listener) { if (listener != null) listener.onError(ServiceCommandError.notSupported()); } private JSONObject parseVolumeXmlToJSON(String data) { SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); try { InputStream stream = new ByteArrayInputStream(data.getBytes("UTF-8")); SAXParser saxParser = saxParserFactory.newSAXParser(); NetcastVolumeParser handler = new NetcastVolumeParser(); saxParser.parse(stream, handler); return handler.getVolumeStatus(); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } private int parseAppNumberXmlToJSON(String data) { SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); try { InputStream stream = new ByteArrayInputStream(data.getBytes("UTF-8")); SAXParser saxParser = saxParserFactory.newSAXParser(); NetcastAppNumberParser handler = new NetcastAppNumberParser(); saxParser.parse(stream, handler); return handler.getApplicationNumber(); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return 0; } private JSONArray parseApplicationsXmlToJSON(String data) { SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); try { InputStream stream = new ByteArrayInputStream(data.getBytes("UTF-8")); SAXParser saxParser = saxParserFactory.newSAXParser(); NetcastApplicationsParser handler = new NetcastApplicationsParser(); saxParser.parse(stream, handler); return handler.getApplications(); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } public String getHttpMessageForHandleKeyInput(final int keycode) { String strKeycode = String.valueOf(keycode); Map <String,String> params = new HashMap<String,String>(); params.put("name", "HandleKeyInput"); params.put("value", strKeycode); return getUDAPMessageBody(UDAP_API_COMMAND, params); } @Override public void sendKeyCode(KeyCode keycode, ResponseListener<Object> listener) { switch (keycode) { case NUM_0: sendVirtualKeyCode(NetcastVirtualKeycodes.NUMBER_0.getCode(), listener); break; case NUM_1: sendVirtualKeyCode(NetcastVirtualKeycodes.NUMBER_1.getCode(), listener); break; case NUM_2: sendVirtualKeyCode(NetcastVirtualKeycodes.NUMBER_2.getCode(), listener); break; case NUM_3: sendVirtualKeyCode(NetcastVirtualKeycodes.NUMBER_3.getCode(), listener); break; case NUM_4: sendVirtualKeyCode(NetcastVirtualKeycodes.NUMBER_4.getCode(), listener); break; case NUM_5: sendVirtualKeyCode(NetcastVirtualKeycodes.NUMBER_5.getCode(), listener); break; case NUM_6: sendVirtualKeyCode(NetcastVirtualKeycodes.NUMBER_6.getCode(), listener); break; case NUM_7: sendVirtualKeyCode(NetcastVirtualKeycodes.NUMBER_7.getCode(), listener); break; case NUM_8: sendVirtualKeyCode(NetcastVirtualKeycodes.NUMBER_8.getCode(), listener); break; case NUM_9: sendVirtualKeyCode(NetcastVirtualKeycodes.NUMBER_9.getCode(), listener); break; case DASH: sendVirtualKeyCode(NetcastVirtualKeycodes.DASH.getCode(), listener); break; case ENTER: sendVirtualKeyCode(NetcastVirtualKeycodes.OK.getCode(), listener); break; default: Util.postError(listener, new ServiceCommandError(0, "The keycode is not available", null)); } } private void sendVirtualKeyCode(final int keycode, final ResponseListener<Object> listener) { ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { try { Thread.sleep(150); } catch (InterruptedException e) { e.printStackTrace(); } String requestURL = getUDAPRequestURL(UDAP_PATH_COMMAND); String httpMessage = getHttpMessageForHandleKeyInput(keycode); ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(NetcastTVService.this, requestURL, httpMessage, listener); request.send(); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; setMouseCursorVisible(false, responseListener); } private String getUDAPRequestURL(String path) { return getUDAPRequestURL(path, null); } private String getUDAPRequestURL(String path, String target) { return getUDAPRequestURL(path, target, null); } private String getUDAPRequestURL(String path, String target, String type) { return getUDAPRequestURL(path, target, type, null, null); } private String getUDAPRequestURL(String path, String target, String type, String index, String number) { // Type Values // 1: List of all apps // 2: List of apps in the Premium category // 3: List of apps in the My Apps category StringBuilder sb = new StringBuilder(); sb.append("http://"); sb.append(serviceDescription.getIpAddress()); sb.append(":"); sb.append(serviceDescription.getPort()); sb.append(path); if (target != null) { sb.append("?target="); sb.append(target); if (type != null) { sb.append("&type="); sb.append(type); } if (index != null) { sb.append("&index="); sb.append(index); } if (number != null) { sb.append("&number="); sb.append(number); } } return sb.toString(); } private String getUDAPMessageBody(String api, Map<String, String> params) { StringBuilder sb = new StringBuilder(); sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>"); sb.append("<envelope>"); sb.append("<api type=\"").append(api).append("\">"); for (Map.Entry<String, String> entry : params.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); sb.append(createNode(key, value)); } sb.append("</api>"); sb.append("</envelope>"); return sb.toString(); } private String createNode(String tag, String value) { return "<" + tag + ">" + value + "</" + tag + ">"; } public String decToHex(String dec) { if (dec != null && dec.length() > 0) { try { return decToHex(Long.parseLong(dec.trim())); } catch (RuntimeException e) { e.printStackTrace(); } } return null; } public String decToHex(long dec) { return String.format("%016x",dec); } @Override public void sendCommand(final ServiceCommand<?> mCommand) { Util.runInBackground(new Runnable() { @SuppressWarnings("unchecked") @Override public void run() { final ServiceCommand<ResponseListener<Object>> command = (ServiceCommand<ResponseListener<Object>>) mCommand; Object payload = command.getPayload(); try { HttpConnection connection = HttpConnection.newInstance(URI.create(command.getTarget())); connection.setHeader(HttpMessage.USER_AGENT, HttpMessage.UDAP_USER_AGENT); connection.setHeader(HttpMessage.CONTENT_TYPE_HEADER, HttpMessage.CONTENT_TYPE_TEXT_XML); if (payload != null && command.getHttpMethod().equalsIgnoreCase(ServiceCommand.TYPE_POST)) { connection.setMethod(HttpConnection.Method.POST); connection.setPayload(payload.toString()); } connection.execute(); int code = connection.getResponseCode(); Log.d("", "RESP " + code); if (code == 200) { Util.postSuccess(command.getResponseListener(), connection.getResponseString()); } else { Util.postError(command.getResponseListener(), ServiceCommandError.getError(code)); } } catch (IOException e) { e.printStackTrace(); Util.postError(command.getResponseListener(), new ServiceCommandError(0, e.getMessage(), null)); } } }); } private void addSubscription(URLServiceSubscription<?> subscription) { subscriptions.add(subscription); if (httpServer != null) httpServer.setSubscriptions(subscriptions); } @Override public void unsubscribe(URLServiceSubscription<?> subscription) { subscriptions.remove(subscription); if (httpServer != null) httpServer.setSubscriptions(subscriptions); } // @Override // public LaunchSession decodeLaunchSession(String type, JSONObject obj) throws JSONException { // if ("netcasttv".equals(type)) { // LaunchSession launchSession = LaunchSession.launchSessionFromJSONObject(obj); // launchSession.setService(this); // // return launchSession; // } // return null; // } @Override protected void updateCapabilities() { List<String> capabilities = new ArrayList<String>(); if (DiscoveryManager.getInstance().getPairingLevel() == PairingLevel.ON) { Collections.addAll(capabilities, TextInputControl.Capabilities); Collections.addAll(capabilities, MouseControl.Capabilities); Collections.addAll(capabilities, KeyControl.Capabilities); Collections.addAll(capabilities, MediaPlayer.Capabilities); capabilities.add(PowerControl.Off); capabilities.add(Play); capabilities.add(Pause); capabilities.add(Stop); capabilities.add(Duration); capabilities.add(Position); capabilities.add(Seek); capabilities.add(Application); capabilities.add(Application_Close); capabilities.add(Application_List); capabilities.add(Browser); capabilities.add(Hulu); capabilities.add(Netflix); capabilities.add(Netflix_Params); capabilities.add(YouTube); capabilities.add(YouTube_Params); capabilities.add(AppStore); capabilities.add(Channel_Up); capabilities.add(Channel_Down); capabilities.add(Channel_Get); capabilities.add(Channel_List); capabilities.add(Channel_Subscribe); capabilities.add(Get_3D); capabilities.add(Set_3D); capabilities.add(Subscribe_3D); capabilities.add(Picker_Launch); capabilities.add(Picker_Close); capabilities.add(Volume_Get); capabilities.add(Volume_Up_Down); capabilities.add(Mute_Get); capabilities.add(Mute_Set); if (serviceDescription.getModelNumber().equals("4.0")) { capabilities.add(AppStore_Params); } } else { Collections.addAll(capabilities, MediaPlayer.Capabilities); capabilities.add(Play); capabilities.add(Pause); capabilities.add(Stop); capabilities.add(YouTube); capabilities.add(YouTube_Params); } capabilities.add(MediaPlayer.Subtitle_SRT); setCapabilities(capabilities); } public DLNAService getDLNAService() { if (dlnaService == null) { DiscoveryManager discoveryManager = DiscoveryManager.getInstance(); ConnectableDevice device = discoveryManager.getAllDevices().get(serviceDescription.getIpAddress()); if (device != null) { DLNAService foundService = null; for (DeviceService service: device.getServices()) { if (DLNAService.class.isAssignableFrom(service.getClass())) { foundService = (DLNAService)service; break; } } dlnaService = foundService; } } return dlnaService; } public DIALService getDIALService() { if (dialService == null) { DiscoveryManager discoveryManager = DiscoveryManager.getInstance(); ConnectableDevice device = discoveryManager.getAllDevices().get(serviceDescription.getIpAddress()); if (device != null) { DIALService foundService = null; for (DeviceService service: device.getServices()) { if (DIALService.class.isAssignableFrom(service.getClass())) { foundService = (DIALService)service; break; } } dialService = foundService; } } return dialService; } }