calendar trigger

This commit is contained in:
jens 2023-12-29 19:43:50 +01:00
parent cd47b33449
commit 97a3344e81
9 changed files with 209 additions and 71 deletions

View File

@ -144,12 +144,7 @@
<receiver android:name=".receivers.DateTimeListener" /> <receiver android:name=".receivers.DateTimeListener" />
<receiver android:name=".receivers.ConnectivityReceiver" /> <receiver android:name=".receivers.ConnectivityReceiver" />
<receiver android:name=".receivers.TimeZoneListener" /> <receiver android:name=".receivers.TimeZoneListener" />
<receiver android:name=".receivers.CalendarReceiver"> <receiver android:name=".receivers.CalendarReceiver" />
<intent-filter>
<action android:name="android.intent.action.PROVIDER_CHANGED"/>
<data android:scheme="content"/>
<data android:host="com.android.calendar"/>
</intent-filter> </receiver>
<receiver <receiver
android:name=".DeviceAdmin" android:name=".DeviceAdmin"

View File

@ -1,7 +1,12 @@
package com.jens.automation2; package com.jens.automation2;
import android.Manifest;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.provider.CalendarContract; import android.provider.CalendarContract;
import android.view.View; import android.view.View;
@ -14,6 +19,7 @@ import android.widget.LinearLayout;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.jens.automation2.receivers.CalendarReceiver; import com.jens.automation2.receivers.CalendarReceiver;
@ -36,6 +42,7 @@ public class ActivityManageTriggerCalendar extends Activity
private static String[] directions; private static String[] directions;
ArrayAdapter<String> directionSpinnerAdapter; ArrayAdapter<String> directionSpinnerAdapter;
public static int requestCodePermissionReadCalendar = 815;
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) protected void onCreate(@Nullable Bundle savedInstanceState)
@ -79,14 +86,36 @@ public class ActivityManageTriggerCalendar extends Activity
spinnerCalendarDescriptionDirection.setAdapter(directionSpinnerAdapter); spinnerCalendarDescriptionDirection.setAdapter(directionSpinnerAdapter);
directionSpinnerAdapter.notifyDataSetChanged(); directionSpinnerAdapter.notifyDataSetChanged();
for(CalendarReceiver.AndroidCalendar cal : CalendarReceiver.readCalendars(ActivityManageTriggerCalendar.this)) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{ {
CheckBox oneCalCheckbox = new CheckBox(ActivityManageTriggerCalendar.this); if(ActivityPermissions.havePermission(Manifest.permission.READ_CALENDAR, ActivityManageTriggerCalendar.this) || ActivityPermissions.havePermission(Manifest.permission.WRITE_CALENDAR, ActivityManageTriggerCalendar.this))
oneCalCheckbox.setText(cal.toString()); populateCalenderCheckboxes();
oneCalCheckbox.setTag(cal); else
llCalendarSelection.addView(oneCalCheckbox); {
checkboxesCalendars.add(oneCalCheckbox); 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() chkCalendarEventActive.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
{ {
@ -172,6 +201,30 @@ public class ActivityManageTriggerCalendar extends Activity
loadValuesIntoGui(inputIntent); 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) void loadValuesIntoGui(Intent data)
{ {
//TODO:try-catch //TODO:try-catch
@ -181,7 +234,7 @@ public class ActivityManageTriggerCalendar extends Activity
if(data.hasExtra(ActivityManageRule.intentNameTriggerParameter2)) if(data.hasExtra(ActivityManageRule.intentNameTriggerParameter2))
{ {
String input[] = data.getStringExtra(ActivityManageRule.intentNameTriggerParameter2).split(Trigger.triggerParameter2Split); String input[] = data.getStringExtra(ActivityManageRule.intentNameTriggerParameter2).split(Trigger.triggerParameter2Split, -1);
/* /*
0 = titleDir 0 = titleDir
1 = title 1 = title
@ -216,10 +269,6 @@ public class ActivityManageTriggerCalendar extends Activity
if(!StringUtils.isEmpty(input[7])) if(!StringUtils.isEmpty(input[7]))
availabilities = input[7].split(separator); availabilities = input[7].split(separator);
String[] calendars = null;
if(!StringUtils.isEmpty(input[8]))
calendars = input[8].split(separator);
if(availabilities != null) if(availabilities != null)
{ {
for (String avail : availabilities) for (String avail : availabilities)
@ -237,6 +286,10 @@ public class ActivityManageTriggerCalendar extends Activity
} }
} }
String[] calendars = null;
if(!StringUtils.isEmpty(input[8]))
calendars = input[8].split(separator);
if(calendars != null) if(calendars != null)
{ {
List<String> usedCalendarIDs = new ArrayList<>(); List<String> usedCalendarIDs = new ArrayList<>();
@ -272,4 +325,25 @@ public class ActivityManageTriggerCalendar extends Activity
} }
} }
} }
@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

@ -806,7 +806,7 @@ public class Miscellaneous extends Service
alertDialog.setTitle(title); alertDialog.setTitle(title);
alertDialog.setMessage(message); 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) public void onClick(DialogInterface dialog, int whichButton)
{ {

View File

@ -1933,49 +1933,42 @@ public class Trigger
returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.variableCheckStringDeleted), triggerParameter2)); returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.variableCheckStringDeleted), triggerParameter2));
break; break;
case calendarEvent: case calendarEvent:
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.calendarEvent));
returnString.append(" (");
if(triggerParameter) if(triggerParameter)
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.eventIsCurrentlyHappening)); returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.eventIsCurrentlyHappening));
else else
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.eventIsCurrentlyNotHappening)); returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.eventIsCurrentlyNotHappening));
returnString.append( ", ");
String[] conditions = triggerParameter2.split(triggerParameter2Split, -1); String[] conditions = triggerParameter2.split(triggerParameter2Split, -1);
if( if (!StringUtils.isEmpty(conditions[1]))
!StringUtils.isEmpty(conditions[1]) returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.title) + " " + conditions[0] + " " + conditions[1] + ", ");
|| if (!StringUtils.isEmpty(conditions[3]))
!StringUtils.isEmpty(conditions[3]) returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.location) + " " + conditions[2] + " " + conditions[3] + ", ");
|| if (!StringUtils.isEmpty(conditions[5]))
!StringUtils.isEmpty(conditions[5]) returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.calendarDescription) + " " + conditions[4] + " " + conditions[5] + ", ");
||
!StringUtils.isEmpty(conditions[7])
||
!StringUtils.isEmpty(conditions[8]))
{
returnString.append(" (");
if (!StringUtils.isEmpty(conditions[1])) if(Boolean.parseBoolean(conditions[6]))
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.title) + " " + conditions[0] + " " + conditions[1] + ", "); returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.allDayEventTrue) + ", ");
if (!StringUtils.isEmpty(conditions[3])) else
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.location) + " " + conditions[2] + " " + conditions[3] + ", "); returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.allDayEventFalse) + ", ");
if (!StringUtils.isEmpty(conditions[5]))
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.calendarDescription) + " " + conditions[4] + " " + conditions[5] + ", ");
if(Boolean.parseBoolean(conditions[6])) if (!StringUtils.isEmpty(conditions[7]))
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.allDayEventTrue) + ", "); returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.availabilities) + " " + conditions[7] + ", ");
else
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.allDayEventFalse) + ", ");
if (!StringUtils.isEmpty(conditions[7])) if (!StringUtils.isEmpty(conditions[8]))
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.availabilities) + " " + conditions[7] + ", "); returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.calendars) + " " + conditions[8]);
if (!StringUtils.isEmpty(conditions[8])) if (returnString.toString().endsWith(", "))
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.calendars) + " " + conditions[8]); returnString.delete(returnString.length() - 2, returnString.length());
if (returnString.toString().endsWith(", ")) returnString.append(")");
returnString.delete(returnString.length() - 2, returnString.length());
returnString.append(")");
}
break; break;
default: default:
returnString.append("error"); returnString.append("error");

View File

@ -17,6 +17,8 @@ import com.jens.automation2.Trigger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.List; import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
public class CalendarReceiver extends BroadcastReceiver implements AutomationListenerInterface public class CalendarReceiver extends BroadcastReceiver implements AutomationListenerInterface
{ {
@ -32,6 +34,10 @@ public class CalendarReceiver extends BroadcastReceiver implements AutomationLis
static List<AndroidCalendar> calendarsCache = null; static List<AndroidCalendar> calendarsCache = null;
static List<CalendarEvent> calendarEventsCache = null; static List<CalendarEvent> calendarEventsCache = null;
static Timer timer = null;
static TimerTask timerTask = null;
static Calendar nextWakeup = null;
public static CalendarReceiver getInstance() public static CalendarReceiver getInstance()
{ {
if(calendarReceiverInstance == null) if(calendarReceiverInstance == null)
@ -45,15 +51,23 @@ public class CalendarReceiver extends BroadcastReceiver implements AutomationLis
{ {
if(intent.getAction().equalsIgnoreCase(Intent.ACTION_PROVIDER_CHANGED)) if(intent.getAction().equalsIgnoreCase(Intent.ACTION_PROVIDER_CHANGED))
{ {
Miscellaneous.logEvent("i", "CalendarReceiver", "Received " + intent.getAction(), 5);
calendarsCache = null; calendarsCache = null;
calendarEventsCache = null; calendarEventsCache = null;
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidates(Trigger.Trigger_Enum.calendarEvent); checkForRules(context);
for(int i = 0; i < ruleCandidates.size(); i++) armOrRearmTimer();
{ }
if(ruleCandidates.get(i).getsGreenLight(context)) }
ruleCandidates.get(i).activate(AutomationService.getInstance(), false);
} 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);
} }
} }
@ -242,11 +256,79 @@ public class CalendarReceiver extends BroadcastReceiver implements AutomationLis
if(cursor != null) if(cursor != null)
cursor.close(); cursor.close();
} }
return calendarEventsCache; return calendarEventsCache;
} }
private static void armOrRearmTimer()
{
timerTask = new TimerTask()
{
@Override
public void run()
{
checkForRules(Miscellaneous.getAnyContext());
// Set next timer
calculateNextWakeup();
armOrRearmTimer();
}
};
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)
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);
}
}
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);
}
}
}
}
//else
// we expect to be called byOnReceive() when new items exist
}
public static void startCalendarReceiver(final AutomationService automationServiceRef) public static void startCalendarReceiver(final AutomationService automationServiceRef)
{ {
if (!calendarReceiverActive) if (!calendarReceiverActive)
@ -260,25 +342,15 @@ public class CalendarReceiver extends BroadcastReceiver implements AutomationLis
{ {
calendarIntentFilter = new IntentFilter(); calendarIntentFilter = new IntentFilter();
calendarIntentFilter.addAction(Intent.ACTION_PROVIDER_CHANGED); calendarIntentFilter.addAction(Intent.ACTION_PROVIDER_CHANGED);
calendarIntentFilter.addDataScheme("content");
calendarIntentFilter.addDataAuthority("com.android.calendar", null);
} }
calendarIntent = automationServiceRef.registerReceiver(calendarReceiverInstance, calendarIntentFilter); calendarIntent = automationServiceRef.registerReceiver(calendarReceiverInstance, calendarIntentFilter);
calendarReceiverActive = true; calendarReceiverActive = true;
}
}
public static void stopScreenStateReceiver() armOrRearmTimer();
{
if (calendarReceiverActive)
{
if (calendarReceiverInstance != null)
{
automationServiceRef.unregisterReceiver(calendarReceiverInstance);
calendarReceiverInstance = null;
}
calendarReceiverActive = false;
} }
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -301,8 +301,9 @@
<TextView <TextView
android:id="@+id/tvMissingCalendarHint" android:id="@+id/tvMissingCalendarHint"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginRight="@dimen/default_margin"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:textStyle="bold" /> android:textStyle="bold" />

View File

@ -765,7 +765,7 @@
<string name="startPhoneCall">Llamar al número de teléfono</string> <string name="startPhoneCall">Llamar al número de teléfono</string>
<string name="android.permission.CALL_PHONE">Llamar al número de teléfono</string> <string name="android.permission.CALL_PHONE">Llamar al número de teléfono</string>
<string name="makePhoneCallExplanation1">Aquí puede ingresar un número de teléfono al que se llamará sin más indicaciones. Puede usar esto para realizar configuraciones como realizar ajustes en el enrutamiento de llamadas, etc. Por favor, busque los códigos necesarios para esto por su cuenta.</string> <string name="makePhoneCallExplanation1">Aquí puede ingresar un número de teléfono al que se llamará sin más indicaciones. Puede usar esto para realizar configuraciones como realizar ajustes en el enrutamiento de llamadas, etc. Por favor, busque los códigos necesarios para esto por su cuenta.</string>
<string name="endPhoneCall">Terminar llamda de teléfono</string> <string name="endPhoneCall">Terminar llamada de teléfono</string>
<string name="android.permission.ANSWER_PHONE_CALLS">Terminar llamda de teléfono</string> <string name="android.permission.ANSWER_PHONE_CALLS">Terminar llamda de teléfono</string>
<string name="settingsReferringToRestrictedFeaturesInGoogle">La configuración y/o las reglas hacen referencia a funciones que no se pueden proporcionar en la versión de Google Play. Entre otras cosas que incluye todo lo relacionado con llamadas telefónicas y mensajes de texto.</string> <string name="settingsReferringToRestrictedFeaturesInGoogle">La configuración y/o las reglas hacen referencia a funciones que no se pueden proporcionar en la versión de Google Play. Entre otras cosas que incluye todo lo relacionado con llamadas telefónicas y mensajes de texto.</string>
<string name="variableCheckStringDeleted">Si la variable %1$s no está establecida</string> <string name="variableCheckStringDeleted">Si la variable %1$s no está establecida</string>

View File

@ -918,9 +918,9 @@
<string name="calendarStringTentative">tentative</string> <string name="calendarStringTentative">tentative</string>
<string name="calendarStringOutOfOffice">out of office</string> <string name="calendarStringOutOfOffice">out of office</string>
<string name="calendarStringWorkingElsewhere">working elsewhere</string> <string name="calendarStringWorkingElsewhere">working elsewhere</string>
<string name="selectingNoneItemForAllToMatch">Select no item if any will do.</string> <string name="selectingNoneItemForAllToMatch">If you select no item, any one will be ok.</string>
<string name="calendars">Calendars</string> <string name="calendars">Calendars</string>
<string name="calendarAvailabilityTypesUnsupported">It may be that only the first 3 types are actually working becaue the other types are not part of Google Calendar.</string> <string name="calendarAvailabilityTypesUnsupported">It may be that only the first 3 types are actually working because the other types are not part of Google Calendar.</string>
<string name="anyCalendar">any calender</string> <string name="anyCalendar">any calender</string>
<string name="availabilities">availabilities</string> <string name="availabilities">availabilities</string>
<string name="calendarsMissingHint">In this trigger calendars with IDs %1$s have been previously configured, but have been deleted since. With the next save those will be removed from this trigger. Until then this trigger/condition will never be met.</string> <string name="calendarsMissingHint">In this trigger calendars with IDs %1$s have been previously configured, but have been deleted since. With the next save those will be removed from this trigger. Until then this trigger/condition will never be met.</string>
@ -928,4 +928,7 @@
<string name="allDayEvent">All day event</string> <string name="allDayEvent">All day event</string>
<string name="allDayEventTrue">event is an all day event</string> <string name="allDayEventTrue">event is an all day event</string>
<string name="allDayEventFalse">event is not an all day event</string> <string name="allDayEventFalse">event is not an all day event</string>
<string name="permissionCalendarRequired">The permission to read your calendar will be required for a calendar trigger. It will already be required to populate the calendar fields in this window.</string>
<string name="noCalendarsOnYourDevice">It appears like no calendars have been set up on your device. You can save this trigger, but it will never return true.</string>
<string name="errorReadingCalendars">There was an error reading the calendars on your device.</string>
</resources> </resources>