So I encountered an issue on an Android 10 phone where deleted events are still present when I query the user's calendar. The problem persisted across several hours (3 at the time of writing this)
I've looked through the following posts already in an attempt to find solutions, but none of them seemed to have worked for me, and I'm not sure if I'm just implementing their solutions incorrectly, if something's wrong with the phone, or whatever else.
Here's the posts I mentioned above:
Calendar deleted event exists in cursor
Deleting events from Calendar not being deleted
Android CalendarContract, deleting a recurring event causes all events to disappear on calendar?
Querying android calendar retrieves even deleted events
I know that when a user deletes something off their calendar, there's a possibility of it hanging around in whatever DB or structure Android stores the events in with a dirty or deleted flag set. My problem is that the events are both still present, and have neither of the previously mentioned flags set.
Additionally, I know that it could be a sync issue between Google's calendar and whatever local datastore the events are being stored in, but this issue persisted on the phone I'm testing on even after pulling in newly created events from the user's calendar, which makes it seem to me that the local datastore and the calendar should be in sync.
Here's the full code for the file where this problem is occurring for me - some things may not be related to the issue but I'm including everything just in case.
package com.example.plumbingreportgenerator.util.calendar;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.CalendarContract;
import android.provider.CalendarContract.*;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
public class EventReader {
// the context of the application this is being used in
private Context applicationContext;
public static final String[] EVENT_PROJECTION = new String[] {
Events.CALENDAR_ID, // 0
Events.TITLE, // 1
Events.DTSTART, // 2
Events.DELETED,
Events.DIRTY
};
// The indices for the projection array above.
private static final int PROJECTION_CALENDAR_ID_INDEX = 0;
private static final int PROJECTION_TITLE_INDEX = 1;
private static final int PROJECTION_DTSTART_INDEX = 2;
private static final int PROJECTION_DELETED_INDEX = 3;
private static final int PROJECTION_DIRTY_INDEX = 4;
public EventReader(Context context){
applicationContext = context;
}
// use android and java date libraries to determine the start of the month given by year and month
private static long getStartOfMonth(int year, int month){
java.util.Calendar cal = java.util.Calendar.getInstance();
cal.set(java.util.Calendar.YEAR, year);
cal.set(java.util.Calendar.MONTH, month);
cal.set(java.util.Calendar.DAY_OF_MONTH, 1);
cal.set(java.util.Calendar.HOUR_OF_DAY, 0);
cal.set(java.util.Calendar.MINUTE, 0);
cal.set(java.util.Calendar.SECOND, 0);
cal.set(java.util.Calendar.MILLISECOND, 0);
return cal.getTimeInMillis();
}
private static long getEndOfMonth(int year, int month){
java.util.Calendar cal = java.util.Calendar.getInstance();
cal.set(java.util.Calendar.YEAR, year);
cal.set(java.util.Calendar.MONTH, month);
cal.set(java.util.Calendar.DAY_OF_MONTH, cal.getActualMaximum(java.util.Calendar.DAY_OF_MONTH));
cal.set(java.util.Calendar.HOUR_OF_DAY, 23);
cal.set(java.util.Calendar.MINUTE, 59);
cal.set(java.util.Calendar.SECOND, 59);
cal.set(java.util.Calendar.MILLISECOND, 999);
return cal.getTimeInMillis();
}
// gets event titles for the given calendar from the given month
public ArrayList<EventTitleDateTuple> getEventDetailsForMonth(long calendarId, int year, int month){
// get the millisecond values for the start and end of the month given by year and month
long startOfMonth = getStartOfMonth(year, month);
long endOfMonth = getEndOfMonth(year, month);
// Create cursor and query for the events table
Cursor cur = null;
ContentResolver cr = applicationContext.getContentResolver();
Uri uri = Events.CONTENT_URI;
String selection = "((" + Events.CALENDAR_ID + " = ?) AND (" + Events.DELETED + " != 1) AND (" + Events.DIRTY + " != 1 ))";
String[] selectionArgs = new String[] {Long.toString(calendarId)};
// Submit the query and get a Cursor object back.
cur = cr.query(uri, EVENT_PROJECTION, selection, selectionArgs, null);
ArrayList<EventTitleDateTuple> eventDetails = new ArrayList<EventTitleDateTuple>();
while (cur.moveToNext()) {
long calID = 0;
String title = null;
long dtStart = 0;
// Get the field values
calID = cur.getLong(PROJECTION_CALENDAR_ID_INDEX);
title = cur.getString(PROJECTION_TITLE_INDEX);
int deleted = cur.getInt(PROJECTION_DELETED_INDEX);
int dirty = cur.getInt(PROJECTION_DIRTY_INDEX);
dtStart = cur.getLong(PROJECTION_DTSTART_INDEX);
// if the start date of the event is after this month and before the end of this month
if(dtStart >= startOfMonth && dtStart <= endOfMonth && title != null && title.length() > 0 && deleted != 1 && dirty != 1 && !title.contains("testy mates face")){
// the deleted events still make it through to here
eventDetails.add(new EventTitleDateTuple(title, dtStart));
}
}
cur.close();
return eventDetails;
}
}
This is not a problem with deleting events, it is problem with syncing. Sometimes in 2020/2021 Google added a "feature" (or a bug) in form of some kind of annoying cache.
In reality it means, from now on (unlike before) you simply never know if you have a fresh data or outdated data from their cache. The only way to somewhat force their cache to update regularly is to have constantly turned on auto-syncing of ALL CALENDARS, all the time.
This way changes take only seconds and Google Calendar cache is always being updated. If the auto-syncing of just 1 calendar is turned off, from unknown reasons cache is not updated on time and often it takes hours to reflect changes...
Similar problems can be find on these links:
Android CalendarContract.Instances table returns old values
Android Calendar Provider does not return latest data
Or even here on Google Support page:
https://support.google.com/calendar/thread/47536340?hl=en
I would love to know who in Google had this genius idea...Argh
Related
This query generates all upcoming appointments for the week but its not returning the appointments from this week that have already occurred. The logic is flawed and I have tried everything.
ObservableList<Appointments> appts = AppointmentsDAO.getAppts();
LocalDateTime now = LocalDateTime.now();
LocalDateTime startWeek = now.with(DayOfWeek.MONDAY);
LocalDateTime endWeek = now.with(DayOfWeek.SUNDAY);
FilteredList<Appointments> appointmentsFilteredList = new FilteredList<>(appts);
appointmentsFilteredList.setPredicate(row -> {
LocalDateTime start = (row.getStartTime().toLocalDateTime());
if (startWeek.isAfter(start) || endWeek.isBefore(start)) {
return false;
}
return true;
});
appointmentsTableView.setItems(appointmentsFilteredList);
appointmentsTableView.refresh();
}
I created this lambda based of a sql statement which was :
"SELECT * FROM appointments AS a INNER JOIN contacts AS c " +
"ON a.Contact_ID=c.Contact_ID WHERE YEARWEEK(start) = YEARWEEK(NOW())"
The sql worked perfect but I'm trying to eliminate it. Can somebody point me into the direction I need to go in? I'm assuming it has to do with the startWeek parameter and adding minus weeks one yields results for the previous week to which I do not want. Is there a way to fix it?
I have an app that uses alarms to start radio streams. It has a 'repeat daily' function. To check if the alarm should fire on a particular day I check if 'DAY_OF_WEEK' is in an array. Something like this:
int[] repeatOnDays = [0,1,1,1,1,1,1]; // first nr is sunday, last is saturday
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK)-1; // -1 because Sunday==1 but its index in the array is 0
if (repeatOnDays[dayOfWeek]>0) { /* FIRE ALARM TODAY */ }
else { /* DON'T FIRE ALARM TODAY */ }
(Note: above code may not be 100% java, I've simplified stuff)
This morning, when my code ran, it said dayOfWeek was '0' (Sunday) but it's Monday! And when I set another alarm it suddenly said dayOfWeek was '1'.
Wth? How can this happen?
// UPDATE: Here's the actual code:
JSONArray repeatDaily = new JSONArray("[0,1,1,1,1,1,1]"); // <- This is not actually here but it may help read the rest of the code :)
boolean fireToday = true;
if (repeat.equals("daily")) {
Log.d(APPTAG," > Daily repeat..");
int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK)-1;
if (repeatDaily.length()<dayOfWeek) { fireToday = false; }
else if (repeatDaily.getInt(dayOfWeek)>0) { fireToday = true; }
else { fireToday = false; }
Log.d(APPTAG," > Day: "+ dayOfWeek +", "+ repeatDaily.getInt(dayOfWeek));
}
if (!fireToday) {
Log.d(APPTAG," > Do not need to fire today");
return; // <-- important stuff
}
Logcat:
06:30:01 D/AlarmMgr > Daily repeat..
06:30:01 D/AlarmMgr > Day: 0, 0
06:30:01 D/AlarmMgr > Do not need to fire today
Looks like it's my own fault. A couple of lines before the code I pasted I set the calendar's time:
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(timeInMs);
But I had some weird code that, in certain conditions, caused timeInMs to be a date in the past.
Changed it to
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(System.currentTimeMillis());
that should fix it :P
I've a program which includes service. This program has settings which allows user to set up, disable and enable time. Between these two times (if option is enabled of course), the program should not work.
I'm actually having hard time to do this. I've already sucessfully converted "disabled" and "enabled" time in milliseconds. I have following code but it doesn't work as expected. I want to detect if current time is between two set up times, so i can disable service at that time.
public boolean isCurrentTimeBetween_enableDisable() {
long sysTime = System.currentTimeMillis();
if((sysTime > disableTime && sysTime < enableTime)) {
return true;
}
return false;
}
Anyone can give me better hint?
UPDATE:
If user selects lets say
Disable hour: 15:00
Enable hour: 22:00
Then code work as expected.
But if user selects lets say:
Disable hour: 22:00
Enable hour: 06:00
Then its obviously that Enable hour is the NEXT day. So i wrote the following code:
if(todaysDisableDate(context).getTime() > enableAt.getTime()) {
enableCal.add(Calendar.DAY_OF_MONTH, 1);
enableAt = formatEnableDate.parse(enableCal.get(Calendar.DAY_OF_MONTH) + "-" +(enableCal.get(Calendar.MONTH)+1) + "-" + enableCal.get(Calendar.YEAR) + " " + endHours_string + ":" + endMinutes_string);
}
Code below is getting the actual date.
public Date todaysDisableDate(Context context) {
Calendar disableCal = Calendar.getInstance();
getTimeValues_preferences((ContextWrapper) context, true, false); // this only gets a string for hour and minute (which is set up in preferences )
Date disableAt = null;
try {
disableAt = formatDisableDate.parse(disableCal.get(Calendar.DAY_OF_MONTH)+"-"+(disableCal.get(Calendar.MONTH)+1)+"-"+disableCal.get(Calendar.YEAR)+" "+startHours_string+":"+startMinutes_string); // današnji datum z današnjo uro
} catch (ParseException e) {
e.printStackTrace();
}
return disableAt;
}
public Date todaysEnableDate(Context context) {
Calendar enableCal = Calendar.getInstance();
getTimeValues_preferences((ContextWrapper) context, false, true);
Date enableAt = null;
try {
enableAt = formatEnableDate.parse(enableCal.get(Calendar.DAY_OF_MONTH)+"-"+(enableCal.get(Calendar.MONTH)+1)+"-"+enableCal.get(Calendar.YEAR)+" "+endHours_string+":"+endMinutes_string); // današnji datum z današnjo uro
if(todaysDisableDate(context).getTime() > enableAt.getTime()) {
enableCal.add(Calendar.DAY_OF_MONTH, 1);
enableAt = formatEnableDate.parse(enableCal.get(Calendar.DAY_OF_MONTH)+"-"+(enableCal.get(Calendar.MONTH)+1)+"-"+enableCal.get(Calendar.YEAR)+" "+endHours_string+":"+endMinutes_string);
}
} catch (ParseException e) {
}
return enableAt;
}
Code is working fine if service starts before 00:00. But if the service starts after midnight (of next day), then i'm getting false from method isCurrentTimeBetween_enableDisable(), because methods todaysDisableDate(Context context) and todaysEnableDate(Context context) are pulling out the next day (the same day as system hour is in)
Do you have to compare dates in your code? If that's a project requirement then you can ignore the following.
Otherwise, I think you can use AlarmManager to create the feature without actually comparing the date. You can create a "Enable" intent and a "Disable" intent for the AlarmManager to fire at the scheduled time. Something like this:
Register your alarms when the user confirmed the time schedule.
Intent intent = new Intent(context, yourAlarmReceiver.class); //or implicit with action
PendingIntent pIntent = PendingIntent.getBroadcast(
context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.setRepeating (typeConstant, triggerAtMillis, intervalMillis, pIntent);
You just need to figure out what triggerAtMillis is to determine the first shot of that broadcast, and intervalMillis will be a full day, which is a constant in the AlarmManager class.
Setup your custom receiver class (which I wrote as yourAlarmReceiver) which should extend BroadcastReceiver, and register the receiver in your service. In the onReceive() you should perform the corresponding actions based on intent.getAction(). Don't forget to register your receiver with an intent filter if you want more customization.
#Override
public void onReceive(Context context, Intent intent) {
switch(intent.getAction()){
case "enable": //enable if not enabled
case "disable": //disable if not disabled
default: break;
}
}
In this way it may save you some time from struggling with comparing today and tomorrow. You can determine the time of the very first shot by getting the current system time, probably in 24-hour format, and determine if your intended time has already passed. Whether it's been passed, you just need to set the initial firing time to currentTime + difference.
Hope it will shed some light.
I would recommend you to use the start date and end date itself...and not convert them to miliseconds. But this is only if you're not sure.
private String compareStringOne = "9:45";
private String compareStringTwo = "1:45";
SimpleDateFormat inputParser = new SimpleDateFormat(inputFormat, Locale.US);
private void compareDates(){
Calendar now = Calendar.getInstance();
int hour = now.get(Calendar.HOUR);
int minute = now.get(Calendar.MINUTE);
date = parseDate(hour + ":" + minute);
dateCompareOne = parseDate(compareStringOne);
dateCompareTwo = parseDate(compareStringTwo);
if ( dateCompareOne.before( date ) && dateCompareTwo.after(date)) {
//This is where you determine if the date is inbetween
}
}
private Date parseDate(String date) {
try {
return inputParser.parse(date);
} catch (java.text.ParseException e) {
return new Date(0);
}
}
What I want to do is to delete only the content that is saved by me in the calendar instead of all the content which is already present in the calendar. For that, I use the following code. But it will delete all the content of the calendar. So can anyone tell me how that can be prevented?
Uri CALENDAR_URI = Uri.parse("content://calendar/events");
ContentResolver cr = getContentResolver();
cr.delete(CALENDAR_URI, null, null); // Delete all
ContentValues values = new ContentValues();
values.put("calendar_id", 1);
values.put("title", this.title);
values.put("allDay", this.allDay);
values.put("dtstart", this.dtstart.toMillis(false));
values.put("dtend", this.dtend.toMillis(false));
values.put("description", this.description);
values.put("eventLocation", this.eventLocation);
values.put("visibility", this.visibility);
values.put("hasAlarm", this.hasAlarm);
cr.insert(CALENDAR_URI, values);
So what I want is to delete only that entry that is put by me.
Deleting the event
Uri EVENTS_URI = Uri.parse("content://com.android.calendar/" + "events");
ContentResolver cr = c.getContentResolver();
deleteEvent(cr, EVENTS_URI, 1);
private void deleteEvent(ContentResolver resolver, Uri eventsUri, int calendarId) {
Cursor cursor;
cursor = resolver.query(eventsUri, new String[]{ "_id" }, "calendar_id=" + calendarId, null, null);
while(cursor.moveToNext()) {
long eventId = cursor.getLong(cursor.getColumnIndex("_id"));
resolver.delete(ContentUris.withAppendedId(eventsUri, eventId), null, null);
}
cursor.close();
}
After reading the data from the Calendar just try this out..
Adding a Single-Occurrence Event to a Calendar
To add an entry to a specific calendar, we need to configure a calendar entry to insert using the ContentValues as follows:
ContentValues event = new ContentValues();
Each event needs to be tied to a specific Calendar, so the first thing you're going to want to set is the identifier of the Calendar to insert this event into:
event.put("calendar_id", calId);
We then set some of the basic information about the event, including String fields such as the event title, description and location.
event.put("title", "Event Title");
event.put("description", "Event Desc");
event.put("eventLocation", "Event Location");
There are a number of different options for configuring the time and date of an event.
We can set the event start and end information as follows:
long startTime = START_TIME_MS;
long endTime = END_TIME_MS;
event.put("dtstart", startTime);
event.put("dtend", endTime);
If we are adding a birthday or holiday, we would set the entry to be an all day event:
event.put("allDay", 1); // 0 for false, 1 for true
This information is sufficient for most entries. However, there are a number of other useful calendar entry attributes.
For example, you can set the event status to tentative (0), confirmed (1) or canceled (2):
event.put("eventStatus", 1);
You can control who can see this event by setting its visibility to default (0), confidential (1), private (2), or public (3):
event.put("visibility", 0);
You can control whether an event consumes time (can have schedule conflicts) on the calendar by setting its transparency to opaque (0) or transparent (1).
event.put("transparency", 0);
You can control whether an event triggers a reminder alarm as follows:
event.put("hasAlarm", 1); // 0 for false, 1 for true
Once the calendar event is configured correctly, we're ready to use the ContentResolver to insert the new calendar entry into the appropriate Uri for calendar events:
Uri eventsUri = Uri.parse("content://calendar/events");
Uri url = getContentResolver().insert(eventsUri, event);
The call to the insert() method contacts the Calendar content provider and attempts to insert the entry into the appropriate user Calendar. If you navigate to the Calendar application and launch it, you should see your calendar entry in the appropriate Calendar. Since the Calendar syncs, you will also see the Calendar entry online, if you're using the Google Calendar on the web.
Delete the event
private int DeleteCalendarEntry(int entryID) {
int iNumRowsDeleted = 0;
Uri eventsUri = Uri.parse(getCalendarUriBase()+"events");
Uri eventUri = ContentUris.withAppendedId(eventsUri, entryID);
iNumRowsDeleted = getContentResolver().delete(eventUri, null, null);
Log.i(DEBUG_TAG, "Deleted " + iNumRowsDeleted + " calendar entry.");
return iNumRowsDeleted;
}
Also go through this link for deleting
Problem. I need a way to find Starteam server time through Starteam Java SDK 8.0. Version of server is 8.0.172 so method Server.getCurrentTime() is not available since it was added only in server version 9.0.
Motivation. My application needs to use views at specific dates. So if there's some difference in system time between client (where the app is running) and server then obtained views are not accurate. In the worst case the client's requested date is in the future for server so the operation results in exception.
After some investigation I haven't found any cleaner solution than using a temporary item. My app requests the item's time of creation and compares it with local time. Here's the method I use to get server time:
public Date getCurrentServerTime() {
Folder rootFolder = project.getDefaultView().getRootFolder();
Topic newItem = (Topic) Item.createItem(project.getTypeNames().TOPIC, rootFolder);
newItem.update();
newItem.remove();
newItem.update();
return newItem.getCreatedTime().createDate();
}
If your StarTeam server is on a Windows box and your code will be executing on a Windows box, you could shell out and execute the NET time command to fetch the time on that machine and then compare it to the local time.
net time \\my_starteam_server_machine_name
which should return:
"Current time at \\my_starteam_server_machine_name is 10/28/2008 2:19 PM"
"The command completed successfully."
We needed to come up with a way of finding the server time for use with CodeCollab. Here is a (longish) C# code sample of how to do it without creating a temporary file. Resolution is 1 second.
static void Main(string[] args)
{
// ServerTime replacement for pre-2006 StarTeam servers.
// Picks a date in the future.
// Gets a view, sets the configuration to the date, and tries to get a property from the root folder.
// If it cannot retrieve the property, the date is too far in the future. Roll back the date to an earlier time.
DateTime StartTime = DateTime.Now;
Server s = new Server("serverAddress", 49201);
s.LogOn("User", "Password");
// Getting a view - doesn't matter which, as long as it is not deleted.
Project p = s.Projects[0];
View v = p.AccessibleViews[0]; // AccessibleViews saves checking permissions.
// Timestep to use when searching. One hour is fairly quick for resolution.
TimeSpan deltaTime = new TimeSpan(1, 0, 0);
deltaTime = new TimeSpan(24 * 365, 0, 0);
// Invalid calls return faster - start a ways in the future.
TimeSpan offset = new TimeSpan(24, 0, 0);
// Times before the view was created are invalid.
DateTime minTime = v.CreatedTime;
DateTime localTime = DateTime.Now;
if (localTime < minTime)
{
System.Console.WriteLine("Current time is older than view creation time: " + minTime);
// If the dates are so dissimilar that the current date is before the creation date,
// it is probably a good idea to use a bigger delta.
deltaTime = new TimeSpan(24 * 365, 0, 0);
// Set the offset to the minimum time and work up from there.
offset = minTime - localTime;
}
// Storage for calculated date.
DateTime testTime;
// Larger divisors converge quicker, but might take longer depending on offset.
const float stepDivisor = 10.0f;
bool foundValid = false;
while (true)
{
localTime = DateTime.Now;
testTime = localTime.Add(offset);
ViewConfiguration vc = ViewConfiguration.CreateFromTime(testTime);
View tempView = new View(v, vc);
System.Console.Write("Testing " + testTime + " (Offset " + (int)offset.TotalSeconds + ") (Delta " + deltaTime.TotalSeconds + "): ");
// Unfortunately, there is no isValid operation. Attempting to
// read a property from an invalid date configuration will
// throw an exception.
// An alternate to this would be proferred.
bool valid = true;
try
{
string testname = tempView.RootFolder.Name;
}
catch (ServerException)
{
System.Console.WriteLine(" InValid");
valid = false;
}
if (valid)
{
System.Console.WriteLine(" Valid");
// If the last check was invalid, the current check is valid, and
// If the change is this small, the time is very close to the server time.
if (foundValid == false && deltaTime.TotalSeconds <= 1)
{
break;
}
foundValid = true;
offset = offset.Add(deltaTime);
}
else
{
offset = offset.Subtract(deltaTime);
// Once a valid time is found, start reducing the timestep.
if (foundValid)
{
foundValid = false;
deltaTime = new TimeSpan(0,0,Math.Max((int)(deltaTime.TotalSeconds / stepDivisor), 1));
}
}
}
System.Console.WriteLine("Run time: " + (DateTime.Now - StartTime).TotalSeconds + " seconds.");
System.Console.WriteLine("The local time is " + localTime);
System.Console.WriteLine("The server time is " + testTime);
System.Console.WriteLine("The server time is offset from the local time by " + offset.TotalSeconds + " seconds.");
}
Output:
Testing 4/9/2009 3:05:40 PM (Offset 86400) (Delta 31536000): InValid
Testing 4/9/2008 3:05:40 PM (Offset -31449600) (Delta 31536000): Valid
...
Testing 4/8/2009 10:05:41 PM (Offset 25200) (Delta 3): InValid
Testing 4/8/2009 10:05:38 PM (Offset 25197) (Delta 1): Valid
Run time: 9.0933426 seconds.
The local time is 4/8/2009 3:05:41 PM
The server time is 4/8/2009 10:05:38 PM
The server time is offset from the local time by 25197 seconds.
<stab_in_the_dark>
I'm not familiar with that SDK but from looking at the API if the server is in a known timezone why not create and an OLEDate object whose date is going to be the client's time rolled appropriately according to the server's timezone?
</stab_in_the_dark>