Merge branch 'calendar_trigger' into development

This commit is contained in:
2023-12-31 16:00:13 +01:00
18 changed files with 1390 additions and 8 deletions

View File

@ -144,7 +144,7 @@ public class Action
case takeScreenshot:
return context.getResources().getString(R.string.takeScreenshot);
case setLocationService:
return context.getResources().getString(R.string.setLocationService);
return context.getResources().getString(R.string.setLocationServiceCapital);
default:
return "Unknown";
}

View File

@ -143,6 +143,8 @@ public class ActivityManageRule extends Activity
final static int requestCodeActionCopyTextToClipboardEdit = 830;
final static int requestCodeActionSetLocationServiceAdd = 831;
final static int requestCodeActionSetLocationServiceEdit = 832;
final static int requestCodeTriggerCalendarEventAdd = 833;
final static int requestCodeTriggerCalendarEventEdit = 834;
public static ActivityManageRule getInstance()
{
@ -347,6 +349,12 @@ public class ActivityManageRule extends Activity
variableStateEditor.putExtra(intentNameTriggerParameter2, selectedTrigger.getTriggerParameter2());
startActivityForResult(variableStateEditor, requestCodeTriggerCheckVariableEdit);
break;
case calendarEvent:
Intent calendarStateEditor = new Intent(ActivityManageRule.this, ActivityManageTriggerCalendar.class);
calendarStateEditor.putExtra(intentNameTriggerParameter1, selectedTrigger.getTriggerParameter());
calendarStateEditor.putExtra(intentNameTriggerParameter2, selectedTrigger.getTriggerParameter2());
startActivityForResult(calendarStateEditor, requestCodeTriggerCalendarEventEdit);
break;
default:
break;
}
@ -636,6 +644,10 @@ public class ActivityManageRule extends Activity
items.add(new Item(typesLong[i].toString(), R.drawable.router));
else if(types[i].toString().equals(Trigger_Enum.subSystemState.toString()))
items.add(new Item(typesLong[i].toString(), R.drawable.subsystemstate));
else if(types[i].toString().equals(Trigger_Enum.checkVariable.toString()))
items.add(new Item(typesLong[i].toString(), R.drawable.variable));
else if(types[i].toString().equals(Trigger_Enum.calendarEvent.toString()))
items.add(new Item(typesLong[i].toString(), R.drawable.calendar));
else
items.add(new Item(typesLong[i].toString(), R.drawable.placeholder));
}
@ -644,15 +656,15 @@ public class ActivityManageRule extends Activity
{
public View getView(int position, View convertView, ViewGroup parent)
{
//User super class to create the View
// User super class to create the View
View v = super.getView(position, convertView, parent);
TextView tv = (TextView)v.findViewById(android.R.id.text1);
//Put the image on the TextView
// Put the image on the TextView
tv.setCompoundDrawablesWithIntrinsicBounds(items.get(position).icon, 0, 0, 0);
//Add margin between image and text (support various screen densities)
// Add margin between image and text (support various screen densities)
int dp5 = (int) (5 * getResources().getDisplayMetrics().density + 0.5f);
tv.setCompoundDrawablePadding(dp5);
@ -861,6 +873,13 @@ public class ActivityManageRule extends Activity
startActivityForResult(variableTriggerEditor, requestCodeTriggerCheckVariableAdd);
return;
}
else if(triggerType == Trigger_Enum.calendarEvent)
{
newTrigger.setTriggerType(Trigger_Enum.calendarEvent);
Intent calendarTriggerEditor = new Intent(myContext, ActivityManageTriggerCalendar.class);
startActivityForResult(calendarTriggerEditor, requestCodeTriggerCalendarEventAdd);
return;
}
else
getTriggerParameterDialog(context, booleanChoices).show();
@ -1994,6 +2013,17 @@ public class ActivityManageRule extends Activity
this.refreshTriggerList();
}
}
else if(requestCode == requestCodeTriggerCalendarEventAdd)
{
if(resultCode == RESULT_OK)
{
newTrigger.setTriggerParameter(data.getBooleanExtra(intentNameTriggerParameter1, true));
newTrigger.setTriggerParameter2(data.getStringExtra(intentNameTriggerParameter2));
newTrigger.setParentRule(ruleToEdit);
ruleToEdit.getTriggerSet().add(newTrigger);
this.refreshTriggerList();
}
}
else if(requestCode == requestCodeTriggerTetheringEdit)
{
if(resultCode == RESULT_OK)
@ -2033,6 +2063,19 @@ public class ActivityManageRule extends Activity
this.refreshTriggerList();
}
}
else if(requestCode == requestCodeTriggerCalendarEventEdit)
{
if(resultCode == RESULT_OK)
{
Trigger editedTrigger = new Trigger();
editedTrigger.setTriggerType(Trigger_Enum.calendarEvent);
editedTrigger.setTriggerParameter(data.getBooleanExtra(intentNameTriggerParameter1, true));
editedTrigger.setTriggerParameter2(data.getStringExtra(intentNameTriggerParameter2));
editedTrigger.setParentRule(ruleToEdit);
ruleToEdit.getTriggerSet().set(editIndex, editedTrigger);
this.refreshTriggerList();
}
}
else if(requestCode == requestCodeActionCopyTextToClipboardAdd)
{
if(resultCode == RESULT_OK)
@ -2162,6 +2205,8 @@ public class ActivityManageRule extends Activity
items.add(new Item(typesLong[i].toString(), R.drawable.clipboard));
else if(types[i].toString().equals(Action_Enum.takeScreenshot.toString()))
items.add(new Item(typesLong[i].toString(), R.drawable.copier));
else if(types[i].toString().equals(Action_Enum.setVariable.toString()))
items.add(new Item(typesLong[i].toString(), R.drawable.variable));
else if(types[i].toString().equals(Action_Enum.setLocationService.toString()))
items.add(new Item(typesLong[i].toString(), R.drawable.compass_small));
else

View File

@ -0,0 +1,349 @@
package com.jens.automation2;
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.provider.CalendarContract;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.jens.automation2.receivers.CalendarReceiver;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
public class ActivityManageTriggerCalendar extends Activity
{
CheckBox chkCalendarEventActive, chkCalendarAvailabilityBusy, chkCalendarAvailabilityFree, chkCalendarAvailabilityTentative, chkCalendarAvailabilityOutOfOffice, chkCalendarAvailabilityWorkingElsewhere, chkCalendarAllDayEvent;
Spinner spinnerCalendarTitleDirection, spinnerCalendarLocationDirection, spinnerCalendarDescriptionDirection;
EditText etCalendarTitle, etCalendarLocation, etCalendarDescription;
LinearLayout llCalendarSelection;
Button bSaveTriggerCalendar;
List<CheckBox> checkboxesCalendars = new ArrayList<>();
final static String separator = ",";
TextView tvMissingCalendarHint;
private static String[] directions;
ArrayAdapter<String> directionSpinnerAdapter;
public static int requestCodePermissionReadCalendar = 815;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Miscellaneous.setDisplayLanguage(this);
setContentView(R.layout.activity_manage_trigger_calendar);
chkCalendarEventActive = (CheckBox) findViewById(R.id.chkCalendarEventActive);
spinnerCalendarTitleDirection = (Spinner)findViewById(R.id.spinnerCalendarTitleDirection);
spinnerCalendarLocationDirection = (Spinner)findViewById(R.id.spinnerCalendarLocationDirection);
spinnerCalendarDescriptionDirection = (Spinner)findViewById(R.id.spinnerCalendarDescriptionDirection);
chkCalendarAllDayEvent = (CheckBox)findViewById(R.id.chkCalendarAllDayEvent);
chkCalendarAvailabilityBusy = (CheckBox)findViewById(R.id.chkCalendarAvailabilityBusy);
chkCalendarAvailabilityFree = (CheckBox)findViewById(R.id.chkCalendarAvailabilityFree);
chkCalendarAvailabilityTentative = (CheckBox)findViewById(R.id.chkCalendarAvailabilityTentative);
chkCalendarAvailabilityOutOfOffice = (CheckBox)findViewById(R.id.chkCalendarAvailabilityOutOfOffice);
chkCalendarAvailabilityWorkingElsewhere = (CheckBox)findViewById(R.id.chkCalendarAvailabilityWorkingElsewhere);
tvMissingCalendarHint = (TextView) findViewById(R.id.tvMissingCalendarHint);
llCalendarSelection = (LinearLayout)findViewById(R.id.llCalendarSelection);
etCalendarTitle = (EditText)findViewById(R.id.etCalendarTitle);
etCalendarLocation = (EditText)findViewById(R.id.etCalendarLocation);
etCalendarDescription = (EditText)findViewById(R.id.etCalendarDescription);
bSaveTriggerCalendar = (Button)findViewById(R.id.bSaveTriggerCalendar);
directions = new String[] {
getResources().getString(R.string.directionStringEquals),
getResources().getString(R.string.directionStringContains),
getResources().getString(R.string.directionStringDoesNotContain),
getResources().getString(R.string.directionStringStartsWith),
getResources().getString(R.string.directionStringEndsWith),
getResources().getString(R.string.directionStringNotEquals)
};
directionSpinnerAdapter = new ArrayAdapter<String>(this, R.layout.text_view_for_poi_listview_mediumtextsize, ActivityManageTriggerCalendar.directions);
spinnerCalendarTitleDirection.setAdapter(directionSpinnerAdapter);
spinnerCalendarLocationDirection.setAdapter(directionSpinnerAdapter);
spinnerCalendarDescriptionDirection.setAdapter(directionSpinnerAdapter);
directionSpinnerAdapter.notifyDataSetChanged();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
if(ActivityPermissions.havePermission(Manifest.permission.READ_CALENDAR, ActivityManageTriggerCalendar.this) || ActivityPermissions.havePermission(Manifest.permission.WRITE_CALENDAR, ActivityManageTriggerCalendar.this))
populateCalenderCheckboxes();
else
{
AlertDialog.Builder builder = new AlertDialog.Builder(ActivityManageTriggerCalendar.this);
builder.setTitle(getResources().getString(R.string.info));
builder.setMessage(getResources().getString(R.string.permissionCalendarRequired));
builder.setNegativeButton(getResources().getString(R.string.cancel), new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialogInterface, int i)
{
ActivityManageTriggerCalendar.this.finish();
}
});
builder.setPositiveButton(getResources().getString(R.string.ok), new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialogInterface, int i)
{
requestPermissions(new String[]{ Manifest.permission.READ_CALENDAR } , requestCodePermissionReadCalendar);
}
});
builder.show();
}
}
else
populateCalenderCheckboxes();
chkCalendarEventActive.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
{
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked)
{
if(checked)
chkCalendarEventActive.setText(R.string.eventIsCurrentlyHappening);
else
chkCalendarEventActive.setText(R.string.eventIsCurrentlyNotHappening);
}
});
chkCalendarAllDayEvent.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
{
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked)
{
if(checked)
chkCalendarAllDayEvent.setText(getResources().getString(R.string.allDayEventTrue));
else
chkCalendarAllDayEvent.setText(getResources().getString(R.string.allDayEventFalse));
}
});
bSaveTriggerCalendar.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View view)
{
String titleDir = Trigger.getMatchCode(spinnerCalendarTitleDirection.getSelectedItem().toString());
String title = etCalendarTitle.getText().toString();
String descriptionDir = Trigger.getMatchCode(spinnerCalendarDescriptionDirection.getSelectedItem().toString());
String description = etCalendarDescription.getText().toString();
String locationDir = Trigger.getMatchCode(spinnerCalendarLocationDirection.getSelectedItem().toString());
String location = etCalendarLocation.getText().toString();
List<String> availabilityList = new ArrayList<>();
if(chkCalendarAvailabilityBusy.isChecked())
availabilityList.add(String.valueOf(CalendarContract.Events.AVAILABILITY_BUSY));
if(chkCalendarAvailabilityFree.isChecked())
availabilityList.add(String.valueOf(CalendarContract.Events.AVAILABILITY_FREE));
if(chkCalendarAvailabilityTentative.isChecked())
availabilityList.add(String.valueOf(CalendarContract.Events.AVAILABILITY_TENTATIVE));
if(chkCalendarAvailabilityOutOfOffice.isChecked())
availabilityList.add(String.valueOf(CalendarReceiver.AVAILABILITY_OUT_OF_OFFICE));
if(chkCalendarAvailabilityWorkingElsewhere.isChecked())
availabilityList.add(String.valueOf(CalendarReceiver.AVAILABILITY_WORKING_ELSEWHERE));
List<CalendarReceiver.AndroidCalendar> selectedCalendarsList = new ArrayList<>();
for(CheckBox calCheckbox : checkboxesCalendars)
{
if(calCheckbox.isChecked())
selectedCalendarsList.add((CalendarReceiver.AndroidCalendar) calCheckbox.getTag());
}
List<String> selectedCalendarsIdArray = new ArrayList<>();
for(CalendarReceiver.AndroidCalendar cal : selectedCalendarsList)
selectedCalendarsIdArray.add(String.valueOf(cal.calendarId));
String returnString =
titleDir + Trigger.triggerParameter2Split + title + Trigger.triggerParameter2Split +
descriptionDir + Trigger.triggerParameter2Split + description + Trigger.triggerParameter2Split +
locationDir + Trigger.triggerParameter2Split + location + Trigger.triggerParameter2Split +
String.valueOf(chkCalendarAllDayEvent.isChecked()) + Trigger.triggerParameter2Split +
Miscellaneous.explode(separator, availabilityList.toArray(new String[availabilityList.size()])) + Trigger.triggerParameter2Split +
Miscellaneous.explode(separator, selectedCalendarsIdArray.toArray(new String[selectedCalendarsIdArray.size()]));
Intent data = new Intent();
data.putExtra(ActivityManageRule.intentNameTriggerParameter1, chkCalendarEventActive.isChecked());
data.putExtra(ActivityManageRule.intentNameTriggerParameter2, returnString);
ActivityManageTriggerCalendar.this.setResult(RESULT_OK, data);
finish();
}
});
Intent inputIntent = getIntent();
if(inputIntent.hasExtra(ActivityManageRule.intentNameTriggerParameter1))
loadValuesIntoGui(inputIntent);
}
private void populateCalenderCheckboxes()
{
List<CalendarReceiver.AndroidCalendar> calList = CalendarReceiver.readCalendars(ActivityManageTriggerCalendar.this);
if(calList != null)
{
if(calList.size() > 0)
{
for (CalendarReceiver.AndroidCalendar cal : calList)
{
CheckBox oneCalCheckbox = new CheckBox(ActivityManageTriggerCalendar.this);
oneCalCheckbox.setText(cal.toString());
oneCalCheckbox.setTag(cal);
llCalendarSelection.addView(oneCalCheckbox);
checkboxesCalendars.add(oneCalCheckbox);
}
}
else
Miscellaneous.messageBox(getResources().getString(R.string.warning), getResources().getString(R.string.noCalendarsOnYourDevice), ActivityManageTriggerCalendar.this).show();
}
else
Miscellaneous.messageBox(getResources().getString(R.string.warning), getResources().getString(R.string.errorReadingCalendars), ActivityManageTriggerCalendar.this).show();
}
void loadValuesIntoGui(Intent data)
{
//TODO:try-catch
if(data.hasExtra(ActivityManageRule.intentNameTriggerParameter1))
chkCalendarEventActive.setChecked(data.getBooleanExtra(ActivityManageRule.intentNameTriggerParameter1, true));
if(data.hasExtra(ActivityManageRule.intentNameTriggerParameter2))
{
String input[] = data.getStringExtra(ActivityManageRule.intentNameTriggerParameter2).split(Trigger.triggerParameter2Split, -1);
/*
0 = titleDir
1 = title
2 = descriptionDir
3 = description
4 = locationDir
5 = location
6 = all day event
7 = availability list
8 = calendars list
*/
for(int i = 0; i < directions.length; i++)
{
if(Trigger.getMatchCode(directions[i]).equalsIgnoreCase(input[0]))
spinnerCalendarTitleDirection.setSelection(i);
if(Trigger.getMatchCode(directions[i]).equalsIgnoreCase(input[2]))
spinnerCalendarDescriptionDirection.setSelection(i);
if(Trigger.getMatchCode(directions[i]).equalsIgnoreCase(input[4]))
spinnerCalendarLocationDirection.setSelection(i);
}
etCalendarTitle.setText(input[1]);
etCalendarDescription.setText(input[3]);
etCalendarLocation.setText(input[5]);
chkCalendarAllDayEvent.setChecked(Boolean.parseBoolean(input[6]));
String[] availabilities = null;
if(!StringUtils.isEmpty(input[7]))
availabilities = input[7].split(separator);
if(availabilities != null)
{
for (String avail : availabilities)
{
if (Integer.parseInt(avail) == CalendarContract.Events.AVAILABILITY_BUSY)
chkCalendarAvailabilityBusy.setChecked(true);
else if (Integer.parseInt(avail) == CalendarContract.Events.AVAILABILITY_FREE)
chkCalendarAvailabilityFree.setChecked(true);
else if (Integer.parseInt(avail) == CalendarContract.Events.AVAILABILITY_TENTATIVE)
chkCalendarAvailabilityTentative.setChecked(true);
else if (Integer.parseInt(avail) == CalendarReceiver.AVAILABILITY_OUT_OF_OFFICE)
chkCalendarAvailabilityOutOfOffice.setChecked(true);
else if (Integer.parseInt(avail) == CalendarReceiver.AVAILABILITY_WORKING_ELSEWHERE)
chkCalendarAvailabilityWorkingElsewhere.setChecked(true);
}
}
String[] calendars = null;
if(!StringUtils.isEmpty(input[8]))
calendars = input[8].split(separator);
if(calendars != null)
{
List<String> usedCalendarIDs = new ArrayList<>();
List<String> unusedCalendarIDs = new ArrayList<>();
for (CheckBox checkbox : checkboxesCalendars)
{
int id = ((CalendarReceiver.AndroidCalendar) checkbox.getTag()).calendarId;
for (String calId : calendars)
{
if (calId.equals(String.valueOf(id)))
{
usedCalendarIDs.add(String.valueOf(id));
checkbox.setChecked(true);
break;
}
}
}
for (String calId : calendars)
{
if (!Miscellaneous.arraySearch((ArrayList<String>) usedCalendarIDs, calId, false, true))
unusedCalendarIDs.add(calId);
}
if (unusedCalendarIDs.size() > 0)
{
/*
A calendar has been configured that has been deleted since. We cannot resolve it.
It will be removed with the next save, but we should inform this user
of these circumstances.
*/
tvMissingCalendarHint.setText(String.format(getResources().getString(R.string.calendarsMissingHint), Miscellaneous.explode(", ", (ArrayList<String>) unusedCalendarIDs)));
}
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
{
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode == requestCodePermissionReadCalendar)
{
if(
permissions[0].equals(Manifest.permission.READ_CALENDAR)
||
permissions[0].equals(Manifest.permission.WRITE_CALENDAR)
)
{
if(grantResults[0] == PackageManager.PERMISSION_GRANTED)
populateCalenderCheckboxes();
else
finish();
}
}
}
}

View File

@ -54,6 +54,7 @@ public class ActivityPermissions extends Activity
private static final int requestCodeForPermissionNotificationAccessAndroid13 = 12049;
private static final int requestCodeForPermissionsManageOverlay = 12050;
private static final int requestCodeForPermissionsAccessibility = 12051;
private static final int requestCodeForPermissionsScheduleExactAlarms = 12052;
protected String[] specificPermissionsToRequest = null;
public static String intentExtraName = "permissionsToBeRequested";
@ -558,6 +559,8 @@ public class ActivityPermissions extends Activity
addToArrayListUnique(Manifest.permission.INTERNET, requiredPermissions);
break;
case timeFrame:
if(Build.VERSION.SDK_INT >= 31 && Miscellaneous.getTargetSDK(Miscellaneous.getAnyContext()) >= 31)
addToArrayListUnique(Manifest.permission.SCHEDULE_EXACT_ALARM, requiredPermissions);
break;
case usb_host_connection:
addToArrayListUnique(Manifest.permission.READ_PHONE_STATE, requiredPermissions);
@ -580,6 +583,11 @@ public class ActivityPermissions extends Activity
case notification:
addToArrayListUnique(Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE, requiredPermissions);
break;
case calendarEvent:
addToArrayListUnique(Manifest.permission.READ_CALENDAR, requiredPermissions);
if(Build.VERSION.SDK_INT >= 31 && Miscellaneous.getTargetSDK(Miscellaneous.getAnyContext()) >= 31)
addToArrayListUnique(Manifest.permission.SCHEDULE_EXACT_ALARM, requiredPermissions);
break;
default:
break;
}
@ -828,6 +836,12 @@ public class ActivityPermissions extends Activity
case Manifest.permission.WRITE_EXTERNAL_STORAGE:
usingElements.add(getResources().getString(R.string.storeSettings));
break;
case Manifest.permission.SCHEDULE_EXACT_ALARM:
for(String ruleName : getRulesUsing(Trigger.Trigger_Enum.timeFrame))
usingElements.add(String.format(getResources().getString(R.string.ruleXrequiresThis), ruleName));
for(String ruleName : getRulesUsing(Trigger.Trigger_Enum.calendarEvent))
usingElements.add(String.format(getResources().getString(R.string.ruleXrequiresThis), ruleName));
break;
case Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE:
for(String ruleName : getRulesUsing(Trigger.Trigger_Enum.notification))
usingElements.add(String.format(getResources().getString(R.string.ruleXrequiresThis), ruleName));
@ -1023,6 +1037,10 @@ public class ActivityPermissions extends Activity
for(String ruleName : getRulesUsing(Action.Action_Enum.setLocationService))
usingElements.add(String.format(getResources().getString(R.string.ruleXrequiresThis), ruleName));
break;
case Manifest.permission.READ_CALENDAR:
for(String ruleName : getRulesUsing(Trigger.Trigger_Enum.calendarEvent))
usingElements.add(String.format(getResources().getString(R.string.ruleXrequiresThis), ruleName));
break;
}
return usingElements;
@ -1091,6 +1109,10 @@ public class ActivityPermissions extends Activity
if (requestCode == requestCodeForPermissionsAccessibility)
if(havePermission(Manifest.permission.BIND_ACCESSIBILITY_SERVICE, ActivityPermissions.this))
requestPermissions(cachedPermissionsToRequest, true);
if (requestCode == requestCodeForPermissionsScheduleExactAlarms)
if(havePermission(Manifest.permission.SCHEDULE_EXACT_ALARM, ActivityPermissions.this))
requestPermissions(cachedPermissionsToRequest, true);
}
}
@ -1218,6 +1240,22 @@ public class ActivityPermissions extends Activity
return;
}
else if (s.equalsIgnoreCase(Manifest.permission.SCHEDULE_EXACT_ALARM))
{
AlertDialog diag = Miscellaneous.messageBox(getResources().getString(R.string.info), getResources().getString(R.string.alarmsPermissionHint), ActivityPermissions.this);
diag.setOnDismissListener(new DialogInterface.OnDismissListener()
{
@Override
public void onDismiss(DialogInterface dialogInterface)
{
requiredPermissions.remove(s);
cachedPermissionsToRequest = requiredPermissions;
requestScheduleExactAlarms();
}
});
diag.show();
return;
}
else if(s.equals(Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS))
{
requiredPermissions.remove(s);
@ -1303,6 +1341,14 @@ public class ActivityPermissions extends Activity
startActivityForResult(intent, requestCodeForPermissionsNotifications);
}
void requestScheduleExactAlarms()
{
Intent intent = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM);
startActivityForResult(intent, requestCodeForPermissionsScheduleExactAlarms);
}
protected void applyChanges()
{
AutomationService service = AutomationService.getInstance();

View File

@ -806,7 +806,7 @@ public class Miscellaneous extends Service
alertDialog.setTitle(title);
alertDialog.setMessage(message);
alertDialog.setPositiveButton("Ok", new DialogInterface.OnClickListener()
alertDialog.setPositiveButton(context.getResources().getString(R.string.ok), new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int whichButton)
{

View File

@ -6,6 +6,7 @@ import android.util.Log;
import com.jens.automation2.location.CellLocationChangedReceiver;
import com.jens.automation2.location.WifiBroadcastReceiver;
import com.jens.automation2.receivers.BroadcastListener;
import com.jens.automation2.receivers.CalendarReceiver;
import com.jens.automation2.receivers.DateTimeListener;
import com.jens.automation2.receivers.AutomationListenerInterface;
import com.jens.automation2.receivers.BatteryReceiver;
@ -210,6 +211,9 @@ public class ReceiverCoordinator
if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.screenState))
ScreenStateReceiver.startScreenStateReceiver(AutomationService.getInstance());
if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.calendarEvent))
CalendarReceiver.startCalendarReceiver(AutomationService.getInstance());
}
public static void stopAllReceivers()
@ -243,6 +247,7 @@ public class ReceiverCoordinator
BluetoothReceiver.stopBluetoothReceiver();
HeadphoneJackListener.getInstance().stopListener(AutomationService.getInstance());
DeviceOrientationListener.getInstance().stopListener(AutomationService.getInstance());
CalendarReceiver.getInstance().stopListener(AutomationService.getInstance());
}
catch(Exception e)
{

View File

@ -14,6 +14,7 @@ import com.jens.automation2.location.WifiBroadcastReceiver;
import com.jens.automation2.receivers.BatteryReceiver;
import com.jens.automation2.receivers.BluetoothReceiver;
import com.jens.automation2.receivers.BroadcastListener;
import com.jens.automation2.receivers.CalendarReceiver;
import com.jens.automation2.receivers.ConnectivityReceiver;
import com.jens.automation2.receivers.DeviceOrientationListener;
import com.jens.automation2.receivers.HeadphoneJackListener;
@ -31,6 +32,7 @@ import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
public class Trigger
@ -63,6 +65,7 @@ public class Trigger
tethering,
subSystemState,
checkVariable,
calendarEvent,
phoneCall; //phoneCall always needs to be at the very end because of Google's shitty so called privacy
public String getFullName(Context context)
@ -123,6 +126,8 @@ public class Trigger
return context.getResources().getString(R.string.subSystemState);
case checkVariable:
return context.getResources().getString(R.string.checkVariable);
case calendarEvent:
return context.getResources().getString(R.string.calendarEventCapital);
default:
return "Unknown";
}
@ -247,11 +252,14 @@ public class Trigger
case subSystemState:
if(!checkSubSystemState())
result = false;
break;
case checkVariable:
if(!checkVariable())
result = false;
break;
case calendarEvent:
if(!checkCalendarEvent())
result = false;
break;
default:
break;
}
@ -609,6 +617,117 @@ public class Trigger
return false;
}
boolean checkCalendarEvent()
{
try
{
String[] conditions = this.getTriggerParameter2().split(Trigger.triggerParameter2Split);
List<CalendarReceiver.CalendarEvent> calendarEvents = CalendarReceiver.readCalendarEvents(AutomationService.getInstance(), false);
/*
0 = titleDirection
1 = title;
2 = descriptionDirection
3 = description
4 = eventLocationDirection
5 = eventLocation
6 = all day event
7 = availabilityList
8 = calendarList
*/
for(CalendarReceiver.CalendarEvent event : calendarEvents)
{
boolean isActive = getTriggerParameter();
if(isActive != event.isCurrentlyActive())
{
Miscellaneous.logEvent("i", "CalendarCheck", "Event has to be currently active: " + String.valueOf(triggerParameter) + ", but is required otherwise.", 5);
continue;
}
if(!StringUtils.isEmpty(conditions[1]))
{
if (!Miscellaneous.compare(conditions[0], conditions[1], event.title))
{
Miscellaneous.logEvent("i", "CalendarCheck", "Title does not match.", 5);
continue;
}
}
if(!StringUtils.isEmpty(conditions[3]))
{
if (!Miscellaneous.compare(conditions[2], conditions[3], event.description))
{
Miscellaneous.logEvent("i", "CalendarCheck", "Description does not match.", 5);
continue;
}
}
if(!StringUtils.isEmpty(conditions[5]))
{
if (!Miscellaneous.compare(conditions[4], conditions[5], event.location))
{
Miscellaneous.logEvent("i", "CalendarCheck", "Location does not match.", 5);
continue;
}
}
if (Boolean.parseBoolean(conditions[6]) != event.allDay)
{
Miscellaneous.logEvent("i", "CalendarCheck", "All day setting does not match.", 5);
continue;
}
if(!StringUtils.isEmpty(conditions[7]))
{
String[] availabilities = conditions[7].split(ActivityManageTriggerCalendar.separator);
if (availabilities.length > 0)
{
if (!Miscellaneous.arraySearch(availabilities, event.availability, false, true))
{
Miscellaneous.logEvent("i", "CalendarCheck", "Availability does not match.", 5);
continue;
}
}
}
if(!StringUtils.isEmpty(conditions[8]))
{
String[] calendars = conditions[8].split(ActivityManageTriggerCalendar.separator);
if (calendars.length > 0)
{
if (!Miscellaneous.arraySearch(calendars, String.valueOf(event.calendarId), false, true))
{
Miscellaneous.logEvent("i", "CalendarCheck", "Calendar does not match.", 5);
continue;
}
}
}
// No contradictions found
Miscellaneous.logEvent("i", "CalendarCheck", "Event " + event + " matches.", 4);
return true;
}
// At this point none of the calendar items matches this trigger
// If trigger demands no calendar event and there is absolutely no future event, we'll need to check for that.
if(calendarEvents.size() == 0)
{
if(getTriggerParameter() == false)
return true;
// Further criteria don't matter if there are no events to check
}
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "checkVariable()", Log.getStackTraceString(e), 1);
}
return false;
}
boolean checkBluetooth()
{
Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format("Checking for bluetooth...", this.getParentRule().getName()), 4);
@ -1813,6 +1932,44 @@ public class Trigger
else
returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.variableCheckStringDeleted), triggerParameter2));
break;
case calendarEvent:
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.calendarEvent));
returnString.append(" (");
if(triggerParameter)
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.eventIsCurrentlyHappening));
else
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.eventIsCurrentlyNotHappening));
returnString.append( ", ");
String[] conditions = triggerParameter2.split(triggerParameter2Split, -1);
if (!StringUtils.isEmpty(conditions[1]))
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.title) + " " + conditions[0] + " " + conditions[1] + ", ");
if (!StringUtils.isEmpty(conditions[3]))
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.location) + " " + conditions[2] + " " + conditions[3] + ", ");
if (!StringUtils.isEmpty(conditions[5]))
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.calendarDescription) + " " + conditions[4] + " " + conditions[5] + ", ");
if(Boolean.parseBoolean(conditions[6]))
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.allDayEventTrue) + ", ");
else
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.allDayEventFalse) + ", ");
if (!StringUtils.isEmpty(conditions[7]))
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.availabilities) + " " + conditions[7] + ", ");
if (!StringUtils.isEmpty(conditions[8]))
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.calendars) + " " + conditions[8]);
if (returnString.toString().endsWith(", "))
returnString.delete(returnString.length() - 2, returnString.length());
returnString.append(")");
break;
default:
returnString.append("error");
break;

View File

@ -0,0 +1,412 @@
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 android.content.IntentFilter;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.SystemClock;
import androidx.annotation.NonNull;
import com.jens.automation2.AutomationService;
import com.jens.automation2.Miscellaneous;
import com.jens.automation2.Rule;
import com.jens.automation2.Trigger;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
public class CalendarReceiver extends BroadcastReceiver implements AutomationListenerInterface
{
static CalendarReceiver calendarReceiverInstance = null;
static boolean calendarReceiverActive = false;
static IntentFilter calendarIntentFilter = null;
private static AutomationService automationServiceRef;
private static Intent calendarIntent = null;
public static final int AVAILABILITY_OUT_OF_OFFICE = 4;
public static final int AVAILABILITY_WORKING_ELSEWHERE = 5;
public static final String calendarAlarmAction = "ALARM_FOR_CALENDAR";
static List<AndroidCalendar> calendarsCache = null;
static List<CalendarEvent> calendarEventsCache = null;
static Timer timer = null;
static TimerTask timerTask = null;
static Calendar nextWakeup = null;
static AlarmManager alarmManager = null;
static boolean alarmHasChanged = false;
public static CalendarReceiver getInstance()
{
if(calendarReceiverInstance == null)
calendarReceiverInstance = new CalendarReceiver();
return calendarReceiverInstance;
}
@Override
public void onReceive(Context context, Intent intent)
{
if(intent.getAction().equalsIgnoreCase(Intent.ACTION_PROVIDER_CHANGED))
{
Miscellaneous.logEvent("i", "CalendarReceiver", "Received " + intent.getAction(), 5);
calendarsCache = null;
calendarEventsCache = null;
checkForRules(context);
armOrRearmTimer();
}
else if(intent.getAction().equalsIgnoreCase(calendarAlarmAction))
{
Miscellaneous.logEvent("i", "AlarmReceiver", "Received alarm for calendar receiver.", 5);
routineAtAlarm();
}
}
private static void checkForRules(Context context)
{
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidates(Trigger.Trigger_Enum.calendarEvent);
for(int i = 0; i < ruleCandidates.size(); i++)
{
if(ruleCandidates.get(i).getsGreenLight(context))
ruleCandidates.get(i).activate(AutomationService.getInstance(), false);
}
}
@Override
public void startListener(AutomationService automationService)
{
if(!calendarReceiverActive)
{
if(calendarReceiverInstance == null)
calendarReceiverInstance = new CalendarReceiver();
if(calendarIntentFilter == null)
{
calendarIntentFilter = new IntentFilter();
calendarIntentFilter.addAction(Intent.ACTION_PROVIDER_CHANGED);
// calendarIntentFilter.addDataScheme("content");
}
AutomationService.getInstance().registerReceiver(calendarReceiverInstance, calendarIntentFilter);
calendarReceiverActive = true;
}
}
@Override
public void stopListener(AutomationService automationService)
{
if(calendarReceiverActive)
{
if(calendarReceiverInstance != null)
{
AutomationService.getInstance().unregisterReceiver(calendarReceiverInstance);
calendarReceiverInstance = null;
}
calendarReceiverActive = false;
}
}
@Override
public boolean isListenerRunning()
{
return calendarReceiverActive;
}
@Override
public Trigger.Trigger_Enum[] getMonitoredTrigger()
{
return new Trigger.Trigger_Enum[]{Trigger.Trigger_Enum.calendarEvent};
}
public static class AndroidCalendar
{
public int calendarId;
public String displayName;
public String accountString;
@NonNull
@Override
public String toString()
{
return displayName + " (" + accountString + ")";
}
}
public static class CalendarEvent
{
public AndroidCalendar calendar;
public int calendarId;
public String eventId;
public String title;
public String description;
public String location;
public String availability;
public Calendar start, end;
public boolean allDay;
public boolean isCurrentlyActive()
{
Calendar now = Calendar.getInstance();
return now.getTimeInMillis() >= start.getTimeInMillis() && now.getTimeInMillis() < end.getTimeInMillis();
}
@NonNull
@Override
public String toString()
{
return title;
}
}
public static List<AndroidCalendar> readCalendars(Context context)
{
if(calendarsCache == null)
{
calendarsCache = new ArrayList<>();
Cursor cursor;
cursor = context.getContentResolver().query(
Uri.parse("content://com.android.calendar/calendars"),
new String[]{ "_id", "calendar_displayName", "ownerAccount", },
null, null, null);
cursor.moveToFirst();
// fetching calendars name
String CNames[] = new String[cursor.getCount()];
List<AndroidCalendar> calendarlist = new ArrayList<>();
for (int i = 0; i < CNames.length; i++)
{
try
{
AndroidCalendar calendar = new AndroidCalendar();
calendar.calendarId = Integer.parseInt(cursor.getString(0));
calendar.displayName = cursor.getString(1);
calendar.accountString = cursor.getString(2);
calendarsCache.add(calendar);
}
catch (Exception e)
{
}
cursor.moveToNext();
}
if (cursor != null)
cursor.close();
}
return calendarsCache;
}
public static List<CalendarEvent> readCalendarEvents(Context context, boolean includePastEvents)
{
if(calendarEventsCache == null)
{
calendarEventsCache = new ArrayList<>();
Cursor cursor;
cursor = context.getContentResolver().query(
Uri.parse("content://com.android.calendar/events"),
new String[] { "calendar_id", "_id", "title", "description", "allDay", "dtstart", "dtend", "eventLocation", "availability" },
null, null, null);
cursor.moveToFirst();
// fetching calendars name
String CNames[] = new String[cursor.getCount()];
Calendar now = Calendar.getInstance();
for (int i = 0; i < CNames.length; i++)
{
try
{
CalendarEvent event = new CalendarEvent();
event.calendarId = Integer.parseInt(cursor.getString(0));
for(AndroidCalendar cal : readCalendars(context))
{
if(cal.calendarId == event.calendarId)
{
event.calendar = cal;
break;
}
}
event.eventId = cursor.getString(1);
event.title = cursor.getString(2);
event.description = cursor.getString(3);
event.allDay = cursor.getString(4).equals("1");
event.start = Miscellaneous.calendarFromLong(Long.parseLong(cursor.getString(5)));
event.end = Miscellaneous.calendarFromLong(Long.parseLong(cursor.getString(6)));
event.location = cursor.getString(7);
event.availability = cursor.getString(8);
if(includePastEvents || event.end.getTimeInMillis() > now.getTimeInMillis())
calendarEventsCache.add(event);
}
catch (Exception e)
{}
cursor.moveToNext();
}
if(cursor != null)
cursor.close();
}
return calendarEventsCache;
}
protected static void routineAtAlarm()
{
checkForRules(Miscellaneous.getAnyContext());
// Set next timer
calculateNextWakeup();
armOrRearmTimer();
}
private static void armOrRearmTimer()
{
PendingIntent pi = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
if (alarmManager == null)
{
alarmManager = (AlarmManager) Miscellaneous.getAnyContext().getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(Miscellaneous.getAnyContext(), CalendarReceiver.class);
intent.setAction(calendarAlarmAction);
pi = PendingIntent.getBroadcast(AutomationService.getInstance(), 0, intent, 0);
}
}
else
{
timerTask = new TimerTask()
{
@Override
public void run()
{
routineAtAlarm();
}
};
if(timer != null)
{
timer.cancel();
timer.purge();
}
timer = new Timer();
}
if(nextWakeup == null)
{
readCalendarEvents(Miscellaneous.getAnyContext(), false);
calculateNextWakeup();
}
// If it's now filled, go on
if(nextWakeup != null)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && alarmManager.canScheduleExactAlarms()))
{
try
{
alarmManager.cancel(pi);
}
catch (Exception e)
{
}
Miscellaneous.logEvent("i", "armOrRearmTimer()", "Setting calendar alarm for " + nextWakeup.toString(), 5);
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nextWakeup.getTimeInMillis(), pi);
//alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextWakeup.getTimeInMillis(), pi);
}
}
else
timer.schedule(timerTask, nextWakeup.getTimeInMillis());
}
}
private static void calculateNextWakeup()
{
Calendar now = Calendar.getInstance();
if (nextWakeup != null && nextWakeup.getTimeInMillis() < now.getTimeInMillis())
nextWakeup = null;
List<CalendarEvent> events = readCalendarEvents(Miscellaneous.getAnyContext(), false);
if (events.size() > 0)
{
for (CalendarEvent event : events)
{
if (event.isCurrentlyActive())
{
if (nextWakeup == null || event.end.getTimeInMillis() < nextWakeup.getTimeInMillis())
{
nextWakeup = event.end;
Miscellaneous.logEvent("i", "calculateNextWakeupForCalendar()", "Choosing end of event " + event.title + " as next wakeup.", 5);
if(!alarmHasChanged)
alarmHasChanged = true;
}
}
else
{
if (nextWakeup == null || event.start.getTimeInMillis() < nextWakeup.getTimeInMillis())
{
nextWakeup = event.start;
Miscellaneous.logEvent("i", "calculateNextWakeupForCalendar()", "Choosing start of event " + event.title + " as next wakeup.", 5);
if(!alarmHasChanged)
alarmHasChanged = true;
}
}
}
}
//else
// we expect to be called byOnReceive() when new items exist
}
public static void startCalendarReceiver(final AutomationService automationServiceRef)
{
if (!calendarReceiverActive)
{
CalendarReceiver.automationServiceRef = automationServiceRef;
if (calendarReceiverInstance == null)
calendarReceiverInstance = new CalendarReceiver();
if (calendarIntentFilter == null)
{
calendarIntentFilter = new IntentFilter();
calendarIntentFilter.addAction(Intent.ACTION_PROVIDER_CHANGED);
calendarIntentFilter.addDataScheme("content");
calendarIntentFilter.addDataAuthority("com.android.calendar", null);
}
calendarIntent = automationServiceRef.registerReceiver(calendarReceiverInstance, calendarIntentFilter);
calendarReceiverActive = true;
armOrRearmTimer();
}
}
}