Android Storing Socket.io object for multiple activities - android

I am making my first Socket.io based android application. The socket sends and receives data from a web service. There are a number of screens in the application for different features. How do i use the same socket connection in these different activities.
I have tried setting and storing the Socket Object in the Application class and it appears to work well but when the application goes into the background and left there for some time the application is killed and the socket object is then NULL causing the aoo to crash with a null pointer exception.
public class MyApplication extends Application {
private Socket mSocket;
private final String TAG = "Application Class";
public Socket getSocket() {
return mSocket;
}
public Socket createSocket() {
try {
Manager manager = new Manager(new URI("http://0.0.0.0"));
} catch (URISyntaxException URIse) {
URIse.printStackTrace();
}
return mSocket;
}
}
Access the socket in the activities
MyApplication app;
app = (MyApplication ) getApplication();
app.getSocket;

You can create a singleton manager class for socket. It will allow you to keep single socket connection accessible to entire app. See following code and change it according to your requirement
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
import android.util.Log;
import com.myapp.app.ui.adapter.OnSocketConnectionListener;
import java.util.ArrayList;
import java.util.List;
import io.socket.client.IO;
import io.socket.client.Socket;
import io.socket.emitter.Emitter;
/**
* Created by penguin on 6/30/2016.
* <p/>
* SocketManager manages socket and internet connection.
* It also provide listeners for connection status
*/
public class SocketManager {
/**
* The constant STATE_CONNECTING.
*/
public static final int STATE_CONNECTING = 1;
/**
* The constant STATE_CONNECTED.
*/
public static final int STATE_CONNECTED = 2;
/**
* The constant STATE_DISCONNECTED.
*/
public static final int STATE_DISCONNECTED = 3;
/**
* The constant CONNECTING.
*/
public static final String CONNECTING = "Connecting";
/**
* The constant CONNECTED.
*/
public static final String CONNECTED = "Connected";
/**
* The constant DISCONNECTED.
*/
public static final String DISCONNECTED = "Disconnected";
private static SocketManager instance;
private SocketManager() {
}
/**
* Gets instance.
*
* #return the instance
*/
public synchronized static SocketManager getInstance() {
if (instance == null) {
instance = new SocketManager();
}
return instance;
}
/**
* The constant TAG.
*/
public static final String TAG = SocketManager.class.getSimpleName();
private Socket socket;
private List<OnSocketConnectionListener> onSocketConnectionListenerList;
/**
* Connect socket.
*
* #param token the token
* #param userId the user id
* #param host the host
* #param port the port
*/
public void connectSocket(String token,String userId, String host, String port) {
try {
if(socket==null){
String serverAddress = host+":"+port;
IO.Options opts = new IO.Options();
opts.forceNew = true;
opts.reconnection = true;
opts.reconnectionAttempts=5;
opts.secure = true;
opts.query = "token=" + token + "&" + "user_id=" + userId;
socket = IO.socket(serverAddress, opts);
socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
#Override
public void call(Object... args) {
fireSocketStatus(SocketManager.STATE_CONNECTED);
Log.i(TAG, "socket connected");
}
}).on(Socket.EVENT_RECONNECTING, new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.e(TAG, "Socket reconnecting");
fireSocketStatus(SocketManager.STATE_CONNECTING);
}
}).on(Socket.EVENT_RECONNECT_FAILED, new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.e(TAG, "Socket reconnection failed");
// fireSocketStatusIntent(SocketManager.STATE_DISCONNECTED);
}
}).on(Socket.EVENT_RECONNECT_ERROR, new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.e(TAG, "Socket reconnection error");
// fireSocketStatus(SocketManager.STATE_DISCONNECTED);
}
}).on(Socket.EVENT_CONNECT_ERROR, new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.e(TAG, "Socket connect error");
fireSocketStatus(SocketManager.STATE_DISCONNECTED);
socket.disconnect();
}
}).on(Socket.EVENT_DISCONNECT, new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.e(TAG, "Socket disconnect event");
fireSocketStatus(SocketManager.STATE_DISCONNECTED);
}
}).on(Socket.EVENT_ERROR, new Emitter.Listener() {
#Override
public void call(Object... args) {
try {
final String error = (String) args[0];
Log.e(TAG + " error EVENT_ERROR ", error);
if (error.contains("Unauthorized") && !socket.connected()) {
if (onSocketConnectionListenerList != null) {
for (final OnSocketConnectionListener listener : onSocketConnectionListenerList) {
new Handler(Looper.getMainLooper())
.post(new Runnable() {
#Override
public void run() {
listener.onSocketEventFailed();
}
});
}
}
}
} catch (Exception e) {
Log.e(TAG, e.getMessage() != null ? e.getMessage() : "");
}
}
}).on("Error", new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.d(TAG, " Error");
}
});
socket.connect();
}else if(!socket.connected()){
socket.connect();
}
} catch (Exception e) {
e.printStackTrace();
}
}
private int lastState = -1;
/**
* Fire socket status intent.
*
* #param socketState the socket state
*/
public synchronized void fireSocketStatus(final int socketState) {
if(onSocketConnectionListenerList !=null && lastState!=socketState){
lastState = socketState;
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
for(OnSocketConnectionListener listener: onSocketConnectionListenerList){
listener.onSocketConnectionStateChange(socketState);
}
}
});
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
#Override
public void run() {
lastState=-1;
}
},1000);
}
}
/**
* Fire internet status intent.
*
* #param socketState the socket state
*/
public synchronized void fireInternetStatusIntent(final int socketState) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
if(onSocketConnectionListenerList !=null){
for(OnSocketConnectionListener listener: onSocketConnectionListenerList){
listener.onInternetConnectionStateChange(socketState);
}
}
}
});
}
/**
* Gets socket.
*
* #return the socket
*/
public Socket getSocket() {
return socket;
}
/**
* Sets socket.
*
* #param socket the socket
*/
public void setSocket(Socket socket) {
this.socket = socket;
}
/**
* Destroy.
*/
public void destroy(){
if (socket != null) {
socket.off();
socket.disconnect();
socket.close();
socket=null;
}
}
/**
* Sets socket connection listener.
*
* #param onSocketConnectionListenerListener the on socket connection listener listener
*/
public void setSocketConnectionListener(OnSocketConnectionListener onSocketConnectionListenerListener) {
if(onSocketConnectionListenerList ==null){
onSocketConnectionListenerList = new ArrayList<>();
onSocketConnectionListenerList.add(onSocketConnectionListenerListener);
}else if(!onSocketConnectionListenerList.contains(onSocketConnectionListenerListener)){
onSocketConnectionListenerList.add(onSocketConnectionListenerListener);
}
}
/**
* Remove socket connection listener.
*
* #param onSocketConnectionListenerListener the on socket connection listener listener
*/
public void removeSocketConnectionListener(OnSocketConnectionListener onSocketConnectionListenerListener) {
if(onSocketConnectionListenerList !=null
&& onSocketConnectionListenerList.contains(onSocketConnectionListenerListener)){
onSocketConnectionListenerList.remove(onSocketConnectionListenerListener);
}
}
/**
* Remove all socket connection listener.
*/
public void removeAllSocketConnectionListener() {
if(onSocketConnectionListenerList !=null){
onSocketConnectionListenerList.clear();
}
}
/**
* The type Net receiver.
*/
public static class NetReceiver extends BroadcastReceiver {
/**
* The Tag.
*/
public final String TAG = NetReceiver.class.getSimpleName();
#Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager cm =
(ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isConnected = activeNetwork != null &&
activeNetwork.isConnectedOrConnecting();
SocketManager.getInstance().fireInternetStatusIntent(
isConnected?SocketManager.STATE_CONNECTED:SocketManager.STATE_DISCONNECTED);
if (isConnected) {
if(SocketManager.getInstance().getSocket()!=null
&& !SocketManager.getInstance().getSocket().connected()){
SocketManager.getInstance().fireSocketStatus(SocketManager.STATE_CONNECTING);
}
PowerManager powerManager =
(PowerManager) context.getSystemService(Context.POWER_SERVICE);
boolean isScreenOn;
if (android.os.Build.VERSION.SDK_INT
>= android.os.Build.VERSION_CODES.KITKAT_WATCH) {
isScreenOn = powerManager.isInteractive();
}else{
//noinspection deprecation
isScreenOn = powerManager.isScreenOn();
}
if (isScreenOn && SocketManager.getInstance().getSocket() !=null) {
Log.d(TAG, "NetReceiver: Connecting Socket");
if(!SocketManager.getInstance().getSocket().connected()){
SocketManager.getInstance().getSocket().connect();
}
}
}else{
SocketManager.getInstance().fireSocketStatus(SocketManager.STATE_DISCONNECTED);
if(SocketManager.getInstance().getSocket() !=null){
Log.d(TAG, "NetReceiver: disconnecting socket");
SocketManager.getInstance().getSocket().disconnect();
}
}
}
}
}
Connecting socket
You can connecting/disconnect socket from any activity or background service
SocketManager.getInstance().connectSocket(user.getToken(), user.getUserId(),
getResources().getString(R.string.host), "8000");
Update
If in background your app is killed socket will also destroy. If you want socket to remain connected in background you have to make you own logic with background service which has nothing to do with socket.
implement OnSocketConnectionListener
public interface OnSocketConnectionListener {
void onSocketEventFailed();
void onSocketConnectionStateChange(int socketState);
void onInternetConnectionStateChange(int socketState);
}

Related

How to setup an Android Socket.io client?

After i have made my SOCKET.IO server for a multiple room chat application
how do i create the android client using https://github.com/socketio/socket.io-client-java ?
I have searched a lot and haven`t found up to date examples on the client side of socket.io for android , most of the tutorials and examples are for the server side with node.js.
private static NetworkManager mInstance;
private static NetworkInterface mNetworkInterface;
private Socket mSocket;
private int RECONNECTION_ATTEMPT = 10;
private long CONNECTION_TIMEOUT = 30000;
/**
* The purpose of this method is to get the call back for any type of connection error
*/
private Emitter.Listener testing = new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.e("Response", args[0].toString());
}
};
/**
* The purpose of this method is to get the call back for any type of connection error
*/
private Emitter.Listener onConnectionError = new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.e("Response", "onConnectionError");
mNetworkInterface.networkCallReceive(NetworkSocketConstant.SERVER_CONNECTION_ERROR, args);
}
};
/**
* The purpose of this method to get the call back for connection getting timed out
*/
private Emitter.Listener onConnectionTimeOut = new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.e("Response", "onConnectionTimeOut");
mNetworkInterface.networkCallReceive(NetworkSocketConstant.SERVER_CONNECTION_TIMEOUT, args);
}
};
/**
* The purpose of this method is to receive the call back when the server get connected
*/
private Emitter.Listener onServerConnect = new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.e("Response", "onServerConnected");
mNetworkInterface.networkCallReceive(NetworkSocketConstant.SERVER_CONNECTED, args);
}
};
/**
* The purpose of this method is to receive the call back when the server get disconnected
*/
private Emitter.Listener onServerDisconnect = new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.e("Response", "onServerDisconnection");
mNetworkInterface.networkCallReceive(NetworkSocketConstant.SERVER_DISCONNECTED, args);
}
};
public static NetworkManager getInstance(Context context) {
if (mInstance == null) {
mInstance = new NetworkManager();
}
return mInstance;
}
public void registerInterface(NetworkInterface interfaces) {
mNetworkInterface = interfaces;
}
/**
* The purpose of this method to create the socket object
*/
public void connectToSocket() {
try {
IO.Options opts = new IO.Options();
opts.timeout = CONNECTION_TIMEOUT;
opts.reconnection = true;
opts.reconnectionAttempts = RECONNECTION_ATTEMPT;
opts.reconnectionDelay = 1000;
opts.forceNew = true;
mSocket = IO.socket(NetworkSocketConstant.SOCKET_CONNECTION_URL, opts);
makeConnection();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* The purpose of the method is to return the instance of socket
*
* #return
*/
public Socket getSocket() {
return mSocket;
}
/**
* The purpose of this method is to connect with the socket
*/
public void makeConnection() {
if (mSocket != null) {
registerConnectionAttributes();
mSocket.connect();
}
}
/**
* The purpose of this method is to disconnect from the socket interface
*/
public void disconnectFromSocket() {
unregisterConnectionAttributes();
if (mSocket != null) {
mSocket.disconnect();
mSocket = null;
mInstance = null;
BaseApplicationActivty.networkManager = null;
}
}
/**
* The purpose of this method is to register default connection attributes
*/
public void registerConnectionAttributes() {
try {
if (mSocket != null) {
mSocket.on(Socket.EVENT_CONNECT_ERROR, onConnectionError);
mSocket.on(Socket.EVENT_CONNECT_TIMEOUT, onConnectionTimeOut);
mSocket.on(Socket.EVENT_DISCONNECT, onServerDisconnect);
mSocket.on(Socket.EVENT_CONNECT, onServerConnect);
registerHandlers();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* The purpose of this method is to unregister default connection attributes
*/
public void unregisterConnectionAttributes() {
try {
if (mSocket != null) {
mSocket.off(Socket.EVENT_CONNECT_ERROR, onConnectionError);
mSocket.off(Socket.EVENT_CONNECT_TIMEOUT, onConnectionTimeOut);
mSocket.off(Socket.EVENT_DISCONNECT, onServerDisconnect);
mSocket.off(Socket.EVENT_CONNECT, onServerConnect);
unRegisterHandlers();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* The purpose of this method is to unregister the connection from the socket
*/
private void unRegisterHandlers() {
try {
//unregister your all methods here
mSocket.off("hello", testing);
mSocket.off("android", testing);
mSocket.off("hello2", testing);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* The purpose of this method is to register the connection from the socket
*/
private void registerHandlers() {
try {
//register you all method here
mSocket.on("hello", testing);
mSocket.on("android", testing);
mSocket.on("hello2", testing);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* The purpose of this method is register a single method on server
*
* #param methodOnServer
* #param handlerName
*/
public void registerHandler(String methodOnServer, Emitter.Listener handlerName) {
try {
if (mSocket != null) {
mSocket.on(methodOnServer, handlerName);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* The purpose of this method is to unregister a single method from server
*
* #param methodOnServer
* #param handlerName
*/
public void unRegisterHandler(String methodOnServer, Emitter.Listener handlerName) {
try {
if (mSocket != null) {
mSocket.off(methodOnServer, handlerName);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* The purpose of this method is to send the data to the server
*
* #param methodOnServer
* #param request
*/
public void sendDataToServer(String methodOnServer, JsonObject request) {
try {
if (mSocket != null && mSocket.connected()) {
Log.e("JSON ", request.toString());
mSocket.emit(methodOnServer, request);
} else {
mNetworkInterface.networkCallReceive(NetworkSocketConstant.SERVER_CONNECTION_ERROR);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public interface NetworkInterface {
void networkCallReceive(int responseType, Object... args);
}
public abstract class BaseActivity extends AppCompatActivity {
/**
* The purpose of this method is to receive the callback from the server
*/
private NetworkManager.NetworkInterface networkInterface = new NetworkManager.NetworkInterface() {
#Override
public void networkCallReceive(int responseType, Object... args) {
switch (responseType) {
case NetworkSocketConstant.SERVER_CONNECTION_TIMEOUT:
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(BaseActivity.this, getResources().getString(R.string.sorry_no_internet_connection), Toast.LENGTH_SHORT).show();
}
});
break;
case NetworkSocketConstant.SERVER_CONNECTION_ERROR:
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(BaseActivity.this, (getResources().getString(R.string.connection_error)), Toast.LENGTH_SHORT).show();
}
});
break;
case NetworkSocketConstant.SERVER_CONNECTED:
SharedPreferenceUtility sharedPreferenceUtility = new SharedPreferenceUtility(BaseActivity.this);
if (sharedPreferenceUtility.getUserId() != null && !sharedPreferenceUtility.getUserId().isEmpty()) {
SocketEmitter.emitSocketConnectionService(BaseActivity.this);
SocketEmitter.emitTesting(BaseActivity.this, "android");
}
break;
case NetworkSocketConstant.SERVER_DISCONNECTED:
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(BaseActivity.this, getResources().getString(R.string.disconnected), Toast.LENGTH_SHORT).show();
}
});
break;
}
};
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
socketInit();
}
// initialization socket
private void socketInit() {
if (BaseApplicationActivty.networkManager == null) {
BaseApplicationActivty.networkManager = NetworkManager.getInstance(getApplicationContext());
BaseApplicationActivty.networkManager.registerInterface(networkInterface);
BaseApplicationActivty.networkManager.connectToSocket();
} else {
BaseApplicationActivty.networkManager.registerInterface(networkInterface);
}
}
/**
* This Method must call all ui fragment/activity using Rx subs.
*/
protected abstract void onPermissionResult(int requestCode, boolean isPermissionGranted);
/**
* This Method must call all ui fragment/activity using Rx subs.
*/
protected abstract void onSocketApiResult(int requestCode, Object... args);
/**
* Request For Permission of particular type.
*
* #param requestCode
* #param permission
* #return
*/
public boolean requestPermission(int requestCode, String... permission) {
boolean isAlreadyGranted = false;
isAlreadyGranted = checkPermission(permission);
if (!isAlreadyGranted)
ActivityCompat.requestPermissions(this, permission, requestCode);
return isAlreadyGranted;
}
protected boolean checkPermission(String[] permission) {
boolean isPermission = true;
for (String s : permission)
isPermission = isPermission && ContextCompat.checkSelfPermission(this, s) == PackageManager.PERMISSION_GRANTED;
return isPermission;
}
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
onPermissionResult(requestCode, true);
} else {
onPermissionResult(requestCode, false);
Toast.makeText(this, R.string.permission_denied, Toast.LENGTH_LONG).show();
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
}
}
it has an example chat app implemented in android link below:
https://github.com/nkzawa/socket.io-android-chat
any way, for using socket.io in android, i think best way is using it inside a service something like this:
public class ChatService extends Service {
public void connectSocket() {
try {
IO.Options options = new IO.Options();
socket = IO.socket("http://192.168.1.1:8080", options);
socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
#Override
public void call(Object... args) {
}
}).on(Socket.EVENT_DISCONNECT, new Emitter.Listener() {
#Override
public void call(Object... args) {
}
}).on("error", new Emitter.Listener() {
#Override
public void call(Object... args) {
}
});
socket.connect();
} catch (Exception ignored) {
}
}
#Override
public void onDestroy() {
socket.disconnect();
super.onDestroy();
}
}

Android - how to run a socket in a long lives background thread

I'm building a simple chat that connects between an android client and a java server (running on my pc). The user can send and receive messages to/from the android app and the desktop server.
I'm dealing now with the question of how to run the client-socket in a different thread than the UI Thread.
I saw solutions using AsyncTask, but as the user may communicate using the app for a long sequential time, AsyncTask looks like a bad approach.
AsyncTasks should ideally be used for short operations (a few seconds at the most.) API
Because i need the client socket to consistently listen for messages from the desktop server, i thought of creating new Thread receiving a Runnable implementing class.
My questions
1. In which "thread mechanism" to place the client socket rows (Thread, IntentService)?
Socket client = new Socket(host, port);
InputStreamReader inputStreamReader = new InputStreamReader(client.getInputStream());
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
while ((messageFromServer = bufferedReader.readLine()) != null) { //... }
2. How can the client socket (running from a different thread than the main thread) post the messageFromServer to an TextView?
How will i send the user messages from the app to the server (using the client-socket ofcourse), upon user entering text and clicking a button?
Thanks!
I've created a similar app and I used a Service which runs in the background.
I've copied the code from the IntentService clas and updated handleMessage(Message msg) method and removed stopSelf(msg.arg1); line. In this way you have a service that runs in the background. After it I used a Thread for the connection.
You have two choices here. Store the data into the database and the GUI refreshes itself. Or use LocalBroadcastManager.
Here you can also store the data into the db or Start the service with a special intent.
Here is my implementation. I hope you will understand the code.
public class KeepAliveService extends Service {
/**
* The source of the log message.
*/
private static final String TAG = "KeepAliveService";
private static final long INTERVAL_KEEP_ALIVE = 1000 * 60 * 4;
private static final long INTERVAL_INITIAL_RETRY = 1000 * 10;
private static final long INTERVAL_MAXIMUM_RETRY = 1000 * 60 * 2;
private ConnectivityManager mConnMan;
protected NotificationManager mNotifMan;
protected AlarmManager mAlarmManager;
private boolean mStarted;
private boolean mLoggedIn;
protected static ConnectionThread mConnection;
protected static SharedPreferences mPrefs;
private final int maxSize = 212000;
private Handler mHandler;
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private final class ServiceHandler extends Handler {
public ServiceHandler(final Looper looper) {
super(looper);
}
#Override
public void handleMessage(final Message msg) {
onHandleIntent((Intent) msg.obj);
}
}
public static void actionStart(final Context context) {
context.startService(SystemHelper.createExplicitFromImplicitIntent(context, new Intent(IntentActions.KEEP_ALIVE_SERVICE_START)));
}
public static void actionStop(final Context context) {
context.startService(SystemHelper.createExplicitFromImplicitIntent(context, new Intent(IntentActions.KEEP_ALIVE_SERVICE_STOP)));
}
public static void actionPing(final Context context) {
context.startService(SystemHelper.createExplicitFromImplicitIntent(context, new Intent(IntentActions.KEEP_ALIVE_SERVICE_PING_SERVER)));
}
#Override
public void onCreate() {
Log.i(TAG, "onCreate called.");
super.onCreate();
mPrefs = getSharedPreferences("KeepAliveService", MODE_PRIVATE);
mConnMan = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
mNotifMan = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
mHandler = new Handler();
final HandlerThread thread = new HandlerThread("IntentService[KeepAliveService]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
// If our process was reaped by the system for any reason we need to
// restore our state with merely a
// call to onCreate.
// We record the last "started" value and restore it here if necessary.
handleCrashedService();
}
#Override
public void onDestroy() {
Log.i(TAG, "Service destroyed (started=" + mStarted + ")");
if (mStarted) {
stop();
}
mServiceLooper.quit();
}
private void handleCrashedService() {
Log.i(TAG, "handleCrashedService called.");
if (isStarted()) {
// We probably didn't get a chance to clean up gracefully, so do it now.
stopKeepAlives();
// Formally start and attempt connection.
start();
}
}
/**
* Returns the last known value saved in the database.
*/
private boolean isStarted() {
return mStarted;
}
private void setStarted(final boolean started) {
Log.i(TAG, "setStarted called with value: " + started);
mStarted = started;
}
protected void setLoggedIn(final boolean value) {
Log.i(TAG, "setLoggedIn called with value: " + value);
mLoggedIn = value;
}
protected boolean isLoggedIn() {
return mLoggedIn;
}
public static boolean isConnected() {
return mConnection != null;
}
#Override
public void onStart(final Intent intent, final int startId) {
final Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
#Override
public int onStartCommand(final Intent intent, final int flags, final int startId) {
Log.i(TAG, "Service started with intent : " + intent);
onStart(intent, startId);
return START_NOT_STICKY;
}
private void onHandleIntent(final Intent intent) {
if (IntentActions.KEEP_ALIVE_SERVICE_STOP.equals(intent.getAction())) {
stop();
stopSelf();
} else if (IntentActions.KEEP_ALIVE_SERVICE_START.equals(intent.getAction())) {
start();
} else if (IntentActions.KEEP_ALIVE_SERVICE_PING_SERVER.equals(intent.getAction())) {
keepAlive(false);
}
}
#Override
public IBinder onBind(final Intent intent) {
return null;
}
private synchronized void start() {
if (mStarted) {
Log.w(TAG, "Attempt to start connection that is already active");
setStarted(true);
return;
}
try {
registerReceiver(mConnectivityChanged, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
} catch (final Exception e) {
Log.e(TAG, "Exception occurred while trying to register the receiver.", e);
}
if (mConnection == null) {
Log.i(TAG, "Connecting...");
mConnection = new ConnectionThread(Config.PLUGIN_BASE_HOST, Config.PLUGIN_BASE_PORT);
mConnection.start();
}
}
private synchronized void stop() {
if (mConnection != null) {
mConnection.abort(true);
mConnection = null;
}
setStarted(false);
try {
unregisterReceiver(mConnectivityChanged);
} catch (final Exception e) {
Log.e(TAG, "Exception occurred while trying to unregister the receiver.", e);
}
cancelReconnect();
}
/**
* Sends the keep-alive message if the service is started and we have a
* connection with it.
*/
private synchronized void keepAlive(final Boolean forced) {
try {
if (mStarted && isConnected() && isLoggedIn()) {
mConnection.sendKeepAlive(forced);
}
} catch (final IOException e) {
Log.w(TAG, "Error occurred while sending the keep alive message.", e);
} catch (final JSONException e) {
Log.w(TAG, "JSON error occurred while sending the keep alive message.", e);
}
}
/**
* Uses the {#link android.app.AlarmManager} to start the keep alive service in every {#value #INTERVAL_KEEP_ALIVE} milliseconds.
*/
private void startKeepAlives() {
final PendingIntent pi = PendingIntent.getService(this, 0, new Intent(IntentActions.KEEP_ALIVE_SERVICE_PING_SERVER), PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + INTERVAL_KEEP_ALIVE, INTERVAL_KEEP_ALIVE, pi);
}
/**
* Removes the repeating alarm which was started by the {#link #startKeepAlives()} function.
*/
private void stopKeepAlives() {
final PendingIntent pi = PendingIntent.getService(this, 0, new Intent(IntentActions.KEEP_ALIVE_SERVICE_PING_SERVER), PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.cancel(pi);
}
public void scheduleReconnect(final long startTime) {
long interval = mPrefs.getLong("retryInterval", INTERVAL_INITIAL_RETRY);
final long now = System.currentTimeMillis();
final long elapsed = now - startTime;
if (elapsed < interval) {
interval = Math.min(interval * 4, INTERVAL_MAXIMUM_RETRY);
} else {
interval = INTERVAL_INITIAL_RETRY;
}
Log.i(TAG, "Rescheduling connection in " + interval + "ms.");
mPrefs.edit().putLong("retryInterval", interval).apply();
final PendingIntent pi = PendingIntent.getService(this, 0, new Intent(IntentActions.KEEP_ALIVE_SERVICE_RECONNECT), PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.set(AlarmManager.RTC_WAKEUP, now + interval, pi);
}
public void cancelReconnect() {
final PendingIntent pi = PendingIntent.getService(this, 0, new Intent(IntentActions.KEEP_ALIVE_SERVICE_RECONNECT), PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.cancel(pi);
}
private synchronized void reconnectIfNecessary() {
if (mStarted && !isConnected()) {
Log.i(TAG, "Reconnecting...");
mConnection = new ConnectionThread(Config.PLUGIN_BASE_HOST, Config.PLUGIN_BASE_PORT);
mConnection.start();
}
}
private final BroadcastReceiver mConnectivityChanged = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, final Intent intent) {
final NetworkInfo info = mConnMan.getActiveNetworkInfo(); // (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
final boolean hasConnectivity = info != null && info.isConnected();
Log.i(TAG, "Connecting changed: connected=" + hasConnectivity);
if (hasConnectivity) {
reconnectIfNecessary();
} else if (mConnection != null) {
mConnection.abort(false);
mConnection = null;
}
}
};
protected class ConnectionThread extends Thread {
private final Socket mSocket;
private final String mHost;
private final int mPort;
private volatile boolean mAbort = false;
public ConnectionThread(final String host, final int port) {
mHost = host;
mPort = port;
mSocket = new Socket();
}
/**
* Returns whether we have an active internet connection or not.
*
* #return <code>true</code> if there is an active internet connection.
* <code>false</code> otherwise.
*/
private boolean isNetworkAvailable() {
final NetworkInfo info = mConnMan.getActiveNetworkInfo();
return info != null && info.isConnected();
}
#Override
public void run() {
final Socket s = mSocket;
final long startTime = System.currentTimeMillis();
try {
// Now we can say that the service is started.
setStarted(true);
// Connect to server.
s.connect(new InetSocketAddress(mHost, mPort), 20000);
Log.i(TAG, "Connection established to " + s.getInetAddress() + ":" + mPort);
// Start keep alive alarm.
startKeepAlives();
final DataOutputStream dos = new DataOutputStream(s.getOutputStream());
final BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
// Send the login data.
final JSONObject login = new JSONObject();
// Send the login message.
dos.write((login.toString() + "\r\n").getBytes());
// Wait until we receive something from the server.
String receivedMessage;
while ((receivedMessage = in.readLine()) != null) {
Log.i(TAG, "Received data: " + receivedMessage);
processMessagesFromServer(dos, receivedMessage);
}
if (!mAbort) {
Log.i(TAG, "Server closed connection unexpectedly.");
}
} catch (final IOException e) {
Log.e(TAG, "Unexpected I/O error.", e);
} catch (final Exception e) {
Log.e(TAG, "Exception occurred.", e);
} finally {
setLoggedIn(false);
stopKeepAlives();
if (mAbort) {
Log.i(TAG, "Connection aborted, shutting down.");
} else {
try {
s.close();
} catch (final IOException e) {
// Do nothing.
}
synchronized (KeepAliveService.this) {
mConnection = null;
}
if (isNetworkAvailable()) {
scheduleReconnect(startTime);
}
}
}
}
/**
* Sends the PING word to the server.
*
* #throws java.io.IOException if an error occurs while writing to this stream.
* #throws org.json.JSONException
*/
public void sendKeepAlive(final Boolean forced) throws IOException, JSONException {
final JSONObject ping = new JSONObject();
final Socket s = mSocket;
s.getOutputStream().write((ping.toString() + "\r\n").getBytes());
}
/**
* Aborts the connection with the server.
*/
public void abort(boolean manual) {
mAbort = manual;
try {
// Close the output stream.
mSocket.shutdownOutput();
} catch (final IOException e) {
// Do nothing.
}
try {
// Close the input stream.
mSocket.shutdownInput();
} catch (final IOException e) {
// Do nothing.
}
try {
// Close the socket.
mSocket.close();
} catch (final IOException e) {
// Do nothing.
}
while (true) {
try {
join();
break;
} catch (final InterruptedException e) {
// Do nothing.
}
}
}
}
public void processMessagesFromServer(final DataOutputStream dos, final String receivedMessage) throws IOException {
}
}
You can start the service by calling KeepAliveService.actionStart() and you can also define custom functions.
Please note that the service will be stopped only if you call KeepAliveService.actionStop(). Otherwise it will run forever. If you call e.g. KeepAliveService.actionSendMessage(String message) then the intent will be passed to the service and you can handle it easily.
EDIT:
The SystemHelper class is only a utility class which contains static methods.
public class SystemHelper {
/**
* Android Lollipop, API 21 introduced a new problem when trying to invoke implicit intent,
* "java.lang.IllegalArgumentException: Service Intent must be explicit"
*
* If you are using an implicit intent, and know only 1 target would answer this intent,
* This method will help you turn the implicit intent into the explicit form.
*
* Inspired from SO answer: http://stackoverflow.com/a/26318757/1446466
* #param context the application context
* #param implicitIntent - The original implicit intent
* #return Explicit Intent created from the implicit original intent
*/
public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
Log.i(TAG, "createExplicitFromImplicitIntent ... called with intent: " + implicitIntent);
// Retrieve all services that can match the given intent
PackageManager pm = context.getPackageManager();
List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
// Make sure only one match was found
if (resolveInfo == null || resolveInfo.size() != 1) {
Log.i(TAG, "createExplicitFromImplicitIntent ... resolveInfo is null or there are more than one element.");
return null;
}
// Get component info and create ComponentName
ResolveInfo serviceInfo = resolveInfo.get(0);
String packageName = serviceInfo.serviceInfo.packageName;
String className = serviceInfo.serviceInfo.name;
ComponentName component = new ComponentName(packageName, className);
Log.i(TAG, "createExplicitFromImplicitIntent ... found package name:" + packageName + ", class name: " + className + ".");
// Create a new intent. Use the old one for extras and such reuse
Intent explicitIntent = new Intent(implicitIntent);
// Set the component to be explicit
explicitIntent.setComponent(component);
return explicitIntent;
}
}
The Config class.
public class Config {
public static final String PACKAGE_NAME = "com.yourapp.package";
public static final String PLUGIN_BASE_HOST = "test.yoursite.com";
public static final int PLUGIN_BASE_PORT = 10000;
}
And the IntentActions class.
public class IntentActions {
public static final String KEEP_ALIVE_SERVICE_START = Config.PACKAGE_NAME + ".intent.action.KEEP_ALIVE_SERVICE_START";
public static final String KEEP_ALIVE_SERVICE_STOP = Config.PACKAGE_NAME + ".intent.action.KEEP_ALIVE_SERVICE_STOP";
public static final String KEEP_ALIVE_SERVICE_PING_SERVER = Config.PACKAGE_NAME + ".intent.action.KEEP_ALIVE_SERVICE_PING_SERVER";
}
In the AndroidManifest file the service is defined in the following way:
<service android:name="com.yourapp.package.services.KeepAliveService"
android:exported="false">
<intent-filter>
<action android:name="com.yourapp.package.intent.action.KEEP_ALIVE_SERVICE_START" />
<action android:name="com.yourapp.package.intent.action.KEEP_ALIVE_SERVICE_STOP" />
<action android:name="com.yourapp.package.intent.action.KEEP_ALIVE_SERVICE_PING_SERVER" />
</intent-filter>
</service>
I suggest you take a look at the Android documentation for background services. Personally I would use the IntentService as it's a well established pattern within Android.
http://developer.android.com/training/run-background-service/index.html

Android Mosquitto - Client connection state

I've a MQTT client runnning in Android, and a MQTT Broker in the server. My problem is where we will use the app we have some connections drop so my web app needs to know the current state of the client.
So what we are doing right now is:
1 - The server sends a random number to the clients (each client will receive a different random number)
2 - The android client receives the number and send to a web service
3 - The web service writes in SQL db
4 - The server wait 4 secs to the response from android client and if the random number sent by the server == to the number in the db , the client is connected.
But now the problem is when, multi-users sends the random number the only one that will be write in the db is the last one so it's huge design fault.
In order to fix the only good solution is to get a direct response from the MQTT client and have to be unique per client but i don't know if is possible or if is the best way to go.
Some draw to better understand:
Flow
Here is my android code:
public class MQTTService extends Service implements MqttCallback {
public static final String DEBUG_TAG = "MqttService"; // Debug TAG
private static final String MQTT_THREAD_NAME = "MqttService[" + DEBUG_TAG + "]"; // Handler
// Thread
// ID
private String MQTT_BROKER = ""; // Broker URL
// or IP
// Address
private static final int MQTT_PORT = 1883; // Broker Port
public static final int MQTT_QOS_0 = 0; // QOS Level 0 ( Delivery Once no
// confirmation )
public static final int MQTT_QOS_1 = 1; // QOS Level 1 ( Delivery at least
// Once with confirmation )
public static final int MQTT_QOS_2 = 2; // QOS Level 2 ( Delivery only once
// with confirmation with handshake
// )
private static final int MQTT_KEEP_ALIVE = 30000; // KeepAlive Interval in
// MS
private static final String MQTT_KEEP_ALIVE_TOPIC_FORMAT = "/users/%s/keepalive"; // Topic
// format
// for
// KeepAlives
private static final byte[] MQTT_KEEP_ALIVE_MESSAGE = { 0 }; // Keep Alive
// message
// to send
private static final int MQTT_KEEP_ALIVE_QOS = MQTT_QOS_2; // Default
// Keepalive QOS
private static final boolean MQTT_CLEAN_SESSION = false; // Start a clean
// session?
private static final String MQTT_URL_FORMAT = "tcp://%s:%d"; // URL Format
// normally
// don't
// change
public static final String ACTION_START = DEBUG_TAG + ".START"; // Action
// to
// start
public static final String ACTION_STOP = DEBUG_TAG + ".STOP"; // Action to
// stop
public static final String ACTION_KEEPALIVE = DEBUG_TAG + ".KEEPALIVE"; // Action
// to
// keep
// alive
// used
// by
// alarm
// manager
private static final String ACTION_RECONNECT = DEBUG_TAG + ".RECONNECT"; // Action
// to
// reconnect
// private final String DEVICE_ID_FORMAT = "andr_%s"; // Device ID
// Format, add
// any prefix
// you'd like
// Note: There
// is a 23
// character
// limit you
// will get
// An NPE if you
// go over that
// limit
private boolean mStarted = false; // Is the Client started?
private String user_ID; // Device ID, Secure.ANDROID_ID
private Handler mConnHandler; // Seperate Handler thread for networking
private MqttDefaultFilePersistence mDataStore; // Defaults to FileStore
private MemoryPersistence mMemStore; // On Fail reverts to MemoryStore
private MqttConnectOptions mOpts; // Connection Options
private MqttTopic mKeepAliveTopic; // Instance Variable for Keepalive topic
private MqttClient mClient; // Mqtt Client
private AlarmManager mAlarmManager; // Alarm manager to perform repeating
// tasks
private ConnectivityManager mConnectivityManager; // To check for
// connectivity changes
public static final String TAG_CONNECTED = "CONNECTED";
public static final String TAG_ASSIGNED = "ASSIGNED";
public static final String TAG_REFRESH = "REFRESH";
public String TOPIC_CONNECTED = null;
public String TOPIC_ASSIGNED = null;
public String TOPIC_REFRESH = null;
private Intent intent;
private PendingIntent alarmIntent;
private AppMaintenance appStatus;
/**
* Initializes the DeviceId and most instance variables Including the
* Connection Handler, Datastore, Alarm Manager and ConnectivityManager.
*/
#Override
public void onCreate() {
super.onCreate();
// mDeviceId = String.format(DEVICE_ID_FORMAT,
// Secure.getString(getContentResolver(), Secure.ANDROID_ID));
android.os.Debug.waitForDebugger(); // Debugger
appStatus = (AppMaintenance) getApplicationContext();
ExceptionHandler.register(this, appStatus.getException_URL());
HandlerThread thread = new HandlerThread(MQTT_THREAD_NAME);
thread.start();
mConnHandler = new Handler(thread.getLooper());
try {
mDataStore = new MqttDefaultFilePersistence(getCacheDir().getAbsolutePath());
} catch (Exception e) {
// writeToFile("Exception - onCreate()");
e.printStackTrace();
mDataStore = null;
mMemStore = new MemoryPersistence();
}
mOpts = new MqttConnectOptions();
mOpts.setCleanSession(MQTT_CLEAN_SESSION);
// Do not set keep alive interval on mOpts we keep track of it with
// alarm's
mAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
mConnectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
registerReceiver(mConnectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
}
/**
* Start MQTT Client
*
* #param Context
* context to start the service with
* #return void
*/
public static void actionStart(Context ctx) {
Intent i = new Intent(ctx, MQTTService.class);
i.setAction(ACTION_START);
ctx.startService(i);
}
/**
* Stop MQTT Client
*
* #param Context
* context to start the service with
* #return void
*/
public static void actionStop(Context ctx) {
Intent i = new Intent(ctx, MQTTService.class);
i.setAction(ACTION_STOP);
ctx.startService(i);
}
/**
* Send a KeepAlive Message
*
* #param Context
* context to start the service with
* #return void
*/
public static void actionKeepalive(Context ctx) {
Intent i = new Intent(ctx, MQTTService.class);
i.setAction(ACTION_KEEPALIVE);
ctx.startService(i);
}
/**
* Service onStartCommand Handles the action passed via the Intent
*
* #return START_REDELIVER_INTENT
*/
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
this.intent = intent;
SharedPreferences myPrefs = getSharedPreferences("UserPreferences", MODE_PRIVATE);
MQTT_BROKER = myPrefs.getString("broker", "");
user_ID = myPrefs.getString("userID", "");
String action = intent.getAction();
TOPIC_CONNECTED = user_ID + "\\" + TAG_CONNECTED;
TOPIC_ASSIGNED = user_ID + "\\" + TAG_ASSIGNED;
TOPIC_REFRESH = user_ID + "\\" + TAG_REFRESH;
Log.i(DEBUG_TAG, "Received action of " + action);
// writeToFile("Received action of " + action);
if (user_ID.isEmpty() || user_ID == null)
action = null;
if (action == null) {
Log.i(DEBUG_TAG, "Starting service with no action\n Probably from a crash");
// writeToFile("Starting service with no action\n Probably from a crash");
Toast.makeText(getApplicationContext(), getString(R.string.mqtt_warning_userid), Toast.LENGTH_LONG).show();
action = null;
} else {
if (action.equals(ACTION_START)) {
Log.i(DEBUG_TAG, "Received ACTION_START");
// writeToFile("Received ACTION_START");
start();
} else if (action.equals(ACTION_STOP)) {
Log.i(DEBUG_TAG, "Received ACTION_STOP");
// writeToFile("Received ACTION_STOP");
stop();
} else if (action.equals(ACTION_KEEPALIVE)) {
Log.i(DEBUG_TAG, "Received ACTION_KEEPALIVE");
// writeToFile("Received ACTION_KEEPALIVE");
keepAlive();
} else if (action.equals(ACTION_RECONNECT)) {
Log.i(DEBUG_TAG, "Received ACTION_RECONNECT");
// writeToFile("Received ACTION_RECONNECT");
reconnectIfNecessary();
}
}
return START_NOT_STICKY;
}
/**
* Attempts connect to the Mqtt Broker and listen for Connectivity changes
* via ConnectivityManager.CONNECTVITIY_ACTION BroadcastReceiver
*/
private synchronized void start() {
if (mStarted) {
Log.i(DEBUG_TAG, "Attempt to start while already started");
// writeToFile("Attempt to start while already started");
return;
}
if (hasScheduledKeepAlives()) {
stopKeepAlives();
}
connect();
}
/**
* Attempts to stop the Mqtt client as well as halting all keep alive
* messages queued in the alarm manager
*/
private synchronized void stop() {
if (!mStarted) {
Log.i(DEBUG_TAG, "Attemt to stop connection that isn't running");
// writeToFile("Attemt to stop connection that isn't running");
return;
}
if (mClient != null) {
mConnHandler.post(new Runnable() {
#Override
public void run() {
try {
mClient.disconnect();
} catch (Exception ex) {
// writeToFile("Exception - stop() ");
ex.printStackTrace();
mClient = null;
mStarted = false;
} finally {
mClient = null;
mStarted = false;
stopKeepAlives();
}
}
});
}
}
/**
* Connects to the broker with the appropriate datastore
*/
private synchronized void connect() {
String url = String.format(Locale.US, MQTT_URL_FORMAT, MQTT_BROKER, MQTT_PORT);
Log.i(DEBUG_TAG, "Connecting with URL: " + url);
// writeToFile("Connecting with URL: " + url);
try {
if (mDataStore != null) {
Log.i(DEBUG_TAG, "Connecting with DataStore");
// writeToFile("Connecting with DataStore");
mClient = new MqttClient(url, user_ID, mDataStore);
} else {
Log.i(DEBUG_TAG, "Connecting with MemStore");
// writeToFile("Connecting with MemStore");
mClient = new MqttClient(url, user_ID, mMemStore);
}
} catch (Exception e) {
// writeToFile("Exception - connect L.343");
e.printStackTrace();
}
mConnHandler.post(new Runnable() {
#Override
public void run() {
try {
mClient.connect(mOpts);
mClient.subscribe(new String[] { TOPIC_CONNECTED, TOPIC_ASSIGNED, TOPIC_REFRESH }, new int[] { MQTT_QOS_0,
MQTT_KEEP_ALIVE_QOS, MQTT_KEEP_ALIVE_QOS });
mClient.setCallback(new MQTTPushCallback(MQTTService.this, intent, user_ID, TOPIC_CONNECTED, TOPIC_ASSIGNED,
TOPIC_REFRESH));
mStarted = true; // Service is now connected
Log.i(DEBUG_TAG, "Successfully connected and subscribed starting keep alives");
// writeToFile("Successfully connected and subscribed starting keep alives");
startKeepAlives();
} catch (Exception e) {
// writeToFile("Exception - connect L.366");
e.printStackTrace();
}
}
});
}
/**
* Schedules keep alives via a PendingIntent in the Alarm Manager
*/
private void startKeepAlives() {
Intent i = new Intent();
i.setClass(this, MQTTService.class);
i.setAction(ACTION_KEEPALIVE);
alarmIntent = PendingIntent.getService(this, 0, i, 0);
mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + MQTT_KEEP_ALIVE, MQTT_KEEP_ALIVE, alarmIntent);
Log.i(DEBUG_TAG, "Started keepAlives sucessfully");
// writeToFile("Started keepAlives sucessfully");
}
/**
* Cancels the Pending Intent in the alarm manager
*/
private void stopKeepAlives() {
if (mAlarmManager != null) {
mAlarmManager.cancel(alarmIntent);
}
}
/**
* Publishes a KeepALive to the topic in the broker
*/
private synchronized void keepAlive() {
// if (isForeground()) {
if (isConnected()) {
try {
sendKeepAlive();
return;
} catch (MqttConnectivityException ex) {
// writeToFile("Exception - KeepAlive() 1");
ex.printStackTrace();
reconnectIfNecessary();
} catch (MqttPersistenceException ex) {
// writeToFile("Exception - KeepAlive() 2");
ex.printStackTrace();
stop();
restartService();
} catch (MqttException ex) {
// writeToFile("Exception - KeepAlive() 3");
ex.printStackTrace();
stop();
restartService();
} catch (Exception ex) {
// writeToFile("Exception - KeepAlive() 4");
ex.printStackTrace();
stop();
restartService();
}
}
}
/**
* Checks the current connectivity and reconnects if it is required.
*/
private synchronized void reconnectIfNecessary() {
if (!mStarted && mClient == null)
start();
}
/**
* Query's the NetworkInfo via ConnectivityManager to return the current
* connected state
*
* #return boolean true if we are connected false otherwise
*/
private boolean isNetworkAvailable() {
NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
return info == null ? false : info.isConnected();
}
/**
* Verifies the client State with our local connected state
*
* #return true if its a match we are connected false if we aren't connected
*/
private boolean isConnected() {
if (mStarted && mClient != null && !mClient.isConnected()) {
Log.i(DEBUG_TAG, "Mismatch between what we think is connected and what is connected");
// writeToFile("Mismatch between what we think is connected and what is connected");
}
if (mClient != null) {
return mStarted && mClient.isConnected() ? true : false;
}
return false;
}
/**
* Receiver that listens for connectivity changes via ConnectivityManager
*/
private final BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// writeToFile("isNetworkAvailable = " + isNetworkAvailable());
if (isNetworkAvailable() && !mStarted) {
Log.i(DEBUG_TAG, "Connectivity Changed...");
// Intent i = new Intent(context, MQTTService.class);
// i.setAction(ACTION_RECONNECT);
// context.startService(i);
restartService();
} else if (!isNetworkAvailable()) {
stop();
}
}
};
/**
* Sends a Keep Alive message to the specified topic
*
* #see MQTT_KEEP_ALIVE_MESSAGE
* #see MQTT_KEEP_ALIVE_TOPIC_FORMAT
* #return MqttDeliveryToken specified token you can choose to wait for
* completion
*/
private synchronized MqttDeliveryToken sendKeepAlive() throws MqttConnectivityException, MqttPersistenceException, MqttException {
if (!isConnected())
throw new MqttConnectivityException();
if (mKeepAliveTopic == null) {
mKeepAliveTopic = mClient.getTopic(String.format(Locale.US, MQTT_KEEP_ALIVE_TOPIC_FORMAT, user_ID));
}
Log.i(DEBUG_TAG, "Sending Keepalive to " + MQTT_BROKER);
// writeToFile("Sending Keepalive to " + MQTT_BROKER);
MqttMessage message = new MqttMessage(MQTT_KEEP_ALIVE_MESSAGE);
message.setQos(MQTT_KEEP_ALIVE_QOS);
return mKeepAliveTopic.publish(message);
}
/**
* Query's the AlarmManager to check if there is a keep alive currently
* scheduled
*
* #return true if there is currently one scheduled false otherwise
*/
private synchronized boolean hasScheduledKeepAlives() {
Intent i = new Intent();
i.setClass(this, MQTTService.class);
i.setAction(ACTION_KEEPALIVE);
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, PendingIntent.FLAG_NO_CREATE);
return pi != null ? true : false;
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
/**
* Connectivity Lost from broker
*/
#Override
public void connectionLost(Throwable arg0) {
stopKeepAlives();
mClient = null;
if (isNetworkAvailable()) {
reconnectIfNecessary();
}
}
/**
* Publish Message Completion
*/
#Override
public void deliveryComplete(IMqttDeliveryToken arg0) {
// TODO Auto-generated method stub
}
/**
* Received Message from broker
*/
#Override
public void messageArrived(String arg0, MqttMessage arg1) throws Exception {
// Log.i(DEBUG_TAG,
// " Topic:\t" + topic.getName() + " Message:\t"
// + new String(message.getPayload()) + " QoS:\t"
// + message.getQos());
}
/**
* MqttConnectivityException Exception class
*/
private class MqttConnectivityException extends Exception {
private static final long serialVersionUID = -7385866796799469420L;
}
#Override
public void onDestroy() {
try {
mClient.unsubscribe(new String[] { TOPIC_CONNECTED, TOPIC_ASSIGNED, TOPIC_REFRESH });
mClient.disconnect(0);
} catch (Exception e) {
// writeToFile("Exception - onDestroy() 1");
e.printStackTrace();
} finally {
new WS_LOGOUT(this).execute(user_ID);
}
}
public void restartService() {
mKeepAliveTopic = null;
actionStart(getApplicationContext()); // restart the service
}
}
What sort of latency can you live with when knowing if the client is disconnected?
You can use the Last Will and Testament feature to publish a value to a topic when the server detects that the MQTT keep alive time has expired with out receiving a ping from the client.
You can set the keep alive time at connection time. But depending on your requirements (battery/network usage) you need to work out what to set it to. If I remember correctly the default is 30 seconds (might be 60)
When your client connects it can set a flag on a persitent topic to say it's online, and the LWT can set this to 0.
e.g.
on connect publish "1" to client/[uniqueid]/online
set the LWT to publish "0" to client/[uniqueid]/online

wifi tethering on android

I have created an app for tethering wifi on android devices
and here`s my code
WiFiAo.java
package com.gado.wifihotspot;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.util.Log;
import java.lang.reflect.Method;
import com.gado.wifihotspot.MainActivity;;
public class WifiAP extends Activity {
private static int constant = 0;
private static final int WIFI_AP_STATE_UNKNOWN = -1;
private static int WIFI_AP_STATE_DISABLING = 0;
private static int WIFI_AP_STATE_DISABLED = 1;
public int WIFI_AP_STATE_ENABLING = 2;
public int WIFI_AP_STATE_ENABLED = 3;
private static int WIFI_AP_STATE_FAILED = 4;
private final String[] WIFI_STATE_TEXTSTATE = new String[] {
"DISABLING","DISABLED","ENABLING","ENABLED","FAILED"
};
private WifiManager wifi;
private String TAG = "WifiAP";
private int stateWifiWasIn = -1;
private boolean alwaysEnableWifi = true; //set to false if you want to try and set wifi state back to what it was before wifi ap enabling, true will result in the wifi always being enabled after wifi ap is disabled
/**
* Toggle the WiFi AP state
* #param wifihandler
* #author http://stackoverflow.com/a/7049074/1233435
*/
public void toggleWiFiAP(WifiManager wifihandler, Context context) {
if (wifi==null){
wifi = wifihandler;
}
boolean wifiApIsOn = getWifiAPState()==WIFI_AP_STATE_ENABLED || getWifiAPState()==WIFI_AP_STATE_ENABLING;
new SetWifiAPTask(!wifiApIsOn,false,context).execute();
}
/**
* Enable/disable wifi
* #param true or false
* #return WifiAP state
* #author http://stackoverflow.com/a/7049074/1233435
*/
private int setWifiApEnabled(boolean enabled) {
Log.d(TAG, "*** setWifiApEnabled CALLED **** " + enabled);
WifiConfiguration config = new WifiConfiguration();
config.SSID = "My AP";
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
//remember wirelesses current state
if (enabled && stateWifiWasIn==-1){
stateWifiWasIn=wifi.getWifiState();
}
//disable wireless
if (enabled && wifi.getConnectionInfo() !=null) {
Log.d(TAG, "disable wifi: calling");
wifi.setWifiEnabled(false);
int loopMax = 10;
while(loopMax>0 && wifi.getWifiState()!=WifiManager.WIFI_STATE_DISABLED){
Log.d(TAG, "disable wifi: waiting, pass: " + (10-loopMax));
try {
Thread.sleep(500);
loopMax--;
} catch (Exception e) {
}
}
Log.d(TAG, "disable wifi: done, pass: " + (10-loopMax));
}
//enable/disable wifi ap
int state = WIFI_AP_STATE_UNKNOWN;
try {
Log.d(TAG, (enabled?"enabling":"disabling") +" wifi ap: calling");
wifi.setWifiEnabled(false);
Method method1 = wifi.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class);
//method1.invoke(wifi, null, enabled); // true
method1.invoke(wifi, config, enabled); // true
Method method2 = wifi.getClass().getMethod("getWifiApState");
state = (Integer) method2.invoke(wifi);
} catch (Exception e) {
Log.e(WIFI_SERVICE, e.getMessage());
// toastText += "ERROR " + e.getMessage();
}
//hold thread up while processing occurs
if (!enabled) {
int loopMax = 10;
while (loopMax>0 && (getWifiAPState()==WIFI_AP_STATE_DISABLING || getWifiAPState()==WIFI_AP_STATE_ENABLED || getWifiAPState()==WIFI_AP_STATE_FAILED)) {
Log.d(TAG, (enabled?"enabling":"disabling") +" wifi ap: waiting, pass: " + (10-loopMax));
try {
Thread.sleep(500);
loopMax--;
} catch (Exception e) {
}
}
Log.d(TAG, (enabled?"enabling":"disabling") +" wifi ap: done, pass: " + (10-loopMax));
//enable wifi if it was enabled beforehand
//this is somewhat unreliable and app gets confused and doesn't turn it back on sometimes so added toggle to always enable if you desire
if(stateWifiWasIn==WifiManager.WIFI_STATE_ENABLED || stateWifiWasIn==WifiManager.WIFI_STATE_ENABLING || stateWifiWasIn==WifiManager.WIFI_STATE_UNKNOWN || alwaysEnableWifi){
Log.d(TAG, "enable wifi: calling");
wifi.setWifiEnabled(true);
//don't hold things up and wait for it to get enabled
}
stateWifiWasIn = -1;
} else if (enabled) {
int loopMax = 10;
while (loopMax>0 && (getWifiAPState()==WIFI_AP_STATE_ENABLING || getWifiAPState()==WIFI_AP_STATE_DISABLED || getWifiAPState()==WIFI_AP_STATE_FAILED)) {
Log.d(TAG, (enabled?"enabling":"disabling") +" wifi ap: waiting, pass: " + (10-loopMax));
try {
Thread.sleep(500);
loopMax--;
} catch (Exception e) {
}
}
Log.d(TAG, (enabled?"enabling":"disabling") +" wifi ap: done, pass: " + (10-loopMax));
}
return state;
}
/**
* Get the wifi AP state
* #return WifiAP state
* #author http://stackoverflow.com/a/7049074/1233435
*/
public int getWifiAPState() {
int state = WIFI_AP_STATE_UNKNOWN;
try {
Method method2 = wifi.getClass().getMethod("getWifiApState");
state = (Integer) method2.invoke(wifi);
} catch (Exception e) {
}
if(state>=10){
//using Android 4.0+ (or maybe 3+, haven't had a 3 device to test it on) so use states that are +10
constant=10;
}
//reset these in case was newer device
WIFI_AP_STATE_DISABLING = 0+constant;
WIFI_AP_STATE_DISABLED = 1+constant;
WIFI_AP_STATE_ENABLING = 2+constant;
WIFI_AP_STATE_ENABLED = 3+constant;
WIFI_AP_STATE_FAILED = 4+constant;
Log.d(TAG, "getWifiAPState.state " + (state==-1?"UNKNOWN":WIFI_STATE_TEXTSTATE[state-constant]));
return state;
}
/**
* the AsyncTask to enable/disable the wifi ap
* #author http://stackoverflow.com/a/7049074/1233435
*/
class SetWifiAPTask extends AsyncTask<Void, Void, Void> {
boolean mMode; //enable or disable wifi AP
boolean mFinish; //finalize or not (e.g. on exit)
ProgressDialog d;
/**
* enable/disable the wifi ap
* #param mode enable or disable wifi AP
* #param finish finalize or not (e.g. on exit)
* #param context the context of the calling activity
* #author http://stackoverflow.com/a/7049074/1233435
*/
public SetWifiAPTask(boolean mode, boolean finish, Context context) {
mMode = mode;
mFinish = finish;
d = new ProgressDialog(context);
}
/**
* do before background task runs
* #author http://stackoverflow.com/a/7049074/1233435
*/
#Override
protected void onPreExecute() {
super.onPreExecute();
d.setTitle("Turning WiFi Tethering " + (mMode?"on":"off") + "...");
d.setMessage("...please wait a moment.");
d.show();
}
/**
* do after background task runs
* #param aVoid
* #author http://stackoverflow.com/a/7049074/1233435
*/
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
try {
d.dismiss();
MainActivity.updateStatusDisplay();
} catch (IllegalArgumentException e) {
};
if (mFinish){
finish();
}
}
/**
* the background task to run
* #param params
* #author http://stackoverflow.com/a/7049074/1233435
*/
#Override
protected Void doInBackground(Void... params) {
setWifiApEnabled(mMode);
return null;
}
}
}
MainActivity.java
package com.gado.wifihotspot;
import android.app.Activity;
import android.content.Context;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.ImageButton;
public class MainActivity extends Activity {
boolean wasAPEnabled = false;
static WifiAP wifiAp;
private WifiManager wifi;
static ImageButton btnWifiToggle;
static CheckBox checkencrypted;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnWifiToggle = (ImageButton) findViewById(R.id.btnWifiToggle);
checkencrypted = (CheckBox) findViewById(R.id.checkBox1);
wifiAp = new WifiAP();
wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
btnWifiToggle.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
wifiAp.toggleWiFiAP(wifi, MainActivity.this);
}
});
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD|WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON|WindowManager.LayoutParams.FLAG_DIM_BEHIND);
}
#Override
public void onResume() {
super.onResume();
if (wasAPEnabled) {
if (wifiAp.getWifiAPState()!=wifiAp.WIFI_AP_STATE_ENABLED && wifiAp.getWifiAPState()!=wifiAp.WIFI_AP_STATE_ENABLING){
wifiAp.toggleWiFiAP(wifi, MainActivity.this);
}
}
updateStatusDisplay();
}
#Override
public void onPause() {
super.onPause();
boolean wifiApIsOn = wifiAp.getWifiAPState()==wifiAp.WIFI_AP_STATE_ENABLED || wifiAp.getWifiAPState()==wifiAp.WIFI_AP_STATE_ENABLING;
if (wifiApIsOn) {
wasAPEnabled = true;
wifiAp.toggleWiFiAP(wifi, MainActivity.this);
} else {
wasAPEnabled = false;
}
updateStatusDisplay();
}
public static void updateStatusDisplay() {
if (wifiAp.getWifiAPState()==wifiAp.WIFI_AP_STATE_ENABLED || wifiAp.getWifiAPState()==wifiAp.WIFI_AP_STATE_ENABLING) {
btnWifiToggle.setImageResource(R.drawable.off);
//findViewById(R.id.bg).setBackgroundResource(R.drawable.bg_wifi_on);
} else {
btnWifiToggle.setImageResource(R.drawable.on);
//findViewById(R.id.bg).setBackgroundResource(R.drawable.bg_wifi_off);
}
}
}
Now I want to add encryption for this tethering & WiFi hotspot name but I haven`t any idea about how to do that?
Update the setWifiApEnabled method in this way.This method provides open, WPA and WPA2 access to your app.
private int setWifiApEnabled(boolean enabled) {
Log.d(TAG, "*** setWifiApEnabled CALLED **** " + enabled);
WifiConfiguration config = new WifiConfiguration();
config.SSID = "My AP";
//changes added here
config.preSharedKey="yourpassword";//enter the set password here
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
.....
You can also try WPA_EAP for different KeyMgmt;
reference:
Unable to connect with WPA2 android
Leave it to the OS, and use WifiConfiguration.Protocol - there you can set it to WPA :)
More info here.
http://developer.android.com/reference/android/net/wifi/WifiConfiguration.html
gl hf
Refael

How To Make JmDNS Work On A Large Network

I'm working on adding discovery to our app using (http://jmdns.sourceforge.net/). It's actually working correctly on my small home network. But it fails on the large network at the office. I seem to recall reading that if an app blocks for more than 5 seconds that it get's reset. And that appears to be what's happening. But first, how can I be sure that's actually the problem?
Of course, my main question is how can I can make JmDNS work on a large network. And the more general question is what do you do when you need more than 5 seconds?
Small snippet of my code (it's in an AsyncTask):
InetAddress bindingAddress = InetAddress.getByName(ipOfThisDevice);
jmdns = JmDNS.create(bindingAddress);
serviceList = Arrays.asList(jmdns.list("_myappname._tcp.local.",5000)); // 5 second timeout
Here is the code I used in my Android app which you say seems to work at your office?
/*
ZeroConf Browser - http://melloware.com/
Copyright (C) 2010 Melloware Inc
All Rights Reserved.
*/
package com.melloware.zeroconf;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.TreeMap;
import javax.jmdns.JmDNS;
import javax.jmdns.ServiceEvent;
import javax.jmdns.ServiceInfo;
import javax.jmdns.ServiceListener;
import javax.jmdns.ServiceTypeListener;
import android.app.ExpandableListActivity;
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.MulticastLock;
import android.os.Bundle;
import android.text.Html;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ImageView;
import android.widget.TextView;
/**
* Service tab which contains the expandable list of DNS Services and the details of each service type as they are
* gathered.
* <p>
* Copyright (c) 2010 Melloware, Inc. <http://www.melloware.com>
* #author Emil A. Lefkof III <info#melloware.com>
* #version 1.0
*/
public class ServiceActivity extends ExpandableListActivity implements ServiceListener, ServiceTypeListener {
/**
* Tag used for logging
*/
private static final String TAG = ServiceActivity.class.getName();
/**
* Value used to identify in ZeroConf
*/
private static final String HOSTNAME = "melloware";
/**
* Sorted array of top level items which are the Service Types
*/
public static final ArrayList<ServiceType> GROUPS = new ArrayList<ServiceType>();
/**
* Sorted list of the details for each service type
*/
public static final TreeMap<String, ArrayList<ServiceInfo>> DETAILS = new TreeMap<String, ArrayList<ServiceInfo>>();
/**
* Instance of Bonjour/Rendezvous/ZeroConf handler
*/
public static JmDNS jmdns = null;
/**
* Allows an application to receive Wifi Multicast packets.
*/
private static MulticastLock multicastLock = null;
/**
* The backing adapter for the ListView of services
*/
private static DNSExpandableListAdapter mAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAdapter = new DNSExpandableListAdapter();
setListAdapter(mAdapter);
}
/*
* (non-Javadoc)
* #see android.app.Activity#onStart()
*/
#Override
protected void onStart() {
Log.i(TAG, "Starting ServiceActivity...");
super.onStart();
try {
Log.i(TAG, "Starting Mutlicast Lock...");
WifiManager wifi = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
// get the device ip address
final InetAddress deviceIpAddress = getDeviceIpAddress(wifi);
multicastLock = wifi.createMulticastLock(getClass().getName());
multicastLock.setReferenceCounted(true);
multicastLock.acquire();
Log.i(TAG, "Starting ZeroConf probe....");
jmdns = JmDNS.create(deviceIpAddress, HOSTNAME);
jmdns.addServiceTypeListener(this);
} catch (IOException ex) {
Log.e(TAG, ex.getMessage(), ex);
}
Log.i(TAG, "Started ZeroConf probe....");
}
/*
* (non-Javadoc)
* #see android.app.ActivityGroup#onStop()
*/
#Override
protected void onStop() {
Log.i(TAG, "Stopping ServiceActivity...");
super.onStop();
stopScan();
DETAILS.clear();
GROUPS.clear();
if (!isFinishing()) {
mAdapter.notifyDataSetChanged();
}
}
/**
* Stops scanning and cleans up locks.
*/
private static void stopScan() {
try {
if (jmdns != null) {
Log.i(TAG, "Stopping ZeroConf probe....");
jmdns.unregisterAllServices();
jmdns.close();
jmdns = null;
}
if (multicastLock != null) {
Log.i(TAG, "Releasing Mutlicast Lock...");
multicastLock.release();
multicastLock = null;
}
} catch (Exception ex) {
Log.e(TAG, ex.getMessage(), ex);
}
}
/**
* Gets the current Android device IP address or return 10.0.0.2 which is localhost on Android.
* <p>
* #return the InetAddress of this Android device
*/
private InetAddress getDeviceIpAddress(WifiManager wifi) {
InetAddress result = null;
try {
// default to Android localhost
result = InetAddress.getByName("10.0.0.2");
// figure out our wifi address, otherwise bail
WifiInfo wifiinfo = wifi.getConnectionInfo();
int intaddr = wifiinfo.getIpAddress();
byte[] byteaddr = new byte[] { (byte) (intaddr & 0xff), (byte) (intaddr >> 8 & 0xff), (byte) (intaddr >> 16 & 0xff), (byte) (intaddr >> 24 & 0xff) };
result = InetAddress.getByAddress(byteaddr);
} catch (UnknownHostException ex) {
Log.w(TAG, String.format("getDeviceIpAddress Error: %s", ex.getMessage()));
}
return result;
}
/**
* Delegate method from mDNS when a service is added.
*/
public void serviceAdded(ServiceEvent event) {
Log.i(TAG, String.format("ZeroConf serviceAdded(event=\n%s\n)", event.toString()));
ArrayList<ServiceInfo> list = DETAILS.get(event.getType());
if (list != null) {
ServiceInfo info = event.getInfo();
if (!list.contains(info)) {
list.add(info);
}
}
}
/**
* Delegate method from mDNS when a service is removed.
*/
public void serviceRemoved(ServiceEvent event) {
Log.w(TAG, String.format("ZeroConf serviceRemoved(event=\n%s\n)", event.toString()));
}
/**
* Delegate method from mDNS when a service is resolved.
*/
public void serviceResolved(ServiceEvent event) {
Log.i(TAG, String.format("ZeroConf serviceResolved(event=\n%s\n)", event.toString()));
ArrayList<ServiceInfo> list = DETAILS.get(event.getType());
if (list != null) {
ServiceInfo info = event.getInfo();
if (!list.contains(info)) {
list.add(info);
}
}
}
/**
* Delegate method from mDNS when a new service type is discovered.
*/
public void serviceTypeAdded(final ServiceEvent event) {
Log.i(TAG, String.format("ZeroConf serviceTypeAdded(event=\n%s\n)", event.toString()));
jmdns.addServiceListener(event.getType(), this);
runOnUiThread(new Runnable() {
public void run() {
final ServiceType type = new ServiceType();
type.setName(event.getType());
GROUPS.add(type);
Collections.sort(GROUPS);
DETAILS.put(event.getType(), new ArrayList<ServiceInfo>());
mAdapter.notifyDataSetChanged();
}
});
}
/**
* Delegate method from mDNS when a subtype is discovered.
*/
public void subTypeForServiceTypeAdded(ServiceEvent event) {
Log.i(TAG, String.format("ZeroConf subTypeForServiceTypeAdded(event=\n%s\n)", event.toString()));
}
/**
* When a scan is complete show a message if no services found.
* <p>
* #param context the ApplicationContext
*/
public static void scanFinished(Context context) {
if (GROUPS.size() == 0) {
final ServiceType type = new ServiceType();
type.setName(context.getResources().getString(R.string.msg_noservices));
GROUPS.add(type);
mAdapter.notifyDataSetChanged();
stopScan();
}
}
/**
* ExpandableListAdapter that displays the Service Types as groups and when each Service Type is expanded displays a
* list of all discovered Services for that Service Type.
*/
public class DNSExpandableListAdapter extends BaseExpandableListAdapter {
LayoutInflater inflater = LayoutInflater.from(getApplicationContext());
public Object getChild(int groupPosition, int childPosition) {
try {
Iterator<ArrayList<ServiceInfo>> it = DETAILS.values().iterator();
int i = 0;
while (it.hasNext()) {
ArrayList<ServiceInfo> type = it.next();
if (i == groupPosition) {
ServiceInfo service = type.get(childPosition);
ServiceInfo resolvedService = jmdns.getServiceInfo(service.getType(), service.getName());
if (resolvedService != null) {
service = resolvedService;
}
StringBuffer buf = new StringBuffer();
buf.append("<b>");
buf.append(service.getName());
buf.append("</b><br/>");
buf.append(service.getTypeWithSubtype());
buf.append("<br/>");
buf.append(service.getServer());
buf.append(':');
buf.append(service.getPort());
buf.append("<br/>");
buf.append(service.getInetAddresses()[0]);
buf.append("<br/>");
for (Enumeration<String> names = service.getPropertyNames(); names.hasMoreElements();) {
buf.append("<br/>");
String prop = names.nextElement();
buf.append("<b>");
buf.append(prop);
buf.append("</b>");
buf.append(" = ");
buf.append("<i>");
buf.append(service.getPropertyString(prop));
buf.append("</i>");
}
return buf.toString();
}
i++;
}
} catch (Exception e) {
Log.w("Exception", e);
}
return "Not Available";
}
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
public int getChildrenCount(int groupPosition) {
Iterator<ArrayList<ServiceInfo>> it = DETAILS.values().iterator();
int i = 0;
while (it.hasNext()) {
ArrayList<ServiceInfo> type = it.next();
if (i == groupPosition) {
return type.size();
}
i++;
}
return 1;
}
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
convertView = this.inflater.inflate(R.layout.child_row, parent, false);
((TextView) convertView.findViewById(R.id.childvalue)).setText(Html.fromHtml(getChild(groupPosition, childPosition).toString()));
return convertView;
}
public Object getGroup(int groupPosition) {
return GROUPS.get(groupPosition);
}
public int getGroupCount() {
return GROUPS.size();
}
public long getGroupId(int groupPosition) {
return groupPosition;
}
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
convertView = this.inflater.inflate(R.layout.group_row, parent, false);
ServiceType type = (ServiceType) getGroup(groupPosition);
ImageView imageView = (ImageView) convertView.findViewById(R.id.serviceicon);
imageView.setImageResource(type.getImageIcon());
((TextView) convertView.findViewById(R.id.service)).setText(type.toString());
return convertView;
}
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
public boolean hasStableIds() {
return true;
}
}
}
Have a look at http://developer.android.com/training/connect-devices-wirelessly/nsd.html - that should get you started if you are running on Android

Categories

Resources