Java tutorial
/* * Copyright (C) 2014 readyState Software Ltd * * 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.readystatesoftware.android.geras.mqtt; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.provider.Settings; import android.support.v4.app.NotificationCompat; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import android.widget.Toast; import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; import org.eclipse.paho.client.mqttv3.MqttCallback; import org.eclipse.paho.client.mqttv3.MqttClient; import org.eclipse.paho.client.mqttv3.MqttException; import org.eclipse.paho.client.mqttv3.MqttMessage; import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence; import java.util.ArrayList; import java.util.HashMap; import java.util.Locale; public class GerasMqttService extends Service implements MqttCallback, SensorEventListener, LocationListener { public static final String EXTRA_HOST = "host"; public static final String EXTRA_API_KEY = "api_key"; public static final String EXTRA_SENSOR_MONITORS = "sensor_monitors"; public static final String EXTRA_LOCATION_MONTITOR = "location_monitor"; public static final String EXTRA_NOTIFICATION_TARGET_CLASS = "target_class"; public static final String ACTION_PUBLISH_DATAPOINT = "publish_datapoint"; public static final String EXTRA_DATAPOINT_SERIES = "datapoint_series"; public static final String EXTRA_DATAPOINT_VALUE = "datapoint_value"; private static final String TAG = "GerasMqttService"; private static final int NOTIFICATION_ID = 1138; private static boolean sIsRunning = false; private HashMap<Integer, GerasSensorConfig> mSensorMonitorMap = new HashMap<Integer, GerasSensorConfig>(); private GerasLocationConfig mLocationMonitor; private NotificationManager mNotificationManager; private SensorManager mSensorManager; private LocationManager mLocationManager; private Handler mConnectionHandler; private HandlerThread mConnectionThread; private MqttClient mClient; private MqttDefaultFilePersistence mDataStore; private String mDeviceId; private Class mNotificationTarget; private BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String series = intent.getStringExtra(EXTRA_DATAPOINT_SERIES); final String value = intent.getStringExtra(EXTRA_DATAPOINT_VALUE); publishDatapoint(series, value); } }; public static boolean isRunning() { return sIsRunning; } @Override public void onCreate() { super.onCreate(); mDeviceId = String.format("an_%s", Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID)); mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE); mConnectionThread = new HandlerThread("mqtt-connection"); mConnectionThread.start(); mConnectionHandler = new Handler(mConnectionThread.getLooper()); mDataStore = new MqttDefaultFilePersistence(getCacheDir().getAbsolutePath()); IntentFilter f = new IntentFilter(); f.addAction(ACTION_PUBLISH_DATAPOINT); LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, f); } @Override public int onStartCommand(Intent intent, int flags, int startId) { String host = intent.getStringExtra(EXTRA_HOST); String apiKey = intent.getStringExtra(EXTRA_API_KEY); ArrayList<GerasSensorConfig> monitors = intent.getParcelableArrayListExtra(EXTRA_SENSOR_MONITORS); for (GerasSensorConfig m : monitors) { mSensorMonitorMap.put(m.getSensorType(), m); } mLocationMonitor = intent.getParcelableExtra(EXTRA_LOCATION_MONTITOR); mNotificationTarget = (Class) intent.getSerializableExtra(EXTRA_NOTIFICATION_TARGET_CLASS); sIsRunning = true; showNotification(); connect(host, apiKey); return Service.START_STICKY; } @Override public void onDestroy() { disconnect(); removeNotification(); //mConnectionThread.quit(); sIsRunning = false; LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver); super.onDestroy(); } @Override public IBinder onBind(Intent intent) { throw new UnsupportedOperationException("Not implemented"); } private void showNotification() { Intent intent = new Intent(getApplicationContext(), mNotificationTarget); PendingIntent target = PendingIntent.getActivity(getApplicationContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); NotificationCompat.Builder builder = new NotificationCompat.Builder(this).setOngoing(true) .setContentTitle("Geras MQTT service running").setContentText("Sensors are active") .setSmallIcon(R.drawable.ic_stat_active).setContentIntent(target); // issue the notification startForeground(NOTIFICATION_ID, builder.build()); } private void removeNotification() { // cancel the notification mNotificationManager.cancel(NOTIFICATION_ID); } private void connect(String host, String apiKey) { String url = String.format(Locale.US, "tcp://%s:%d", host, 1883); Log.i(TAG, "Connecting MQTT with URL: " + url); final GerasMqttConnectOptions opts = new GerasMqttConnectOptions(); opts.setCleanSession(true); opts.setUserName(""); opts.setPassword(apiKey.toCharArray()); try { mClient = new MqttClient(url, mDeviceId, mDataStore); } catch (MqttException e) { e.printStackTrace(); } mConnectionHandler.post(new Runnable() { @Override public void run() { try { mClient.connect(opts); mClient.setCallback(GerasMqttService.this); //mClient.subscribe("/time", 0); for (GerasSensorConfig m : mSensorMonitorMap.values()) { mSensorManager.registerListener(GerasMqttService.this, mSensorManager.getDefaultSensor(m.getSensorType()), m.getRateUs()); } if (mLocationMonitor != null) { mLocationManager.requestLocationUpdates(mLocationMonitor.getProvider(), mLocationMonitor.getMinTime(), mLocationMonitor.getMinDistance(), GerasMqttService.this); Location last = mLocationManager.getLastKnownLocation(mLocationMonitor.getProvider()); if (last != null) { publishLocationValue(last); } } Log.i(TAG, "Successfully connected"); } catch (MqttException e) { e.printStackTrace(); Toast.makeText(GerasMqttService.this, "Connection failed!", Toast.LENGTH_SHORT).show(); } } }); } private void publishDatapoint(final String series, final String value) { if (mClient != null && mClient.isConnected()) { mConnectionHandler.post(new Runnable() { @Override public void run() { try { Log.i(TAG, "pub " + series + ":" + value); mClient.publish(series, value.getBytes(), 0, false); } catch (MqttException e) { e.printStackTrace(); } } }); } } private void publishSensorValue(final int sensorType, final float[] values) { if (mClient != null && mClient.isConnected()) { mConnectionHandler.post(new Runnable() { @Override public void run() { GerasSensorConfig m = mSensorMonitorMap.get(sensorType); if (m != null) { final String series = m.getSeries(); String value; if (values.length >= 3) { // calculate rms value = String.valueOf(Math .sqrt(values[0] * values[0] + values[1] * values[1] + values[2] * values[2])); } else { value = String.valueOf(values[0]); } try { Log.i(TAG, "pub " + series + ":" + value); mClient.publish(series, value.getBytes(), 0, false); } catch (MqttException e) { e.printStackTrace(); } } } }); } } private void publishLocationValue(final Location location) { if (mClient != null && mClient.isConnected()) { mConnectionHandler.post(new Runnable() { @Override public void run() { final String seriesLat = mLocationMonitor.getSeries() + "/latitude"; final String seriesLng = mLocationMonitor.getSeries() + "/longitude"; final String valueLat = String.valueOf(location.getLatitude()); final String valueLng = String.valueOf(location.getLongitude()); try { Log.i(TAG, "pub " + seriesLat + ":" + valueLat); Log.i(TAG, "pub " + seriesLng + ":" + valueLng); mClient.publish(seriesLat, valueLat.getBytes(), 0, false); mClient.publish(seriesLng, valueLng.getBytes(), 0, false); } catch (MqttException e) { e.printStackTrace(); } } }); } } private void disconnect() { mSensorManager.unregisterListener(this); mLocationManager.removeUpdates(this); if (mClient != null && mClient.isConnected()) { mConnectionHandler.post(new Runnable() { @Override public void run() { try { mClient.disconnect(0); mClient = null; Log.i(TAG, "Successfully disconnected"); } catch (MqttException ex) { ex.printStackTrace(); } } }); } } // *** MQTT client callbacks *** @Override public void connectionLost(Throwable cause) { // TODO implement } @Override public void messageArrived(String topic, MqttMessage message) throws Exception { Log.i(TAG, " Topic:\t" + topic + " Message:\t" + new String(message.getPayload()) + " QoS:\t" + message.getQos()); } @Override public void deliveryComplete(IMqttDeliveryToken token) { // ignored } // *** sensor callbacks *** @Override public void onSensorChanged(SensorEvent event) { synchronized (this) { publishSensorValue(event.sensor.getType(), event.values); } } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { // ignored } // *** location callbacks *** @Override public void onLocationChanged(Location location) { publishLocationValue(location); } @Override public void onStatusChanged(String provider, int status, Bundle extras) { // ignored } @Override public void onProviderEnabled(String provider) { // ignored } @Override public void onProviderDisabled(String provider) { // ignored } }