/*
* Copyright (C) 2009 Google Inc. All rights reserved.
*
* 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.google.android.apps.tvremote.protocol;
import java.io.IOException;
import java.net.Socket;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.util.Log;
import com.google.android.apps.tvremote.CoreService;
import com.google.android.apps.tvremote.protocol.AckManager.Listener;
import com.google.anymote.Key.Action;
import com.google.anymote.Key.Code;
import com.google.anymote.Messages.FlingResult;
import com.google.anymote.common.AnymoteFactory;
import com.google.anymote.common.ConnectInfo;
import com.google.anymote.common.ErrorListener;
import com.google.anymote.device.DeviceAdapter;
import com.google.anymote.device.MessageReceiver;
/**
* An implementation of the ICommandSender interface which uses the Anymote
* protocol.
*
*/
public final class AnymoteSender implements ICommandSender {
private final static String LOG_TAG = "AnymoteSender";
/**
* Core service that manages the connection to the server.
*/
private final CoreService coreService;
/**
* Receiver for the Anymote protocol.
*/
private final MessageReceiver receiver;
/**
* Error listener for the Anymote protocol.
*/
private final ErrorListener errorListener;
/**
* Sender for the Anymote protocol.
*/
private DeviceAdapter deviceAdapter;
/**
* The Ack manager.
*/
private final AckManager ackManager;
public AnymoteSender(CoreService service) {
coreService = service;
ackManager = new AckManager(new Listener() {
public void onTimeout() {
onConnectionError();
}
}, this);
receiver = new MessageReceiver() {
public void onAck() {
ackManager.onAck();
}
public void onData(String type, String data) {
Log.d(LOG_TAG, "onData: " + type + " / " + data);
}
// public void onDataList(DataList dataList) {
// Log.d(LOG_TAG, "onDataList: " + dataList.toString());
// }
public void onFlingResult(
FlingResult flingResult, Integer sequenceNumber) {
Log.d(LOG_TAG,
"onFlingResult: " + flingResult.toString() + " " + sequenceNumber);
}
};
errorListener = new ErrorListener() {
public void onIoError(String message, Throwable exception) {
Log.d(LOG_TAG, "IoError: " + message, exception);
onConnectionError();
}
};
}
/**
* Sets the socket the sender will use to communicate with the server.
*
* @param socket the socket to the server
*/
public boolean setSocket(Socket socket) {
if (socket == null) {
throw new NullPointerException("null socket");
}
return instantiateProtocol(socket);
}
public void click(Action action) {
DeviceAdapter sender = getSender();
if (sender != null) {
sender.sendKeyEvent(Code.BTN_MOUSE, action);
}
}
public void flingUrl(String url) {
DeviceAdapter sender = getSender();
if (sender != null) {
sender.sendFling(url, 0);
}
}
public void key(Code keycode, Action action) {
DeviceAdapter sender = getSender();
if (sender != null) {
sender.sendKeyEvent(keycode, action);
}
}
public void keyPress(Code key) {
DeviceAdapter sender = getSender();
if (sender != null) {
sender.sendKeyEvent(key, Action.DOWN);
sender.sendKeyEvent(key, Action.UP);
}
}
public void moveRelative(int deltaX, int deltaY) {
DeviceAdapter sender = getSender();
if (sender != null) {
sender.sendMouseMove(deltaX, deltaY);
}
}
public void scroll(int deltaX, int deltaY) {
DeviceAdapter sender = getSender();
if (sender != null) {
sender.sendMouseWheel(deltaX, deltaY);
}
}
public void string(String text) {
DeviceAdapter sender = getSender();
if (sender != null) {
sender.sendData(ProtocolConstants.DATA_TYPE_STRING, text);
}
}
public void ping() {
DeviceAdapter sender = getSender();
if (sender != null) {
sender.sendPing();
}
}
private void sendConnect() {
DeviceAdapter sender = getSender();
if (sender != null) {
sender.sendConnect(new ConnectInfo(ProtocolConstants.DEVICE_NAME,
getVersionCode()));
}
}
/**
* Called when an error occurs on the transmission.
*/
private void onConnectionError() {
if (disconnect()) {
coreService.notifyConnectionFailed();
}
}
/**
* Instantiates the protocol.
*/
private boolean instantiateProtocol(Socket socket) {
disconnect();
try {
deviceAdapter =
AnymoteFactory.getDeviceAdapter(receiver, socket.getInputStream(),
socket.getOutputStream(), errorListener);
} catch (IOException e) {
Log.d(LOG_TAG, "Unable to create sender", e);
deviceAdapter = null;
return false;
}
sendConnect();
ackManager.start();
return true;
}
/**
* Returns the version number as defined in Android manifest
* {@code versionCode}
*/
private int getVersionCode() {
try {
PackageInfo info = coreService.getPackageManager().getPackageInfo(
coreService.getPackageName(),
0 /* basic info */);
return info.versionCode;
} catch (NameNotFoundException e) {
Log.d(LOG_TAG, "cannot retrieve version number, package name not found");
}
return -1;
}
public synchronized boolean disconnect() {
ackManager.cancel();
if (deviceAdapter != null) {
deviceAdapter.stop();
deviceAdapter = null;
return true;
}
return false;
}
private DeviceAdapter getSender() {
return deviceAdapter;
}
}