/*
* CCNx Android Chat
*
* Copyright (C) 2010 Palo Alto Research Center, Inc.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. You should have received
* a copy of the GNU Lesser General Public License along with this library;
* if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
* Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.ccnx.android.apps.chat;
/**
* Worker thread for ccn Chat. This does all the networking stuff.
* ChatScreen.java is the UI.
*
* Starts CCNx with a blocking call in our thread, then notifies
* the UI when the services are ready.
*/
import java.io.IOException;
import org.ccnx.android.ccnlib.CCNxConfiguration;
import org.ccnx.android.ccnlib.CCNxServiceCallback;
import org.ccnx.android.ccnlib.CCNxServiceControl;
import org.ccnx.android.ccnlib.CCNxServiceStatus.SERVICE_STATUS;
import org.ccnx.android.ccnlib.CcndWrapper.CCND_OPTIONS;
import org.ccnx.android.ccnlib.RepoWrapper.REPO_OPTIONS;
import org.ccnx.ccn.apps.ccnchat.CCNChatNet;
import org.ccnx.ccn.apps.ccnchat.CCNChatNet.CCNChatCallback;
import org.ccnx.ccn.config.ConfigurationException;
import org.ccnx.ccn.profiles.ccnd.CCNDaemonException;
import org.ccnx.ccn.profiles.ccnd.SimpleFaceControl;
import org.ccnx.ccn.protocol.MalformedContentNameStringException;
import android.content.Context;
import android.util.Log;
/**
* All the CCNx code for Chat is in this worker thread. It's basically the code
* from the original ccnChat wrapped inside the worker thread.
*/
public class ChatWorker implements Runnable, CCNxServiceCallback, CCNChatCallback {
protected final static String TAG="ChatWorker";
/**
* Create a worker thread to handle all the CCNx calls.
*
* @param ctx The UI context, needed to start/stop services
* @param callback The UI callback when we receive a chat message or a CCNx service status
*/
public ChatWorker(Context ctx, ChatCallback callback) {
_context = ctx;
_thd = new Thread(this, "ChatWorker");
_chatCallback = callback;
// Use a shared key directory
CCNxConfiguration.config(ctx, false);
}
/**
* Start the worker thread, along with CCN services
* @param username Your "handle" on the Chat
* @param namespace The chat ccnx:/ namespace
* @throws MalformedContentNameStringException
*/
public synchronized void start(String username, String namespace, String remotehost, String remoteport) {
if( false == _running ) {
try {
_remotehost = remotehost;
_remoteport = remoteport;
_chat = new CCNChatNet(this, namespace);
_running = true;
_finished = false;
_thd.start();
} catch(Exception e) {
e.printStackTrace();
}
}
}
/**
* Exit the worker thread, but keep services running
*/
public synchronized void stop() {
if( !_finished ) {
_finished = true;
try {
_chat.shutdown();
} catch (IOException e) {
e.printStackTrace();
}
_ccnxService.disconnect();
}
}
/**
* Exit the worker thread and shutdown services
*/
public synchronized void shutdown() {
if( !_finished ) {
_finished = true;
try {
_chat.shutdown();
} catch (IOException e) {
e.printStackTrace();
}
try {
_ccnxService.stopAll();
} catch(Exception e) {
e.printStackTrace();
}
}
}
/**
* Sent a chat message to the network
* @param text
* @return true if sent, false if some CCN error
*/
public synchronized boolean send(String text) {
Log.d(TAG, "send text = " + text);
try {
_chat.sendMessage(text);
} catch(Exception e) {
return false;
}
return true;
}
/**
* Runnable method
*/
@Override
public void run() {
service_run();
}
// ==============================================================================
// Internal implementation
protected CCNChatNet _chat;
protected final ChatCallback _chatCallback;
protected final Context _context;
protected CCNxServiceControl _ccnxService;
protected final Thread _thd;
protected boolean _running = false;
protected boolean _finished = true;
protected String _remotehost = null;
protected String _remoteport = "9695";
/*********************************************/
// These are all run in the CCN thread
/**
* @param args
*/
protected void service_run() {
// Startup CCNx in a blocking call
if( !initializeCCNx() ) {
Log.e(TAG, "Could not start CCNx services!");
} else {
Log.i(TAG,"Starting ccnChatNet.listen() loop");
// Now do the Chat event loop
try {
_chat.listen();
} catch (ConfigurationException e) {
System.err.println("Configuration exception running ccnChat: "
+ e.getMessage());
e.printStackTrace();
} catch (IOException e) {
System.err.println("IOException handling chat messages: "
+ e.getMessage());
e.printStackTrace();
} catch(Exception e) {
System.err.println("Exception handling chat messages: "
+ e.getMessage());
e.printStackTrace();
}
}
Log.i(TAG, "service_run() exits");
}
private boolean initializeCCNx() {
_ccnxService = new CCNxServiceControl(_context);
_ccnxService.registerCallback(this);
_ccnxService.setCcndOption(CCND_OPTIONS.CCND_DEBUG, "1");
_ccnxService.setRepoOption(REPO_OPTIONS.REPO_DEBUG, "WARNING");
return _ccnxService.startAll();
}
/**
* Called from CCNxServiceControl
*/
@Override
public void newCCNxStatus(SERVICE_STATUS st) {
// NOw pass on the status to the app
if( null != _chatCallback ) {
switch(st) {
case START_ALL_DONE:
try {
// If we specified a remote host, use it not multicast
if( null != _remotehost && _remotehost.length() > 0 ) {
SimpleFaceControl.getInstance().connectTcp(_remotehost, Integer.parseInt(_remoteport));
} else {
SimpleFaceControl.getInstance().openMulicastInterface();
}
_chatCallback.ccnxServices(true);
} catch (CCNDaemonException e) {
// TODO Auto-generated catch block
e.printStackTrace();
_chatCallback.ccnxServices(false);
}
break;
case START_ALL_ERROR:
_chatCallback.ccnxServices(false);
break;
}
}
}
/**
* called from ccnChatNet when there's a new message.
* Pass it on to the UI.
*/
@Override
public void recvMessage(String message) {
Log.d(TAG, "recv text = " + message);
_chatCallback.recv(message);
}
}