package de.rhuber.homedash; import android.app.Notification; import android.app.PendingIntent; import android.app.Service; import android.app.job.JobInfo; import android.app.job.JobScheduler; import android.content.ComponentName; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.BitmapFactory; import android.os.Binder; import android.os.IBinder; import android.preference.PreferenceManager; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import com.jjoe64.motiondetection.MotionDetector; import com.jjoe64.motiondetection.MotionDetectorCallback; import org.eclipse.paho.android.service.MqttAndroidClient; import org.eclipse.paho.client.mqttv3.DisconnectedBufferOptions; import org.eclipse.paho.client.mqttv3.IMqttActionListener; import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; import org.eclipse.paho.client.mqttv3.IMqttMessageListener; import org.eclipse.paho.client.mqttv3.IMqttToken; import org.eclipse.paho.client.mqttv3.MqttCallbackExtended; import org.eclipse.paho.client.mqttv3.MqttConnectOptions; import org.eclipse.paho.client.mqttv3.MqttException; import org.eclipse.paho.client.mqttv3.MqttMessage; import org.json.JSONObject; import java.nio.charset.StandardCharsets; import java.util.Map; public class HomeDashService extends Service { private static final int ONGOING_NOTIFICATION_ID = 1; public static final String MQTT_COMMAND_WAKEUP = "wakeup"; public static final String MQTT_COMMAND_CLEAR_BROWSER_CACHE = "clearBrowserCache"; public static final String MQTT_COMMAND_JS_EXEC = "jsExec"; public static final String MQTT_COMMAND_URL = "url"; final Integer sensorJobId = 1; private final String TAG = HomeDashService.class.getName(); private final IBinder mBinder = new MqttServiceBinder(); MqttAndroidClient mqttAndroidClient; JobScheduler jobScheduler; SensorReader sensorReader; private String topicPrefix; SharedPreferences sharedPreferences; private final String MOTION_SENSOR_MOTION_DETECTED_JSON ="{\"sensor\":\"cameraMotionDetector\",\"unit\":\"Boolean\",\"value\":\"true\"}"; private FaceDetector faceDetector = null; private MotionDetector motionDetector; private MotionDetectorCallback motionDetectorCallback; public HomeDashService() { } @Override public IBinder onBind(Intent intent) { return mBinder; } @Override public void onTaskRemoved(Intent rootIntent) { super.onTaskRemoved(rootIntent); stopSensorJob(); stopMotionDetection(); stopFaceDetection(); stopMqttConnection(); stopSelf(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); stopSensorJob(); stopMotionDetection(); stopFaceDetection(); //stopMqttConnection(); } public void startMqttConnection(String serverUri, String clientId, final String topic, final String username, final String password) { if (mqttAndroidClient == null) { topicPrefix = topic; if (!topicPrefix.endsWith("/")) { topicPrefix += "/"; } mqttAndroidClient = new MqttAndroidClient(getApplicationContext(), serverUri, clientId); mqttAndroidClient.setCallback(new MqttCallbackExtended() { @Override public void connectionLost(Throwable cause) { Log.i("mqtt_service", "connectionLost ", cause); } @Override public void messageArrived(String topic, MqttMessage message) throws Exception { Log.i("mqtt_service", "messageArrived " + message); } @Override public void deliveryComplete(IMqttDeliveryToken token) { Log.i("mqtt_service", "deliveryComplete " + token); } @Override public void connectComplete(boolean reconnect, String serverURI) { Log.i("mqtt_service", "connected to " + serverURI); } }); MqttConnectOptions mqttConnectOptions = new MqttConnectOptions(); if (username.length() > 0) { mqttConnectOptions.setUserName(username); mqttConnectOptions.setPassword(password.toCharArray()); } mqttConnectOptions.setAutomaticReconnect(true); mqttConnectOptions.setCleanSession(false); try { mqttAndroidClient.connect(mqttConnectOptions, null, new IMqttActionListener() { @Override public void onSuccess(IMqttToken asyncActionToken) { DisconnectedBufferOptions disconnectedBufferOptions = new DisconnectedBufferOptions(); disconnectedBufferOptions.setBufferEnabled(true); disconnectedBufferOptions.setBufferSize(100); disconnectedBufferOptions.setPersistBuffer(false); disconnectedBufferOptions.setDeleteOldestMessages(false); mqttAndroidClient.setBufferOpts(disconnectedBufferOptions); subscribeToTopic(topicPrefix + "command"); } @Override public void onFailure(IMqttToken asyncActionToken, Throwable exception) { Log.i("mqtt_service", "Failed to connect to: ", exception); } }); } catch (MqttException ex) { ex.printStackTrace(); } startSensorJob(); } } public void updateSensorData(){ if(sensorReader==null){ sensorReader = new SensorReader(getApplicationContext()); } if(sharedPreferences.getBoolean(getString(R.string.setting_sensor_light_enable),false)){ sensorReader.getLightReading(new SensorReader.SensorDataListener() { @Override public void sensorData(Map<String, String> sensorData) { publishSensorMessage(sensorData, "brightness"); } }); } if(sharedPreferences.getBoolean(getString(R.string.setting_sensor_battery_enable),false)) { sensorReader.getBatteryReading(new SensorReader.SensorDataListener() { @Override public void sensorData(Map<String, String> sensorData) { publishSensorMessage(sensorData, "battery"); } }); } if(sharedPreferences.getBoolean(getString(R.string.setting_sensor_pressure_enable),false)) { sensorReader.getPressureReading(new SensorReader.SensorDataListener() { @Override public void sensorData(Map<String, String> sensorData) { publishSensorMessage(sensorData, "pressure"); } }); } } private void publishSensorMessage(Map<String, String> map, String topicPostfix){ String message = new JSONObject(map).toString(); publishMessage(message.getBytes(),topicPostfix); } public void stopMqttConnection(){ try { if(mqttAndroidClient != null && mqttAndroidClient.isConnected()) { mqttAndroidClient.disconnect(); } } catch (MqttException e) { e.printStackTrace(); } mqttAndroidClient = null; stopSensorJob(); } private void publishMessage(byte[] message, String topicPostfix){ if (mqttAndroidClient != null && mqttAndroidClient.isConnected()) { try { String test = new String(message, StandardCharsets.UTF_8); Log.i(TAG,test); mqttAndroidClient.publish(topicPrefix+"sensor/"+topicPostfix, message, 0, false); } catch (MqttException e) { e.printStackTrace(); } } } private boolean subscribeToTopic(final String subscriptionTopic) { if (mqttAndroidClient.isConnected()) { try { mqttAndroidClient.subscribe(subscriptionTopic, 0, null, new IMqttActionListener() { @Override public void onSuccess(IMqttToken asyncActionToken) { Log.i(TAG, "subscribed to: " + subscriptionTopic); } @Override public void onFailure(IMqttToken asyncActionToken, Throwable exception) { Log.i(TAG, "Failed to subscribe to: " + subscriptionTopic); } }); mqttAndroidClient.subscribe(subscriptionTopic, 0, new IMqttMessageListener() { @Override public void messageArrived(String topic, MqttMessage message) throws Exception { String payload = new String(message.getPayload(), StandardCharsets.UTF_8); JSONObject jsonObject = new JSONObject(payload); String url = jsonObject.has(MQTT_COMMAND_URL) ? jsonObject.getString(MQTT_COMMAND_URL) : null; if(url != null){ Intent intent = new Intent(BrowserActivity.BROADCAST_ACTION_LOAD_URL); intent.putExtra(BrowserActivity.BROADCAST_ACTION_LOAD_URL, url); LocalBroadcastManager bm = LocalBroadcastManager.getInstance(getApplicationContext()); bm.sendBroadcast(intent); } if(jsonObject.has(MQTT_COMMAND_WAKEUP)){ switchScreenOn(); } if(jsonObject.has(MQTT_COMMAND_CLEAR_BROWSER_CACHE)){ clearBrowserCache(); } String js = jsonObject.has(MQTT_COMMAND_JS_EXEC) ? jsonObject.getString("MQTT_COMMAND_JS_EXEC") : null; if(js != null){ Intent intent = new Intent(BrowserActivity.BROADCAST_ACTION_JS_EXEC); intent.putExtra(BrowserActivity.BROADCAST_ACTION_JS_EXEC, js); LocalBroadcastManager bm = LocalBroadcastManager.getInstance(getApplicationContext()); bm.sendBroadcast(intent); } Log.i(TAG, "messageArrived: " + jsonObject); } }); return true; } catch (MqttException ex) { System.err.println("Exception whilst subscribing"); ex.printStackTrace(); } } return false; } public void startSensorJob(){ if(jobScheduler == null) { jobScheduler = (JobScheduler) getApplication().getSystemService(getApplicationContext().JOB_SCHEDULER_SERVICE); ComponentName mServiceComponent = new ComponentName(getApplicationContext(), SensorJob.class); JobInfo.Builder builder = new JobInfo.Builder(sensorJobId, mServiceComponent); Integer updateFrequencySeconds = sharedPreferences.getInt(getString(R.string.setting_sensor_update_frequency),60); if(updateFrequencySeconds!= 0){ Integer updateFrequencyMiliSeconds = 1000 * updateFrequencySeconds; builder.setPeriodic(updateFrequencyMiliSeconds); jobScheduler.schedule(builder.build()); } } } public void stopSensorJob(){ if(jobScheduler != null ){ jobScheduler.cancel(sensorJobId); jobScheduler = null; } } public class MqttServiceBinder extends Binder { HomeDashService getService() { return HomeDashService.this; } } @Override public void onCreate() { super.onCreate(); sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); startForeground(); } public void startForeground(){ Intent notificationIntent = new Intent(this, HomeDashService.class); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); Notification notification = new Notification.Builder(this) .setContentTitle(getText(R.string.homedash_service_notification_title)) .setContentText(getText(R.string.homedash_service_notification_message)) .setSmallIcon(R.drawable.ic_home_white_24dp) .setLargeIcon(BitmapFactory.decodeResource(getApplication().getResources(),R.mipmap.ic_launcher)) .setContentIntent(pendingIntent) .setLocalOnly(true) .build(); startForeground(ONGOING_NOTIFICATION_ID, notification); } public void startFaceDetection(){ if(faceDetector==null){ faceDetector = new FaceDetector(); faceDetector.startDetection(new FaceDetector.FaceDetectionCallback() { @Override public void facesDetected(int faceCount) { switchScreenOn(); } }); } } public void stopFaceDetection(){ if(faceDetector != null) { faceDetector.stopDetection(); faceDetector = null; } } public void startMotionDetection(){ if(motionDetector==null){ motionDetector = new MotionDetector(this, null); motionDetector.setCheckInterval(Long.valueOf(sharedPreferences.getString(getString(R.string.key_setting_motion_detection_intervall),"500"))); motionDetector.setLeniency(Integer.valueOf(sharedPreferences.getString(getString(R.string.key_setting_motion_detection_leniency),"20"))); motionDetector.setMinLuma(Integer.valueOf(sharedPreferences.getString(getString(R.string.key_setting_motion_detection_min_luma),"1000"))); if(motionDetectorCallback==null){ motionDetectorCallback = new MotionDetectorCallback() { @Override public void onMotionDetected() { switchScreenOn(); publishMessage(MOTION_SENSOR_MOTION_DETECTED_JSON.getBytes(StandardCharsets.UTF_8),"motion"); Log.i(TAG, "Motion detected"); } @Override public void onTooDark() { Log.i(TAG, "Too dark for motion detection"); } }; } motionDetector.setMotionDetectorCallback(motionDetectorCallback); motionDetector.onResume(); } } public void stopMotionDetection() { if (motionDetector != null) { motionDetector.onPause(); motionDetector = null; motionDetectorCallback = null; } } private void switchScreenOn(){ Intent intent = new Intent(BrowserActivity.BROADCAST_ACTION_SCREEN_ON); LocalBroadcastManager bm = LocalBroadcastManager.getInstance(getApplicationContext()); bm.sendBroadcast(intent); } private void clearBrowserCache(){ Intent intent = new Intent(BrowserActivity.BROADCAST_ACTION_CLEAR_BROWSER_CACHE); LocalBroadcastManager bm = LocalBroadcastManager.getInstance(getApplicationContext()); bm.sendBroadcast(intent); } }