/*
* NetcastHttpServer
* 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.netcast;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.json.JSONException;
import org.json.JSONObject;
import org.xml.sax.SAXException;
import android.util.Log;
import com.connectsdk.core.ChannelInfo;
import com.connectsdk.core.TextInputStatusInfo;
import com.connectsdk.core.Util;
import com.connectsdk.service.NetcastTVService;
import com.connectsdk.service.capability.listeners.ResponseListener;
import com.connectsdk.service.command.URLServiceSubscription;
public class NetcastHttpServer {
static final String UDAP_PATH_EVENT = "/udap/api/event";
NetcastTVService service;
ServerSocket welcomeSocket;
ResponseListener<String> textChangedListener;
int port = -1;
List<URLServiceSubscription<?>> subscriptions;
boolean running = false;
public NetcastHttpServer(NetcastTVService service, int port, ResponseListener<String> textChangedListener) {
this.service = service;
this.port = port;
this.textChangedListener = textChangedListener;
}
public void start() {
//TODO: this method is too complex and should be refactored
if (running)
return;
running = true;
try {
welcomeSocket = new ServerSocket(this.port);
} catch (IOException ex) {
ex.printStackTrace();
}
while (running) {
if (welcomeSocket == null || welcomeSocket.isClosed()) {
stop();
break;
}
Socket connectionSocket = null;
BufferedReader inFromClient = null;
DataOutputStream outToClient = null;
try {
connectionSocket = welcomeSocket.accept();
} catch (IOException ex) {
ex.printStackTrace();
// this socket may have been closed, so we'll stop
stop();
return;
}
String str = null;
int c;
StringBuilder sb = new StringBuilder();
try {
inFromClient = new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));
while ((str = inFromClient.readLine()) != null) {
if (str.equals("")) {
break;
}
}
while ((c = inFromClient.read()) != -1) {
sb.append((char)c);
String temp = sb.toString();
if (temp.endsWith("</envelope>"))
break;
}
} catch (IOException ex) {
ex.printStackTrace();
}
String body = sb.toString();
Log.d(Util.T, "got message body: " + body);
Calendar calendar = Calendar.getInstance();
SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
String date = dateFormat.format(calendar.getTime());
String androidOSVersion = android.os.Build.VERSION.RELEASE;
PrintWriter out = null;
try {
outToClient = new DataOutputStream(connectionSocket.getOutputStream());
out = new PrintWriter(outToClient);
out.println("HTTP/1.1 200 OK");
out.println("Server: Android/" + androidOSVersion + " UDAP/2.0 ConnectSDK/1.2.1");
out.println("Cache-Control: no-store, no-cache, must-revalidate");
out.println("Date: " + date);
out.println("Connection: Close");
out.println("Content-Length: 0");
out.println();
out.flush();
} catch (IOException ex) {
ex.printStackTrace();
} finally {
try {
inFromClient.close();
out.close();
outToClient.close();
connectionSocket.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
InputStream stream = null;
try {
stream = new ByteArrayInputStream(body.getBytes("UTF-8"));
} catch (UnsupportedEncodingException ex) {
ex.printStackTrace();
}
NetcastPOSTRequestParser handler = new NetcastPOSTRequestParser();
SAXParser saxParser;
try {
saxParser = saxParserFactory.newSAXParser();
saxParser.parse(stream, handler);
} catch (IOException ex) {
ex.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
}
if (body.contains("ChannelChanged")) {
ChannelInfo channel = NetcastChannelParser.parseRawChannelData(handler.getJSONObject());
Log.d(Util.T, "Channel Changed: " + channel.getNumber());
for (URLServiceSubscription<?> sub: subscriptions) {
if (sub.getTarget().equalsIgnoreCase("ChannelChanged")) {
for (int i = 0; i < sub.getListeners().size(); i++) {
@SuppressWarnings("unchecked")
ResponseListener<Object> listener = (ResponseListener<Object>) sub.getListeners().get(i);
Util.postSuccess(listener, channel);
}
}
}
}
else if (body.contains("KeyboardVisible")) {
boolean focused = false;
TextInputStatusInfo keyboard = new TextInputStatusInfo();
keyboard.setRawData(handler.getJSONObject());
try {
JSONObject currentWidget = (JSONObject) handler.getJSONObject().get("currentWidget");
focused = (Boolean) currentWidget.get("focus");
keyboard.setFocused(focused);
} catch (JSONException e) {
e.printStackTrace();
}
Log.d(Util.T, "KeyboardFocused?: " + focused);
for (URLServiceSubscription<?> sub: subscriptions) {
if (sub.getTarget().equalsIgnoreCase("KeyboardVisible")) {
for (int i = 0; i < sub.getListeners().size(); i++) {
@SuppressWarnings("unchecked")
ResponseListener<Object> listener = (ResponseListener<Object>) sub.getListeners().get(i);
Util.postSuccess(listener, keyboard);
}
}
}
}
else if (body.contains("TextEdited")) {
System.out.println("TextEdited");
String newValue = "";
try {
newValue = handler.getJSONObject().getString("value");
} catch (JSONException ex) {
ex.printStackTrace();
}
Util.postSuccess(textChangedListener, newValue);
}
else if (body.contains("3DMode")) {
try {
String enabled = (String) handler.getJSONObject().get("value");
boolean bEnabled;
bEnabled = enabled.equalsIgnoreCase("true");
for (URLServiceSubscription<?> sub: subscriptions) {
if (sub.getTarget().equalsIgnoreCase("3DMode")) {
for (int i = 0; i < sub.getListeners().size(); i++) {
@SuppressWarnings("unchecked")
ResponseListener<Object> listener = (ResponseListener<Object>) sub.getListeners().get(i);
Util.postSuccess(listener, bEnabled);
}
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
public void stop() {
if (!running)
return;
if (welcomeSocket != null && !welcomeSocket.isClosed()) {
try {
welcomeSocket.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
welcomeSocket = null;
running = false;
}
public void setSubscriptions(List<URLServiceSubscription<?>> subscriptions) {
this.subscriptions = subscriptions;
}
}