diff --git a/.gitignore b/.gitignore index 74284c4..1428103 100644 --- a/.gitignore +++ b/.gitignore @@ -148,3 +148,5 @@ fabric.properties /app/app-release.apk Automation_settings.xml +/app/googlePlayFlavor/ +/.idea/deploymentTargetDropDown.xml diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml deleted file mode 100644 index 078aa39..0000000 --- a/.idea/deploymentTargetDropDown.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 0ad877c..f051eb1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,8 +11,8 @@ android { compileSdkVersion 29 buildToolsVersion '29.0.2' useLibrary 'org.apache.http.legacy' - versionCode 113 - versionName "1.6.43" + versionCode 114 + versionName "1.7" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/googlePlayFlavor/release/output-metadata.json b/app/googlePlayFlavor/release/output-metadata.json index 03ae99d..d2a31df 100644 --- a/app/googlePlayFlavor/release/output-metadata.json +++ b/app/googlePlayFlavor/release/output-metadata.json @@ -11,8 +11,8 @@ "type": "SINGLE", "filters": [], "attributes": [], - "versionCode": 113, - "versionName": "1.6.43-googlePlay", + "versionCode": 114, + "versionName": "1.7-googlePlay", "outputFile": "app-googlePlayFlavor-release.apk" } ], diff --git a/app/src/apkFlavor/AndroidManifest.xml b/app/src/apkFlavor/AndroidManifest.xml index acac67e..da2d840 100644 --- a/app/src/apkFlavor/AndroidManifest.xml +++ b/app/src/apkFlavor/AndroidManifest.xml @@ -132,10 +132,23 @@ android:scheme="package" />--> - + + + + + + + + @@ -145,6 +158,7 @@ + + @@ -204,6 +219,7 @@ + diff --git a/app/src/apkFlavor/java/com/jens/automation2/Rule.java b/app/src/apkFlavor/java/com/jens/automation2/Rule.java index fd26414..c828fbc 100644 --- a/app/src/apkFlavor/java/com/jens/automation2/Rule.java +++ b/app/src/apkFlavor/java/com/jens/automation2/Rule.java @@ -1,46 +1,22 @@ package com.jens.automation2; +import static com.jens.automation2.Trigger.triggerParameter2Split; + import android.annotation.SuppressLint; -import android.app.Notification; -import android.bluetooth.BluetoothDevice; import android.content.Context; import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; import android.os.Looper; -import android.os.Parcelable; -import android.service.notification.StatusBarNotification; -import android.telephony.TelephonyManager; import android.util.Log; import android.widget.Toast; import com.google.android.gms.location.DetectedActivity; -import com.jens.automation2.location.LocationProvider; -import com.jens.automation2.location.WifiBroadcastReceiver; import com.jens.automation2.receivers.ActivityDetectionReceiver; -import com.jens.automation2.receivers.BatteryReceiver; -import com.jens.automation2.receivers.BluetoothReceiver; -import com.jens.automation2.receivers.ConnectivityReceiver; -import com.jens.automation2.receivers.HeadphoneJackListener; -import com.jens.automation2.receivers.NfcReceiver; -import com.jens.automation2.receivers.NoiseListener; -import com.jens.automation2.receivers.NotificationListener; -import com.jens.automation2.receivers.PhoneStatusListener; -import com.jens.automation2.receivers.ProcessListener; import java.sql.Time; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; -import static com.jens.automation2.Trigger.triggerParameter2Split; -import static com.jens.automation2.receivers.NotificationListener.EXTRA_TEXT; -import static com.jens.automation2.receivers.NotificationListener.EXTRA_TITLE; - -import androidx.core.app.NotificationCompat; - -import org.apache.commons.lang3.StringUtils; - public class Rule implements Comparable { @@ -349,6 +325,8 @@ public class Rule implements Comparable return true; case setWifiTethering: return true; + case setBluetoothTethering: + return true; default: break; } @@ -356,12 +334,38 @@ public class Rule implements Comparable return false; } + + public boolean hasNotAppliedSinceLastExecution() + { + for(Trigger oneTrigger : this.getTriggerSet()) + { + if (oneTrigger.hasStateNotAppliedSinceLastRuleExecution()) + return true; + } + + return false; + } + + public boolean getsGreenLight(Context context) + { + if(applies(context)) + { + if(hasNotAppliedSinceLastExecution()) + return true; + else + Miscellaneous.logEvent("i", "getsGreenLight()", "Rule " + getName() + " has not flipped since its last execution.", 4); + } + else + Miscellaneous.logEvent("i", "getsGreenLight()", "Rule " + getName() + " does not apply.", 4); + + return false; + } public boolean applies(Context context) { if(AutomationService.getInstance() == null) { - Miscellaneous.logEvent("i", "RuleCheck", "Automation service not running. Rule cannot apply.", 3); + Miscellaneous.logEvent("i", "RuleCheck", "Automation service not running. Rule " + getName() + " cannot apply.", 3); return false; } @@ -369,553 +373,8 @@ public class Rule implements Comparable { for(Trigger oneTrigger : this.getTriggerSet()) { - if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.pointOfInterest)) - { - // Am I here? - PointOfInterest activePoi = PointOfInterest.getActivePoi(); - if(activePoi != null) //entering one - { - if(oneTrigger.getPointOfInterest() != null) - { - if(activePoi.equals(oneTrigger.getPointOfInterest())) - { - if(!oneTrigger.getTriggerParameter()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Rule doesn't apply. We're entering POI: " + oneTrigger.getPointOfInterest().getName() + ", not leaving it.", 4); - return false; - } - } - else - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Rule doesn't apply. This is " + activePoi.getName() + ", not " + oneTrigger.getPointOfInterest().getName() + ".", 4); - return false; - } - } - else if(oneTrigger.getPointOfInterest() == null) - { - if(oneTrigger.getTriggerParameter()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Rule doesn't apply. We're at a POI. Rule specifies not at none, so leaving any.", 4); - return false; - } - } - } - else //leaving one - { - // We are not at any POI. But if this trigger requires us NOT to be there, that may be fine. - if(oneTrigger.getPointOfInterest() != null) - { -// if(activePoi.equals(oneTrigger.getPointOfInterest())) -// { - if(!oneTrigger.getTriggerParameter()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "We are not at POI \"" + oneTrigger.getPointOfInterest().getName() + "\". But since that's required by this rule that's fine.", 4); - } - else - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Rule doesn't apply. We're not at POI \"" + oneTrigger.getPointOfInterest().getName() + "\".", 3); - return false; - } -// } - } - else if(oneTrigger.getPointOfInterest() == null) - { - if(!oneTrigger.getTriggerParameter()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Rule doesn't apply. We're at no POI. Rule specifies to be at anyone.", 5); - return false; - } - } - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.timeFrame)) - { - Date now = new Date(); - String timeString = String.valueOf(now.getHours()) + ":" + String.valueOf(now.getMinutes()) + ":" + String.valueOf(now.getSeconds()); - Time nowTime = Time.valueOf(timeString); - Calendar calNow = Calendar.getInstance(); - - - if(oneTrigger.getTimeFrame().getDayList().contains(calNow.get(Calendar.DAY_OF_WEEK))) - { - if( - // Regular case, start time is lower than end time - ( - Miscellaneous.compareTimes(oneTrigger.getTimeFrame().getTriggerTimeStart(), nowTime) >= 0 - && - Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0 - ) - || - // Other case, start time higher than end time, timeframe goes over midnight - ( - Miscellaneous.compareTimes(oneTrigger.getTimeFrame().getTriggerTimeStart(), oneTrigger.getTimeFrame().getTriggerTimeStop()) < 0 - && - (Miscellaneous.compareTimes(oneTrigger.getTimeFrame().getTriggerTimeStart(), nowTime) >= 0 - || - Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0) - ) - - ) - { - // We are in the timeframe - Miscellaneous.logEvent("i", "TimeFrame", "We're currently (" + calNow.getTime().toString() + ") in the specified TimeFrame (" + oneTrigger.getTimeFrame().toString() + "). Trigger of Rule " + this.getName() + " applies.", 3); - if(oneTrigger.getTriggerParameter()) - { - Miscellaneous.logEvent("i", "TimeFrame", "That's what's specified. Trigger of Rule " + this.getName() + " applies.", 3); - //return true; - } - else - { - Miscellaneous.logEvent("i", "TimeFrame", "That's not what's specified. Trigger of Rule " + this.getName() + " doesn't apply.", 3); - return false; - } - } - else - { - Miscellaneous.logEvent("i", "TimeFrame", "We're currently (" + calNow.getTime().toString() + ", Day: " + String.valueOf(calNow.get(Calendar.DAY_OF_WEEK)) + ") not in the specified TimeFrame (" + oneTrigger.getTimeFrame().toString() + ") because of the time. Trigger of Rule " + this.getName() + " doesn\'t apply..", 5); - if(!oneTrigger.getTriggerParameter()) - { - Miscellaneous.logEvent("i", "TimeFrame", "That's what's specified. Trigger of Rule " + this.getName() + " applies.", 5); - //return true; - } - else - { - Miscellaneous.logEvent("i", "TimeFrame", "That's not what's specified. Trigger of Rule " + this.getName() + " doesn't apply.", 5); - return false; - } - // return false; - } - } - else - { - Miscellaneous.logEvent("i", "TimeFrame", "We're currently (" + calNow.getTime().toString() + ", Day: " + String.valueOf(calNow.get(Calendar.DAY_OF_WEEK)) + ") not in the specified TimeFrame (" + oneTrigger.getTimeFrame().toString() + ") because of the day. Trigger of Rule " + this.getName() + " doesn\'t apply.", 5); - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.charging)) - { - if(BatteryReceiver.isDeviceCharging(context) == 0) - { - return false; // unknown charging state, can't activate rule under these conditions - } - else if(BatteryReceiver.isDeviceCharging(context) == 1) - { - if(oneTrigger.getTriggerParameter()) //rule says when charging, but we're currently discharging - return false; - } - else if(BatteryReceiver.isDeviceCharging(context) == 2) - { - if(!oneTrigger.getTriggerParameter()) //rule says when discharging, but we're currently charging - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.usb_host_connection)) - { - if(BatteryReceiver.isUsbHostConnected() != oneTrigger.getTriggerParameter()) - { - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.batteryLevel)) - { - if(oneTrigger.getTriggerParameter()) - { - if(BatteryReceiver.getBatteryLevel() <= oneTrigger.getBatteryLevel()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryLowerThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3); - return false; - } - } - else - { - if(oneTrigger.getBatteryLevel() >= oneTrigger.getBatteryLevel()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryHigherThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3); - return false; - } - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.speed)) - { - if(oneTrigger.getTriggerParameter()) - { - if(LocationProvider.getSpeed() < oneTrigger.getSpeed()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreSlowerThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3); - return false; - } - } - else - { - if(LocationProvider.getSpeed() > oneTrigger.getSpeed()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreFasterThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3); - return false; - } - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.noiseLevel)) - { - if(oneTrigger.getTriggerParameter()) - { - if(NoiseListener.getNoiseLevelDb() < oneTrigger.getNoiseLevelDb()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyItsQuieterThan) + " " + String.valueOf(oneTrigger.getNoiseLevelDb()), 3); - return false; - } - } - else - { - if(NoiseListener.getNoiseLevelDb() > oneTrigger.getNoiseLevelDb()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyItsLouderThan) + " " + String.valueOf(oneTrigger.getNoiseLevelDb()), 3); - return false; - } - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.wifiConnection)) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Checking for wifi state", 4); - if(oneTrigger.getTriggerParameter() == WifiBroadcastReceiver.lastConnectedState) // connected / disconnected - { - if(oneTrigger.getTriggerParameter2().length() > 0) // only check if any wifi name specified, otherwise any wifi will do - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Wifi name specified, checking that.", 4); - if(!WifiBroadcastReceiver.getLastWifiSsid().equals(oneTrigger.getTriggerParameter2())) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(context.getResources().getString(R.string.ruleDoesntApplyNotTheCorrectSsid), oneTrigger.getTriggerParameter2(), WifiBroadcastReceiver.getLastWifiSsid()), 3); - return false; - } - else - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Wifi name matches. Rule will apply.", 4); - } - else - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "No wifi name specified, any will do.", 4); - } - else - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Wifi state not correct, demanded " + String.valueOf(oneTrigger.getTriggerParameter() + ", got " + String.valueOf(WifiBroadcastReceiver.lastConnectedState)), 4); - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.process_started_stopped)) - { - boolean running = ProcessListener.getRunningApps().contains(oneTrigger.getProcessName()); - - if(running) - Miscellaneous.logEvent("i", "ProcessMonitoring", "App " + oneTrigger.getProcessName() + " is currently running.", 4); - else - Miscellaneous.logEvent("i", "ProcessMonitoring", "App " + oneTrigger.getProcessName() + " is not running.", 4); - - if(running != oneTrigger.getTriggerParameter()) - { - Miscellaneous.logEvent("i", "ProcessMonitoring", "Trigger doesn't apply.", 4); - return false; - } - - Miscellaneous.logEvent("i", "ProcessMonitoring", "Trigger applies.", 4); - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.airplaneMode)) - { - if(ConnectivityReceiver.isAirplaneMode(context) != oneTrigger.getTriggerParameter()) - { - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.roaming)) - { - if(ConnectivityReceiver.isRoaming(context) != oneTrigger.getTriggerParameter()) - { - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.phoneCall)) - { - String[] elements = oneTrigger.getTriggerParameter2().split(triggerParameter2Split); - // state dir number - - if(elements[2].equals(Trigger.triggerPhoneCallNumberAny) || Miscellaneous.comparePhoneNumbers(PhoneStatusListener.getLastPhoneNumber(), elements[2]) || (Miscellaneous.isRegularExpression(elements[2]) && PhoneStatusListener.getLastPhoneNumber().matches(elements[2]))) - { - //if(PhoneStatusListener.isInACall() == oneTrigger.getTriggerParameter()) - if( - (elements[0].equals(Trigger.triggerPhoneCallStateRinging) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_RINGING) - || - (elements[0].equals(Trigger.triggerPhoneCallStateStarted) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_OFFHOOK) - || - (elements[0].equals(Trigger.triggerPhoneCallStateStopped) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_IDLE) - ) - { - if( - elements[1].equals(Trigger.triggerPhoneCallDirectionAny) - || - (elements[1].equals(Trigger.triggerPhoneCallDirectionIncoming) && PhoneStatusListener.getLastPhoneDirection() == 1) - || - (elements[1].equals(Trigger.triggerPhoneCallDirectionOutgoing) && PhoneStatusListener.getLastPhoneDirection() == 2) - ) - { - // Trigger conditions are met - } - else - { - Miscellaneous.logEvent("i", "Rule", "Rule doesn't apply. Wrong direction. Demanded: " + String.valueOf(oneTrigger.getPhoneDirection()) + ", got: " + String.valueOf(PhoneStatusListener.getLastPhoneDirection()), 4); - return false; - } - } - else - { - Miscellaneous.logEvent("i", "Rule", "Rule doesn't apply. Wrong call status. Demanded: " + String.valueOf(oneTrigger.getTriggerParameter()) + ", got: " + String.valueOf(PhoneStatusListener.isInACall()), 4); - return false; - } - } - else - { - Miscellaneous.logEvent("i", "Rule", "Rule doesn't apply. Wrong phone number. Demanded: " + oneTrigger.getPhoneNumber() + ", got: " + PhoneStatusListener.getLastPhoneNumber(), 4); - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.nfcTag)) - { - if(NfcReceiver.lastReadLabel == null) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyNoTagLabel), 3); - return false; - } - else if(!NfcReceiver.lastReadLabel.equals(oneTrigger.getNfcTagId())) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWrongTagLabel) + " " + NfcReceiver.lastReadLabel + " / " + oneTrigger.getNfcTagId(), 3); - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.activityDetection)) - { - if(ActivityDetectionReceiver.getActivityDetectionLastResult() != null) - { - boolean found = false; - for(DetectedActivity oneDetectedActivity : ActivityDetectionReceiver.getActivityDetectionLastResult().getProbableActivities()) - { - if(oneDetectedActivity.getType() == oneTrigger.getActivityDetectionType()) - found = true; - } - - if(!found) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(context.getResources().getString(R.string.ruleDoesntApplyActivityNotPresent), ActivityDetectionReceiver.getDescription(oneTrigger.getActivityDetectionType())), 3); - return false; - } - else - { - for(DetectedActivity oneDetectedActivity : ActivityDetectionReceiver.getActivityDetectionLastResult().getProbableActivities()) - { - if(oneDetectedActivity.getType() == oneTrigger.getActivityDetectionType() && oneDetectedActivity.getConfidence() < Settings.activityDetectionRequiredProbability) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(context.getResources().getString(R.string.ruleDoesntApplyActivityGivenButTooLowProbability), ActivityDetectionReceiver.getDescription(oneDetectedActivity.getType()), String.valueOf(oneDetectedActivity.getConfidence()), String.valueOf(Settings.activityDetectionRequiredProbability)), 3); - return false; - } - } - } - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.bluetoothConnection)) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Checking for bluetooth...", 4); - - if(oneTrigger.getBluetoothDeviceAddress().equals("")) - { - if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) - { - if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter()) - return false; - } - else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) - { - if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter()) - return false; - } - else - { - // range - if(BluetoothReceiver.isAnyDeviceInRange() != oneTrigger.getTriggerParameter()) - return false; - } - } - else if(oneTrigger.getBluetoothDeviceAddress().equals("")) - { - if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) - { - if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter()) - return false; - } - else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) - { - if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter()) - return false; - } - else - { - // range - if(BluetoothReceiver.isAnyDeviceInRange() == oneTrigger.getTriggerParameter()) - return false; - } - } - else if(oneTrigger.getBluetoothDeviceAddress().length() > 0) - { - if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) - { - if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter()) - return false; - } - else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) - { - if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter()) - return false; - } - else - { - // range - if(BluetoothReceiver.isDeviceInRange(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter()) - return false; - } - } - else - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyStateNotCorrect), 3); - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.headsetPlugged)) - { - if(HeadphoneJackListener.isHeadsetConnected() != oneTrigger.getTriggerParameter()) - return false; - else - if(oneTrigger.getHeadphoneType() != 2 && oneTrigger.getHeadphoneType() != HeadphoneJackListener.getHeadphoneType()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWrongHeadphoneType), 3); - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.notification)) - { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) - { - String[] params = oneTrigger.getTriggerParameter2().split(triggerParameter2Split); - - String myApp = params[0]; - String myTitleDir = params[1]; - String requiredTitle = params[2]; - String myTextDir = params[3]; - String requiredText; - if (params.length >= 5) - requiredText = params[4]; - else - requiredText = ""; - - if(oneTrigger.getTriggerParameter()) - { - // Check an active notification that is still there - - boolean foundMatch = false; - - for (StatusBarNotification sbn : NotificationListener.getInstance().getActiveNotifications()) - { - if(getLastExecution() == null || sbn.getPostTime() > this.lastExecution.getTimeInMillis()) - { - String notificationApp = sbn.getPackageName(); - String notificationTitle = null; - String notificationText = null; - - Miscellaneous.logEvent("i", "NotificationCheck", "Checking if this notification matches our rule " + this.getName() + ". App: " + notificationApp + ", title: " + notificationTitle + ", text: " + notificationText, 5); - - if (!myApp.equals("-1")) - { - if (!notificationApp.equalsIgnoreCase(myApp)) - { - Miscellaneous.logEvent("i", "NotificationCheck", "Notification app name does not match rule.", 5); - continue; - } - } - - /* - If there are multiple notifications ("stacked") title or text might be null: - https://stackoverflow.com/questions/28047767/notificationlistenerservice-not-reading-text-of-stacked-notifications - */ - - Bundle extras = sbn.getNotification().extras; - - // T I T L E - if (extras.containsKey(EXTRA_TITLE)) - notificationTitle = sbn.getNotification().extras.getString(EXTRA_TITLE); - - if (!StringUtils.isEmpty(requiredTitle)) - { - if (!Miscellaneous.compare(myTitleDir, requiredTitle, notificationTitle)) - { - Miscellaneous.logEvent("i", "NotificationCheck", "Notification title does not match rule.", 5); - continue; - } - } - else - Miscellaneous.logEvent("i", "NotificationCheck", "A required title for a notification trigger was not specified.", 5); - - // T E X T - - if (extras.containsKey(EXTRA_TEXT)) - notificationText = sbn.getNotification().extras.getString(EXTRA_TEXT); - - if (!StringUtils.isEmpty(requiredText)) - { - if (!Miscellaneous.compare(myTextDir, requiredText, notificationText)) - { - Miscellaneous.logEvent("i", "NotificationCheck", "Notification text does not match rule.", 5); - continue; - } - } - else - Miscellaneous.logEvent("i", "NotificationCheck", "A required text for a notification trigger was not specified.", 5); - - foundMatch = true; - break; - } - } - - if(!foundMatch) - return false; - } - else - { - // check a notification that is gone - - if(NotificationListener.getLastNotification() != null) - { - if(!NotificationListener.getLastNotification().isCreated()) - { - String app = NotificationListener.getLastNotification().getApp(); - String title = NotificationListener.getLastNotification().getTitle(); - String text = NotificationListener.getLastNotification().getText(); - - if (!myApp.equals("-1")) - { - if (!app.equalsIgnoreCase(myApp)) - return false; - } - - if (requiredTitle.length() > 0) - { - if (!Miscellaneous.compare(myTitleDir, title, requiredTitle)) - return false; - } - - if (requiredText.length() > 0) - { - if (!Miscellaneous.compare(myTextDir, text, requiredText)) - return false; - } - } - else - return false; - } - } - } - } + if (!oneTrigger.applies(null, context)) + return false; } return true; @@ -925,6 +384,44 @@ public class Rule implements Comparable return false; } + /** + * This is actually a function of the class Trigger, but Rule is already distinguished by flavors, Trigger is not. + * Hence it is here. + * @param oneTrigger + * @return + */ + boolean checkActivityDetection(Trigger oneTrigger) + { + if (ActivityDetectionReceiver.getActivityDetectionLastResult() != null) + { + boolean found = false; + for (DetectedActivity oneDetectedActivity : ActivityDetectionReceiver.getActivityDetectionLastResult().getProbableActivities()) + { + if (oneDetectedActivity.getType() == oneTrigger.getActivityDetectionType()) + found = true; + } + + if (!found) + { + Miscellaneous.logEvent("i", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyActivityNotPresent), getName(), ActivityDetectionReceiver.getDescription(oneTrigger.getActivityDetectionType())), 3); + return false; + } + else + { + for (DetectedActivity oneDetectedActivity : ActivityDetectionReceiver.getActivityDetectionLastResult().getProbableActivities()) + { + if (oneDetectedActivity.getType() == oneTrigger.getActivityDetectionType() && oneDetectedActivity.getConfidence() < Settings.activityDetectionRequiredProbability) + { + Miscellaneous.logEvent("i", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyActivityGivenButTooLowProbability), getName(), ActivityDetectionReceiver.getDescription(oneDetectedActivity.getType()), String.valueOf(oneDetectedActivity.getConfidence()), String.valueOf(Settings.activityDetectionRequiredProbability)), 3); + return false; + } + } + } + } + + return true; + } + private class ActivateRuleTask extends AsyncTask { boolean wasActivated = false; @@ -942,7 +439,8 @@ public class Rule implements Comparable if (Looper.myLooper() == null) Looper.prepare(); - + + setLastExecution(Calendar.getInstance()); wasActivated = activateInternally((AutomationService)params[0], (Boolean)params[1]); return null; @@ -967,7 +465,7 @@ public class Rule implements Comparable */ if(wasActivated) { - setLastExecution(Calendar.getInstance()); +// setLastExecution(Calendar.getInstance()); AutomationService.updateNotification(); ActivityMainScreen.updateMainScreen(); super.onPostExecute(result); @@ -985,8 +483,9 @@ public class Rule implements Comparable boolean notLastActive = getLastActivatedRule() == null || !getLastActivatedRule().equals(Rule.this); boolean doToggle = ruleToggle && isActuallyToggable; - if(notLastActive || force || doToggle) - { + //if(notLastActive || force || doToggle) +// if(force || doToggle) +// { String message; if(!doToggle) message = String.format(automationService.getResources().getString(R.string.ruleActivate), Rule.this.getName()); @@ -1015,6 +514,7 @@ public class Rule implements Comparable { Rule.ruleRunHistory.add(0, Rule.this); // add at beginning for better visualization Rule.lastActivatedRuleActivationTime = new Date(); + while(ruleRunHistory.size() > Settings.rulesThatHaveBeenRanHistorySize) ruleRunHistory.remove(ruleRunHistory.size()-1); String history = ""; @@ -1030,12 +530,12 @@ public class Rule implements Comparable } Miscellaneous.logEvent("i", "Rule", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleActivationComplete), Rule.this.getName()), 2); - } - else - { - Miscellaneous.logEvent("i", "Rule", "Request to activate rule " + Rule.this.getName() + ", but it is the last one that was activated. Won't do it again.", 3); - return false; - } +// } +// else +// { +// Miscellaneous.logEvent("i", "Rule", "Request to activate rule " + Rule.this.getName() + ", but it is the last one that was activated. Won't do it again.", 3); +// return false; +// } return true; } @@ -1044,15 +544,27 @@ public class Rule implements Comparable public void activate(AutomationService automationService, boolean force) { ActivateRuleTask task = new ActivateRuleTask(); - -// if(Settings.startNewThreadForRuleActivation) - task.execute(automationService, force); -// else -// { -// task.activateInternally(automationService, force); -// AutomationService.updateNotification(); -// ActivityMainScreen.updateMainScreen(); -// } + task.execute(automationService, force); + } + + public static ArrayList findRuleCandidates(Trigger.Trigger_Enum triggerType) + { + ArrayList ruleCandidates = new ArrayList(); + + for(Rule oneRule : ruleCollection) + { + innerloop: + for(Trigger oneTrigger : oneRule.getTriggerSet()) + { + if(oneTrigger.getTriggerType() == triggerType) + { + ruleCandidates.add(oneRule); + break innerloop; // we don't need to check the other triggers in the same rule + } + } + } + + return ruleCandidates; } public static ArrayList findRuleCandidatesByPoi(PointOfInterest searchPoi, boolean triggerParameter) @@ -1096,7 +608,7 @@ public class Rule implements Comparable return ruleCandidates; } - public static ArrayList findRuleCandidatesByTimeFrame(TimeFrame searchTimeFrame, boolean triggerParameter) + /*public static ArrayList findRuleCandidatesByTimeFrame(TimeFrame searchTimeFrame, boolean triggerParameter) { ArrayList ruleCandidates = new ArrayList(); @@ -1117,8 +629,9 @@ public class Rule implements Comparable } return ruleCandidates; - } - public static ArrayList findRuleCandidatesByTime(Time searchTime) + }*/ + + /*public static ArrayList findRuleCandidatesByTime(Time searchTime) { Miscellaneous.logEvent("i", "RuleSearch", "Searching for rules with TimeFrame with time " + searchTime.toString() + ". RuleCollection-Size: " + String.valueOf(ruleCollection.size()), 3);; ArrayList ruleCandidates = new ArrayList(); @@ -1137,7 +650,7 @@ public class Rule implements Comparable if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() > oneTrigger.getTimeFrame().getTriggerTimeStop().getTime()) { - Miscellaneous.logEvent("i", "Timeframe search", "Rule goes over midnight.", 5); + Miscellaneous.logEvent("i", "Timeframe search", "Rule (" + oneRule.getName() + ") stretches over midnight.", 5); if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() <= searchTime.getTime() || searchTime.getTime() <= oneTrigger.getTimeFrame().getTriggerTimeStop().getTime()+20000) //add 20 seconds because of delay { ruleCandidates.add(oneRule); @@ -1146,7 +659,7 @@ public class Rule implements Comparable } else if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() <= searchTime.getTime() && searchTime.getTime() <= oneTrigger.getTimeFrame().getTriggerTimeStop().getTime()+20000) //add 20 seconds because of delay { - Miscellaneous.logEvent("i", "RuleSearch", "Rule found with TimeFrame with time " + searchTime.toString(), 3); + Miscellaneous.logEvent("i", "RuleSearch", "Rule found (" + oneRule.getName() + ") with TimeFrame with time " + searchTime.toString(), 3); ruleCandidates.add(oneRule); break innerloop; //if the poi is found we don't need to search the other triggers in the same rule } @@ -1157,29 +670,9 @@ public class Rule implements Comparable Miscellaneous.logEvent("i", "RuleSearch", String.valueOf(ruleCandidates.size()) + " Rule(s) found with TimeFrame with time " + searchTime.toString(), 3); return ruleCandidates; - } + }*/ - public static ArrayList findRuleCandidatesByTimeFrame() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.timeFrame) - { - ruleCandidates.add(oneRule); - break innerloop; //if the poi is found we don't need to search the other triggers in the same rule - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByCharging(boolean triggerParameter) + /*public static ArrayList findRuleCandidatesByCharging(boolean triggerParameter) { ArrayList ruleCandidates = new ArrayList(); @@ -1200,9 +693,9 @@ public class Rule implements Comparable } return ruleCandidates; - } + }*/ - public static ArrayList findRuleCandidatesByUsbHost(boolean triggerParameter) + /*public static ArrayList findRuleCandidatesByUsbHost(boolean triggerParameter) { ArrayList ruleCandidates = new ArrayList(); @@ -1223,147 +716,9 @@ public class Rule implements Comparable } return ruleCandidates; - } + }*/ - public static ArrayList findRuleCandidatesByBatteryLevel() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.batteryLevel) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //if the poi is found we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesBySpeed() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.speed) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //if the poi is found we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByNoiseLevel() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.noiseLevel) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //if the poi is found we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByWifiConnection() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.wifiConnection) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByBluetoothConnection() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.bluetoothConnection) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByProcess() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.process_started_stopped) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByAirplaneMode(boolean triggerParameter) + /*public static ArrayList findRuleCandidatesByAirplaneMode(boolean triggerParameter) { ArrayList ruleCandidates = new ArrayList(); @@ -1384,9 +739,9 @@ public class Rule implements Comparable } return ruleCandidates; - } + }*/ - public static ArrayList findRuleCandidatesByRoaming(boolean triggerParameter) + /*public static ArrayList findRuleCandidatesByRoaming(boolean triggerParameter) { ArrayList ruleCandidates = new ArrayList(); @@ -1407,9 +762,9 @@ public class Rule implements Comparable } return ruleCandidates; - } + }*/ - public static ArrayList findRuleCandidatesByPhoneCall(String direction) + /*public static ArrayList findRuleCandidatesByPhoneCall(String direction) { ArrayList ruleCandidates = new ArrayList(); @@ -1431,73 +786,7 @@ public class Rule implements Comparable } return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByNfc() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.nfcTag) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidates(Trigger.Trigger_Enum triggerType) - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == triggerType) - { - ruleCandidates.add(oneRule); - break innerloop; //we don't need to search the other triggers in the same rule - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByActivityDetection() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.activityDetection) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } + }*/ public static ArrayList findRuleCandidatesByPoi(PointOfInterest searchPoi) { @@ -1522,7 +811,7 @@ public class Rule implements Comparable return ruleCandidates; } - public static ArrayList findRuleCandidatesByHeadphoneJack(boolean triggerParameter) + /*public static ArrayList findRuleCandidatesByHeadphoneJack(boolean triggerParameter) { ArrayList ruleCandidates = new ArrayList(); @@ -1543,7 +832,7 @@ public class Rule implements Comparable } return ruleCandidates; - } + }*/ public static ArrayList findRuleCandidatesByProfile(Profile profile) { diff --git a/app/src/apkFlavor/java/com/jens/automation2/receivers/ActivityDetectionReceiver.java b/app/src/apkFlavor/java/com/jens/automation2/receivers/ActivityDetectionReceiver.java index 90ca7e6..09ae98a 100644 --- a/app/src/apkFlavor/java/com/jens/automation2/receivers/ActivityDetectionReceiver.java +++ b/app/src/apkFlavor/java/com/jens/automation2/receivers/ActivityDetectionReceiver.java @@ -291,10 +291,10 @@ public class ActivityDetectionReceiver extends IntentService implements Automati * and some activities are hierarchical (ON_FOOT is a generalization of WALKING and RUNNING). */ - ArrayList allRulesWithActivityDetection = Rule.findRuleCandidatesByActivityDetection(); + ArrayList allRulesWithActivityDetection = Rule.findRuleCandidates(Trigger_Enum.activityDetection); for(int i=0; i--> - + + + + + + + + @@ -142,6 +155,7 @@ + { @@ -339,6 +321,8 @@ public class Rule implements Comparable return true; case setWifiTethering: return true; + case setBluetoothTethering: + return true; default: break; } @@ -346,12 +330,38 @@ public class Rule implements Comparable return false; } + + public boolean hasNotAppliedSinceLastExecution() + { + for(Trigger oneTrigger : this.getTriggerSet()) + { + if (oneTrigger.hasStateNotAppliedSinceLastRuleExecution()) + return true; + } + + return false; + } + + public boolean getsGreenLight(Context context) + { + if(applies(context)) + { + if(hasNotAppliedSinceLastExecution()) + return true; + else + Miscellaneous.logEvent("i", "getsGreenLight()", "Rule " + getName() + " has not flipped since its last execution.", 4); + } + else + Miscellaneous.logEvent("i", "getsGreenLight()", "Rule " + getName() + " does not apply.", 4); + + return false; + } public boolean applies(Context context) { if(AutomationService.getInstance() == null) { - Miscellaneous.logEvent("i", "RuleCheck", "Automation service not running. Rule cannot apply.", 3); + Miscellaneous.logEvent("i", "RuleCheck", "Automation service not running. Rule " + getName() + " cannot apply.", 3); return false; } @@ -359,504 +369,8 @@ public class Rule implements Comparable { for(Trigger oneTrigger : this.getTriggerSet()) { - if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.pointOfInterest)) - { - // Am I here? - PointOfInterest activePoi = PointOfInterest.getActivePoi(); - if(activePoi != null) //entering one - { - if(oneTrigger.getPointOfInterest() != null) - { - if(activePoi.equals(oneTrigger.getPointOfInterest())) - { - if(!oneTrigger.getTriggerParameter()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Rule doesn't apply. We're entering POI: " + oneTrigger.getPointOfInterest().getName() + ", not leaving it.", 4); - return false; - } - } - else - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Rule doesn't apply. This is " + activePoi.getName() + ", not " + oneTrigger.getPointOfInterest().getName() + ".", 4); - return false; - } - } - else if(oneTrigger.getPointOfInterest() == null) - { - if(oneTrigger.getTriggerParameter()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Rule doesn't apply. We're at a POI. Rule specifies not at none, so leaving any.", 4); - return false; - } - } - } - else //leaving one - { - // We are not at any POI. But if this trigger requires us NOT to be there, that may be fine. - if(oneTrigger.getPointOfInterest() != null) - { -// if(activePoi.equals(oneTrigger.getPointOfInterest())) -// { - if(!oneTrigger.getTriggerParameter()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "We are not at POI \"" + oneTrigger.getPointOfInterest().getName() + "\". But since that's required by this rule that's fine.", 4); - } - else - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Rule doesn't apply. We're not at POI \"" + oneTrigger.getPointOfInterest().getName() + "\".", 3); - return false; - } -// } - } - else if(oneTrigger.getPointOfInterest() == null) - { - if(!oneTrigger.getTriggerParameter()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Rule doesn't apply. We're at no POI. Rule specifies to be at anyone.", 5); - return false; - } - } - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.timeFrame)) - { - Date now = new Date(); - String timeString = String.valueOf(now.getHours()) + ":" + String.valueOf(now.getMinutes()) + ":" + String.valueOf(now.getSeconds()); - Time nowTime = Time.valueOf(timeString); - Calendar calNow = Calendar.getInstance(); - - - if(oneTrigger.getTimeFrame().getDayList().contains(calNow.get(Calendar.DAY_OF_WEEK))) - { - if( - // Regular case, start time is lower than end time - ( - Miscellaneous.compareTimes(oneTrigger.getTimeFrame().getTriggerTimeStart(), nowTime) >= 0 - && - Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0 - ) - || - // Other case, start time higher than end time, timeframe goes over midnight - ( - Miscellaneous.compareTimes(oneTrigger.getTimeFrame().getTriggerTimeStart(), oneTrigger.getTimeFrame().getTriggerTimeStop()) < 0 - && - (Miscellaneous.compareTimes(oneTrigger.getTimeFrame().getTriggerTimeStart(), nowTime) >= 0 - || - Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0) - ) - - ) - { - // We are in the timeframe - Miscellaneous.logEvent("i", "TimeFrame", "We're currently (" + calNow.getTime().toString() + ") in the specified TimeFrame (" + oneTrigger.getTimeFrame().toString() + "). Trigger of Rule " + this.getName() + " applies.", 3); - if(oneTrigger.getTriggerParameter()) - { - Miscellaneous.logEvent("i", "TimeFrame", "That's what's specified. Trigger of Rule " + this.getName() + " applies.", 3); - //return true; - } - else - { - Miscellaneous.logEvent("i", "TimeFrame", "That's not what's specified. Trigger of Rule " + this.getName() + " doesn't apply.", 3); - return false; - } - } - else - { - Miscellaneous.logEvent("i", "TimeFrame", "We're currently (" + calNow.getTime().toString() + ", Day: " + String.valueOf(calNow.get(Calendar.DAY_OF_WEEK)) + ") not in the specified TimeFrame (" + oneTrigger.getTimeFrame().toString() + ") because of the time. Trigger of Rule " + this.getName() + " doesn\'t apply..", 5); - if(!oneTrigger.getTriggerParameter()) - { - Miscellaneous.logEvent("i", "TimeFrame", "That's what's specified. Trigger of Rule " + this.getName() + " applies.", 5); - //return true; - } - else - { - Miscellaneous.logEvent("i", "TimeFrame", "That's not what's specified. Trigger of Rule " + this.getName() + " doesn't apply.", 5); - return false; - } - // return false; - } - } - else - { - Miscellaneous.logEvent("i", "TimeFrame", "We're currently (" + calNow.getTime().toString() + ", Day: " + String.valueOf(calNow.get(Calendar.DAY_OF_WEEK)) + ") not in the specified TimeFrame (" + oneTrigger.getTimeFrame().toString() + ") because of the day. Trigger of Rule " + this.getName() + " doesn\'t apply.", 5); - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.charging)) - { - if(BatteryReceiver.isDeviceCharging(context) == 0) - { - return false; // unknown charging state, can't activate rule under these conditions - } - else if(BatteryReceiver.isDeviceCharging(context) == 1) - { - if(oneTrigger.getTriggerParameter()) //rule says when charging, but we're currently discharging - return false; - } - else if(BatteryReceiver.isDeviceCharging(context) == 2) - { - if(!oneTrigger.getTriggerParameter()) //rule says when discharging, but we're currently charging - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.usb_host_connection)) - { - if(BatteryReceiver.isUsbHostConnected() != oneTrigger.getTriggerParameter()) - { - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.batteryLevel)) - { - if(oneTrigger.getTriggerParameter()) - { - if(BatteryReceiver.getBatteryLevel() <= oneTrigger.getBatteryLevel()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryLowerThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3); - return false; - } - } - else - { - if(oneTrigger.getBatteryLevel() >= oneTrigger.getBatteryLevel()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryHigherThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3); - return false; - } - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.speed)) - { - if(oneTrigger.getTriggerParameter()) - { - if(LocationProvider.getSpeed() < oneTrigger.getSpeed()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreSlowerThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3); - return false; - } - } - else - { - if(LocationProvider.getSpeed() > oneTrigger.getSpeed()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreFasterThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3); - return false; - } - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.noiseLevel)) - { - if(oneTrigger.getTriggerParameter()) - { - if(NoiseListener.getNoiseLevelDb() < oneTrigger.getNoiseLevelDb()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyItsQuieterThan) + " " + String.valueOf(oneTrigger.getNoiseLevelDb()), 3); - return false; - } - } - else - { - if(NoiseListener.getNoiseLevelDb() > oneTrigger.getNoiseLevelDb()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyItsLouderThan) + " " + String.valueOf(oneTrigger.getNoiseLevelDb()), 3); - return false; - } - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.wifiConnection)) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Checking for wifi state", 4); - if(oneTrigger.getTriggerParameter() == WifiBroadcastReceiver.lastConnectedState) // connected / disconnected - { - if(oneTrigger.getTriggerParameter2().length() > 0) // only check if any wifi name specified, otherwise any wifi will do - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Wifi name specified, checking that.", 4); - if(!WifiBroadcastReceiver.getLastWifiSsid().equals(oneTrigger.getTriggerParameter2())) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(context.getResources().getString(R.string.ruleDoesntApplyNotTheCorrectSsid), oneTrigger.getTriggerParameter2(), WifiBroadcastReceiver.getLastWifiSsid()), 3); - return false; - } - else - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Wifi name matches. Rule will apply.", 4); - } - else - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "No wifi name specified, any will do.", 4); - } - else - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Wifi state not correct, demanded " + String.valueOf(oneTrigger.getTriggerParameter() + ", got " + String.valueOf(WifiBroadcastReceiver.lastConnectedState)), 4); - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.process_started_stopped)) - { - boolean running = ProcessListener.getRunningApps().contains(oneTrigger.getProcessName()); - - if(running) - Miscellaneous.logEvent("i", "ProcessMonitoring", "App " + oneTrigger.getProcessName() + " is currently running.", 4); - else - Miscellaneous.logEvent("i", "ProcessMonitoring", "App " + oneTrigger.getProcessName() + " is not running.", 4); - - if(running != oneTrigger.getTriggerParameter()) - { - Miscellaneous.logEvent("i", "ProcessMonitoring", "Trigger doesn't apply.", 4); - return false; - } - - Miscellaneous.logEvent("i", "ProcessMonitoring", "Trigger applies.", 4); - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.airplaneMode)) - { - if(ConnectivityReceiver.isAirplaneMode(context) != oneTrigger.getTriggerParameter()) - { - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.roaming)) - { - if(ConnectivityReceiver.isRoaming(context) != oneTrigger.getTriggerParameter()) - { - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.phoneCall)) - { - String[] elements = oneTrigger.getTriggerParameter2().split(triggerParameter2Split); - // state dir number - - if(elements[2].equals(Trigger.triggerPhoneCallNumberAny) || Miscellaneous.comparePhoneNumbers(PhoneStatusListener.getLastPhoneNumber(), elements[2]) || (Miscellaneous.isRegularExpression(elements[2]) && PhoneStatusListener.getLastPhoneNumber().matches(elements[2]))) - { - //if(PhoneStatusListener.isInACall() == oneTrigger.getTriggerParameter()) - if( - (elements[0].equals(Trigger.triggerPhoneCallStateRinging) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_RINGING) - || - (elements[0].equals(Trigger.triggerPhoneCallStateStarted) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_OFFHOOK) - || - (elements[0].equals(Trigger.triggerPhoneCallStateStopped) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_IDLE) - ) - { - if( - elements[1].equals(Trigger.triggerPhoneCallDirectionAny) - || - (elements[1].equals(Trigger.triggerPhoneCallDirectionIncoming) && PhoneStatusListener.getLastPhoneDirection() == 1) - || - (elements[1].equals(Trigger.triggerPhoneCallDirectionOutgoing) && PhoneStatusListener.getLastPhoneDirection() == 2) - ) - { - // Trigger conditions are met - } - else - { - Miscellaneous.logEvent("i", "Rule", "Rule doesn't apply. Wrong direction. Demanded: " + String.valueOf(oneTrigger.getPhoneDirection()) + ", got: " + String.valueOf(PhoneStatusListener.getLastPhoneDirection()), 4); - return false; - } - } - else - { - Miscellaneous.logEvent("i", "Rule", "Rule doesn't apply. Wrong call status. Demanded: " + String.valueOf(oneTrigger.getTriggerParameter()) + ", got: " + String.valueOf(PhoneStatusListener.isInACall()), 4); - return false; - } - } - else - { - Miscellaneous.logEvent("i", "Rule", "Rule doesn't apply. Wrong phone number. Demanded: " + oneTrigger.getPhoneNumber() + ", got: " + PhoneStatusListener.getLastPhoneNumber(), 4); - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.nfcTag)) - { - if(NfcReceiver.lastReadLabel == null) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyNoTagLabel), 3); - return false; - } - else if(!NfcReceiver.lastReadLabel.equals(oneTrigger.getNfcTagId())) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWrongTagLabel) + " " + NfcReceiver.lastReadLabel + " / " + oneTrigger.getNfcTagId(), 3); - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.bluetoothConnection)) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Checking for bluetooth...", 4); - - if(oneTrigger.getBluetoothDeviceAddress().equals("")) - { - if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) - { - if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter()) - return false; - } - else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) - { - if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter()) - return false; - } - else - { - // range - if(BluetoothReceiver.isAnyDeviceInRange() != oneTrigger.getTriggerParameter()) - return false; - } - } - else if(oneTrigger.getBluetoothDeviceAddress().equals("")) - { - if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) - { - if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter()) - return false; - } - else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) - { - if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter()) - return false; - } - else - { - // range - if(BluetoothReceiver.isAnyDeviceInRange() == oneTrigger.getTriggerParameter()) - return false; - } - } - else if(oneTrigger.getBluetoothDeviceAddress().length() > 0) - { - if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) - { - if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter()) - return false; - } - else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) - { - if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter()) - return false; - } - else - { - // range - if(BluetoothReceiver.isDeviceInRange(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter()) - return false; - } - } - else - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyStateNotCorrect), 3); - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.headsetPlugged)) - { - if(HeadphoneJackListener.isHeadsetConnected() != oneTrigger.getTriggerParameter()) - return false; - else - if(oneTrigger.getHeadphoneType() != 2 && oneTrigger.getHeadphoneType() != HeadphoneJackListener.getHeadphoneType()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWrongHeadphoneType), 3); - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.notification)) - { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) - { - String[] params = oneTrigger.getTriggerParameter2().split(triggerParameter2Split); - - String myApp = params[0]; - String myTitleDir = params[1]; - String myTitle = params[2]; - String myTextDir = params[3]; - String myText; - if (params.length >= 5) - myText = params[4]; - else - myText = ""; - - if(oneTrigger.getTriggerParameter()) - { - // Check an active notification that is still there - - boolean foundMatch = false; - - for (StatusBarNotification sbn : NotificationListener.getInstance().getActiveNotifications()) - { - if(getLastExecution() == null || sbn.getPostTime() > this.lastExecution.getTimeInMillis()) - { - String app = sbn.getPackageName(); - String title = sbn.getNotification().extras.getString(EXTRA_TITLE); - String text = sbn.getNotification().extras.getString(EXTRA_TEXT); - - Miscellaneous.logEvent("i", "NotificationCheck", "Checking if this notification matches our rule " + this.getName() + ". App: " + app + ", title: " + title + ", text: " + text, 5); - - if (!myApp.equals("-1")) - { - if (!app.equalsIgnoreCase(myApp)) - { - Miscellaneous.logEvent("i", "NotificationCheck", "Notification app name does not match rule.", 5); - continue; - } - } - - if (myTitle.length() > 0) - { - if (!Miscellaneous.compare(myTitleDir, myTitle, title)) - { - Miscellaneous.logEvent("i", "NotificationCheck", "Notification title does not match rule.", 5); - continue; - } - } - - if (myText.length() > 0) - { - if (!Miscellaneous.compare(myTextDir, myText, text)) - { - Miscellaneous.logEvent("i", "NotificationCheck", "Notification text does not match rule.", 5); - continue; - } - } - - foundMatch = true; - break; - } - } - - if(!foundMatch) - return false; - } - else - { - // check a notification that is gone - - if(NotificationListener.getLastNotification() != null) - { - if(!NotificationListener.getLastNotification().isCreated()) - { - String app = NotificationListener.getLastNotification().getApp(); - String title = NotificationListener.getLastNotification().getTitle(); - String text = NotificationListener.getLastNotification().getText(); - - if (!myApp.equals("-1")) - { - if (!app.equalsIgnoreCase(myApp)) - return false; - } - - if (myTitle.length() > 0) - { - if (!Miscellaneous.compare(myTitleDir, title, myTitle)) - return false; - } - - if (myText.length() > 0) - { - if (!Miscellaneous.compare(myTextDir, text, myText)) - return false; - } - } - else - return false; - } - } - } - } + if (!oneTrigger.applies(null, context)) + return false; } return true; @@ -865,7 +379,21 @@ public class Rule implements Comparable Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(context.getResources().getString(R.string.ruleIsDeactivatedCantApply), this.getName()), 3); return false; } - + + /** + * This is actually a function of the class Trigger, but Rule is already distinguished by flavors, Trigger is not. + * Hence it is here. + * @param oneTrigger + * @return + */ + boolean checkActivityDetection(Trigger oneTrigger) + { + /* + Feature not present in FOSS edition. + */ + return false; + } + private class ActivateRuleTask extends AsyncTask { boolean wasActivated = false; @@ -883,7 +411,8 @@ public class Rule implements Comparable if (Looper.myLooper() == null) Looper.prepare(); - + + setLastExecution(Calendar.getInstance()); wasActivated = activateInternally((AutomationService)params[0], (Boolean)params[1]); return null; @@ -908,7 +437,7 @@ public class Rule implements Comparable */ if(wasActivated) { - setLastExecution(Calendar.getInstance()); +// setLastExecution(Calendar.getInstance()); AutomationService.updateNotification(); ActivityMainScreen.updateMainScreen(); super.onPostExecute(result); @@ -926,8 +455,9 @@ public class Rule implements Comparable boolean notLastActive = getLastActivatedRule() == null || !getLastActivatedRule().equals(Rule.this); boolean doToggle = ruleToggle && isActuallyToggable; - if(notLastActive || force || doToggle) - { + //if(notLastActive || force || doToggle) +// if(force || doToggle) +// { String message; if(!doToggle) message = String.format(automationService.getResources().getString(R.string.ruleActivate), Rule.this.getName()); @@ -956,6 +486,7 @@ public class Rule implements Comparable { Rule.ruleRunHistory.add(0, Rule.this); // add at beginning for better visualization Rule.lastActivatedRuleActivationTime = new Date(); + while(ruleRunHistory.size() > Settings.rulesThatHaveBeenRanHistorySize) ruleRunHistory.remove(ruleRunHistory.size()-1); String history = ""; @@ -971,12 +502,12 @@ public class Rule implements Comparable } Miscellaneous.logEvent("i", "Rule", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleActivationComplete), Rule.this.getName()), 2); - } - else - { - Miscellaneous.logEvent("i", "Rule", "Request to activate rule " + Rule.this.getName() + ", but it is the last one that was activated. Won't do it again.", 3); - return false; - } +// } +// else +// { +// Miscellaneous.logEvent("i", "Rule", "Request to activate rule " + Rule.this.getName() + ", but it is the last one that was activated. Won't do it again.", 3); +// return false; +// } return true; } @@ -985,15 +516,27 @@ public class Rule implements Comparable public void activate(AutomationService automationService, boolean force) { ActivateRuleTask task = new ActivateRuleTask(); - -// if(Settings.startNewThreadForRuleActivation) - task.execute(automationService, force); -// else -// { -// task.activateInternally(automationService, force); -// AutomationService.updateNotification(); -// ActivityMainScreen.updateMainScreen(); -// } + task.execute(automationService, force); + } + + public static ArrayList findRuleCandidates(Trigger.Trigger_Enum triggerType) + { + ArrayList ruleCandidates = new ArrayList(); + + for(Rule oneRule : ruleCollection) + { + innerloop: + for(Trigger oneTrigger : oneRule.getTriggerSet()) + { + if(oneTrigger.getTriggerType() == triggerType) + { + ruleCandidates.add(oneRule); + break innerloop; // we don't need to check the other triggers in the same rule + } + } + } + + return ruleCandidates; } public static ArrayList findRuleCandidatesByPoi(PointOfInterest searchPoi, boolean triggerParameter) @@ -1037,7 +580,7 @@ public class Rule implements Comparable return ruleCandidates; } - public static ArrayList findRuleCandidatesByTimeFrame(TimeFrame searchTimeFrame, boolean triggerParameter) + /*public static ArrayList findRuleCandidatesByTimeFrame(TimeFrame searchTimeFrame, boolean triggerParameter) { ArrayList ruleCandidates = new ArrayList(); @@ -1058,8 +601,9 @@ public class Rule implements Comparable } return ruleCandidates; - } - public static ArrayList findRuleCandidatesByTime(Time searchTime) + }*/ + + /*public static ArrayList findRuleCandidatesByTime(Time searchTime) { Miscellaneous.logEvent("i", "RuleSearch", "Searching for rules with TimeFrame with time " + searchTime.toString() + ". RuleCollection-Size: " + String.valueOf(ruleCollection.size()), 3);; ArrayList ruleCandidates = new ArrayList(); @@ -1078,7 +622,7 @@ public class Rule implements Comparable if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() > oneTrigger.getTimeFrame().getTriggerTimeStop().getTime()) { - Miscellaneous.logEvent("i", "Timeframe search", "Rule goes over midnight.", 5); + Miscellaneous.logEvent("i", "Timeframe search", "Rule (" + oneRule.getName() + ") stretches over midnight.", 5); if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() <= searchTime.getTime() || searchTime.getTime() <= oneTrigger.getTimeFrame().getTriggerTimeStop().getTime()+20000) //add 20 seconds because of delay { ruleCandidates.add(oneRule); @@ -1087,7 +631,7 @@ public class Rule implements Comparable } else if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() <= searchTime.getTime() && searchTime.getTime() <= oneTrigger.getTimeFrame().getTriggerTimeStop().getTime()+20000) //add 20 seconds because of delay { - Miscellaneous.logEvent("i", "RuleSearch", "Rule found with TimeFrame with time " + searchTime.toString(), 3); + Miscellaneous.logEvent("i", "RuleSearch", "Rule found (" + oneRule.getName() + ") with TimeFrame with time " + searchTime.toString(), 3); ruleCandidates.add(oneRule); break innerloop; //if the poi is found we don't need to search the other triggers in the same rule } @@ -1098,29 +642,9 @@ public class Rule implements Comparable Miscellaneous.logEvent("i", "RuleSearch", String.valueOf(ruleCandidates.size()) + " Rule(s) found with TimeFrame with time " + searchTime.toString(), 3); return ruleCandidates; - } + }*/ - public static ArrayList findRuleCandidatesByTimeFrame() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.timeFrame) - { - ruleCandidates.add(oneRule); - break innerloop; //if the poi is found we don't need to search the other triggers in the same rule - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByCharging(boolean triggerParameter) + /*public static ArrayList findRuleCandidatesByCharging(boolean triggerParameter) { ArrayList ruleCandidates = new ArrayList(); @@ -1141,9 +665,9 @@ public class Rule implements Comparable } return ruleCandidates; - } + }*/ - public static ArrayList findRuleCandidatesByUsbHost(boolean triggerParameter) + /*public static ArrayList findRuleCandidatesByUsbHost(boolean triggerParameter) { ArrayList ruleCandidates = new ArrayList(); @@ -1164,147 +688,9 @@ public class Rule implements Comparable } return ruleCandidates; - } + }*/ - public static ArrayList findRuleCandidatesByBatteryLevel() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.batteryLevel) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //if the poi is found we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesBySpeed() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.speed) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //if the poi is found we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByNoiseLevel() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.noiseLevel) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //if the poi is found we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByWifiConnection() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.wifiConnection) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByBluetoothConnection() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.bluetoothConnection) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByProcess() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.process_started_stopped) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByAirplaneMode(boolean triggerParameter) + /*public static ArrayList findRuleCandidatesByAirplaneMode(boolean triggerParameter) { ArrayList ruleCandidates = new ArrayList(); @@ -1325,9 +711,9 @@ public class Rule implements Comparable } return ruleCandidates; - } + }*/ - public static ArrayList findRuleCandidatesByRoaming(boolean triggerParameter) + /*public static ArrayList findRuleCandidatesByRoaming(boolean triggerParameter) { ArrayList ruleCandidates = new ArrayList(); @@ -1348,9 +734,9 @@ public class Rule implements Comparable } return ruleCandidates; - } + }*/ - public static ArrayList findRuleCandidatesByPhoneCall(String direction) + /*public static ArrayList findRuleCandidatesByPhoneCall(String direction) { ArrayList ruleCandidates = new ArrayList(); @@ -1372,73 +758,7 @@ public class Rule implements Comparable } return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByNfc() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.nfcTag) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidates(Trigger.Trigger_Enum triggerType) - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == triggerType) - { - ruleCandidates.add(oneRule); - break innerloop; //we don't need to search the other triggers in the same rule - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByActivityDetection() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.activityDetection) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } + }*/ public static ArrayList findRuleCandidatesByPoi(PointOfInterest searchPoi) { @@ -1463,7 +783,7 @@ public class Rule implements Comparable return ruleCandidates; } - public static ArrayList findRuleCandidatesByHeadphoneJack(boolean triggerParameter) + /*public static ArrayList findRuleCandidatesByHeadphoneJack(boolean triggerParameter) { ArrayList ruleCandidates = new ArrayList(); @@ -1484,7 +804,7 @@ public class Rule implements Comparable } return ruleCandidates; - } + }*/ public static ArrayList findRuleCandidatesByProfile(Profile profile) { diff --git a/app/src/googlePlayFlavor/AndroidManifest.xml b/app/src/googlePlayFlavor/AndroidManifest.xml index 71eee3e..b4bf054 100644 --- a/app/src/googlePlayFlavor/AndroidManifest.xml +++ b/app/src/googlePlayFlavor/AndroidManifest.xml @@ -50,6 +50,7 @@ + @@ -123,10 +124,23 @@ android:scheme="package" />--> - + + + + + + + + @@ -136,6 +150,7 @@ + { @@ -341,6 +325,8 @@ public class Rule implements Comparable return true; case setWifiTethering: return true; + case setBluetoothTethering: + return true; default: break; } @@ -348,12 +334,38 @@ public class Rule implements Comparable return false; } + + public boolean hasNotAppliedSinceLastExecution() + { + for(Trigger oneTrigger : this.getTriggerSet()) + { + if (oneTrigger.hasStateNotAppliedSinceLastRuleExecution()) + return true; + } + + return false; + } + + public boolean getsGreenLight(Context context) + { + if(applies(context)) + { + if(hasNotAppliedSinceLastExecution()) + return true; + else + Miscellaneous.logEvent("i", "getsGreenLight()", "Rule " + getName() + " has not flipped since its last execution.", 4); + } + else + Miscellaneous.logEvent("i", "getsGreenLight()", "Rule " + getName() + " does not apply.", 4); + + return false; + } public boolean applies(Context context) { if(AutomationService.getInstance() == null) { - Miscellaneous.logEvent("i", "RuleCheck", "Automation service not running. Rule cannot apply.", 3); + Miscellaneous.logEvent("i", "RuleCheck", "Automation service not running. Rule " + getName() + " cannot apply.", 3); return false; } @@ -361,533 +373,8 @@ public class Rule implements Comparable { for(Trigger oneTrigger : this.getTriggerSet()) { - if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.pointOfInterest)) - { - // Am I here? - PointOfInterest activePoi = PointOfInterest.getActivePoi(); - if(activePoi != null) //entering one - { - if(oneTrigger.getPointOfInterest() != null) - { - if(activePoi.equals(oneTrigger.getPointOfInterest())) - { - if(!oneTrigger.getTriggerParameter()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Rule doesn't apply. We're entering POI: " + oneTrigger.getPointOfInterest().getName() + ", not leaving it.", 4); - return false; - } - } - else - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Rule doesn't apply. This is " + activePoi.getName() + ", not " + oneTrigger.getPointOfInterest().getName() + ".", 4); - return false; - } - } - else if(oneTrigger.getPointOfInterest() == null) - { - if(oneTrigger.getTriggerParameter()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Rule doesn't apply. We're at a POI. Rule specifies not at none, so leaving any.", 4); - return false; - } - } - } - else //leaving one - { - // We are not at any POI. But if this trigger requires us NOT to be there, that may be fine. - if(oneTrigger.getPointOfInterest() != null) - { -// if(activePoi.equals(oneTrigger.getPointOfInterest())) -// { - if(!oneTrigger.getTriggerParameter()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "We are not at POI \"" + oneTrigger.getPointOfInterest().getName() + "\". But since that's required by this rule that's fine.", 4); - } - else - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Rule doesn't apply. We're not at POI \"" + oneTrigger.getPointOfInterest().getName() + "\".", 3); - return false; - } -// } - } - else if(oneTrigger.getPointOfInterest() == null) - { - if(!oneTrigger.getTriggerParameter()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Rule doesn't apply. We're at no POI. Rule specifies to be at anyone.", 5); - return false; - } - } - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.timeFrame)) - { - Date now = new Date(); - String timeString = String.valueOf(now.getHours()) + ":" + String.valueOf(now.getMinutes()) + ":" + String.valueOf(now.getSeconds()); - Time nowTime = Time.valueOf(timeString); - Calendar calNow = Calendar.getInstance(); - - - if(oneTrigger.getTimeFrame().getDayList().contains(calNow.get(Calendar.DAY_OF_WEEK))) - { - if( - // Regular case, start time is lower than end time - ( - Miscellaneous.compareTimes(oneTrigger.getTimeFrame().getTriggerTimeStart(), nowTime) >= 0 - && - Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0 - ) - || - // Other case, start time higher than end time, timeframe goes over midnight - ( - Miscellaneous.compareTimes(oneTrigger.getTimeFrame().getTriggerTimeStart(), oneTrigger.getTimeFrame().getTriggerTimeStop()) < 0 - && - (Miscellaneous.compareTimes(oneTrigger.getTimeFrame().getTriggerTimeStart(), nowTime) >= 0 - || - Miscellaneous.compareTimes(nowTime, oneTrigger.getTimeFrame().getTriggerTimeStop()) > 0) - ) - - ) - { - // We are in the timeframe - Miscellaneous.logEvent("i", "TimeFrame", "We're currently (" + calNow.getTime().toString() + ") in the specified TimeFrame (" + oneTrigger.getTimeFrame().toString() + "). Trigger of Rule " + this.getName() + " applies.", 3); - if(oneTrigger.getTriggerParameter()) - { - Miscellaneous.logEvent("i", "TimeFrame", "That's what's specified. Trigger of Rule " + this.getName() + " applies.", 3); - //return true; - } - else - { - Miscellaneous.logEvent("i", "TimeFrame", "That's not what's specified. Trigger of Rule " + this.getName() + " doesn't apply.", 3); - return false; - } - } - else - { - Miscellaneous.logEvent("i", "TimeFrame", "We're currently (" + calNow.getTime().toString() + ", Day: " + String.valueOf(calNow.get(Calendar.DAY_OF_WEEK)) + ") not in the specified TimeFrame (" + oneTrigger.getTimeFrame().toString() + ") because of the time. Trigger of Rule " + this.getName() + " doesn\'t apply..", 5); - if(!oneTrigger.getTriggerParameter()) - { - Miscellaneous.logEvent("i", "TimeFrame", "That's what's specified. Trigger of Rule " + this.getName() + " applies.", 5); - //return true; - } - else - { - Miscellaneous.logEvent("i", "TimeFrame", "That's not what's specified. Trigger of Rule " + this.getName() + " doesn't apply.", 5); - return false; - } - // return false; - } - } - else - { - Miscellaneous.logEvent("i", "TimeFrame", "We're currently (" + calNow.getTime().toString() + ", Day: " + String.valueOf(calNow.get(Calendar.DAY_OF_WEEK)) + ") not in the specified TimeFrame (" + oneTrigger.getTimeFrame().toString() + ") because of the day. Trigger of Rule " + this.getName() + " doesn\'t apply.", 5); - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.charging)) - { - if(BatteryReceiver.isDeviceCharging(context) == 0) - { - return false; // unknown charging state, can't activate rule under these conditions - } - else if(BatteryReceiver.isDeviceCharging(context) == 1) - { - if(oneTrigger.getTriggerParameter()) //rule says when charging, but we're currently discharging - return false; - } - else if(BatteryReceiver.isDeviceCharging(context) == 2) - { - if(!oneTrigger.getTriggerParameter()) //rule says when discharging, but we're currently charging - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.usb_host_connection)) - { - if(BatteryReceiver.isUsbHostConnected() != oneTrigger.getTriggerParameter()) - { - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.batteryLevel)) - { - if(oneTrigger.getTriggerParameter()) - { - if(BatteryReceiver.getBatteryLevel() <= oneTrigger.getBatteryLevel()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryLowerThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3); - return false; - } - } - else - { - if(oneTrigger.getBatteryLevel() >= oneTrigger.getBatteryLevel()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryHigherThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3); - return false; - } - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.speed)) - { - if(oneTrigger.getTriggerParameter()) - { - if(LocationProvider.getSpeed() < oneTrigger.getSpeed()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreSlowerThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3); - return false; - } - } - else - { - if(LocationProvider.getSpeed() > oneTrigger.getSpeed()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreFasterThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3); - return false; - } - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.noiseLevel)) - { - if(oneTrigger.getTriggerParameter()) - { - if(NoiseListener.getNoiseLevelDb() < oneTrigger.getNoiseLevelDb()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyItsQuieterThan) + " " + String.valueOf(oneTrigger.getNoiseLevelDb()), 3); - return false; - } - } - else - { - if(NoiseListener.getNoiseLevelDb() > oneTrigger.getNoiseLevelDb()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyItsLouderThan) + " " + String.valueOf(oneTrigger.getNoiseLevelDb()), 3); - return false; - } - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.wifiConnection)) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Checking for wifi state", 4); - if(oneTrigger.getTriggerParameter() == WifiBroadcastReceiver.lastConnectedState) // connected / disconnected - { - if(oneTrigger.getTriggerParameter2().length() > 0) // only check if any wifi name specified, otherwise any wifi will do - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Wifi name specified, checking that.", 4); - if(!WifiBroadcastReceiver.getLastWifiSsid().equals(oneTrigger.getTriggerParameter2())) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(context.getResources().getString(R.string.ruleDoesntApplyNotTheCorrectSsid), oneTrigger.getTriggerParameter2(), WifiBroadcastReceiver.getLastWifiSsid()), 3); - return false; - } - else - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Wifi name matches. Rule will apply.", 4); - } - else - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "No wifi name specified, any will do.", 4); - } - else - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Wifi state not correct, demanded " + String.valueOf(oneTrigger.getTriggerParameter() + ", got " + String.valueOf(WifiBroadcastReceiver.lastConnectedState)), 4); - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.process_started_stopped)) - { - boolean running = ProcessListener.getRunningApps().contains(oneTrigger.getProcessName()); - - if(running) - Miscellaneous.logEvent("i", "ProcessMonitoring", "App " + oneTrigger.getProcessName() + " is currently running.", 4); - else - Miscellaneous.logEvent("i", "ProcessMonitoring", "App " + oneTrigger.getProcessName() + " is not running.", 4); - - if(running != oneTrigger.getTriggerParameter()) - { - Miscellaneous.logEvent("i", "ProcessMonitoring", "Trigger doesn't apply.", 4); - return false; - } - - Miscellaneous.logEvent("i", "ProcessMonitoring", "Trigger applies.", 4); - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.airplaneMode)) - { - if(ConnectivityReceiver.isAirplaneMode(context) != oneTrigger.getTriggerParameter()) - { - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.roaming)) - { - if(ConnectivityReceiver.isRoaming(context) != oneTrigger.getTriggerParameter()) - { - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.phoneCall)) - { - String[] elements = oneTrigger.getTriggerParameter2().split(triggerParameter2Split); - // state dir number - - if(elements[2].equals(Trigger.triggerPhoneCallNumberAny) || Miscellaneous.comparePhoneNumbers(PhoneStatusListener.getLastPhoneNumber(), elements[2]) || (Miscellaneous.isRegularExpression(elements[2]) && PhoneStatusListener.getLastPhoneNumber().matches(elements[2]))) - { - //if(PhoneStatusListener.isInACall() == oneTrigger.getTriggerParameter()) - if( - (elements[0].equals(Trigger.triggerPhoneCallStateRinging) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_RINGING) - || - (elements[0].equals(Trigger.triggerPhoneCallStateStarted) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_OFFHOOK) - || - (elements[0].equals(Trigger.triggerPhoneCallStateStopped) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_IDLE) - ) - { - if( - elements[1].equals(Trigger.triggerPhoneCallDirectionAny) - || - (elements[1].equals(Trigger.triggerPhoneCallDirectionIncoming) && PhoneStatusListener.getLastPhoneDirection() == 1) - || - (elements[1].equals(Trigger.triggerPhoneCallDirectionOutgoing) && PhoneStatusListener.getLastPhoneDirection() == 2) - ) - { - // Trigger conditions are met - } - else - { - Miscellaneous.logEvent("i", "Rule", "Rule doesn't apply. Wrong direction. Demanded: " + String.valueOf(oneTrigger.getPhoneDirection()) + ", got: " + String.valueOf(PhoneStatusListener.getLastPhoneDirection()), 4); - return false; - } - } - else - { - Miscellaneous.logEvent("i", "Rule", "Rule doesn't apply. Wrong call status. Demanded: " + String.valueOf(oneTrigger.getTriggerParameter()) + ", got: " + String.valueOf(PhoneStatusListener.isInACall()), 4); - return false; - } - } - else - { - Miscellaneous.logEvent("i", "Rule", "Rule doesn't apply. Wrong phone number. Demanded: " + oneTrigger.getPhoneNumber() + ", got: " + PhoneStatusListener.getLastPhoneNumber(), 4); - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.nfcTag)) - { - if(NfcReceiver.lastReadLabel == null) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyNoTagLabel), 3); - return false; - } - else if(!NfcReceiver.lastReadLabel.equals(oneTrigger.getNfcTagId())) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWrongTagLabel) + " " + NfcReceiver.lastReadLabel + " / " + oneTrigger.getNfcTagId(), 3); - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.activityDetection)) - { - if(ActivityDetectionReceiver.getActivityDetectionLastResult() != null) - { - boolean found = false; - for(DetectedActivity oneDetectedActivity : ActivityDetectionReceiver.getActivityDetectionLastResult().getProbableActivities()) - { - if(oneDetectedActivity.getType() == oneTrigger.getActivityDetectionType()) - found = true; - } - - if(!found) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(context.getResources().getString(R.string.ruleDoesntApplyActivityNotPresent), ActivityDetectionReceiver.getDescription(oneTrigger.getActivityDetectionType())), 3); - return false; - } - else - { - for(DetectedActivity oneDetectedActivity : ActivityDetectionReceiver.getActivityDetectionLastResult().getProbableActivities()) - { - if(oneDetectedActivity.getType() == oneTrigger.getActivityDetectionType() && oneDetectedActivity.getConfidence() < Settings.activityDetectionRequiredProbability) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(context.getResources().getString(R.string.ruleDoesntApplyActivityGivenButTooLowProbability), ActivityDetectionReceiver.getDescription(oneDetectedActivity.getType()), String.valueOf(oneDetectedActivity.getConfidence()), String.valueOf(Settings.activityDetectionRequiredProbability)), 3); - return false; - } - } - } - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.bluetoothConnection)) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Checking for bluetooth...", 4); - - if(oneTrigger.getBluetoothDeviceAddress().equals("")) - { - if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) - { - if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter()) - return false; - } - else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) - { - if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter()) - return false; - } - else - { - // range - if(BluetoothReceiver.isAnyDeviceInRange() != oneTrigger.getTriggerParameter()) - return false; - } - } - else if(oneTrigger.getBluetoothDeviceAddress().equals("")) - { - if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) - { - if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter()) - return false; - } - else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) - { - if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter()) - return false; - } - else - { - // range - if(BluetoothReceiver.isAnyDeviceInRange() == oneTrigger.getTriggerParameter()) - return false; - } - } - else if(oneTrigger.getBluetoothDeviceAddress().length() > 0) - { - if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) - { - if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter()) - return false; - } - else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) - { - if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter()) - return false; - } - else - { - // range - if(BluetoothReceiver.isDeviceInRange(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter()) - return false; - } - } - else - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyStateNotCorrect), 3); - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.headsetPlugged)) - { - if(HeadphoneJackListener.isHeadsetConnected() != oneTrigger.getTriggerParameter()) - return false; - else - if(oneTrigger.getHeadphoneType() != 2 && oneTrigger.getHeadphoneType() != HeadphoneJackListener.getHeadphoneType()) - { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWrongHeadphoneType), 3); - return false; - } - } - else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.notification)) - { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) - { - String[] params = oneTrigger.getTriggerParameter2().split(triggerParameter2Split); - - String myApp = params[0]; - String myTitleDir = params[1]; - String myTitle = params[2]; - String myTextDir = params[3]; - String myText; - if (params.length >= 5) - myText = params[4]; - else - myText = ""; - - if(oneTrigger.getTriggerParameter()) - { - // Check an active notification that is still there - - boolean foundMatch = false; - - for (StatusBarNotification sbn : NotificationListener.getInstance().getActiveNotifications()) - { - if(getLastExecution() == null || sbn.getPostTime() > this.lastExecution.getTimeInMillis()) - { - String app = sbn.getPackageName(); - String title = sbn.getNotification().extras.getString(EXTRA_TITLE); - String text = sbn.getNotification().extras.getString(EXTRA_TEXT); - - Miscellaneous.logEvent("i", "NotificationCheck", "Checking if this notification matches our rule " + this.getName() + ". App: " + app + ", title: " + title + ", text: " + text, 5); - - if (!myApp.equals("-1")) - { - if (!app.equalsIgnoreCase(myApp)) - { - Miscellaneous.logEvent("i", "NotificationCheck", "Notification app name does not match rule.", 5); - continue; - } - } - - if (myTitle.length() > 0) - { - if (!Miscellaneous.compare(myTitleDir, myTitle, title)) - { - Miscellaneous.logEvent("i", "NotificationCheck", "Notification title does not match rule.", 5); - continue; - } - } - - if (myText.length() > 0) - { - if (!Miscellaneous.compare(myTextDir, myText, text)) - { - Miscellaneous.logEvent("i", "NotificationCheck", "Notification text does not match rule.", 5); - continue; - } - } - - foundMatch = true; - break; - } - } - - if(!foundMatch) - return false; - } - else - { - // check a notification that is gone - - if(NotificationListener.getLastNotification() != null) - { - if(!NotificationListener.getLastNotification().isCreated()) - { - String app = NotificationListener.getLastNotification().getApp(); - String title = NotificationListener.getLastNotification().getTitle(); - String text = NotificationListener.getLastNotification().getText(); - - if (!myApp.equals("-1")) - { - if (!app.equalsIgnoreCase(myApp)) - return false; - } - - if (myTitle.length() > 0) - { - if (!Miscellaneous.compare(myTitleDir, title, myTitle)) - return false; - } - - if (myText.length() > 0) - { - if (!Miscellaneous.compare(myTextDir, text, myText)) - return false; - } - } - else - return false; - } - } - } - } + if (!oneTrigger.applies(null, context)) + return false; } return true; @@ -896,7 +383,45 @@ public class Rule implements Comparable Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(context.getResources().getString(R.string.ruleIsDeactivatedCantApply), this.getName()), 3); return false; } - + + /** + * This is actually a function of the class Trigger, but Rule is already distinguished by flavors, Trigger is not. + * Hence it is here. + * @param oneTrigger + * @return + */ + boolean checkActivityDetection(Trigger oneTrigger) + { + if (ActivityDetectionReceiver.getActivityDetectionLastResult() != null) + { + boolean found = false; + for (DetectedActivity oneDetectedActivity : ActivityDetectionReceiver.getActivityDetectionLastResult().getProbableActivities()) + { + if (oneDetectedActivity.getType() == oneTrigger.getActivityDetectionType()) + found = true; + } + + if (!found) + { + Miscellaneous.logEvent("i", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyActivityNotPresent), getName(), ActivityDetectionReceiver.getDescription(oneTrigger.getActivityDetectionType())), 3); + return false; + } + else + { + for (DetectedActivity oneDetectedActivity : ActivityDetectionReceiver.getActivityDetectionLastResult().getProbableActivities()) + { + if (oneDetectedActivity.getType() == oneTrigger.getActivityDetectionType() && oneDetectedActivity.getConfidence() < Settings.activityDetectionRequiredProbability) + { + Miscellaneous.logEvent("i", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), this.getName()), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyActivityGivenButTooLowProbability), getName(), ActivityDetectionReceiver.getDescription(oneDetectedActivity.getType()), String.valueOf(oneDetectedActivity.getConfidence()), String.valueOf(Settings.activityDetectionRequiredProbability)), 3); + return false; + } + } + } + } + + return true; + } + private class ActivateRuleTask extends AsyncTask { boolean wasActivated = false; @@ -914,7 +439,8 @@ public class Rule implements Comparable if (Looper.myLooper() == null) Looper.prepare(); - + + setLastExecution(Calendar.getInstance()); wasActivated = activateInternally((AutomationService)params[0], (Boolean)params[1]); return null; @@ -939,7 +465,7 @@ public class Rule implements Comparable */ if(wasActivated) { - setLastExecution(Calendar.getInstance()); +// setLastExecution(Calendar.getInstance()); AutomationService.updateNotification(); ActivityMainScreen.updateMainScreen(); super.onPostExecute(result); @@ -957,8 +483,9 @@ public class Rule implements Comparable boolean notLastActive = getLastActivatedRule() == null || !getLastActivatedRule().equals(Rule.this); boolean doToggle = ruleToggle && isActuallyToggable; - if(notLastActive || force || doToggle) - { + //if(notLastActive || force || doToggle) +// if(force || doToggle) +// { String message; if(!doToggle) message = String.format(automationService.getResources().getString(R.string.ruleActivate), Rule.this.getName()); @@ -987,6 +514,7 @@ public class Rule implements Comparable { Rule.ruleRunHistory.add(0, Rule.this); // add at beginning for better visualization Rule.lastActivatedRuleActivationTime = new Date(); + while(ruleRunHistory.size() > Settings.rulesThatHaveBeenRanHistorySize) ruleRunHistory.remove(ruleRunHistory.size()-1); String history = ""; @@ -1002,12 +530,12 @@ public class Rule implements Comparable } Miscellaneous.logEvent("i", "Rule", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleActivationComplete), Rule.this.getName()), 2); - } - else - { - Miscellaneous.logEvent("i", "Rule", "Request to activate rule " + Rule.this.getName() + ", but it is the last one that was activated. Won't do it again.", 3); - return false; - } +// } +// else +// { +// Miscellaneous.logEvent("i", "Rule", "Request to activate rule " + Rule.this.getName() + ", but it is the last one that was activated. Won't do it again.", 3); +// return false; +// } return true; } @@ -1016,15 +544,27 @@ public class Rule implements Comparable public void activate(AutomationService automationService, boolean force) { ActivateRuleTask task = new ActivateRuleTask(); - -// if(Settings.startNewThreadForRuleActivation) - task.execute(automationService, force); -// else -// { -// task.activateInternally(automationService, force); -// AutomationService.updateNotification(); -// ActivityMainScreen.updateMainScreen(); -// } + task.execute(automationService, force); + } + + public static ArrayList findRuleCandidates(Trigger.Trigger_Enum triggerType) + { + ArrayList ruleCandidates = new ArrayList(); + + for(Rule oneRule : ruleCollection) + { + innerloop: + for(Trigger oneTrigger : oneRule.getTriggerSet()) + { + if(oneTrigger.getTriggerType() == triggerType) + { + ruleCandidates.add(oneRule); + break innerloop; // we don't need to check the other triggers in the same rule + } + } + } + + return ruleCandidates; } public static ArrayList findRuleCandidatesByPoi(PointOfInterest searchPoi, boolean triggerParameter) @@ -1068,7 +608,7 @@ public class Rule implements Comparable return ruleCandidates; } - public static ArrayList findRuleCandidatesByTimeFrame(TimeFrame searchTimeFrame, boolean triggerParameter) + /*public static ArrayList findRuleCandidatesByTimeFrame(TimeFrame searchTimeFrame, boolean triggerParameter) { ArrayList ruleCandidates = new ArrayList(); @@ -1089,8 +629,9 @@ public class Rule implements Comparable } return ruleCandidates; - } - public static ArrayList findRuleCandidatesByTime(Time searchTime) + }*/ + + /*public static ArrayList findRuleCandidatesByTime(Time searchTime) { Miscellaneous.logEvent("i", "RuleSearch", "Searching for rules with TimeFrame with time " + searchTime.toString() + ". RuleCollection-Size: " + String.valueOf(ruleCollection.size()), 3);; ArrayList ruleCandidates = new ArrayList(); @@ -1109,7 +650,7 @@ public class Rule implements Comparable if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() > oneTrigger.getTimeFrame().getTriggerTimeStop().getTime()) { - Miscellaneous.logEvent("i", "Timeframe search", "Rule goes over midnight.", 5); + Miscellaneous.logEvent("i", "Timeframe search", "Rule (" + oneRule.getName() + ") stretches over midnight.", 5); if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() <= searchTime.getTime() || searchTime.getTime() <= oneTrigger.getTimeFrame().getTriggerTimeStop().getTime()+20000) //add 20 seconds because of delay { ruleCandidates.add(oneRule); @@ -1118,7 +659,7 @@ public class Rule implements Comparable } else if(oneTrigger.getTimeFrame().getTriggerTimeStart().getTime() <= searchTime.getTime() && searchTime.getTime() <= oneTrigger.getTimeFrame().getTriggerTimeStop().getTime()+20000) //add 20 seconds because of delay { - Miscellaneous.logEvent("i", "RuleSearch", "Rule found with TimeFrame with time " + searchTime.toString(), 3); + Miscellaneous.logEvent("i", "RuleSearch", "Rule found (" + oneRule.getName() + ") with TimeFrame with time " + searchTime.toString(), 3); ruleCandidates.add(oneRule); break innerloop; //if the poi is found we don't need to search the other triggers in the same rule } @@ -1129,29 +670,9 @@ public class Rule implements Comparable Miscellaneous.logEvent("i", "RuleSearch", String.valueOf(ruleCandidates.size()) + " Rule(s) found with TimeFrame with time " + searchTime.toString(), 3); return ruleCandidates; - } + }*/ - public static ArrayList findRuleCandidatesByTimeFrame() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.timeFrame) - { - ruleCandidates.add(oneRule); - break innerloop; //if the poi is found we don't need to search the other triggers in the same rule - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByCharging(boolean triggerParameter) + /*public static ArrayList findRuleCandidatesByCharging(boolean triggerParameter) { ArrayList ruleCandidates = new ArrayList(); @@ -1172,9 +693,9 @@ public class Rule implements Comparable } return ruleCandidates; - } + }*/ - public static ArrayList findRuleCandidatesByUsbHost(boolean triggerParameter) + /*public static ArrayList findRuleCandidatesByUsbHost(boolean triggerParameter) { ArrayList ruleCandidates = new ArrayList(); @@ -1195,147 +716,9 @@ public class Rule implements Comparable } return ruleCandidates; - } + }*/ - public static ArrayList findRuleCandidatesByBatteryLevel() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.batteryLevel) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //if the poi is found we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesBySpeed() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.speed) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //if the poi is found we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByNoiseLevel() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.noiseLevel) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //if the poi is found we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByWifiConnection() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.wifiConnection) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByBluetoothConnection() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.bluetoothConnection) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByProcess() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.process_started_stopped) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByAirplaneMode(boolean triggerParameter) + /*public static ArrayList findRuleCandidatesByAirplaneMode(boolean triggerParameter) { ArrayList ruleCandidates = new ArrayList(); @@ -1356,9 +739,9 @@ public class Rule implements Comparable } return ruleCandidates; - } + }*/ - public static ArrayList findRuleCandidatesByRoaming(boolean triggerParameter) + /*public static ArrayList findRuleCandidatesByRoaming(boolean triggerParameter) { ArrayList ruleCandidates = new ArrayList(); @@ -1379,9 +762,9 @@ public class Rule implements Comparable } return ruleCandidates; - } + }*/ - public static ArrayList findRuleCandidatesByPhoneCall(String direction) + /*public static ArrayList findRuleCandidatesByPhoneCall(String direction) { ArrayList ruleCandidates = new ArrayList(); @@ -1403,73 +786,7 @@ public class Rule implements Comparable } return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByNfc() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.nfcTag) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidates(Trigger.Trigger_Enum triggerType) - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == triggerType) - { - ruleCandidates.add(oneRule); - break innerloop; //we don't need to search the other triggers in the same rule - } - } - } - - return ruleCandidates; - } - - public static ArrayList findRuleCandidatesByActivityDetection() - { - ArrayList ruleCandidates = new ArrayList(); - - for(Rule oneRule : ruleCollection) - { - innerloop: - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.activityDetection) - { -// if(oneTrigger.getTriggerParameter() == triggerParameter) -// { - ruleCandidates.add(oneRule); - break innerloop; //we don't need to search the other triggers in the same rule -// } - } - } - } - - return ruleCandidates; - } + }*/ public static ArrayList findRuleCandidatesByPoi(PointOfInterest searchPoi) { @@ -1494,7 +811,7 @@ public class Rule implements Comparable return ruleCandidates; } - public static ArrayList findRuleCandidatesByHeadphoneJack(boolean triggerParameter) + /*public static ArrayList findRuleCandidatesByHeadphoneJack(boolean triggerParameter) { ArrayList ruleCandidates = new ArrayList(); @@ -1515,7 +832,7 @@ public class Rule implements Comparable } return ruleCandidates; - } + }*/ public static ArrayList findRuleCandidatesByProfile(Profile profile) { diff --git a/app/src/googlePlayFlavor/java/com/jens/automation2/receivers/ActivityDetectionReceiver.java b/app/src/googlePlayFlavor/java/com/jens/automation2/receivers/ActivityDetectionReceiver.java index 26f568e..c9d74c8 100644 --- a/app/src/googlePlayFlavor/java/com/jens/automation2/receivers/ActivityDetectionReceiver.java +++ b/app/src/googlePlayFlavor/java/com/jens/automation2/receivers/ActivityDetectionReceiver.java @@ -301,7 +301,7 @@ public class ActivityDetectionReceiver extends IntentService implements Automati * and some activities are hierarchical (ON_FOOT is a generalization of WALKING and RUNNING). */ - ArrayList allRulesWithActivityDetection = Rule.findRuleCandidatesByActivityDetection(); + ArrayList allRulesWithActivityDetection = Rule.findRuleCandidates(Trigger_Enum.activityDetection); for(int i=0; i actionTypesList = new ArrayList(); @@ -370,6 +396,9 @@ public class Action case setWifiTethering: Actions.setWifiTethering(context, getParameter1(), toggleActionIfPossible); break; + case setBluetoothTethering: + Actions.BluetoothTetheringClass.setBluetoothTethering(context, getParameter1(), toggleActionIfPossible); + break; case setDisplayRotation: Actions.setDisplayRotation(context, getParameter1(), toggleActionIfPossible); break; @@ -379,16 +408,26 @@ public class Action case waitBeforeNextAction: Actions.waitBeforeNextAction(Long.parseLong(this.getParameter2())); break; - case wakeupDevice: - Actions.wakeupDevice(Long.parseLong(this.getParameter2())); - // wakeupDevice() will create a separate thread. That'll take some time, we wait 100ms. - try + case turnScreenOnOrOff: + if(getParameter1()) { - Thread.sleep(100); + if(StringUtils.isNumeric(this.getParameter2())) + Actions.wakeupDevice(Long.parseLong(this.getParameter2())); + else + Actions.wakeupDevice((long)1000); + // wakeupDevice() will create a separate thread. That'll take some time, we wait 100ms. + try + { + Thread.sleep(100); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } } - catch (InterruptedException e) + else { - e.printStackTrace(); + Actions.turnOffScreen(); } break; case setAirplaneMode: diff --git a/app/src/main/java/com/jens/automation2/Actions.java b/app/src/main/java/com/jens/automation2/Actions.java index fb3614c..0c7fb41 100644 --- a/app/src/main/java/com/jens/automation2/Actions.java +++ b/app/src/main/java/com/jens/automation2/Actions.java @@ -5,7 +5,11 @@ import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.NotificationManager; import android.app.PendingIntent; +import android.app.admin.DevicePolicyManager; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; +import android.bluetooth.BluetoothProfile; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; @@ -43,13 +47,16 @@ import org.apache.http.conn.util.InetAddressUtils; import org.apache.http.impl.client.DefaultHttpClient; import java.io.File; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.InetAddress; import java.net.NetworkInterface; import java.security.KeyStore; import java.util.Collections; import java.util.List; +import java.util.Set; import javax.net.ssl.SSLContext; @@ -304,6 +311,149 @@ public class Actions return true; } + public static class BluetoothTetheringClass + { + static Object instance = null; + static Method setTetheringOn = null; + static Method isTetheringOn = null; + static Object mutex = new Object(); + + public static Boolean setBluetoothTethering(Context context, Boolean desiredState, boolean toggleActionIfPossible) + { + Miscellaneous.logEvent("i", "Bluetooth Tethering", "Changing Bluetooth Tethering to " + String.valueOf(desiredState), 4); + +// boolean state = isTetheringOn(context); + +// if (toggleActionIfPossible) +// { +// Miscellaneous.logEvent("i", "Bluetooth Tethering", context.getResources().getString(R.string.toggling), 2); +// desiredState = !state; +// } + +// if (((state && !desiredState) || (!state && desiredState))) +// { + BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + Class classBluetoothPan = null; + Constructor BTPanCtor = null; + Object BTSrvInstance = null; + Method mBTPanConnect = null; + + String sClassName = "android.bluetooth.BluetoothPan"; + try + { + classBluetoothPan = Class.forName(sClassName); + Constructor ctor = classBluetoothPan.getDeclaredConstructor(Context.class, BluetoothProfile.ServiceListener.class); + + ctor.setAccessible(true); + // Set Tethering ON + + Class[] paramSet = new Class[1]; + paramSet[0] = boolean.class; + + synchronized (mutex) + { + setTetheringOn = classBluetoothPan.getDeclaredMethod("setBluetoothTethering", paramSet); + isTetheringOn = classBluetoothPan.getDeclaredMethod("isTetheringOn", null); + instance = ctor.newInstance(context, new BTPanServiceListener(context)); + } + + classBluetoothPan = Class.forName("android.bluetooth.BluetoothPan"); + mBTPanConnect = classBluetoothPan.getDeclaredMethod("connect", BluetoothDevice.class); + BTPanCtor = classBluetoothPan.getDeclaredConstructor(Context.class, BluetoothProfile.ServiceListener.class); + BTPanCtor.setAccessible(true); + BTSrvInstance = BTPanCtor.newInstance(context, new BTPanServiceListener(context)); + + Set pairedDevices = mBluetoothAdapter.getBondedDevices(); + + // If there are paired devices + if (pairedDevices.size() > 0) + { + // Loop through paired devices + for (BluetoothDevice device : pairedDevices) + { + try + { + mBTPanConnect.invoke(BTSrvInstance, device); + } + catch (Exception e) + { + Miscellaneous.logEvent("e", "Bluetooth Tethering", Log.getStackTraceString(e), 1); + } + } + } + return true; + } + catch (NoSuchMethodException e) + { + Miscellaneous.logEvent("e", "Bluetooth Tethering", Log.getStackTraceString(e), 1); + } + catch (ClassNotFoundException e) + { + Miscellaneous.logEvent("e", "Bluetooth Tethering", Log.getStackTraceString(e), 1); + } + catch(InvocationTargetException e) + { + /* + Exact error message: "Bluetooth binder is null" + This means this device doesn't have bluetooth. + */ + Miscellaneous.logEvent("e", "Bluetooth Tethering", "Device probably doesn't have bluetooth. " + Log.getStackTraceString(e), 1); + Toast.makeText(context, context.getResources().getString(R.string.deviceDoesNotHaveBluetooth), Toast.LENGTH_SHORT).show(); + } + catch (Exception e) + { + Miscellaneous.logEvent("e", "Bluetooth Tethering", Log.getStackTraceString(e), 1); + } + + return false; + } + + public static class BTPanServiceListener implements BluetoothProfile.ServiceListener + { + private final Context context; + + public BTPanServiceListener(final Context context) + { + this.context = context; + } + + @Override + public void onServiceConnected(final int profile, final BluetoothProfile proxy) + { + //Some code must be here or the compiler will optimize away this callback. + + try + { + synchronized (mutex) + { + setTetheringOn.invoke(instance, true); + if ((Boolean) isTetheringOn.invoke(instance, null)) + { + Miscellaneous.logEvent("e", "Bluetooth Tethering", "BT Tethering is on", 1); + } + else + { + Miscellaneous.logEvent("e", "Bluetooth Tethering", "BT Tethering is off", 1); + } + } + } + catch (InvocationTargetException e) + { + Miscellaneous.logEvent("e", "Bluetooth Tethering", Log.getStackTraceString(e), 1); + } + catch (IllegalAccessException e) + { + Miscellaneous.logEvent("e", "Bluetooth Tethering", Log.getStackTraceString(e), 1); + } + } + + @Override + public void onServiceDisconnected(final int profile) + { + } + } + } + public static boolean setUsbTethering(Context context2, Boolean desiredState, boolean toggleActionIfPossible) { //TODO:toggle not really implemented, yet @@ -945,23 +1095,96 @@ public class Actions @Override public void run() { - PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - WakeLock wakeLock = pm.newWakeLock((WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP), "Automation:Wakelock"); - wakeLock.acquire(); - try { - Thread.sleep(awakeTime); - } - catch (InterruptedException e) - { - Miscellaneous.logEvent("w", context.getResources().getString(R.string.wakeupDevice), "Error keeping device awake: " + Log.getStackTraceString(e), 4); - } + PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + WakeLock wakeLock = pm.newWakeLock((PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP), "Automation:Wakelock"); + wakeLock.acquire(); - wakeLock.release(); + try + { + Thread.sleep(awakeTime); + } + catch (InterruptedException e) + { + Miscellaneous.logEvent("w", context.getResources().getString(R.string.wakeupDevice), "Error keeping device awake: " + Log.getStackTraceString(e), 4); + } + + wakeLock.release(); + } + catch(Exception e) + { + Miscellaneous.logEvent("e", "Wakeup device action", "Error while waking up device: " + Log.getStackTraceString(e), 1); + } } } + /*public static void turnOnScreen() + { + // turn on screen + Miscellaneous.logEvent("i", "Actions", "Turning screen on.", 3); + PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + WakeLock wakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, AutomationService.NOTIFICATION_CHANNEL_ID + ":turnOffScreen"); + wakeLock.acquire(); + }*/ + + @TargetApi(21) //Suppress lint error for PROXIMITY_SCREEN_OFF_WAKE_LOCK + public static void turnOffScreen() + { + Miscellaneous.logEvent("i", "Actions", "Turning screen off.", 3); + + + /*params.flags |= LayoutParams.FLAG_KEEP_SCREEN_ON; + params.screenBrightness = 0; + getWindow().setAttributes(params);*/ + +// PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); +// WakeLock wakeLock = pm.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK,AutomationService.NOTIFICATION_CHANNEL_ID + ":turnOffScreen"); +// WakeLock wakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK , AutomationService.NOTIFICATION_CHANNEL_ID + ":turnOffScreen"); +// wakeLock.acquire(); + +// WakeLock wakeLock = pm.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, "tag"); +// WakeLock wakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK , "tag"); +// wakeLock.acquire(); + +// wakeLock.release(); + lockScreen(); + } + + public static void lockScreen() + { + Miscellaneous.logEvent("i", "Actions", "Locking screen.", 3); + + // Works, but requires Manifest.permission.BIND_DEVICE_ADMIN +// https://stackoverflow.com/questions/23898406/java-lang-securityexception-no-active-admin-owned-by-uid-10047-for-policy-4-on + DevicePolicyManager deviceManager = (DevicePolicyManager)Miscellaneous.getAnyContext().getSystemService(Context.DEVICE_POLICY_SERVICE); + deviceManager.lockNow(); + } + + // using root + /*private void turnOffScreen() + { + try + { + Class c = Class.forName("android.os.PowerManager"); + PowerManager mPowerManager = (PowerManager) this.getSystemService(Context.POWER_SERVICE); + for(Method m : c.getDeclaredMethods()) + { + if(m.getName().equals("goToSleep")) + { + m.setAccessible(true); + if(m.getParameterTypes().length == 1) + { + m.invoke(mPowerManager,SystemClock.uptimeMillis()-2); + } + } + } + } + catch (Exception e) + { + } + }*/ + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) @SuppressLint("NewApi") public static boolean setAirplaneMode(boolean desiredState, boolean toggleActionIfPossible) diff --git a/app/src/main/java/com/jens/automation2/ActivityHelp.java b/app/src/main/java/com/jens/automation2/ActivityHelp.java index 9a4f849..aab4119 100644 --- a/app/src/main/java/com/jens/automation2/ActivityHelp.java +++ b/app/src/main/java/com/jens/automation2/ActivityHelp.java @@ -9,7 +9,6 @@ import com.jens.automation2.R.layout; public class ActivityHelp extends Activity { - @Override protected void onCreate(Bundle savedInstanceState) { @@ -19,5 +18,4 @@ public class ActivityHelp extends Activity TextView tvHelpTextEnergySaving = (TextView) findViewById(R.id.tvHelpTextEnergySaving); tvHelpTextEnergySaving.setMovementMethod(LinkMovementMethod.getInstance()); } - -} +} \ No newline at end of file diff --git a/app/src/main/java/com/jens/automation2/ActivityMainRules.java b/app/src/main/java/com/jens/automation2/ActivityMainRules.java index fa037e6..afcacbc 100644 --- a/app/src/main/java/com/jens/automation2/ActivityMainRules.java +++ b/app/src/main/java/com/jens/automation2/ActivityMainRules.java @@ -19,7 +19,7 @@ import android.widget.TextView; import android.widget.Toast; import com.jens.automation2.AutomationService.serviceCommands; -import com.jens.automation2.receivers.AlarmListener; +import com.jens.automation2.receivers.DateTimeListener; import java.util.ArrayList; @@ -254,7 +254,7 @@ public class ActivityMainRules extends ActivityGeneric try { if(AutomationService.isMyServiceRunning(this)) - AlarmListener.reloadAlarms(); + DateTimeListener.reloadAlarms(); } catch(NullPointerException e) { diff --git a/app/src/main/java/com/jens/automation2/ActivityMainScreen.java b/app/src/main/java/com/jens/automation2/ActivityMainScreen.java index ca9ecaf..722ac78 100644 --- a/app/src/main/java/com/jens/automation2/ActivityMainScreen.java +++ b/app/src/main/java/com/jens/automation2/ActivityMainScreen.java @@ -44,7 +44,7 @@ public class ActivityMainScreen extends ActivityGeneric private static ActivityMainScreen activityMainScreenInstance = null; private ToggleButton toggleService, tbLockSound; - private Button bShowHelp, bPrivacy, bSettingsErase, bAddSoundLockTIme; + private Button bShowHelp, bPrivacy, bSettingsErase, bAddSoundLockTIme, bDonate; private TextView tvActivePoi, tvClosestPoi, tvLastRule, tvMainScreenNotePermissions, tvMainScreenNoteFeaturesFromOtherFlavor, tvMainScreenNoteLocationImpossibleBlameGoogle, tvMainScreenNoteNews, tvlockSoundDuration; private static boolean updateNoteDisplayed = false; @@ -82,6 +82,12 @@ public class ActivityMainScreen extends ActivityGeneric tvlockSoundDuration = (TextView)findViewById(R.id.tvlockSoundDuration); tbLockSound = (ToggleButton) findViewById(R.id.tbLockSound); toggleService = (ToggleButton) findViewById(R.id.tbArmMastListener); + + bDonate = (Button)findViewById(R.id.bDonate); + + if(!BuildConfig.FLAVOR.equalsIgnoreCase("googlePlayFlavor")) + bDonate.setVisibility(View.VISIBLE); + toggleService.setChecked(AutomationService.isMyServiceRunning(this)); toggleService.setOnCheckedChangeListener(new OnCheckedChangeListener() { @@ -111,6 +117,18 @@ public class ActivityMainScreen extends ActivityGeneric } }); + bDonate.setOnClickListener(new OnClickListener() + { + @Override + public void onClick(View v) + { + String privacyPolicyUrl = "https://server47.de/donate"; + + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(privacyPolicyUrl)); + startActivity(browserIntent); + } + }); + tbLockSound.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override diff --git a/app/src/main/java/com/jens/automation2/ActivityMainTabLayout.java b/app/src/main/java/com/jens/automation2/ActivityMainTabLayout.java index e8f815d..11ab97f 100644 --- a/app/src/main/java/com/jens/automation2/ActivityMainTabLayout.java +++ b/app/src/main/java/com/jens/automation2/ActivityMainTabLayout.java @@ -72,4 +72,4 @@ public class ActivityMainTabLayout extends TabActivity // setIntent(intent); NfcReceiver.checkIntentForNFC(this, intent); } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/jens/automation2/ActivityMaintenance.java b/app/src/main/java/com/jens/automation2/ActivityMaintenance.java index 499e713..9cc335c 100644 --- a/app/src/main/java/com/jens/automation2/ActivityMaintenance.java +++ b/app/src/main/java/com/jens/automation2/ActivityMaintenance.java @@ -201,6 +201,9 @@ public class ActivityMaintenance extends Activity try { XmlFileInterface.readFile(); + ActivityMainPoi.getInstance().updateListView(); + ActivityMainRules.getInstance().updateListView(); + ActivityMainProfiles.getInstance().updateListView(); } catch (Exception e) { @@ -314,18 +317,9 @@ public class ActivityMaintenance extends Activity String subject = "Automation logs"; - StringBuilder emailBody = new StringBuilder(); - emailBody.append("Device details" + Miscellaneous.lineSeparator); - emailBody.append("OS version: " + System.getProperty("os.version") + Miscellaneous.lineSeparator); - emailBody.append("API Level: " + android.os.Build.VERSION.SDK + Miscellaneous.lineSeparator); - emailBody.append("Device: " + android.os.Build.DEVICE + Miscellaneous.lineSeparator); - emailBody.append("Model: " + android.os.Build.MODEL + Miscellaneous.lineSeparator); - emailBody.append("Product: " + android.os.Build.PRODUCT); - emailBody.append("Flavor: " + BuildConfig.FLAVOR); - Uri uri = Uri.parse("content://com.jens.automation2/" + Settings.zipFileName); - Miscellaneous.sendEmail(ActivityMaintenance.this, "android-development@gmx.de", "Automation logs", emailBody.toString(), uri); + Miscellaneous.sendEmail(ActivityMaintenance.this, "android-development@gmx.de", "Automation logs", getSystemInfo(), uri); } }); alertDialogBuilder.setNegativeButton(context.getResources().getString(R.string.no), null); @@ -334,6 +328,19 @@ public class ActivityMaintenance extends Activity return alertDialog; } + public static String getSystemInfo() + { + StringBuilder systemInfoText = new StringBuilder(); + systemInfoText.append("Device details" + Miscellaneous.lineSeparator); + systemInfoText.append("OS version: " + System.getProperty("os.version") + Miscellaneous.lineSeparator); + systemInfoText.append("API Level: " + android.os.Build.VERSION.SDK + Miscellaneous.lineSeparator); + systemInfoText.append("Device: " + android.os.Build.DEVICE + Miscellaneous.lineSeparator); + systemInfoText.append("Model: " + android.os.Build.MODEL + Miscellaneous.lineSeparator); + systemInfoText.append("Product: " + android.os.Build.PRODUCT + Miscellaneous.lineSeparator); + systemInfoText.append("Flavor: " + BuildConfig.FLAVOR); + return systemInfoText.toString(); + } + @Override protected void onResume() { diff --git a/app/src/main/java/com/jens/automation2/ActivityManageActionSendTextMessage.java b/app/src/main/java/com/jens/automation2/ActivityManageActionSendTextMessage.java index b7558f9..ab54925 100644 --- a/app/src/main/java/com/jens/automation2/ActivityManageActionSendTextMessage.java +++ b/app/src/main/java/com/jens/automation2/ActivityManageActionSendTextMessage.java @@ -59,7 +59,7 @@ public class ActivityManageActionSendTextMessage extends Activity backToRuleManager(); } else - Toast.makeText(getBaseContext(), getResources().getString(R.string.textTooShort), Toast.LENGTH_LONG).show(); + Toast.makeText(getBaseContext(), getResources().getString(R.string.enterPhoneNumberAndText), Toast.LENGTH_LONG).show(); } }); diff --git a/app/src/main/java/com/jens/automation2/ActivityManageActionSpeakText.java b/app/src/main/java/com/jens/automation2/ActivityManageActionSpeakText.java index 9f9cf3f..cb23f2e 100644 --- a/app/src/main/java/com/jens/automation2/ActivityManageActionSpeakText.java +++ b/app/src/main/java/com/jens/automation2/ActivityManageActionSpeakText.java @@ -44,7 +44,7 @@ public class ActivityManageActionSpeakText extends Activity backToRuleManager(); } else - Toast.makeText(getBaseContext(), getResources().getString(R.string.textTooShort), Toast.LENGTH_LONG).show(); + Toast.makeText(getBaseContext(), getResources().getString(R.string.enterPhoneNumberAndText), Toast.LENGTH_LONG).show(); } }); diff --git a/app/src/main/java/com/jens/automation2/ActivityManageRule.java b/app/src/main/java/com/jens/automation2/ActivityManageRule.java index 22081bb..d63211b 100644 --- a/app/src/main/java/com/jens/automation2/ActivityManageRule.java +++ b/app/src/main/java/com/jens/automation2/ActivityManageRule.java @@ -49,6 +49,9 @@ import java.util.Collections; public class ActivityManageRule extends Activity { final static String activityDetectionClassPath = "com.jens.automation2.receivers.ActivityDetectionReceiver"; + public final static String intentNameTriggerParameter1 = "triggerParameter1"; + public final static String intentNameActionParameter1 = "actionParameter1"; + public final static String intentNameActionParameter2 = "actionParameter2"; public Context context; private Button cmdTriggerAdd, cmdActionAdd, cmdSaveRule; @@ -95,6 +98,8 @@ public class ActivityManageRule extends Activity final static int requestCodeTriggerBluetoothEdit = 6001; final static int requestCodeActionScreenBrightnessAdd = 401; final static int requestCodeActionScreenBrightnessEdit = 402; + final static int requestCodeTriggerDeviceOrientationAdd = 301; + final static int requestCodeTriggerDeviceOrientationEdit = 302; final static int requestCodeTriggerNotificationAdd = 8000; final static int requestCodeTriggerNfcNotificationEdit = 8001; final static int requestCodeActionPlaySoundAdd = 501; @@ -165,7 +170,13 @@ public class ActivityManageRule extends Activity { hideKeyboard(); newTrigger = new Trigger(); - getTriggerTypeDialog(context).show(); + + AlertDialog dia = getTriggerTypeDialog(context); + + if(Miscellaneous.isDarkModeEnabled(ActivityManageRule.this)) + dia.getListView().setBackgroundColor(getResources().getColor(R.color.darkScreenBackgroundColor)); + + dia.show(); } }); @@ -175,12 +186,18 @@ public class ActivityManageRule extends Activity public void onClick(View v) { hideKeyboard(); - getActionTypeDialog().show(); + + AlertDialog dia = getActionTypeDialog(); + + if(Miscellaneous.isDarkModeEnabled(ActivityManageRule.this)) + dia.getListView().setBackgroundColor(getResources().getColor(R.color.darkScreenBackgroundColor)); + + dia.show(); } }); cmdSaveRule.setOnClickListener(new OnClickListener() - { + { @Override public void onClick(View v) { @@ -261,6 +278,12 @@ public class ActivityManageRule extends Activity wifiEditor.putExtra("wifiName", selectedTrigger.getTriggerParameter2()); startActivityForResult(wifiEditor, requestCodeTriggerWifiEdit); break; + case deviceOrientation: + Intent devicePositionEditor = new Intent(ActivityManageRule.this, ActivityManageTriggerDeviceOrientation.class); + devicePositionEditor.putExtra(ActivityManageRule.intentNameTriggerParameter1, selectedTrigger.getTriggerParameter()); + devicePositionEditor.putExtra(ActivityManageTriggerDeviceOrientation.vectorFieldName, selectedTrigger.getTriggerParameter2()); + startActivityForResult(devicePositionEditor, requestCodeTriggerDeviceOrientationEdit); + break; default: break; } @@ -303,6 +326,7 @@ public class ActivityManageRule extends Activity case triggerUrl: Intent activityEditTriggerUrlIntent = new Intent(ActivityManageRule.this, ActivityManageActionTriggerUrl.class); ActivityManageActionTriggerUrl.resultingAction = a; + ActivityManageActionTriggerUrl.resultingAction.setParentRule(ruleToEdit); activityEditTriggerUrlIntent.putExtra("edit", true); startActivityForResult(activityEditTriggerUrlIntent, requestCodeActionTriggerUrlEdit); break; @@ -332,8 +356,8 @@ public class ActivityManageRule extends Activity case playSound: Intent actionPlaySoundIntent = new Intent(context, ActivityManageActionPlaySound.class); actionPlaySoundIntent.putExtra("edit", true); - actionPlaySoundIntent.putExtra("actionParameter1", a.getParameter1()); - actionPlaySoundIntent.putExtra("actionParameter2", a.getParameter2()); + actionPlaySoundIntent.putExtra(intentNameActionParameter1, a.getParameter1()); + actionPlaySoundIntent.putExtra(intentNameActionParameter2, a.getParameter2()); startActivityForResult(actionPlaySoundIntent, requestCodeActionPlaySoundEdit); break; default: @@ -403,6 +427,11 @@ public class ActivityManageRule extends Activity ruleToEdit.setName(etRuleName.getText().toString()); ruleToEdit.setRuleActive(chkRuleActive.isChecked()); ruleToEdit.setRuleToggle(chkRuleToggle.isChecked()); + + for(Trigger t : ruleToEdit.getTriggerSet()) + t.setParentRule(ruleToEdit); + for(Action a : ruleToEdit.getActionSet()) + a.setParentRule(ruleToEdit); } private void loadVariablesIntoGui() @@ -465,164 +494,175 @@ public class ActivityManageRule extends Activity items.add(new Item(typesLong[i].toString(), R.drawable.headphone)); else if(types[i].toString().equals(Trigger_Enum.notification.toString())) items.add(new Item(typesLong[i].toString(), R.drawable.notification)); + else if(types[i].toString().equals(Trigger_Enum.deviceOrientation.toString())) + items.add(new Item(typesLong[i].toString(), R.drawable.smartphone)); else items.add(new Item(typesLong[i].toString(), R.drawable.placeholder)); } - ListAdapter adapter = new ArrayAdapter(this, android.R.layout.select_dialog_item, android.R.id.text1, items) - { - public View getView(int position, View convertView, ViewGroup parent) - { - //User super class to create the View - View v = super.getView(position, convertView, parent); - - TextView tv = (TextView)v.findViewById(android.R.id.text1); + ListAdapter adapter = new ArrayAdapter(this, android.R.layout.select_dialog_item, android.R.id.text1, items) + { + public View getView(int position, View convertView, ViewGroup parent) + { + //User super class to create the View + View v = super.getView(position, convertView, parent); - //Put the image on the TextView - tv.setCompoundDrawablesWithIntrinsicBounds(items.get(position).icon, 0, 0, 0); + TextView tv = (TextView)v.findViewById(android.R.id.text1); - //Add margin between image and text (support various screen densities) - int dp5 = (int) (5 * getResources().getDisplayMetrics().density + 0.5f); - tv.setCompoundDrawablePadding(dp5); + //Put the image on the TextView + tv.setCompoundDrawablesWithIntrinsicBounds(items.get(position).icon, 0, 0, 0); - return v; - } - }; + //Add margin between image and text (support various screen densities) + int dp5 = (int) (5 * getResources().getDisplayMetrics().density + 0.5f); + tv.setCompoundDrawablePadding(dp5); - AlertDialog.Builder builder = new AlertDialog.Builder(this) - .setTitle(getResources().getString(R.string.selectTypeOfTrigger)) - .setAdapter(adapter, new DialogInterface.OnClickListener() - { - @RequiresApi(api = Build.VERSION_CODES.KITKAT) - public void onClick(DialogInterface dialog, int which) - { - triggerType = Trigger_Enum.values()[which]; + return v; + } + }; - String[] booleanChoices = null; - if(triggerType == Trigger_Enum.pointOfInterest) + AlertDialog.Builder builder = new AlertDialog.Builder(this) + .setTitle(getResources().getString(R.string.selectTypeOfTrigger)) + .setAdapter(adapter, new DialogInterface.OnClickListener() + { + @RequiresApi(api = Build.VERSION_CODES.KITKAT) + public void onClick(DialogInterface dialog, int which) + { + triggerType = Trigger_Enum.values()[which]; + + String[] booleanChoices = null; + if(triggerType == Trigger_Enum.pointOfInterest) + { + if(Miscellaneous.googleToBlameForLocation(false)) { - if(Miscellaneous.googleToBlameForLocation(false)) - { - ActivityMainScreen.openGoogleBlamingWindow(); - return; - } - else - { - if (PointOfInterest.getPointOfInterestCollection() != null && PointOfInterest.getPointOfInterestCollection().size() > 0) - booleanChoices = new String[]{getResources().getString(R.string.entering), getResources().getString(R.string.leaving)}; - else - { - Toast.makeText(myContext, getResources().getString(R.string.noPoisSpecified), Toast.LENGTH_LONG).show(); - return; - } - } - } - else if(triggerType == Trigger_Enum.timeFrame) - { - newTrigger.setTriggerType(Trigger_Enum.timeFrame); - ActivityManageTriggerTimeFrame.editedTimeFrameTrigger = newTrigger; - Intent timeFrameEditor = new Intent(myContext, ActivityManageTriggerTimeFrame.class); - startActivityForResult(timeFrameEditor, requestCodeTriggerTimeframeAdd); + ActivityMainScreen.openGoogleBlamingWindow(); return; } - else if(triggerType == Trigger_Enum.charging) - booleanChoices = new String[]{getResources().getString(R.string.started), getResources().getString(R.string.stopped)}; - else if(triggerType == Trigger_Enum.usb_host_connection) - booleanChoices = new String[]{getResources().getString(R.string.connected), getResources().getString(R.string.disconnected)}; - else if(triggerType == Trigger_Enum.speed | triggerType == Trigger_Enum.noiseLevel | triggerType == Trigger_Enum.batteryLevel) - booleanChoices = new String[]{getResources().getString(R.string.exceeds), getResources().getString(R.string.dropsBelow)}; - else if(triggerType == Trigger_Enum.wifiConnection) - { - newTrigger.setTriggerType(Trigger_Enum.wifiConnection); - Intent wifiTriggerEditor = new Intent(myContext, ActivityManageTriggerWifi.class); - startActivityForResult(wifiTriggerEditor, requestCodeTriggerWifiAdd); - return; -// booleanChoices = new String[]{getResources().getString(R.string.started), getResources().getString(R.string.stopped)}; - } -// else if(triggerType == Trigger_Enum.wifiConnection) -// booleanChoices = new String[]{getResources().getString(R.string.connected), getResources().getString(R.string.disconnected)}; - else if(triggerType == Trigger_Enum.process_started_stopped) - booleanChoices = new String[]{getResources().getString(R.string.started), getResources().getString(R.string.stopped)}; - else if(triggerType == Trigger_Enum.notification) - { - newTrigger.setTriggerType(Trigger_Enum.notification); - Intent nfcEditor = new Intent(myContext, ActivityManageTriggerNotification.class); - startActivityForResult(nfcEditor, requestCodeTriggerNotificationAdd); - return; - } - else if(triggerType == Trigger_Enum.airplaneMode) - booleanChoices = new String[]{getResources().getString(R.string.activated), getResources().getString(R.string.deactivated)}; - else if(triggerType == Trigger_Enum.roaming) - booleanChoices = new String[]{getResources().getString(R.string.activated), getResources().getString(R.string.deactivated)}; - else if(triggerType == Trigger_Enum.phoneCall) - { - newTrigger.setTriggerType(Trigger_Enum.phoneCall); - Intent phoneTriggerEditor = new Intent(myContext, ActivityManageTriggerPhoneCall.class); - startActivityForResult(phoneTriggerEditor, requestCodeTriggerPhoneCallAdd); - return; -// booleanChoices = new String[]{getResources().getString(R.string.started), getResources().getString(R.string.stopped)}; - } - else if(triggerType == Trigger_Enum.activityDetection) - { - try - { - Method m = Miscellaneous.getClassMethodReflective(activityDetectionClassPath, "isPlayServiceAvailable"); - if(m != null) - { - boolean available = (Boolean)m.invoke(null); - if(available) - { - newTrigger.setTriggerType(Trigger_Enum.activityDetection); - getTriggerActivityDetectionDialog().show(); - } - else - Toast.makeText(myContext, getResources().getString(R.string.triggerOnlyAvailableIfPlayServicesInstalled), Toast.LENGTH_LONG).show(); - } - else - Miscellaneous.messageBox(getResources().getString(R.string.error), getResources().getString(R.string.featureNotInFdroidVersion), ActivityManageRule.this).show(); - } - catch (IllegalAccessException | InvocationTargetException e) - { - e.printStackTrace(); - } - return; - } - else if(triggerType == Trigger_Enum.nfcTag) - { - if(NfcReceiver.checkNfcRequirements(ActivityManageRule.this, true)) - { - newTrigger.setTriggerType(Trigger_Enum.nfcTag); - Intent nfcEditor = new Intent(myContext, ActivityManageTriggerNfc.class); - startActivityForResult(nfcEditor, requestCodeTriggerNfcTagAdd); - return; - } - } - else if(triggerType == Trigger_Enum.bluetoothConnection) - { - if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) - Miscellaneous.messageBox("Bluetooth", getResources().getString(R.string.deviceDoesNotHaveBluetooth), ActivityManageRule.this).show();; - - newTrigger.setTriggerType(Trigger_Enum.bluetoothConnection); - ActivityManageTriggerBluetooth.editedBluetoothTrigger = newTrigger; - Intent bluetoothEditor = new Intent(myContext, ActivityManageTriggerBluetooth.class); - startActivityForResult(bluetoothEditor, requestCodeTriggerBluetoothAdd); - return; - } - else if(triggerType == Trigger_Enum.headsetPlugged) - booleanChoices = new String[]{getResources().getString(R.string.connected), getResources().getString(R.string.disconnected)}; - - if(triggerType == Trigger_Enum.nfcTag) - { - if (NfcReceiver.checkNfcRequirements(ActivityManageRule.this, true)) - getTriggerParameterDialog(context, booleanChoices).show(); - } else + { + if (PointOfInterest.getPointOfInterestCollection() != null && PointOfInterest.getPointOfInterestCollection().size() > 0) + booleanChoices = new String[]{getResources().getString(R.string.entering), getResources().getString(R.string.leaving)}; + else + { + Toast.makeText(myContext, getResources().getString(R.string.noPoisSpecified), Toast.LENGTH_LONG).show(); + return; + } + } + } + else if(triggerType == Trigger_Enum.timeFrame) + { + newTrigger.setTriggerType(Trigger_Enum.timeFrame); + ActivityManageTriggerTimeFrame.editedTimeFrameTrigger = newTrigger; + Intent timeFrameEditor = new Intent(myContext, ActivityManageTriggerTimeFrame.class); + startActivityForResult(timeFrameEditor, requestCodeTriggerTimeframeAdd); + return; + } + else if(triggerType == Trigger_Enum.charging) + booleanChoices = new String[]{getResources().getString(R.string.started), getResources().getString(R.string.stopped)}; + else if(triggerType == Trigger_Enum.usb_host_connection) + booleanChoices = new String[]{getResources().getString(R.string.connected), getResources().getString(R.string.disconnected)}; + else if(triggerType == Trigger_Enum.speed | triggerType == Trigger_Enum.noiseLevel | triggerType == Trigger_Enum.batteryLevel) + booleanChoices = new String[]{getResources().getString(R.string.exceeds), getResources().getString(R.string.dropsBelow)}; + else if(triggerType == Trigger_Enum.wifiConnection) + { + newTrigger.setTriggerType(Trigger_Enum.wifiConnection); + Intent wifiTriggerEditor = new Intent(myContext, ActivityManageTriggerWifi.class); + startActivityForResult(wifiTriggerEditor, requestCodeTriggerWifiAdd); + return; +// booleanChoices = new String[]{getResources().getString(R.string.started), getResources().getString(R.string.stopped)}; + } + else if(triggerType == Trigger_Enum.deviceOrientation) + { + newTrigger.setTriggerType(Trigger_Enum.deviceOrientation); + Intent devicePositionTriggerEditor = new Intent(myContext, ActivityManageTriggerDeviceOrientation.class); + startActivityForResult(devicePositionTriggerEditor, requestCodeTriggerDeviceOrientationAdd); + return; +// booleanChoices = new String[]{getResources().getString(R.string.started), getResources().getString(R.string.stopped)}; + } +// else if(triggerType == Trigger_Enum.wifiConnection) +// booleanChoices = new String[]{getResources().getString(R.string.connected), getResources().getString(R.string.disconnected)}; + else if(triggerType == Trigger_Enum.process_started_stopped) + booleanChoices = new String[]{getResources().getString(R.string.started), getResources().getString(R.string.stopped)}; + else if(triggerType == Trigger_Enum.notification) + { + newTrigger.setTriggerType(Trigger_Enum.notification); + Intent nfcEditor = new Intent(myContext, ActivityManageTriggerNotification.class); + startActivityForResult(nfcEditor, requestCodeTriggerNotificationAdd); + return; + } + else if(triggerType == Trigger_Enum.airplaneMode) + booleanChoices = new String[]{getResources().getString(R.string.activated), getResources().getString(R.string.deactivated)}; + else if(triggerType == Trigger_Enum.roaming) + booleanChoices = new String[]{getResources().getString(R.string.activated), getResources().getString(R.string.deactivated)}; + else if(triggerType == Trigger_Enum.phoneCall) + { + newTrigger.setTriggerType(Trigger_Enum.phoneCall); + Intent phoneTriggerEditor = new Intent(myContext, ActivityManageTriggerPhoneCall.class); + startActivityForResult(phoneTriggerEditor, requestCodeTriggerPhoneCallAdd); + return; +// booleanChoices = new String[]{getResources().getString(R.string.started), getResources().getString(R.string.stopped)}; + } + else if(triggerType == Trigger_Enum.activityDetection) + { + try + { + Method m = Miscellaneous.getClassMethodReflective(activityDetectionClassPath, "isPlayServiceAvailable"); + if(m != null) + { + boolean available = (Boolean)m.invoke(null); + if(available) + { + newTrigger.setTriggerType(Trigger_Enum.activityDetection); + getTriggerActivityDetectionDialog().show(); + } + else + Toast.makeText(myContext, getResources().getString(R.string.triggerOnlyAvailableIfPlayServicesInstalled), Toast.LENGTH_LONG).show(); + } + else + Miscellaneous.messageBox(getResources().getString(R.string.error), getResources().getString(R.string.featureNotInFdroidVersion), ActivityManageRule.this).show(); + } + catch (IllegalAccessException | InvocationTargetException e) + { + e.printStackTrace(); + } + return; + } + else if(triggerType == Trigger_Enum.nfcTag) + { + if(NfcReceiver.checkNfcRequirements(ActivityManageRule.this, true)) + { + newTrigger.setTriggerType(Trigger_Enum.nfcTag); + Intent nfcEditor = new Intent(myContext, ActivityManageTriggerNfc.class); + startActivityForResult(nfcEditor, requestCodeTriggerNfcTagAdd); + return; + } + } + else if(triggerType == Trigger_Enum.bluetoothConnection) + { + if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) + Miscellaneous.messageBox("Bluetooth", getResources().getString(R.string.deviceDoesNotHaveBluetooth), ActivityManageRule.this).show();; + + newTrigger.setTriggerType(Trigger_Enum.bluetoothConnection); + ActivityManageTriggerBluetooth.editedBluetoothTrigger = newTrigger; + Intent bluetoothEditor = new Intent(myContext, ActivityManageTriggerBluetooth.class); + startActivityForResult(bluetoothEditor, requestCodeTriggerBluetoothAdd); + return; + } + else if(triggerType == Trigger_Enum.headsetPlugged) + booleanChoices = new String[]{getResources().getString(R.string.connected), getResources().getString(R.string.disconnected)}; + + if(triggerType == Trigger_Enum.nfcTag) + { + if (NfcReceiver.checkNfcRequirements(ActivityManageRule.this, true)) getTriggerParameterDialog(context, booleanChoices).show(); - } - }); - - return builder.create(); + } + else + getTriggerParameterDialog(context, booleanChoices).show(); + } + }); + + return builder.create(); } + private AlertDialog getTriggerParameterDialog(final Context myContext, final String[] choices) { AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context); @@ -1086,6 +1126,7 @@ public class ActivityManageRule extends Activity if(resultCode == RESULT_OK) { //add TriggerUrl + ActivityManageActionTriggerUrl.resultingAction.setParentRule(ruleToEdit); ruleToEdit.getActionSet().add(ActivityManageActionTriggerUrl.resultingAction); this.refreshActionList(); } @@ -1103,6 +1144,7 @@ public class ActivityManageRule extends Activity //add TimeFrame if(resultCode == RESULT_OK && ActivityManageTriggerTimeFrame.editedTimeFrameTrigger != null) { + newTrigger.setParentRule(ruleToEdit); ruleToEdit.getTriggerSet().add(newTrigger); this.refreshTriggerList(); } @@ -1114,6 +1156,7 @@ public class ActivityManageRule extends Activity //edit TimeFrame if(resultCode == RESULT_OK && ActivityManageTriggerTimeFrame.editedTimeFrameTrigger != null) { + ActivityManageTriggerTimeFrame.editedTimeFrameTrigger.setParentRule(ruleToEdit); this.refreshTriggerList(); } else @@ -1125,6 +1168,7 @@ public class ActivityManageRule extends Activity { newTrigger.setTriggerParameter(data.getBooleanExtra("wifiState", false)); newTrigger.setTriggerParameter2(data.getStringExtra("wifiName")); + newTrigger.setParentRule(ruleToEdit); ruleToEdit.getTriggerSet().add(newTrigger); this.refreshTriggerList(); } @@ -1133,8 +1177,12 @@ public class ActivityManageRule extends Activity { if(resultCode == RESULT_OK) { - newTrigger.setTriggerParameter(data.getBooleanExtra("wifiState", false)); - newTrigger.setTriggerParameter2(data.getStringExtra("wifiName")); + Trigger editedTrigger = new Trigger(); + editedTrigger.setTriggerType(Trigger_Enum.wifiConnection); + editedTrigger.setTriggerParameter(data.getBooleanExtra("wifiState", false)); + editedTrigger.setTriggerParameter2(data.getStringExtra("wifiName")); + editedTrigger.setParentRule(ruleToEdit); + ruleToEdit.getTriggerSet().set(editIndex, editedTrigger); this.refreshTriggerList(); } } @@ -1144,6 +1192,7 @@ public class ActivityManageRule extends Activity if(resultCode == RESULT_OK) { newAction = ActivityManageActionStartActivity.resultingAction; + newAction.setParentRule(ruleToEdit); ruleToEdit.getActionSet().add(newAction); this.refreshActionList(); } @@ -1154,6 +1203,7 @@ public class ActivityManageRule extends Activity if(resultCode == RESULT_OK) { newAction = ActivityManageActionStartActivity.resultingAction; + newAction.setParentRule(ruleToEdit); // ruleToEdit.getActionSet().add(newAction); this.refreshActionList(); } @@ -1164,6 +1214,7 @@ public class ActivityManageRule extends Activity if(resultCode == RESULT_OK && ActivityManageTriggerNfc.generatedId != null) { newTrigger.setNfcTagId(ActivityManageTriggerNfc.generatedId); + newTrigger.setParentRule(ruleToEdit); ruleToEdit.getTriggerSet().add(newTrigger); this.refreshTriggerList(); } @@ -1185,6 +1236,7 @@ public class ActivityManageRule extends Activity data.getStringExtra("textDir") + Trigger.triggerParameter2Split + data.getStringExtra("text") ); + newTrigger.setParentRule(ruleToEdit); this.refreshTriggerList(); } } @@ -1193,6 +1245,7 @@ public class ActivityManageRule extends Activity if(resultCode == RESULT_OK) { newTrigger = ActivityManageTriggerNotification.resultingTrigger; + newTrigger.setParentRule(ruleToEdit); this.refreshTriggerList(); } } @@ -1200,6 +1253,7 @@ public class ActivityManageRule extends Activity { if(resultCode == RESULT_OK) { + newTrigger.setParentRule(ruleToEdit); ruleToEdit.getTriggerSet().add(newTrigger); newTrigger.setTriggerParameter2(data.getStringExtra("triggerParameter2")); this.refreshTriggerList(); @@ -1210,6 +1264,7 @@ public class ActivityManageRule extends Activity if(resultCode == RESULT_OK) { newTrigger = ActivityManageTriggerPhoneCall.resultingTrigger; + newTrigger.setParentRule(ruleToEdit); this.refreshTriggerList(); } } @@ -1218,6 +1273,7 @@ public class ActivityManageRule extends Activity if(resultCode == RESULT_OK) { //add SpeakText + ActivityManageActionSpeakText.resultingAction.setParentRule(ruleToEdit); ruleToEdit.getActionSet().add(ActivityManageActionSpeakText.resultingAction); this.refreshActionList(); } @@ -1227,6 +1283,7 @@ public class ActivityManageRule extends Activity if(resultCode == RESULT_OK) { //edit SpeakText + ActivityManageActionSpeakText.resultingAction.setParentRule(ruleToEdit); newAction = ActivityManageActionSpeakText.resultingAction; this.refreshActionList(); } @@ -1236,6 +1293,7 @@ public class ActivityManageRule extends Activity //add bluetooth trigger if(resultCode == RESULT_OK && ActivityManageTriggerBluetooth.editedBluetoothTrigger != null) { + newTrigger.setParentRule(ruleToEdit); ruleToEdit.getTriggerSet().add(newTrigger); this.refreshTriggerList(); } @@ -1247,6 +1305,7 @@ public class ActivityManageRule extends Activity //edit bluetooth trigger if(resultCode == RESULT_OK && ActivityManageTriggerBluetooth.editedBluetoothTrigger != null) { + ActivityManageTriggerBluetooth.editedBluetoothTrigger.setParentRule(ruleToEdit); this.refreshTriggerList(); } else @@ -1258,6 +1317,7 @@ public class ActivityManageRule extends Activity { newAction.setParameter1(data.getBooleanExtra("autoBrightness", false)); newAction.setParameter2(String.valueOf(data.getIntExtra("brightnessValue", 0))); + newAction.setParentRule(ruleToEdit); ruleToEdit.getActionSet().add(newAction); this.refreshActionList(); } @@ -1272,6 +1332,8 @@ public class ActivityManageRule extends Activity if(data.hasExtra("brightnessValue")) ruleToEdit.getActionSet().get(editIndex).setParameter2(String.valueOf(data.getIntExtra("brightnessValue", 0))); + ruleToEdit.getActionSet().get(editIndex).setParentRule(ruleToEdit); + this.refreshActionList(); } } @@ -1279,6 +1341,7 @@ public class ActivityManageRule extends Activity { if(resultCode == RESULT_OK) { + newAction.setParentRule(ruleToEdit); newAction.setParameter2(data.getStringExtra("vibratePattern")); ruleToEdit.getActionSet().add(newAction); this.refreshActionList(); @@ -1288,6 +1351,8 @@ public class ActivityManageRule extends Activity { if(resultCode == RESULT_OK) { + ruleToEdit.getActionSet().get(editIndex).setParentRule(ruleToEdit); + if(data.hasExtra("vibratePattern")) ruleToEdit.getActionSet().get(editIndex).setParameter2(data.getStringExtra("vibratePattern")); @@ -1298,8 +1363,9 @@ public class ActivityManageRule extends Activity { if(resultCode == RESULT_OK) { - newAction.setParameter1(data.getBooleanExtra("actionParameter1", false)); - newAction.setParameter2(data.getStringExtra("actionParameter2")); + newAction.setParentRule(ruleToEdit); + newAction.setParameter1(data.getBooleanExtra(intentNameActionParameter1, false)); + newAction.setParameter2(data.getStringExtra(intentNameActionParameter2)); ruleToEdit.getActionSet().add(newAction); this.refreshActionList(); } @@ -1308,11 +1374,13 @@ public class ActivityManageRule extends Activity { if(resultCode == RESULT_OK) { - if(data.hasExtra("actionParameter1")) - ruleToEdit.getActionSet().get(editIndex).setParameter1(data.getBooleanExtra("actionParameter1", false)); + ruleToEdit.getActionSet().get(editIndex).setParentRule(ruleToEdit); - if(data.hasExtra("actionParameter2")) - ruleToEdit.getActionSet().get(editIndex).setParameter2(data.getStringExtra("actionParameter2")); + if(data.hasExtra(intentNameActionParameter1)) + ruleToEdit.getActionSet().get(editIndex).setParameter1(data.getBooleanExtra(intentNameActionParameter1, false)); + + if(data.hasExtra(intentNameActionParameter2)) + ruleToEdit.getActionSet().get(editIndex).setParameter2(data.getStringExtra(intentNameActionParameter2)); this.refreshActionList(); } @@ -1321,7 +1389,7 @@ public class ActivityManageRule extends Activity { if(resultCode == RESULT_OK) { - //add SendTextMessage + ActivityManageActionSendTextMessage.resultingAction.setParentRule(ruleToEdit); ruleToEdit.getActionSet().add(ActivityManageActionSendTextMessage.resultingAction); this.refreshActionList(); } @@ -1330,15 +1398,39 @@ public class ActivityManageRule extends Activity { if(resultCode == RESULT_OK) { - //edit SendTextMessage newAction = ActivityManageActionSendTextMessage.resultingAction; + newAction.setParentRule(ruleToEdit); //ruleToEdit.getActionSet().add(ActivityManageActionSendTextMessage.resultingAction); this.refreshActionList(); } } + else if(requestCode == requestCodeTriggerDeviceOrientationAdd) + { + if(resultCode == RESULT_OK) + { + newTrigger.setTriggerParameter(data.getBooleanExtra(ActivityManageRule.intentNameTriggerParameter1, true)); + newTrigger.setTriggerParameter2(data.getStringExtra(ActivityManageTriggerDeviceOrientation.vectorFieldName)); + newTrigger.setParentRule(ruleToEdit); + ruleToEdit.getTriggerSet().add(newTrigger); + this.refreshTriggerList(); + } + } + else if(requestCode == requestCodeTriggerDeviceOrientationEdit) + { + if(resultCode == RESULT_OK) + { + Trigger editedTrigger = new Trigger(); + editedTrigger.setTriggerType(Trigger_Enum.deviceOrientation); + editedTrigger.setTriggerParameter(data.getBooleanExtra(ActivityManageRule.intentNameTriggerParameter1, true)); + editedTrigger.setTriggerParameter2(data.getStringExtra(ActivityManageTriggerDeviceOrientation.vectorFieldName)); + editedTrigger.setParentRule(ruleToEdit); + ruleToEdit.getTriggerSet().set(editIndex, editedTrigger); + this.refreshTriggerList(); + } + } } - protected Dialog getActionTypeDialog() + protected AlertDialog getActionTypeDialog() { final ArrayList items = new ArrayList(); @@ -1355,14 +1447,16 @@ public class ActivityManageRule extends Activity items.add(new Item(typesLong[i].toString(), R.drawable.router)); else if(types[i].toString().equals(Action_Enum.setWifiTethering.toString())) items.add(new Item(typesLong[i].toString(), R.drawable.router)); + else if(types[i].toString().equals(Action_Enum.setBluetoothTethering.toString())) + items.add(new Item(typesLong[i].toString(), R.drawable.router)); else if(types[i].toString().equals(Action_Enum.setDisplayRotation.toString())) items.add(new Item(typesLong[i].toString(), R.drawable.displayrotation)); else if(types[i].toString().equals(Action_Enum.waitBeforeNextAction.toString())) items.add(new Item(typesLong[i].toString(), R.drawable.wait)); else if(types[i].toString().equals(Action_Enum.setAirplaneMode.toString())) items.add(new Item(typesLong[i].toString(), R.drawable.plane)); - else if(types[i].toString().equals(Action_Enum.wakeupDevice.toString())) - items.add(new Item(typesLong[i].toString(), R.drawable.alarm)); + else if(types[i].toString().equals(Action_Enum.turnScreenOnOrOff.toString())) + items.add(new Item(typesLong[i].toString(), R.drawable.smartphone)); else if(types[i].toString().equals(Action_Enum.changeSoundProfile.toString())) items.add(new Item(typesLong[i].toString(), R.drawable.sound)); else if(types[i].toString().equals(Action_Enum.triggerUrl.toString())) @@ -1455,6 +1549,17 @@ public class ActivityManageRule extends Activity newAction.setAction(Action_Enum.setWifiTethering); getActionParameter1Dialog(ActivityManageRule.this).show(); } + else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setBluetoothTethering.toString())) + { + newAction.setAction(Action_Enum.setBluetoothTethering); + getActionParameter1Dialog(ActivityManageRule.this).show(); + + if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) + Miscellaneous.messageBox("Bluetooth", getResources().getString(R.string.deviceDoesNotHaveBluetooth), ActivityManageRule.this).show();; + + if(Build.VERSION.SDK_INT > Build.VERSION_CODES.O) + Miscellaneous.messageBox(context.getResources().getString(R.string.notice), context.getResources().getString(R.string.btTetheringNotice), context).show(); + } else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setDisplayRotation.toString())) { newAction.setAction(Action_Enum.setDisplayRotation); @@ -1481,10 +1586,10 @@ public class ActivityManageRule extends Activity newAction.setAction(Action_Enum.waitBeforeNextAction); getActionWaitBeforeNextActionDialog(ActivityManageRule.this).show(); } - else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.wakeupDevice.toString())) + else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.turnScreenOnOrOff.toString())) { - newAction.setAction(Action_Enum.wakeupDevice); - getActionWakeupDeviceDialog(ActivityManageRule.this).show(); + newAction.setAction(Action_Enum.turnScreenOnOrOff); + getActionParameter1Dialog(ActivityManageRule.this).show(); } else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setAirplaneMode.toString())) { @@ -1492,7 +1597,6 @@ public class ActivityManageRule extends Activity getActionParameter1Dialog(ActivityManageRule.this).show(); if(Build.VERSION.SDK_INT >= 17) { -// Toast.makeText(context, getResources().getString(R.string.airplaneModeSdk17Warning), Toast.LENGTH_LONG).show(); Miscellaneous.messageBox(getResources().getString(R.string.airplaneMode), getResources().getString(R.string.rootExplanation), ActivityManageRule.this).show(); } } @@ -1512,7 +1616,7 @@ public class ActivityManageRule extends Activity } else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.sendTextMessage.toString())) { - if(ActivityPermissions.isPermissionDeclaratedInManifest(ActivityManageRule.this, "android.permission.SEND_SMS")) + if(ActivityPermissions.isPermissionDeclaratedInManifest(ActivityManageRule.this, Manifest.permission.SEND_SMS)) { //launch other activity to enter parameters; newAction.setAction(Action_Enum.sendTextMessage); diff --git a/app/src/main/java/com/jens/automation2/ActivityManageTriggerDeviceOrientation.java b/app/src/main/java/com/jens/automation2/ActivityManageTriggerDeviceOrientation.java new file mode 100644 index 0000000..0f1922f --- /dev/null +++ b/app/src/main/java/com/jens/automation2/ActivityManageTriggerDeviceOrientation.java @@ -0,0 +1,295 @@ +package com.jens.automation2; + +import android.app.Activity; +import android.content.Intent; +import android.graphics.Color; +import android.os.Bundle; +import android.text.InputFilter; +import android.text.Spanned; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.Nullable; + +import com.jens.automation2.receivers.DeviceOrientationListener; + +import org.apache.commons.lang3.StringUtils; + +public class ActivityManageTriggerDeviceOrientation extends Activity +{ + TextView currentAzimuth, currentPitch, currentRoll, tvAppliesAzimuth, tvAppliesPitch, tvAppliesRoll; + Button bApplyPositionValues, bSavePositionValues; + EditText etDesiredAzimuth, etDesiredAzimuthTolerance, etDesiredPitch, etDesiredPitchTolerance, etDesiredRoll, etDesiredRollTolerance; + CheckBox chkDevicePositionApplies; + + public static String vectorFieldName = "deviceVector"; + + boolean editMode = false; + + float desiredAzimuth, desiredPitch, desiredRoll, desiredAzimuthTolerance, desiredPitchTolerance, desiredRollTolerance; + + public void updateFields(float azimuth, float pitch, float roll) + { + currentAzimuth.setText(Float.toString(azimuth)); + currentPitch.setText(Float.toString(pitch)); + currentRoll.setText(Float.toString(roll)); + + try + { + desiredAzimuth = Float.parseFloat(etDesiredAzimuth.getText().toString()); + desiredAzimuthTolerance = Float.parseFloat(etDesiredAzimuthTolerance.getText().toString()); + if (Math.abs(azimuth) <= Math.abs(desiredAzimuth - desiredAzimuthTolerance) || Math.abs(azimuth) <= desiredAzimuth + desiredAzimuthTolerance) + { + tvAppliesAzimuth.setText(getResources().getString(R.string.yes)); + tvAppliesAzimuth.setTextColor(Color.GREEN); + } + else + { + tvAppliesAzimuth.setText(getResources().getString(R.string.no)); + tvAppliesAzimuth.setTextColor(Color.RED); + } + } + catch(Exception e) + { + tvAppliesAzimuth.setText(""); + } + + try + { + desiredPitch = Float.parseFloat(etDesiredPitch.getText().toString()); + desiredPitchTolerance = Float.parseFloat(etDesiredPitchTolerance.getText().toString()); + if (Math.abs(pitch) <= Math.abs(desiredPitch - desiredPitchTolerance) || Math.abs(pitch) <= desiredPitch + desiredPitchTolerance) + { + tvAppliesPitch.setText(getResources().getString(R.string.yes)); + tvAppliesPitch.setTextColor(Color.GREEN); + } + else + { + tvAppliesPitch.setText(getResources().getString(R.string.no)); + tvAppliesPitch.setTextColor(Color.RED); + } + } + catch(Exception e) + { + tvAppliesPitch.setText(""); + } + + try + { + desiredRoll = Float.parseFloat(etDesiredRoll.getText().toString()); + desiredRollTolerance = Float.parseFloat(etDesiredRollTolerance.getText().toString()); + if (Math.abs(roll) <= Math.abs(desiredRoll - desiredRollTolerance) || Math.abs(roll) <= desiredRoll + desiredRollTolerance) + { + tvAppliesRoll.setText(getResources().getString(R.string.yes)); + tvAppliesRoll.setTextColor(Color.GREEN); + } + else + { + tvAppliesRoll.setText(getResources().getString(R.string.no)); + tvAppliesRoll.setTextColor(Color.RED); + } + } + catch(Exception e) + { + tvAppliesRoll.setText(""); + } + } + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_manage_trigger_device_orientation); + + currentAzimuth = (TextView) findViewById(R.id.tvCurrentAzimuth); + currentPitch = (TextView) findViewById(R.id.tvCurrentOrientationPitch); + currentRoll = (TextView) findViewById(R.id.tvCurrentRoll); + tvAppliesAzimuth = (TextView) findViewById(R.id.tvAppliesAzimuth); + tvAppliesPitch = (TextView) findViewById(R.id.tvAppliesPitch); + tvAppliesRoll = (TextView) findViewById(R.id.tvAppliesRoll); + + bApplyPositionValues = (Button) findViewById(R.id.bApplyPositionValues); + bSavePositionValues = (Button) findViewById(R.id.bSavePositionValues); + + etDesiredAzimuth = (EditText) findViewById(R.id.etDesiredAzimuth); + etDesiredAzimuthTolerance = (EditText) findViewById(R.id.etDesiredAzimuthTolerance); + etDesiredPitch = (EditText) findViewById(R.id.etDesiredPitch); + etDesiredPitchTolerance = (EditText) findViewById(R.id.etDesiredPitchTolerance); + etDesiredRoll = (EditText) findViewById(R.id.etDesiredRoll); + etDesiredRollTolerance = (EditText) findViewById(R.id.etDesiredRollTolerance); + + chkDevicePositionApplies = (CheckBox)findViewById(R.id.chkDevicePositionApplies); + +// etDesiredAzimuth.setFilters(new InputFilter[]{new InputFilterMinMax(-180, 180)}); +// etDesiredPitch.setFilters(new InputFilter[]{new InputFilterMinMax(-180, 180)}); +// etDesiredRoll.setFilters(new InputFilter[]{new InputFilterMinMax(-180, 180)}); + etDesiredAzimuthTolerance.setFilters(new InputFilter[]{new InputFilterMinMax(0, 180)}); + etDesiredPitchTolerance.setFilters(new InputFilter[]{new InputFilterMinMax(0, 180)}); + etDesiredRollTolerance.setFilters(new InputFilter[]{new InputFilterMinMax(0, 180)}); + + if(getIntent().hasExtra(vectorFieldName)) + { + editMode = true; + try + { + boolean chkValue = getIntent().getBooleanExtra(ActivityManageRule.intentNameTriggerParameter1, true); + chkDevicePositionApplies.setChecked(chkValue); + String values[] = getIntent().getStringExtra(vectorFieldName).split(Trigger.triggerParameter2Split); + etDesiredAzimuth.setText(values[0]); + etDesiredAzimuthTolerance.setText(values[1]); + etDesiredPitch.setText(values[2]); + etDesiredPitchTolerance.setText(values[3]); + etDesiredRoll.setText(values[4]); + etDesiredRollTolerance.setText(values[5]); + } + catch(Exception e) + { + Toast.makeText(ActivityManageTriggerDeviceOrientation.this, getResources().getString(R.string.triggerWrong), Toast.LENGTH_SHORT).show(); + Miscellaneous.logEvent("e", "DevicePositionTrigger", "There\'s something wrong with a device position trigger. Content: " + getIntent().getStringExtra(vectorFieldName) + ", " + Log.getStackTraceString(e), 1); + } + } + + bApplyPositionValues.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(View v) + { + if(!StringUtils.isEmpty(currentAzimuth.getText())) + etDesiredAzimuth.setText(currentAzimuth.getText()); + + if(!StringUtils.isEmpty(currentPitch.getText())) + etDesiredPitch.setText(currentPitch.getText()); + + if(!StringUtils.isEmpty(currentRoll.getText())) + etDesiredRoll.setText(currentRoll.getText()); + } + }); + + bSavePositionValues.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(View v) + { + if(!checkInputs(true)) + { + Toast.makeText(ActivityManageTriggerDeviceOrientation.this, getResources().getString(R.string.enterValidNumbersIntoAllFields), Toast.LENGTH_LONG).show(); + } + else + { + // Save + Intent returnData = new Intent(); + returnData.putExtra(ActivityManageRule.intentNameTriggerParameter1, chkDevicePositionApplies.isChecked()); + returnData.putExtra(vectorFieldName, + etDesiredAzimuth.getText().toString() + Trigger.triggerParameter2Split + + etDesiredAzimuthTolerance.getText().toString() + Trigger.triggerParameter2Split + + etDesiredPitch.getText().toString() + Trigger.triggerParameter2Split + + etDesiredPitchTolerance.getText().toString() + Trigger.triggerParameter2Split + + etDesiredRoll.getText().toString() + Trigger.triggerParameter2Split + + etDesiredRollTolerance.getText().toString()); + + setResult(RESULT_OK, returnData); + finish(); + } + } + }); + } + + boolean checkInputs(boolean showMessages) + { + if( + !StringUtils.isEmpty(etDesiredAzimuth.getText().toString()) && Miscellaneous.isNumeric(etDesiredAzimuth.getText().toString()) + && + !StringUtils.isEmpty(etDesiredAzimuthTolerance.getText().toString()) && Miscellaneous.isNumeric(etDesiredAzimuthTolerance.getText().toString()) + && + !StringUtils.isEmpty(etDesiredPitch.getText().toString()) && Miscellaneous.isNumeric(etDesiredPitch.getText().toString()) + && + !StringUtils.isEmpty(etDesiredPitchTolerance.getText().toString()) && Miscellaneous.isNumeric(etDesiredPitchTolerance.getText().toString()) + && + !StringUtils.isEmpty(etDesiredRoll.getText().toString()) && Miscellaneous.isNumeric(etDesiredRoll.getText().toString()) + && + !StringUtils.isEmpty(etDesiredRollTolerance.getText().toString()) && Miscellaneous.isNumeric(etDesiredRollTolerance.getText().toString()) + ) + { + float da = Float.parseFloat(etDesiredAzimuth.getText().toString()); + float dp = Float.parseFloat(etDesiredPitch.getText().toString()); + float dr = Float.parseFloat(etDesiredRoll.getText().toString()); + + if(Math.abs(da) > 180 || Math.abs(dp) > 180 || Math.abs(dr) > 180) + { + return false; + } + + if(showMessages) + { + float dat = Float.parseFloat(etDesiredAzimuthTolerance.getText().toString()); + float dpt = Float.parseFloat(etDesiredPitchTolerance.getText().toString()); + float drt = Float.parseFloat(etDesiredRollTolerance.getText().toString()); + + /* + The user may enter a tolerance of 180° for two directions, but not all three. + Otherwise this trigger would always apply. + */ + if (Math.abs(dat) >= 180 && Math.abs(dpt) >= 180 && Math.abs(drt) >= 180) + { + Miscellaneous.messageBox(getResources().getString(R.string.warning), getResources().getString(R.string.toleranceOf180OnlyAllowedIn2Fields), ActivityManageTriggerDeviceOrientation.this).show(); + return false; + } + } + + return true; + } + + return false; + } + + @Override + protected void onResume() + { + super.onResume(); + DeviceOrientationListener.getInstance().startSensorFromConfigActivity(ActivityManageTriggerDeviceOrientation.this, this); + } + + @Override + protected void onPause() + { + super.onPause(); + DeviceOrientationListener.getInstance().stopSensorFromConfigActivity(); + } + + public class InputFilterMinMax implements InputFilter + { + private float minimumValue; + private float maximumValue; + + public InputFilterMinMax(float minimumValue, float maximumValue) + { + this.minimumValue = minimumValue; + this.maximumValue = maximumValue; + } + + private boolean isInRange(float a, float b, float c) + { + return b > a ? c >= a && c <= b : c >= b && c <= a; + } + + @Override + public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) + { + try + { + int input = Integer.parseInt(dest.subSequence(0, dstart).toString() + source + dest.subSequence(dend, dest.length())); + if (isInRange(minimumValue, maximumValue, input)) + return null; + } + catch (NumberFormatException nfe) + { + } + return ""; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jens/automation2/ActivityManageTriggerTimeFrame.java b/app/src/main/java/com/jens/automation2/ActivityManageTriggerTimeFrame.java index c67edf7..9bbf606 100644 --- a/app/src/main/java/com/jens/automation2/ActivityManageTriggerTimeFrame.java +++ b/app/src/main/java/com/jens/automation2/ActivityManageTriggerTimeFrame.java @@ -6,11 +6,16 @@ import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.EditText; import android.widget.RadioButton; import android.widget.TimePicker; import android.widget.Toast; +import org.apache.commons.lang3.StringUtils; + import java.sql.Time; +import java.text.ParseException; import java.util.ArrayList; import java.util.Calendar; @@ -18,8 +23,9 @@ public class ActivityManageTriggerTimeFrame extends Activity { Button bSaveTimeFrame; TimePicker startPicker, stopPicker; - CheckBox checkMonday, checkTuesday, checkWednesday, checkThursday, checkFriday, checkSaturday, checkSunday; + CheckBox checkMonday, checkTuesday, checkWednesday, checkThursday, checkFriday, checkSaturday, checkSunday, chkRepeat; RadioButton radioTimeFrameEntering, radioTimeFrameLeaving; + EditText etRepeatEvery; public static Trigger editedTimeFrameTrigger = null; @@ -44,7 +50,9 @@ public class ActivityManageTriggerTimeFrame extends Activity checkSunday = (CheckBox)findViewById(R.id.checkSunday); radioTimeFrameEntering = (RadioButton)findViewById(R.id.radioTimeFrameEntering); radioTimeFrameLeaving = (RadioButton)findViewById(R.id.radioTimeFrameLeaving); - + chkRepeat = (CheckBox)findViewById(R.id.chkRepeat); + etRepeatEvery = (EditText)findViewById(R.id.etRepeatEvery); + bSaveTimeFrame.setOnClickListener(new OnClickListener() { @Override @@ -92,11 +100,43 @@ public class ActivityManageTriggerTimeFrame extends Activity { Toast.makeText(getBaseContext(), getResources().getString(R.string.selectOneDay), Toast.LENGTH_LONG).show(); return; - } + } + + boolean goOn = false; + if(chkRepeat.isChecked()) + { + if(!StringUtils.isEmpty(etRepeatEvery.getText().toString())) + { + try + { + long value = Long.parseLong(etRepeatEvery.getText().toString()); + if(value > 0) + { + goOn = true; + } + } + catch(Exception e) + { + } + } + } + else + goOn = true; + + if(!goOn) + { + Toast.makeText(getBaseContext(), getResources().getString(R.string.enterRepetitionTime), Toast.LENGTH_LONG).show(); + return; + } if(editedTimeFrameTrigger.getTimeFrame() == null) + { // add new one - editedTimeFrameTrigger.setTimeFrame(new TimeFrame(startTime, stopTime, dayList)); + if(chkRepeat.isChecked()) + editedTimeFrameTrigger.setTimeFrame(new TimeFrame(startTime, stopTime, dayList, Long.parseLong(etRepeatEvery.getText().toString()))); + else + editedTimeFrameTrigger.setTimeFrame(new TimeFrame(startTime, stopTime, dayList, 0)); + } else { // edit one @@ -104,6 +144,11 @@ public class ActivityManageTriggerTimeFrame extends Activity editedTimeFrameTrigger.getTimeFrame().setTriggerTimeStop(stopTime); editedTimeFrameTrigger.getTimeFrame().getDayList().clear(); editedTimeFrameTrigger.getTimeFrame().setDayList(dayList); + + if(chkRepeat.isChecked()) + editedTimeFrameTrigger.getTimeFrame().setRepetition(Long.parseLong(etRepeatEvery.getText().toString())); + else + editedTimeFrameTrigger.getTimeFrame().setRepetition(0); } editedTimeFrameTrigger.setTriggerParameter(radioTimeFrameEntering.isChecked()); @@ -112,6 +157,15 @@ public class ActivityManageTriggerTimeFrame extends Activity finish(); } }); + + chkRepeat.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() + { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) + { + etRepeatEvery.setEnabled(isChecked); + } + }); if(editedTimeFrameTrigger.getTimeFrame() != null) loadVariableIntoGui(); @@ -158,6 +212,12 @@ public class ActivityManageTriggerTimeFrame extends Activity break; } } + + if(editedTimeFrameTrigger.getTimeFrame().getRepetition() > 0) + { + chkRepeat.setChecked(true); + etRepeatEvery.setText(String.valueOf(editedTimeFrameTrigger.getTimeFrame().getRepetition())); + } } } diff --git a/app/src/main/java/com/jens/automation2/ActivityPermissions.java b/app/src/main/java/com/jens/automation2/ActivityPermissions.java index 76738cb..72264d3 100644 --- a/app/src/main/java/com/jens/automation2/ActivityPermissions.java +++ b/app/src/main/java/com/jens/automation2/ActivityPermissions.java @@ -5,6 +5,7 @@ import android.app.Activity; import android.app.AlertDialog; import android.app.Notification; import android.app.NotificationManager; +import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; @@ -46,6 +47,7 @@ public class ActivityPermissions extends Activity private static final int requestCodeForPermissionsNotificationPolicy = 12044; private static final int requestCodeForPermissionsBackgroundLocation = 12045; private static final int requestCodeForPermissionsNotifications = 12046; + private static final int requestCodeForPermissionsDeviceAdmin = 12047; protected String[] specificPermissionsToRequest = null; public static String intentExtraName = "permissionsToBeRequested"; @@ -238,9 +240,9 @@ public class ActivityPermissions extends Activity if( s.equalsIgnoreCase(Manifest.permission.ACCESS_BACKGROUND_LOCATION) || - s.equalsIgnoreCase(Manifest.permission.ACCESS_FINE_LOCATION) + s.equalsIgnoreCase(Manifest.permission.ACCESS_FINE_LOCATION) || - s.equalsIgnoreCase(Manifest.permission.ACCESS_COARSE_LOCATION) + s.equalsIgnoreCase(Manifest.permission.ACCESS_COARSE_LOCATION) ) { if (!Miscellaneous.googleToBlameForLocation(true)) @@ -282,6 +284,10 @@ public class ActivityPermissions extends Activity { return verifyNotificationPermission(); } + else if (s.equals(Manifest.permission.BIND_DEVICE_ADMIN)) + { + return haveDeviceAdmin(); + } else { int res = context.checkCallingOrSelfPermission(s); @@ -292,6 +298,33 @@ public class ActivityPermissions extends Activity return true; } + public static boolean haveDeviceAdmin() + { + DevicePolicyManager deviceManger = (DevicePolicyManager)Miscellaneous.getAnyContext().getSystemService(Context.DEVICE_POLICY_SERVICE); +// ComponentName compName = new ComponentName(ActivityPermissions.getInstance(), DeviceAdmin.class ) ; + ComponentName compName = new ComponentName(Miscellaneous.getAnyContext(), DeviceAdmin.class) ; + boolean active = deviceManger.isAdminActive(compName); + return active; + } + + public static void requestDeviceAdmin() + { + if(!haveDeviceAdmin()) + { +// deviceManger.removeActiveAdmin(compName); +// } +// else +// { + DevicePolicyManager deviceManger = (DevicePolicyManager)Miscellaneous.getAnyContext().getSystemService(Context.DEVICE_POLICY_SERVICE); + ComponentName compName = new ComponentName(ActivityPermissions.getInstance(), DeviceAdmin.class) ; + + Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN ); + intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN , compName ); + intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION , Miscellaneous.getAnyContext().getResources().getString(R.string.deviceAdminNote)); + ActivityPermissions.getInstance().startActivityForResult(intent, requestCodeForPermissionsDeviceAdmin); + } + } + public static String[] getRequiredPermissions(boolean onlyGeneral) { ArrayList requiredPermissions = new ArrayList(); @@ -531,6 +564,12 @@ public class ActivityPermissions extends Activity // https://stackoverflow.com/questions/32185628/connectivitymanager-requestnetwork-in-android-6-0 // addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions); break; + case setBluetoothTethering: + //addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions); + addToArrayListUnique(Manifest.permission.BLUETOOTH, requiredPermissions); + addToArrayListUnique(Manifest.permission.BLUETOOTH_ADMIN, requiredPermissions); + addToArrayListUnique(Manifest.permission.WRITE_SETTINGS, requiredPermissions); + break; case setWifi: addToArrayListUnique(Manifest.permission.WRITE_SETTINGS, requiredPermissions); // https://stackoverflow.com/questions/32185628/connectivitymanager-requestnetwork-in-android-6-0 @@ -557,9 +596,9 @@ public class ActivityPermissions extends Activity if( action.getParameter2().contains(Actions.wireguard_tunnel_up) || - action.getParameter2().contains(Actions.wireguard_tunnel_down) + action.getParameter2().contains(Actions.wireguard_tunnel_down) || - action.getParameter2().contains(Actions.wireguard_tunnel_refresh) + action.getParameter2().contains(Actions.wireguard_tunnel_refresh) ) addToArrayListUnique(ActivityPermissions.permissionNameWireguard, requiredPermissions); // if( @@ -587,40 +626,37 @@ public class ActivityPermissions extends Activity break; case turnUsbTetheringOff: addToArrayListUnique(Manifest.permission.WRITE_SETTINGS, requiredPermissions); -// addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions); break; case turnUsbTetheringOn: addToArrayListUnique(Manifest.permission.WRITE_SETTINGS, requiredPermissions); -// addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions); break; case turnWifiOff: addToArrayListUnique(Manifest.permission.WRITE_SETTINGS, requiredPermissions); -// addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions); addToArrayListUnique(Manifest.permission.ACCESS_NETWORK_STATE, requiredPermissions); break; case turnWifiOn: addToArrayListUnique(Manifest.permission.WRITE_SETTINGS, requiredPermissions); -// addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions); addToArrayListUnique(Manifest.permission.ACCESS_NETWORK_STATE, requiredPermissions); break; case turnWifiTetheringOff: addToArrayListUnique(Manifest.permission.WRITE_SETTINGS, requiredPermissions); -// addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions); addToArrayListUnique(Manifest.permission.ACCESS_NETWORK_STATE, requiredPermissions); break; case turnWifiTetheringOn: addToArrayListUnique(Manifest.permission.WRITE_SETTINGS, requiredPermissions); -// addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions); addToArrayListUnique(Manifest.permission.ACCESS_NETWORK_STATE, requiredPermissions); break; case waitBeforeNextAction: break; - case wakeupDevice: - addToArrayListUnique(Manifest.permission.WAKE_LOCK, requiredPermissions); - break; case playSound: addToArrayListUnique(Manifest.permission.READ_EXTERNAL_STORAGE, requiredPermissions); break; + case turnScreenOnOrOff: + if(action.getParameter1()) + addToArrayListUnique(Manifest.permission.WAKE_LOCK, requiredPermissions); + else + addToArrayListUnique(Manifest.permission.BIND_DEVICE_ADMIN, requiredPermissions); + break; default: break; } @@ -696,13 +732,6 @@ public class ActivityPermissions extends Activity usingElements.add(String.format(getResources().getString(R.string.ruleXrequiresThis), ruleName)); break; case Manifest.permission.ACCESS_COARSE_LOCATION: -// usingElements.add(getResources().getString(R.string.android_permission_ACCESS_COARSE_LOCATION)); - usingElements.add(getResources().getString(R.string.manageLocations)); - for(String ruleName : getRulesUsing(Trigger.Trigger_Enum.pointOfInterest)) - usingElements.add(String.format(getResources().getString(R.string.ruleXrequiresThis), ruleName)); - for(String ruleName : getRulesUsing(Trigger.Trigger_Enum.speed)) - usingElements.add(String.format(getResources().getString(R.string.ruleXrequiresThis), ruleName)); - break; case Manifest.permission.ACCESS_FINE_LOCATION: usingElements.add(getResources().getString(R.string.manageLocations)); for(String ruleName : getRulesUsing(Trigger.Trigger_Enum.pointOfInterest)) @@ -759,14 +788,6 @@ public class ActivityPermissions extends Activity for(String ruleName : getRulesUsing(Trigger.Trigger_Enum.wifiConnection)) usingElements.add(String.format(getResources().getString(R.string.ruleXrequiresThis), ruleName)); break; - /*case "android.permission.BATTERY_STATS": - for(String ruleName : getRulesUsing(Trigger.Trigger_Enum.batteryLevel)) - usingElements.add(String.format(getResources().getString(R.string.ruleXrequiresThis), ruleName)); - for(String ruleName : getRulesUsing(Trigger.Trigger_Enum.charging)) - usingElements.add(String.format(getResources().getString(R.string.ruleXrequiresThis), ruleName)); - for(String ruleName : getRulesUsing(Trigger.Trigger_Enum.usb_host_connection)) - usingElements.add(String.format(getResources().getString(R.string.ruleXrequiresThis), ruleName)); - break;*/ case Manifest.permission.BLUETOOTH_ADMIN: for(String ruleName : getRulesUsing(Trigger.Trigger_Enum.bluetoothConnection)) usingElements.add(String.format(getResources().getString(R.string.ruleXrequiresThis), ruleName)); @@ -834,6 +855,10 @@ public class ActivityPermissions extends Activity for(String ruleName : getRulesUsing(Action.Action_Enum.playSound)) usingElements.add(String.format(getResources().getString(R.string.ruleXrequiresThis), ruleName)); break; + case Manifest.permission.BIND_DEVICE_ADMIN: + for(String ruleName : getRulesUsing(Action.Action_Enum.turnScreenOnOrOff)) + usingElements.add(String.format(getResources().getString(R.string.ruleXrequiresThis), ruleName)); + break; } return usingElements; @@ -860,6 +885,13 @@ public class ActivityPermissions extends Activity requestPermissions(cachedPermissionsToRequest, true); } } + if (requestCode == requestCodeForPermissionsDeviceAdmin) + { + NotificationManager mNotificationManager = (NotificationManager) ActivityPermissions.this.getSystemService(Context.NOTIFICATION_SERVICE); + + if (mNotificationManager.isNotificationPolicyAccessGranted()) + requestPermissions(cachedPermissionsToRequest, true); + } if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { @@ -923,12 +955,18 @@ public class ActivityPermissions extends Activity startActivityForResult(intent, requestCodeForPermissionsWriteSettings); return; } + if (s.equalsIgnoreCase(Manifest.permission.BIND_DEVICE_ADMIN)) + { + requiredPermissions.remove(s); + cachedPermissionsToRequest = requiredPermissions; + requestDeviceAdmin(); + return; + } else if (s.equalsIgnoreCase(Manifest.permission.ACCESS_NOTIFICATION_POLICY)) { requiredPermissions.remove(s); cachedPermissionsToRequest = requiredPermissions; Intent intent = new Intent(android.provider.Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS); -// intent.setData(Uri.parse("package:" + getPackageName())); startActivityForResult(intent, requestCodeForPermissionsNotificationPolicy); return; } @@ -968,7 +1006,7 @@ public class ActivityPermissions extends Activity { if(!ActivityPermissions.isPermissionDeclaratedInManifest(Miscellaneous.getAnyContext(), Manifest.permission.SEND_SMS) && - Miscellaneous.isGooglePlayInstalled(Miscellaneous.getAnyContext()) + Miscellaneous.isGooglePlayInstalled(Miscellaneous.getAnyContext()) ) { requiredPermissions.remove(Manifest.permission.PROCESS_OUTGOING_CALLS); @@ -978,8 +1016,6 @@ public class ActivityPermissions extends Activity if(requiredPermissions.contains(Manifest.permission.SEND_SMS)) { if(!ActivityPermissions.isPermissionDeclaratedInManifest(Miscellaneous.getAnyContext(), Manifest.permission.SEND_SMS) -// && -// Miscellaneous.isGooglePlayInstalled(Miscellaneous.getAnyContext()) ) { requiredPermissions.remove(Manifest.permission.SEND_SMS); @@ -996,11 +1032,8 @@ public class ActivityPermissions extends Activity Miscellaneous.logEvent("i", "Permissions", "Requesting permissions: " + permissions, 2); -// Toast.makeText(ActivityPermissions.this, "Requesting permissions. Amount: " + String.valueOf(requiredPermissions.size()), Toast.LENGTH_LONG).show(); if(requiredPermissions.size() > 0) requestPermissions(requiredPermissions.toArray(new String[requiredPermissions.size()]), requestCodeForPermissions); -// else -// Miscellaneous.messageBox(getResources().getString(R.string.warning), getResources().getString(R.string.permissionsRequiredNotAvailable), ActivityPermissions.this).show(); } else setHaveAllPermissions(); @@ -1018,17 +1051,11 @@ public class ActivityPermissions extends Activity public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { Miscellaneous.logEvent("i", "onRequestPermissionsResult()", "onRequestPermissionsResult()", 3); -// Toast.makeText(ActivityPermissions.this, "onRequestPermissionsResult()", Toast.LENGTH_LONG).show(); -// ArrayList disabledFeatures = new ArrayList(); ArrayList deniedPermissions = new ArrayList(); if (requestCode == requestCodeForPermissions) { - /*ArrayList affectedGeneralList = new ArrayList(); - ArrayList affectedTriggersList = new ArrayList(); - ArrayList affectedActionList = new ArrayList();*/ - for (int i=0; i < grantResults.length; i++) { if(permissions[i].equalsIgnoreCase(Manifest.permission.WRITE_EXTERNAL_STORAGE) && grantResults[i] == PackageManager.PERMISSION_GRANTED) @@ -1088,8 +1115,8 @@ public class ActivityPermissions extends Activity if(deniedPermissions.size() > 0) { /* - The user denied certain permissions. With the exception of write-storage we need to live with that - and simply disable features while keeping the notification alive. The user may dismiss it anyway. + The user denied certain permissions. We need to live with that and simply disable + features while keeping the notification alive. The user may dismiss it anyway. */ Miscellaneous.logEvent("w", "Denied permissions", getResources().getString(R.string.theFollowingPermissionsHaveBeenDenied) + Miscellaneous.explode(", ", deniedPermissions), 3); @@ -1314,10 +1341,7 @@ public class ActivityPermissions extends Activity mapActionPermissions.put("setWifiTethering", Manifest.permission.WRITE_SETTINGS); mapActionPermissions.put("setWifiTethering", Manifest.permission.CHANGE_NETWORK_STATE); mapActionPermissions.put("setWifiTethering", Manifest.permission.ACCESS_NETWORK_STATE); -// mapActionPermissions.put("speakText", Manifest.permission.ACCESS_NOTIFICATION_POLICY); -// mapActionPermissions.put("startOtherActivity", ""); mapActionPermissions.put("triggerUrl", Manifest.permission.INTERNET); -// Hier müßte ein Hinweis kommen, daß nur die Variablen verwendet werden können, für die es Rechte gibt. mapActionPermissions.put("turnBluetoothOff", Manifest.permission.BLUETOOTH_ADMIN); mapActionPermissions.put("turnBluetoothOff", Manifest.permission.BLUETOOTH); mapActionPermissions.put("turnBluetoothOff", Manifest.permission.ACCESS_NETWORK_STATE); @@ -1347,209 +1371,6 @@ public class ActivityPermissions extends Activity mapActionPermissions.put("wakeupDevice", Manifest.permission.WAKE_LOCK); } - /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - public static boolean isPermissionDeclaratedInManifest(Context context, String permission) { PackageManager pm = context.getPackageManager(); @@ -1568,12 +1389,11 @@ public class ActivityPermissions extends Activity ArrayList requestedPermissionsArrayList = new ArrayList(); requestedPermissionsArrayList.addAll(requestedPermissionsList); return (requestedPermissionsArrayList.contains(permission)); -// Log.i(ExConsts.TAG, ""+requestedPermissionsArrayList); } } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); + Miscellaneous.logEvent("w", "ActivityPermissions", Log.getStackTraceString(e), 2); } return false; diff --git a/app/src/main/java/com/jens/automation2/ActivityVolumeTest.java b/app/src/main/java/com/jens/automation2/ActivityVolumeTest.java index 7c4de0b..b8977b7 100644 --- a/app/src/main/java/com/jens/automation2/ActivityVolumeTest.java +++ b/app/src/main/java/com/jens/automation2/ActivityVolumeTest.java @@ -30,14 +30,14 @@ public class ActivityVolumeTest extends Activity instance = this; super.onCreate(savedInstanceState); - setContentView(R.layout.activity_volume_test); + setContentView(R.layout.activity_volume_calibration); tvCurrentVolume = (TextView)findViewById(R.id.tvCurrentVolume); etReferenceValue = (EditText)findViewById(R.id.etReferenceValue); sbReferenceValue = (SeekBar)findViewById(R.id.sbReferenceValue); - tvVolumeTestExplanation = (TextView)findViewById(R.id.tvVolumeTestExplanation); + tvVolumeTestExplanation = (TextView)findViewById(R.id.tvVolumeCalibrationExplanation); - tvVolumeTestExplanation.setText(String.format(getResources().getString(R.string.volumeTesterExplanation), String.valueOf(volumeRefreshInterval))); + tvVolumeTestExplanation.setText(String.format(getResources().getString(R.string.volumeCalibrationExplanation), String.valueOf(volumeRefreshInterval))); etReferenceValue.setText(String.valueOf(Settings.referenceValueForNoiseLevelMeasurements)); diff --git a/app/src/main/java/com/jens/automation2/AutomationService.java b/app/src/main/java/com/jens/automation2/AutomationService.java index 5c39d87..a14a91d 100644 --- a/app/src/main/java/com/jens/automation2/AutomationService.java +++ b/app/src/main/java/com/jens/automation2/AutomationService.java @@ -199,6 +199,7 @@ public class AutomationService extends Service implements OnInitListener if (checkStartupRequirements(this, startAtBoot)) { Miscellaneous.logEvent("i", "Service", this.getResources().getString(R.string.logServiceStarting) + " VERSION_CODE: " + BuildConfig.VERSION_CODE + ", VERSION_NAME: " + BuildConfig.VERSION_NAME + ", flavor: " + BuildConfig.FLAVOR, 1); + Miscellaneous.logEvent("i", "Service", ActivityMaintenance.getSystemInfo(), 1); startUpRoutine(); @@ -273,16 +274,7 @@ public class AutomationService extends Service implements OnInitListener public void applySettingsAndRules() { - if (Settings.useTextToSpeechOnNormal | Settings.useTextToSpeechOnSilent | Settings.useTextToSpeechOnVibrate) - { - if (ttsEngine == null) - ttsEngine = new TextToSpeech(this, this); - } - else - { - if (ttsEngine != null) - ttsEngine.shutdown(); - } + checkForTtsEngine(); startLocationProvider(); ReceiverCoordinator.startAllReceivers(); @@ -305,7 +297,7 @@ public class AutomationService extends Service implements OnInitListener public void checkForTtsEngine() { - if (Settings.useTextToSpeechOnNormal | Settings.useTextToSpeechOnSilent | Settings.useTextToSpeechOnVibrate | Rule.isAnyRuleUsing(Action.Action_Enum.speakText)) + if (Settings.useTextToSpeechOnNormal || Settings.useTextToSpeechOnSilent || Settings.useTextToSpeechOnVibrate || Rule.isAnyRuleUsing(Action.Action_Enum.speakText)) { if (ttsEngine == null) ttsEngine = new TextToSpeech(this, this); @@ -352,12 +344,8 @@ public class AutomationService extends Service implements OnInitListener if(r.isRuleActive()) { if(!r.haveEnoughPermissions()) -// for (String permission : ActivityPermissions.getPermissionsForRule(r)) { -// if (!ActivityPermissions.havePermission(permission, AutomationService.this)) { -// r.setRuleActive(false); -// r.change(AutomationService.this); if(!displayNotification) { displayNotification = true; @@ -410,10 +398,7 @@ public class AutomationService extends Service implements OnInitListener public void cancelNotification() { -// stopForeground(false); NotificationManagerCompat.from(AutomationService.this).cancelAll(); -// NotificationManagerCompat.from(AutomationService.this).cancel(ActivityPermissions.notificationIdPermissions); -// NotificationManagerCompat.from(AutomationService.this).cancel(AutomationService.notificationIdRestrictions); } protected void checkForMissingBackgroundLocationPermission() @@ -450,7 +435,6 @@ public class AutomationService extends Service implements OnInitListener private void stopRoutine() { Miscellaneous.logEvent("i", "Service", "Stopping service...", 3); -// Log.i("STOP", "Stopping"); try { myLocationProvider.stopLocationService(); @@ -479,7 +463,6 @@ public class AutomationService extends Service implements OnInitListener builder.setWhen(System.currentTimeMillis()); builder.setContentIntent(myPendingIntent); -// Notification defaultNotification = new Notification(); Notification defaultNotification = builder.build(); defaultNotification.icon = R.drawable.ic_launcher; @@ -497,31 +480,6 @@ public class AutomationService extends Service implements OnInitListener // defaultNotification.ledOffMS = 1500; return builder; - - /*NotificationManager mNotificationManager = (NotificationManager) AutomationService.getInstance().getSystemService(Context.NOTIFICATION_SERVICE); - - NotificationCompat.Builder builder; - builder = new NotificationCompat.Builder(AutomationService.getInstance()); - - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) - builder.setCategory(Notification.CATEGORY_EVENT); - - builder.setWhen(System.currentTimeMillis()); - - builder.setContentTitle("Automation"); - builder.setSmallIcon(R.drawable.ic_launcher); -// builder.setContentText(textToDisplay); -// builder.setSmallIcon(icon); -// builder.setContentIntent(pendingIntent); -// builder.setStyle(new NotificationCompat.BigTextStyle().bigText(textToDisplay)); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) - { - NotificationChannel channel = new NotificationChannel("notify_001", "Channel human readable title", NotificationManager.IMPORTANCE_DEFAULT); - mNotificationManager.createNotificationChannel(channel); - } - - return builder;*/ } protected static NotificationCompat.Builder createDefaultNotificationBuilder() @@ -571,79 +529,68 @@ public class AutomationService extends Service implements OnInitListener if(instance != null) { -// if(Settings.showIconWhenServiceIsRunning) -// { - Miscellaneous.logEvent("i", "Notification", "Request to update notification.", 4); - - String bodyText=""; - String lastRuleString = ""; - - if(PointOfInterest.getPointOfInterestCollection() != null && PointOfInterest.getPointOfInterestCollection().size() > 0) - { - try - { - PointOfInterest activePoi = PointOfInterest.getActivePoi(); - if(activePoi == null) - { - PointOfInterest closestPoi = PointOfInterest.getClosestPOI(instance.getLocationProvider().getCurrentLocation()); - bodyText = AutomationService.getInstance().getResources().getString(R.string.activePoi) + ": " + AutomationService.getInstance().getResources().getString(R.string.none) + "\n" + AutomationService.getInstance().getResources().getString(R.string.closestPoi) + ": " + closestPoi.getName() + lastRuleString; - } - else - { - bodyText = AutomationService.getInstance().getResources().getString(R.string.activePoi) + ": " + activePoi.getName() + lastRuleString; - } - } - catch(NullPointerException e) - { - if( - Rule.isAnyRuleUsing(Trigger_Enum.pointOfInterest) - && - ActivityPermissions.havePermission(Manifest.permission.ACCESS_COARSE_LOCATION, AutomationService.getInstance()) - && - ActivityPermissions.havePermission(Manifest.permission.ACCESS_FINE_LOCATION, AutomationService.getInstance()) - ) - bodyText = instance.getResources().getString(R.string.stillGettingPosition); - else - bodyText = instance.getResources().getString(R.string.locationEngineNotActive); - } - } - + Miscellaneous.logEvent("i", "Notification", "Request to update notification.", 4); + + String bodyText=""; + String lastRuleString = ""; + + if(PointOfInterest.getPointOfInterestCollection() != null && PointOfInterest.getPointOfInterestCollection().size() > 0) + { try { - lastRuleString = instance.getResources().getString(R.string.lastRule) + " " + Rule.getLastActivatedRule().getName() + " " + instance.getResources().getString(R.string.at) + " " + Rule.getLastActivatedRuleActivationTime().toLocaleString(); + PointOfInterest activePoi = PointOfInterest.getActivePoi(); + if(activePoi == null) + { + PointOfInterest closestPoi = PointOfInterest.getClosestPOI(instance.getLocationProvider().getCurrentLocation()); + bodyText = AutomationService.getInstance().getResources().getString(R.string.activePoi) + ": " + AutomationService.getInstance().getResources().getString(R.string.none) + "\n" + AutomationService.getInstance().getResources().getString(R.string.closestPoi) + ": " + closestPoi.getName() + lastRuleString; + } + else + { + bodyText = AutomationService.getInstance().getResources().getString(R.string.activePoi) + ": " + activePoi.getName() + lastRuleString; + } } - catch(Exception e) + catch(NullPointerException e) { - lastRuleString = instance.getResources().getString(R.string.lastRule) + " n./a."; + if( + Rule.isAnyRuleUsing(Trigger_Enum.pointOfInterest) + && + ActivityPermissions.havePermission(Manifest.permission.ACCESS_COARSE_LOCATION, AutomationService.getInstance()) + && + ActivityPermissions.havePermission(Manifest.permission.ACCESS_FINE_LOCATION, AutomationService.getInstance()) + ) + bodyText = instance.getResources().getString(R.string.stillGettingPosition); + else + bodyText = instance.getResources().getString(R.string.locationEngineNotActive); } + } - String textToDisplay = bodyText + " " + lastRuleString; -// if(Build.VERSION.SDK_INT < 11) -// { -// myNotification.setLatestEventInfo(instance, "Automation", textToDisplay, myPendingIntent); -// } -// else -// { - if(notificationBuilder == null) - notificationBuilder = createDefaultNotificationBuilder(); + try + { + lastRuleString = instance.getResources().getString(R.string.lastRule) + " " + Rule.getLastActivatedRule().getName() + " " + instance.getResources().getString(R.string.at) + " " + Rule.getLastActivatedRuleActivationTime().toLocaleString(); + } + catch(Exception e) + { + lastRuleString = instance.getResources().getString(R.string.lastRule) + " n./a."; + } - notificationBuilder.setContentText(textToDisplay); - notificationBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(textToDisplay)); + String textToDisplay = bodyText + " " + lastRuleString; - myNotification = notificationBuilder.build(); - myNotification.defaults = 0; -// } + if(notificationBuilder == null) + notificationBuilder = createDefaultNotificationBuilder(); + + notificationBuilder.setContentText(textToDisplay); + notificationBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(textToDisplay)); + + myNotification = notificationBuilder.build(); + myNotification.defaults = 0; // NotificationManager notificationManager = (NotificationManager) instance.getSystemService(NOTIFICATION_SERVICE); - // hide the notification after its selected + // hide the notification after its selected // myNotification.flags |= Notification.FLAG_AUTO_CANCEL; - myNotification.flags |= Notification.FLAG_NO_CLEAR; + myNotification.flags |= Notification.FLAG_NO_CLEAR; // notificationManager.notify(notificationId, myNotification); - instance.startForeground(notificationId, myNotification); -// } -// else -// instance.startForeground(notificationId, null); // do not show icon in task bar + instance.startForeground(notificationId, myNotification); } } @@ -667,18 +614,18 @@ public class AutomationService extends Service implements OnInitListener **/ public void speak(String text, boolean force) { - if(text.length() > 0 && (force | Settings.useTextToSpeechOnNormal | Settings.useTextToSpeechOnSilent | Settings.useTextToSpeechOnVibrate)) + if(text.length() > 0 && (force || Settings.useTextToSpeechOnNormal || Settings.useTextToSpeechOnSilent || Settings.useTextToSpeechOnVibrate)) { AudioManager myAudioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE); int mode = myAudioManager.getRingerMode(); if( (mode == AudioManager.RINGER_MODE_NORMAL && Settings.useTextToSpeechOnNormal) - | + || (mode == AudioManager.RINGER_MODE_VIBRATE && Settings.useTextToSpeechOnVibrate) - | + || (mode == AudioManager.RINGER_MODE_SILENT && Settings.useTextToSpeechOnSilent) - | + || force ) { @@ -708,12 +655,12 @@ public class AutomationService extends Service implements OnInitListener {} } } + Miscellaneous.logEvent("i", "TextToSpeech", "Speaking " + text + " in language " + ttsEngine.getLanguage().toLanguageTag(), 3); this.ttsEngine.speak(text, TextToSpeech.QUEUE_ADD, null); } catch(Exception e) { Miscellaneous.logEvent("e", "TextToSpeech", Log.getStackTraceString(e), 3); - e.printStackTrace(); } } } @@ -726,31 +673,6 @@ public class AutomationService extends Service implements OnInitListener return false; else return true; - -// boolean isActivityFound = false; -// ActivityManager activityManager = (ActivityManager)context.getSystemService (Context.ACTIVITY_SERVICE); -// List activitys = activityManager.getRunningTasks(Integer.MAX_VALUE); -// isActivityFound = false; -// for (int i = 0; i < activitys.size(); i++) -// { -// if (activitys.get(i).topActivity.toString().equalsIgnoreCase("ComponentInfo{com.jens.automation/com.jens.automation.ActivityMainScreen}")) -// { -// isActivityFound = true; -// } -// } -// Miscellaneous.logEvent("i", "ActivityMainScreen", "Activity running status: " + String.valueOf(isActivityFound), 5); -// return isActivityFound; - -// ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); -// List tasks = activityManager.getRunningTasks(Integer.MAX_VALUE); -// -// for (RunningTaskInfo task : tasks) -// { -// if (context.getPackageName().equalsIgnoreCase(task.baseActivity.getPackageName())) -// return true; -// } -// -// return false; } public static boolean isMyServiceRunning(Context context) @@ -762,7 +684,6 @@ public class AutomationService extends Service implements OnInitListener { if(AutomationService.class.getName().equals(service.service.getClassName())) { -// return AutomationService.getInstance() != null && AutomationService.getInstance().isRunning; return true; } } @@ -771,9 +692,8 @@ public class AutomationService extends Service implements OnInitListener { if(Log.getStackTraceString(e).contains("activate")) // Means that a poi has been activated/deactivated. Service is running. return true; -// return AutomationService.getInstance() != null && AutomationService.getInstance().isRunning; } return false; } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/jens/automation2/DeviceAdmin.java b/app/src/main/java/com/jens/automation2/DeviceAdmin.java new file mode 100644 index 0000000..0f1f406 --- /dev/null +++ b/app/src/main/java/com/jens/automation2/DeviceAdmin.java @@ -0,0 +1,22 @@ +package com.jens.automation2; + +import android.app.admin.DeviceAdminReceiver; +import android.content.Context; +import android.content.Intent; + +public class DeviceAdmin extends DeviceAdminReceiver +{ + @Override + public void onEnabled (Context context , Intent intent) + { + super.onEnabled(context , intent) ; + Miscellaneous.logEvent("i", "DeviceAdmin", "Got permission BIND_DEVICE_ADMIN.", 3); + } + + @Override + public void onDisabled (Context context , Intent intent) + { + super.onDisabled(context , intent) ; + Miscellaneous.logEvent("i", "DeviceAdmin", "Permission BIND_DEVICE_ADMIN taken.", 3); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jens/automation2/Miscellaneous.java b/app/src/main/java/com/jens/automation2/Miscellaneous.java index bae4a4c..1a8a18f 100644 --- a/app/src/main/java/com/jens/automation2/Miscellaneous.java +++ b/app/src/main/java/com/jens/automation2/Miscellaneous.java @@ -14,6 +14,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.res.Configuration; import android.database.Cursor; import android.net.ConnectivityManager; import android.net.NetworkInfo; @@ -77,6 +78,8 @@ import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.sql.Time; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; @@ -541,6 +544,22 @@ public class Miscellaneous extends Service return null; } + + public static boolean isDarkModeEnabled(Context context) + { + int mode = context.getResources().getConfiguration().uiMode; + switch(mode) + { + case 33: + case Configuration.UI_MODE_NIGHT_YES: + return true; + case 17: + case Configuration.UI_MODE_NIGHT_NO: + case Configuration.UI_MODE_NIGHT_UNDEFINED: + default: + return false; + } + } @SuppressLint("NewApi") public static String replaceVariablesInText(String source, Context context) throws Exception @@ -1133,11 +1152,11 @@ public class Miscellaneous extends Service } catch (IllegalAccessException e) { - e.printStackTrace(); + Miscellaneous.logEvent("w", "runMethodReflective", Log.getStackTraceString(e),5 ); } catch (InvocationTargetException e) { - e.printStackTrace(); + Miscellaneous.logEvent("w", "runMethodReflective", Log.getStackTraceString(e),5 ); } return result; @@ -1532,4 +1551,21 @@ public class Miscellaneous extends Service else*/ return PhoneNumberUtils.compare(number1, number2); } + + public static String formatDate(Date input) + { + DateFormat sdf = null; + SimpleDateFormat fallBackFormatter = new SimpleDateFormat(Settings.dateFormat); + + if(sdf == null && Settings.dateFormat != null) + sdf = new SimpleDateFormat(Settings.dateFormat); + + String formattedDate; + if(sdf != null) + formattedDate = sdf.format(input); + else + formattedDate = fallBackFormatter.format(input); + + return formattedDate; + } } \ No newline at end of file diff --git a/app/src/main/java/com/jens/automation2/News.java b/app/src/main/java/com/jens/automation2/News.java index 58df458..040b8de 100644 --- a/app/src/main/java/com/jens/automation2/News.java +++ b/app/src/main/java/com/jens/automation2/News.java @@ -23,7 +23,7 @@ import java.util.Map; public class News { Calendar publishDate; - String applicablePlattform; + String applicablePlatform; Map translations = new HashMap<>(); public static class NewsTranslation @@ -151,9 +151,9 @@ public class News String publishDateString = neEl.getElementsByTagName("publishDate").item(0).getTextContent(); newsEntry.setPublishDate(Miscellaneous.calendarFromLong(Long.parseLong(publishDateString) * 1000)); - newsEntry.setApplicablePlattform(neEl.getElementsByTagName("applicablePlattforms").item(0).getTextContent()); + newsEntry.setApplicablePlatform(neEl.getElementsByTagName("applicablePlattforms").item(0).getTextContent()); - if(newsEntry.getApplicablePlattform().equalsIgnoreCase("all") || newsEntry.getApplicablePlattform().equalsIgnoreCase(BuildConfig.FLAVOR)) + if(newsEntry.getApplicablePlatform().equalsIgnoreCase("all") || newsEntry.getApplicablePlatform().equalsIgnoreCase(BuildConfig.FLAVOR)) returnList.add(newsEntry); } } @@ -199,14 +199,14 @@ public class News this.publishDate = publishDate; } - public String getApplicablePlattform() + public String getApplicablePlatform() { - return applicablePlattform; + return applicablePlatform; } - public void setApplicablePlattform(String applicablePlattform) + public void setApplicablePlatform(String applicablePlatform) { - this.applicablePlattform = applicablePlattform; + this.applicablePlatform = applicablePlatform; } @NonNull diff --git a/app/src/main/java/com/jens/automation2/PointOfInterest.java b/app/src/main/java/com/jens/automation2/PointOfInterest.java index 7a42031..eb0a766 100644 --- a/app/src/main/java/com/jens/automation2/PointOfInterest.java +++ b/app/src/main/java/com/jens/automation2/PointOfInterest.java @@ -252,8 +252,9 @@ public class PointOfInterest implements Comparable Settings.writeSettings(parentService); Miscellaneous.logEvent("i", "POI", "Reached POI " + this.getName() + ". Checking if there's a rule that applies to that.", 2); - - ArrayList ruleCandidates = Rule.findRuleCandidatesByPoi(this, true); + + ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.pointOfInterest); +// ArrayList ruleCandidates = Rule.findRuleCandidatesByPoi(this); if(ruleCandidates.size()==0) { Miscellaneous.logEvent("i", "POI", "POI " + this.getName() + " not found in ANY rule.", 2); @@ -264,18 +265,22 @@ public class PointOfInterest implements Comparable for(int i=0; i Settings.writeSettings(parentService); Miscellaneous.logEvent("i", "POI", "Left POI " + this.getName() + ". Checking if there's a rule that applies to that.", 2); - - ArrayList ruleCandidates = Rule.findRuleCandidatesByPoi(this, false); + + ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.pointOfInterest); +// ArrayList ruleCandidates = Rule.findRuleCandidatesByPoi(this); if(ruleCandidates.size()==0) { Miscellaneous.logEvent("i", "POI", "POI " + this.getName() + " not found in ANY rule.", 2); @@ -296,7 +302,7 @@ public class PointOfInterest implements Comparable Miscellaneous.logEvent("i", "POI", "POI " + this.getName() + " found in " + ruleCandidates.size() + " rule(s).", 2); for(int i=0; i dayList = new ArrayList(); - public ArrayList getDayList() + protected final static String separator = "/"; + + private ArrayList dayList = new ArrayList(); + public ArrayList getDayList() + { + return dayList; + } + public void setDayList(ArrayList dayList) + { + this.dayList = dayList; + } + public void setDayListFromString(String dayListString) + { +// Log.i("Parsing", "Full string: " + dayListString); + char[] dayListCharArray = dayListString.toCharArray(); + + dayList = new ArrayList(); + for(char item : dayListCharArray) { - return dayList; - } - public void setDayList(ArrayList dayList) - { - this.dayList = dayList; - } - public void setDayListFromString(String dayListString) - { -// Log.i("Parsing", "Full string: " + dayListString); - char[] dayListCharArray = dayListString.toCharArray(); - - dayList = new ArrayList(); - for(char item : dayListCharArray) - { // Log.i("Parsing", String.valueOf(item)); - dayList.add(Integer.parseInt(String.valueOf(item))); - } + dayList.add(Integer.parseInt(String.valueOf(item))); } + } - - public Time getTriggerTimeStart() - { - return triggerTimeStart; - } - public void setTriggerTimeStart(Time triggerTimeStart) - { - this.triggerTimeStart = triggerTimeStart; - } - public Time getTriggerTimeStop() - { - return triggerTimeStop; - } - public void setTriggerTimeStop(Time triggerTimeStop) - { - this.triggerTimeStop = triggerTimeStop; - } - - public TimeFrame (Time timeStart, Time timeEnd, ArrayList dayList2) - { - this.setTriggerTimeStart(timeStart); - this.setTriggerTimeStop(timeEnd); - this.setDayList(dayList2); - } - TimeFrame (String fileContent) - { - String[] dateArray = fileContent.split("/"); // example: timestart/timestop/days[int] - this.setTriggerTimeStart(Time.valueOf(dateArray[0])); - this.setTriggerTimeStop(Time.valueOf(dateArray[1])); - this.setDayListFromString(dateArray[2]); - } - @Override - public String toString() - { - String returnString = this.getTriggerTimeStart().toString() + "/" + this.getTriggerTimeStop().toString() + "/"; - - for(Integer oneDay : this.getDayList()) - returnString += String.valueOf(oneDay); - - return returnString; - } -} + public Time getTriggerTimeStart() + { + return triggerTimeStart; + } + public void setTriggerTimeStart(Time triggerTimeStart) + { + this.triggerTimeStart = triggerTimeStart; + } + + public Time getTriggerTimeStop() + { + return triggerTimeStop; + } + public void setTriggerTimeStop(Time triggerTimeStop) + { + this.triggerTimeStop = triggerTimeStop; + } + + public long getRepetition() + { + return repetition; + } + + public void setRepetition(long repetition) + { + this.repetition = repetition; + } + + public TimeFrame (Time timeStart, Time timeEnd, ArrayList dayList2, long repetition) + { + this.setTriggerTimeStart(timeStart); + this.setTriggerTimeStop(timeEnd); + this.setDayList(dayList2); + this.setRepetition(repetition); + } + + public TimeFrame (String fileContent) + { + String[] dateArray = fileContent.split(separator); // example: timestart/timestop/days[int]/repetition + this.setTriggerTimeStart(Time.valueOf(dateArray[0])); + this.setTriggerTimeStop(Time.valueOf(dateArray[1])); + this.setDayListFromString(dateArray[2]); + if(dateArray.length > 3) // may not exist in old config files + this.setRepetition(Long.parseLong(dateArray[3])); + } + + @Override + public String toString() + { + String returnString = this.getTriggerTimeStart().toString() + separator + this.getTriggerTimeStop().toString() + separator; + + for(Integer oneDay : this.getDayList()) + returnString += String.valueOf(oneDay); + + returnString += separator + String.valueOf(repetition); + + return returnString; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jens/automation2/Trigger.java b/app/src/main/java/com/jens/automation2/Trigger.java index 04e4198..8b24e1f 100644 --- a/app/src/main/java/com/jens/automation2/Trigger.java +++ b/app/src/main/java/com/jens/automation2/Trigger.java @@ -3,25 +3,990 @@ package com.jens.automation2; import android.bluetooth.BluetoothDevice; import android.content.Context; import android.os.Build; +import android.os.Bundle; +import android.service.notification.StatusBarNotification; +import android.telephony.TelephonyManager; +import android.util.Log; import androidx.annotation.RequiresApi; +import com.jens.automation2.location.LocationProvider; +import com.jens.automation2.location.WifiBroadcastReceiver; +import com.jens.automation2.receivers.BatteryReceiver; import com.jens.automation2.receivers.BluetoothReceiver; +import com.jens.automation2.receivers.ConnectivityReceiver; +import com.jens.automation2.receivers.DeviceOrientationListener; +import com.jens.automation2.receivers.HeadphoneJackListener; +import com.jens.automation2.receivers.NfcReceiver; +import com.jens.automation2.receivers.NoiseListener; +import com.jens.automation2.receivers.NotificationListener; +import com.jens.automation2.receivers.PhoneStatusListener; +import com.jens.automation2.receivers.ProcessListener; +import static com.jens.automation2.receivers.NotificationListener.EXTRA_TEXT; +import static com.jens.automation2.receivers.NotificationListener.EXTRA_TITLE; +import org.apache.commons.lang3.StringUtils; + +import java.sql.Time; import java.util.ArrayList; - +import java.util.Calendar; +import java.util.Date; public class Trigger { - /* + Rule parentRule = null; + Calendar lastTimeNotApplied = null; + + public boolean applies(Object triggeringObject, Context context) + { + boolean result = true; + + try + { + switch(this.getTriggerType()) + { + case timeFrame: + if(!checkDateTime(triggeringObject, false)) + result = false; + break; + case pointOfInterest: + if(!checkLocation()) + result = false; + break; + case charging: + if(!checkCharging()) + result = false; + break; + case usb_host_connection: + if(!checkUsbHostConnection()) + result = false; + break; + case batteryLevel: + if(!checkBatteryLevel()) + result = false; + break; + case speed: + if(!checkSpeed()) + result = false; + break; + case noiseLevel: + if(!checkNoiseLevel()) + result = false; + break; + case wifiConnection: + if(!checkWifiConnection()) + result = false; + break; + case process_started_stopped: + if(!checkProcess()) + result = false; + break; + case airplaneMode: + if(!checkAirplaneMode()) + result = false; + break; + case roaming: + if(!checkRoaming()) + result = false; + break; + case phoneCall: + if(!checkPhoneCall()) + result = false; + break; + case nfcTag: + if(!checkNfc()) + result = false; + break; + case bluetoothConnection: + if(!checkBluetooth()) + result = false; + break; + case headsetPlugged: + if(!checkHeadsetPlugged()) + result = false; + break; + case notification: + if(!checkNotification()) + result = false; + break; + case deviceOrientation: + if(!checkDeviceOrientation()) + result = false; + break; + case activityDetection: + if(!getParentRule().checkActivityDetection(this)) + result = false; + break; + default: + break; + } + } + catch(Exception e) + { + Miscellaneous.logEvent("e", "Trigger", "Error while checking if rule " + getParentRule().getName() + " applies." + Miscellaneous.lineSeparator + Log.getStackTraceString(e), 1); + result = false; + } + + if(!result) + lastTimeNotApplied = Calendar.getInstance(); + + return result; + } + + boolean checkNotification() + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) + { + String[] params = this.getTriggerParameter2().split(triggerParameter2Split); + + String myApp = params[0]; + String myTitleDir = params[1]; + String requiredTitle = params[2]; + String myTextDir = params[3]; + String requiredText; + if (params.length >= 5) + requiredText = params[4]; + else + requiredText = ""; + + if(this.getTriggerParameter()) + { + // Check an active notification that is still there + + boolean foundMatch = false; + + for (StatusBarNotification sbn : NotificationListener.getInstance().getActiveNotifications()) + { + if(getParentRule().getLastExecution() == null || sbn.getPostTime() > this.getParentRule().getLastExecution().getTimeInMillis()) + { + String notificationApp = sbn.getPackageName(); + String notificationTitle = null; + String notificationText = null; + + Miscellaneous.logEvent("i", "NotificationCheck", "Checking if this notification matches our rule " + this.getParentRule().getName() + ". App: " + notificationApp + ", title: " + notificationTitle + ", text: " + notificationText, 5); + + if (!myApp.equals("-1")) + { + if (!notificationApp.equalsIgnoreCase(myApp)) + { + Miscellaneous.logEvent("i", "NotificationCheck", "Notification app name does not match rule.", 5); + continue; + } + } + else + { + if(myApp.equals(BuildConfig.APPLICATION_ID)) + { + return false; + } + } + + /* + If there are multiple notifications ("stacked") title or text might be null: + https://stackoverflow.com/questions/28047767/notificationlistenerservice-not-reading-text-of-stacked-notifications + */ + + Bundle extras = sbn.getNotification().extras; + + // T I T L E + if (extras.containsKey(EXTRA_TITLE)) + notificationTitle = sbn.getNotification().extras.getString(EXTRA_TITLE); + + if (!StringUtils.isEmpty(requiredTitle)) + { + if (!Miscellaneous.compare(myTitleDir, requiredTitle, notificationTitle)) + { + Miscellaneous.logEvent("i", "NotificationCheck", "Notification title does not match rule.", 5); + continue; + } + } + else + Miscellaneous.logEvent("i", "NotificationCheck", "A required title for a notification trigger was not specified.", 5); + + // T E X T + + if (extras.containsKey(EXTRA_TEXT)) + notificationText = sbn.getNotification().extras.getString(EXTRA_TEXT); + + if (!StringUtils.isEmpty(requiredText)) + { + if (!Miscellaneous.compare(myTextDir, requiredText, notificationText)) + { + Miscellaneous.logEvent("i", "NotificationCheck", "Notification text does not match rule.", 5); + continue; + } + } + else + Miscellaneous.logEvent("i", "NotificationCheck", "A required text for a notification trigger was not specified.", 5); + + foundMatch = true; + break; + } + } + + if(!foundMatch) + return false; + } + else + { + // check a notification that is gone + + if(NotificationListener.getLastNotification() != null) + { + if(!NotificationListener.getLastNotification().isCreated()) + { + String app = NotificationListener.getLastNotification().getApp(); + String title = NotificationListener.getLastNotification().getTitle(); + String text = NotificationListener.getLastNotification().getText(); + + if (!myApp.equals("-1")) + { + if (!app.equalsIgnoreCase(myApp)) + return false; + } + else + { + if(myApp.equals(BuildConfig.APPLICATION_ID)) + { + return false; + } + } + + if (requiredTitle.length() > 0) + { + if (!Miscellaneous.compare(myTitleDir, title, requiredTitle)) + return false; + } + + if (requiredText.length() > 0) + { + if (!Miscellaneous.compare(myTextDir, text, requiredText)) + return false; + } + } + else + return false; + } + } + } + + return true; + } + + boolean checkDeviceOrientation() + { + String deviceOrientationPieces[] = getTriggerParameter2().split(Trigger.triggerParameter2Split); + float desiredAzimuth = Float.parseFloat(deviceOrientationPieces[0]); + float desiredAzimuthTolerance = Float.parseFloat(deviceOrientationPieces[1]); + float desiredPitch = Float.parseFloat(deviceOrientationPieces[2]); + float desiredPitchTolerance = Float.parseFloat(deviceOrientationPieces[3]); + float desiredRoll = Float.parseFloat(deviceOrientationPieces[4]); + float desiredRollTolerance = Float.parseFloat(deviceOrientationPieces[5]); + float currentAzimuth = DeviceOrientationListener.getInstance().getAzimuth(); + float currentPitch = DeviceOrientationListener.getInstance().getPitch(); + float currentRoll = DeviceOrientationListener.getInstance().getRoll(); + + if(desiredAzimuthTolerance < 180) + { + if ( + !( + Math.abs(currentAzimuth) <= Math.abs(desiredAzimuth - desiredAzimuthTolerance) + || + Math.abs(currentAzimuth) <= desiredAzimuth + desiredAzimuthTolerance + ) + ) + { + Miscellaneous.logEvent("i", "DeviceOrientation", "Azimuth outside of tolerance area.", 5); + if (getTriggerParameter()) + return false; + else + return true; + } + } + + if(desiredPitchTolerance < 180) + { + if ( + !( + ( + Math.abs(currentPitch) <= Math.abs(desiredPitch - desiredPitchTolerance) + || + Math.abs(currentPitch) <= desiredPitch + desiredPitchTolerance + ) + ) + ) + { + Miscellaneous.logEvent("i", "DeviceOrientation", "Pitch outside of tolerance area.", 5); + if (getTriggerParameter()) + return false; + else + return true; + } + } + + if(desiredRollTolerance < 180) + { + if ( + !( + ( + Math.abs(currentRoll) <= Math.abs(desiredRoll - desiredRollTolerance) + || + Math.abs(currentRoll) <= desiredRoll + desiredRollTolerance + ) + ) + ) + { + Miscellaneous.logEvent("i", "DeviceOrientation", "Roll outside of tolerance area.", 5); + if (getTriggerParameter()) + return false; + else + return true; + } + } + + if(getTriggerParameter()) + return true; + else + return false; + } + + boolean checkHeadsetPlugged() + { + if(HeadphoneJackListener.isHeadsetConnected() != this.getTriggerParameter()) + return false; + else + if(this.getHeadphoneType() != 2 && this.getHeadphoneType() != HeadphoneJackListener.getHeadphoneType()) + { + Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyWrongHeadphoneType), this.getParentRule().getName()), 3); + return false; + } + + return true; + } + + boolean checkBluetooth() + { + Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format("Checking for bluetooth...", this.getParentRule().getName()), 4); + + if(this.getBluetoothDeviceAddress().equals("")) + { + if(this.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) + { + if(BluetoothReceiver.isAnyDeviceConnected() != this.getTriggerParameter()) + return false; + } + else if((this.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) + { + if(BluetoothReceiver.isAnyDeviceConnected() != this.getTriggerParameter()) + return false; + } + else + { + // range + if(BluetoothReceiver.isAnyDeviceInRange() != this.getTriggerParameter()) + return false; + } + } + else if(this.getBluetoothDeviceAddress().equals("")) + { + if(this.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) + { + if(BluetoothReceiver.isAnyDeviceConnected() == this.getTriggerParameter()) + return false; + } + else if((this.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) + { + if(BluetoothReceiver.isAnyDeviceConnected() == this.getTriggerParameter()) + return false; + } + else + { + // range + if(BluetoothReceiver.isAnyDeviceInRange() == this.getTriggerParameter()) + return false; + } + } + else if(this.getBluetoothDeviceAddress().length() > 0) + { + if(this.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) + { + if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(this.getBluetoothDeviceAddress())) != this.getTriggerParameter()) + return false; + } + else if((this.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) + { + if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(this.getBluetoothDeviceAddress())) != this.getTriggerParameter()) + return false; + } + else + { + // range + if(BluetoothReceiver.isDeviceInRange(BluetoothReceiver.getDeviceByAddress(this.getBluetoothDeviceAddress())) != this.getTriggerParameter()) + return false; + } + } + else + { + Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyStateNotCorrect), this.getParentRule().getName()), 3); + return false; + } + + return true; + } + + boolean checkNfc() + { + if(NfcReceiver.lastReadLabel == null) + { + Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyNoTagLabel), this.getParentRule().getName()),3); + return false; + } + else if(!NfcReceiver.lastReadLabel.equals(this.getNfcTagId())) + { + Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyWrongTagLabel) + " " + NfcReceiver.lastReadLabel + " / " + this.getNfcTagId(), this.getParentRule().getName()), 3); + return false; + } + + return true; + } + + boolean checkPhoneCall() + { + String[] elements = this.getTriggerParameter2().split(triggerParameter2Split); + // state dir number + + if(elements[2].equals(Trigger.triggerPhoneCallNumberAny) || Miscellaneous.comparePhoneNumbers(PhoneStatusListener.getLastPhoneNumber(), elements[2]) || (Miscellaneous.isRegularExpression(elements[2]) && PhoneStatusListener.getLastPhoneNumber().matches(elements[2]))) + { + //if(PhoneStatusListener.isInACall() == oneTrigger.getTriggerParameter()) + if( + (elements[0].equals(Trigger.triggerPhoneCallStateRinging) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_RINGING) + || + (elements[0].equals(Trigger.triggerPhoneCallStateStarted) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_OFFHOOK) + || + (elements[0].equals(Trigger.triggerPhoneCallStateStopped) && PhoneStatusListener.getCurrentState() == TelephonyManager.CALL_STATE_IDLE) + ) + { + if( + elements[1].equals(Trigger.triggerPhoneCallDirectionAny) + || + (elements[1].equals(Trigger.triggerPhoneCallDirectionIncoming) && PhoneStatusListener.getLastPhoneDirection() == 1) + || + (elements[1].equals(Trigger.triggerPhoneCallDirectionOutgoing) && PhoneStatusListener.getLastPhoneDirection() == 2) + ) + { + // Trigger conditions are met + } + else + { + Miscellaneous.logEvent("i", "Rule", "A trigger of rule " + getParentRule().getName() + " doesn't apply. Wrong direction. Demanded: " + String.valueOf(this.getPhoneDirection()) + ", got: " + String.valueOf(PhoneStatusListener.getLastPhoneDirection()), 4); + return false; + } + } + else + { + Miscellaneous.logEvent("i", "Rule", "A trigger of rule " + getParentRule().getName() + " doesn't apply. Wrong call status. Demanded: " + elements[0] + ", got: " + String.valueOf(PhoneStatusListener.getCurrentState()) + " (0=idle, 1=ringing, 2=offhook)", 4); + return false; + } + } + else + { + Miscellaneous.logEvent("i", "Rule", "A trigger of rule " + getParentRule().getName() + " doesn't apply. Wrong phone number. Demanded: " + this.getPhoneNumber() + ", got: " + PhoneStatusListener.getLastPhoneNumber(), 4); + return false; + } + + return true; + } + + boolean checkRoaming() + { + if(ConnectivityReceiver.isRoaming(Miscellaneous.getAnyContext()) != this.getTriggerParameter()) + { + return false; + } + + return true; + } + + boolean checkAirplaneMode() + { + if(ConnectivityReceiver.isAirplaneMode(Miscellaneous.getAnyContext()) != this.getTriggerParameter()) + { + return false; + } + + return true; + } + + boolean checkProcess() + { + boolean running = ProcessListener.getRunningApps().contains(this.getProcessName()); + + if(running) + Miscellaneous.logEvent("i", "ProcessMonitoring", "App " + this.getProcessName() + " is currently running.", 4); + else + Miscellaneous.logEvent("i", "ProcessMonitoring", "App " + this.getProcessName() + " is not running.", 4); + + if(running != this.getTriggerParameter()) + { + Miscellaneous.logEvent("i", "ProcessMonitoring", "Trigger doesn't apply.", 4); + return false; + } + + Miscellaneous.logEvent("i", "ProcessMonitoring", "Trigger applies.", 4); + + return true; + } + + boolean checkWifiConnection() + { + Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format( "Checking for wifi state", this.getParentRule().getName()),4); + if(this.getTriggerParameter() == WifiBroadcastReceiver.lastConnectedState) // connected / disconnected + { + if(this.getTriggerParameter2().length() > 0) // only check if any wifi name specified, otherwise any wifi will do + { + Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format("Wifi name specified, checking that.", this.getParentRule().getName()), 4); + if(!WifiBroadcastReceiver.getLastWifiSsid().equals(this.getTriggerParameter2())) + { + Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyNotTheCorrectSsid), getParentRule().getName(), this.getTriggerParameter2(), WifiBroadcastReceiver.getLastWifiSsid()),this.getParentRule().getName()), 3); + return false; + } + else + Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format("Wifi name matches. Rule will apply.", this.getParentRule().getName()), 4); + } + else + Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format("No wifi name specified, any will do.", this.getParentRule().getName()), 4); + } + else + { + Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format("Wifi state not correct, demanded " + String.valueOf(this.getTriggerParameter() + ", got " + String.valueOf(WifiBroadcastReceiver.lastConnectedState)), this.getParentRule().getName()), 4); + return false; + } + + return true; + } + + boolean checkNoiseLevel() + { + if(this.getTriggerParameter()) + { + if(NoiseListener.getNoiseLevelDb() < this.getNoiseLevelDb()) + { + Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyItsQuieterThan) + " " + String.valueOf(this.getNoiseLevelDb()), this.getParentRule().getName()), 3); + return false; + } + } + else + { + if(NoiseListener.getNoiseLevelDb() > this.getNoiseLevelDb()) + { + Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyItsLouderThan) + " " + String.valueOf(this.getNoiseLevelDb()), this.getParentRule().getName()), 3); + return false; + } + } + + return true; + } + + boolean checkSpeed() + { + if(this.getTriggerParameter()) + { + if(LocationProvider.getSpeed() < this.getSpeed()) + { + Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyWeAreSlowerThan) + " " + String.valueOf(this.getSpeed()), this.getParentRule().getName()), 3); + return false; + } + } + else + { + if(LocationProvider.getSpeed() > this.getSpeed()) + { + Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyWeAreFasterThan) + " " + String.valueOf(this.getSpeed()), this.getParentRule().getName()), 3); + return false; + } + } + + return true; + } + + boolean checkBatteryLevel() + { + if(this.getTriggerParameter()) + { + if(BatteryReceiver.getBatteryLevel() <= this.getBatteryLevel()) + { + Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyBatteryLowerThan) + " " + String.valueOf(this.getBatteryLevel()), this.getParentRule().getName()), 3); + return false; + } + } + else + { + if(this.getBatteryLevel() >= this.getBatteryLevel()) + { + Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleDoesntApplyBatteryHigherThan) + " " + String.valueOf(this.getBatteryLevel()), this.getParentRule().getName()), 3); + return false; + } + } + + return true; + } + + boolean checkUsbHostConnection() + { + if(BatteryReceiver.isUsbHostConnected() != this.getTriggerParameter()) + { + return false; + } + + return true; + } + + boolean checkLocation() + { + // Am I here? + PointOfInterest activePoi = PointOfInterest.getActivePoi(); + if(activePoi != null) //entering one + { + if(this.getPointOfInterest() != null) + { + if(activePoi.equals(this.getPointOfInterest())) + { + if(!this.getTriggerParameter()) + { + Miscellaneous.logEvent("i", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), this.getParentRule().getName()), String.format("Rule %1$s doesn't apply. We're entering POI: " + this.getPointOfInterest().getName() + ", not leaving it.", getParentRule().getName()), 4); + return false; + } + } + else + { + Miscellaneous.logEvent("i", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), this.getParentRule().getName()), String.format("Rule %1$s doesn't apply. This is " + activePoi.getName() + ", not " + this.getPointOfInterest().getName() + ".", getParentRule().getName()), 4); + return false; + } + } + else if(this.getPointOfInterest() == null) + { + if(this.getTriggerParameter()) + { + Miscellaneous.logEvent("i", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), this.getParentRule().getName()), String.format("Rule %s$s doesn't apply. We're at a POI. Rule specifies not at none, so leaving any.",getParentRule().getName()), 4); + return false; + } + } + } + else //leaving one + { + // We are not at any POI. But if this trigger requires us NOT to be there, that may be fine. + if(this.getPointOfInterest() != null) + { +// if(activePoi.equals(oneTrigger.getPointOfInterest())) +// { + if(!this.getTriggerParameter()) + { + Miscellaneous.logEvent("i", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), this.getParentRule().getName()), "We are not at POI \"" + this.getPointOfInterest().getName() + "\". But since that's required by this rule that's fine.", 4); + } + else + { + Miscellaneous.logEvent("i", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), this.getParentRule().getName()), String.format("Rule %1$s doesn't apply. We're not at POI \"" + this.getPointOfInterest().getName() + "\".", getParentRule().getName()), 3); + return false; + } +// } + } + else if(this.getPointOfInterest() == null) + { + if(!this.getTriggerParameter()) + { + Miscellaneous.logEvent("i", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), this.getParentRule().getName()), String.format("Rule %1$s doesn't apply. We're at no POI. Rule specifies to be at anyone.", getParentRule().getName()), 5); + return false; + } + } + } + + return true; + } + + public boolean hasStateNotAppliedSinceLastRuleExecution() + { + if(getParentRule().getLastExecution() == null) + { + Miscellaneous.logEvent("i", "Trigger", "Trigger " + this.toString() + " of rule " + getParentRule().getName() + " has NOT applied since the parent rule\'s last activation.", 4); + return true; + } + else if(lastTimeNotApplied != null) + { + if(lastTimeNotApplied.getTimeInMillis() > getParentRule().getLastExecution().getTimeInMillis()) + { + Miscellaneous.logEvent("i", "Trigger", "Trigger " + this.toString() + " of rule " + getParentRule().getName() + " has NOT applied since the parent rule\'s last activation.", 4); + return true; + } + } + + Miscellaneous.logEvent("i", "Trigger", "Trigger " + this.toString() + " of rule " + getParentRule().getName() + " may apply currently, but has not NOT applied since the rule\'s last execution.", 4); + return false; + } + + boolean checkCharging() + { + if(BatteryReceiver.isDeviceCharging(Miscellaneous.getAnyContext()) == 0) + { + return false; // unknown charging state, can't activate rule under these conditions + } + else if(BatteryReceiver.isDeviceCharging(Miscellaneous.getAnyContext()) == 1) + { + if(this.getTriggerParameter()) //rule says when charging, but we're currently discharging + return false; + } + else if(BatteryReceiver.isDeviceCharging(Miscellaneous.getAnyContext()) == 2) + { + if(!this.getTriggerParameter()) //rule says when discharging, but we're currently charging + return false; + } + + return true; + } + + public boolean checkDateTime(Object triggeringObject, boolean checkifStateChangedSinceLastRuleExecution) + { + /* + * Use format known from Automation + * 07:30:00/17:30:00/23456/300 <-- last parameter is optional: repetition in seconds + * Also required: inside or outside that interval + */ + + Date triggeringTime; + if(triggeringObject instanceof Date) + triggeringTime = (Date)triggeringObject; + else + triggeringTime = new Date(); + + String timeString = String.valueOf(triggeringTime.getHours()) + ":" + String.valueOf(triggeringTime.getMinutes()) + ":" + String.valueOf(triggeringTime.getSeconds()); + Time nowTime = Time.valueOf(timeString); + Calendar calNow = Calendar.getInstance(); + + try + { + TimeFrame tf = new TimeFrame(getTriggerParameter2()); + + if(tf.getDayList().contains(calNow.get(Calendar.DAY_OF_WEEK))) + { + if( + // Regular case, start time is lower than end time + ( + Miscellaneous.compareTimes(tf.getTriggerTimeStart(), nowTime) >= 0 + && + Miscellaneous.compareTimes(nowTime, tf.getTriggerTimeStop()) > 0 + ) + | + // Other case, start time higher than end time, timeframe goes over midnight + ( + Miscellaneous.compareTimes(tf.getTriggerTimeStart(), tf.getTriggerTimeStop()) < 0 + && + (Miscellaneous.compareTimes(tf.getTriggerTimeStart(), nowTime) >= 0 + | + Miscellaneous.compareTimes(nowTime, tf.getTriggerTimeStop()) > 0) + ) + + ) + { + // We are in the timeframe + Miscellaneous.logEvent("i", "Trigger", "TimeFrame: We're currently (" + calNow.getTime().toString() + ") in the specified TimeFrame (" + tf.toString() + ").", 4); + if(getTriggerParameter()) + { + if(checkifStateChangedSinceLastRuleExecution) + { + /* + * Was there a target repetition time between last execution and now? + * If not -> return false. + */ + Calendar compareCal = Calendar.getInstance(); + compareCal.setTimeInMillis(triggeringTime.getTime()); + if(tf.getRepetition() > 0) + { + if(!isSupposedToRepeatSinceLastExecution(compareCal)) + { + Miscellaneous.logEvent("i", "TimeFrame", "TimeFrame: Trigger of rule " + this.getParentRule().getName() + " applies, but repeated execution is not due, yet.", 4); + return false; + } + } + else + { + /* + * This is not a repeating rule. Have we left + * the relevant timeframe since the last run? + * Determine if it has ran today already. If yes + * return false because every rule that is not + * repeating can only be executed once per day. + */ + + if( + getParentRule().getLastExecution().get(Calendar.YEAR) == calNow.get(Calendar.YEAR) + && + getParentRule().getLastExecution().get(Calendar.MONTH) == calNow.get(Calendar.MONTH) + && + getParentRule().getLastExecution().get(Calendar.DAY_OF_MONTH) == calNow.get(Calendar.DAY_OF_MONTH) + ) + { + Miscellaneous.logEvent("i", "TimeFrame", "TimeFrame: Trigger of rule " + this.getParentRule().getName() + " applies, but it was already executed today.", 4); + return false; + } + } + } + + Miscellaneous.logEvent("i", "Trigger", "TimeFrame: That's what's specified. Trigger of rule " + this.getParentRule().getName() + " applies.", 4); + return true; + } + else + { + Miscellaneous.logEvent("i", "Trigger", "TimeFrame: That's not what's specified. Trigger of rule " + this.getParentRule().getName() + " doesn't apply.", 4); + return false; + } + } + else + { + Miscellaneous.logEvent("i", "Trigger", "TimeFrame: We're currently (" + calNow.getTime().toString() + ", Day: " + String.valueOf(calNow.get(Calendar.DAY_OF_WEEK)) + ") not in the specified TimeFrame (" + tf.toString() + ") because of the time.", 5); + if(!getTriggerParameter()) + { + if(checkifStateChangedSinceLastRuleExecution) + { + /* + * Was there a target repetition time between last execution and now? + * If not -> return false. + */ + Calendar compareCal = Calendar.getInstance(); + compareCal.setTimeInMillis(triggeringTime.getTime()); + if(tf.getRepetition() > 0) + { + if(!isSupposedToRepeatSinceLastExecution(compareCal)) + { + Miscellaneous.logEvent("i", "Trigger", "TimeFrame: Trigger of rule " + this.getParentRule().getName() + " applies, but repeated execution is not due, yet.", 4); + return false; + } + } + else + { + /* + * This is not a repeating rule. Have we left + * the relevant timeframe since the last run? + * Determine if it has ran today already. If yes + * return false because every rule that is not + * repeating can only be executed once per day. + */ + + if( + getParentRule().getLastExecution().get(Calendar.YEAR) == calNow.get(Calendar.YEAR) + && + getParentRule().getLastExecution().get(Calendar.MONTH) == calNow.get(Calendar.MONTH) + && + getParentRule().getLastExecution().get(Calendar.DAY_OF_MONTH) == calNow.get(Calendar.DAY_OF_MONTH) + ) + { + Miscellaneous.logEvent("i", "Trigger", "TimeFrame: Trigger of rule " + this.getParentRule().getName() + " applies, but it was already executed today.", 4); + return false; + } + } + } + Miscellaneous.logEvent("i", "Trigger", "TimeFrame: That's what's specified. Trigger of rule " + this.getParentRule().getName() + " applies.", 5); + return true; + } + else + { + Miscellaneous.logEvent("i", "Trigger", "TimeFrame: That's not what's specified. Trigger of rule " + this.getParentRule().getName() + " doesn't apply.", 5); + return false; + } + } + } + else + { + Miscellaneous.logEvent("i", "Trigger", "TimeFrame: We're currently (" + calNow.getTime().toString() + ", Day: " + String.valueOf(calNow.get(Calendar.DAY_OF_WEEK)) + ") not in the specified TimeFrame (" + tf.toString() + ") because of the day.", 5); + return false; + } + } + catch(Exception e) + { + Miscellaneous.logEvent("e", "Trigger", "There was an error while checking if the time based trigger applies: " + Log.getStackTraceString(e), 1); + return false; + } + } + + public static Calendar getNextRepeatedExecutionAfter(Trigger trigger, Calendar now) + { + Calendar calSet; + Time setTime; + TimeFrame tf = new TimeFrame(trigger.getTriggerParameter2()); + + if(tf.getRepetition() > 0) + { + if(trigger.getTriggerParameter()) + setTime = tf.getTriggerTimeStart(); + else + setTime = tf.getTriggerTimeStop(); + + calSet = (Calendar) now.clone(); + calSet.set(Calendar.HOUR_OF_DAY, setTime.getHours()); + calSet.set(Calendar.MINUTE, setTime.getMinutes()); + calSet.set(Calendar.SECOND, 0); + calSet.set(Calendar.MILLISECOND, 0); + +// if(this.applies(null)) +// { + // If the starting time is a day ahead remove 1 day. + if(calSet.getTimeInMillis() > now.getTimeInMillis()) + calSet.add(Calendar.DAY_OF_MONTH, -1); + + long differenceInSeconds = Math.abs(now.getTimeInMillis() - calSet.getTimeInMillis()) / 1000; + long nextExecutionMultiplier = Math.floorDiv(differenceInSeconds, tf.getRepetition()) + 1; + long nextScheduleTimestamp = (calSet.getTimeInMillis() / 1000) + (nextExecutionMultiplier * tf.getRepetition()); + Calendar calSchedule = Calendar.getInstance(); + calSchedule.setTimeInMillis(nextScheduleTimestamp * 1000); + + /* + * Das war mal aktiviert. Allerdings: Die ganze Funktion liefert zurück, wenn die Regel NOCH nicht + * zutrifft, aber wir z.B. gleich den zeitlichen Bereich betreten. + */ +// if(trigger.checkDateTime(calSchedule.getTime(), false)) +// { + return calSchedule; +// } +// } + } + else + Miscellaneous.logEvent("i", "Trigger", "Trigger " + trigger.toString() + " is not executed repeatedly.", 5); + + return null; + } + + boolean isSupposedToRepeatSinceLastExecution(Calendar now) + { + TimeFrame tf = new TimeFrame(getTriggerParameter2()); + Calendar lastExec = getParentRule().getLastExecution(); + + // the simple stuff: + + if(lastExec == null) // rule never run, go any way + return true; + else if(tf.getRepetition() <= 0) // is not set to repeat at all + return false; + + /* + * We don't need to check if the trigger currently applies, that has + * been done externally via the applies() function. We can safely assume + * we're inside the specified timeframe. + */ + + Calendar timeSupposedToRunNext = getNextRepeatedExecutionAfter(this, lastExec); + if(now.getTimeInMillis() > timeSupposedToRunNext.getTimeInMillis()) + return true; + + return false; + } + + /* * Can be several things: * -PointOfInterest * -TimeFrame * -Event (like charging, cable plugged, etc.) */ - public enum Trigger_Enum { - pointOfInterest, timeFrame, charging, batteryLevel, usb_host_connection, speed, noiseLevel, wifiConnection, process_started_stopped, airplaneMode, roaming, nfcTag, activityDetection, bluetoothConnection, headsetPlugged, notification, phoneCall; //phoneCall always needs to be at the very end because of Google's shitty so called privacy + public enum Trigger_Enum { + pointOfInterest, timeFrame, charging, batteryLevel, usb_host_connection, speed, noiseLevel, wifiConnection, process_started_stopped, airplaneMode, roaming, nfcTag, activityDetection, bluetoothConnection, headsetPlugged, notification, deviceOrientation, phoneCall; //phoneCall always needs to be at the very end because of Google's shitty so called privacy public String getFullName(Context context) { @@ -61,6 +1026,8 @@ public class Trigger return context.getResources().getString(R.string.triggerHeadsetPlugged); case notification: return context.getResources().getString(R.string.notification); + case deviceOrientation: + return context.getResources().getString(R.string.deviceOrientation); default: return "Unknown"; } @@ -294,7 +1261,11 @@ public class Trigger else returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.leaving) + " "); - returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.triggerTimeFrame) + ": " + this.getTimeFrame().getTriggerTimeStart().toString() + " " + Miscellaneous.getAnyContext().getResources().getString(R.string.until) + " " + this.getTimeFrame().getTriggerTimeStop().toString() + " on days " + this.getTimeFrame().getDayList().toString()); + String repeat = ", no repetition"; + if(this.getTimeFrame().getRepetition() > 0) + repeat = ", " + String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.repeatEveryXsecondsWithVariable), String.valueOf(this.getTimeFrame().getRepetition())); + + returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.triggerTimeFrame) + ": " + this.getTimeFrame().getTriggerTimeStart().toString() + " " + Miscellaneous.getAnyContext().getResources().getString(R.string.until) + " " + this.getTimeFrame().getTriggerTimeStop().toString() + " on days " + this.getTimeFrame().getDayList().toString() + repeat); break; case speed: if(getTriggerParameter()) @@ -428,40 +1399,41 @@ public class Trigger break; case bluetoothConnection: String device = Miscellaneous.getAnyContext().getResources().getString(R.string.anyDevice); -// if(this.bluetoothDeviceAddress != null) -// { - if(bluetoothDeviceAddress.equals("")) + if(bluetoothDeviceAddress.equals("")) + { + device = Miscellaneous.getAnyContext().getResources().getString(R.string.any); + } + else if(bluetoothDeviceAddress.equals("")) + { + device = Miscellaneous.getAnyContext().getResources().getString(R.string.noDevice); + } + else + { + try { - device = Miscellaneous.getAnyContext().getResources().getString(R.string.any); + device = BluetoothReceiver.getDeviceByAddress(bluetoothDeviceAddress).getName() + " (" + this.bluetoothDeviceAddress + ")"; } - else if(bluetoothDeviceAddress.equals("")) + catch(NullPointerException e) { - device = Miscellaneous.getAnyContext().getResources().getString(R.string.noDevice); + device = Miscellaneous.getAnyContext().getResources().getString(R.string.invalidDevice) + ": " + this.bluetoothDeviceAddress; + Miscellaneous.logEvent("w", "Trigger", device, 3); } + } + + if(bluetoothEvent.equals(BluetoothDevice.ACTION_ACL_CONNECTED) || bluetoothEvent.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) + { + if (this.triggerParameter) + returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.bluetoothConnectionTo), device)); else - { - try - { - device = BluetoothReceiver.getDeviceByAddress(bluetoothDeviceAddress).getName() + " (" + this.bluetoothDeviceAddress + ")"; - } - catch(NullPointerException e) - { - device = Miscellaneous.getAnyContext().getResources().getString(R.string.invalidDevice); - Miscellaneous.logEvent("w", "Trigger", Miscellaneous.getAnyContext().getResources().getString(R.string.invalidDevice), 3); - } - } - - if(bluetoothEvent.equals(BluetoothDevice.ACTION_ACL_CONNECTED) | bluetoothEvent.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) - if(this.triggerParameter) - returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.bluetoothConnectionTo), device)); - else - returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.bluetoothDisconnectFrom), device)); - else if(bluetoothEvent.equals(BluetoothDevice.ACTION_FOUND)) - if(this.triggerParameter) - returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.bluetoothDeviceInRange), device)); - else - returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.bluetoothDeviceOutOfRange), device)); -// } + returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.bluetoothDisconnectFrom), device)); + } + else if(bluetoothEvent.equals(BluetoothDevice.ACTION_FOUND)) + { + if (this.triggerParameter) + returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.bluetoothDeviceInRange), device)); + else + returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.bluetoothDeviceOutOfRange), device)); + } break; case headsetPlugged: String type; @@ -525,6 +1497,9 @@ public class Trigger setTriggerParameter2("-1" + triggerParameter2Split + directionEquals + triggerParameter2Split + triggerParameter2Split + directionEquals + triggerParameter2Split + triggerParameter2Split); } break; + case deviceOrientation: + returnString.append(Miscellaneous.getAnyContext().getString(R.string.deviceIsInCertainOrientation)); + break; default: returnString.append("error"); break; @@ -611,5 +1586,14 @@ public class Trigger { return this.bluetoothEvent; } - + + public Rule getParentRule() + { + return parentRule; + } + + public void setParentRule(Rule parentRule) + { + this.parentRule = parentRule; + } } \ No newline at end of file diff --git a/app/src/main/java/com/jens/automation2/XmlFileInterface.java b/app/src/main/java/com/jens/automation2/XmlFileInterface.java index 2b170b7..8adfc2e 100644 --- a/app/src/main/java/com/jens/automation2/XmlFileInterface.java +++ b/app/src/main/java/com/jens/automation2/XmlFileInterface.java @@ -764,6 +764,8 @@ public class XmlFileInterface try { newRule.setTriggerSet(readTriggerCollection(parser)); + for(Trigger t : newRule.getTriggerSet()) + t.setParentRule(newRule); } catch (XmlPullParserException e) { @@ -779,6 +781,8 @@ public class XmlFileInterface try { newRule.setActionSet(readActionCollection(parser)); + for(Action a : newRule.getActionSet()) + a.setParentRule(newRule); } catch (XmlPullParserException e) { @@ -815,7 +819,15 @@ public class XmlFileInterface // Starts by looking for the entry tag if (name.equals("Trigger")) { - triggerCollection.add(readTrigger(parser)); + try + { + triggerCollection.add(readTrigger(parser)); + } + catch (IllegalArgumentException | NullPointerException e) + { + Miscellaneous.logEvent("e", "XMLFileInterface", "Unknown trigger found in config file. File was probably created by a newer program version. Details: " + Log.getStackTraceString(e), 1); + Miscellaneous.messageBox(context.getString(R.string.error), context.getString(R.string.elementSkipped), context).show(); + } } else { @@ -826,7 +838,6 @@ public class XmlFileInterface return (triggerCollection); } - private static Trigger readTrigger(XmlPullParser parser) throws IOException, XmlPullParserException { @@ -878,7 +889,7 @@ public class XmlFileInterface { String triggerEventString = readTag(parser, "TriggerEvent"); - if(triggerEventString.equals("process_started_stopped") | triggerEventString.equals("process_running")) + if(triggerEventString.equals("process_started_stopped") || triggerEventString.equals("process_running")) newTrigger.setTriggerType(Trigger_Enum.process_started_stopped); else newTrigger.setTriggerType(Trigger_Enum.valueOf(triggerEventString)); @@ -928,7 +939,6 @@ public class XmlFileInterface } else if(newTrigger.getTriggerType() == Trigger_Enum.wifiConnection) { -// newTrigger.setWifiName(triggerParameter2); newTrigger.setTriggerParameter2(triggerParameter2); } else if(newTrigger.getTriggerType() == Trigger_Enum.process_started_stopped) @@ -1051,7 +1061,15 @@ public class XmlFileInterface // Starts by looking for the entry tag if (name.equals("Action")) { - actionCollection.add(readAction(parser)); + try + { + actionCollection.add(readAction(parser)); + } + catch (IllegalArgumentException | NullPointerException e) + { + Miscellaneous.logEvent("e", "XMLFileInterface", "Unknown action found in config file. File was probably created by a newer program version. Details: " + Log.getStackTraceString(e), 1); + Miscellaneous.messageBox(context.getString(R.string.error), context.getString(R.string.elementSkipped), context).show(); + } } else { @@ -1060,7 +1078,6 @@ public class XmlFileInterface } return (actionCollection); } - private static Action readAction(XmlPullParser parser) throws IOException, XmlPullParserException { @@ -1135,7 +1152,14 @@ public class XmlFileInterface newAction.setAction(Action_Enum.enableScreenRotation); else if(actionNameString.equals("disableScreenRotation")) newAction.setAction(Action_Enum.disableScreenRotation); - // *** deprecated + else if(actionNameString.equals("disableScreenRotation")) + newAction.setAction(Action_Enum.disableScreenRotation); + else if(actionNameString.equals("wakeupDevice")) + { + newAction.setAction(Action_Enum.turnScreenOnOrOff); + newAction.setParameter1(true); + } + // *** deprecated else newAction.setAction(Action_Enum.valueOf(actionNameString)); @@ -1203,6 +1227,18 @@ public class XmlFileInterface newAction.setParameter1(false); readTag(parser, "ActionParameter1"); //read the tag for the parser to head on } + else if(newAction.getAction().equals(Action_Enum.disableScreenRotation)) + { + newAction.setAction(Action_Enum.setDisplayRotation); + newAction.setParameter1(false); + readTag(parser, "ActionParameter1"); //read the tag for the parser to head on + } + else if(newAction.getAction().equals(Action_Enum.turnScreenOnOrOff) && newAction.getParameter1()) + { + /* + If param1 == true we will keep it because this action used to be of type wakeUpDevice. + */ + } else // exclusion for deprecated types newAction.setParameter1(Boolean.parseBoolean(readTag(parser, "ActionParameter1"))); @@ -1224,7 +1260,6 @@ public class XmlFileInterface { newAction.setParameter2(tag); } - /* androidx.security.crypto.MasterKey.Builder @@ -1283,9 +1318,6 @@ public class XmlFileInterface } } } - -// Miscellaneous.logEvent("i", "New Rule from file", newPoi.name + "/" + String.valueOf(newPoi.radius) + "/" + String.valueOf(newPoi.location.getLatitude()) + "/" + String.valueOf(newPoi.location.getLongitude()) + "/" + String.valueOf(newPoi.changeWifiState) + "/" + String.valueOf(newPoi.desiredWifiState) + "/" + String.valueOf(newPoi.changeCameraState) + "/" + String.valueOf(newPoi.desiredCameraState) + "/" + String.valueOf(newPoi.changeSoundSetting) + "/" + String.valueOf(newPoi.desiredSoundSetting)); - return newAction; } diff --git a/app/src/main/java/com/jens/automation2/location/CellLocationChangedReceiver.java b/app/src/main/java/com/jens/automation2/location/CellLocationChangedReceiver.java index a738d67..e3b7015 100644 --- a/app/src/main/java/com/jens/automation2/location/CellLocationChangedReceiver.java +++ b/app/src/main/java/com/jens/automation2/location/CellLocationChangedReceiver.java @@ -369,7 +369,7 @@ public class CellLocationChangedReceiver extends PhoneStateListener // Miscellaneous.logEvent("i", "cellReceiver", "Not starting cellLocationListener because we have no data connection.", 4); } else - Miscellaneous.logEvent("w", "cellReceiver", "Wanted to activate CellLocationChangedReceiver, but Wifi-Receiver says not to.", 4); + Miscellaneous.logEvent("w", "cellReceiver", "Wanted to activate CellLocationChangedReceiver, but Wifi-Receiver says not to.", 4); } else Miscellaneous.logEvent("i", "cellReceiver", "Not starting cellLocationListener because Airplane mode is active or SIM_STATE is not ready.", 4); diff --git a/app/src/main/java/com/jens/automation2/location/LocationProvider.java b/app/src/main/java/com/jens/automation2/location/LocationProvider.java index 8a2956e..fcab166 100644 --- a/app/src/main/java/com/jens/automation2/location/LocationProvider.java +++ b/app/src/main/java/com/jens/automation2/location/LocationProvider.java @@ -121,9 +121,9 @@ public class LocationProvider if ( locationList.size() >= 1 && - locationList.get(locationList.size() - 1).getTime() == newLocation.getTime() + locationList.get(locationList.size() - 1).getTime() == newLocation.getTime() && - locationList.get(locationList.size() - 1).getProvider().equals(newLocation.getProvider()) + locationList.get(locationList.size() - 1).getProvider().equals(newLocation.getProvider()) ) { // This is a duplicate update, do not store it @@ -198,10 +198,10 @@ public class LocationProvider setSpeed(currentSpeed); // execute matching rules containing speed - ArrayList ruleCandidates = Rule.findRuleCandidatesBySpeed(); + ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.speed); for (Rule oneRule : ruleCandidates) { - if (oneRule.applies(this.getParentService())) + if(oneRule.getsGreenLight(this.getParentService())) oneRule.activate(getParentService(), false); } } diff --git a/app/src/main/java/com/jens/automation2/location/WifiBroadcastReceiver.java b/app/src/main/java/com/jens/automation2/location/WifiBroadcastReceiver.java index c2b2ea5..3d426a9 100644 --- a/app/src/main/java/com/jens/automation2/location/WifiBroadcastReceiver.java +++ b/app/src/main/java/com/jens/automation2/location/WifiBroadcastReceiver.java @@ -16,6 +16,7 @@ import com.jens.automation2.PointOfInterest; import com.jens.automation2.R; import com.jens.automation2.Rule; import com.jens.automation2.Settings; +import com.jens.automation2.Trigger; import java.util.ArrayList; @@ -144,10 +145,10 @@ public class WifiBroadcastReceiver extends BroadcastReceiver public static void findRules(AutomationService automationServiceInstance) { - ArrayList ruleCandidates = Rule.findRuleCandidatesByWifiConnection(); + ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger.Trigger_Enum.wifiConnection); for(Rule oneRule : ruleCandidates) { - if(oneRule.applies(automationServiceInstance)) + if(oneRule.getsGreenLight(automationServiceInstance)) oneRule.activate(automationServiceInstance, false); } } diff --git a/app/src/main/java/com/jens/automation2/receivers/AlarmListener.java b/app/src/main/java/com/jens/automation2/receivers/AlarmListener.java deleted file mode 100644 index 4c7bf10..0000000 --- a/app/src/main/java/com/jens/automation2/receivers/AlarmListener.java +++ /dev/null @@ -1,301 +0,0 @@ -package com.jens.automation2.receivers; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -import com.jens.automation2.AutomationService; -import com.jens.automation2.Miscellaneous; -import com.jens.automation2.Rule; -import com.jens.automation2.Trigger; -import com.jens.automation2.Trigger.Trigger_Enum; - -import java.sql.Time; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; - -public class AlarmListener extends BroadcastReceiver implements AutomationListenerInterface -{ - private static AutomationService automationServiceRef; - private static AlarmManager centralAlarmManagerInstance; -// private static Intent alarmIntent; -// private static PendingIntent alarmPendingIntent; - private static boolean alarmListenerActive=false; - private static ArrayList alarmCandidates = new ArrayList(); - - private static ArrayList requestCodeList = new ArrayList(); - - public static void startAlarmListener(final AutomationService automationServiceRef) - { - AlarmListener.startAlarmListenerInternal(automationServiceRef); - } - public static void stopAlarmListener(Context context) - { - AlarmListener.stopAlarmListenerInternal(); - } - - public static boolean isAlarmListenerActive() - { - return alarmListenerActive; - } - - @Override - public void onReceive(Context context, Intent intent) - { - Miscellaneous.logEvent("i", "AlarmListener", "Alarm received", 2); - Date now = new Date(); - String timeString = String.valueOf(now.getHours()) + ":" + String.valueOf(now.getMinutes()) + ":" + String.valueOf(now.getSeconds()); - Time passTime = Time.valueOf(timeString); - - ArrayList allRulesWithNowInTimeFrame = Rule.findRuleCandidatesByTime(passTime); - for(int i=0; i allRulesWithTimeFrames = new ArrayList(); - allRulesWithTimeFrames = Rule.findRuleCandidatesByTimeFrame(); - for(Rule oneRule : allRulesWithTimeFrames) - { - for(Trigger oneTrigger : oneRule.getTriggerSet()) - { - if(oneTrigger.getTriggerType() == Trigger_Enum.timeFrame) - { - Calendar calNow, calSet; - Time setTime; - - if(oneTrigger.getTriggerParameter()) - setTime = oneTrigger.getTimeFrame().getTriggerTimeStart(); - else - setTime = oneTrigger.getTimeFrame().getTriggerTimeStop(); - - calNow = Calendar.getInstance(); - calSet = (Calendar) calNow.clone(); - calSet.set(Calendar.HOUR_OF_DAY, setTime.getHours()); - calSet.set(Calendar.MINUTE, setTime.getMinutes()); - calSet.set(Calendar.SECOND, 0); - calSet.set(Calendar.MILLISECOND, 0); - // At this point calSet would be a scheduling candidate. It's just the day the might not be right, yet. - - long milliSecondsInAWeek = 1000 * 60 * 60 * 24 * 7; - - for(int dayOfWeek : oneTrigger.getTimeFrame().getDayList()) - { - Calendar calSetWorkingCopy = (Calendar) calSet.clone(); - -// calSetWorkingCopy.set(Calendar.HOUR_OF_DAY, setTime.getHours()); -// calSetWorkingCopy.set(Calendar.MINUTE, setTime.getMinutes()); -// calSetWorkingCopy.set(Calendar.SECOND, 0); -// calSetWorkingCopy.set(Calendar.MILLISECOND, 0); - - int diff = dayOfWeek - calNow.get(Calendar.DAY_OF_WEEK); -// Log.i("AlarmManager", "Today: " + String.valueOf(calNow.get(Calendar.DAY_OF_WEEK)) + " / Sched.Day: " + String.valueOf(dayOfWeek) + " Difference to target day is: " + String.valueOf(diff)); - if(diff == 0) //if we're talking about the current day, is the time still in the future? - { - if(calSetWorkingCopy.getTime().getHours() < calNow.getTime().getHours()) - { -// Log.i("AlarmManager", "calSetWorkingCopy.getTime().getHours(" + String.valueOf(calSetWorkingCopy.getTime().getHours()) + ") < calNow.getTime().getHours(" + String.valueOf(calNow.getTime().getHours()) + ")"); - calSetWorkingCopy.add(Calendar.DAY_OF_MONTH, 7); //add a week - } - else if(calSetWorkingCopy.getTime().getHours() == calNow.getTime().getHours()) - { -// Log.i("AlarmManager", "calSetWorkingCopy.getTime().getHours() == calNow.getTime().getHours()"); - if(calSetWorkingCopy.getTime().getMinutes() <= calNow.getTime().getMinutes()) - { -// Log.i("AlarmManager", "calSetWorkingCopy.getTime().getMinutes() < calNow.getTime().getMinutes()"); - calSetWorkingCopy.add(Calendar.DAY_OF_MONTH, 7); //add a week - } - } - } - else if(diff < 0) - { -// Miscellaneous.logEvent("i", "AlarmManager", "Adding " + String.valueOf(diff+7) + " on top of " + String.valueOf(calSetWorkingCopy.get(Calendar.DAY_OF_WEEK))); - calSetWorkingCopy.add(Calendar.DAY_OF_WEEK, diff+7); // it's a past weekday, schedule for next week - } - else - { -// Miscellaneous.logEvent("i", "AlarmManager", "Adding " + String.valueOf(diff) + " on top of " + String.valueOf(calSetWorkingCopy.get(Calendar.DAY_OF_WEEK))); - calSetWorkingCopy.add(Calendar.DAY_OF_WEEK, diff); // it's a future weekday, schedule for that day - } - - i++; - i=(int)System.currentTimeMillis(); - String calSetWorkingCopyString = sdf.format(calSetWorkingCopy.getTime()) + " RequestCode: " + String.valueOf(i); -// Miscellaneous.logEvent("i", "AlarmManager", "Setting repeating alarm because of rule: " + oneRule.getName() + " beginning at " + calSetWorkingCopyString); - - alarmCandidates.add(calSetWorkingCopy.getTimeInMillis()); -// Intent alarmIntent = new Intent(automationServiceRef, AlarmListener.class); -// alarmIntent.setData(Uri.parse("myalarms://" + i)); -// PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, i, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT); -// centralAlarmManagerInstance.setInexactRepeating(AlarmManager.RTC_WAKEUP, calSetWorkingCopy.getTimeInMillis(), milliSecondsInAWeek, alarmPendingIntent); -// requestCodeList.add(i); - } - } - } - } - -// // get a Calendar object with current time -// Calendar cal = Calendar.getInstance(); -// cal.add(Calendar.SECOND, 10); -// String calSetWorkingCopyString2 = sdf.format(cal.getTime()); -// Miscellaneous.logEvent("i", "AlarmManager", "Setting repeating alarm because of hardcoded test: beginning at " + calSetWorkingCopyString2); -// Intent alarmIntent2 = new Intent(automationServiceRef, AlarmListener.class); -// PendingIntent alarmPendingIntent2 = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent2, 0); -// centralAlarmManagerInstance.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 5000, alarmPendingIntent2); -// requestCodeList.add(0); - - scheduleNextAlarm(); - } - - private static void scheduleNextAlarm() - { - Long currentTime = System.currentTimeMillis(); - Long scheduleCandidate = null; - - if(alarmCandidates.size() == 0) - { - Miscellaneous.logEvent("i", "AlarmManager", "No alarms to be scheduled.", 3); - return; - } - else if(alarmCandidates.size() == 1) - { - // only one alarm, schedule that - scheduleCandidate = alarmCandidates.get(0); - } - else if(alarmCandidates.size() > 1) - { - scheduleCandidate = alarmCandidates.get(0); - - for(long alarmCandidate : alarmCandidates) - { - if(Math.abs(currentTime - alarmCandidate) < Math.abs(currentTime - scheduleCandidate)) - scheduleCandidate = alarmCandidate; - } - } - - Intent alarmIntent = new Intent(automationServiceRef, AlarmListener.class); - PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT); - centralAlarmManagerInstance.set(AlarmManager.RTC_WAKEUP, scheduleCandidate, alarmPendingIntent); - - - SimpleDateFormat sdf = new SimpleDateFormat("E dd.MM.yyyy HH:mm"); - Calendar calendar = Calendar.getInstance(); - calendar.setTimeInMillis(scheduleCandidate); - Miscellaneous.logEvent("i", "AlarmManager", "Chose " + sdf.format(calendar.getTime()) + " as next scheduled alarm.", 4); - - } - - public static void clearAlarms() - { - Miscellaneous.logEvent("i", "AlarmManager", "Clearing possibly standing alarms.", 4); - for(int requestCode : requestCodeList) - { - Intent alarmIntent = new Intent(automationServiceRef, AlarmListener.class); - PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, requestCode, alarmIntent, 0); -// Miscellaneous.logEvent("i", "AlarmManager", "Clearing alarm with request code: " + String.valueOf(requestCode)); - centralAlarmManagerInstance.cancel(alarmPendingIntent); - } - requestCodeList.clear(); - } - - private static void startAlarmListenerInternal(AutomationService givenAutomationServiceRef) - { - if(!alarmListenerActive) - { - Miscellaneous.logEvent("i", "AlarmListener", "Starting alarm listener.", 4); - AlarmListener.automationServiceRef = givenAutomationServiceRef; - centralAlarmManagerInstance = (AlarmManager)automationServiceRef.getSystemService(automationServiceRef.ALARM_SERVICE); -// alarmIntent = new Intent(automationServiceRef, AlarmListener.class); -// alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent, 0); - alarmListenerActive = true; - Miscellaneous.logEvent("i", "AlarmListener", "Alarm listener started.", 4); - AlarmListener.setAlarms(); - -// // get a Calendar object with current time -// Calendar cal = Calendar.getInstance(); -// // add 5 minutes to the calendar object -// cal.add(Calendar.SECOND, 10); -// centralAlarmManagerInstance.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 5000, alarmPendingIntent); - } - else - Miscellaneous.logEvent("i", "AlarmListener", "Request to start AlarmListener. But it's already active.", 5); - } - - private static void stopAlarmListenerInternal() - { - if(alarmListenerActive) - { - Miscellaneous.logEvent("i", "AlarmListener", "Stopping alarm listener.", 4); - clearAlarms(); -// centralAlarmManagerInstance.cancel(alarmPendingIntent); - alarmListenerActive = false; - } - else - Miscellaneous.logEvent("i", "AlarmListener", "Request to stop AlarmListener. But it's not running.", 5); - } - public static void reloadAlarms() - { - AlarmListener.setAlarms(); - } - @Override - public void startListener(AutomationService automationService) - { - AlarmListener.startAlarmListener(automationService); - } - @Override - public void stopListener(AutomationService automationService) - { - AlarmListener.stopAlarmListener(automationService); - } - - public static boolean haveAllPermission() - { - return true; - } - - @Override - public boolean isListenerRunning() - { - return isAlarmListenerActive(); - } - @Override - public Trigger_Enum[] getMonitoredTrigger() - { - return new Trigger_Enum[] { Trigger_Enum.timeFrame }; - } - -} diff --git a/app/src/main/java/com/jens/automation2/receivers/BatteryReceiver.java b/app/src/main/java/com/jens/automation2/receivers/BatteryReceiver.java index 0d75214..732d44f 100644 --- a/app/src/main/java/com/jens/automation2/receivers/BatteryReceiver.java +++ b/app/src/main/java/com/jens/automation2/receivers/BatteryReceiver.java @@ -1,5 +1,6 @@ package com.jens.automation2.receivers; +import android.Manifest; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -18,7 +19,7 @@ import java.util.ArrayList; public class BatteryReceiver extends BroadcastReceiver implements AutomationListenerInterface { - private static int batteryLevel=-1; // initialize with a better value than this + private static int batteryLevel = -1; // initialize with a better value than this public static AutomationService automationServiceRef = null; private static boolean usbHostConnected = false; @@ -26,6 +27,7 @@ public class BatteryReceiver extends BroadcastReceiver implements AutomationList private static IntentFilter batteryIntentFilter = null; private static Intent batteryStatus = null; private static BroadcastReceiver batteryInfoReceiverInstance = null; + public static void startBatteryReceiver(final AutomationService automationServiceRef) { if(!batteryReceiverActive) @@ -78,11 +80,11 @@ public class BatteryReceiver extends BroadcastReceiver implements AutomationList return batteryLevel; } - private static int deviceIsCharging = 0; //0=unknown, 1=no, 2=yes + private static int currentChargingState = 0; //0=unknown, 1=no, 2=yes - public static int getDeviceIsCharging() + public static int getCurrentChargingState() { - return deviceIsCharging; + return currentChargingState; } @Override @@ -145,31 +147,6 @@ public class BatteryReceiver extends BroadcastReceiver implements AutomationList this.actionDischarging(context); break; } - // } - // else if(intent.getAction().equals(Intent.ACTION_POWER_CONNECTED)) - // { - //// Miscellaneous.logEvent("i", "BatteryReceiver", "Battery is charging or full."); - // deviceIsCharging = 2; - // //activate rule(s) - // ArrayList ruleCandidates = Rule.findRuleCandidatesByCharging(true); - // for(int i=0; i ruleCandidates = Rule.findRuleCandidatesByCharging(false); - // for(int i=0; i ruleCandidates = Rule.findRuleCandidatesByCharging(true); + ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.charging); +// ArrayList ruleCandidates = Rule.findRuleCandidatesByCharging(true); for(int i=0; i ruleCandidates = Rule.findRuleCandidatesByBatteryLevel(); + ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.batteryLevel); for(int i=0; i ruleCandidates = Rule.findRuleCandidatesByCharging(false); + ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.charging); +// ArrayList ruleCandidates = Rule.findRuleCandidatesByCharging(false); for(int i=0; i ruleCandidates = Rule.findRuleCandidatesByUsbHost(true); + + ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.usb_host_connection); +// ArrayList ruleCandidates = Rule.findRuleCandidatesByUsbHost(true); for(Rule oneRule : ruleCandidates) { - if(oneRule.applies(context)) + if(oneRule.getsGreenLight(context)) oneRule.activate(automationServiceRef, false); } @@ -274,11 +254,12 @@ public class BatteryReceiver extends BroadcastReceiver implements AutomationList usbHostConnected = false; Miscellaneous.logEvent("i", "BatteryReceiver", "Disconnected from computer.", 3); Toast.makeText(context, "Disconnected from computer.", Toast.LENGTH_LONG).show(); - - ArrayList ruleCandidates = Rule.findRuleCandidatesByUsbHost(false); + + ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.usb_host_connection); +// ArrayList ruleCandidates = Rule.findRuleCandidatesByUsbHost(false); for(Rule oneRule : ruleCandidates) { - if(oneRule.applies(context)) + if(oneRule.getsGreenLight(context)) oneRule.activate(automationServiceRef, false); } } @@ -296,8 +277,8 @@ public class BatteryReceiver extends BroadcastReceiver implements AutomationList public static boolean haveAllPermission() { - return ActivityPermissions.havePermission("android.permission.READ_PHONE_STATE", Miscellaneous.getAnyContext()) && - ActivityPermissions.havePermission("android.permission.BATTERY_STATS", Miscellaneous.getAnyContext()); + return ActivityPermissions.havePermission(Manifest.permission.READ_PHONE_STATE, Miscellaneous.getAnyContext()) && + ActivityPermissions.havePermission(Manifest.permission.BATTERY_STATS, Miscellaneous.getAnyContext()); } @Override diff --git a/app/src/main/java/com/jens/automation2/receivers/BluetoothReceiver.java b/app/src/main/java/com/jens/automation2/receivers/BluetoothReceiver.java index 0415c78..449bc60 100644 --- a/app/src/main/java/com/jens/automation2/receivers/BluetoothReceiver.java +++ b/app/src/main/java/com/jens/automation2/receivers/BluetoothReceiver.java @@ -124,10 +124,10 @@ public class BluetoothReceiver extends BroadcastReceiver implements AutomationLi Miscellaneous.logEvent("i", "BluetoothReceiver", String.format(context.getResources().getString(R.string.bluetoothDeviceInRange), bluetoothDevice.getName()), 3); } - ArrayList ruleCandidates = Rule.findRuleCandidatesByBluetoothConnection(); + ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.bluetoothConnection); for(int i=0; i ruleCandidates = Rule.findRuleCandidatesByAirplaneMode(isAirplaneMode); + ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.airplaneMode); +// ArrayList ruleCandidates = Rule.findRuleCandidatesByAirplaneMode(isAirplaneMode); for(int i=0; i ruleCandidates = Rule.findRuleCandidatesByRoaming(isRoaming); + + ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.roaming); +// ArrayList ruleCandidates = Rule.findRuleCandidatesByRoaming(isRoaming); for(int i=0; i alarmCandidates = new ArrayList<>(); + + private static ArrayList requestCodeList = new ArrayList(); + + public static void startAlarmListener(final AutomationService automationServiceRef) + { + DateTimeListener.startAlarmListenerInternal(automationServiceRef); + } + public static void stopAlarmListener(Context context) + { + DateTimeListener.stopAlarmListenerInternal(); + } + + public static boolean isAlarmListenerActive() + { + return alarmListenerActive; + } + + @Override + public void onReceive(Context context, Intent intent) + { + Miscellaneous.logEvent("i", "AlarmListener", "Alarm received", 2); + Date now = new Date(); + String timeString = String.valueOf(now.getHours()) + ":" + String.valueOf(now.getMinutes()) + ":" + String.valueOf(now.getSeconds()); + Time passTime = Time.valueOf(timeString); + + ArrayList allRulesWithNowInTimeFrame = Rule.findRuleCandidates(Trigger_Enum.timeFrame); +// ArrayList allRulesWithNowInTimeFrame = Rule.findRuleCandidatesByTime(passTime); + for(int i=0; i allRulesWithTimeFrames = new ArrayList(); + allRulesWithTimeFrames = Rule.findRuleCandidates(Trigger_Enum.timeFrame); +// allRulesWithTimeFrames = Rule.findRuleCandidatesByTimeFrame(); + /* + * Take care of regular executions, no repetitions in between. + */ + Miscellaneous.logEvent("i", "DateTimeListener", "Checking rules for single run alarm candidates.", 5); + for(Rule oneRule : allRulesWithTimeFrames) + { + Miscellaneous.logEvent("i", "DateTimeListener","Checking rule " + oneRule.getName() + " for single run alarm candidates.", 5); + if(oneRule.isRuleActive()) + { + try + { + for(Trigger oneTrigger : oneRule.getTriggerSet()) + { + Miscellaneous.logEvent("i", "DateTimeListener","Checking trigger " + oneTrigger.toString() + " for single run alarm candidates.", 5); + + if(oneTrigger.getTriggerType().equals(Trigger_Enum.timeFrame)) + { + TimeFrame tf = new TimeFrame(oneTrigger.getTriggerParameter2()); + + Calendar calSet; + Time setTime; + + if(oneTrigger.getTriggerParameter()) + setTime = tf.getTriggerTimeStart(); + else + setTime = tf.getTriggerTimeStop(); + + calSet = (Calendar) calNow.clone(); + calSet.set(Calendar.HOUR_OF_DAY, setTime.getHours()); + calSet.set(Calendar.MINUTE, setTime.getMinutes()); + calSet.set(Calendar.SECOND, 0); + calSet.set(Calendar.MILLISECOND, 0); + // At this point calSet would be a scheduling candidate. It's just the day that might not be right, yet. + + for(int dayOfWeek : tf.getDayList()) + { + Calendar calSetWorkingCopy = (Calendar) calSet.clone(); + + int diff = dayOfWeek - calNow.get(Calendar.DAY_OF_WEEK); + if(diff == 0) // We're talking about the current weekday, but is the time still in the future? + { + if(calSetWorkingCopy.getTime().getHours() < calNow.getTime().getHours()) + { + calSetWorkingCopy.add(Calendar.DAY_OF_MONTH, 7); //add a week + } + else if(calSetWorkingCopy.getTime().getHours() == calNow.getTime().getHours()) + { + if(calSetWorkingCopy.getTime().getMinutes() <= calNow.getTime().getMinutes()) + { + calSetWorkingCopy.add(Calendar.DAY_OF_MONTH, 7); //add a week + } + } + } + else if(diff < 0) + { + calSetWorkingCopy.add(Calendar.DAY_OF_WEEK, diff+7); // it's a past weekday, schedule for next week + } + else + { + calSetWorkingCopy.add(Calendar.DAY_OF_WEEK, diff); // it's a future weekday, schedule for that day + } + + i++; + i=(int)System.currentTimeMillis(); + sdf.format(calSetWorkingCopy.getTime()); + String.valueOf(i); + + alarmCandidates.add(new ScheduleElement(calSetWorkingCopy, "Rule " + oneRule.getName() + ", trigger " + oneTrigger.toString())); + } + } + } + } + catch(Exception e) + { + Miscellaneous.logEvent("e", "DateTimeListener","Error checking anything for rule " + oneRule.toString() + " needs to be added to candicates list: " + Log.getStackTraceString(e), 1); + } + } + } + + /* + * Only take care of repeated executions. + */ + Miscellaneous.logEvent("i", "DateTimeListener","Checking rules for repeated run alarm candidates.", 5); + for(Rule oneRule : allRulesWithTimeFrames) + { + Miscellaneous.logEvent("i", "DateTimeListener","Checking rule " + oneRule.getName() + " for repeated run alarm candidates.", 5); + if(oneRule.isRuleActive()) + { + try + { + Miscellaneous.logEvent("i", "DateTimeListener","Checking rule " + oneRule.toString() , 5); + + for(Trigger oneTrigger : oneRule.getTriggerSet()) + { + Miscellaneous.logEvent("i", "DateTimeListener","Checking trigger " + oneTrigger.toString() + " for repeated run alarm candidates.", 5); + if(oneTrigger.getTriggerType().equals(Trigger_Enum.timeFrame)) + { + Miscellaneous.logEvent("i", "DateTimeListener","Checking rule trigger " + oneTrigger.toString() , 5); + + /* + * Check for next repeated execution: + * + * Check if the rule currently applies.... + * + * If no -> do nothing + * If yes -> Take starting time and calculate the next repeated execution + * 1. Take starting time + * 2. Take current time + * 3. Calculate difference, but include check to see if we're after that time, + * be it start or end of the timeframe. + * 4. Take div result +1 and add this on top of starting time + * 5. Is this next possible execution still inside timeframe? Also consider timeframes spanning over midnight + */ + Calendar calSet; + Time setTime; + TimeFrame tf = new TimeFrame(oneTrigger.getTriggerParameter2()); + + if(tf.getRepetition() > 0) + { + if(oneTrigger.applies(calNow, Miscellaneous.getAnyContext())) + { + Calendar calSchedule = getNextRepeatedExecutionAfter(oneTrigger, calNow); + + alarmCandidates.add(new ScheduleElement(calSchedule, "Rule " + oneRule.getName() + ", trigger " + oneTrigger.toString())); + } + } + } + } + } + catch(Exception e) + { + Miscellaneous.logEvent("e", "DateTimeListener","Error checking anything for rule " + oneRule.toString() + " needs to be added to candicates list: " + Log.getStackTraceString(e), 1); + } + } + } + + scheduleNextAlarm(); + } + + private static void scheduleNextAlarm() + { + Long currentTime = System.currentTimeMillis(); + ScheduleElement scheduleCandidate = null; + + if(alarmCandidates.size() == 0) + { + Miscellaneous.logEvent("i", "AlarmManager", "No alarms to be scheduled.", 3); + return; + } + else if(alarmCandidates.size() == 1) + { + // only one alarm, schedule that + scheduleCandidate = alarmCandidates.get(0); + } + else if(alarmCandidates.size() > 1) + { + scheduleCandidate = alarmCandidates.get(0); + + for(ScheduleElement alarmCandidate : alarmCandidates) + { + if(Math.abs(currentTime - alarmCandidate.time.getTimeInMillis()) < Math.abs(currentTime - scheduleCandidate.time.getTimeInMillis())) + scheduleCandidate = alarmCandidate; + } + } + + Intent alarmIntent = new Intent(automationServiceRef, DateTimeListener.class); + PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT); + centralAlarmManagerInstance.set(AlarmManager.RTC_WAKEUP, scheduleCandidate.time.getTimeInMillis(), alarmPendingIntent); + + + SimpleDateFormat sdf = new SimpleDateFormat("E dd.MM.yyyy HH:mm"); + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(scheduleCandidate.time.getTimeInMillis()); + Miscellaneous.logEvent("i", "AlarmManager", "Chose " + sdf.format(calendar.getTime()) + " as next scheduled alarm.", 4); + } + + public static void clearAlarms() + { + Miscellaneous.logEvent("i", "AlarmManager", "Clearing possibly standing alarms.", 4); + for(int requestCode : requestCodeList) + { + Intent alarmIntent = new Intent(automationServiceRef, DateTimeListener.class); + PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, requestCode, alarmIntent, 0); +// Miscellaneous.logEvent("i", "AlarmManager", "Clearing alarm with request code: " + String.valueOf(requestCode)); + centralAlarmManagerInstance.cancel(alarmPendingIntent); + } + requestCodeList.clear(); + } + + private static void startAlarmListenerInternal(AutomationService givenAutomationServiceRef) + { + if(!alarmListenerActive) + { + Miscellaneous.logEvent("i", "AlarmListener", "Starting alarm listener.", 4); + DateTimeListener.automationServiceRef = givenAutomationServiceRef; + centralAlarmManagerInstance = (AlarmManager)automationServiceRef.getSystemService(automationServiceRef.ALARM_SERVICE); +// alarmIntent = new Intent(automationServiceRef, AlarmListener.class); +// alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent, 0); + alarmListenerActive = true; + Miscellaneous.logEvent("i", "AlarmListener", "Alarm listener started.", 4); + DateTimeListener.setAlarms(); + +// // get a Calendar object with current time +// Calendar cal = Calendar.getInstance(); +// // add 5 minutes to the calendar object +// cal.add(Calendar.SECOND, 10); +// centralAlarmManagerInstance.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 5000, alarmPendingIntent); + } + else + Miscellaneous.logEvent("i", "AlarmListener", "Request to start AlarmListener. But it's already active.", 5); + } + + private static void stopAlarmListenerInternal() + { + if(alarmListenerActive) + { + Miscellaneous.logEvent("i", "AlarmListener", "Stopping alarm listener.", 4); + clearAlarms(); +// centralAlarmManagerInstance.cancel(alarmPendingIntent); + alarmListenerActive = false; + } + else + Miscellaneous.logEvent("i", "AlarmListener", "Request to stop AlarmListener. But it's not running.", 5); + } + public static void reloadAlarms() + { + DateTimeListener.setAlarms(); + } + @Override + public void startListener(AutomationService automationService) + { + DateTimeListener.startAlarmListener(automationService); + } + @Override + public void stopListener(AutomationService automationService) + { + DateTimeListener.stopAlarmListener(automationService); + } + + public static boolean haveAllPermission() + { + return true; + } + + @Override + public boolean isListenerRunning() + { + return isAlarmListenerActive(); + } + @Override + public Trigger_Enum[] getMonitoredTrigger() + { + return new Trigger_Enum[] { Trigger_Enum.timeFrame }; + } + + static class ScheduleElement implements Comparable + { + Calendar time; + String reason; + + public ScheduleElement(Calendar timestamp, String reason) + { + super(); + this.time = timestamp; + this.reason = reason; + } + + @Override + public int compareTo(ScheduleElement o) + { + if(time.getTimeInMillis() == o.time.getTimeInMillis()) + return 0; + if(time.getTimeInMillis() < o.time.getTimeInMillis()) + return -1; + else + return 1; + } + + @Override + public String toString() + { + return Miscellaneous.formatDate(time.getTime()) + ", reason : " + reason; + } + } + + @RequiresApi(api = Build.VERSION_CODES.N) + public static Calendar getNextRepeatedExecutionAfter(Trigger trigger, Calendar now) + { + Calendar calSet; + Time setTime; + TimeFrame tf = new TimeFrame(trigger.getTriggerParameter2()); + + if(tf.getRepetition() > 0) + { + if(trigger.getTriggerParameter()) + setTime = tf.getTriggerTimeStart(); + else + setTime = tf.getTriggerTimeStop(); + + calSet = (Calendar) now.clone(); + calSet.set(Calendar.HOUR_OF_DAY, setTime.getHours()); + calSet.set(Calendar.MINUTE, setTime.getMinutes()); + calSet.set(Calendar.SECOND, 0); + calSet.set(Calendar.MILLISECOND, 0); + +// if(this.applies(null)) +// { + // If the starting time is a day ahead remove 1 day. + if(calSet.getTimeInMillis() > now.getTimeInMillis()) + calSet.add(Calendar.DAY_OF_MONTH, -1); + + long differenceInSeconds = Math.abs(now.getTimeInMillis() - calSet.getTimeInMillis()) / 1000; + long nextExecutionMultiplier = Math.floorDiv(differenceInSeconds, tf.getRepetition()) + 1; + long nextScheduleTimestamp = (calSet.getTimeInMillis() / 1000) + (nextExecutionMultiplier * tf.getRepetition()); + Calendar calSchedule = Calendar.getInstance(); + calSchedule.setTimeInMillis(nextScheduleTimestamp * 1000); + + /* + * Das war mal aktiviert. Allerdings: Die ganze Funktion liefert zurück, wenn die Regel NOCH nicht + * zutrifft, aber wir z.B. gleich den zeitlichen Bereich betreten. + */ +// if(trigger.checkDateTime(calSchedule.getTime(), false)) +// { + return calSchedule; +// } +// } + } + else + Miscellaneous.logEvent("i", "DateTimeListener", "Trigger " + trigger.toString() + " is not executed repeatedly.", 5); + + return null; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jens/automation2/receivers/DeviceOrientationListener.java b/app/src/main/java/com/jens/automation2/receivers/DeviceOrientationListener.java new file mode 100644 index 0000000..9fcc94b --- /dev/null +++ b/app/src/main/java/com/jens/automation2/receivers/DeviceOrientationListener.java @@ -0,0 +1,251 @@ +package com.jens.automation2.receivers; + +import static android.content.Context.SENSOR_SERVICE; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; + +import com.jens.automation2.ActivityManageTriggerDeviceOrientation; +import com.jens.automation2.AutomationService; +import com.jens.automation2.Miscellaneous; +import com.jens.automation2.Rule; +import com.jens.automation2.Settings; +import com.jens.automation2.Trigger; + +import java.util.ArrayList; +import java.util.Calendar; + +public class DeviceOrientationListener implements SensorEventListener, AutomationListenerInterface +{ + // https://developer.android.com/guide/topics/sensors/sensors_position#java + + ActivityManageTriggerDeviceOrientation activityManageTriggerDeviceOrientationInstance = null; + + //the Sensor Manager + private SensorManager sManager; + static DeviceOrientationListener instance = null; + boolean isRunning = false; + + Calendar now = null; + static Calendar lastTimeSignalArrived = null; + static int sensorValueCounter = 0; + + // Gravity rotational data + private float gravity[]; + // Magnetic rotational data + private float magnetic[]; //for magnetic rotational data + private float accels[] = new float[3]; + private float mags[] = new float[3]; + private float[] values = new float[3]; + + // azimuth, pitch and roll + private float azimuth; + private float pitch; + private float roll; + + boolean applies = false; + boolean flipped = false; + boolean toggable = false; + + + public static DeviceOrientationListener getInstance() + { + if (instance == null) + instance = new DeviceOrientationListener(); + + return instance; + } + + public float getAzimuth() + { + return azimuth; + } + + public float getPitch() + { + return pitch; + } + + public float getRoll() + { + return roll; + } + + public void startSensorFromConfigActivity(Context context, ActivityManageTriggerDeviceOrientation activityManageTriggerDeviceOrientationInstance) + { + this.activityManageTriggerDeviceOrientationInstance = activityManageTriggerDeviceOrientationInstance; + + if(!isRunning) + { + sManager = (SensorManager) context.getSystemService(SENSOR_SERVICE); + + /* + register the sensor listener to listen to the gyroscope sensor, use the + callbacks defined in this class, and gather the sensor information as quick + as possible + */ + + isRunning = true; + + sManager.registerListener(this, sManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL); + sManager.registerListener(this, sManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), SensorManager.SENSOR_DELAY_NORMAL); + } + } + + public void stopSensorFromConfigActivity() + { + activityManageTriggerDeviceOrientationInstance = null; + + if(isRunning) + { + if(!Rule.isAnyRuleUsing(Trigger.Trigger_Enum.deviceOrientation)) + { + //unregister the sensor listener + sManager.unregisterListener(this); + isRunning = false; + } + } + } + + @Override + public void onAccuracyChanged(Sensor arg0, int arg1) + { + //Do nothing. + } + + @Override + public void onSensorChanged(SensorEvent event) + { + switch (event.sensor.getType()) + { + case Sensor.TYPE_MAGNETIC_FIELD: + mags = event.values.clone(); + break; + case Sensor.TYPE_ACCELEROMETER: + accels = event.values.clone(); + break; + } + + if (mags != null && accels != null) + { + gravity = new float[9]; + magnetic = new float[9]; + SensorManager.getRotationMatrix(gravity, magnetic, accels, mags); + float[] outGravity = new float[9]; + SensorManager.remapCoordinateSystem(gravity, SensorManager.AXIS_X, SensorManager.AXIS_Z, outGravity); + SensorManager.getOrientation(outGravity, values); + + azimuth = values[0] * 57.2957795f; + pitch = values[1] * 57.2957795f; + roll = values[2] * 57.2957795f; + mags = null; + accels = null; + } + + //else it will output the Roll, Pitch and Yawn values + if(activityManageTriggerDeviceOrientationInstance != null) + activityManageTriggerDeviceOrientationInstance.updateFields(azimuth, pitch, roll); + + /* + For some reason the first 3 values after starting the listener + are crap. + */ + if(sensorValueCounter > 3) + { + now = Calendar.getInstance(); + if (lastTimeSignalArrived == null || now.getTimeInMillis() >= lastTimeSignalArrived.getTimeInMillis() + Settings.acceptDeviceOrientationSignalEveryX_MilliSeconds) + { + lastTimeSignalArrived = now; + + Miscellaneous.logEvent("i", "DeviceOrientation", "Got device orientation update: azimuth: " + String.valueOf(azimuth) + ", pitch: " + String.valueOf(pitch) + ", roll: " + String.valueOf(pitch), 4); + + if (AutomationService.isMyServiceRunning(Miscellaneous.getAnyContext())) + { + ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger.Trigger_Enum.deviceOrientation); + for (int i = 0; i < ruleCandidates.size(); i++) + { + if (ruleCandidates.get(i).getsGreenLight(Miscellaneous.getAnyContext())) + ruleCandidates.get(i).activate(AutomationService.getInstance(), false); + } + } + } + } + else + sensorValueCounter++; + } + + @Override + public void startListener(AutomationService automationService) + { + if(!isRunning) + { + sManager = (SensorManager) Miscellaneous.getAnyContext().getSystemService(SENSOR_SERVICE); + + /* + register the sensor listener to listen to the gyroscope sensor, use the + callbacks defined in this class, and gather the sensor information as quick + as possible + */ + + isRunning = true; + + sManager.registerListener(this, sManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL); + sManager.registerListener(this, sManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), SensorManager.SENSOR_DELAY_NORMAL); + } + } + + @Override + public void stopListener(AutomationService automationService) + { + this.activityManageTriggerDeviceOrientationInstance = null; + + if(isRunning) + { + //unregister the sensor listener + sManager.unregisterListener(this); + isRunning = false; + } + } + + @Override + public boolean isListenerRunning() + { + return isRunning; + } + + @Override + public Trigger.Trigger_Enum[] getMonitoredTrigger() + { + return new Trigger.Trigger_Enum[] { Trigger.Trigger_Enum.deviceOrientation}; + } + + /* + Azimuth (degrees of rotation about the -z axis). + This is the angle between the device's current compass direction and magnetic north. If the top edge of the + device faces magnetic north, the azimuth is 0 degrees; if the top edge faces south, the azimuth is 180 degrees. + Similarly, if the top edge faces east, the azimuth is 90 degrees, and if the top edge faces west, the azimuth is 270 degrees. + + Pitch (degrees of rotation about the x axis). + This is the angle between a plane parallel to the device's screen and a plane parallel to the ground. If you hold the device + parallel to the ground with the bottom edge closest to you and tilt the top edge of the device toward the ground, the pitch + angle becomes positive. Tilting in the opposite direction— moving the top edge of the device away from the ground—causes + the pitch angle to become negative. The range of values is -180 degrees to 180 degrees. + + Roll (degrees of rotation about the y axis). + This is the angle between a plane perpendicular to the device's screen and a plane perpendicular to the ground. + If you hold the device parallel to the ground with the bottom edge closest to you and tilt the left edge of the + device toward the ground, the roll angle becomes positive. Tilting in the opposite direction—moving the right + edge of the device toward the ground— causes the roll angle to become negative. The range of values is -90 degrees + to 90 degrees. + + Computes the device's orientation based on the rotation matrix. + When it returns, the array values are as follows: + values[0]: Azimuth, angle of rotation about the -z axis. This value represents the angle between the device's y axis and the magnetic north pole. When facing north, this angle is 0, when facing south, this angle is π. Likewise, when facing east, this angle is π/2, and when facing west, this angle is -π/2. The range of values is -π to π. + values[1]: Pitch, angle of rotation about the x axis. This value represents the angle between a plane parallel to the device's screen and a plane parallel to the ground. Assuming that the bottom edge of the device faces the user and that the screen is face-up, tilting the top edge of the device toward the ground creates a positive pitch angle. The range of values is -π to π. + values[2]: Roll, angle of rotation about the y axis. This value represents the angle between a plane perpendicular to the device's screen and a plane perpendicular to the ground. Assuming that the bottom edge of the device faces the user and that the screen is face-up, tilting the left edge of the device toward the ground creates a positive roll angle. The range of values is -π/2 to π/2. + Applying these three rotations in the azimuth, pitch, roll order transforms an identity matrix to the rotation matrix passed into this method. Also, note that all three orientation angles are expressed in radians. + */ +} \ No newline at end of file diff --git a/app/src/main/java/com/jens/automation2/receivers/HeadphoneJackListener.java b/app/src/main/java/com/jens/automation2/receivers/HeadphoneJackListener.java index fc37718..dcd5f26 100644 --- a/app/src/main/java/com/jens/automation2/receivers/HeadphoneJackListener.java +++ b/app/src/main/java/com/jens/automation2/receivers/HeadphoneJackListener.java @@ -1,5 +1,6 @@ package com.jens.automation2.receivers; +import android.Manifest; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -73,11 +74,12 @@ public class HeadphoneJackListener extends BroadcastReceiver implements Automati headsetConnected = true; Miscellaneous.logEvent("i", "HeadphoneJackListener", "Headset " + name + " plugged in.", 4); } - - ArrayList ruleCandidates = Rule.findRuleCandidatesByHeadphoneJack(isHeadsetConnected()); + + ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.headsetPlugged); +// ArrayList ruleCandidates = Rule.findRuleCandidatesByHeadphoneJack(isHeadsetConnected()); for(int i=0; i allRulesWithNfcTags = Rule.findRuleCandidatesByNfc(); + ArrayList allRulesWithNfcTags = Rule.findRuleCandidates(Trigger.Trigger_Enum.nfcTag); for(int i=0; i ruleCandidates = Rule.findRuleCandidatesByNoiseLevel(); + ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.noiseLevel); for(Rule oneRule : ruleCandidates) { - if(oneRule.applies(automationService)) + if(oneRule.getsGreenLight(automationService)) oneRule.activate(automationService, false); } } diff --git a/app/src/main/java/com/jens/automation2/receivers/NotificationListener.java b/app/src/main/java/com/jens/automation2/receivers/NotificationListener.java index 8daaf34..28ef525 100644 --- a/app/src/main/java/com/jens/automation2/receivers/NotificationListener.java +++ b/app/src/main/java/com/jens/automation2/receivers/NotificationListener.java @@ -89,19 +89,12 @@ public class NotificationListener extends NotificationListenerService lastNotification.title = title; lastNotification.text = text; -// if(lastResponseToNotification == null || lastResponseToNotification.getTimeInMillis() < lastNotification.publishTime.getTimeInMillis()) -// { -// lastResponseToNotification = Calendar.getInstance(); - - ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger.Trigger_Enum.notification); - for (int i = 0; i < ruleCandidates.size(); i++) - { - if (ruleCandidates.get(i).applies(NotificationListener.this)) - ruleCandidates.get(i).activate(AutomationService.getInstance(), false); - } -// } -// else -// Miscellaneous.logEvent("e", "NotificationCheck", "Ignoring notification as it is old.", 5); + ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger.Trigger_Enum.notification); + for (int i = 0; i < ruleCandidates.size(); i++) + { + if(ruleCandidates.get(i).getsGreenLight(NotificationListener.this)) + ruleCandidates.get(i).activate(AutomationService.getInstance(), false); + } } return false; diff --git a/app/src/main/java/com/jens/automation2/receivers/PhoneStatusListener.java b/app/src/main/java/com/jens/automation2/receivers/PhoneStatusListener.java index ae67a2f..22dc1e5 100644 --- a/app/src/main/java/com/jens/automation2/receivers/PhoneStatusListener.java +++ b/app/src/main/java/com/jens/automation2/receivers/PhoneStatusListener.java @@ -109,12 +109,12 @@ public class PhoneStatusListener implements AutomationListenerInterface break; } - ArrayList ruleCandidates = Rule.findRuleCandidatesByPhoneCall(Trigger.triggerPhoneCallDirectionOutgoing); + ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.phoneCall); for(int i=0; i ruleCandidates = Rule.findRuleCandidatesByPhoneCall(Trigger.triggerPhoneCallDirectionIncoming); + ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.phoneCall); for (int i = 0; i < ruleCandidates.size(); i++) { AutomationService asInstance = AutomationService.getInstance(); if (asInstance != null) - if (ruleCandidates.get(i).applies(asInstance)) + if (ruleCandidates.get(i).getsGreenLight(asInstance)) ruleCandidates.get(i).activate(asInstance, false); } } @@ -168,22 +168,18 @@ public class PhoneStatusListener implements AutomationListenerInterface */ setLastPhoneDirection(2); -// TelephonyManager tm = (TelephonyManager)context.getSystemService(Service.TELEPHONY_SERVICE); -// int newState = tm.getCallState(); -// setCurrentState(newState); - setCurrentState(TelephonyManager.CALL_STATE_RINGING); String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER); setLastPhoneNumber(phoneNumber); Miscellaneous.logEvent("i", "Call state", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.outgoingCallTo), getLastPhoneNumber()), 4); - ArrayList ruleCandidates = Rule.findRuleCandidatesByPhoneCall(Trigger.triggerPhoneCallDirectionOutgoing); + ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.phoneCall); for(int i=0; i0 | getRecentlyStoppedApps().size()>0) + Miscellaneous.logEvent("i", automationService.getResources().getString(R.string.processMonitoring), automationService.getResources().getString(R.string.messageReceivedStatingProcessMonitoringIsComplete), 5); + // This will take care of results delivered by the actual monitoring instance + + for(String entry : getRunningApps()) + Miscellaneous.logEvent("i", automationService.getResources().getString(R.string.runningApp), entry, 5); + + // execute matching rules containing processes + if(getRecentlyStartedApps().size()>0 | getRecentlyStoppedApps().size()>0) + { + for(String entry : getRecentlyStartedApps()) + Miscellaneous.logEvent("i", automationService.getResources().getString(R.string.appStarted), entry, 3); + for(String entry : getRecentlyStoppedApps()) + Miscellaneous.logEvent("i", automationService.getResources().getString(R.string.appStopped), entry, 3); + + ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger_Enum.process_started_stopped); + for(int i=0; i ruleCandidates = Rule.findRuleCandidatesByProcess(); - for(int i=0; ihandleMessage(): " + e.getMessage()); -// } + } } }; diff --git a/app/src/main/java/com/jens/automation2/receivers/TimeZoneListener.java b/app/src/main/java/com/jens/automation2/receivers/TimeZoneListener.java index c2856f2..5961a98 100644 --- a/app/src/main/java/com/jens/automation2/receivers/TimeZoneListener.java +++ b/app/src/main/java/com/jens/automation2/receivers/TimeZoneListener.java @@ -77,12 +77,12 @@ public class TimeZoneListener extends BroadcastReceiver implements AutomationLis if(action.equals(Intent.ACTION_TIMEZONE_CHANGED)) { Miscellaneous.logEvent("i", "TimeZoneListener", "Device timezone changed. Reloading alarms.", 3); - AlarmListener.reloadAlarms(); + DateTimeListener.reloadAlarms(); } else if(action.equals(Intent.ACTION_TIME_CHANGED)) { Miscellaneous.logEvent("i", "TimeZoneListener", "Device time changed. Reloading alarms.", 4); - AlarmListener.reloadAlarms(); + DateTimeListener.reloadAlarms(); } } @Override diff --git a/app/src/main/res/drawable-hdpi/smartphone.png b/app/src/main/res/drawable-hdpi/smartphone.png new file mode 100644 index 0000000..c992725 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/smartphone.png differ diff --git a/app/src/main/res/layout/activity_manage_specific_poi.xml b/app/src/main/res/layout/activity_manage_specific_poi.xml index c05c498..13f70f0 100644 --- a/app/src/main/res/layout/activity_manage_specific_poi.xml +++ b/app/src/main/res/layout/activity_manage_specific_poi.xml @@ -57,7 +57,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:ems="10" - android:inputType="numberDecimal" /> + android:inputType="numberSigned" /> @@ -72,14 +72,14 @@ android:layout_height="wrap_content" android:text="@string/longitude" android:textAppearance="?android:attr/textAppearanceMedium" /> - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +