/* * WebOSTVService * 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 java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.annotation.SuppressLint; import android.content.Context; import android.content.pm.PackageManager.NameNotFoundException; import android.graphics.Bitmap; import android.graphics.PointF; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.util.Base64; 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.ProgramInfo; import com.connectsdk.core.ProgramList; 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.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.PlaylistControl; import com.connectsdk.service.capability.PowerControl; import com.connectsdk.service.capability.TVControl; import com.connectsdk.service.capability.TextInputControl; import com.connectsdk.service.capability.ToastControl; import com.connectsdk.service.capability.VolumeControl; import com.connectsdk.service.capability.WebAppLauncher; 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.ServiceConfig; import com.connectsdk.service.config.ServiceDescription; import com.connectsdk.service.config.WebOSTVServiceConfig; import com.connectsdk.service.sessions.LaunchSession; import com.connectsdk.service.sessions.LaunchSession.LaunchSessionType; import com.connectsdk.service.sessions.WebAppSession; import com.connectsdk.service.sessions.WebAppSession.WebAppPinStatusListener; import com.connectsdk.service.sessions.WebOSWebAppSession; import com.connectsdk.service.webos.WebOSTVKeyboardInput; import com.connectsdk.service.webos.WebOSTVMouseSocketConnection; import com.connectsdk.service.webos.WebOSTVServiceSocketClient; import com.connectsdk.service.webos.WebOSTVServiceSocketClient.WebOSTVServiceSocketClientListener; @SuppressLint("DefaultLocale") public class WebOSTVService extends DeviceService implements Launcher, MediaControl, MediaPlayer, VolumeControl, TVControl, ToastControl, ExternalInputControl, MouseControl, TextInputControl, PowerControl, KeyControl, WebAppLauncher, PlaylistControl { public static final String ID = "webOS TV"; private static final String MEDIA_PLAYER_ID = "MediaPlayer"; public interface WebOSTVServicePermission { public enum Open implements WebOSTVServicePermission { LAUNCH, LAUNCH_WEB, APP_TO_APP, CONTROL_AUDIO, CONTROL_INPUT_MEDIA_PLAYBACK } public static final WebOSTVServicePermission[] OPEN = { Open.LAUNCH, Open.LAUNCH_WEB, Open.APP_TO_APP, Open.CONTROL_AUDIO, Open.CONTROL_INPUT_MEDIA_PLAYBACK }; public enum Protected implements WebOSTVServicePermission { CONTROL_POWER, READ_INSTALLED_APPS, CONTROL_DISPLAY, CONTROL_INPUT_JOYSTICK, CONTROL_INPUT_MEDIA_RECORDING, CONTROL_INPUT_TV, READ_INPUT_DEVICE_LIST, READ_NETWORK_STATE, READ_TV_CHANNEL_LIST, WRITE_NOTIFICATION_TOAST } public static final WebOSTVServicePermission[] PROTECTED = { Protected.CONTROL_POWER, Protected.READ_INSTALLED_APPS, Protected.CONTROL_DISPLAY, Protected.CONTROL_INPUT_JOYSTICK, Protected.CONTROL_INPUT_MEDIA_RECORDING, Protected.CONTROL_INPUT_TV, Protected.READ_INPUT_DEVICE_LIST, Protected.READ_NETWORK_STATE, Protected.READ_TV_CHANNEL_LIST, Protected.WRITE_NOTIFICATION_TOAST }; public enum PersonalActivity implements WebOSTVServicePermission { CONTROL_INPUT_TEXT, CONTROL_MOUSE_AND_KEYBOARD, READ_CURRENT_CHANNEL, READ_RUNNING_APPS } public static final WebOSTVServicePermission[] PERSONAL_ACTIVITY = { PersonalActivity.CONTROL_INPUT_TEXT, PersonalActivity.CONTROL_MOUSE_AND_KEYBOARD, PersonalActivity.READ_CURRENT_CHANNEL, PersonalActivity.READ_RUNNING_APPS }; } public final static String[] kWebOSTVServiceOpenPermissions = { "LAUNCH", "LAUNCH_WEBAPP", "APP_TO_APP", "CONTROL_AUDIO", "CONTROL_INPUT_MEDIA_PLAYBACK" }; public final static String[] kWebOSTVServiceProtectedPermissions = { "CONTROL_POWER", "READ_INSTALLED_APPS", "CONTROL_DISPLAY", "CONTROL_INPUT_JOYSTICK", "CONTROL_INPUT_MEDIA_RECORDING", "CONTROL_INPUT_TV", "READ_INPUT_DEVICE_LIST", "READ_NETWORK_STATE", "READ_TV_CHANNEL_LIST", "WRITE_NOTIFICATION_TOAST" }; public final static String[] kWebOSTVServicePersonalActivityPermissions = { "CONTROL_INPUT_TEXT", "CONTROL_MOUSE_AND_KEYBOARD", "READ_CURRENT_CHANNEL", "READ_RUNNING_APPS" }; public interface SecureAccessTestListener extends ResponseListener<Boolean> { } public interface ACRAuthTokenListener extends ResponseListener<String> { } public interface LaunchPointsListener extends ResponseListener<JSONArray> { } static String FOREGROUND_APP = "ssap://com.webos.applicationManager/getForegroundAppInfo"; static String APP_STATUS = "ssap://com.webos.service.appstatus/getAppStatus"; static String APP_STATE = "ssap://system.launcher/getAppState"; static String VOLUME = "ssap://audio/getVolume"; static String MUTE = "ssap://audio/getMute"; static String VOLUME_STATUS = "ssap://audio/getStatus"; static String CHANNEL_LIST = "ssap://tv/getChannelList"; static String CHANNEL = "ssap://tv/getCurrentChannel"; static String PROGRAM = "ssap://tv/getChannelProgramInfo"; static final String CLOSE_APP_URI = "ssap://system.launcher/close"; static final String CLOSE_MEDIA_URI = "ssap://media.viewer/close"; static final String CLOSE_WEBAPP_URI = "ssap://webapp/closeWebApp"; WebOSTVMouseSocketConnection mouseSocket; WebOSTVKeyboardInput keyboardInput; ConcurrentHashMap<String, String> mAppToAppIdMappings; ConcurrentHashMap<String, WebOSWebAppSession> mWebAppSessions; WebOSTVServiceSocketClient socket; List<String> permissions; public WebOSTVService(ServiceDescription serviceDescription, ServiceConfig serviceConfig) { super(serviceDescription, serviceConfig); setServiceDescription(serviceDescription); pairingType = PairingType.FIRST_SCREEN; mAppToAppIdMappings = new ConcurrentHashMap<String, String>(); mWebAppSessions = new ConcurrentHashMap<String, WebOSWebAppSession>(); } @Override public void setPairingType(PairingType pairingType) { this.pairingType = pairingType; } @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(); } else if (clazz.equals(ToastControl.class)) { return getToastControlCapabilityLevel(); } else if (clazz.equals(WebAppLauncher.class)) { return getWebAppLauncherCapabilityLevel(); } else if (clazz.equals(PlaylistControl.class)) { return getPlaylistControlCapabilityLevel(); } return CapabilityPriorityLevel.NOT_SUPPORTED; } @Override public void setServiceDescription(ServiceDescription serviceDescription) { super.setServiceDescription(serviceDescription); if (this.serviceDescription.getVersion() == null && this.serviceDescription.getResponseHeaders() != null) { String serverInfo = serviceDescription.getResponseHeaders().get("Server").get(0); String systemOS = serverInfo.split(" ")[0]; String[] versionComponents = systemOS.split("/"); String systemVersion = versionComponents[versionComponents.length - 1]; this.serviceDescription.setVersion(systemVersion); this.updateCapabilities(); } } private DeviceService getDLNAService() { Map<String, ConnectableDevice> allDevices = DiscoveryManager.getInstance().getAllDevices(); ConnectableDevice device = null; DeviceService service = null; if (allDevices != null && allDevices.size() > 0) device = allDevices.get(this.serviceDescription.getIpAddress()); if (device != null) service = device.getServiceByName("DLNA"); return service; } public static DiscoveryFilter discoveryFilter() { return new DiscoveryFilter(ID, "urn:lge-com:service:webos-second-screen:1"); } @Override public boolean isConnected() { if (DiscoveryManager.getInstance().getPairingLevel() == PairingLevel.ON) { return this.socket != null && this.socket.isConnected() && (((WebOSTVServiceConfig)serviceConfig).getClientKey() != null); } else { return this.socket != null && this.socket.isConnected(); } } @Override public void connect() { if (this.socket == null) { this.socket = new WebOSTVServiceSocketClient(this, WebOSTVServiceSocketClient.getURI(this)); this.socket.setListener(mSocketListener); } if (!this.isConnected()) this.socket.connect(); } @Override public void disconnect() { Log.d(Util.T, "attempting to disconnect to " + serviceDescription.getIpAddress()); Util.runOnUI(new Runnable() { @Override public void run() { if (listener != null) listener.onDisconnect(WebOSTVService.this, null); } }); if (socket != null) { socket.setListener(null); socket.disconnect(); socket = null; } if (mAppToAppIdMappings != null) mAppToAppIdMappings.clear(); if (mWebAppSessions != null) { Enumeration<WebOSWebAppSession> iterator = mWebAppSessions.elements(); while (iterator.hasMoreElements()) { WebOSWebAppSession session = iterator.nextElement(); session.disconnectFromWebApp(); } mWebAppSessions.clear(); } } @Override public void cancelPairing() { if (this.socket != null) { this.socket.disconnect(); } } private WebOSTVServiceSocketClientListener mSocketListener = new WebOSTVServiceSocketClientListener() { @Override public void onRegistrationFailed(final ServiceCommandError error) { disconnect(); Util.runOnUI(new Runnable() { @Override public void run() { if (listener != null) listener.onConnectionFailure(WebOSTVService.this, error); } }); } @Override public Boolean onReceiveMessage(JSONObject message) { return true; } @Override public void onFailWithError(final ServiceCommandError error) { socket.setListener(null); socket.disconnect(); socket = null; Util.runOnUI(new Runnable() { @Override public void run() { if (listener != null) listener.onConnectionFailure(WebOSTVService.this, error); } }); } @Override public void onConnect() { reportConnected(true); } @Override public void onCloseWithError(final ServiceCommandError error) { socket.setListener(null); socket.disconnect(); socket = null; Util.runOnUI(new Runnable() { @Override public void run() { if (listener != null) listener.onDisconnect(WebOSTVService.this, error); } }); } @Override public void onBeforeRegister(final PairingType pairingType) { if (DiscoveryManager.getInstance().getPairingLevel() == PairingLevel.ON) { Util.runOnUI(new Runnable() { @Override public void run() { if (listener != null) listener.onPairingRequired(WebOSTVService.this, pairingType, null); } }); } } }; // @cond INTERNAL public ConcurrentHashMap<String, String> getWebAppIdMappings() { return mAppToAppIdMappings; } // @endcond @Override public Launcher getLauncher() { return this; } @Override public CapabilityPriorityLevel getLauncherCapabilityLevel() { return CapabilityPriorityLevel.HIGH; } @Override public void launchApp(String appId, AppLaunchListener listener) { AppInfo appInfo = new AppInfo(); appInfo.setId(appId); launchAppWithInfo(appInfo, listener); } @Override public void launchAppWithInfo(AppInfo appInfo, Launcher.AppLaunchListener listener) { launchAppWithInfo(appInfo, null, listener); } @Override public void launchAppWithInfo(final AppInfo appInfo, Object params, final Launcher.AppLaunchListener listener) { String uri = "ssap://system.launcher/launch"; JSONObject payload = new JSONObject(); final String appId = appInfo.getId(); String contentId = null; if (params != null) { try { contentId = (String) ((JSONObject) params).get("contentId"); } catch (JSONException e) { e.printStackTrace(); } } try { payload.put("id", appId); if (contentId != null) payload.put("contentId", contentId); if (params != null) payload.put("params", params); } catch (JSONException e) { e.printStackTrace(); } ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { JSONObject obj = (JSONObject) response; LaunchSession launchSession = new LaunchSession(); launchSession.setService(WebOSTVService.this); launchSession.setAppId(appId); // note that response uses id to mean appId launchSession.setSessionId(obj.optString("sessionId")); launchSession.setSessionType(LaunchSessionType.App); Util.postSuccess(listener, launchSession); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, payload, true, responseListener); request.send(); } @Override public void launchBrowser(String url, final Launcher.AppLaunchListener listener) { String uri = "ssap://system.launcher/open"; JSONObject payload = new JSONObject(); ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { JSONObject obj = (JSONObject) response; LaunchSession launchSession = new LaunchSession(); launchSession.setService(WebOSTVService.this); launchSession.setAppId(obj.optString("id")); // note that response uses id to mean appId launchSession.setSessionId(obj.optString("sessionId")); launchSession.setSessionType(LaunchSessionType.App); launchSession.setRawData(obj); Util.postSuccess(listener, launchSession); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; try { payload.put("target", url); } catch (JSONException e) { e.printStackTrace(); } ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, payload, true, responseListener); request.send(); } @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) { JSONObject params = new JSONObject(); if (contentId != null && contentId.length() > 0) { if (startTime < 0.0) { Util.postError(listener, new ServiceCommandError(0, "Start time may not be negative", null)); return; } try { params.put("contentId", String.format("%s&pairingCode=%s&t=%.1f", contentId, UUID.randomUUID().toString(), startTime)); } catch (JSONException e) { e.printStackTrace(); } } AppInfo appInfo = new AppInfo() {{ setId("youtube.leanback.v4"); setName("YouTube"); }}; launchAppWithInfo(appInfo, params, listener); } @Override public void launchHulu(String contentId, Launcher.AppLaunchListener listener) { JSONObject params = new JSONObject(); try { params.put("contentId", contentId); } catch (JSONException e) { e.printStackTrace(); } AppInfo appInfo = new AppInfo() {{ setId("hulu"); setName("Hulu"); }}; launchAppWithInfo(appInfo, params, listener); } @Override public void launchNetflix(String contentId, Launcher.AppLaunchListener listener) { JSONObject params = new JSONObject(); String netflixContentId = "m=http%3A%2F%2Fapi.netflix.com%2Fcatalog%2Ftitles%2Fmovies%2F" + contentId + "&source_type=4"; try { params.put("contentId", netflixContentId); } catch (JSONException e) { e.printStackTrace(); } AppInfo appInfo = new AppInfo() {{ setId("netflix"); setName("Netflix"); }}; launchAppWithInfo(appInfo, params, listener); } @Override public void launchAppStore(String appId, AppLaunchListener listener) { AppInfo appInfo = new AppInfo("com.webos.app.discovery"); appInfo.setName("LG Store"); JSONObject params = new JSONObject(); if (appId != null && appId.length() > 0) { String query = String.format("category/GAME_APPS/%s", appId); try { params.put("query", query); } catch (JSONException e) { e.printStackTrace(); } } launchAppWithInfo(appInfo, params, listener); } @Override public void closeApp(LaunchSession launchSession, ResponseListener<Object> listener) { String uri = "ssap://system.launcher/close"; String appId = launchSession.getAppId(); String sessionId = launchSession.getSessionId(); JSONObject payload = new JSONObject(); try { payload.put("id", appId); payload.put("sessionId", sessionId); } catch (JSONException e) { e.printStackTrace(); } ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(launchSession.getService(), uri, payload, true, listener); request.send(); } @Override public void getAppList(final AppListListener listener) { String uri = "ssap://com.webos.applicationManager/listApps"; ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { try { JSONObject jsonObj = (JSONObject) response; JSONArray apps = (JSONArray) jsonObj.get("apps"); List<AppInfo> appList = new ArrayList<AppInfo>(); for (int i = 0; i < apps.length(); i++) { final JSONObject appObj = apps.getJSONObject(i); AppInfo appInfo = new AppInfo() {{ setId(appObj.getString("id")); setName(appObj.getString("title")); setRawData(appObj); }}; appList.add(appInfo); } Util.postSuccess(listener, appList); } catch (JSONException e) { e.printStackTrace(); } } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, null, true, responseListener); request.send(); } private ServiceCommand<AppInfoListener> getRunningApp(boolean isSubscription, final AppInfoListener listener) { ServiceCommand<AppInfoListener> request; ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { final JSONObject jsonObj = (JSONObject)response; AppInfo app = new AppInfo() {{ setId(jsonObj.optString("appId")); setName(jsonObj.optString("appName")); setRawData(jsonObj); }}; Util.postSuccess(listener, app); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; if (isSubscription) request = new URLServiceSubscription<AppInfoListener>(this, FOREGROUND_APP, null, true, responseListener); else request = new ServiceCommand<AppInfoListener>(this, FOREGROUND_APP, null, true, responseListener); request.send(); return request; } @Override public void getRunningApp(AppInfoListener listener) { getRunningApp(false, listener); } @Override public ServiceSubscription<AppInfoListener> subscribeRunningApp(AppInfoListener listener) { return (URLServiceSubscription<AppInfoListener>) getRunningApp(true, listener); } private ServiceCommand<AppStateListener> getAppState(boolean subscription, LaunchSession launchSession, final AppStateListener listener) { ServiceCommand<AppStateListener> request; JSONObject params = new JSONObject(); try { params.put("id", launchSession.getAppId()); params.put("sessionId", launchSession.getSessionId()); } catch (JSONException e) { e.printStackTrace(); } ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } @Override public void onSuccess(Object object) { JSONObject json = (JSONObject) object; try { Util.postSuccess(listener, new AppState(json.getBoolean("running"), json.getBoolean("visible"))); } catch (JSONException e) { Util.postError(listener, new ServiceCommandError(0, "Malformed JSONObject", null)); e.printStackTrace(); } } }; if (subscription) { request = new URLServiceSubscription<Launcher.AppStateListener>(this, APP_STATE, params, true, responseListener); } else { request = new ServiceCommand<Launcher.AppStateListener>(this, APP_STATE, params, true, responseListener); } request.send(); return request; } @Override public void getAppState(LaunchSession launchSession, AppStateListener listener) { getAppState(false, launchSession, listener); } @Override public ServiceSubscription<AppStateListener> subscribeAppState(LaunchSession launchSession, AppStateListener listener) { return (URLServiceSubscription<AppStateListener>) getAppState(true, launchSession, listener); } /****************** TOAST CONTROL *****************/ @Override public ToastControl getToastControl() { return this; } @Override public CapabilityPriorityLevel getToastControlCapabilityLevel() { return CapabilityPriorityLevel.HIGH; } @Override public void showToast(String message, ResponseListener<Object> listener) { showToast(message, null, null, listener); } @Override public void showToast(String message, String iconData, String iconExtension, ResponseListener<Object> listener) { JSONObject payload = new JSONObject(); try { payload.put("message", message); if (iconData != null) { payload.put("iconData", iconData); payload.put("iconExtension", iconExtension); } } catch (JSONException e) { e.printStackTrace(); } sendToast(payload, listener); } @Override public void showClickableToastForApp(String message, AppInfo appInfo, JSONObject params, ResponseListener<Object> listener) { showClickableToastForApp(message, appInfo, params, null, null, listener); } @Override public void showClickableToastForApp(String message, AppInfo appInfo, JSONObject params, String iconData, String iconExtension, ResponseListener<Object> listener) { JSONObject payload = new JSONObject(); try { payload.put("message", message); if (iconData != null) { payload.put("iconData", iconData); payload.put("iconExtension", iconExtension); } if (appInfo != null) { JSONObject onClick = new JSONObject(); onClick.put("appId", appInfo.getId()); if (params != null) { onClick.put("params", params); } payload.put("onClick", onClick); } } catch (JSONException e) { e.printStackTrace(); } sendToast(payload, listener); } @Override public void showClickableToastForURL(String message, String url, ResponseListener<Object> listener) { showClickableToastForURL(message, url, null, null, listener); } @Override public void showClickableToastForURL(String message, String url, String iconData, String iconExtension, ResponseListener<Object> listener) { JSONObject payload = new JSONObject(); try { payload.put("message", message); if (iconData != null) { payload.put("iconData", iconData); payload.put("iconExtension", iconExtension); } if (url != null) { JSONObject onClick = new JSONObject(); onClick.put("target", url); payload.put("onClick", onClick); } } catch (JSONException e) { e.printStackTrace(); } sendToast(payload, listener); } private void sendToast(JSONObject payload, ResponseListener<Object> listener) { if (!payload.has("iconData")) { Context context = DiscoveryManager.getInstance().getContext(); try { Drawable drawable = context.getPackageManager().getApplicationIcon(context.getPackageName()); if(drawable != null) { BitmapDrawable bitDw = ((BitmapDrawable) drawable); Bitmap bitmap = bitDw.getBitmap(); ByteArrayOutputStream stream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); byte[] bitmapByte = stream.toByteArray(); bitmapByte = Base64.encode(bitmapByte,Base64.NO_WRAP); String bitmapData = new String(bitmapByte); payload.put("iconData", bitmapData); payload.put("iconExtension", "png"); } } catch (NameNotFoundException e) { e.printStackTrace(); } catch (JSONException e) { e.printStackTrace(); } } String uri = "palm://system.notifications/createToast"; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, payload, true, listener); request.send(); } /****************** VOLUME CONTROL *****************/ @Override public VolumeControl getVolumeControl() { return this; } @Override public CapabilityPriorityLevel getVolumeControlCapabilityLevel() { return CapabilityPriorityLevel.HIGH; } public void volumeUp() { volumeUp(null); } @Override public void volumeUp(ResponseListener<Object> listener) { String uri = "ssap://audio/volumeUp"; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, null, true, listener); request.send(); } public void volumeDown() { volumeDown(null); } @Override public void volumeDown(ResponseListener<Object> listener) { String uri = "ssap://audio/volumeDown"; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, null, true, listener); request.send(); } public void setVolume(int volume) { setVolume(volume, null); } @Override public void setVolume(float volume, ResponseListener<Object> listener) { String uri = "ssap://audio/setVolume"; JSONObject payload = new JSONObject(); int intVolume = (int) Math.round(volume*100.0f); try { payload.put("volume", intVolume); } catch (JSONException e) { e.printStackTrace(); } ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, payload, true, listener); request.send(); } private ServiceCommand<VolumeListener> getVolume(boolean isSubscription, final VolumeListener listener) { ServiceCommand<VolumeListener> request; ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { try { JSONObject jsonObj = (JSONObject)response; int iVolume = (Integer) jsonObj.get("volume"); float fVolume = (float) (iVolume / 100.0); Util.postSuccess(listener, fVolume); } catch (JSONException e) { e.printStackTrace(); } } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; if (isSubscription) request = new URLServiceSubscription<VolumeListener>(this, VOLUME, null, true, responseListener); else request = new ServiceCommand<VolumeListener>(this, VOLUME, null, true, responseListener); request.send(); return request; } @Override public void getVolume(VolumeListener listener) { getVolume(false, listener); } @SuppressWarnings("unchecked") @Override public ServiceSubscription<VolumeListener> subscribeVolume(VolumeListener listener) { return (ServiceSubscription<VolumeListener>) getVolume(true, listener); } @Override public void setMute(boolean isMute, ResponseListener<Object> listener) { String uri = "ssap://audio/setMute"; JSONObject payload = new JSONObject(); try { payload.put("mute", isMute); } catch (JSONException e) { e.printStackTrace(); } ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, payload, true, listener); request.send(); } private ServiceCommand<ResponseListener<Object>> getMuteStatus(boolean isSubscription, final MuteListener listener) { ServiceCommand<ResponseListener<Object>> request; ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { try { JSONObject jsonObj = (JSONObject)response; boolean isMute = (Boolean) jsonObj.get("mute"); Util.postSuccess(listener, isMute); } catch (JSONException e) { e.printStackTrace(); } } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; if (isSubscription) request = new URLServiceSubscription<ResponseListener<Object>>(this, MUTE, null, true, responseListener); else request = new ServiceCommand<ResponseListener<Object>>(this, MUTE, null, true, responseListener); request.send(); return request; } @Override public void getMute(MuteListener listener) { getMuteStatus(false, listener); } @SuppressWarnings("unchecked") @Override public ServiceSubscription<MuteListener> subscribeMute(MuteListener listener) { return (ServiceSubscription<MuteListener>) getMuteStatus(true, listener); } private ServiceCommand<ResponseListener<Object>> getVolumeStatus(boolean isSubscription, final VolumeStatusListener listener) { ServiceCommand<ResponseListener<Object>> request; ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { try { JSONObject jsonObj = (JSONObject) response; boolean isMute = (Boolean) jsonObj.get("mute"); int iVolume = jsonObj.getInt("volume"); float fVolume = (float) (iVolume / 100.0); Util.postSuccess(listener, new VolumeStatus(isMute, fVolume)); } catch (JSONException e) { e.printStackTrace(); } } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; if (isSubscription) request = new URLServiceSubscription<ResponseListener<Object>>(this, VOLUME_STATUS, null, true, responseListener); else request = new ServiceCommand<ResponseListener<Object>>(this, VOLUME_STATUS, null, true, responseListener); request.send(); return request; } public void getVolumeStatus(VolumeStatusListener listener) { getVolumeStatus(false, listener); } @SuppressWarnings("unchecked") public ServiceSubscription<VolumeStatusListener> subscribeVolumeStatus(VolumeStatusListener listener) { return (ServiceSubscription<VolumeStatusListener>) getVolumeStatus(true, listener); } /****************** MEDIA PLAYER *****************/ @Override public MediaPlayer getMediaPlayer() { return this; } @Override public CapabilityPriorityLevel getMediaPlayerCapabilityLevel() { return CapabilityPriorityLevel.HIGH; } @Override public void getMediaInfo(MediaInfoListener listener) { Util.postError(listener, ServiceCommandError.notSupported()); } @Override public ServiceSubscription<MediaInfoListener> subscribeMediaInfo( MediaInfoListener listener) { listener.onError(ServiceCommandError.notSupported()); return null; } private void displayMedia(JSONObject params, final MediaPlayer.LaunchListener listener) { String uri = "ssap://media.viewer/open"; ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { JSONObject obj = (JSONObject) response; LaunchSession launchSession = LaunchSession.launchSessionForAppId(obj.optString("id")); launchSession.setService(WebOSTVService.this); launchSession.setSessionId(obj.optString("sessionId")); launchSession.setSessionType(LaunchSessionType.Media); Util.postSuccess(listener, new MediaLaunchObject(launchSession, WebOSTVService.this)); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, params, true, responseListener); request.send(); } @Override public void displayImage(final String url, final String mimeType, final String title, final String description, final String iconSrc, final MediaPlayer.LaunchListener listener) { if ("4.0.0".equalsIgnoreCase(this.serviceDescription.getVersion())) { DeviceService dlnaService = this.getDLNAService(); if (dlnaService != null) { MediaPlayer mediaPlayer = dlnaService.getAPI(MediaPlayer.class); if (mediaPlayer != null) { mediaPlayer.displayImage(url, mimeType, title, description, iconSrc, listener); return; } } JSONObject params = null; try { params = new JSONObject() {{ put("target", url); put("title", title == null ? NULL : title); put("description", description == null ? NULL : description); put("mimeType", mimeType == null ? NULL : mimeType); put("iconSrc", iconSrc == null ? NULL : iconSrc); }}; } catch (JSONException ex) { ex.printStackTrace(); Util.postError(listener, new ServiceCommandError(-1, ex.getLocalizedMessage(), ex)); } if (params != null) this.displayMedia(params, listener); } else { final WebAppSession.LaunchListener webAppLaunchListener = new WebAppSession.LaunchListener() { @Override public void onError(ServiceCommandError error) { listener.onError(error); } @Override public void onSuccess(WebAppSession webAppSession) { webAppSession.displayImage(url, mimeType, title, description, iconSrc, listener); } }; this.getWebAppLauncher().joinWebApp(MEDIA_PLAYER_ID, new WebAppSession.LaunchListener() { @Override public void onError(ServiceCommandError error) { getWebAppLauncher().launchWebApp(MEDIA_PLAYER_ID, webAppLaunchListener); } @Override public void onSuccess(WebAppSession webAppSession) { webAppSession.displayImage(url, mimeType, title, description, iconSrc, listener); } }); } } @Override public void displayImage(MediaInfo mediaInfo, MediaPlayer.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(final String url, final String mimeType, final String title, final String description, final String iconSrc, final boolean shouldLoop, final MediaPlayer.LaunchListener listener) { if ("4.0.0".equalsIgnoreCase(this.serviceDescription.getVersion())) { DeviceService dlnaService = this.getDLNAService(); if (dlnaService != null) { MediaPlayer mediaPlayer = dlnaService.getAPI(MediaPlayer.class); if (mediaPlayer != null) { mediaPlayer.playMedia(url, mimeType, title, description, iconSrc, shouldLoop, listener); return; } } JSONObject params = null; try { params = new JSONObject() {{ put("target", url); put("title", title == null ? NULL : title); put("description", description == null ? NULL : description); put("mimeType", mimeType == null ? NULL : mimeType); put("iconSrc", iconSrc == null ? NULL : iconSrc); put("loop", shouldLoop); }}; } catch (JSONException ex) { ex.printStackTrace(); Util.postError(listener, new ServiceCommandError(-1, ex.getLocalizedMessage(), ex)); } if (params != null) this.displayMedia(params, listener); } else { final WebAppSession.LaunchListener webAppLaunchListener = new WebAppSession.LaunchListener() { @Override public void onError(ServiceCommandError error) { listener.onError(error); } @Override public void onSuccess(WebAppSession webAppSession) { webAppSession.playMedia(url, mimeType, title, description, iconSrc, shouldLoop, listener); } }; this.getWebAppLauncher().joinWebApp(MEDIA_PLAYER_ID, new WebAppSession.LaunchListener() { @Override public void onError(ServiceCommandError error) { getWebAppLauncher().launchWebApp(MEDIA_PLAYER_ID, webAppLaunchListener); } @Override public void onSuccess(WebAppSession webAppSession) { webAppSession.playMedia(url, mimeType, title, description, iconSrc, shouldLoop, listener); } }); } } @Override public void playMedia(MediaInfo mediaInfo, boolean shouldLoop, MediaPlayer.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(); } } playMedia(mediaUrl, mimeType, title, desc, iconSrc, shouldLoop, listener); } @Override public void closeMedia(LaunchSession launchSession, ResponseListener<Object> listener) { JSONObject payload = new JSONObject(); try { if (launchSession.getAppId() != null && launchSession.getAppId().length() > 0) payload.put("id", launchSession.getAppId()); if (launchSession.getSessionId() != null && launchSession.getSessionId().length() > 0) payload.put("sessionId", launchSession.getSessionId()); } catch (JSONException e) { e.printStackTrace(); } ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(launchSession.getService(), CLOSE_MEDIA_URI, payload, true, listener); request.send(); } /****************** MEDIA CONTROL *****************/ @Override public MediaControl getMediaControl() { return this; } @Override public CapabilityPriorityLevel getMediaControlCapabilityLevel() { return CapabilityPriorityLevel.HIGH; } @Override public void play(ResponseListener<Object> listener) { String uri = "ssap://media.controls/play"; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, null, true, listener); request.send(); } @Override public void pause(ResponseListener<Object> listener) { String uri = "ssap://media.controls/pause"; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, null, true, listener); request.send(); } @Override public void stop(ResponseListener<Object> listener) { String uri = "ssap://media.controls/stop"; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, null, true, listener); request.send(); } @Override public void rewind(ResponseListener<Object> listener) { String uri = "ssap://media.controls/rewind"; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, null, true, listener); request.send(); } @Override public void fastForward(ResponseListener<Object> listener) { String uri = "ssap://media.controls/fastForward"; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, null, true, listener); request.send(); } @Override public void previous(ResponseListener<Object> listener) { Util.postError(listener, ServiceCommandError.notSupported()); } @Override public void next(ResponseListener<Object> listener) { Util.postError(listener, ServiceCommandError.notSupported()); } @Override public void seek(long position, ResponseListener<Object> listener) { Util.postError(listener, ServiceCommandError.notSupported()); } @Override public void getDuration(DurationListener listener) { Util.postError(listener, ServiceCommandError.notSupported()); } @Override public void getPosition(PositionListener listener) { Util.postError(listener, ServiceCommandError.notSupported()); } /****************** TV CONTROL *****************/ @Override public TVControl getTVControl() { return this; } @Override public CapabilityPriorityLevel getTVControlCapabilityLevel() { return CapabilityPriorityLevel.HIGH; } public void channelUp() { channelUp(null); } @Override public void channelUp(ResponseListener<Object> listener) { String uri = "ssap://tv/channelUp"; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, null, true, listener); request.send(); } public void channelDown() { channelDown(null); } @Override public void channelDown(ResponseListener<Object> listener) { String uri = "ssap://tv/channelDown"; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, null, true, listener); request.send(); } @Override public void setChannel(ChannelInfo channelInfo, ResponseListener<Object> listener) { String uri = "ssap://tv/openChannel"; JSONObject payload = new JSONObject(); try { payload.put("channelNumber", channelInfo.getNumber()); } catch (JSONException e) { e.printStackTrace(); } ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, payload, true, listener); request.send(); } public void setChannelById(String channelId) { setChannelById(channelId, null); } public void setChannelById(String channelId, ResponseListener<Object> listener) { String uri = "ssap://tv/openChannel"; JSONObject payload = new JSONObject(); try { payload.put("channelId", channelId); } catch (JSONException e) { e.printStackTrace(); } ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, payload, true, listener); request.send(); } private ServiceCommand<ResponseListener<Object>> getCurrentChannel(boolean isSubscription, final ChannelListener listener) { ServiceCommand<ResponseListener<Object>> request; ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { JSONObject jsonObj = (JSONObject) response; ChannelInfo channel = parseRawChannelData(jsonObj); Util.postSuccess(listener, channel); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; if (isSubscription) { request = new URLServiceSubscription<ResponseListener<Object>>(this, CHANNEL, null, true, responseListener); } else request = new ServiceCommand<ResponseListener<Object>>(this, CHANNEL, null, true, responseListener); request.send(); return request; } @Override public void getCurrentChannel(ChannelListener listener) { getCurrentChannel(false, listener); } @SuppressWarnings("unchecked") @Override public ServiceSubscription<ChannelListener> subscribeCurrentChannel(ChannelListener listener) { return (ServiceSubscription<ChannelListener>) getCurrentChannel(true, listener); } private ServiceCommand<ResponseListener<Object>> getChannelList(boolean isSubscription, final ChannelListListener listener) { ServiceCommand<ResponseListener<Object>> request; ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { try { JSONObject jsonObj = (JSONObject)response; ArrayList<ChannelInfo> list = new ArrayList<ChannelInfo>(); JSONArray array = (JSONArray) jsonObj.get("channelList"); for (int i = 0; i < array.length(); i++) { JSONObject object = (JSONObject) array.get(i); ChannelInfo channel = parseRawChannelData(object); list.add(channel); } Util.postSuccess(listener, list); } catch (JSONException e) { e.printStackTrace(); } } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; if (isSubscription) request = new URLServiceSubscription<ResponseListener<Object>>(this, CHANNEL_LIST, null, true, responseListener); else request = new ServiceCommand<ResponseListener<Object>>(this, CHANNEL_LIST, null, true, responseListener); request.send(); return request; } @Override public void getChannelList(ChannelListListener listener) { getChannelList(false, listener); } @SuppressWarnings("unchecked") public ServiceSubscription<ChannelListListener> subscribeChannelList(final ChannelListListener listener) { return (ServiceSubscription<ChannelListListener>) getChannelList(true, listener); } private ServiceCommand<ResponseListener<Object>> getChannelCurrentProgramInfo(boolean isSubscription, final ProgramInfoListener listener) { String uri ="ssap://tv/getChannelCurrentProgramInfo"; ServiceCommand<ResponseListener<Object>> request; ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { JSONObject jsonObj = (JSONObject)response; ProgramInfo programInfo = parseRawProgramInfo(jsonObj); Util.postSuccess(listener, programInfo); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; if (isSubscription) request = new URLServiceSubscription<ResponseListener<Object>>(this, uri, null, true, responseListener); else request = new ServiceCommand<ResponseListener<Object>>(this, uri, null, true, responseListener); request.send(); return request; } public void getChannelCurrentProgramInfo(ProgramInfoListener listener) { getChannelCurrentProgramInfo(false, listener); } @SuppressWarnings("unchecked") public ServiceSubscription<ProgramInfoListener> subscribeChannelCurrentProgramInfo(ProgramInfoListener listener) { return (ServiceSubscription<ProgramInfoListener>) getChannelCurrentProgramInfo(true, listener); } private ServiceCommand<ResponseListener<Object>> getProgramList(boolean isSubscription, final ProgramListListener listener) { ServiceCommand<ResponseListener<Object>> request; ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { try { JSONObject jsonObj = (JSONObject)response; JSONObject jsonChannel = (JSONObject) jsonObj.get("channel"); ChannelInfo channel = parseRawChannelData(jsonChannel); JSONArray programList = (JSONArray) jsonObj.get("programList"); Util.postSuccess(listener, new ProgramList(channel, programList)); } catch (JSONException e) { e.printStackTrace(); } } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; if (isSubscription) request = new URLServiceSubscription<ResponseListener<Object>>(this, PROGRAM, null, true, responseListener); else request = new ServiceCommand<ResponseListener<Object>>(this, PROGRAM, null, true, responseListener); request.send(); return request; } @Override public void getProgramInfo(ProgramInfoListener listener) { // TODO need to parse current program when program id is correct Util.postError(listener, ServiceCommandError.notSupported()); } @Override public ServiceSubscription<ProgramInfoListener> subscribeProgramInfo(ProgramInfoListener listener) { Util.postError(listener, ServiceCommandError.notSupported()); return new NotSupportedServiceSubscription<ProgramInfoListener>(); } @Override public void getProgramList(ProgramListListener listener) { getProgramList(false, listener); } @SuppressWarnings("unchecked") @Override public ServiceSubscription<ProgramListListener> subscribeProgramList(ProgramListListener listener) { return (ServiceSubscription<ProgramListListener>) getProgramList(true, listener); } @Override public void set3DEnabled(final boolean enabled, final ResponseListener<Object> listener) { String uri; if (enabled) uri = "ssap://com.webos.service.tv.display/set3DOn"; else uri = "ssap://com.webos.service.tv.display/set3DOff"; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, null, true, listener); request.send(); } private ServiceCommand<State3DModeListener> get3DEnabled(boolean isSubscription, final State3DModeListener listener) { String uri = "ssap://com.webos.service.tv.display/get3DStatus"; ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { JSONObject jsonObj = (JSONObject)response; JSONObject status; try { status = jsonObj.getJSONObject("status3D"); boolean isEnabled = status.getBoolean("status"); Util.postSuccess(listener, isEnabled); } catch (JSONException e) { e.printStackTrace(); } } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; ServiceCommand<State3DModeListener> request; if (isSubscription) request = new URLServiceSubscription<State3DModeListener>(this, uri, null, true, responseListener); else request = new ServiceCommand<State3DModeListener>(this, uri, null, true, responseListener); request.send(); return request; } @Override public void get3DEnabled(final State3DModeListener listener) { get3DEnabled(false, listener); } @SuppressWarnings("unchecked") @Override public ServiceSubscription<State3DModeListener> subscribe3DEnabled(final State3DModeListener listener) { return (ServiceSubscription<State3DModeListener>) get3DEnabled(true, listener); } /************** EXTERNAL INPUT **************/ @Override public ExternalInputControl getExternalInput() { return this; } @Override public CapabilityPriorityLevel getExternalInputControlPriorityLevel() { return CapabilityPriorityLevel.HIGH; } @Override public void launchInputPicker(final AppLaunchListener listener) { final AppInfo appInfo = new AppInfo() {{ setId("com.webos.app.inputpicker"); setName("InputPicker"); }}; launchAppWithInfo(appInfo, null, new AppLaunchListener() { @Override public void onSuccess(LaunchSession object) { listener.onSuccess(object); } @Override public void onError(ServiceCommandError error) { appInfo.setId("com.webos.app.inputmgr"); launchAppWithInfo(appInfo, null, listener); } }); } @Override public void closeInputPicker(LaunchSession launchSession, ResponseListener<Object> listener) { closeApp(launchSession, listener); } @Override public void getExternalInputList(final ExternalInputListListener listener) { String uri = "ssap://tv/getExternalInputList"; ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { try { JSONObject jsonObj = (JSONObject)response; JSONArray devices = (JSONArray) jsonObj.get("devices"); Util.postSuccess(listener, externalnputInfoFromJSONArray(devices)); } catch (JSONException e) { e.printStackTrace(); } } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, null, true, responseListener); request.send(); } @Override public void setExternalInput(ExternalInputInfo externalInputInfo , final ResponseListener<Object> listener) { String uri = "ssap://tv/switchInput"; JSONObject payload = new JSONObject(); try { if (externalInputInfo != null && externalInputInfo .getId() != null) { payload.put("inputId", externalInputInfo.getId()); } else { Log.w(Util.T, "ExternalInputInfo has no id"); } } catch (JSONException e) { e.printStackTrace(); } ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, payload, true, listener); request.send(); } /************** MOUSE CONTROL **************/ @Override public MouseControl getMouseControl() { return this; } @Override public CapabilityPriorityLevel getMouseControlCapabilityLevel() { return CapabilityPriorityLevel.HIGH; } @Override public void connectMouse() { connectMouse(new WebOSTVMouseSocketConnection.WebOSTVMouseSocketListener() { @Override public void onConnected() { // intentionally left empty } }); } @Override public void disconnectMouse() { if (mouseSocket == null) return; mouseSocket.disconnect(); mouseSocket = null; } private void connectMouse(final WebOSTVMouseSocketConnection.WebOSTVMouseSocketListener successHandler) { if (mouseSocket != null) return; String uri = "ssap://com.webos.service.networkinput/getPointerInputSocket"; ResponseListener<Object> listener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { try { JSONObject jsonObj = (JSONObject)response; String socketPath = (String) jsonObj.get("socketPath"); mouseSocket = new WebOSTVMouseSocketConnection(socketPath, successHandler); } catch (JSONException e) { e.printStackTrace(); } } @Override public void onError(ServiceCommandError error) { Log.w(Util.T, "Connect mouse error: " + error.getMessage()); } }; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, null, true, listener); request.send(); } @Override public void click() { if (mouseSocket != null) { mouseSocket.click(); } else { connectMouse(new WebOSTVMouseSocketConnection.WebOSTVMouseSocketListener() { @Override public void onConnected() { mouseSocket.click(); } }); } } @Override public void move(final double dx, final double dy) { if (mouseSocket != null) { mouseSocket.move(dx, dy); } else { connectMouse(new WebOSTVMouseSocketConnection.WebOSTVMouseSocketListener() { @Override public void onConnected() { mouseSocket.move(dx, dy); } }); } } @Override public void move(PointF diff) { move(diff.x, diff.y); } @Override public void scroll(final double dx, final double dy) { if (mouseSocket != null) { mouseSocket.scroll(dx, dy); } else { connectMouse(new WebOSTVMouseSocketConnection.WebOSTVMouseSocketListener() { @Override public void onConnected() { mouseSocket.scroll(dx, dy); } }); } } @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(TextInputStatusListener listener) { keyboardInput = new WebOSTVKeyboardInput(this); return keyboardInput.connect(listener); } @Override public void sendText(String input) { if (keyboardInput != null) { keyboardInput.addToQueue(input); } } @Override public void sendKeyCode(KeyCode keycode, ResponseListener<Object> listener) { switch (keycode) { case NUM_0: case NUM_1: case NUM_2: case NUM_3: case NUM_4: case NUM_5: case NUM_6: case NUM_7: case NUM_8: case NUM_9: sendSpecialKey(String.valueOf(keycode.getCode()), listener); break; case DASH: sendSpecialKey("DASH", listener); break; case ENTER: sendSpecialKey("ENTER", listener); default: Util.postError(listener, new ServiceCommandError(0, "The keycode is not available", null)); } } @Override public void sendEnter() { if (keyboardInput != null) { keyboardInput.sendEnter(); } } @Override public void sendDelete() { if (keyboardInput != null) { keyboardInput.sendDel(); } } /************** 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) { } }; String uri = "ssap://system/turnOff"; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, null, true, responseListener); request.send(); } @Override public void powerOn(ResponseListener<Object> listener) { Util.postError(listener, ServiceCommandError.notSupported()); } /************** KEY CONTROL **************/ @Override public KeyControl getKeyControl() { return this; } @Override public CapabilityPriorityLevel getKeyControlCapabilityLevel() { return CapabilityPriorityLevel.HIGH; } private void sendSpecialKey(final String key, final ResponseListener<Object> listener) { if (mouseSocket != null) { mouseSocket.button(key); Util.postSuccess(listener, null); } else { connectMouse(new WebOSTVMouseSocketConnection.WebOSTVMouseSocketListener() { @Override public void onConnected() { mouseSocket.button(key); Util.postSuccess(listener, null); } }); } } @Override public void up(ResponseListener<Object> listener) { sendSpecialKey("UP", listener); } @Override public void down(ResponseListener<Object> listener) { sendSpecialKey("DOWN", listener); } @Override public void left(ResponseListener<Object> listener) { sendSpecialKey("LEFT", listener); } @Override public void right(ResponseListener<Object> listener) { sendSpecialKey("RIGHT", listener); } @Override public void ok(final ResponseListener<Object> listener) { if (mouseSocket != null) { mouseSocket.click(); Util.postSuccess(listener, null); } else { connectMouse(new WebOSTVMouseSocketConnection.WebOSTVMouseSocketListener() { @Override public void onConnected() { mouseSocket.click(); Util.postSuccess(listener, null); } }); } } @Override public void back(ResponseListener<Object> listener) { sendSpecialKey("BACK", listener); } @Override public void home(ResponseListener<Object> listener) { sendSpecialKey("HOME", listener); } /************** Web App Launcher **************/ @Override public WebAppLauncher getWebAppLauncher() { return this; } @Override public CapabilityPriorityLevel getWebAppLauncherCapabilityLevel() { return CapabilityPriorityLevel.HIGH; } @Override public void launchWebApp(final String webAppId, final WebAppSession.LaunchListener listener) { this.launchWebApp(webAppId, null, true, listener); } @Override public void launchWebApp(String webAppId, boolean relaunchIfRunning, WebAppSession.LaunchListener listener) { launchWebApp(webAppId, null, relaunchIfRunning, listener); } public void launchWebApp(final String webAppId, final JSONObject params, final WebAppSession.LaunchListener listener) { if (webAppId == null || webAppId.length() == 0) { Util.postError(listener, new ServiceCommandError(-1, "You need to provide a valid webAppId.", null)); return; } final WebOSWebAppSession _webAppSession = mWebAppSessions.get(webAppId); String uri = "ssap://webapp/launchWebApp"; JSONObject payload = new JSONObject(); try { payload.put("webAppId", webAppId); if (params != null) payload.put("urlParams", params); } catch (JSONException e) { e.printStackTrace(); } ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(final Object response) { JSONObject obj = (JSONObject) response; LaunchSession launchSession = null; WebOSWebAppSession webAppSession = _webAppSession; if (webAppSession != null) launchSession = webAppSession.launchSession; else { launchSession = LaunchSession.launchSessionForAppId(webAppId); webAppSession = new WebOSWebAppSession(launchSession, WebOSTVService.this); mWebAppSessions.put(webAppId, webAppSession); } launchSession.setService(WebOSTVService.this); launchSession.setSessionId(obj.optString("sessionId")); launchSession.setSessionType(LaunchSessionType.WebApp); launchSession.setRawData(obj); Util.postSuccess(listener, webAppSession); } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, payload, true, responseListener); request.send(); } @Override public void launchWebApp(final String webAppId, final JSONObject params, boolean relaunchIfRunning, final WebAppSession.LaunchListener listener) { if (webAppId == null) { Util.postError(listener, new ServiceCommandError(0, "Must pass a web App id", null)); return; } if (relaunchIfRunning) { launchWebApp(webAppId, params, listener); } else { getLauncher().getRunningApp(new AppInfoListener() { @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } @Override public void onSuccess(AppInfo appInfo) { // TODO: this will only work on pinned apps, currently if (appInfo.getId().indexOf(webAppId) != -1) { LaunchSession launchSession = LaunchSession.launchSessionForAppId(webAppId); launchSession.setSessionType(LaunchSessionType.WebApp); launchSession.setService(WebOSTVService.this); launchSession.setRawData(appInfo.getRawData()); WebOSWebAppSession webAppSession = webAppSessionForLaunchSession(launchSession); Util.postSuccess(listener, webAppSession); } else { launchWebApp(webAppId, params, listener); } } }); } } @Override public void closeWebApp(LaunchSession launchSession, final ResponseListener<Object> listener) { if (launchSession == null || launchSession.getAppId() == null || launchSession.getAppId().length() == 0) { Util.postError(listener, new ServiceCommandError(0, "Must provide a valid launch session", null)); return; } final WebOSWebAppSession webAppSession = mWebAppSessions.get(launchSession.getAppId()); if (webAppSession != null) { webAppSession.disconnectFromWebApp(); } String uri = "ssap://webapp/closeWebApp"; JSONObject payload = new JSONObject(); try { if (launchSession.getAppId() != null) payload.put("webAppId", launchSession.getAppId()); if (launchSession.getSessionId() != null) payload.put("sessionId", launchSession.getSessionId()); } catch (JSONException e) { e.printStackTrace(); } ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, payload, true, listener); request.send(); } public void connectToWebApp(final WebOSWebAppSession webAppSession, final boolean joinOnly, final ResponseListener<Object> connectionListener) { if (mWebAppSessions == null) mWebAppSessions = new ConcurrentHashMap<String, WebOSWebAppSession>(); if (mAppToAppIdMappings == null) mAppToAppIdMappings = new ConcurrentHashMap<String, String>(); if (webAppSession == null || webAppSession.launchSession == null) { Util.postError(connectionListener, new ServiceCommandError(0, "You must provide a valid LaunchSession object", null)); return; } String _appId = webAppSession.launchSession.getAppId(); String _idKey = null; if (webAppSession.launchSession.getSessionType() == LaunchSession.LaunchSessionType.WebApp) _idKey = "webAppId"; else _idKey = "appId"; if (_appId == null || _appId.length() == 0) { Util.postError(connectionListener, new ServiceCommandError(-1, "You must provide a valid web app session", null)); return; } final String appId = _appId; final String idKey = _idKey; String uri = "ssap://webapp/connectToApp"; JSONObject payload = new JSONObject(); try { payload.put(idKey, appId); } catch (JSONException e) { e.printStackTrace(); } ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(final Object response) { JSONObject jsonObj = (JSONObject)response; String state = jsonObj.optString("state"); if (!state.equalsIgnoreCase("CONNECTED")) { if (joinOnly && state.equalsIgnoreCase("WAITING_FOR_APP")) { Util.postError(connectionListener, new ServiceCommandError(0, "Web app is not currently running", null)); } return; } String fullAppId = jsonObj.optString("appId"); if (fullAppId != null && fullAppId.length() != 0) { if (webAppSession.launchSession.getSessionType() == LaunchSessionType.WebApp) mAppToAppIdMappings.put(fullAppId, appId); webAppSession.setFullAppId(fullAppId); } if (connectionListener != null) { Util.runOnUI(new Runnable() { @Override public void run() { connectionListener.onSuccess(response); } }); } } @Override public void onError(ServiceCommandError error) { webAppSession.disconnectFromWebApp(); boolean appChannelDidClose = false; if (error != null && error.getPayload() != null) appChannelDidClose = error.getPayload().toString().contains("app channel closed"); if (appChannelDidClose) { if (webAppSession.getWebAppSessionListener() != null) { Util.runOnUI(new Runnable() { @Override public void run() { webAppSession.getWebAppSessionListener().onWebAppSessionDisconnect(webAppSession); } }); } } else { Util.postError(connectionListener, error); } } }; webAppSession.appToAppSubscription = new URLServiceSubscription<ResponseListener<Object>>(webAppSession.socket, uri, payload, true, responseListener); webAppSession.appToAppSubscription.subscribe(); } private void notifyPairingRequired() { if (listener != null) { listener.onPairingRequired(this, pairingType, null); } } @Override public void pinWebApp(String webAppId, final ResponseListener<Object> listener) { if (webAppId == null || webAppId.length() == 0) { if (listener != null) { listener.onError(new ServiceCommandError(-1, "You must provide a valid web app id", null)); } return; } String uri = "ssap://webapp/pinWebApp"; JSONObject payload = new JSONObject(); try { payload.put("webAppId", webAppId); } catch (JSONException e) { e.printStackTrace(); } ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(final Object response) { JSONObject obj = (JSONObject) response; if (obj.has("pairingType")) { notifyPairingRequired(); } else if (listener != null) { listener.onSuccess(response); } } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; ServiceCommand<ResponseListener<Object>> request = new URLServiceSubscription<ResponseListener<Object>>(this, uri, payload, true, responseListener); request.send(); } @Override public void unPinWebApp(String webAppId, final ResponseListener<Object> listener) { if (webAppId == null || webAppId.length() == 0) { if (listener != null) { listener.onError(new ServiceCommandError(-1, "You must provide a valid web app id", null)); } return; } String uri = "ssap://webapp/removePinnedWebApp"; JSONObject payload = new JSONObject(); try { payload.put("webAppId", webAppId); } catch (JSONException e) { e.printStackTrace(); } ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(final Object response) { JSONObject obj = (JSONObject) response; if (obj.has("pairingType")) { notifyPairingRequired(); } else if (listener != null) { listener.onSuccess(response); } } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; ServiceCommand<ResponseListener<Object>> request = new URLServiceSubscription<ResponseListener<Object>>(this, uri, payload, true, responseListener); request.send(); } private ServiceCommand<WebAppPinStatusListener> isWebAppPinned(boolean isSubscription, String webAppId, final WebAppPinStatusListener listener) { if (webAppId == null || webAppId.length() == 0) { if (listener != null) { listener.onError(new ServiceCommandError(-1, "You must provide a valid web app id", null)); } return null; } String uri = "ssap://webapp/isWebAppPinned"; JSONObject payload = new JSONObject(); try { payload.put("webAppId", webAppId); } catch (JSONException e) { e.printStackTrace(); } ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(final Object response) { JSONObject obj = (JSONObject) response; boolean status = obj.optBoolean("pinned"); if (listener != null) { listener.onSuccess(status); } } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; ServiceCommand<WebAppPinStatusListener> request; if (isSubscription) request = new URLServiceSubscription<WebAppPinStatusListener>(this, uri, payload, true, responseListener); else request = new ServiceCommand<WebAppPinStatusListener>(this, uri, payload, true, responseListener); request.send(); return request; } @Override public void isWebAppPinned(String webAppId, WebAppPinStatusListener listener) { isWebAppPinned(false, webAppId, listener); } @Override public ServiceSubscription<WebAppPinStatusListener> subscribeIsWebAppPinned( String webAppId, WebAppPinStatusListener listener) { return (URLServiceSubscription<WebAppPinStatusListener>)isWebAppPinned(true, webAppId, listener); } /* Join a native/installed webOS app */ public void joinApp(String appId, WebAppSession.LaunchListener listener) { LaunchSession launchSession = LaunchSession.launchSessionForAppId(appId); launchSession.setSessionType(LaunchSessionType.App); launchSession.setService(this); joinWebApp(launchSession, listener); } /* Connect to a native/installed webOS app */ public void connectToApp(String appId, final WebAppSession.LaunchListener listener) { LaunchSession launchSession = LaunchSession.launchSessionForAppId(appId); launchSession.setSessionType(LaunchSessionType.App); launchSession.setService(this); final WebOSWebAppSession webAppSession = webAppSessionForLaunchSession(launchSession); connectToWebApp(webAppSession, false, new ResponseListener<Object>() { @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } @Override public void onSuccess(Object object) { Util.postSuccess(listener, webAppSession); } }); } @Override public void joinWebApp(final LaunchSession webAppLaunchSession, final WebAppSession.LaunchListener listener) { final WebOSWebAppSession webAppSession = this.webAppSessionForLaunchSession(webAppLaunchSession); webAppSession.join(new ResponseListener<Object>() { @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } @Override public void onSuccess(Object object) { Util.postSuccess(listener, webAppSession); } }); } @Override public void joinWebApp(String webAppId, WebAppSession.LaunchListener listener) { LaunchSession launchSession = LaunchSession.launchSessionForAppId(webAppId); launchSession.setSessionType(LaunchSessionType.WebApp); launchSession.setService(this); joinWebApp(launchSession, listener); } private WebOSWebAppSession webAppSessionForLaunchSession(LaunchSession launchSession) { if (mWebAppSessions == null) mWebAppSessions = new ConcurrentHashMap<String, WebOSWebAppSession>(); if (launchSession.getService() == null) launchSession.setService(this); WebOSWebAppSession webAppSession = mWebAppSessions.get(launchSession.getAppId()); if (webAppSession == null) { webAppSession = new WebOSWebAppSession(launchSession, this); mWebAppSessions.put(launchSession.getAppId(), webAppSession); } return webAppSession; } @SuppressWarnings("unused") private void sendMessage(Object message, LaunchSession launchSession, ResponseListener<Object> listener) { if (launchSession == null || launchSession.getAppId() == null) { Util.postError(listener, new ServiceCommandError(0, "Must provide a valid LaunchSession object", null)); return; } if (message == null) { Util.postError(listener, new ServiceCommandError(0, "Cannot send a null message", null)); return; } if (socket == null) { connect(); } String appId = launchSession.getAppId(); String fullAppId = appId; if (launchSession.getSessionType() == LaunchSessionType.WebApp) fullAppId = mAppToAppIdMappings.get(appId); if (fullAppId == null || fullAppId.length() == 0) { Util.postError(listener, new ServiceCommandError(-1, "You must provide a valid LaunchSession to send messages to", null)); return; } JSONObject payload = new JSONObject(); try { payload.put("type", "p2p"); payload.put("to", fullAppId); payload.put("payload", message); } catch (JSONException e) { e.printStackTrace(); } ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, null, payload, true, listener); sendCommand(request); } public void sendMessage(String message, LaunchSession launchSession, ResponseListener<Object> listener) { if (message != null && message.length() > 0) { sendMessage((Object) message, launchSession, listener); } else { Util.postError(listener, new ServiceCommandError(0, "Cannot send a null message", null)); } } public void sendMessage(JSONObject message, LaunchSession launchSession, ResponseListener<Object> listener) { if (message != null && message.length() > 0) sendMessage((Object) message, launchSession, listener); else Util.postError(listener, new ServiceCommandError(0, "Cannot send a null message", null)); } /************** SYSTEM CONTROL **************/ public void getServiceInfo(final ServiceInfoListener listener) { String uri = "ssap://api/getServiceList"; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, null, true, new ResponseListener<Object>() { @Override public void onSuccess(Object response) { try { JSONObject jsonObj = (JSONObject)response; JSONArray services = (JSONArray) jsonObj.get("services"); Util.postSuccess(listener, services); } catch (JSONException e) { e.printStackTrace(); } } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }); request.send(); } public void getSystemInfo(final SystemInfoListener listener) { String uri = "ssap://system/getSystemInfo"; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, null, true, new ResponseListener<Object>() { @Override public void onSuccess(Object response) { try { JSONObject jsonObj = (JSONObject)response; JSONObject features = (JSONObject) jsonObj.get("features"); Util.postSuccess(listener, features); } catch (JSONException e) { e.printStackTrace(); } } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }); request.send(); } public void secureAccessTest(final SecureAccessTestListener listener) { String uri = "ssap://com.webos.service.secondscreen.gateway/test/secure"; ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { try { JSONObject jsonObj = (JSONObject) response; boolean isSecure = (Boolean) jsonObj.get("returnValue"); Util.postSuccess(listener, isSecure); } catch (JSONException e) { e.printStackTrace(); } } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, null, true, responseListener); request.send(); } public void getACRAuthToken(final ACRAuthTokenListener listener) { String uri = "ssap://tv/getACRAuthToken"; ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { try { JSONObject jsonObj = (JSONObject) response; String authToken = (String) jsonObj.get("token"); Util.postSuccess(listener, authToken); } catch (JSONException e) { e.printStackTrace(); } } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, null, true, responseListener); request.send(); } public void getLaunchPoints(final LaunchPointsListener listener) { String uri = "ssap://com.webos.applicationManager/listLaunchPoints"; ResponseListener<Object> responseListener = new ResponseListener<Object>() { @Override public void onSuccess(Object response) { try { JSONObject jsonObj = (JSONObject) response; JSONArray launchPoints = (JSONArray) jsonObj.get("launchPoints"); Util.postSuccess(listener, launchPoints); } catch (JSONException e) { e.printStackTrace(); } } @Override public void onError(ServiceCommandError error) { Util.postError(listener, error); } }; ServiceCommand<ResponseListener<Object>> request = new ServiceCommand<ResponseListener<Object>>(this, uri, null, true, responseListener); request.send(); } /****************** PLAYLIST CONTROL *****************/ @Override public PlaylistControl getPlaylistControl() { return this; } @Override public CapabilityPriorityLevel getPlaylistControlCapabilityLevel() { return CapabilityPriorityLevel.HIGH; } @Override public void jumpToTrack(long index, ResponseListener<Object> listener) { Util.postError(listener, ServiceCommandError.notSupported()); } @Override public void setPlayMode(PlayMode playMode, ResponseListener<Object> listener) { Util.postError(listener, ServiceCommandError.notSupported()); } @Override public void sendCommand(ServiceCommand<?> command) { if (socket != null) socket.sendCommand(command); } @Override public void unsubscribe(URLServiceSubscription<?> subscription) { if (socket != null) socket.unsubscribe(subscription); } @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); Collections.addAll(capabilities, Launcher.Capabilities); Collections.addAll(capabilities, TVControl.Capabilities); Collections.addAll(capabilities, ExternalInputControl.Capabilities); Collections.addAll(capabilities, VolumeControl.Capabilities); Collections.addAll(capabilities, ToastControl.Capabilities); capabilities.add(PowerControl.Off); } else { Collections.addAll(capabilities, VolumeControl.Capabilities); Collections.addAll(capabilities, MediaPlayer.Capabilities); capabilities.add(Application); capabilities.add(Application_Params); capabilities.add(Application_Close); capabilities.add(Browser); capabilities.add(Browser_Params); capabilities.add(Hulu); capabilities.add(Netflix); capabilities.add(Netflix_Params); capabilities.add(YouTube); capabilities.add(YouTube_Params); capabilities.add(AppStore); capabilities.add(AppStore_Params); capabilities.add(AppState); capabilities.add(AppState_Subscribe); } if (serviceDescription != null && serviceDescription.getVersion() != null) { if (serviceDescription.getVersion().contains("4.0.0") || serviceDescription.getVersion().contains("4.0.1")) { capabilities.add(Launch); capabilities.add(Launch_Params); capabilities.add(Play); capabilities.add(Pause); capabilities.add(Stop); capabilities.add(Seek); capabilities.add(Position); capabilities.add(Duration); capabilities.add(PlayState); capabilities.add(WebAppLauncher.Close); } else { Collections.addAll(capabilities, WebAppLauncher.Capabilities); Collections.addAll(capabilities, MediaControl.Capabilities); capabilities.add(PlaylistControl.JumpToTrack); capabilities.add(PlaylistControl.Next); capabilities.add(PlaylistControl.Previous); capabilities.add(MediaPlayer.Loop); } } setCapabilities(capabilities); } public List<String> getPermissions() { if (permissions != null) return permissions; List<String> defaultPermissions = new ArrayList<String>(); Collections.addAll(defaultPermissions, kWebOSTVServiceOpenPermissions); if (DiscoveryManager.getInstance().getPairingLevel() == PairingLevel.ON) { Collections.addAll(defaultPermissions, kWebOSTVServiceProtectedPermissions); Collections.addAll(defaultPermissions, kWebOSTVServicePersonalActivityPermissions); } permissions = defaultPermissions; return permissions; } public void setPermissions(List<String> permissions) { this.permissions = permissions; WebOSTVServiceConfig config = (WebOSTVServiceConfig) serviceConfig; if (config.getClientKey() != null) { config.setClientKey(null); if (isConnected()) { Log.w(Util.T, "Permissions changed -- you will need to re-pair to the TV."); disconnect(); } } } private ProgramInfo parseRawProgramInfo(JSONObject programRawData) { String programId; String programName; ProgramInfo programInfo = new ProgramInfo(); programInfo.setRawData(programRawData); programId = programRawData.optString("programId"); programName = programRawData.optString("programName"); ChannelInfo channelInfo = parseRawChannelData(programRawData); programInfo.setId(programId); programInfo.setName(programName); programInfo.setChannelInfo(channelInfo); return programInfo; } private ChannelInfo parseRawChannelData(JSONObject channelRawData) { String channelName = null; String channelId = null; String channelNumber = null; int minorNumber; int majorNumber; ChannelInfo channelInfo = new ChannelInfo(); channelInfo.setRawData(channelRawData); try { if (!channelRawData.isNull("channelName")) channelName = (String) channelRawData.get("channelName"); if (!channelRawData.isNull("channelId")) channelId = (String) channelRawData.get("channelId"); channelNumber = channelRawData.optString("channelNumber"); if (!channelRawData.isNull("majorNumber")) majorNumber = (Integer) channelRawData.get("majorNumber"); else majorNumber = parseMajorNumber(channelNumber); if (!channelRawData.isNull("minorNumber")) minorNumber = (Integer) channelRawData.get("minorNumber"); else minorNumber = parseMinorNumber(channelNumber); channelInfo.setName(channelName); channelInfo.setId(channelId); channelInfo.setNumber(channelNumber); channelInfo.setMajorNumber(majorNumber); channelInfo.setMinorNumber(minorNumber); } catch (JSONException e) { e.printStackTrace(); } return channelInfo; } private int parseMinorNumber(String channelNumber) { if (channelNumber != null) { String tokens[] = channelNumber.split("-"); return Integer.valueOf(tokens[tokens.length-1]); } else return 0; } private int parseMajorNumber(String channelNumber) { if (channelNumber != null) { String tokens[] = channelNumber.split("-"); return Integer.valueOf(tokens[0]); } else return 0; } private List<ExternalInputInfo> externalnputInfoFromJSONArray(JSONArray inputList) { List<ExternalInputInfo> externalInputInfoList = new ArrayList<ExternalInputInfo>(); for (int i = 0; i < inputList.length(); i++) { try { JSONObject input = (JSONObject) inputList.get(i); String id = input.getString("id"); String name = input.getString("label"); boolean connected = input.getBoolean("connected"); String iconURL = input.getString("icon"); ExternalInputInfo inputInfo = new ExternalInputInfo(); inputInfo.setRawData(input); inputInfo.setId(id); inputInfo.setName(name); inputInfo.setConnected(connected); inputInfo.setIconURL(iconURL); externalInputInfoList.add(inputInfo); } catch (JSONException e) { e.printStackTrace(); } } return externalInputInfoList; } // @Override // public LaunchSession decodeLaunchSession(String type, JSONObject obj) throws JSONException { // if ("webostv".equals(type)) { // LaunchSession launchSession = LaunchSession.launchSessionFromJSONObject(obj); // launchSession.setService(this); // return launchSession; // } // return null; // } @Override public void getPlayState(PlayStateListener listener) { Util.postError(listener, ServiceCommandError.notSupported()); } @Override public ServiceSubscription<PlayStateListener> subscribePlayState(PlayStateListener listener) { Util.postError(listener, ServiceCommandError.notSupported()); return null; } @Override public boolean isConnectable() { return true; } @Override public void sendPairingKey(String pairingKey) { if (this.socket != null) { this.socket.sendPairingKey(pairingKey); } } public static interface ServiceInfoListener extends ResponseListener<JSONArray> { } public static interface SystemInfoListener extends ResponseListener<JSONObject> { } }