Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(android): added new methods in Ti.Calendar.Calendar module for bulk operations #14149

Merged
merged 12 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@

package ti.modules.titanium.calendar;

import static ti.modules.titanium.calendar.EventProxy.getEventsUri;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.appcelerator.kroll.KrollDict;
import org.appcelerator.kroll.KrollProxy;
Expand All @@ -19,11 +23,17 @@

import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.provider.CalendarContract;
import android.text.format.DateUtils;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.OperationApplicationException;
import android.os.RemoteException;

@Kroll.proxy(parentModule = CalendarModule.class)
public class CalendarProxy extends KrollProxy
Expand Down Expand Up @@ -188,12 +198,147 @@ public EventProxy getEventById(int id)
return null;
}

@Kroll.method
public EventProxy[] getEventsById(Object args)
prashantsaini1 marked this conversation as resolved.
Show resolved Hide resolved
{
ArrayList<EventProxy> events = new ArrayList<>();

if (args instanceof Object[] eventIds && eventIds.length > 0) {
String query = CalendarUtils.prepareQuerySelection("_id", eventIds.length);
String[] queryArgs = CalendarUtils.prepareQueryArguments(eventIds);

events.addAll(EventProxy.queryEvents(query, queryArgs));
}

return events.toArray(new EventProxy[0]);
}

@Kroll.method
public EventProxy createEvent(KrollDict data)
{
return EventProxy.createEvent(this, data);
}

@Kroll.method
public EventProxy[] createEvents(Object data)
{
// Validate arguments to be an array.
if (!(data instanceof Object[] dataList && dataList.length > 0)) {
Log.e(TAG, "Argument expected to be an array.");
return null;
}

// Check for permissions.
ContentResolver contentResolver = TiApplication.getInstance().getContentResolver();
if (!CalendarProxy.hasCalendarPermissions()) {
prashantsaini1 marked this conversation as resolved.
Show resolved Hide resolved
Log.e(TAG, "Calendar permissions are missing.");
return null;
}

ArrayList<ContentProviderOperation> operations = new ArrayList<>();
ArrayList<EventProxy> eventProxies = new ArrayList<>();
Map<Integer, Integer> proxyResultIndexMapping = new HashMap<>();

for (int i = 0, firstIndex = 0; i < dataList.length; i++) {
KrollDict krollDict = new KrollDict((HashMap) dataList[i]);

EventProxy eventProxy = new EventProxy();
ContentValues contentValues = CalendarUtils.createContentValues(this, krollDict, eventProxy);

// We cannot pass null data to ContentProviderOperation.
// Necessary to keep track of non-null items later.
if (contentValues == null) {
eventProxies.add(null);
Log.e(TAG, "Title was not created, no title found for event");
prashantsaini1 marked this conversation as resolved.
Show resolved Hide resolved
continue;
}

ContentProviderOperation.Builder builder = ContentProviderOperation
.newInsert(CalendarContract.Events.CONTENT_URI)
.withValues(contentValues);

operations.add(builder.build());
eventProxies.add(eventProxy);

proxyResultIndexMapping.put(i, firstIndex);
firstIndex++;
}

try {
// Execute the batch operation
ContentProviderResult[] results = contentResolver.applyBatch(
getEventsUri().getAuthority(),
operations
);

// Find non-null proxies and map their IDs
for (int proxyIndex : proxyResultIndexMapping.keySet()) {
int proxyIndexInResults = proxyResultIndexMapping.get(proxyIndex);
Uri eventUri = results[proxyIndexInResults].uri;

if (eventUri != null) {
// Set event id to proxy.
eventProxies.get(proxyIndex).id = eventUri.getLastPathSegment();
} else {
// Event failed to get a proper URI path, should set to null in this case too.
eventProxies.set(proxyIndex, null);
}
}

return eventProxies.toArray(new EventProxy[0]);

} catch (RemoteException | OperationApplicationException e) {
Log.e(TAG, "Batch insert operation failed: " + e.getMessage());
return null;
}
}

@Kroll.method
public int deleteEvents(Object args)
{
int deletedCount = 0;

// Validate arguments to be an array.
if (!(args instanceof Object[] eventIds && eventIds.length > 0)) {
Log.e(TAG, "Argument expected to be an array.");
return deletedCount;
}

// Check for permissions.
ContentResolver contentResolver = TiApplication.getInstance().getContentResolver();
prashantsaini1 marked this conversation as resolved.
Show resolved Hide resolved
if (!CalendarProxy.hasCalendarPermissions()) {
prashantsaini1 marked this conversation as resolved.
Show resolved Hide resolved
Log.e(TAG, "Calendar permissions are missing.");
return deletedCount;
}

String query = CalendarUtils.prepareQuerySelection("_id", eventIds.length);
String[] queryArgs = CalendarUtils.prepareQueryArguments(eventIds);

ArrayList<ContentProviderOperation> operations = new ArrayList<>();
ContentProviderOperation.Builder builder = ContentProviderOperation
.newDelete(CalendarContract.Events.CONTENT_URI)
.withSelection(query, queryArgs);

operations.add(builder.build());

try {
// Execute the batch operation
ContentProviderResult[] results = contentResolver.applyBatch(
getEventsUri().getAuthority(),
operations
);

if (results.length > 0 && results[0].count != null) {
deletedCount = results[0].count;
}

} catch (RemoteException | OperationApplicationException e) {
Log.e(TAG, "Batch deletion failed: " + e.getMessage());
}

return deletedCount;
}

@Kroll.getProperty
public String getName()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package ti.modules.titanium.calendar;

import android.content.ContentValues;
import android.provider.CalendarContract;
import android.text.TextUtils;

import org.appcelerator.kroll.KrollDict;
import org.appcelerator.titanium.TiC;
import org.appcelerator.titanium.util.TiConvert;

import java.util.Collections;
import java.util.Date;

public class CalendarUtils
{
public static final String TAG = "CalendarUtils";

// Build the selection string for IN clause.
public static String prepareQuerySelection(String columnName, int limit)
{
return columnName + " IN (" + TextUtils.join(", ", Collections.nCopies(limit, "?")) + ")";
}

// Creates String[] for selectionArgs.
public static String[] prepareQueryArguments(Object[] data)
{
String[] queryArgs = new String[data.length];
for (int i = 0; i < data.length; i++) {
queryArgs[i] = String.valueOf(data[i]);
}
return queryArgs;
}

public static ContentValues createContentValues(CalendarProxy calendar, KrollDict data, EventProxy event)
{
if (!data.containsKey(TiC.PROPERTY_TITLE)) {
return null;
}

ContentValues contentValues = new ContentValues();
contentValues.put("hasAlarm", 1);
contentValues.put("hasExtendedProperties", 1);

event.title = TiConvert.toString(data, TiC.PROPERTY_TITLE);
contentValues.put(TiC.PROPERTY_TITLE, event.title);
contentValues.put("calendar_id", calendar.getId());
contentValues.put(CalendarContract.Events.EVENT_TIMEZONE, new Date().toString());

if (data.containsKey(TiC.PROPERTY_LOCATION)) {
event.location = TiConvert.toString(data, TiC.PROPERTY_LOCATION);
contentValues.put(CalendarModule.EVENT_LOCATION, event.location);
}

if (data.containsKey(TiC.PROPERTY_DESCRIPTION)) {
event.description = TiConvert.toString(data, TiC.PROPERTY_DESCRIPTION);
contentValues.put(TiC.PROPERTY_DESCRIPTION, event.description);
}

if (data.containsKey("begin")) {
event.begin = TiConvert.toDate(data, "begin");
if (event.begin != null) {
contentValues.put("dtstart", event.begin.getTime());
}
}

if (data.containsKey(TiC.PROPERTY_END)) {
event.end = TiConvert.toDate(data, TiC.PROPERTY_END);
if (event.end != null) {
contentValues.put("dtend", event.end.getTime());
}
}

if (data.containsKey("allDay")) {
event.allDay = TiConvert.toBoolean(data, "allDay");
contentValues.put("allDay", event.allDay ? 1 : 0);
}

if (data.containsKey("hasExtendedProperties")) {
event.hasExtendedProperties = TiConvert.toBoolean(data, "hasExtendedProperties");
contentValues.put("hasExtendedProperties", event.hasExtendedProperties ? 1 : 0);
}

if (data.containsKey("hasAlarm")) {
event.hasAlarm = TiConvert.toBoolean(data, "hasAlarm");
contentValues.put("hasAlarm", event.hasAlarm ? 1 : 0);
}

return contentValues;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ public EventProxy()
super();
}

public static String getEventsUri()
public static Uri getEventsUri()
{
return CalendarProxy.getBaseCalendarUri() + "/events";
return Events.CONTENT_URI;
}

public static String getInstancesWhenUri()
Expand All @@ -72,7 +72,7 @@ public static String getExtendedPropertiesUri()

public static ArrayList<EventProxy> queryEvents(String query, String[] queryArgs)
{
return queryEvents(Uri.parse(getEventsUri()), query, queryArgs, "dtstart ASC");
return queryEvents(getEventsUri(), query, queryArgs, "dtstart ASC");
}

public static ArrayList<EventProxy> queryEventsBetweenDates(long date1, long date2, String query,
Expand Down Expand Up @@ -146,7 +146,7 @@ public void save()
contentValues.put(Events.RRULE, ruleToSave);
ContentResolver contentResolver = TiApplication.getInstance().getContentResolver();
try {
contentResolver.update(Events.CONTENT_URI, contentValues, Events._ID + "=?", new String[] { id });
contentResolver.update(getEventsUri(), contentValues, Events._ID + "=?", new String[] { id });
} catch (IllegalArgumentException e) {
Log.e(TAG, "Invalid event recurrence rule.");
}
Expand Down Expand Up @@ -203,60 +203,20 @@ public static EventProxy createEvent(CalendarProxy calendar, KrollDict data)
if (!CalendarProxy.hasCalendarPermissions()) {
return null;
}
EventProxy event = new EventProxy();

ContentValues eventValues = new ContentValues();
eventValues.put("hasAlarm", 1);
eventValues.put("hasExtendedProperties", 1);
EventProxy event = new EventProxy();
ContentValues contentValues = CalendarUtils.createContentValues(calendar, data, event);

if (!data.containsKey("title")) {
if (contentValues == null) {
Log.e(TAG, "Title was not created, no title found for event");
return null;
}

event.title = TiConvert.toString(data, "title");
eventValues.put("title", event.title);
eventValues.put("calendar_id", calendar.getId());
eventValues.put(Events.EVENT_TIMEZONE, new Date().toString());

if (data.containsKey(TiC.PROPERTY_LOCATION)) {
event.location = TiConvert.toString(data, TiC.PROPERTY_LOCATION);
eventValues.put(CalendarModule.EVENT_LOCATION, event.location);
}
if (data.containsKey("description")) {
event.description = TiConvert.toString(data, "description");
eventValues.put("description", event.description);
}
if (data.containsKey("begin")) {
event.begin = TiConvert.toDate(data, "begin");
if (event.begin != null) {
eventValues.put("dtstart", event.begin.getTime());
}
}
if (data.containsKey("end")) {
event.end = TiConvert.toDate(data, "end");
if (event.end != null) {
eventValues.put("dtend", event.end.getTime());
}
}
if (data.containsKey("allDay")) {
event.allDay = TiConvert.toBoolean(data, "allDay");
eventValues.put("allDay", event.allDay ? 1 : 0);
}

if (data.containsKey("hasExtendedProperties")) {
event.hasExtendedProperties = TiConvert.toBoolean(data, "hasExtendedProperties");
eventValues.put("hasExtendedProperties", event.hasExtendedProperties ? 1 : 0);
}

if (data.containsKey("hasAlarm")) {
event.hasAlarm = TiConvert.toBoolean(data, "hasAlarm");
eventValues.put("hasAlarm", event.hasAlarm ? 1 : 0);
Uri eventUri = contentResolver.insert(Uri.parse(CalendarProxy.getBaseCalendarUri() + "/events"), contentValues);
if (eventUri == null) {
return null;
}

Uri eventUri = contentResolver.insert(Uri.parse(CalendarProxy.getBaseCalendarUri() + "/events"), eventValues);
Log.d("TiEvents", "created event with uri: " + eventUri, Log.DEBUG_MODE);

String eventId = eventUri.getLastPathSegment();
event.id = eventId;

Expand Down Expand Up @@ -565,13 +525,12 @@ public boolean remove()
ContentResolver contentResolver = TiApplication.getInstance().getContentResolver();

try {
Uri deleteUri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, TiConvert.toInt(id));
contentResolver.delete(deleteUri, null, null);
Uri deleteUri = ContentUris.withAppendedId(getEventsUri(), TiConvert.toInt(id));
int deletedCount = contentResolver.delete(deleteUri, null, null);
return deletedCount == 1;
} catch (IllegalArgumentException e) {
return false;
}

return true;
}

@Override
Expand Down
Loading
Loading