package com.jens.automation2; import static com.jens.automation2.Trigger.triggerParameter2Split; import android.annotation.SuppressLint; import android.content.Context; import android.os.AsyncTask; import android.os.Build; import android.os.Looper; import android.util.Log; import android.widget.Toast; import com.google.android.gms.location.DetectedActivity; import com.jens.automation2.receivers.ActivityDetectionReceiver; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; public class Rule implements Comparable { private static ArrayList ruleCollection = new ArrayList(); private static List ruleRunHistory = new ArrayList(); public static List getRuleRunHistory() { return ruleRunHistory; } private ArrayList triggerSet; private ArrayList actionSet; private String name; private boolean ruleActive = true; // rules can be deactivated, so they won't fire if you don't want them temporarily private boolean ruleToggle = false; // rule will run again and do the opposite of its actions if applicable private Calendar lastExecution; private static Date lastActivatedRuleActivationTime; public Calendar getLastExecution() { return lastExecution; } public void setLastExecution(Calendar lastExecution) { this.lastExecution = lastExecution; } public boolean isRuleToggle() { return ruleToggle; } public void setRuleToggle(boolean ruleToggle) { this.ruleToggle = ruleToggle; } public static ArrayList getRuleCollection() { return ruleCollection; } public boolean isRuleActive() { return ruleActive; } public void setRuleActive(boolean ruleActive) { this.ruleActive = ruleActive; } public static void setRuleCollection(ArrayList ruleCollection) { Rule.ruleCollection = ruleCollection; } public static Date getLastActivatedRuleActivationTime() { return lastActivatedRuleActivationTime; } public static Rule getLastActivatedRule() { if(ruleRunHistory.size() > 0) return ruleRunHistory.get(0); else return null; } public ArrayList getTriggerSet() { return triggerSet; } public void setTriggerSet(ArrayList triggerSet) { this.triggerSet = triggerSet; } public ArrayList getActionSet() { return actionSet; } public void setActionSet(ArrayList actionSet) { this.actionSet = actionSet; } public String getName() { return name; } public void setName(String name) { this.name = name; } public static void readFromFile() { ruleCollection = XmlFileInterface.ruleCollection; } @Override public String toString() { return this.getName(); } @SuppressLint("NewApi") public String toStringLong() { String returnString = ""; if(isRuleActive()) returnString += "Active: "; else returnString += "Inactive: "; returnString += this.getName() + ": If "; for(int i=0; i { boolean wasActivated = false; @Override protected Void doInBackground(Object... params) { // Miscellaneous.logEvent("i", "Rule", ((Context) params[0]).getResources().getString(R.string.usingNewThreadForRuleExecution), 5); Thread.setDefaultUncaughtExceptionHandler(Miscellaneous.uncaughtExceptionHandler); // without this line the debugger will - for some reason - skip all breakpoints in this class if(android.os.Debug.isDebuggerConnected()) android.os.Debug.waitForDebugger(); if (Looper.myLooper() == null) Looper.prepare(); setLastExecution(Calendar.getInstance()); wasActivated = activateInternally((AutomationService)params[0]); return null; } @Override protected void onProgressUpdate(String... messages) { AutomationService service = AutomationService.getInstance(); service.speak(messages[0], false); Toast.makeText(service, messages[0], Toast.LENGTH_LONG).show(); super.onProgressUpdate(messages); } @Override protected void onPostExecute(Void result) { /* Only update if the rules was actually executed. Became necessary for the notification trigger. If a user created a rule with a notification trigger and this app creates a notification itself this will otherwise end in an infinite loop. */ if(wasActivated) { // setLastExecution(Calendar.getInstance()); AutomationService.updateNotification(); ActivityMainScreen.updateMainScreen(); super.onPostExecute(result); } } /** * Will activate the rule. Should be called by a separate execution thread * @param automationService */ protected boolean activateInternally(AutomationService automationService) { boolean isActuallyToggleable = isActuallyToggable(); boolean notLastActive = getLastActivatedRule() == null || !getLastActivatedRule().equals(Rule.this); boolean doToggle = ruleToggle && isActuallyToggleable; String message; if(!doToggle) message = String.format(automationService.getResources().getString(R.string.ruleActivate), Rule.this.getName()); else message = String.format(automationService.getResources().getString(R.string.ruleActivateToggle), Rule.this.getName()); Miscellaneous.logEvent("i", "Rule", message, 2); if(Settings.startNewThreadForRuleActivation) publishProgress(message); for(int i = 0; i< Rule.this.getActionSet().size(); i++) { try { Rule.this.getActionSet().get(i).run(automationService, doToggle); } catch(Exception e) { Miscellaneous.logEvent("e", "RuleExecution", "Error running action of rule " + Rule.this.getName() + ": " + Log.getStackTraceString(e), 1); } } // Keep log of last x rule activations (Settings) try { 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 = ""; for(Rule rule : ruleRunHistory) history += rule.getName() + ", "; if(history.length() > 0) history = history.substring(0, history.length()-2); Miscellaneous.logEvent("i", "Rule history", "Most recent first: " + history, 4); } catch(Exception e) { Miscellaneous.logEvent("e", "Rule history error", Log.getStackTraceString(e), 3); } Miscellaneous.logEvent("i", "Rule", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleActivationComplete), Rule.this.getName()), 2); return true; } } public void activate(AutomationService automationService, boolean force) { ActivateRuleTask task = new ActivateRuleTask(); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, automationService, force); else 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().equals(triggerType)) { ruleCandidates.add(oneRule); break innerLoop; // we don't need to check the other triggers in the same rule } } } return ruleCandidates; } public static ArrayList findRuleCandidates(Action.Action_Enum actionType) { ArrayList ruleCandidates = new ArrayList(); for(Rule oneRule : ruleCollection) { innerloop: for(Action oneAction : oneRule.getActionSet()) { if(oneAction.getAction().equals(actionType)) { ruleCandidates.add(oneRule); break innerloop; // we don't need to check the other actions in the same rule } } } return ruleCandidates; } public static ArrayList findRuleCandidatesByPoi(PointOfInterest searchPoi, boolean triggerParameter) { Miscellaneous.logEvent("i", "RuleSearch", "Searching for rules referencing POI " + searchPoi.getName() + ". Total size of ruleset: " + String.valueOf(ruleCollection.size()), 4); ArrayList ruleCandidates = new ArrayList(); for(int i=0; i findRuleCandidatesByPoi(PointOfInterest searchPoi) { ArrayList ruleCandidates = new ArrayList(); for(Rule oneRule : ruleCollection) { innerloop: for(Trigger oneTrigger : oneRule.getTriggerSet()) { if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.pointOfInterest) { if(oneTrigger.getPointOfInterest() != null && oneTrigger.getPointOfInterest().equals(searchPoi)) // != null to exclude those who are referring all locations ("entering any location") { 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 findRuleCandidatesByTriggerProfile(Profile profile) { ArrayList ruleCandidates = new ArrayList(); for(Rule oneRule : ruleCollection) { innerloop: for(Trigger oneTrigger : oneRule.getTriggerSet()) { if(oneTrigger.getTriggerType() == Trigger.Trigger_Enum.profileActive) { String profileName = oneTrigger.getTriggerParameter2().split(triggerParameter2Split)[0]; if(profileName.equals(profile.getName())) { ruleCandidates.add(oneRule); break innerloop; //if the profile is found we don't need to search the other triggers in the same rule } } } } return ruleCandidates; } public static ArrayList findRuleCandidatesByActionProfile(Profile profile) { ArrayList ruleCandidates = new ArrayList(); for(Rule oneRule : ruleCollection) { innerloop: for(Action oneAction : oneRule.getActionSet()) { if(oneAction.getAction() == Action.Action_Enum.changeSoundProfile) { if(oneAction.getParameter2().equals(profile.getOldName())) // != null to exclude those who are referring all locations ("entering any location") { ruleCandidates.add(oneRule); break innerloop; //if the profile is found we don't need to search the other triggers in the same rule } } } } return ruleCandidates; } public static boolean isAnyRuleUsing(Trigger.Trigger_Enum triggerType) { for(Rule rule: ruleCollection) { if(rule.isRuleActive()) { for(Trigger trigger : rule.getTriggerSet()) { if(trigger.getTriggerType().equals(triggerType)) { Miscellaneous.logEvent("i", "Rule->isAnyRuleUsing()", String.format(Miscellaneous.getAnyContext().getString(R.string.atLeastRuleXisUsingY), rule.getName(), triggerType.getFullName(Miscellaneous.getAnyContext())), 5); return true; } } } } return false; } public static boolean isAnyRuleUsing(Action.Action_Enum actionType) { for(Rule rule: ruleCollection) { if(rule.isRuleActive()) { for(Action action : rule.getActionSet()) { if(action.getAction().equals(actionType)) { Miscellaneous.logEvent("i", "Rule->isAnyRuleUsing()", String.format(Miscellaneous.getAnyContext().getString(R.string.atLeastRuleXisUsingY), rule.getName(), actionType.getFullName(Miscellaneous.getAnyContext())), 5); return true; } } } } return false; } @Override public int compareTo(Rule another) { return this.getName().compareTo(another.getName()); } public boolean haveEnoughPermissions() { return ActivityPermissions.havePermissionsForRule(this, Miscellaneous.getAnyContext()); } public static Rule getByName(String ruleName) { for(Rule r : Rule.getRuleCollection()) { if(r.getName().equals(ruleName)) return r; } return null; } }