/*
* Copyright (C) 2010 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 android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.util.Log;
import com.google.anymote.Key.Action;
import com.google.anymote.Key.Code;
/**
* An implementation of the ICommand sender that lies on top of the core
* service and sends commands with a separate thread.
*
*/
public final class QueuingSender implements ICommandSender {
private static final String LOG_TAG = "QueuingSender";
/**
* Buffered command that is will be sent when connected.
*/
private Command bufferedCommand;
private final Handler handler;
/**
* Action that should be taken when sender is missing.
*/
private enum MissingAction {
/**
* Do nothing.
*/
IGNORE,
/**
* Missing action listener should be notified.
*/
NOTIFY,
/**
* Command should be enqueued for future execution.
*/
ENQUEUE
}
/**
* Listener that will be notified about attempt of sending an event when no
* sender is configured.
*/
public interface MissingSenderListener {
public void onMissingSender();
}
/**
* The remote service through which commands should be sent.
*/
private ICommandSender sender;
private final MissingSenderListener missingSenderListener;
public QueuingSender(MissingSenderListener listener) {
missingSenderListener = listener;
HandlerThread handlerThread = new HandlerThread("Sender looper");
handlerThread.start();
handler = new Handler(handlerThread.getLooper(), new Handler.Callback() {
public boolean handleMessage(Message msg) {
Command command = (Command) msg.obj;
ICommandSender currentSender = sender;
if (currentSender != null) {
command.execute(currentSender);
} else {
Log.w(LOG_TAG, "Sender removed before sending command");
}
return true;
}
});
}
public synchronized void setSender(ICommandSender sender) {
this.sender = sender;
if (sender != null) {
flushBufferedEvents();
}
}
private boolean hasSender() {
return sender != null;
}
private synchronized void sendCommand(Command command,
MissingAction actionIfMissing) {
if (!hasSender()) {
switch (actionIfMissing) {
case IGNORE:
break;
case ENQUEUE:
bufferedCommand = command;
break;
case NOTIFY:
missingSenderListener.onMissingSender();
break;
default:
throw new IllegalStateException("Unsupported action: "
+ actionIfMissing);
}
return;
}
Message msg = handler.obtainMessage(0, command);
handler.sendMessage(msg);
}
private void sendCommand(Command command) {
sendCommand(command, MissingAction.NOTIFY);
}
public void flingUrl(String url) {
sendCommand(Commands.buildFlingUrlCommand(url), MissingAction.ENQUEUE);
}
public void key(Code keycode, Action action) {
sendCommand(Commands.buildKeyCommand(keycode, action));
}
public void keyPress(Code key) {
sendCommand(Commands.buildKeyPressCommand(key));
}
public void moveRelative(int deltaX, int deltaY) {
sendCommand(Commands.buildMoveCommand(deltaX, deltaY));
}
public void scroll(int deltaX, int deltaY) {
sendCommand(Commands.buildScrollCommand(deltaX, deltaY));
}
public void string(String text) {
sendCommand(Commands.buildStringCommand(text));
}
/**
* Sends buffered events (currently last flinged URI is buffered).
*/
private void flushBufferedEvents() {
if (!hasSender()) {
throw new IllegalStateException("No sender is set.");
}
if (bufferedCommand != null) {
sendCommand(bufferedCommand, MissingAction.IGNORE);
bufferedCommand = null;
}
}
}